From a3881a94fcbbd637e4609e463d2c0012af2bb0a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Tomulik?= Date: Sun, 10 Nov 2013 02:03:47 +0100 Subject: [PATCH 001/800] Suppress misleading warn. in openbsd provider I have observed that `pkginfo` command used as `listcmd` by `ports` package provider sometimes outputs extra messages to stdout (the message starts with `Updating the pkgdb`). This causes puppet to issue its own warning as the line obtained from `listcmd` is not a valid package name. This patch suppresses this spurious message. --- lib/puppet/provider/package/openbsd.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/puppet/provider/package/openbsd.rb b/lib/puppet/provider/package/openbsd.rb index d51bc8f04..1a4dcb878 100644 --- a/lib/puppet/provider/package/openbsd.rb +++ b/lib/puppet/provider/package/openbsd.rb @@ -35,9 +35,11 @@ Puppet::Type.type(:package).provide :openbsd, :parent => Puppet::Provider::Packa packages << new(hash) hash = {} else - # Print a warning on lines we can't match, but move - # on, since it should be non-fatal - warning("Failed to match line #{line}") + unless line =~ /Updating the pkgdb/ + # Print a warning on lines we can't match, but move + # on, since it should be non-fatal + warning("Failed to match line #{line}") + end end } end From 0d0c9992d0533eaf73084dc263980a65ea150f62 Mon Sep 17 00:00:00 2001 From: Dominic Cleal Date: Wed, 13 Nov 2013 08:37:40 +0000 Subject: [PATCH 002/800] (maint) Add ruby-augeas to Gemfile and Travis for more test coverage --- .travis.yml | 1 + Gemfile | 1 + 2 files changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 09f7db6e5..50ac9f693 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,6 @@ language: ruby bundler_args: --without development +before_install: sudo apt-get update && sudo apt-get install libaugeas-dev libxml2-dev script: "bundle exec rake spec" notifications: email: false diff --git a/Gemfile b/Gemfile index 767f2496a..e72f01da3 100644 --- a/Gemfile +++ b/Gemfile @@ -16,6 +16,7 @@ platforms :ruby do gem 'yard', :group => :development gem 'redcarpet', '~> 2.0', :group => :development gem "racc", "1.4.9", :group => :development + gem "ruby-augeas", '~> 0.3', :require => false, :group => :extra end gem "puppet", :path => File.dirname(__FILE__), :require => false From c6c2c744b9eb44af605d96fa3f3a14723588bfa7 Mon Sep 17 00:00:00 2001 From: Jasper Lievisse Adriaanse Date: Wed, 13 Nov 2013 15:45:10 +0100 Subject: [PATCH 003/800] (#23141) Add OpenBSD to the exclusion list for 'remounts' in mount type --- lib/puppet/type/mount.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/type/mount.rb b/lib/puppet/type/mount.rb index 3f14fdc83..55a5f962b 100644 --- a/lib/puppet/type/mount.rb +++ b/lib/puppet/type/mount.rb @@ -249,7 +249,7 @@ module Puppet newvalues(:true, :false) defaultto do case Facter.value(:operatingsystem) - when "FreeBSD", "Darwin", "AIX", "DragonFly" + when "FreeBSD", "Darwin", "AIX", "DragonFly", "OpenBSD" false else true From 87b7299ab42f8714661b419d10cb48a45c226def Mon Sep 17 00:00:00 2001 From: Jasper Lievisse Adriaanse Date: Wed, 13 Nov 2013 15:58:41 +0100 Subject: [PATCH 004/800] On systems without "-o remount", use "-o update" except for AIX. --- lib/puppet/provider/mount.rb | 2 ++ spec/unit/provider/mount_spec.rb | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/puppet/provider/mount.rb b/lib/puppet/provider/mount.rb index 0839d99ce..0bad58f30 100644 --- a/lib/puppet/provider/mount.rb +++ b/lib/puppet/provider/mount.rb @@ -27,6 +27,8 @@ module Puppet::Provider::Mount info "Remounting" if resource[:remounts] == :true mountcmd "-o", "remount", resource[:name] + elsif ["FreeBSD", "Darwin", "DragonFly", "OpenBSD"].include?(Facter.value(:operatingsystem)) + mountcmd "-o", "update", resource[:name] else unmount mount diff --git a/spec/unit/provider/mount_spec.rb b/spec/unit/provider/mount_spec.rb index 34d819c57..27d576023 100755 --- a/spec/unit/provider/mount_spec.rb +++ b/spec/unit/provider/mount_spec.rb @@ -82,8 +82,8 @@ describe Puppet::Provider::Mount do it "should unmount and mount if the resource does not specify it supports remounting" do @mounter.stubs(:info) @resource.stubs(:[]).with(:remounts).returns(false) - @mounter.expects(:unmount) - @mounter.expects(:mount) + Facter.expects(:value).with(:operatingsystem).returns 'Darwin' + @mounter.expects(:mountcmd).with("-o", "update", @name) @mounter.remount end From 0b84445cc408aa9130040cc3c2170b1033ed1eb5 Mon Sep 17 00:00:00 2001 From: Charlie Sharpsteen Date: Tue, 19 Nov 2013 11:55:31 -0800 Subject: [PATCH 005/800] (#21869) Fix recursion in cert expiration check During every authenticated request, the expiration date of the involved certificates is checked. However, if the localhost cert is loaded when the CA cert is not present an authenticated request will be initiated to download the CA cert. This triggers another expiration check for authenticated requests, which loads the localhost cert, which initiates another authenticated request to download the CA cert... and so on until stack space is exhausted. This patch skips the expiration check for the localhost cert if the CA cert is missing. --- lib/puppet/network/authentication.rb | 9 +++++++-- spec/unit/network/authentication_spec.rb | 24 +++++++++++++++++++----- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/lib/puppet/network/authentication.rb b/lib/puppet/network/authentication.rb index f10e461d3..2517fd97e 100644 --- a/lib/puppet/network/authentication.rb +++ b/lib/puppet/network/authentication.rb @@ -13,8 +13,13 @@ module Puppet::Network::Authentication # Check CA cert if we're functioning as a CA certs << Puppet::SSL::CertificateAuthority.instance.host.certificate if Puppet::SSL::CertificateAuthority.ca? - # Always check the host cert if we have one, this will be the agent or master cert depending on the run mode - certs << Puppet::SSL::Host.localhost.certificate if Puppet::FileSystem::File.exist?(Puppet[:hostcert]) + # Depending on the run mode, the localhost certificate will be for the + # master or the agent. Don't load the certificate if the CA cert is not + # present: infinite recursion will occur as another authenticated request + # will be spawned to download the CA cert. + if Puppet::FileSystem::File.exist?(Puppet[:hostcert]) && Puppet::FileSystem::File.exist?(Puppet[:localcacert]) + certs << Puppet::SSL::Host.localhost.certificate + end # Remove nil values for caller convenience certs.compact.each do |cert| diff --git a/spec/unit/network/authentication_spec.rb b/spec/unit/network/authentication_spec.rb index c18552ab8..90308e924 100755 --- a/spec/unit/network/authentication_spec.rb +++ b/spec/unit/network/authentication_spec.rb @@ -31,11 +31,25 @@ describe Puppet::Network::Authentication do subject.warn_if_near_expiration end - it "should check the expiration of the localhost certificate" do - Puppet::SSL::Host.stubs(:localhost).returns(host) - cert.expects(:near_expiration?).returns(false) - Puppet::FileSystem::File.stubs(:exist?).with(Puppet[:hostcert]).returns(true) - subject.warn_if_near_expiration + context "when examining the local host" do + before do + Puppet::SSL::Host.stubs(:localhost).returns(host) + Puppet::FileSystem::File.stubs(:exist?).with(Puppet[:hostcert]).returns(true) + end + + it "should not load the localhost certificate if the local CA certificate is missing" do + # Redmine-21869: Infinite recursion occurs if CA cert is missing. + Puppet::FileSystem::File.stubs(:exist?).with(Puppet[:localcacert]).returns(false) + host.unstub(:certificate) + host.expects(:certificate).never + subject.warn_if_near_expiration + end + + it "should check the expiration of the localhost certificate if the local CA certificate is present" do + Puppet::FileSystem::File.stubs(:exist?).with(Puppet[:localcacert]).returns(true) + cert.expects(:near_expiration?).returns(false) + subject.warn_if_near_expiration + end end it "should check the expiration of any certificates passed in as arguments" do From 107f0e440fab5c28bbf729306a00bc341fd31e11 Mon Sep 17 00:00:00 2001 From: Adrien Thebo Date: Wed, 20 Nov 2013 12:12:32 -0800 Subject: [PATCH 006/800] (maint) Handle empty or malformed JSON lockfiles If a JSON lockfile was empty or had malformed JSON, it would raise a PSON::ParserError and could lead to application failures with an unclear cause. This commit changes the JSON lockfile behavior to treat empty or malformed file contents to be nil and generate warnings when the file contents are malformed JSON. --- lib/puppet/util/json_lockfile.rb | 5 ++++- spec/unit/util/json_lockfile_spec.rb | 29 ++++++++++++++++++++++++---- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/lib/puppet/util/json_lockfile.rb b/lib/puppet/util/json_lockfile.rb index ee2603c4e..df67b170f 100644 --- a/lib/puppet/util/json_lockfile.rb +++ b/lib/puppet/util/json_lockfile.rb @@ -34,8 +34,11 @@ class Puppet::Util::JsonLockfile < Puppet::Util::Lockfile def lock_data return nil unless file_locked? file_contents = super - return nil if file_contents.nil? + return nil if file_contents.nil? or file_contents.empty? PSON.parse(file_contents) + rescue PSON::ParserError => e + Puppet.warning "Unable to read lockfile data from #{@file_path}: not in PSON" + nil end end diff --git a/spec/unit/util/json_lockfile_spec.rb b/spec/unit/util/json_lockfile_spec.rb index 8f184646c..a547508a5 100644 --- a/spec/unit/util/json_lockfile_spec.rb +++ b/spec/unit/util/json_lockfile_spec.rb @@ -21,9 +21,30 @@ describe Puppet::Util::JsonLockfile do end end - it "should return the lock data" do - data = { "foo" => "foofoo", "bar" => "barbar" } - @lock.lock(data) - @lock.lock_data.should == data + describe "reading lock data" do + it "returns deserialized JSON from the lockfile" do + data = { "foo" => "foofoo", "bar" => "barbar" } + @lock.lock(data) + expect(@lock.lock_data).to eq data + end + + it "returns nil if the file read returned nil" do + @lock.lock + File.stubs(:read).returns nil + expect(@lock.lock_data).to be_nil + end + + it "returns nil if the file was empty" do + @lock.lock + File.stubs(:read).returns '' + expect(@lock.lock_data).to be_nil + end + + it "returns nil if the file was not in PSON" do + @lock.lock + File.stubs(:read).returns '][' + expect(@lock.lock_data).to be_nil + end + end end From 38198f6c6f9960864e979c5101ecf3f3e5b7ef3d Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 1 Sep 2013 04:15:25 +0200 Subject: [PATCH 007/800] (#22363) Add [] method to PPatternType to create regexp. The future evaluator makes it possible to reference types and perform operations on them. This implements an operation that allows a regular expression to be created from a string. Thus, String interpolation can be performed when constructing a regular expression. Example: $name = 'Fred' $a ~= Pattern["[a-z]*:${name}"] $b = Pattern['.*'] --- lib/puppet/pops/types/types.rb | 5 +++++ spec/unit/pops/types/type_operations_spec.rb | 10 ++++++++++ 2 files changed, 15 insertions(+) create mode 100644 spec/unit/pops/types/type_operations_spec.rb diff --git a/lib/puppet/pops/types/types.rb b/lib/puppet/pops/types/types.rb index f524308b4..70bd0ed18 100644 --- a/lib/puppet/pops/types/types.rb +++ b/lib/puppet/pops/types/types.rb @@ -68,6 +68,11 @@ module Puppet::Pops::Types # @api public class PPatternType < PLiteralType + module ClassModule + def [](pattern) + Regexp.new(pattern) + end + end end # @api public diff --git a/spec/unit/pops/types/type_operations_spec.rb b/spec/unit/pops/types/type_operations_spec.rb new file mode 100644 index 000000000..12e22d77b --- /dev/null +++ b/spec/unit/pops/types/type_operations_spec.rb @@ -0,0 +1,10 @@ +require 'spec_helper' +require 'puppet/pops' + +describe 'Puppet::Pops::Types::PPatternType' do + it 'can create a regular expression via the [] operator' do + result = Puppet::Pops::Types::PPatternType.new()['a*'] + expect(result.class).to eql(Regexp) + expect(result).to eql(Regexp.new('a*')) + end +end \ No newline at end of file From 31104582898806612cc1261d1e92cac45d87a13f Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 1 Sep 2013 23:28:22 +0200 Subject: [PATCH 008/800] (#22363) Extend type system with PHostClassType and PResourceType This extends the type system with support for host classes and resources. The primary reason for adding these to the type system is that the evaluator needs to expose something that represents references to types. In the 3.x these were simply qualified references/names/strings that only resolved to a type when passed as values to certain methods in the runtime. --- lib/puppet/parser/scope.rb | 13 ++ lib/puppet/pops/types/type_calculator.rb | 52 +++++++ lib/puppet/pops/types/type_factory.rb | 24 ++++ lib/puppet/pops/types/type_parser.rb | 67 ++++++++- lib/puppet/pops/types/types.rb | 32 ++++- spec/unit/pops/types/type_calculator_spec.rb | 135 +++++++++++++++++++ spec/unit/pops/types/type_factory_spec.rb | 32 +++++ spec/unit/pops/types/type_parser_spec.rb | 37 ++++- 8 files changed, 386 insertions(+), 6 deletions(-) diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb index 2cad0c1dd..86d7f5244 100644 --- a/lib/puppet/parser/scope.rb +++ b/lib/puppet/parser/scope.rb @@ -123,10 +123,23 @@ class Puppet::Parser::Scope compiler.node.facts end + # Returns true if the variable of the given name has a non nil value. + # def include?(name) ! self[name].nil? end + # Returns true if the variable of the given name is set to any value (including nil) + # + def exist?(name) + effective.symtable(true).include?(name) + end + + # Returns true if the given name is bound in the current (most nested) scope. + def bound?(name) + effective.symtable(true).bound?(name) + end + # Is the value true? This allows us to control the definition of truth # in one place. def self.true?(value) diff --git a/lib/puppet/pops/types/type_calculator.rb b/lib/puppet/pops/types/type_calculator.rb index 3158a2cc5..b9b694b0f 100644 --- a/lib/puppet/pops/types/type_calculator.rb +++ b/lib/puppet/pops/types/type_calculator.rb @@ -246,6 +246,20 @@ class Puppet::Pops::Types::TypeCalculator return type end + # when both are hot-classes, reduce to PHostClass[] (since one was not assignable to the other) + if t1.is_a?(Types::PHostClassType) && t2.is_a?(Types::PHostClassType) + return Types::PHostClassType.new() + end + + # when both are resources, reduce to Resource[T] or Resource[] (since one was not assignable to the other) + if t1.is_a?(Types::PResourceType) && t2.is_a?(Types::PResourceType) + result = Types::PResourceType.new() + # only Resource[] unless the type name is the same + if t1.type_name == t2.type_name then result.type_name = t1.type_name end + # the cross assignability test above has already determined that they do not have the same type and title + return result + end + # Common abstract types, from most specific to most general if common_numeric?(t1, t2) return Types::PNumericType.new() @@ -472,6 +486,22 @@ class Puppet::Pops::Types::TypeCalculator assignable?(t.key_type, t2.key_type) && assignable?(t.element_type, t2.element_type) end + def assignable_PHostClassType(t1, t2) + return false unless t2.is_a?(Types::PHostClassType) + # Class = Class[name}, Class[name] != Class + return true if t1.class_name.nil? + # Class[name] = Class[name] + return t1.class_name == t2.class_name + end + + def assignable_PResourceType(t1, t2) + return false unless t2.is_a?(Types::PResourceType) + return true if t1.type_name.nil? + return false if t1.type_name != t2.type_name + return true if t1.title.nil? + return t1.title == t2.title + end + # Data is assignable by other Data and by Array[Data] and Hash[Literal, Data] # @api private def assignable_PDataType(t, t2) @@ -531,6 +561,28 @@ class Puppet::Pops::Types::TypeCalculator "Hash[#{string(t.key_type)}, #{string(t.element_type)}]" end + # @api private + def string_PHostClassType(t) + if t.class_name + "Class[#{t.class_name}]" + else + "Class" + end + end + + # @api private + def string_PResourceType(t) + if t.type_name + if t.title + "#{t.type_name.capitalize}['#{t.title}']" + else + "#{t.type_name.capitalize}" + end + else + "Resource" + end + end + private def class_from_string(str) diff --git a/lib/puppet/pops/types/type_factory.rb b/lib/puppet/pops/types/type_factory.rb index 2be11be11..a44cf3b8e 100644 --- a/lib/puppet/pops/types/type_factory.rb +++ b/lib/puppet/pops/types/type_factory.rb @@ -69,6 +69,30 @@ module Puppet::Pops::Types::TypeFactory Types::PDataType.new() end + # Produces a PResourceType with a String type_name + # A PResourceType with a nil or empty name is compatible with any other PResourceType. + # A PResourceType with a given name is only compatible with a PResourceType with the same name. + # (There is no resource-type subtyping in Puppet (yet)). + # + def self.resource(type_name = nil, title = nil) + type = Types::PResourceType.new() + type_name = type_name.type_name if type_name.is_a?(Types::PResourceType) + type.type_name = type_name + type.title = title + type + end + + # Produces PHostClassType with a string class_name, and an optional super type. + # A PHostClassType with nil or empty name is compatible with any other PHostClassType. + # A PHostClassType with a given name is only compatible with a PHostClassType with the same name. + # + def self.host_class(class_name = nil, super_type = nil) + type = Types::PHostClassType.new() + type.class_name = class_name + type.super_type = super_type + type + end + # Produces a type for Array[o] where o is either a type, or an instance for which a type is inferred. # @api public # diff --git a/lib/puppet/pops/types/type_parser.rb b/lib/puppet/pops/types/type_parser.rb index f62178a4d..162b14277 100644 --- a/lib/puppet/pops/types/type_parser.rb +++ b/lib/puppet/pops/types/type_parser.rb @@ -40,6 +40,13 @@ class Puppet::Pops::Types::TypeParser # @api private def interpret(ast) + result = @type_transformer.visit_this(self, ast) + raise_invalid_type_specification_error unless result.is_a?(Puppet::Pops::Types::PObjectType) + result + end + + # @api private + def interpret_any(ast) @type_transformer.visit_this(self, ast) end @@ -48,6 +55,14 @@ class Puppet::Pops::Types::TypeParser raise_invalid_type_specification_error end + def interpret_QualifiedName(name_ast) + name_ast.value + end + + def interpret_LiteralString(string_ast) + string_ast.value + end + # @api private def interpret_QualifiedReference(name_ast) case name_ast.value @@ -67,35 +82,81 @@ class Puppet::Pops::Types::TypeParser TYPES.array_of_data when "hash" TYPES.hash_of_data - else + when "class" + TYPES.host_class() + when "resource" + TYPES.resource() + when "object", "collection", "ruby", "type" + # should not be interpreted as Resource type raise_unknown_type_error(name_ast) + else + TYPES.resource(name_ast.value) end end # @api private def interpret_AccessExpression(parameterized_ast) - parameters = parameterized_ast.keys.collect { |param| interpret(param) } + parameters = parameterized_ast.keys.collect { |param| interpret_any(param) } + + unless parameterized_ast.left_expr.is_a?(Puppet::Pops::Model::QualifiedReference) + raise_invalid_type_specification_error + end + case parameterized_ast.left_expr.value when "array" if parameters.size != 1 raise_invalid_parameters_error("Array", 1, parameters.size) end + assert_type(parameters[0]) TYPES.array_of(parameters[0]) + when "hash" if parameters.size == 1 + assert_type(parameters[0]) TYPES.hash_of(parameters[0]) elsif parameters.size != 2 raise_invalid_parameters_error("Hash", "1 or 2", parameters.size) else + assert_type(parameters[0]) + assert_type(parameters[1]) TYPES.hash_of(parameters[1], parameters[0]) end - else + + when "class" + if parameters.size != 1 + raise_invalid_parameters_error("Class", 1, parameters.size) + end + TYPES.host_class(parameters[0]) + + when "resource" + if parameters.size == 1 + TYPES.resource(parameters[0]) + elsif parameters.size != 2 + raise_invalid_parameters_error("Resource", "1 or 2", parameters.size) + else + TYPES.resource(parameters[0], parameters[1]) + end + + when "object", "collection", "ruby", "type" + # should not be interpreted as Resource type raise_unknown_type_error(parameterized_ast.left_expr) + + else + # It is a resource such a File['/tmp/foo'] + type_name = parameterized_ast.left_expr.value + if parameters.size != 1 + raise_invalid_parameters_error(type_name.capitalize, 1, parameters.size) + end + TYPES.resource(type_name, parameters[0]) end end private + def assert_type(t) + raise_invalid_type_specification_error unless t.is_a?(Puppet::Pops::Types::PObjectType) + end + def raise_invalid_type_specification_error raise Puppet::ParseError, "The expression <#{@string}> is not a valid type specification." diff --git a/lib/puppet/pops/types/types.rb b/lib/puppet/pops/types/types.rb index 70bd0ed18..b214a7b67 100644 --- a/lib/puppet/pops/types/types.rb +++ b/lib/puppet/pops/types/types.rb @@ -132,6 +132,36 @@ module Puppet::Pops::Types self.class == o.class && ruby_class == o.ruby_class end end - end + + # Represents a (host-) class in the Puppet Language. + # @api public + class PHostClassType < PObjectType + has_attr 'class_name', String + contains_one_uni 'super_type', PHostClassType + module ClassModule + def hash + [self.class, host_class].hash + end + def ==(o) + self.class == o.class && class_name == o.class_name + end + end + end + + # Represents a Resource Type in the Puppet Language + # @api public + class PResourceType < PObjectType + has_attr 'type_name', String + has_attr 'title', String + module ClassModule + def hash + [self.class, type_name, title].hash + end + def ==(o) + self.class == o.class && type_name == o.type_name && title == o.title + end + end + end + end diff --git a/spec/unit/pops/types/type_calculator_spec.rb b/spec/unit/pops/types/type_calculator_spec.rb index 7ee47d648..8b6bf47c2 100644 --- a/spec/unit/pops/types/type_calculator_spec.rb +++ b/spec/unit/pops/types/type_calculator_spec.rb @@ -150,6 +150,55 @@ describe 'The type calculator' do end end + # Deal with cases not covered by infer computing common type + context 'when computing common type' do + it 'computes given resource type commonality' do + r1 = Puppet::Pops::Types::PResourceType.new() + r1.type_name = 'File' + r2 = Puppet::Pops::Types::PResourceType.new() + r2.type_name = 'File' + calculator.string(calculator.common_type(r1, r2)).should == "File" + + r2 = Puppet::Pops::Types::PResourceType.new() + r2.type_name = 'File' + r2.title = '/tmp/foo' + calculator.string(calculator.common_type(r1, r2)).should == "File" + + r1 = Puppet::Pops::Types::PResourceType.new() + r1.type_name = 'File' + r1.title = '/tmp/foo' + calculator.string(calculator.common_type(r1, r2)).should == "File['/tmp/foo']" + + r1 = Puppet::Pops::Types::PResourceType.new() + r1.type_name = 'File' + r1.title = '/tmp/bar' + calculator.string(calculator.common_type(r1, r2)).should == "File" + + r2 = Puppet::Pops::Types::PResourceType.new() + r2.type_name = 'Package' + r2.title = 'apache' + calculator.string(calculator.common_type(r1, r2)).should == "Resource" + end + + it 'computes given hostclass type commonality' do + r1 = Puppet::Pops::Types::PHostClassType.new() + r1.class_name = 'foo' + r2 = Puppet::Pops::Types::PHostClassType.new() + r2.class_name = 'foo' + calculator.string(calculator.common_type(r1, r2)).should == "Class[foo]" + + r2 = Puppet::Pops::Types::PHostClassType.new() + r2.class_name = 'bar' + calculator.string(calculator.common_type(r1, r2)).should == "Class" + + r2 = Puppet::Pops::Types::PHostClassType.new() + calculator.string(calculator.common_type(r1, r2)).should == "Class" + + r1 = Puppet::Pops::Types::PHostClassType.new() + calculator.string(calculator.common_type(r1, r2)).should == "Class" + end + end + context 'when testing if x is assignable to y' do it 'should allow all object types to PObjectType' do t = Puppet::Pops::Types::PObjectType.new() @@ -167,6 +216,8 @@ describe 'The type calculator' do calculator.assignable?(t,Puppet::Pops::Types::PArrayType.new()).should() == true calculator.assignable?(t,Puppet::Pops::Types::PHashType.new()).should() == true calculator.assignable?(t,Puppet::Pops::Types::PRubyType.new()).should() == true + calculator.assignable?(t,Puppet::Pops::Types::PHostClassType.new()).should() == true + calculator.assignable?(t,Puppet::Pops::Types::PResourceType.new()).should() == true end it 'should reject PObjectType to less generic types' do @@ -183,6 +234,8 @@ describe 'The type calculator' do calculator.assignable?(Puppet::Pops::Types::PArrayType.new(), t).should() == false calculator.assignable?(Puppet::Pops::Types::PHashType.new(), t).should() == false calculator.assignable?(Puppet::Pops::Types::PRubyType.new(), t).should() == false + calculator.assignable?(Puppet::Pops::Types::PHostClassType.new(), t).should() == false + calculator.assignable?(Puppet::Pops::Types::PResourceType.new(), t).should() == false end it 'should allow all data types, array, and hash to PDataType' do @@ -295,6 +348,8 @@ describe 'The type calculator' do calculator.assignable?(Puppet::Pops::Types::PPatternType.new(), t).should() == false calculator.assignable?(Puppet::Pops::Types::PBooleanType.new(), t).should() == false calculator.assignable?(Puppet::Pops::Types::PRubyType.new(), t).should() == false + calculator.assignable?(Puppet::Pops::Types::PHostClassType.new(), t).should() == false + calculator.assignable?(Puppet::Pops::Types::PResourceType.new(), t).should() == false end it 'should reject PArrayType to non array type collections' do @@ -341,6 +396,55 @@ describe 'The type calculator' do calculator.assignable?(barType, fooType).should == false calculator.assignable?(Bar, fooType).should == false end + + it "should allow host class with same name" do + hc1 = Puppet::Pops::Types::TypeFactory.host_class('the_name') + hc2 = Puppet::Pops::Types::TypeFactory.host_class('the_name') + calculator.assignable?(hc1, hc2).should == true + end + + it "should allow host class with name assigned to hostclass without name" do + hc1 = Puppet::Pops::Types::TypeFactory.host_class() + hc2 = Puppet::Pops::Types::TypeFactory.host_class('the_name') + calculator.assignable?(hc1, hc2).should == true + end + + it "should reject host classes with different names" do + hc1 = Puppet::Pops::Types::TypeFactory.host_class('the_name') + hc2 = Puppet::Pops::Types::TypeFactory.host_class('another_name') + calculator.assignable?(hc1, hc2).should == false + end + + it "should reject host classes without name assigned to host class with name" do + hc1 = Puppet::Pops::Types::TypeFactory.host_class('the_name') + hc2 = Puppet::Pops::Types::TypeFactory.host_class() + calculator.assignable?(hc1, hc2).should == false + end + + it "should allow resource with same type_name and title" do + r1 = Puppet::Pops::Types::TypeFactory.resource('file', 'foo') + r2 = Puppet::Pops::Types::TypeFactory.resource('file', 'foo') + calculator.assignable?(r1, r2).should == true + end + + it "should allow more specific resource assignment" do + r1 = Puppet::Pops::Types::TypeFactory.resource() + r2 = Puppet::Pops::Types::TypeFactory.resource('file') + calculator.assignable?(r1, r2).should == true + r2 = Puppet::Pops::Types::TypeFactory.resource('file', '/tmp/foo') + calculator.assignable?(r1, r2).should == true + r1 = Puppet::Pops::Types::TypeFactory.resource('file') + calculator.assignable?(r1, r2).should == true + end + + it "should reject less specific resource assignment" do + r1 = Puppet::Pops::Types::TypeFactory.resource('file', '/tmp/foo') + r2 = Puppet::Pops::Types::TypeFactory.resource('file') + calculator.assignable?(r1, r2).should == false + r2 = Puppet::Pops::Types::TypeFactory.resource() + calculator.assignable?(r1, r2).should == false + end + end context 'when testing if x is instance of type t' do @@ -449,6 +553,35 @@ describe 'The type calculator' do t.element_type = Puppet::Pops::Types::PIntegerType.new() calculator.string(t).should == 'Hash[String, Integer]' end + + it "should yield 'Class' for a PHostClassType" do + t = Puppet::Pops::Types::PHostClassType.new() + calculator.string(t).should == 'Class' + end + + it "should yield 'Class[x]' for a PHostClassType[x]" do + t = Puppet::Pops::Types::PHostClassType.new() + t.class_name = 'x' + calculator.string(t).should == 'Class[x]' + end + + it "should yield 'Resource' for a PResourceType" do + t = Puppet::Pops::Types::PResourceType.new() + calculator.string(t).should == 'Resource' + end + + it 'should yield \'File\' for a PResourceType[\'File\']' do + t = Puppet::Pops::Types::PResourceType.new() + t.type_name = 'File' + calculator.string(t).should == 'File' + end + + it "should yield 'File['/tmp/foo']' for a PResourceType['File', '/tmp/foo']" do + t = Puppet::Pops::Types::PResourceType.new() + t.type_name = 'File' + t.title = '/tmp/foo' + calculator.string(t).should == "File['/tmp/foo']" + end end context 'when processing meta type' do @@ -467,6 +600,8 @@ describe 'The type calculator' do calculator.infer(Puppet::Pops::Types::PArrayType.new() ).is_a?(ptype).should() == true calculator.infer(Puppet::Pops::Types::PHashType.new() ).is_a?(ptype).should() == true calculator.infer(Puppet::Pops::Types::PRubyType.new() ).is_a?(ptype).should() == true + calculator.infer(Puppet::Pops::Types::PHostClassType.new() ).is_a?(ptype).should() == true + calculator.infer(Puppet::Pops::Types::PResourceType.new() ).is_a?(ptype).should() == true end it 'should infer PType as the type of ruby classes' do diff --git a/spec/unit/pops/types/type_factory_spec.rb b/spec/unit/pops/types/type_factory_spec.rb index be95871c4..b7ef16359 100644 --- a/spec/unit/pops/types/type_factory_spec.rb +++ b/spec/unit/pops/types/type_factory_spec.rb @@ -31,6 +31,38 @@ describe 'The type factory' do Puppet::Pops::Types::TypeFactory.data().class().should == Puppet::Pops::Types::PDataType end + it 'resource() creates a generic PResourceType' do + pr = Puppet::Pops::Types::TypeFactory.resource() + pr.class().should == Puppet::Pops::Types::PResourceType + pr.type_name.should == nil + end + + it 'resource(x) creates a PResourceType[x]' do + pr = Puppet::Pops::Types::TypeFactory.resource('x') + pr.class().should == Puppet::Pops::Types::PResourceType + pr.type_name.should == 'x' + end + + it 'host_class() creates a generic PHostClassType' do + hc = Puppet::Pops::Types::TypeFactory.host_class() + hc.class().should == Puppet::Pops::Types::PHostClassType + hc.class_name.should == nil + end + + it 'host_class(x) creates a PHostClassType[x]' do + hc = Puppet::Pops::Types::TypeFactory.host_class('x') + hc.class().should == Puppet::Pops::Types::PHostClassType + hc.class_name.should == 'x' + end + + it 'host_class(x, HostClassType) creates a PHostClassType[x]' do + hc_super = Puppet::Pops::Types::TypeFactory.host_class('base_type') + hc = Puppet::Pops::Types::TypeFactory.host_class('x', hc_super) + hc.class().should == Puppet::Pops::Types::PHostClassType + hc.class_name.should == 'x' + hc.super_type.should == hc_super + end + it 'array_of(fixnum) returns PArrayType[PIntegerType]' do at = Puppet::Pops::Types::TypeFactory.array_of(1) at.class().should == Puppet::Pops::Types::PArrayType diff --git a/spec/unit/pops/types/type_parser_spec.rb b/spec/unit/pops/types/type_parser_spec.rb index f0b9ea9a4..abf9eb459 100644 --- a/spec/unit/pops/types/type_parser_spec.rb +++ b/spec/unit/pops/types/type_parser_spec.rb @@ -16,16 +16,25 @@ describe Puppet::Pops::Types::TypeParser do end it "rejects an invalid type simple type" do - expect { parser.parse("NotAType") }.to raise_type_error_for("NotAType") + expect { parser.parse("notAType") }.to raise_error(Puppet::ParseError, /The expression is not a valid type specification/) end it "rejects an unknown parameterized type" do - expect { parser.parse("NotAType[Integer]") }.to raise_type_error_for("NotAType") + expect { parser.parse("notAType[Integer]") }.to raise_error(Puppet::ParseError, + /The expression is not a valid type specification/) + end + + it "rejects an unknown type parameter" do + expect { parser.parse("Array[notAType]") }.to raise_error(Puppet::ParseError, + /The expression is not a valid type specification/) end it "does not support types that do not make sense in the puppet language" do expect { parser.parse("Object") }.to raise_type_error_for("Object") expect { parser.parse("Collection[Integer]") }.to raise_type_error_for("Collection") + # These will/may make sense later, but are not yet implemented and should not be interpreted as a PResourceType + expect { parser.parse("Ruby") }.to raise_type_error_for("Ruby") + expect { parser.parse("Type") }.to raise_type_error_for("Type") end it "parses a simple, unparameterized type into the type object" do @@ -62,6 +71,30 @@ describe Puppet::Pops::Types::TypeParser do expect { parser.parse("Hash[Integer, Integer, Integer]") }.to raise_the_parameter_error("Hash", "1 or 2", 3) end + it "interprets anything that is not a built in type to be a resource type" do + expect(parser.parse("File")).to be_the_type(types.resource('file')) + end + + it "parses a resource type with title" do + expect(parser.parse("File['/tmp/foo']")).to be_the_type(types.resource('file', '/tmp/foo')) + end + + it "parses a resource type using 'Resource[type]' form" do + expect(parser.parse("Resource[File]")).to be_the_type(types.resource('file')) + end + + it "parses a resource type with title using 'Resource[type, title]'" do + expect(parser.parse("Resource[File, '/tmp/foo']")).to be_the_type(types.resource('file', '/tmp/foo')) + end + + it "parses a host class type" do + expect(parser.parse("Class")).to be_the_type(types.host_class()) + end + + it "parses a parameterized host class type" do + expect(parser.parse("Class[foo::bar]")).to be_the_type(types.host_class('foo::bar')) + end + matcher :be_the_type do |type| calc = Puppet::Pops::Types::TypeCalculator.new From 120774f963f15421935fb7cca57d236c8d106777 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 2 Sep 2013 02:36:53 +0200 Subject: [PATCH 009/800] (#22363) Add type parameter to PType This adds a type parameter to PType (the type of types). Without this it is not possible to infer types (everything reduces to PType). As an example, the evaluator want to assert that an Array only contains Resources (e.g. when evaluating x -> y). In the language, it is not possible to directly manipulate instances of resources (they are in the catalog, only the handles can be passed around and be manipulated). The idea is that instead of using strings, or qualified references or stringified 'File[foo]' as references, these are instead represented as types! This centrailizes the handling of what a "resource reference" is. This change thus infers(T) to be Type[T]. As an example the array [PIntegerType, PStringType] is infered to have the type Array[Type[Literal]]. --- lib/puppet/pops/types/type_calculator.rb | 42 ++++++++++++++++--- lib/puppet/pops/types/types.rb | 44 +++++++++++++++----- spec/unit/pops/types/type_calculator_spec.rb | 27 ++++++++++++ 3 files changed, 97 insertions(+), 16 deletions(-) diff --git a/lib/puppet/pops/types/type_calculator.rb b/lib/puppet/pops/types/type_calculator.rb index b9b694b0f..eb4effff5 100644 --- a/lib/puppet/pops/types/type_calculator.rb +++ b/lib/puppet/pops/types/type_calculator.rb @@ -201,7 +201,7 @@ class Puppet::Pops::Types::TypeCalculator # @api public # def is_ptype?(t) - return t.is_a?(Types::PObjectType) + return t.is_a?(Types::PAbstractType) end # Answers if t represents the puppet type PNilType @@ -273,6 +273,13 @@ class Puppet::Pops::Types::TypeCalculator return Types::PDataType.new() end + # Meta types Type[Integer] + Type[String] => Type[Data] + if t1.is_a?(Types::PType) && t2.is_a?(Types::PType) + type = Types::PType.new() + type.type = common_type(t1.type, t2.type) + return type + end + if t1.is_a?(Types::PRubyType) && t2.is_a?(Types::PRubyType) if t1.ruby_class == t2.ruby_class return t1 @@ -349,7 +356,9 @@ class Puppet::Pops::Types::TypeCalculator # @api private # def infer_PObjectType(o) - Types::PType.new() + type = Types::PType.new() + type.type = o.copy + type end # The type of all types is PType @@ -357,7 +366,9 @@ class Puppet::Pops::Types::TypeCalculator # @api private # def infer_PType(o) - Types::PType.new() + type = Types::PType.new() + type.type = o.copy + type end # @api private @@ -472,6 +483,12 @@ class Puppet::Pops::Types::TypeCalculator t2.is_a?(Types::PCollectionType) end + # @api private + def assignable_PType(t, t2) + return false unless t2.is_a?(Types::PType) + assignable?(t.type, t2.type) + end + # Array is assignable if t2 is an Array and t2's element type is assignable # @api private def assignable_PArrayType(t, t2) @@ -519,7 +536,19 @@ class Puppet::Pops::Types::TypeCalculator end # @api private - def string_PType(t) ; "Type" ; end + def string_PType(t) + if t.type.nil? + "Type" + else + "Type[#{string(t.type)}]" + end + end + + # @api private + def string_NilClass(t) ; '?' ; end + + # @api private + def string_String(t) ; t ; end # @api private def string_PObjectType(t) ; "Object" ; end @@ -549,7 +578,10 @@ class Puppet::Pops::Types::TypeCalculator def string_PStringType(t) ; "String" ; end # @api private - def string_PRubyType(t) ; "Ruby[#{t.ruby_class}]" ; end + def string_PCollectionType(t) ; "Collection" ; end + + # @api private + def string_PRubyType(t) ; "Ruby[#{string(t.ruby_class)}]" ; end # @api private def string_PArrayType(t) diff --git a/lib/puppet/pops/types/types.rb b/lib/puppet/pops/types/types.rb index b214a7b67..cfcb85d02 100644 --- a/lib/puppet/pops/types/types.rb +++ b/lib/puppet/pops/types/types.rb @@ -12,16 +12,14 @@ require 'rgen/metamodel_builder' # module Puppet::Pops::Types - # The type of types. - # @api public - class PType < Puppet::Pops::Model::PopsObject - end - - # Base type for all types except {Puppet::Pops::Types::PType PType}, the type of types. - # @api public - class PObjectType < Puppet::Pops::Model::PopsObject - + class PAbstractType < Puppet::Pops::Model::PopsObject + abstract module ClassModule + # Produce a deep copy of the type + def copy + Marshal.load(Marshal.dump(self)) + end + def hash self.class.hash end @@ -31,6 +29,30 @@ module Puppet::Pops::Types end alias eql? == + + end + end + + # The type of types. + # @api public + class PType < PAbstractType + contains_one_uni 'type', PAbstractType + module ClassModule + def hash + [self.class, type].hash + end + + def ==(o) + self.class == o.class && type == o.type + end + end + end + + # Base type for all types except {Puppet::Pops::Types::PType PType}, the type of types. + # @api public + class PObjectType < PAbstractType + + module ClassModule end end @@ -81,7 +103,7 @@ module Puppet::Pops::Types # @api public class PCollectionType < PObjectType - contains_one_uni 'element_type', PObjectType + contains_one_uni 'element_type', PAbstractType module ClassModule def hash [self.class, element_type].hash @@ -108,7 +130,7 @@ module Puppet::Pops::Types # @api public class PHashType < PCollectionType - contains_one_uni 'key_type', PObjectType + contains_one_uni 'key_type', PAbstractType module ClassModule def hash [self.class, key_type, element_type].hash diff --git a/spec/unit/pops/types/type_calculator_spec.rb b/spec/unit/pops/types/type_calculator_spec.rb index 8b6bf47c2..27c7938b2 100644 --- a/spec/unit/pops/types/type_calculator_spec.rb +++ b/spec/unit/pops/types/type_calculator_spec.rb @@ -602,6 +602,33 @@ describe 'The type calculator' do calculator.infer(Puppet::Pops::Types::PRubyType.new() ).is_a?(ptype).should() == true calculator.infer(Puppet::Pops::Types::PHostClassType.new() ).is_a?(ptype).should() == true calculator.infer(Puppet::Pops::Types::PResourceType.new() ).is_a?(ptype).should() == true + calculator.string(calculator.infer(Puppet::Pops::Types::PIntegerType.new())).should == "Type[Integer]" + end + + it 'should infer PType as the type of all other types' do + ptype = Puppet::Pops::Types::PType + calculator.string(calculator.infer(Puppet::Pops::Types::PNilType.new() )).should == "Type[Object]" + calculator.string(calculator.infer(Puppet::Pops::Types::PDataType.new() )).should == "Type[Data]" + calculator.string(calculator.infer(Puppet::Pops::Types::PLiteralType.new() )).should == "Type[Literal]" + calculator.string(calculator.infer(Puppet::Pops::Types::PStringType.new() )).should == "Type[String]" + calculator.string(calculator.infer(Puppet::Pops::Types::PNumericType.new() )).should == "Type[Numeric]" + calculator.string(calculator.infer(Puppet::Pops::Types::PIntegerType.new() )).should == "Type[Integer]" + calculator.string(calculator.infer(Puppet::Pops::Types::PFloatType.new() )).should == "Type[Float]" + calculator.string(calculator.infer(Puppet::Pops::Types::PPatternType.new() )).should == "Type[Pattern]" + calculator.string(calculator.infer(Puppet::Pops::Types::PBooleanType.new() )).should == "Type[Boolean]" + calculator.string(calculator.infer(Puppet::Pops::Types::PCollectionType.new())).should == "Type[Collection]" + calculator.string(calculator.infer(Puppet::Pops::Types::PArrayType.new() )).should == "Type[Array[?]]" + calculator.string(calculator.infer(Puppet::Pops::Types::PHashType.new() )).should == "Type[Hash[?, ?]]" + calculator.string(calculator.infer(Puppet::Pops::Types::PRubyType.new() )).should == "Type[Ruby[?]]" + calculator.string(calculator.infer(Puppet::Pops::Types::PHostClassType.new() )).should == "Type[Class]" + calculator.string(calculator.infer(Puppet::Pops::Types::PResourceType.new() )).should == "Type[Resource]" + end + + it "computes the common type of PType's type parameter" do + int_t = Puppet::Pops::Types::PIntegerType.new() + string_t = Puppet::Pops::Types::PStringType.new() + calculator.string(calculator.infer([int_t])).should == "Array[Type[Integer]]" + calculator.string(calculator.infer([int_t, string_t])).should == "Array[Type[Literal]]" end it 'should infer PType as the type of ruby classes' do From 9305ffa00e7f99106169842592c98d9a97d2e7a9 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 3 Sep 2013 17:48:06 +0200 Subject: [PATCH 010/800] (#22363) Add abstract type PCatalogEntryType An abstract type for things that are catalog types (host classes and resources) is needed to be able to assert things like Array[CatalogEntry]. --- lib/puppet/pops/types/type_calculator.rb | 9 +++++++++ lib/puppet/pops/types/type_factory.rb | 5 +++++ lib/puppet/pops/types/type_parser.rb | 16 ++++++++++++++-- lib/puppet/pops/types/types.rb | 12 ++++++++++-- spec/unit/pops/types/type_parser_spec.rb | 2 ++ 5 files changed, 40 insertions(+), 4 deletions(-) diff --git a/lib/puppet/pops/types/type_calculator.rb b/lib/puppet/pops/types/type_calculator.rb index eb4effff5..62c43cdf3 100644 --- a/lib/puppet/pops/types/type_calculator.rb +++ b/lib/puppet/pops/types/type_calculator.rb @@ -503,6 +503,10 @@ class Puppet::Pops::Types::TypeCalculator assignable?(t.key_type, t2.key_type) && assignable?(t.element_type, t2.element_type) end + def assignable_PCatalogEntryType(t1, t2) + t2.is_a?(Types::PCatalogEntryType) + end + def assignable_PHostClassType(t1, t2) return false unless t2.is_a?(Types::PHostClassType) # Class = Class[name}, Class[name] != Class @@ -593,6 +597,11 @@ class Puppet::Pops::Types::TypeCalculator "Hash[#{string(t.key_type)}, #{string(t.element_type)}]" end + # @api private + def string_PCatalogEntryType(t) + "CatalogEntry" + end + # @api private def string_PHostClassType(t) if t.class_name diff --git a/lib/puppet/pops/types/type_factory.rb b/lib/puppet/pops/types/type_factory.rb index a44cf3b8e..bbd566390 100644 --- a/lib/puppet/pops/types/type_factory.rb +++ b/lib/puppet/pops/types/type_factory.rb @@ -69,6 +69,11 @@ module Puppet::Pops::Types::TypeFactory Types::PDataType.new() end + # Produces an instance of the abstract type PCatalogEntryType + def self.catalog_entry() + Types::PCatalogEntryType.new() + end + # Produces a PResourceType with a String type_name # A PResourceType with a nil or empty name is compatible with any other PResourceType. # A PResourceType with a given name is only compatible with a PResourceType with the same name. diff --git a/lib/puppet/pops/types/type_parser.rb b/lib/puppet/pops/types/type_parser.rb index 162b14277..ce431f625 100644 --- a/lib/puppet/pops/types/type_parser.rb +++ b/lib/puppet/pops/types/type_parser.rb @@ -41,7 +41,7 @@ class Puppet::Pops::Types::TypeParser # @api private def interpret(ast) result = @type_transformer.visit_this(self, ast) - raise_invalid_type_specification_error unless result.is_a?(Puppet::Pops::Types::PObjectType) + raise_invalid_type_specification_error unless result.is_a?(Puppet::Pops::Types::PAbstractType) result end @@ -55,14 +55,21 @@ class Puppet::Pops::Types::TypeParser raise_invalid_type_specification_error end + # @api private def interpret_QualifiedName(name_ast) name_ast.value end + # @api private def interpret_LiteralString(string_ast) string_ast.value end + # @api private + def interpret_String(string_object) + string_object + end + # @api private def interpret_QualifiedReference(name_ast) case name_ast.value @@ -86,8 +93,13 @@ class Puppet::Pops::Types::TypeParser TYPES.host_class() when "resource" TYPES.resource() - when "object", "collection", "ruby", "type" + when "collection" + TYPES.collection() + when "catalogentry" + TYPES.catalog_entry() + when "object", "ruby", "type" # should not be interpreted as Resource type + # TODO: these should not be errors raise_unknown_type_error(name_ast) else TYPES.resource(name_ast.value) diff --git a/lib/puppet/pops/types/types.rb b/lib/puppet/pops/types/types.rb index cfcb85d02..244655386 100644 --- a/lib/puppet/pops/types/types.rb +++ b/lib/puppet/pops/types/types.rb @@ -156,9 +156,16 @@ module Puppet::Pops::Types end end + # Abstract representation of a type that can be placed in a Catalog. + # @api public + # + class PCatalogEntryType < PObjectType + end + # Represents a (host-) class in the Puppet Language. # @api public - class PHostClassType < PObjectType + # + class PHostClassType < PCatalogEntryType has_attr 'class_name', String contains_one_uni 'super_type', PHostClassType module ClassModule @@ -173,7 +180,8 @@ module Puppet::Pops::Types # Represents a Resource Type in the Puppet Language # @api public - class PResourceType < PObjectType + # + class PResourceType < PCatalogEntryType has_attr 'type_name', String has_attr 'title', String module ClassModule diff --git a/spec/unit/pops/types/type_parser_spec.rb b/spec/unit/pops/types/type_parser_spec.rb index abf9eb459..49b154e67 100644 --- a/spec/unit/pops/types/type_parser_spec.rb +++ b/spec/unit/pops/types/type_parser_spec.rb @@ -44,6 +44,8 @@ describe Puppet::Pops::Types::TypeParser do expect(the_type_parsed_from(types.boolean)).to be_the_type(types.boolean) expect(the_type_parsed_from(types.pattern)).to be_the_type(types.pattern) expect(the_type_parsed_from(types.data)).to be_the_type(types.data) + expect(the_type_parsed_from(types.catalog_entry)).to be_the_type(types.catalog_entry) + expect(the_type_parsed_from(types.collection)).to be_the_type(types.collection) end it "interprets an unparameterized Array as an Array of Data" do From f1eab0855c84b2a9c8eaf25f73943a9821056a5d Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 5 Sep 2013 04:46:09 +0200 Subject: [PATCH 011/800] (#22363) Remove super type from PHostClassType It was a bad idea. The super_class has to be resolved at runtime after having loaded the class. (The same way it is done for Ruby types). This commit just removes the ability to declare a PHostClass with a super type. --- lib/puppet/pops/types/type_factory.rb | 5 ++--- lib/puppet/pops/types/types.rb | 2 +- spec/unit/pops/types/type_factory_spec.rb | 8 -------- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/lib/puppet/pops/types/type_factory.rb b/lib/puppet/pops/types/type_factory.rb index bbd566390..48bd7d9f2 100644 --- a/lib/puppet/pops/types/type_factory.rb +++ b/lib/puppet/pops/types/type_factory.rb @@ -87,14 +87,13 @@ module Puppet::Pops::Types::TypeFactory type end - # Produces PHostClassType with a string class_name, and an optional super type. + # Produces PHostClassType with a string class_name. # A PHostClassType with nil or empty name is compatible with any other PHostClassType. # A PHostClassType with a given name is only compatible with a PHostClassType with the same name. # - def self.host_class(class_name = nil, super_type = nil) + def self.host_class(class_name = nil) type = Types::PHostClassType.new() type.class_name = class_name - type.super_type = super_type type end diff --git a/lib/puppet/pops/types/types.rb b/lib/puppet/pops/types/types.rb index 244655386..ef018cad0 100644 --- a/lib/puppet/pops/types/types.rb +++ b/lib/puppet/pops/types/types.rb @@ -167,7 +167,7 @@ module Puppet::Pops::Types # class PHostClassType < PCatalogEntryType has_attr 'class_name', String - contains_one_uni 'super_type', PHostClassType + # contains_one_uni 'super_type', PHostClassType module ClassModule def hash [self.class, host_class].hash diff --git a/spec/unit/pops/types/type_factory_spec.rb b/spec/unit/pops/types/type_factory_spec.rb index b7ef16359..e4524d23b 100644 --- a/spec/unit/pops/types/type_factory_spec.rb +++ b/spec/unit/pops/types/type_factory_spec.rb @@ -55,14 +55,6 @@ describe 'The type factory' do hc.class_name.should == 'x' end - it 'host_class(x, HostClassType) creates a PHostClassType[x]' do - hc_super = Puppet::Pops::Types::TypeFactory.host_class('base_type') - hc = Puppet::Pops::Types::TypeFactory.host_class('x', hc_super) - hc.class().should == Puppet::Pops::Types::PHostClassType - hc.class_name.should == 'x' - hc.super_type.should == hc_super - end - it 'array_of(fixnum) returns PArrayType[PIntegerType]' do at = Puppet::Pops::Types::TypeFactory.array_of(1) at.class().should == Puppet::Pops::Types::PArrayType From c0591c2f5098d74e73532cfd226ef77f9bdaaf25 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 5 Sep 2013 05:45:45 +0200 Subject: [PATCH 012/800] (#22363) Add [] type operation to subtypes of PCatalogType The [] operator is defined for PHostClassType and PResourceType to allow unspecific types to be used as templates for creating specific type references. i.e. to support the expressions File['a'], Class['a'] more naturally by applying [] to the LHS base type. --- lib/puppet/pops/types/types.rb | 13 +++++ spec/unit/pops/types/type_operations_spec.rb | 60 ++++++++++++++++++-- 2 files changed, 68 insertions(+), 5 deletions(-) diff --git a/lib/puppet/pops/types/types.rb b/lib/puppet/pops/types/types.rb index ef018cad0..67e13d59d 100644 --- a/lib/puppet/pops/types/types.rb +++ b/lib/puppet/pops/types/types.rb @@ -175,6 +175,12 @@ module Puppet::Pops::Types def ==(o) self.class == o.class && class_name == o.class_name end + def [](*class_names) + raise ArgumentError, "Cannot create new Class references from a specific Class reference" unless class_name.nil? + return self if class_names.size == 0 + result = class_names.collect {|n| x = self.class.new; x.class_name = n; x} + result.size == 1 ? result.pop : result + end end end @@ -191,6 +197,13 @@ module Puppet::Pops::Types def ==(o) self.class == o.class && type_name == o.type_name && title == o.title end + def [](*titles) + raise ArgumentError, "Cannot create new Resource references from a specific Resource reference" unless title.nil? + return self if titles.size == 0 + raise ArgumentError, "A Resource reference without type name can not produce Resource references." if type_name.nil? + result = titles.collect {|t| x = self.class.new; x.type_name = type_name; x.title = t; x} + result.size == 1 ? result.pop : result + end end end diff --git a/spec/unit/pops/types/type_operations_spec.rb b/spec/unit/pops/types/type_operations_spec.rb index 12e22d77b..4ee7b521a 100644 --- a/spec/unit/pops/types/type_operations_spec.rb +++ b/spec/unit/pops/types/type_operations_spec.rb @@ -1,10 +1,60 @@ require 'spec_helper' require 'puppet/pops' -describe 'Puppet::Pops::Types::PPatternType' do - it 'can create a regular expression via the [] operator' do - result = Puppet::Pops::Types::PPatternType.new()['a*'] - expect(result.class).to eql(Regexp) - expect(result).to eql(Regexp.new('a*')) +describe 'operations on types' do + + describe 'Puppet::Pops::Types::PPatternType' do + it 'can create a regular expression via the [] operator' do + result = Puppet::Pops::Types::PPatternType.new()['a*'] + expect(result.class).to eql(Regexp) + expect(result).to eql(Regexp.new('a*')) + end end + + describe 'the Puppet::Pops::Types::PCatalog subtypes' do + it 'can create an unspecific type by using the [] operator without arguments' do + x = Puppet::Pops::Types::TypeFactory.host_class() + expect(x[]).to be_the_type(Puppet::Pops::Types::TypeFactory.host_class()) + + x = Puppet::Pops::Types::TypeFactory.resource() + expect(x[]).to be_the_type(Puppet::Pops::Types::TypeFactory.resource()) + + x = Puppet::Pops::Types::TypeFactory.resource('File') + expect(x[]).to be_the_type(Puppet::Pops::Types::TypeFactory.resource('File')) + end + + it 'can create a specific class by using [name] on PHostClassType with given class name' do + x = Puppet::Pops::Types::TypeFactory.host_class() + expect(x['a']).to be_the_type(Puppet::Pops::Types::TypeFactory.host_class('a')) + end + + it 'can create a specific resource reference by using [name] on a PResourceType with given type name' do + x = Puppet::Pops::Types::TypeFactory.resource('File') + expect(x['a']).to be_the_type(Puppet::Pops::Types::TypeFactory.resource('File', 'a')) + end + + it 'checks error conditions' do + x = Puppet::Pops::Types::TypeFactory.host_class('a') + expect{x['b']}.to raise_error(/Cannot create new Class references from a specific Class reference/) + + x = Puppet::Pops::Types::TypeFactory.resource('File', 'foo') + expect{x['b']}.to raise_error(/Cannot create new Resource references from a specific Resource reference/) + + x = Puppet::Pops::Types::TypeFactory.resource() + expect{x['b']}.to raise_error(/A Resource reference without type name can not produce Resource references/) + end + end + + matcher :be_the_type do |type| + calc = Puppet::Pops::Types::TypeCalculator.new + + match do |actual| + calc.assignable?(actual, type) && calc.assignable?(type, actual) + end + + failure_message_for_should do |actual| + "expected #{calc.string(type)}, but was #{calc.string(actual)}" + end + end + end \ No newline at end of file From ff551b57242ba17b0bceab4d0f76b05bfb9836ed Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 5 Sep 2013 05:52:14 +0200 Subject: [PATCH 013/800] (#22363) Add test of capability to create array of types using [] This checks the feature that class and resource references support the shorthand notation X[a,b,c] to produce [X[a], X[b], X[c]]. This applies only to subtypes of PCatalogType. It is not possible to create an Array of other parameterized types. --- spec/unit/pops/types/type_operations_spec.rb | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/spec/unit/pops/types/type_operations_spec.rb b/spec/unit/pops/types/type_operations_spec.rb index 4ee7b521a..7f0827873 100644 --- a/spec/unit/pops/types/type_operations_spec.rb +++ b/spec/unit/pops/types/type_operations_spec.rb @@ -33,6 +33,24 @@ describe 'operations on types' do expect(x['a']).to be_the_type(Puppet::Pops::Types::TypeFactory.resource('File', 'a')) end + it 'can create an Array of class references' do + x = Puppet::Pops::Types::TypeFactory.host_class() + result = x['a', 'b', 'c'] + expect(result.class).to eql(Array) + expect(result[0]).to be_the_type(Puppet::Pops::Types::TypeFactory.host_class('a')) + expect(result[1]).to be_the_type(Puppet::Pops::Types::TypeFactory.host_class('b')) + expect(result[2]).to be_the_type(Puppet::Pops::Types::TypeFactory.host_class('c')) + end + + it 'can create an Array of Resource references' do + x = Puppet::Pops::Types::TypeFactory.resource('F') + result = x['a', 'b', 'c'] + expect(result.class).to eql(Array) + expect(result[0]).to be_the_type(Puppet::Pops::Types::TypeFactory.resource('F', 'a')) + expect(result[1]).to be_the_type(Puppet::Pops::Types::TypeFactory.resource('F', 'b')) + expect(result[2]).to be_the_type(Puppet::Pops::Types::TypeFactory.resource('F', 'c')) + end + it 'checks error conditions' do x = Puppet::Pops::Types::TypeFactory.host_class('a') expect{x['b']}.to raise_error(/Cannot create new Class references from a specific Class reference/) From 43c7b3e4d9b175335e1a69e3b0300440366a54dd Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 8 Sep 2013 00:51:07 +0200 Subject: [PATCH 014/800] (#22454) Fix partial shadowing of match variables. Scope only performed partial shadowing of match variables. This was caused by a search of missing numeric variables in outer scopes. This commit changes that by making the most nested match scope authoritative wrt numerical variables. It also changes the scope to use individually stored variables to simply looking them up in the match data that is used to construct the match scope. The scope_spec is changed to test for this. Some other tests changes as a consequence of the internal refactor. --- lib/puppet/parser/scope.rb | 36 ++++++++++++++++++++++++++-------- spec/unit/parser/scope_spec.rb | 29 ++++++++++++++++++++------- 2 files changed, 50 insertions(+), 15 deletions(-) diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb index 86d7f5244..999f053f4 100644 --- a/lib/puppet/parser/scope.rb +++ b/lib/puppet/parser/scope.rb @@ -91,6 +91,26 @@ class Puppet::Parser::Scope end end + class MatchScope < Ephemeral + + def initialize(parent, match_data) + super(parent, false) + @match_data = match_data + end + + def [](name) + if bound?(name) + @match_data[name.to_i] + else + super + end + end + + def bound?(name) + # A "match variables" scope reports all numeric variables to be bound + name =~ /^\d+$/ + end + end # Initialize a new scope suitable for parser function testing. This method # should be considered a public API for external modules. A shared spec @@ -132,12 +152,12 @@ class Puppet::Parser::Scope # Returns true if the variable of the given name is set to any value (including nil) # def exist?(name) - effective.symtable(true).include?(name) + effective_symtable(true).include?(name) end # Returns true if the given name is bound in the current (most nested) scope. def bound?(name) - effective.symtable(true).bound?(name) + effective_symtable(true).bound?(name) end # Is the value true? This allows us to control the definition of truth @@ -606,7 +626,7 @@ class Puppet::Parser::Scope @ephemeral.any? {|eph| eph.include?(name) } end - # Checks whether the variable should be processed in the ephemeral scope or not. + # Checks whether the variable should be processed in the ephemeral (non local) scope or not. # All numerical variables are processed in ephemeral scope at all times, and all other # variables when the ephemeral scope is a local scope. # @@ -622,6 +642,10 @@ class Puppet::Parser::Scope @ephemeral.push(Ephemeral.new(@ephemeral.last, local_scope)) end + def new_match_scope(match_data) + @ephemeral.push(MatchScope.new(@ephemeral.last, match_data)) + end + def ephemeral_from(match, file = nil, line = nil) case match when Hash @@ -631,11 +655,7 @@ class Puppet::Parser::Scope else raise(ArgumentError,"Invalid regex match data. Got a #{match.class}") unless match.is_a?(MatchData) # Create a match ephemeral and set values from match data - new_ephemeral false - setvar("0", match[0], :file => file, :line => line, :ephemeral => true) - match.captures.each_with_index do |m,i| - setvar("#{i+1}", m, :file => file, :line => line, :ephemeral => true) - end + new_match_scope(match) end end diff --git a/spec/unit/parser/scope_spec.rb b/spec/unit/parser/scope_spec.rb index 00f807dc9..03e64b83c 100755 --- a/spec/unit/parser/scope_spec.rb +++ b/spec/unit/parser/scope_spec.rb @@ -531,22 +531,37 @@ describe Puppet::Parser::Scope do end it "should set $0 with the full match" do - @scope.expects(:setvar).with { |*arg| arg[0] == "0" and arg[1] == "this is a string" and arg[2][:ephemeral] } - + # This is an internal impl detail test + @scope.expects(:new_match_scope).with { |*arg| arg[0][0] == "this is a string" } @scope.ephemeral_from(@match) end it "should set every capture as ephemeral var" do - @match.stubs(:captures).returns([:capture1,:capture2]) - @scope.expects(:setvar).with { |*arg| arg[0] == "1" and arg[1] == :capture1 and arg[2][:ephemeral] } - @scope.expects(:setvar).with { |*arg| arg[0] == "2" and arg[1] == :capture2 and arg[2][:ephemeral] } + # This is an internal impl detail test + @match.stubs(:[]).with(1).returns(:capture1) + @match.stubs(:[]).with(2).returns(:capture2) + @scope.expects(:new_match_scope).with { |*arg| arg[0][1] == :capture1 && arg[0][2] == :capture2 } @scope.ephemeral_from(@match) end - it "should create a new ephemeral level" do - @scope.expects(:new_ephemeral) + it "should shadow previous match variables" do + # This is an internal impl detail test + @match.stubs(:[]).with(1).returns(:capture1) + @match.stubs(:[]).with(2).returns(:capture2) + + @match2 = stub 'match', :is_a? => true + @match2.stubs(:[]).with(1).returns(:capture2_1) + @match2.stubs(:[]).with(2).returns(nil) @scope.ephemeral_from(@match) + @scope.ephemeral_from(@match2) + @scope.lookupvar('2').should == nil + end + + it "should create a new ephemeral level" do + level_before = @scope.ephemeral_level + @scope.ephemeral_from(@match) + expect(level_before < @scope.ephemeral_level) end end From 2a3d6193a5669c352427b323a4a6177d01c94e81 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 8 Sep 2013 00:52:37 +0200 Subject: [PATCH 015/800] (maint) Remove unused ephemeral?(name) from scope. The method was only used from scope_spec.rb --- lib/puppet/parser/scope.rb | 8 -------- spec/unit/parser/scope_spec.rb | 8 -------- 2 files changed, 16 deletions(-) diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb index 999f053f4..da353b918 100644 --- a/lib/puppet/parser/scope.rb +++ b/lib/puppet/parser/scope.rb @@ -626,14 +626,6 @@ class Puppet::Parser::Scope @ephemeral.any? {|eph| eph.include?(name) } end - # Checks whether the variable should be processed in the ephemeral (non local) scope or not. - # All numerical variables are processed in ephemeral scope at all times, and all other - # variables when the ephemeral scope is a local scope. - # - def ephemeral?(name) - @ephemeral.last.is_local_scope? || name =~ /^\d+$/ - end - def ephemeral_level @ephemeral.size end diff --git a/spec/unit/parser/scope_spec.rb b/spec/unit/parser/scope_spec.rb index 03e64b83c..0865c4c7f 100755 --- a/spec/unit/parser/scope_spec.rb +++ b/spec/unit/parser/scope_spec.rb @@ -420,14 +420,6 @@ describe Puppet::Parser::Scope do }.to raise_error(Puppet::ParseError, /Cannot reassign variable 1/) end - it "should declare ephemeral number only variable names" do - @scope.ephemeral?("0").should be_true - end - - it "should not declare ephemeral other variable names" do - @scope.ephemeral?("abc0").should be_nil - end - describe "with more than one level" do it "should prefer latest ephemeral scopes" do @scope.setvar("0", :earliest, :ephemeral => true) From ff5be26f1850c649a29dfc4618d75ef184dec363 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 9 Sep 2013 16:27:17 +0200 Subject: [PATCH 016/800] (#22363) Add "future evaluator" This commit contains the initial work on a new evaluator. It operates on the output of the future parser. --- lib/puppet/pops.rb | 5 + lib/puppet/pops/evaluator/access_operator.rb | 201 ++++ lib/puppet/pops/evaluator/call_operator.rb | 28 + lib/puppet/pops/evaluator/compare_operator.rb | 158 ++++ lib/puppet/pops/evaluator/evaluator.rb | 16 + lib/puppet/pops/evaluator/evaluator_impl.rb | 867 ++++++++++++++++++ .../pops/evaluator/relationship_operator.rb | 104 +++ lib/puppet/pops/evaluator/runtime3_support.rb | 172 ++++ lib/puppet/pops/types/type_factory.rb | 2 +- lib/puppet/pops/types/types.rb | 30 +- spec/unit/pops/evaluator/access_ops_spec.rb | 210 +++++ .../pops/evaluator/arithmetic_ops_spec.rb | 75 ++ .../pops/evaluator/basic_expressions_spec.rb | 93 ++ .../pops/evaluator/comparison_ops_spec.rb | 255 ++++++ .../pops/evaluator/evaluator_rspec_helper.rb | 66 ++ spec/unit/pops/evaluator/logical_ops_spec.rb | 90 ++ .../evaluator/string_interpolation_spec.rb | 44 + spec/unit/pops/evaluator/variables_spec.rb | 156 ++++ 18 files changed, 2549 insertions(+), 23 deletions(-) create mode 100644 lib/puppet/pops/evaluator/access_operator.rb create mode 100644 lib/puppet/pops/evaluator/call_operator.rb create mode 100644 lib/puppet/pops/evaluator/compare_operator.rb create mode 100644 lib/puppet/pops/evaluator/evaluator.rb create mode 100644 lib/puppet/pops/evaluator/evaluator_impl.rb create mode 100644 lib/puppet/pops/evaluator/relationship_operator.rb create mode 100644 lib/puppet/pops/evaluator/runtime3_support.rb create mode 100644 spec/unit/pops/evaluator/access_ops_spec.rb create mode 100644 spec/unit/pops/evaluator/arithmetic_ops_spec.rb create mode 100644 spec/unit/pops/evaluator/basic_expressions_spec.rb create mode 100644 spec/unit/pops/evaluator/comparison_ops_spec.rb create mode 100644 spec/unit/pops/evaluator/evaluator_rspec_helper.rb create mode 100644 spec/unit/pops/evaluator/logical_ops_spec.rb create mode 100644 spec/unit/pops/evaluator/string_interpolation_spec.rb create mode 100644 spec/unit/pops/evaluator/variables_spec.rb diff --git a/lib/puppet/pops.rb b/lib/puppet/pops.rb index be30a59ee..65d7d11cb 100644 --- a/lib/puppet/pops.rb +++ b/lib/puppet/pops.rb @@ -79,6 +79,11 @@ module Puppet require 'puppet/pops/validation/checker3_1' require 'puppet/pops/validation/validator_factory_3_1' end + + module Evaluator + require 'puppet/pops/evaluator/runtime3_support' + require 'puppet/pops/evaluator/evaluator_impl' + end end require 'puppet/bindings' diff --git a/lib/puppet/pops/evaluator/access_operator.rb b/lib/puppet/pops/evaluator/access_operator.rb new file mode 100644 index 000000000..ad46726f1 --- /dev/null +++ b/lib/puppet/pops/evaluator/access_operator.rb @@ -0,0 +1,201 @@ +# AccessOperator handles operator [] +# This operator is part of evaluation. +# +class Puppet::Pops::Evaluator::AccessOperator + # Provides access to the Puppet 3.x runtime (scope, etc.) + # This separation has been made to make it easier to later migrate the evaluator to an improved runtime. + # + include Puppet::Pops::Evaluator::Runtime3Support + + attr_reader :semantic + + # Initialize with AccessExpression to enable reporting issues + # @param access_expression [Puppet::Pops::Model::AccessExpression] the semantic object being evaluated + # @return [void] + # + def initialize(access_expression) + @@access_visitor ||= Puppet::Pops::Visitor.new(self, "access", 2, nil) + @semantic = access_expression + end + + def access (o, scope, *keys) + @@access_visitor.visit_this(self, o, scope, keys) + end + + protected + + def access_Object(o, scope, keys) + # TODO: Use Issues for better handling + fail("The [] operator is not applicable to the result of the LHS expression: #{o.class}", semantic.left_expr, scope) + end + + def access_String(o, scope, keys) + case keys.size + when 0 + fail("String supports [] with one or two arguments. Got #{keys.size}", @semantic.left_expr, scope) + when 1 + o[box_numeric(keys[0], @semantic.keys, scope)] + when 2 + o[box_numeric(keys[0], @semantic.keys, scope), box_numeric(keys[1], @semantic.keys, scope)] + else + fail("String supports [] with one or two arguments. Got #{keys.size}", @semantic.left_expr, scope) + end + end + + # Speciaizes the Pattern p into itself p[], one regexp instance p[], or array of regexp instances + # p[, ]. + # + def access_PPatternType(o, scope, keys) + if keys.size == 0 + return Marshal.load(Marshal.dump(o)) + end + result = keys.collect {|p| Regexp.new(keys[0]) } + result.size == 1 ? result.pop : result + end + + # Evaluates [] with 1 or 2 arguments. One argument is an index lookup, two arguments is a slice from/to. + # + def access_Array(o, scope, keys) + case keys.size + when 0 + # What does this mean: [] ? Is it error, unit, empty array + fail("Array supports [] with one or two arguments. Got #{keys.size}", @semantic.left_expr, scope) + when 1 + # TODO: Objects passed in case of failure are not ideal, it gives all the keys in the model, not the + # failing key (needs to pass index as well) + o[box_numeric(keys[0], @semantic.keys, scope)] + when 2 + # A slice [from, to] with support for -1 to mean start, or end respectively. + # TODO: Objects passed in case of failure are not ideal, it gives all the keys in the model, not the + # failing key (needs to pass index as well) + o[box_numeric(keys[0], @semantic.keys, scope), box_numeric(keys[1], @semantic.keys, scope)] + else + # TODO: Use Issues for better handling + fail("Array supports [] with one or two arguments. Got #{keys.size}", semantic.left_expr, scope) + end + end + + # Evaluates [] with support for one or more arguments. If more than one argument is used, the result + # is an array with each lookup. + # + def access_Hash(o, scope, keys) + result = keys.collect {|k| o[k] } + case result.size + when 0 + # TODO: Use Issues for better handling + fail("Hash supports [] with one or more arguments. Got #{keys.size}", semantic.left_expr, scope) + when 1 + result.pop + else + # remove nil elements and return + result.compact! + result + end + end + + # A Hash can create a new Hash type, one arg sets value type, two args sets key and value type in new type + # It is not possible to create a collection of Hash types. + # + def access_PHashType(o, scope, keys) + keys.each do |k| + unless k.is_a?(Puppet::Pops::Types::PAbstractType) + fail("Arguments to Hash[] must be types. Got #{k.class}", @semantic.keys, scope) + end + end + case keys.size + when 0 + Marshal.load(Marshal.dump(o)) # Deep copy + when 1 + result = Puppet::Pops::Types::PHashType.new() + result.key_type = Marshal.load(Marshal.dump(o.key_type)) + result.element_type = keys[0] + result + when 2 + result = Puppet::Pops::Types::PHashType.new() + result.key_type = keys[0] + result.element_type = keys[1] + result + else + # TODO: Use Issues for better handling + fail("Hash type only accepts one or two type parameters (key, and value type). Got #{keys.size}", @semantic.keys, scope) + end + end + + # An Array can create a new Array type. It is not possible to create a collection of Array types. + # + def access_PArrayType(o, scope, keys) + case keys.size + when 0 + Marshal.load(Marshal.dump(o)) # Deep copy + when 1 + unless keys[0].is_a?(Puppet::Pops::Types::PAbstractType) + fail("Argument to Array[] must be a type. Got #{keys[0].class}", @semantic.keys, scope) + end + result = Puppet::Pops::Types::PArrayType.new() + result.element_type = keys[0] + result + else + # TODO: Use Issues for better handling + fail("Array type only accepts one type parameter (element type). Got #{keys.size}", semantic.right_expr, scope) + end + end + + # A Resource can create a new more specific Resource type, and/or an array of resource types + # If the given type has title set, it can not be specified further. + # @example + # Resource[File] # => File + # Resource[File, 'foo'] # => File[foo] + # Resource[File. 'foo', 'bar] # => [File[foo], File[bar]] + # File['foo', 'bar'] # => [File[foo], File[bar]] + # File['foo']['bar'] # => ERROR + # Resource[File]['foo', 'bar'] # => [File[Foo], File[bar]] + # Resource[File, 'foo', 'bar'] # => [File[foo], File[bar]] + # Resource[???][] # => deep copy of the type + # + def access_PResourceType(o, scope, keys) + if keys.size == 0 + return Marshal.load(Marshal.dump(o)) # Deep copy + end + unless o.title.nil? + # TODO: Use Issues for better handling + fail("Cannot specialize an already specialized resource type", semantic.left_expr, scope) + end + + # type_name is LHS type_name if set, else the first given arg + type_name = o.type_name || keys.shift + type_name = case type_name + when Puppet::Pops::Types::PResourceType + type_name.type_name + when String + type_name.downcase + else + fail("First argument to Resource[] must be a resource type or a string. Got #[type_name.class}") + end + keys = [nil] if keys.size < 1 # if there was only a type_name and it was consumed + result = keys.collect do |t| + rtype = Puppet::Pops::Types::PResourceType.new() + rtype.type_name = type_name + rtype.title = t + rtype + end + # returns single type as type, else an array of types + result.size == 1 ? result.pop : result + end + + def access_PHostClassType(o, scope, keys) + if keys.size == 0 + return Marshal.load(Marshal.dump(o)) # Deep copy + end + unless o.class_name.nil? + # TODO: Use Issues for better handling + fail("Cannot specialize an already specialized Class type", semantic.left_expr, scope) + end + result = keys.collect do |c| + ctype = Puppet::Pops::Types::PHostClassType.new() + ctype.class_name = c + ctype + end + # returns single type as type, else an array of types + result.size == 1 ? result.pop : result + end +end diff --git a/lib/puppet/pops/evaluator/call_operator.rb b/lib/puppet/pops/evaluator/call_operator.rb new file mode 100644 index 000000000..50f12fba4 --- /dev/null +++ b/lib/puppet/pops/evaluator/call_operator.rb @@ -0,0 +1,28 @@ +# CallOperator +# Call operator is part of evaluation +# +class Puppet::Pops::Evaluator::CallOperator + # Provides access to the Puppet 3.x runtime (scope, etc.) + # This separation has been made to make it easier to later migrate the evaluator to an improved runtime. + # + include Puppet::Pops::Evaluator::Runtime3Support + + attr_reader :eval_visitor + def initialize + @call_visitor = Puppet::Pops::Visitor.new(self, "call", 2, 2) + end + + def call (o, scope, *params, &block) + x = @call_visitor.visit(o, scope, params) + if block_given? + block.call(x) + else + x + end + end + + protected + + def call_FunctionExpression o, scope, params + end +end diff --git a/lib/puppet/pops/evaluator/compare_operator.rb b/lib/puppet/pops/evaluator/compare_operator.rb new file mode 100644 index 000000000..e930d46e5 --- /dev/null +++ b/lib/puppet/pops/evaluator/compare_operator.rb @@ -0,0 +1,158 @@ +# Compares the puppet DSL way +# +# ==Equality +# All string vs. numeric equalities check for numeric equality first, then string equality +# Arrays are equal to arrays if they have the same length, and each element #equals +# Hashes are equal to hashes if they have the same size and keys and values #equals. +# All other objects are equal if they are ruby #== equal +# +class Puppet::Pops::Evaluator::CompareOperator + include Puppet::Pops::Utils + + def initialize + @@equals_visitor = Puppet::Pops::Visitor.new(self, "equals", 1, 1) + @@compare_visitor = Puppet::Pops::Visitor.new(self, "cmp", 1, 1) + @@include_visitor = Puppet::Pops::Visitor.new(self, "include", 1, 1) + @type_calculator = Puppet::Pops::Types::TypeCalculator.new() + end + + def equals (a, b) + @@equals_visitor.visit_this(self, a, b) + end + + # Performs a comparison of a and b, and return > 0 if a is bigger, 0 if equal, and < 0 if b is bigger. + # Comparison of String vs. Numeric always compares using numeric. + def compare(a, b) + @@compare_visitor.visit_this(self, a, b) + end + + # Answers is b included in a + def include?(a, b) + @@include_visitor.visit_this(self, a, b) + end + + protected + + def cmp_String(a, b) + # if both are numerics in string form, compare as number + n1 = Puppet::Pops::Utils.to_n(a) + n2 = Puppet::Pops::Utils.to_n(b) + + # Numeric is always lexically smaller than a string, even if the string is empty. + return n1 <=> n2 if n1 && n2 + return -1 if n1 && b.is_a?(String) + return 1 if n2 + return a.casecmp(b) if b.is_a?(String) + + raise ArgumentError.new("A String is not comparable to a non String or Number") + end + + # Equality is case independent. + def equals_String(a, b) + if n1 = Puppet::Pops::Utils.to_n(a) + if n2 = Puppet::Pops::Utils.to_n(b) + n1 == n2 + else + false + end + else + return false unless b.is_a?(String) + a.casecmp(b) == 0 + end + end + + def cmp_Numeric(a, b) + if n2 = Puppet::Pops::Utils.to_n(b) + a <=> n2 + elsif b.kind_of(String) + # Numeric is always lexiographically smaller than a string, even if the string is empty. + -1 + else + raise ArgumentError.new("A Numeric is not comparable to non Numeric or String") + end + end + + def equals_Numeric(a, b) + if n2 = Puppet::Pops::Utils.to_n(b) + a == n2 + else + false + end + end + + def equals_Array(a, b) + return false unless b.is_a?(Array) && a.size == b.size + a.each_index {|i| return false unless equals(a.slice(i), b.slice(i)) } + true + end + + def equals_Hash(a, b) + return false unless b.is_a?(Hash) && a.size == b.size + a.each {|ak, av| return false unless equals(b[ak], av)} + true + end + + def cmp_Symbol(a, b) + if b.is_a?(Symbol) + a <=> b + else + raise ArgumentError.new("Symbol not comparable to non Symbol") + end + end + + def cmp_Object(a, b) + raise ArgumentError.new("Only Strings and Numbers are comparable") + end + + + def equals_Object(a, b) + a == b + end + + def include_Object(a, b) + false + end + + def include_String(a, b) + case b + when String + # subsstring search downcased + a.downcase.include?(b.downcase) + when Regexp + # match (convert to boolean) + !!(a =~ b) + when Number + # convert string to number, true if == + equals(a, b) + when Puppet::Pops::Types::PStringType + # is there a string in a string? (yes, each char is a string, and an empty string contains an empty string) + true + when Puppet::Pops::Types::PDataType + # is there data in a string?, (yes, a substring is data) + true + else + false + end + end + + def include_Array(a, b) + case b + when Regexp + a.each do |element| + next unless element.is_a? String + return true if element =~ b + end + return false + when Puppet::Pops::Types::PAbstractType + a.each {|element| return true if @type_calculator.instance?(b, element) } + return false + else + a.each {|element| return true if equals(element, b) } + return false + end + end + + def include_Hash(a, b) + include?(a.keys, b) + end +end diff --git a/lib/puppet/pops/evaluator/evaluator.rb b/lib/puppet/pops/evaluator/evaluator.rb new file mode 100644 index 000000000..4fe56cb10 --- /dev/null +++ b/lib/puppet/pops/evaluator/evaluator.rb @@ -0,0 +1,16 @@ +# An evaluator evaluates a given object in the given Puppet::Pops::API::Scope scope. +# @abstract +# +class Puppet::Pops::Evaluator + # Evaluates the given object o in the given scope, optionally passing a block which will be + # called with the result of the evaluation. + # @abstract + # @param o [Object] the object to evaluate + # @param scope [Object] the runtime specific scope to evaluate in + # @yieldparam r {Object] the result of the evaluation + # @return [Object] the result of the evaluation, or the result of evaluating the optional block + # + def evaluate(o, scope, &block) + raise NotImplementedError, "A concrete Evaluator implementation should have implemented this method." + end +end diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb new file mode 100644 index 000000000..b368cdb73 --- /dev/null +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -0,0 +1,867 @@ +require 'rgen/ecore/ecore' +require 'puppet/pops/evaluator/compare_operator' +require 'puppet/pops/evaluator/call_operator' +require 'puppet/pops/evaluator/relationship_operator' +require 'puppet/pops/evaluator/access_operator' + +# This implementation of {Puppet::Pops::Evaluator} performs evaluation using the puppet 3.x runtime system +# in a manner largely compatible with Puppet 3.x, but adds new features and introduces constraints. +# +# The evaluation uses _polymorphic dispatch_ which works by dispatching to the first found method named after +# the class or one of its super-classes. The EvaluatorImpl itself mainly deals with evaluation (it currently +# also handles assignment), and it uses a delegation pattern to more specialized handlers of some operators +# that in turn use polymorphic dispatch; this to not clutter EvaluatorImpl with too much responsibility). +# +# Since a pattern is used, only the main entry points are fully documented. The parameters _o_ and _scope_ are +# the same in all the polymorphic methods, (the type of the parameter _o_ is reflected in the method's name; +# either the actual class, or one of its super classes). The _scope_ parameter is always the scope in which +# the evaluation takes place. If nothing else is mentioned, the return is always the result of evaluation. +# +# See {Puppet::Pops::Visitable} and {Puppet::Pops::Visitor} for more information about +# polymorphic calling. +# @api private +# +class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator + include Puppet::Pops::Utils + + # Provides access to the Puppet 3.x runtime (scope, etc.) + # This separation has been made to make it easier to later migrate the evaluator to an improved runtime. + # + include Puppet::Pops::Evaluator::Runtime3Support + + def initialize + # TODO: Change these to class variables to get caching. + @@eval_visitor ||= Puppet::Pops::Visitor.new(self, "eval", 1, 1) + @@assign_visitor ||= Puppet::Pops::Visitor.new(self, "assign", 3, 3) + + @@type_calculator ||= Puppet::Pops::Types::TypeCalculator.new() + @@type_parser ||= Puppet::Pops::Types::TypeParser.new() + + @@compare_operator ||= Puppet::Pops::Evaluator::CompareOperator.new() + @@call_operator ||= Puppet::Pops::Evaluator::CallOperator.new() + @@relationship_operator ||= Puppet::Pops::Evaluator::RelationshipOperator.new() + end + + # Polymorphic evaluate - calls eval_TYPE + # + # ## Polymorphic evaluate + # Polymorphic evaluate calls a method on the format eval_TYPE where classname is the last + # part of the class of the given _target_. A search is performed starting with the actual class, continuing + # with each of the _target_ class's super classes until a matching method is found. + # + # # Description + # Evaluates the given _target_ object in the given scope, optionally passing a block which will be + # called with the result of the evaluation. + # + # @overload evaluate(target, scope, {|result| block}) + # @param target [Object] evaluation target - see methods on the pattern assign_TYPE for actual supported types. + # @param scope [Object] the runtime specific scope class where evaluation should take place + # @return [Object] the result of the evaluation + # @yieldparam [Object] the result of the evaluation of target + # @yieldreturn [Object] the result of evaluating the optional block + # + # @api + # + def evaluate(target, scope, &block) + x = @@eval_visitor.visit_this(self, target, scope) + if block_given? + block.call(x) + else + x + end + end + + # Polymorphic assign - calls assign_TYPE. + # + # # Polymorphic assign + # Polymorphic assign calls a method on the format assign_TYPE where TYPE is the last + # part of the class of the given _target_. A search is performed starting with the actual class, continuing + # with each of the _target_ class's super classes until a matching method is found. + # + # # Description + # Assigns the given _value_ to the given _target_. The additional argument _o_ is the instruction that + # produced the target/value tuple and it is used to set the origin of the result. + # @param target [Object] assignment target - see methods on the pattern assign_TYPE for actual supported types. + # @param value [Object] the value to assign to `target` + # @param o [Puppet::Pops::Model::PopsObject] originating instruction + # @param scope [Object] the runtime specific scope where evaluation should take place + + def assign(target, value, o, scope) + @@assign_visitor.visit_this(self, target, value, o, scope) + end + + protected + + # Assign value to named variable. + # The '$' sign is never part of the name. + # @example In Puppet DSL + # $name = value + # @param name [String] name of variable without $ + # @param value [Object] value to assign to the varible + # @param o [Puppet::Pops::Model::PopsObject] originating instruction + # @param scope [Object] the runtime specific scope where evaluation should take place + # @return [value] + # + def assign_String(name, value, o, scope) + set_variable(name, value, o, scope) + value + end + + # Assign values to named variables in an array. + # The '$' sign is never part of the name. + # @example Puppet DSL + # # all set to 10 + # [a, b, c] = 10 + # + # # set from matching entry in hash + # [a, b, c] = {a => 10, b => 20, c => 30} + # + # # set in sequence from array + # [a, b, c] = [1,2,3] + # + # @param names [Array] array of variable names without $ + # @param value [Object] value to assign to each variable + # @param o [Puppet::Pops::Model::PopsObject] originating instruction + # @param scope [Object] the runtime specific scope where evaluation should take place + # @return [value] + # + def assign_Array(names, value, o, scope) + case value + when Array + names.zip(value).each {|x| set_variable(x, value, o, scope) } + when Hash + names.each {|x| set_variable(x, value[x], o, scope) } + else + names.each {|x| set_variable(x, value, o, scope) } + end + value + end + + # Catches all illegal assignment (e.g. 1 = 2, {'a'=>1} = 2, etc) + # + def assign_Object(name, value, o, scope) + fail("An object of type #{o.class} is not assignable", o, scope) + end + + # Evaluates any object not evaluated to something else to itself. + def eval_Object o, scope + o + end + + # Allows nil to be used as a Nop. + # Evaluates to nil + # TODO: What is the difference between literal undef, nil, and nop? + # + def eval_NilClass(o, scope) + nil + end + + # Evaluates Nop to nil. + # TODO: or is this the same as :undef + # TODO: is this even needed as a separate instruction when there is a literal undef? + def eval_Nop(o, scope) + nil + end + + # Captures all LiteralValues not handled elsewhere. + #-- + # QualifiedName < LiteralValue end + def eval_LiteralValue(o, scope) + o.value + end + + def eval_LiteralDefault(o, scope) + :default + end + + def eval_LiteralUndef(o, scope) + :undef # TODO: or just use nil for this? + end + + # A QualifiedReference (i.e. a capitalized qualified name such as Foo, or Foo::Bar) evaluates to a PType + # + def eval_QualifiedReference(o, scope) + @@type_parser.interpret(o) + end + + def eval_NotExpression(o, scope) + ! is_true?(evaluate(o.expr, scope)) + end + + def eval_UnaryMinusExpression(o, scope) + - box_numeric(evaluate(o.expr, scope), o, scope) + end + + # Abstract evaluation, returns array [left, right] with the evaluated result of left_expr and + # right_expr + # @return > array with result of evaluating left and right expressions + # + def eval_BinaryExpression o, scope + [ evaluate(o.left_expr, scope), evaluate(o.right_expr, scope) ] + end + + # Evaluates assignment with operators =, +=, -= and []= + # + # @example Puppet DSL + # $a = 1 + # $a += 1 + # @todo support for -= ('without' to remove from array) not yet implemented + # + def eval_AssignmentExpression(o, scope) + name, value = eval_BinaryExpression(o, scope) + + case o.operator + when :'=' # normal assignment + assign(name, value, o, scope) + + when :'+=' + # Typecheck RHS + case value + when String + when Array + when Hash + else + # Note, Numeric is illegal since it is impossible to determine radix, better to fail and let user enter the string, or + # use numeric to string conversion with formatting. + fail("Illegal += right hand side type. Cannot append an instance of #{value.class}", o, scope) + end + # if value does not exist, return RHS (note that type check has already been made so correct type is ensured) + if !variable_exists?(scope, name) + return value + end + begin + # Delegate to concatenate function to deal with check of LHS, and perform concatenation + assign(name, concatenate(get_variable_value(scope, name), value), o, scope) + rescue ArgumentError => e + fail("Append assignment += failed with error: #{e.message}", o, scope) + end + + # TODO: Add concrete syntax for this in lexer and parser + # + when :'-=' + # If an attempt is made to delete values from something that does not exists, the value is :undef (it is guaranteed to not + # include any values the user wants deleted anyway :-) + # + if !variable_exists?(scope, name) + return nil + end + begin + # Delegate to delete function to deal with check of LHS, and perform deletion + assign(name, delete(get_variable_value(scope, name), value), o, scope) + rescue ArgumentError => e + fail("Without assignment -= failed with error: #{e.message}", o, scope) + end + else + fail("Unknown assignment operator: '#{o.operator}'.", o, scope) + end + value + end + + ARITHMETIC_OPERATORS = [:'+', :'-', :'*', :'/', :'%', :'<<', :'>>'] + COLLECTION_OPERATORS = [:'+', :'-', :'<<'] + + # Handles binary expression where lhs and rhs are array/hash or numeric and operator is +, - , *, % / << >> + # + def eval_ArithmeticExpression(o, scope) + unless ARITHMETIC_OPERATORS.include?(o.operator) + fail("Unknown arithmetic operator #{o.operator}", o, scope) + end + left, right = eval_BinaryExpression(o, scope) + + if (left.is_a?(Array) || left.is_a?(Hash)) && COLLECTION_OPERATORS.include?(o.operator) + # Handle operation on collections + case o.operator + when :'+' + concatenate(left, right) + when :'-' + delete(left, right) + when :'<<' + unless left.is_a?(Array) + # TODO: when improving fail, pass o.left_expr + fail("Left operand in '<<' expression is not an Array", o, scope) + end + left + [right] + end + else + # Handle operation on numeric + left = box_numeric(left, o, scope) + right = box_numeric(right, o, scope) + begin + result = left.send(o.operator, right) + rescue NoMethodError => e + fail("Operator #{o.operator} not applicable to a value of type #{left.class}", o, scope) + end + # puts "Arithmetic returns '#{left}' #{o.operator} '#{right}' => #{result}" + # puts "left = #{o.left_expr.class}, #{o.left_expr.value}" + # puts "right = #{o.right_expr.class}, #{o.right_expr.value}" + result + end + end + + # Evaluates Puppet DSL ->, ~>, <-, and <~ + def eval_RelationshipExpression(o, scope) + # First level evaluation, reduction to basic data types or puppet types, the relationship operator then translates this + # to the final set of references (turning strings into references, which can not naturally be done by the main evaluator since + # all strings should not be turned into references. + # + real = eval_BinaryExpression(o, scope) + @@relationship_operator.evaluate(real, o, scope) + end + + # Evaluates x[key, key, ...] + # + def eval_AccessExpression(o, scope) + left = evaluate(o.left_expr, scope) + keys = o.keys.nil? ? [] : o.keys.collect {|key| evaluate(key, scope) } + Puppet::Pops::Evaluator::AccessOperator.new(o).access(left, scope, *keys) + end + + # Evaluates <, <=, >, >=, and == + # + def eval_ComparisonExpression o, scope + left, right = eval_BinaryExpression o, scope + + begin + # Left is a type + if left.is_a?(Puppet::Pops::Types::PAbstractType) + case o.operator + when :'==' + @@compare_operator.equals(left,right) + when :'!=' + ! @@compare_operator.equals(left,right) + when :'<' + # right can be assigned to left, but they are not equal + @@type_calculator.assignable?(left, right) && ! @@compare_operator.equals(left,right) + when :'<=' + # right can be assigned to left + @@type_calculator.assignable?(left, right) + when :'>' + # left can be assigned to right, but they are not equal + @@type_calculator.assignable?(right, left) && ! @@compare_operator.equals(left,right) + when :'>=' + # left can be assigned to right + @@type_calculator.assignable?(right, left) + else + fail("Internal Error: unhandled comparison operator '#{o.operator}'.", o, scope) + end + else + case o.operator + when :'==' + @@compare_operator.equals(left,right) + when :'!=' + ! @@compare_operator.equals(left,right) + when :'<' + @@compare_operator.compare(left,right) < 0 + when :'<=' + @@compare_operator.compare(left,right) <= 0 + when :'>' + @@compare_operator.compare(left,right) > 0 + when :'>=' + @@compare_operator.compare(left,right) >= 0 + else + fail("Internal Error: unhandled comparison operator '#{o.operator}'.", o, scope) + end + end + rescue ArgumentError => e + fail("Comparison of #{left.class} #{o.operator} #{right.class} is not possible. Caused by #{e.message}.", o, scope) + end + end + + # Evaluates matching expressions with string or regexp rhs expression. + # + # @example + # x =~ /abc.*/ + # @example + # x =~ "abc.*/" + # @example + # y = "abc" + # x =~ "${y}.*" + # @return [Boolean] if a match was made or not. Also sets $0..$n to matchdata in current scope. + # + def eval_MatchExpression o, scope + left, pattern = eval_BinaryExpression o, scope + begin + pattern = Regexp.new(pattern) unless pattern.is_a?(Regexp) + rescue StandardError => e + fail "Can not convert right expression to a regular expression. Caused by: '#{e.message}'", o, scope + end + unless left.is_a?(String) + fail("Match expression requires a String as left operand", o.left_expr, scope) + end + + matched = pattern.match(left) # nil, or MatchData + set_match_data(matched, o, scope) # creates or clears ephemeral + + # convert match result to Boolean true, or false + o.operator == :'=~' ? !!matched : !matched + end + + # Evaluates Puppet DSL `in` expression + # + def eval_InExpression o, scope + left, right = eval_BinaryExpression o, scope + @@compare_operator.include?(right, left) + end + + # @example + # $a and $b + # b is only evaluated if a is true + # + def eval_AndExpression o, scope + is_true?(evaluate(o.left_expr, scope)) ? is_true?(evaluate(o.right_expr, scope)) : false + end + + # @example + # a or b + # b is only evaluated if a is false + # + def eval_OrExpression o, scope + is_true?(evaluate(o.left_expr, scope)) ? true : is_true?(evaluate(o.right_expr, scope)) + end + + # Evaluates each entry of the literal list and creates a new Array + # @return [Array] with the evaluated content + # + def eval_LiteralList o, scope + o.values.collect {|expr| evaluate(expr, scope)} + end + + # Evaluates each entry of the literal hash and creates a new Hash. + # @return [Hash] with the evaluated content + # + def eval_LiteralHash o, scope + h = Hash.new + o.entries.each {|entry| h[ evaluate(entry.key, scope)]= evaluate(entry.value, scope)} + h + end + + # Evaluates all statements and produces the last evaluated value + # + def eval_BlockExpression o, scope + r = nil + o.statements.each {|s| r = evaluate(s, scope)} + r + end + + # Performs optimized search over case option values, lazily evaluating each + # until there is a match. If no match is found, the case expression's default expression + # is evaluated (it may be nil or Nop if there is no default, thus producing nil). + # If an option matches, the result of evaluating that option is returned. + # @return [Object, nil] what a matched option returns, or nil if nothing matched. + # + def eval_CaseExpression o, scope + test = evaluate(o.test, scope) + result = nil + the_default = nil + if o.options.find do |co| + # the first case option that matches + if co.values.find do |c| + the_default = co.then_expr if c.is_a? Puppet::Pops::Model::LiteralDefault + is_match?(test, evaluate(c, scope), scope) + end + result = evaluate(co.then_expr, scope) + true # the option was picked + end + end + result # an option was picked, and produced a result + else + evaluate(the_default, scope) # evaluate the default (should be a nop/nil) if there is no default). + end + end + + # @todo not implemented - maybe not needed; this is an abstract class + def eval_QueryExpression o, scope + # TODO: or remove - this is the abstract query + end + + # @todo not implemented + def eval_ExportedQuery o, scope + # TODO + end + + # @todo not implemented + def eval_VirtualQuery o, scope + # TODO + end + + # @todo not implemented + def eval_AttributeOperation o, scope + # TODO + end + + # @todo not implemented + def eval_OptionalAttributeOperation o, scope + # TODO + end + + # @todo not implemented + def eval_CollectExpression o, scope + # TODO + end + + # @todo not implemented + def eval_Parameter o, scope + # TODO + end + + # TODO: + # Definition < Expression (abstract) + # NamedDefinition < Definition (abstract) + # ResourceTypeDefinition < NamedDefinition + + # NodeDefinition < Expression + # HostClassDefinition < NamedDefinition + # CallExpression < Expression + # CallNamedFunctionExpression < CallExpression + # TypeReference < Expression + # InstanceReferences < TypeReference + # ResourceExpression < Expression + # class ResourceBody < ASTObject + # ResourceDefaultsExpression < Expression + # ResourceOverrideExpression < Expression + # NamedAccessExpression < Expression + + # @example + # $x ? { 10 => true, 20 => false, default => 0 } + # + def eval_SelectorExpression o, scope + test = evaluate(o.left_expr, scope) + selected = o.selectors.find {|s| + evaluate(s.matching_expr, scope) {|candidate| + candidate == :default || is_match?(test, candidate, scope) + } + } + if selected + evaluate(selected.value_expr, scope) + else + nil + end + end + + # Evaluates Puppet DSL `if` + def eval_IfExpression o, scope + if is_true?(evaluate(o.test, scope)) + evaluate(o.then_expr, scope) + else + evaluate(o.else_expr, scope) + end + end + + # Evaluates Puppet DSL `unless` + def eval_UnlessExpression o, scope + unless is_true?(evaluate(o.test, scope)) + evaluate(o.then_expr, scope) + else + evaluate(o.else_expr, scope) + end + end + + # Evaluates a variable (getting its value) + # The evaluator is lenient; any expression producing a String is used as a name + # of a variable. + # + def eval_VariableExpression o, scope + # Evaluator is not too fussy about what constitutes a name as long as the result + # is a String and a valid variable name + # + name = evaluate(o.expr, scope) + + # Should be caught by validation, but make this explicit here as well, or mysterious evaluation issues + # may occur. + case name + when String + when Numeric + else + fail "Internal error: a variable name should result in a String when evaluated. Got expression of #{o.expr.class} type.", o, scope + end + # TODO: Check for valid variable name + if variable_exists?(name, scope) + get_variable_value(name, o, scope) + else + # TODO: semantics of undefined variable in scope, this just returns what scope does == value or nil + nil + end + end + + # Evaluates double quoted strings that may contain interpolation + # + def eval_ConcatenatedString o, scope + o.segments.collect {|expr| evaluate(expr, scope).to_s}.join + end + + # Create a metadata object that describes an attribute (an ECore EAttribute). + # Only free-standing metadata is created to the actual attribute in a class (that happens later) + # + # Part of type creation. + # + # @todo possibly support: + # changeable: false (i.e. constants) + # volatile: non having storage allocated (default for derived), if true = some kind of cache + # transient: not serialized + # unsettable: how the value can be reset (to default, or unset state) + # + def eval_CreateAttributeExpression o, scope + # Note: Only some of the validation required takes place here + # There are additional nonsensical invariants; like derived attributes with default values + the_attr = RGen::ECore::EAttribute.new + + evaluator.fail("An attribute name must be a String", o, scope) unless o.name.is_a? String + the_attr.name = o.name + + datatype = datatype_reference(evaluate(o.type, scope), o.name, scope) + evaluator.fail("Invalid data-type expression.", o.type, scope) unless datatype + the_attr.eType = datatype + + min = evaluate(o.min_expr, scope) + max = evaluate(o.max_expr, scope) + min = 0 if !min || min < 0 + max = (min == 0 ? 1 : min) unless max + max = -1 if max == 'unlimited' || max == 'many' || max == '*' || max == 'M' + max = -1 if max < -1 + if max >= 0 && min > max + fail("The max occurrence of an attribute must be bigger than the min occurrence (or be unlimited).", o.max_expr, scope) + end + if(min == 0 && max == 0) + fail("The min and max occurrences of an attribute can not both be 0.", o.max_expr, scope) + end + the_attr.lowerBound = min + the_attr.upperBound = max + + # derived? + the_attr.derived = true if o.derived_expr + + # TODO: possibly support: + # changeable: false (i.e. constants) + # volatile: non having storage allocated (default for derived), if true = cache + # transient: not serialized + # unsettable: how the value can be reset (to default, or unset state) + # + the_attr + end + + # Creates a metadata object describing an Enumerator (An Ecore EEnum) + # This only creates the free standing metadata. It is later used when creating a type. + # + def eval_CreateEnumExpression o, scope + e_enum = RGen::ECore::EEnum.new + e_enum.name = o.name + values = evaluate(o.values, scope) + case values + when Array + # Convert entries, the numerical representation is based on order + values.each_index do |i| + e_literal = RGen::ECore::EEnumLiteral.new + e_literal.literal = values.slice(i).to_s + e_literal.value = i + e_literal.eEnum = e_enum + end + when Hash + begin + # Convert entries, the numerical representation is based on mapping name to value + values.each do |k,v| + e_literal = RGen::ECore::EEnumLiteral.new + e_literal.literal = k.to_s + e_literal.value = v.to_i + e_literal.eEnum = e_enum + end + rescue StandardError => e + fail("The given hash could not be converted to Enum entries. Error: "+e.message, o, scope) + end + else + fail("An enumerator accepts an Array of String values, or a Hash of String to Integer mappings. Got instance of #{values.class}.", o, scope) + end + end + + # Creates a type, and returns a Class implementing this type + # @todo this implementation uses scope to create the type; should use the type creator associated + # with the logic that wants to create a type. + # + def eval_CreateTypeExpression(o, scope) + # The actual type creator is kept in the top scope (it keeps all created types) + # The type_creator calls back to this evaluator to evaluate attributes and enums + # it then creates both the model and a class implementation. + scope.top_scope.type_creator.create_type(o, scope, self) + end + + # If the wrapped expression is a QualifiedName, it is taken as the name of a variable in scope. + # Note that this is different from the 3.x implementation, where an initial qualified name + # is accepted. (e.g. `"---${var + 1}---"` is legal. This implementation requires such concrete + # syntax to be expressed in a model as `(TextExpression (+ (Variable var) 1)` - i.e. moving the decision to + # the parser. + # + # Semantics; the result of an expression is turned into a string, nil is silently transformed to empty + # string. + # @return [String] the interpolated result + # + def eval_TextExpression o, scope + if o.expr.is_a?(Puppet::Pops::Model::QualifiedName) + # TODO: formalize, when scope returns nil, vs error + get_variable_value(o.expr.value, o, scope).to_s + else + evaluate(o.expr, scope).to_s + end + end + + # Produces concatenation / merge of x and y. + # + # When x is an Array, y of type produces: + # + # * Array => concatenation `[1,2], [3,4] => [1,2,3,4]` + # * Hash => concatenation of hash as array `[key, value, key, value, ...]` + # * any other => concatenation of single value + # + # When x is a Hash, y of type produces: + # + # * Array => merge of array interpreted as `[key, value, key, value,...]` + # * Hash => a merge, where entries in `y` overrides + # * any other => error + # + # @note to concatenate an Array, nest the array - i.e. `[1,2], [[2,3]]` + # + # @overload concatenate(ary_x, ary_y) + # @param ary_x [Array] array to concatenate to + # @param ary_y [Array] array to concatenate at end of `ary_x` + # @return [Array] new array with `ary_x` + `ary_y` + # @overload concatenate(ary_x, hsh_y) + # @param ary_x [Array] array to concatenate to + # @param hsh_y [Hash] converted to array form, and concatenated to array + # @return [Array] new array with `ary_x` + `hsh_y` converted to array + # @overload concatenate (ary_x, obj_y) + # @param ary_x [Array] array to concatenate to + # @param obj_y [Object] non array or hash object to add to array + # @return [Array] new array with `ary_x` + `obj_y` added as last entry + # @overload concatenate(hsh_x, ary_y) + # @param hsh_x [Hash] the hash to merge with + # @param ary_y [Array] array interpreted as even numbered sequence of key, value merged with `hsh_x` + # @return [Hash] new hash with `hsh_x` merged with `ary_y` interpreted as hash in array form + # @overload concatenate(hsh_x, hsh_y) + # @param hsh_x [Hash] the hash to merge to + # @param hsh_y [Hash] hash merged with `hsh_x` + # @return [Hash] new hash with `hsh_x` merged with `hsh_y` + # @raise [ArgumentError] when `xxx_x` is neither an Array nor a Hash + # @raise [ArgumentError] when `xxx_x` is a Hash, and `xxx_y` is neither Array nor Hash. + # + def concatenate(x, y) + case x + when Array + y = case y + when Array then y + when Hash then y.to_a + else + [y] + end + x + y # new array with concatenation + when Hash + y = case y + when Hash then y + when Array then Hash[*y] + else + raise ArgumentError.new("Can only append Array or Hash to a Hash") + end + x.merge y # new hash with overwrite + else + raise ArgumentError.new("Can only append to an Array or a Hash.") + end + end + + # Produces the result x \ y (set difference) + # When `x` is an Array, `y` is transformed to an array and then all matching elements removed from x. + # When `x` is a Hash, all contained keys are removed from x as listed in `y` if it is an Array, or all its keys if it is a Hash. + # The difference is returned. The given `x` and `y` are not modified by this operation. + # @raise [ArgumentError] when `x` is neither an Array nor a Hash + # + def delete(x, y) + result = x.dup + case x + when Array + y = case y + when Array then y + when Hash then y.to_a + else + [y] + end + y.each {|e| result.delete(e) } + when Hash + y = case y + when Array then y + when Hash then y.keys + else + [y] + end + y.each {|e| result.delete(e) } + else + raise ArgumentError.new("Can only delete from an Array or Hash.") + end + result + end + + # Implementation of case option matching. + # + # This is the type of matching performed in a case option, using == for every type + # of value except regular expression where a match is performed. + # @todo there are implementation issues left to deal with (see source) + # + def is_match? left, right, scope + # TODO: deal with TypeError + # TODO: match when left is a Number, or something strange + # TODO: solution should be used in MatchExpression + if right.is_a?(Regexp) + matched = right.match(left) + scope.set_match_data(matched) + !!matched + else + left == right + end + end + + # Translates data type name to instance of a data type. + # + # Maps an object to an instance of RGen::ECore::EDataType - i.e. returns + # an enum, `EString`, `EInt`, `EFloat`, or `EBoolean`. The default (if nil) is `EString`. + # If an instance of EEnum is passed, a new Enum datatype is created named after the + # attributes (e.g. fooEnumValues) unless the enum is already named. This named enum can not + # be accessed and reused, but it is of value when debugging that it has a name related to + # the attribute). + # + # @param o [String, RGen::Ecore::EDataType, nil] reference to, or real data type + # @param attribute_name [String] the name of the attribute having data type given by `o` + # @param scope [Object] the runtime specific scope where this instruction is evaluated + # @return [RGen::ECore::EDataType] a datatype for `o` with name `attribute_name`, being one of + # a named enum, `EString`, `EInt`, `EFloat`, or `EBoolean`. The default (if nil) is `EString`. + # + # @todo the scope should not be part of the signature; it is currently used to get to a type creator where + # an enum can be created. It should instead use an adapter to find the type creator associated + # with the actual object (i.e. create the datatype in the same package as it's container). + # This is not known by the scope. + # + def datatype_reference(o, attribute_name, scope) + case o + when RGen::ECore::EEnum + o.ePackage = package + # anonymous enums are named after the attribute + # This is slightly problematic as the names are stored as constants in the + # module, and may thus overwrite a constant (which does not really matter) since + # the constant gets erased anyway by the type creator + # after having been associated with the created object/class. + # + o.name = attribute_name + "EnumValues" unless o.name + scope.top_scope.type_creator.create_enum o + when RGen::ECore::EDataType + # Already resolved to a data type + o + when 'String' + RGen::ECore::EString + when 'Integer' + RGen::ECore::EInt + when 'Float' + RGen::ECore::EFloat + when 'Boolean' + RGen::ECore::EBoolean + when NilClass + # Default, if no expression given + RGen::ECore::EString + else + nil + end + end +end diff --git a/lib/puppet/pops/evaluator/relationship_operator.rb b/lib/puppet/pops/evaluator/relationship_operator.rb new file mode 100644 index 000000000..7f2fdfb8d --- /dev/null +++ b/lib/puppet/pops/evaluator/relationship_operator.rb @@ -0,0 +1,104 @@ +# The RelationshipOperator implements the semantics of the -> <- ~> <~ operators creating relationships or notification +# relationships between the left and right hand side's references to resources. +# +# This is separate class since a second level of evaluation is required that transforms string in left or right hand +# to type references. The task of "making a relationship" is delegated to the "runtime support" class that is included. +# This is done to separate the concerns of the new evaluator from the 3x runtime; messy logic goes into the runtime support +# module. Later when more is cleaned up this can be simplified further. +# +class Puppet::Pops::Evaluator::RelationshipOperator + + # Provides access to the Puppet 3.x runtime (scope, etc.) + # This separation has been made to make it easier to later migrate the evaluator to an improved runtime. + # + include Puppet::Pops::Evaluator::Runtime3Support + + def initialize + @type_transformer_visitor = Puppet::Pops::Visitor.new(self, "transform", 1, 1) + @type_calculator = Puppet::Pops::Types::TypeCalculator.new() + @type_parser = Puppet::Pops::Types::TypeParser.new() + + @catalog_type = Puppet::Pops::Types::TypeFactory.catalog_entry() + end + + def transform(o, scope) + @type_transformer_vistor.visit(o, scope) + end + + # Catch all non transformable objects + # @api private + def transform_Object(o, scope) + fail("Not a valid reference in a relationship", o, scope) + end + + # A string must be a type reference in string format + # @api private + def transform_String(o, scope) + assert_catalog_type(@type_parser.parse(o.value)) + end + + # A qualified name is short hand for a class with this name + # @api private + def transform_QualifiedName(o, scope) + Puppet::Pops::Types::TypeFactory.host_class(o.value) + end + + # Types are what they are, just check the type + # @api private + def transform_PAbstractType(o, scope) + assert_catalog_type(o, scope) + end + + # Asserts (and returns) the type if it is a PCatalogEntryType + # (A PCatalogEntryType is the base class of PHostClassType, and PResourceType). + # + def assert_catalog_type(o, scope) + unless @type_calculator.assignable?(@catalog_type, o) + fail("The reference is not a catalog type", o, scope) + end + # TODO must check if this is an abstract PResourceType (i.e. without a type_name) - which should fail + o + end + + RELATIONSHIP_OPERATORS = [:'->', :'~>', :'<-', :'<~'] + REVERSE_OPERATORS = [:'<-', :'<~'] + RELATION_TYPE = { + :'->' => :relationship, + :'<-' => :relationship, + :'~>' => :subscription, + :'<~' => :subscription + } + + def evaluate (left_right_evaluated, relationship_expression, scope) + # assert operator (should have been validated, but this logic makes assumptions which would + # screw things up royally). Better safe than sorry. + unless RELATIONSHIP_OPERATORS.include?(relationship_expression.operator) + fail("Unknown relationship operator #{relationship_expression.operator}.", relationship_expression, scope) + end + + # Turn each side into an array of types (this also asserts their type) + # (note wrap in array first if value is not already an array) + # + # TODO: Later when objects are Puppet Runtime Objects and know their type, it will be more efficient to check/infer + # the type first since a chained operation then does not have to visit each element again. This is not meaningful now + # since inference needs to visit each object each time, and this is what the transformation does anyway). + # + # real is [left, right], and both the left and right may be a single value or an array. In each case all content + # should be flattened, and then transformed to a type. + # + real = real.collect {|x| [x].flatten.collect {|x| transform(x, scope) }} + + # reverse order if operator is Right to Left + source, target = reverse_operator?(relationship_expression) ? real.reverse : real + + # Add the relationships to the catalog + source.each {|s| target.each {|t| add_relationship(s, t, RELATION_TYPE[relationship_expression.operator]) }} + + # Produce the transformed source RHS (if this is a chain, this does not need to be done again) + real.slice(1) + end + + def reverse_operator?(o) + REVERSE_OPERATORS.include?(o.operator) + end +end diff --git a/lib/puppet/pops/evaluator/runtime3_support.rb b/lib/puppet/pops/evaluator/runtime3_support.rb new file mode 100644 index 000000000..e4804d1cd --- /dev/null +++ b/lib/puppet/pops/evaluator/runtime3_support.rb @@ -0,0 +1,172 @@ +module Puppet::Pops::Evaluator::Runtime3Support + # Fails the evaluation of _o_ in the given scope with the given message + # + # @status may need an extra parameter for error code + # @param message [String] the error message + # @param o [Object] the object for which evaluation failed in some way. Used to determine origin. + # @param scope [Puppet::Parser::Scope] the runtime specific scope in which evaluation failed + # @return [!] this method does not return + # @raise [Puppet::ParseError] an evaluation error initialized from the arguments (TODO: Change to EvaluationError) + # @todo fail evaluation with message, failure evaluating o, in scope + # @todo This just fails with the message, should include a label for the expression + # and any origin set in an adapter for o. Scope could be passed for debugging + # purposes / stackdump + # + def fail(message, o, scope) + raise Puppet::ParseError.new(message) + end + + # Binds the given variable name to the given value in the given scope. + # The reference object `o` is used for origin information. + # @todo yardoc this, and pass on origin + # + def set_variable(name, value, o, scope) + scope.setvar(name, value) + end + + # Returns the value of the variable (nil is returned if variable has no value, or if variable does not exist) + # + def get_variable_value(name, o, scope) + scope.lookupvar(name) + end + + # Returns true if the variable of the given name is set in the given most nested scope. True is returned even if + # variable is bound to nil. + # + def variable_bound?(name, scope) + scope.bound?(name) + end + + # Returns true of the variable is bound to a value or nil, in the scope or it's parent scopes. + # + def variable_exists?(name, scope) + scope.exist?(name) + end + + def set_match_data(match_data, o, scope) + # TODO: Get file, line from semantic o and pass as options to scope since it tracks where these values + # came from. + # NOTE: The 3x scope adds one ephemeral(match) to its internal stack per match that succeeds ! It never + # clears anything. Thus a context that performs many matches will get very deep (there simply is no way to + # clear the match variables without rolling back the ephemeral stack.) + # This implementation does not attempt to fix this, it behaves the same bad way. + unless match_data.nil? + scope.ephemeral_from(match_data) + end + end + + # Adds a relationship between the given `source` and `target` of the given `relationship_type` + # @param source [Puppet:Pops::Types::PCatalogEntryType] the source end of the relationship (from) + # @param target [Puppet:Pops::Types::PCatalogEntryType] the target end of the relationship (to) + # @param relationship_type [:relationship, :subscription] the type of the relationship + # + def add_relationship(source, target, relationship_type, scope) + # The 3x way is to record a Puppet::Parser::Relationship that is evaluated at the end of the compilation. + # This means it is not possible to detect any duplicates at this point (and signal where an attempt is made to + # add a duplicate. There is also no location information to signal the original place in the logic. The user will have + # to go fish. + # The 3.x implementation is based on Strings :-o, so the source and target must be transformed. The resolution is + # done by Catalog#resource(type, title). To do that, it creates a Puppet::Resource since it is responsible for + # translating the name/type/title and create index-keys used by the catalog. The Puppet::Resource has bizarre parsing of + # the type and title (scan for [] that is interpreted as type/title (but it gets it wrong). + # Moreover if the type is "" or "component", the type is Class, and if the type is :main, it is :main, all other cases + # undergo capitalization of name-segments (foo::bar becomes Foo::Bar). (This was earlier done in the reverse by the parser). + # Further, the title undergoes the same munging !!! + # + # That bug infested nest of messy logic needs serious Exorcism! + # + # Unfortunately it is not easy to simply call more intelligent methods at a lower level as the compiler evaluates the recorded + # Relationship object at a much later point, and it is responsible for invoking all the messy logic. + # + # TODO: Revisit the below logic when there is a sane implementation of the catalog, compiler and resource. For now + # concentrate on transforming the type references to what is expected by the wacky logic. + # + # HOWEVER, the Compiler only records the Relationships, and the only method it calls is @relationships.each{|x| x.evaluate(catalog) } + # Which means a smarter Relationship class could do this right. Instead of obtaining the resource from the catalog using + # the borked resource(type, title) which creates a resource for the purpose of looking it up, it needs to instead + # scan the catalog's resources + # + # GAAAH, it is even worse! + # It starts in the parser, which parses "File['foo']" into an AST::ResourceReference with type = File, and title = foo + # This AST is evaluated by looking up the type/title in the scope - causing it to be loaded if it exists, and if not, the given + # type name/title is used. It does not search for resource instances, only classes and types. It returns symbolic information + # [type, [title, title]]. From this, instances of Puppet::Resource are created and returned. These only have type/title information + # filled out. One or an array of resources are returned. + # This set of evaluated (empty reference) Resource instances are then passed to the relationship operator. It creates a + # Puppet::Parser::Relationship giving it a source and a target that are (empty reference) Resource instances. These are then remembered + # until the relationship is evaluated by the compiler (at the end). When evaluation takes place, the (empty reference) Resource instances + # are converted to String (!?! WTF) on the simple format "#{type}[#{title}]", and the catalog is told to find a resource, by giving + # it this string. If it cannot find the resource it fails, else the before/notify parameter is appended with the target. + # The search for the resource being with (you guessed it) again creating an (empty reference) resource from type and title (WTF?!?!). + # The catalog now uses the reference resource to compute a key [r.type, r.title.to_s] and also gets a uniqueness key from the + # resource (This is only a reference type created from title and type). If it cannot find it with the first key, it uses the + # uniqueness key to lookup. + # + # This is probably done to allow a resource type to munge/translate the title in some way (but it is quite unclear from the long + # and convoluted path of evaluation. + # In order to do this in a way that is similar to 3.x two resources are created to be used as keys. + # + # + # TODO: logic that creates a PCatalogEntryType should resolve it to ensure it is loaded (to the best of known_resource_types knowledge). + # If this is not done, the order in which things are done may be different? OTOH, it probably works anyway :-) + # + type, title = catalog_type_to_split_type_title(source) + source_resource = Puppet::Resource.new(type, title) + type, title = catalog_type_to_split_type_title(target) + target_resource = Puppet::Resource.new(type, title) + scope.compiler.add_relationship(Puppet::Parser::Relationship.new(source_resource, target_resource, type)) + end + + # Box value `v` to numeric or fails. + # The given value `v` is converted to Numeric, and if that fails the operation + # calls {#fail}. + # @param v [Object] the value to convert + # @param o [Object] originating instruction + # @param scope [Object] the (runtime specific) scope where evaluation of o takes place + # @return [Numeric] value `v` converted to Numeric. + # + def box_numeric(v, o, scope) + unless n = Puppet::Pops::Utils.to_n(v) + fail("Value '#{v}' can not be converted to Numeric.", o, scope) + end + n + end + + # This is the same type of "truth" as used in the current Puppet DSL. + # + def is_true? o + # Is the value true? This allows us to control the definition of truth + # in one place. + case o + when '' + false + when :undef + false + else + !!o + end + end + + # Utility method for TrueClass || FalseClass + # @param x [Object] the object to test if it is instance of TrueClass or FalseClass + def is_boolean? x + x.is_a?(TrueClass) || x.is_a?(FalseClass) + end + + private + + # Produces an array with [type, title] from a PCatalogEntryType + # Only used to produce the reference resource instances that are used to form a relationship. + # + def catalog_type_to_split_type_title(catalog_type) + case catalog_type + when Puppet::Pops::Types::PHostClassType + return ['Class', catalog_type.class_name] + when Puppet::Pops::Types::PResourceType + return [catalog_type.type_name, catalog_type.title] + else + raise ArgumentError, "Cannot split the type #{catalog_type.class}, it is neither a PHostClassType, nor a PResourceClass." + end + end + +end \ No newline at end of file diff --git a/lib/puppet/pops/types/type_factory.rb b/lib/puppet/pops/types/type_factory.rb index 48bd7d9f2..d5373bc5e 100644 --- a/lib/puppet/pops/types/type_factory.rb +++ b/lib/puppet/pops/types/type_factory.rb @@ -82,7 +82,7 @@ module Puppet::Pops::Types::TypeFactory def self.resource(type_name = nil, title = nil) type = Types::PResourceType.new() type_name = type_name.type_name if type_name.is_a?(Types::PResourceType) - type.type_name = type_name + type.type_name = type_name.downcase unless type_name.nil? type.title = title type end diff --git a/lib/puppet/pops/types/types.rb b/lib/puppet/pops/types/types.rb index 67e13d59d..a6a455246 100644 --- a/lib/puppet/pops/types/types.rb +++ b/lib/puppet/pops/types/types.rb @@ -8,6 +8,10 @@ require 'rgen/metamodel_builder' # be used to answer questions about types. The {Puppet::Pops::Types::TypeFactory} should be used to create an instance # of a type whenever one is needed. # +# The implementation of the Types model contains methods that are required for the type objects to behave as +# expected when comparing them and using them as keys in hashes. (No other logic is, or should be included directly in +# the model's classes). +# # @api public # module Puppet::Pops::Types @@ -90,11 +94,6 @@ module Puppet::Pops::Types # @api public class PPatternType < PLiteralType - module ClassModule - def [](pattern) - Regexp.new(pattern) - end - end end # @api public @@ -119,11 +118,11 @@ module Puppet::Pops::Types class PArrayType < PCollectionType module ClassModule def hash - [self.class, element_type].hash + [self.class, self.element_type].hash end def ==(o) - self.class == o.class && element_type == o.element_type + self.class == o.class && self.element_type == o.element_type end end end @@ -133,11 +132,11 @@ module Puppet::Pops::Types contains_one_uni 'key_type', PAbstractType module ClassModule def hash - [self.class, key_type, element_type].hash + [self.class, key_type, self.element_type].hash end def ==(o) - self.class == o.class && key_type == o.key_type && element_type == o.element_type + self.class == o.class && key_type == o.key_type && self.element_type == o.element_type end end end @@ -175,12 +174,6 @@ module Puppet::Pops::Types def ==(o) self.class == o.class && class_name == o.class_name end - def [](*class_names) - raise ArgumentError, "Cannot create new Class references from a specific Class reference" unless class_name.nil? - return self if class_names.size == 0 - result = class_names.collect {|n| x = self.class.new; x.class_name = n; x} - result.size == 1 ? result.pop : result - end end end @@ -197,13 +190,6 @@ module Puppet::Pops::Types def ==(o) self.class == o.class && type_name == o.type_name && title == o.title end - def [](*titles) - raise ArgumentError, "Cannot create new Resource references from a specific Resource reference" unless title.nil? - return self if titles.size == 0 - raise ArgumentError, "A Resource reference without type name can not produce Resource references." if type_name.nil? - result = titles.collect {|t| x = self.class.new; x.type_name = type_name; x.title = t; x} - result.size == 1 ? result.pop : result - end end end diff --git a/spec/unit/pops/evaluator/access_ops_spec.rb b/spec/unit/pops/evaluator/access_ops_spec.rb new file mode 100644 index 000000000..26526686e --- /dev/null +++ b/spec/unit/pops/evaluator/access_ops_spec.rb @@ -0,0 +1,210 @@ +#! /usr/bin/env ruby +require 'spec_helper' + +require 'puppet/pops' +require 'puppet/pops/evaluator/evaluator_impl' +require 'puppet/pops/types/type_factory' + + +# relative to this spec file (./) does not work as this file is loaded by rspec +require File.join(File.dirname(__FILE__), '/evaluator_rspec_helper') + +describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do + include EvaluatorRspecHelper + + context 'The evaluator when operating on a String' do + it 'can get a single character using a single key index to []' do + expect(evaluate(literal('abc')[1])).to eql('b') + end + + it 'can get the last character using the key -1 in []' do + expect(evaluate(literal('abc')[-1])).to eql('c') + end + + it 'can get a substring by giving two keys' do + expect(evaluate(literal('abcd')[1,2])).to eql('bc') + end + + it 'produces nil for a missing entry' do + expect(evaluate(literal('abc')[100])).to eql(nil) + end + + it 'raises an error if arity is wrong for []' do + expect{evaluate(literal('abc')[])}.to raise_error(/String supports \[\] with one or two arguments\. Got 0/) + expect{evaluate(literal('abc')[1,2,3])}.to raise_error(/String supports \[\] with one or two arguments\. Got 3/) + end + end + + context 'The evaluator when operating on an Array' do + it 'is tested with the correct assumptions' do + expect(literal([1,2,3])[1].current.is_a?(Puppet::Pops::Model::AccessExpression)).to eql(true) + end + + it 'can get an element using a single key index to []' do + expect(evaluate(literal([1,2,3])[1])).to eql(2) + end + + it 'can get the last element using the key -1 in []' do + expect(evaluate(literal([1,2,3])[-1])).to eql(3) + end + + it 'can get a slice of elements using two keys' do + expect(evaluate(literal([1,2,3,4])[1,2])).to eql([2,3]) + end + + it 'produces nil for a missing entry' do + expect(evaluate(literal([1,2,3])[100])).to eql(nil) + end + + it 'raises an error if arity is wrong for []' do + expect{evaluate(literal([1,2,3,4])[])}.to raise_error(/Array supports \[\] with one or two arguments\. Got 0/) + expect{evaluate(literal([1,2,3,4])[1,2,3])}.to raise_error(/Array supports \[\] with one or two arguments\. Got 3/) + end + end + + context 'The evaluator when operating on a Hash' do + it 'can get a single element giving a single key to []' do + expect(evaluate(literal({'a'=>1,'b'=>2,'c'=>3})['b'])).to eql(2) + end + + it 'produces nil for a missing key' do + expect(evaluate(literal({'a'=>1,'b'=>2,'c'=>3})['x'])).to eql(nil) + end + + it 'can get multiple elements by giving multiple keys to []' do + expect(evaluate(literal({'a'=>1,'b'=>2,'c'=>3, 'd'=>4})['b', 'd'])).to eql([2, 4]) + end + + it 'compacts the result when using multiple keys' do + expect(evaluate(literal({'a'=>1,'b'=>2,'c'=>3, 'd'=>4})['b', 'x'])).to eql([2]) + end + + it 'produces an empty array if none of multiple given keys were missing' do + expect(evaluate(literal({'a'=>1,'b'=>2,'c'=>3, 'd'=>4})['x', 'y'])).to eql([]) + end + + it 'raises an error if arity is wrong for []' do + expect{evaluate(literal({'a'=>1,'b'=>2,'c'=>3})[])}.to raise_error(/Hash supports \[\] with one or more arguments\. Got 0/) + end + end + + context "When applied to a type it" do + let(:types) { Puppet::Pops::Types::TypeFactory } + + # Hash + # + it 'produces a Hash[Literal,String] from the expression Hash[String]' do + expr = fqr('Hash')[fqr('String')] + expect(evaluate(expr)).to be_the_type(types.hash_of(types.string, types.literal)) + end + + it 'produces a Hash[String,String] from the expression Hash[String, String]' do + expr = fqr('Hash')[fqr('String'), fqr('String')] + expect(evaluate(expr)).to be_the_type(types.hash_of(types.string, types.string)) + end + + it 'produces a Hash[Literal,String] from the expression Hash[Integer][String]' do + expr = fqr('Hash')[fqr('Integer')][fqr('String')] + expect(evaluate(expr)).to be_the_type(types.hash_of(types.string, types.literal)) + end + + it "gives an error if parameter is not a type" do + expr = fqr('Hash')['String'] + expect { evaluate(expr)}.to raise_error(/Arguments to Hash\[\] must be types/) + end + + # Array + # + it 'produces an Array[String] from the expression Array[String]' do + expr = fqr('Array')[fqr('String')] + expect(evaluate(expr)).to be_the_type(types.array_of(types.string)) + end + + it 'produces an Array[String] from the expression Array[Integer][String]' do + expr = fqr('Array')[fqr('Integer')][fqr('String')] + expect(evaluate(expr)).to be_the_type(types.array_of(types.string)) + end + + it "gives an error if parameter is not a type" do + expr = fqr('Array')['String'] + expect { evaluate(expr)}.to raise_error(/Argument to Array\[\] must be a type/) + end + + it 'creates a Regexp instance when applied to a Pattern' do + regexp_expr = fqr('Pattern')['foo'] + expect('foo' =~ evaluate(regexp_expr)).to eql(0) + end + + # Class + # + it 'produces a specific class from Class[classname]' do + expr = fqr('Class')[fqn('apache')] + expect(evaluate(expr)).to be_the_type(types.host_class('apache')) + expr = fqr('Class')[literal('apache')] + expect(evaluate(expr)).to be_the_type(types.host_class('apache')) + end + + it 'produces same class if no class name is given' do + expr = fqr('Class')[fqn('apache')][] + expect(evaluate(expr)).to be_the_type(types.host_class('apache')) + end + + it 'produces a collection of classes when multiple class names are given' do + expr = fqr('Class')[fqn('apache'), literal('nginx')] + result = evaluate(expr) + expect(result[0]).to be_the_type(types.host_class('apache')) + expect(result[1]).to be_the_type(types.host_class('nginx')) + end + + it 'gives an error if class is already specialized' do + expr = fqr('Class')[fqn('apache')][fqn('nginx')] + expect {evaluate(expr)}.to raise_error(/Cannot specialize an already specialized Class type/) + end + + # Resource + it 'produces a specific resource type from Resource[type]' do + expr = fqr('Resource')[fqr('File')] + expect(evaluate(expr)).to be_the_type(types.resource('File')) + expr = fqr('Resource')[literal('File')] + expect(evaluate(expr)).to be_the_type(types.resource('File')) + end + + it 'produces a specific resource reference type from File[title]' do + expr = fqr('File')[literal('/tmp/x')] + expect(evaluate(expr)).to be_the_type(types.resource('File', '/tmp/x')) + end + + it 'produces a collection of specific resource references when multiple titles are used' do + # Using a resource type + expr = fqr('File')[literal('x'),literal('y')] + result = evaluate(expr) + expect(result[0]).to be_the_type(types.resource('File', 'x')) + expect(result[1]).to be_the_type(types.resource('File', 'y')) + + # Using generic resource + expr = fqr('Resource')[fqr('File'), literal('x'),literal('y')] + result = evaluate(expr) + expect(result[0]).to be_the_type(types.resource('File', 'x')) + expect(result[1]).to be_the_type(types.resource('File', 'y')) + end + + it 'gives an error if resource is already specialized' do + expr = fqr('File')[fqn('x')][fqn('y')] + expect {evaluate(expr)}.to raise_error(/Cannot specialize an already specialized resource type/) + end + + end + + matcher :be_the_type do |type| + calc = Puppet::Pops::Types::TypeCalculator.new + + match do |actual| + calc.assignable?(actual, type) && calc.assignable?(type, actual) + end + + failure_message_for_should do |actual| + "expected #{calc.string(type)}, but was #{calc.string(actual)}" + end + end + +end diff --git a/spec/unit/pops/evaluator/arithmetic_ops_spec.rb b/spec/unit/pops/evaluator/arithmetic_ops_spec.rb new file mode 100644 index 000000000..d1567f77b --- /dev/null +++ b/spec/unit/pops/evaluator/arithmetic_ops_spec.rb @@ -0,0 +1,75 @@ +#! /usr/bin/env ruby +require 'spec_helper' + +require 'puppet/pops' +require 'puppet/pops/evaluator/evaluator_impl' + + +# relative to this spec file (./) does not work as this file is loaded by rspec +require File.join(File.dirname(__FILE__), '/evaluator_rspec_helper') + +describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do + include EvaluatorRspecHelper + + context "When the evaluator performs arithmetic" do + context "on Integers" do + it "2 + 2 == 4" do; evaluate(literal(2) + literal(2)).should == 4 ; end + it "7 - 3 == 4" do; evaluate(literal(7) - literal(3)).should == 4 ; end + it "6 * 3 == 18" do; evaluate(literal(6) * literal(3)).should == 18; end + it "6 / 3 == 2" do; evaluate(literal(6) / literal(3)).should == 2 ; end + it "6 % 3 == 0" do; evaluate(literal(6) % literal(3)).should == 0 ; end + it "10 % 3 == 1" do; evaluate(literal(10) % literal(3)).should == 1; end + it "-(6/3) == -2" do; evaluate(minus(literal(6) / literal(3))).should == -2 ; end + it "-6/3 == -2" do; evaluate(minus(literal(6)) / literal(3)).should == -2 ; end + it "8 >> 1 == 4" do; evaluate(literal(8) >> literal(1)).should == 4 ; end + it "8 << 1 == 16" do; evaluate(literal(8) << literal(1)).should == 16; end + end + + context "on Floats" do + it "2.2 + 2.2 == 4.4" do; evaluate(literal(2.2) + literal(2.2)).should == 4.4 ; end + it "7.7 - 3.3 == 4.4" do; evaluate(literal(7.7) - literal(3.3)).should == 4.4 ; end + it "6.1 * 3.1 == 18.91" do; evaluate(literal(6.1) * literal(3.1)).should == 18.91; end + it "6.6 / 3.3 == 2.0" do; evaluate(literal(6.6) / literal(3.3)).should == 2.0 ; end + it "6.6 % 3.3 == 0.0" do; evaluate(literal(6.6) % literal(3.3)).should == 0.0 ; end + it "10.0 % 3.0 == 1.0" do; evaluate(literal(10.0) % literal(3.0)).should == 1.0 ; end + it "-(6.0/3.0) == -2.0" do; evaluate(minus(literal(6.0) / literal(3.0))).should == -2.0; end + it "-6.0/3.0 == -2.0" do; evaluate(minus(literal(6.0)) / literal(3.0)).should == -2.0; end + it "3.14 << 2 == error" do; expect { evaluate(literal(3.14) << literal(2))}.to raise_error(Puppet::ParseError); end + it "3.14 >> 2 == error" do; expect { evaluate(literal(3.14) >> literal(2))}.to raise_error(Puppet::ParseError); end + end + + context "on strings requiring boxing to Numeric" do + it "'2' + '2' == 4" do + evaluate(literal('2') + literal('2')).should == 4 + end + + it "'2.2' + '2.2' == 4.4" do + evaluate(literal('2.2') + literal('2.2')).should == 4.4 + end + + it "'0xF7' + '0x8' == 0xFF" do + evaluate(literal('0xF7') + literal('0x8')).should == 0xFF + end + + it "'0367' + '010' == 0xFF" do + evaluate(literal('0367') + literal('010')).should == 0xFF + end + + it "'0888' + '010' == error" do + expect { evaluate(literal('0888') + literal('010'))}.to raise_error(Puppet::ParseError) + end + + it "'0xWTF' + '010' == error" do + expect { evaluate(literal('0xWTF') + literal('010'))}.to raise_error(Puppet::ParseError) + end + + it "'0x12.3' + '010' == error" do + expect { evaluate(literal('0x12.3') + literal('010'))}.to raise_error(Puppet::ParseError) + end + + it "'012.3' + '0.3' == 12.6 (not error, floats can start with 0)" do + evaluate(literal('012.3') + literal('010')) == 12.6 + end + end + end +end diff --git a/spec/unit/pops/evaluator/basic_expressions_spec.rb b/spec/unit/pops/evaluator/basic_expressions_spec.rb new file mode 100644 index 000000000..367128e22 --- /dev/null +++ b/spec/unit/pops/evaluator/basic_expressions_spec.rb @@ -0,0 +1,93 @@ +#! /usr/bin/env ruby +require 'spec_helper' + +require 'puppet/pops' +require 'puppet/pops/evaluator/evaluator_impl' + + +# relative to this spec file (./) does not work as this file is loaded by rspec +require File.join(File.dirname(__FILE__), '/evaluator_rspec_helper') + +describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do + include EvaluatorRspecHelper + + context "When the evaluator evaluates literals" do + it 'should evaluator numbers to numbers' do + evaluate(literal(1)).should == 1 + evaluate(literal(3.14)).should == 3.14 + end + + it 'should evaluate strings to string' do + evaluate(literal('banana')).should == 'banana' + end + + it 'should evaluate booleans to booleans' do + evaluate(literal(false)).should == false + evaluate(literal(true)).should == true + end + + it 'should evaluate names to strings' do + evaluate(fqn('banana')).should == 'banana' + end + + it 'should evaluator types to types' do + array_type = Puppet::Pops::Types::PArrayType.new() + array_type.element_type = Puppet::Pops::Types::PDataType.new() + evaluate(fqr('Array')).should == array_type + end + end + + context "When the evaluator evaluates Lists" do + it "should create an Array when evaluating a LiteralList" do + evaluate(literal([1,2,3])).should == [1,2,3] + end + + it "[...[...[]]] should create nested arrays without trouble" do + evaluate(literal([1,[2.0, 2.1, [2.2]],[3.0, 3.1]])).should == [1,[2.0, 2.1, [2.2]],[3.0, 3.1]] + end + + it "[2 + 2] should evaluate expressions in entries" do + x = literal([literal(2) + literal(2)]); + Puppet::Pops::Model::ModelTreeDumper.new.dump(x).should == "([] (+ 2 2))" + evaluate(x)[0].should == 4 + end + + it "[1,2,3] == [1,2,3] == true" do + evaluate(literal([1,2,3]) == literal([1,2,3])).should == true; + end + + it "[1,2,3] != [2,3,4] == true" do + evaluate(literal([1,2,3]).ne(literal([2,3,4]))).should == true; + end + + it "[1, 2, 3][2] == 3" do + evaluate(literal([1,2,3])[2]).should == 3 + end + end + + context "When the evaluator evaluates Hashes" do + it "should create a Hash when evaluating a LiteralHash" do + evaluate(literal({'a'=>1,'b'=>2})).should == {'a'=>1,'b'=>2} + end + + it "{...{...{}}} should create nested hashes without trouble" do + evaluate(literal({'a'=>1,'b'=>{'x'=>2.1,'y'=>2.2}})).should == {'a'=>1,'b'=>{'x'=>2.1,'y'=>2.2}} + end + + it "{'a'=> 2 + 2} should evaluate values in entries" do + evaluate(literal({'a'=> literal(2) + literal(2)}))['a'].should == 4 + end + + it "{'a'=> 1, 'b'=>2} == {'a'=> 1, 'b'=>2} == true" do + evaluate(literal({'a'=> 1, 'b'=>2}) == literal({'a'=> 1, 'b'=>2})).should == true; + end + + it "{'a'=> 1, 'b'=>2} != {'x'=> 1, 'y'=>3} == true" do + evaluate(literal({'a'=> 1, 'b'=>2}).ne(literal({'x'=> 1, 'y'=>3}))).should == true; + end + + it "{'a' => 1, 'b' => 2}['b'] == 2" do + evaluate(literal({:a => 1, :b => 2})[:b]).should == 2 + end + end +end diff --git a/spec/unit/pops/evaluator/comparison_ops_spec.rb b/spec/unit/pops/evaluator/comparison_ops_spec.rb new file mode 100644 index 000000000..ce6d79efd --- /dev/null +++ b/spec/unit/pops/evaluator/comparison_ops_spec.rb @@ -0,0 +1,255 @@ +#! /usr/bin/env ruby +require 'spec_helper' + +require 'puppet/pops' +require 'puppet/pops/evaluator/evaluator_impl' + + +# relative to this spec file (./) does not work as this file is loaded by rspec +require File.join(File.dirname(__FILE__), '/evaluator_rspec_helper') + +describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do + include EvaluatorRspecHelper + + context "When the evaluator performs comparisons" do + + context "of string values" do + it "'a' == 'a' == true" do; evaluate(literal('a') == literal('a')).should == true ; end + it "'a' == 'b' == false" do; evaluate(literal('a') == literal('b')).should == false ; end + it "'a' != 'a' == false" do; evaluate(literal('a').ne(literal('a'))).should == false ; end + it "'a' != 'b' == true" do; evaluate(literal('a').ne(literal('b'))).should == true ; end + + it "'a' < 'b' == true" do; evaluate(literal('a') < literal('b')).should == true ; end + it "'a' < 'a' == false" do; evaluate(literal('a') < literal('a')).should == false ; end + it "'b' < 'a' == false" do; evaluate(literal('b') < literal('a')).should == false ; end + + it "'a' <= 'b' == true" do; evaluate(literal('a') <= literal('b')).should == true ; end + it "'a' <= 'a' == true" do; evaluate(literal('a') <= literal('a')).should == true ; end + it "'b' <= 'a' == false" do; evaluate(literal('b') <= literal('a')).should == false ; end + + it "'a' > 'b' == false" do; evaluate(literal('a') > literal('b')).should == false ; end + it "'a' > 'a' == false" do; evaluate(literal('a') > literal('a')).should == false ; end + it "'b' > 'a' == true" do; evaluate(literal('b') > literal('a')).should == true ; end + + it "'a' >= 'b' == false" do; evaluate(literal('a') >= literal('b')).should == false ; end + it "'a' >= 'a' == true" do; evaluate(literal('a') >= literal('a')).should == true ; end + it "'b' >= 'a' == true" do; evaluate(literal('b') >= literal('a')).should == true ; end + + context "with mixed case" do + it "'a' == 'A' == true" do; evaluate(literal('a') == literal('A')).should == true ; end + it "'a' != 'A' == false" do; evaluate(literal('a').ne(literal('A'))).should == false ; end + it "'a' > 'A' == false" do; evaluate(literal('a') > literal('A')).should == false ; end + it "'a' >= 'A' == true" do; evaluate(literal('a') >= literal('A')).should == true ; end + it "'A' < 'a' == false" do; evaluate(literal('A') < literal('a')).should == false ; end + it "'A' <= 'a' == true" do; evaluate(literal('A') <= literal('a')).should == true ; end + end + end + + context "of integer values" do + it "1 == 1 == true" do; evaluate(literal(1) == literal(1)).should == true ; end + it "1 == 2 == false" do; evaluate(literal(1) == literal(2)).should == false ; end + it "1 != 1 == false" do; evaluate(literal(1).ne(literal(1))).should == false ; end + it "1 != 2 == true" do; evaluate(literal(1).ne(literal(2))).should == true ; end + + it "1 < 2 == true" do; evaluate(literal(1) < literal(2)).should == true ; end + it "1 < 1 == false" do; evaluate(literal(1) < literal(1)).should == false ; end + it "2 < 1 == false" do; evaluate(literal(2) < literal(1)).should == false ; end + + it "1 <= 2 == true" do; evaluate(literal(1) <= literal(2)).should == true ; end + it "1 <= 1 == true" do; evaluate(literal(1) <= literal(1)).should == true ; end + it "2 <= 1 == false" do; evaluate(literal(2) <= literal(1)).should == false ; end + + it "1 > 2 == false" do; evaluate(literal(1) > literal(2)).should == false ; end + it "1 > 1 == false" do; evaluate(literal(1) > literal(1)).should == false ; end + it "2 > 1 == true" do; evaluate(literal(2) > literal(1)).should == true ; end + + it "1 >= 2 == false" do; evaluate(literal(1) >= literal(2)).should == false ; end + it "1 >= 1 == true" do; evaluate(literal(1) >= literal(1)).should == true ; end + it "2 >= 1 == true" do; evaluate(literal(2) >= literal(1)).should == true ; end + end + + context "of mixed value types" do + it "1 == 1.0 == true" do; evaluate(literal(1) == literal(1.0)).should == true ; end + it "1 < 1.1 == true" do; evaluate(literal(1) < literal(1.1)).should == true ; end + it "'1' < 1.1 == true" do; evaluate(literal('1') < literal(1.1)).should == true ; end + it "1.0 == 1 == true" do; evaluate(literal(1.0) == literal(1)).should == true ; end + it "1.0 < 2 == true" do; evaluate(literal(1.0) < literal(2)).should == true ; end + it "'1.0' < 1.1 == true" do; evaluate(literal('1.0') < literal(1.1)).should == true ; end + + it "'1.0' < 'a' == true" do; evaluate(literal('1.0') < literal('a')).should == true ; end + it "'1.0' < '' == true" do; evaluate(literal('1.0') < literal('')).should == true ; end + it "'1.0' < ' ' == true" do; evaluate(literal('1.0') < literal(' ')).should == true ; end + it "'a' > '1.0' == true" do; evaluate(literal('a') > literal('1.0')).should == true ; end + end + + context "of regular expressions" do + it "/.*/ == /.*/ == true" do; evaluate(literal(/.*/) == literal(/.*/)).should == true ; end + it "/.*/ != /a.*/ == true" do; evaluate(literal(/.*/).ne(literal(/a.*/))).should == true ; end + end + + context "of booleans" do + it "true == true == true" do; evaluate(literal(true) == literal(true)).should == true ; end; + it "false == false == true" do; evaluate(literal(false) == literal(false)).should == true ; end; + it "true == false != true" do; evaluate(literal(true) == literal(false)).should == false ; end; + end + + context "of collections" do + it "[1,2,3] == [1,2,3] == true" do + evaluate(literal([1,2,3]) == literal([1,2,3])).should == true + evaluate(literal([1,2,3]).ne(literal([1,2,3]))).should == false + evaluate(literal([1,2,4]) == literal([1,2,3])).should == false + evaluate(literal([1,2,4]).ne(literal([1,2,3]))).should == true + end + + it "{'a'=>1, 'b'=>2} == {'a'=>1, 'b'=>2} == true" do + evaluate(literal({'a'=>1, 'b'=>2}) == literal({'a'=>1, 'b'=>2})).should == true + evaluate(literal({'a'=>1, 'b'=>2}).ne(literal({'a'=>1, 'b'=>2}))).should == false + evaluate(literal({'a'=>1, 'b'=>2}) == literal({'x'=>1, 'b'=>2})).should == false + evaluate(literal({'a'=>1, 'b'=>2}).ne(literal({'x'=>1, 'b'=>2}))).should == true + end + end + + context "of non comparable types" do + # TODO: Change the exception type + it "false < true == error" do; expect { evaluate(literal(true) < literal(false))}.to raise_error(Puppet::ParseError); end + it "false <= true == error" do; expect { evaluate(literal(true) <= literal(false))}.to raise_error(Puppet::ParseError); end + it "false > true == error" do; expect { evaluate(literal(true) > literal(false))}.to raise_error(Puppet::ParseError); end + it "false >= true == error" do; expect { evaluate(literal(true) >= literal(false))}.to raise_error(Puppet::ParseError); end + + it "/a/ < /b/ == error" do; expect { evaluate(literal(/a/) < literal(/b/))}.to raise_error(Puppet::ParseError); end + it "/a/ <= /b/ == error" do; expect { evaluate(literal(/a/) <= literal(/b/))}.to raise_error(Puppet::ParseError); end + it "/a/ > /b/ == error" do; expect { evaluate(literal(/a/) > literal(/b/))}.to raise_error(Puppet::ParseError); end + it "/a/ >= /b/ == error" do; expect { evaluate(literal(/a/) >= literal(/b/))}.to raise_error(Puppet::ParseError); end + + it "[1,2,3] < [1,2,3] == error" do + expect{ evaluate(literal([1,2,3]) < literal([1,2,3]))}.to raise_error(Puppet::ParseError) + end + + it "[1,2,3] > [1,2,3] == error" do + expect{ evaluate(literal([1,2,3]) > literal([1,2,3]))}.to raise_error(Puppet::ParseError) + end + + it "[1,2,3] >= [1,2,3] == error" do + expect{ evaluate(literal([1,2,3]) >= literal([1,2,3]))}.to raise_error(Puppet::ParseError) + end + + it "[1,2,3] <= [1,2,3] == error" do + expect{ evaluate(literal([1,2,3]) <= literal([1,2,3]))}.to raise_error(Puppet::ParseError) + end + + it "{'a'=>1, 'b'=>2} < {'a'=>1, 'b'=>2} == error" do + expect{ evaluate(literal({'a'=>1, 'b'=>2}) < literal({'a'=>1, 'b'=>2}))}.to raise_error(Puppet::ParseError) + end + + it "{'a'=>1, 'b'=>2} > {'a'=>1, 'b'=>2} == error" do + expect{ evaluate(literal({'a'=>1, 'b'=>2}) > literal({'a'=>1, 'b'=>2}))}.to raise_error(Puppet::ParseError) + end + + it "{'a'=>1, 'b'=>2} <= {'a'=>1, 'b'=>2} == error" do + expect{ evaluate(literal({'a'=>1, 'b'=>2}) <= literal({'a'=>1, 'b'=>2}))}.to raise_error(Puppet::ParseError) + end + + it "{'a'=>1, 'b'=>2} >= {'a'=>1, 'b'=>2} == error" do + expect{ evaluate(literal({'a'=>1, 'b'=>2}) >= literal({'a'=>1, 'b'=>2}))}.to raise_error(Puppet::ParseError) + end + end + end + + context "When the evaluator performs Regular Expression matching" do + it "'a' =~ /.*/ == true" do; evaluate(literal('a') =~ literal(/.*/)).should == true ; end + it "'a' =~ '.*' == true" do; evaluate(literal('a') =~ literal(".*")).should == true ; end + it "'a' !~ /b.*/ == true" do; evaluate(literal('a').mne(literal(/b.*/))).should == true ; end + it "'a' !~ 'b.*' == true" do; evaluate(literal('a').mne(literal("b.*"))).should == true ; end + + it "'a' =~ Pattern['.*'] == true" do + evaluate(literal('a') =~ fqr('Pattern')[literal(".*")]).should == true + end + + it "$a = Pattern['.*']; 'a' =~ $a == true" do + expr = block(fqn('a').set(fqr('Pattern')['foo']), literal('foo') =~ var('a')) + evaluate(expr).should == true + end + + it 'should fail if LHS is not a string' do + expect { evaluate(literal(666) =~ literal(/6/))}.to raise_error(Puppet::ParseError) + end + end + + context "When evaluator evaluates the 'in' operator" do + it "should find elements in an array" do + evaluate(literal(1).in(literal([1,2,3]))).should == true + evaluate(literal(4).in(literal([1,2,3]))).should == false + end + + it "should find keys in a hash" do + evaluate(literal('a').in(literal({'x'=>1, 'a'=>2, 'y'=> 3}))).should == true + evaluate(literal('z').in(literal({'x'=>1, 'a'=>2, 'y'=> 3}))).should == false + end + + it "should find substrings in a string" do + evaluate(literal('ana').in(literal('bananas'))).should == true + evaluate(literal('xxx').in(literal('bananas'))).should == false + end + + it "should find substrings in a string (regexp)" do + evaluate(literal(/ana/).in(literal('bananas'))).should == true + evaluate(literal(/xxx/).in(literal('bananas'))).should == false + end + + it "should find substrings in a string (ignoring case)" do + evaluate(literal('ANA').in(literal('bananas'))).should == true + evaluate(literal('ana').in(literal('BANANAS'))).should == true + evaluate(literal('xxx').in(literal('BANANAS'))).should == false + end + + it "should find sublists in a list" do + evaluate(literal([2,3]).in(literal([1,[2,3],4]))).should == true + evaluate(literal([2,4]).in(literal([1,[2,3],4]))).should == false + end + + it "should find sublists in a list (case insensitive)" do + evaluate(literal(['a','b']).in(literal(['A',['A','B'],'C']))).should == true + evaluate(literal(['x','y']).in(literal(['A',['A','B'],'C']))).should == false + end + + it "should find keys in a hash" do + evaluate(literal('a').in(literal({'a' => 10, 'b' => 20}))).should == true + evaluate(literal('x').in(literal({'a' => 10, 'b' => 20}))).should == false + end + + it "should find keys in a hash (case insensitive)" do + evaluate(literal('A').in(literal({'a' => 10, 'b' => 20}))).should == true + evaluate(literal('X').in(literal({'a' => 10, 'b' => 20}))).should == false + end + + it "should find keys in a hash (regexp)" do + evaluate(literal(/xxx/).in(literal({'abcxxxabc' => 10, 'xyz' => 20}))).should == true + evaluate(literal(/yyy/).in(literal({'abcxxxabc' => 10, 'xyz' => 20}))).should == false + end + + it "should find numbers as numbers" do + evaluate(literal(15).in(literal([1,0xf,2]))).should == true + end + + it "should find numbers as strings" do + evaluate(literal(15).in(literal([1, '0xf',2]))).should == true + evaluate(literal('15').in(literal([1, 0xf,2]))).should == true + end + + it "should not find numbers embedded in strings, nor digits in numbers" do + evaluate(literal(15).in(literal([1, '115', 2]))).should == false + evaluate(literal(1).in(literal([11, 111, 2]))).should == false + evaluate(literal('1').in(literal([11, 111, 2]))).should == false + end + + it 'should find an entry with compatible type in an Array' do + evaluate(fqr('Array')[fqr('Integer')].in(literal(['a', [1,2,3], 'b']))).should == true + evaluate(fqr('Array')[fqr('Integer')].in(literal(['a', [1,2,'not integer'], 'b']))).should == false + end + + it 'should find an entry with compatible type in a Hash' do + evaluate(fqr('Integer').in(literal({1 => 'a', 'a' => 'b'}))).should == true + evaluate(fqr('Integer').in(literal({'a' => 'a', 'a' => 'b'}))).should == false + end + end +end diff --git a/spec/unit/pops/evaluator/evaluator_rspec_helper.rb b/spec/unit/pops/evaluator/evaluator_rspec_helper.rb new file mode 100644 index 000000000..a6cecd74c --- /dev/null +++ b/spec/unit/pops/evaluator/evaluator_rspec_helper.rb @@ -0,0 +1,66 @@ +require 'puppet/pops' +require 'puppet/pops/evaluator/evaluator_impl' + +require File.join(File.dirname(__FILE__), '/../factory_rspec_helper') + +module EvaluatorRspecHelper + include FactoryRspecHelper + + # Evaluate a Factory wrapper round a model object in top scope + named scope + # Optionally pass two or three model objects (typically blocks) to be executed + # in top scope, named scope, and then top scope again. If a named_scope is used, it must + # be preceded by the name of the scope. + # The optional block is executed before the result of the last specified model object + # is evaluated. This block gets the top scope as an argument. The intent is to pass + # a block that asserts the state of the top scope after the operations. + # + def evaluate in_top_scope, scopename="x", in_named_scope = nil, in_top_scope_again = nil, &block + node = Puppet::Node.new('localhost') + compiler = Puppet::Parser::Compiler.new(node) + top_scope = Puppet::Parser::Scope.new(compiler) + + evaluator = Puppet::Pops::Evaluator::EvaluatorImpl.new + result = evaluator.evaluate(in_top_scope.current, top_scope) + if in_named_scope + other_scope = Puppet::Parser::Scope.new(compiler) + result = evaluator.evaluate(in_named_scope.current, other_scope) + end + if in_top_scope_again + result = evaluator.evaluate(in_top_scope_again.current, top_scope) + end + if block_given? + block.call(top_scope) + end + result + end + + # Evaluate a Factory wrapper round a model object in top scope + local scope + # Optionally pass two or three model objects (typically blocks) to be executed + # in top scope, local scope, and then top scope again + # The optional block is executed before the result of the last specified model object + # is evaluated. This block gets the top scope as an argument. The intent is to pass + # a block that asserts the state of the top scope after the operations. + # + def evaluate_l in_top_scope, in_local_scope = nil, in_top_scope_again = nil, &block + node = Puppet::Node.new('localhost') + compiler = Puppet::Parser::Compiler.new(node) + top_scope = Puppet::Parser::Scope.new(compiler) + + evaluator = Puppet::Pops::Evaluator::EvaluatorImpl.new + result = evaluator.evaluate(in_top_scope.current, top_scope) + if in_local_scope + # This is really bad in 3.x scope + elevel = top_scope.ephemeral_level + top_scope.new_ephemeral(true) + result = evaluator.evaluate(in_local_scope.current, top_scope) + top_scope.unset_ephemeral_var(elevel) + end + if in_top_scope_again + result = evaluator.evaluate(in_top_scope_again.current, top_scope) + end + if block_given? + block.call(top_scope) + end + result + end +end diff --git a/spec/unit/pops/evaluator/logical_ops_spec.rb b/spec/unit/pops/evaluator/logical_ops_spec.rb new file mode 100644 index 000000000..e5cdd1f93 --- /dev/null +++ b/spec/unit/pops/evaluator/logical_ops_spec.rb @@ -0,0 +1,90 @@ +#! /usr/bin/env ruby +require 'spec_helper' + +require 'puppet/pops' +require 'puppet/pops/evaluator/evaluator_impl' + + +# relative to this spec file (./) does not work as this file is loaded by rspec +require File.join(File.dirname(__FILE__), '/evaluator_rspec_helper') + +describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do + include EvaluatorRspecHelper + + context "When the evaluator performs boolean operations" do + context "using operator AND" do + it "true && true == true" do + evaluate(literal(true).and(literal(true))).should == true + end + + it "false && true == false" do + evaluate(literal(false).and(literal(true))).should == false + end + + it "true && false == false" do + evaluate(literal(true).and(literal(false))).should == false + end + + it "false && false == false" do + evaluate(literal(false).and(literal(false))).should == false + end + end + + context "using operator OR" do + it "true || true == true" do + evaluate(literal(true).or(literal(true))).should == true + end + + it "false || true == true" do + evaluate(literal(false).or(literal(true))).should == true + end + + it "true || false == true" do + evaluate(literal(true).or(literal(false))).should == true + end + + it "false || false == false" do + evaluate(literal(false).or(literal(false))).should == false + end + end + + context "using operator NOT" do + it "!false == true" do + evaluate(literal(false).not()).should == true + end + + it "!true == false" do + evaluate(literal(true).not()).should == false + end + end + + context "on values requiring boxing to Boolean" do + it "'x' == true" do + evaluate(literal('x').not()).should == false + end + + it "'' == false" do + evaluate(literal('').not()).should == true + end + + it ":undef == false" do + evaluate(literal(:undef).not()).should == true + end + end + + context "connectives should stop when truth is obtained" do + it "true && false && error == false (and no failure)" do + evaluate(literal(false).and(literal('0xwtf') + literal(1)).and(literal(true))).should == false + end + + it "false || true || error == true (and no failure)" do + evaluate(literal(true).or(literal('0xwtf') + literal(1)).or(literal(false))).should == true + end + + it "false || false || error == error (false positive test)" do + # TODO: Change the exception type + expect {evaluate(literal(true).and(literal('0xwtf') + literal(1)).or(literal(false)))}.to raise_error(Puppet::ParseError) + end + end + end +end diff --git a/spec/unit/pops/evaluator/string_interpolation_spec.rb b/spec/unit/pops/evaluator/string_interpolation_spec.rb new file mode 100644 index 000000000..f1018fcf8 --- /dev/null +++ b/spec/unit/pops/evaluator/string_interpolation_spec.rb @@ -0,0 +1,44 @@ +#! /usr/bin/env ruby +require 'spec_helper' + +require 'puppet/pops' +require 'puppet/pops/evaluator/evaluator_impl' + + +# relative to this spec file (./) does not work as this file is loaded by rspec +require File.join(File.dirname(__FILE__), '/evaluator_rspec_helper') + +describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do + include EvaluatorRspecHelper + + context "When evaluator performs string interpolation" do + it "should interpolate a bare word as a variable name, \"${var}\"" do + a_block = block(fqn('a').set(10), string('value is ', text(fqn('a')), ' yo')) + evaluate(a_block).should == "value is 10 yo" + end + + it "should interpolate a variable in a text expression, \"${$var}\"" do + a_block = block(fqn('a').set(10), string('value is ', text(var(fqn('a'))), ' yo')) + evaluate(a_block).should == "value is 10 yo" + end + + it "should interpolate a variable, \"$var\"" do + a_block = block(fqn('a').set(10), string('value is ', var(fqn('a')), ' yo')) + evaluate(a_block).should == "value is 10 yo" + end + + it "should interpolate any expression in a text expression, \"${var*2}\"" do + a_block = block(fqn('a').set(5), string('value is ', text(var(fqn('a')) * 2) , ' yo')) + evaluate(a_block).should == "value is 10 yo" + end + + it "should interpolate any expression without a text expression, \"${$var*2}\"" do + # there is no concrete syntax for this, but the parser can generate this simpler + # equivalent form where the expression is not wrapped in a TextExpression + a_block = block(fqn('a').set(5), string('value is ', var(fqn('a')) * 2 , ' yo')) + evaluate(a_block).should == "value is 10 yo" + end + + # TODO: Add function call tests - Pending implementation of calls in the evaluator + end +end diff --git a/spec/unit/pops/evaluator/variables_spec.rb b/spec/unit/pops/evaluator/variables_spec.rb new file mode 100644 index 000000000..403642415 --- /dev/null +++ b/spec/unit/pops/evaluator/variables_spec.rb @@ -0,0 +1,156 @@ +#! /usr/bin/env ruby +require 'spec_helper' +require 'puppet/pops' +require 'puppet/pops/evaluator/evaluator_impl' + + +# This file contains basic testing of variable references and assignments +# using a top scope and a local scope. +# It does not test variables and named scopes. +# + +# relative to this spec file (./) does not work as this file is loaded by rspec +require File.join(File.dirname(__FILE__), '/evaluator_rspec_helper') + +describe 'Puppet::Pops::Impl::EvaluatorImpl' do + include EvaluatorRspecHelper + + context "When the evaluator deals with variables" do + context "it should handle" do + it "simple assignment and dereference" do + evaluate_l(block( fqn('a').set(literal(2)+literal(2)), var('a'))).should == 4 + end + + it "local scope shadows top scope" do + top_scope_block = block( fqn('a').set(literal(2)+literal(2)), var('a')) + local_scope_block = block( fqn('a').set(var('a') + literal(2)), var('a')) + evaluate_l(top_scope_block, local_scope_block).should == 6 + end + + it "shadowed in local does not affect parent scope" do + top_scope_block = block( fqn('a').set(literal(2)+literal(2)), var('a')) + local_scope_block = block( fqn('a').set(var('a') + literal(2)), var('a')) + top_scope_again = var('a') + evaluate_l(top_scope_block, local_scope_block, top_scope_again).should == 4 + end + + it "access to global names works in top scope" do + top_scope_block = block( fqn('::a').set(literal(2)+literal(2)), var('::a')) + evaluate_l(top_scope_block).should == 4 + end + + it "access to global names works in local scope" do + top_scope_block = block( fqn('::a').set(literal(2)+literal(2)), var('::a')) + local_scope_block = block( fqn('a').set(var('::a')+literal(2)), var('::a')) + evaluate_l(top_scope_block, local_scope_block).should == 4 + end + + it "can not change a variable value in same scope" do + expect { evaluate_l(block(fqn('a').set(10), fqn('a').set(20))) }.to raise_error(Puppet::Pops::ImmutableError) + end + + context "+= operations" do + it "appending to non existing value, nil += []" do + top_scope_block = fqn('b').set([1,2,3]) + local_scope_block = fqn('a').plus_set([4]) + expect {evaluate_l(top_scope_block, local_scope_block)}.to raise_error(Puppet::Pops::EvaluationError) + end + + context "appending to list" do + it "from list, [] += []" do + top_scope_block = fqn('a').set([1,2,3]) + local_scope_block = fqn('a').plus_set([4]) + evaluate_l(top_scope_block, local_scope_block).should == [1,2,3,4] + end + + it "from hash, [] += {a=>b}" do + top_scope_block = fqn('a').set([1,2,3]) + local_scope_block = fqn('a').plus_set({'a' => 1, 'b'=>2}) + evaluate_l(top_scope_block, local_scope_block).should satisfy {|result| + # hash in 1.8.7 is not insertion order preserving, hence this hoop + result == [1,2,3,['a',1],['b',2]] || result == [1,2,3,['b',2],['a',1]] + } + end + + it "from single value, [] += x" do + top_scope_block = fqn('a').set([1,2,3]) + local_scope_block = fqn('a').plus_set(4) + evaluate_l(top_scope_block, local_scope_block).should == [1,2,3,4] + end + + it "from embedded list, [] += [[x]]" do + top_scope_block = fqn('a').set([1,2,3]) + local_scope_block = fqn('a').plus_set([[4,5]]) + evaluate_l(top_scope_block, local_scope_block).should == [1,2,3,[4,5]] + end + end + + context "appending to hash" do + it "from hash, {a=>b} += {x=>y}" do + top_scope_block = fqn('a').set({'a' => 1, 'b' => 2}) + local_scope_block = fqn('a').plus_set({'c' => 3}) + evaluate_l(top_scope_block, local_scope_block) do |scope| + # Assert no change to top scope hash + scope.get_variable_entry('a').value.should == {'a' =>1, 'b'=> 2} + end.should == {'a' => 1, 'b' => 2, 'c' => 3} + end + + it "from list, {a=>b} += ['x', y]" do + top_scope_block = fqn('a').set({'a' => 1, 'b' => 2}) + local_scope_block = fqn('a').plus_set(['c', 3]) + evaluate_l(top_scope_block, local_scope_block) do |scope| + # Assert no change to top scope hash + scope.get_variable_entry('a').value.should == {'a' =>1, 'b'=> 2} + end.should == {'a' => 1, 'b' => 2, 'c' => 3} + end + + it "with overwrite from hash, {a=>b} += {a=>c}" do + top_scope_block = fqn('a').set({'a' => 1, 'b' => 2}) + local_scope_block = fqn('a').plus_set({'b' => 4, 'c' => 3}) + evaluate_l(top_scope_block, local_scope_block) do |scope| + # Assert no change to top scope hash + scope.get_variable_entry('a').value.should == {'a' =>1, 'b'=> 2} + end.should == {'a' => 1, 'b' => 4, 'c' => 3} + end + + it "with overwrite from list, {a=>b} += ['a', c]" do + top_scope_block = fqn('a').set({'a' => 1, 'b' => 2}) + local_scope_block = fqn('a').plus_set(['b', 4, 'c', 3]) + evaluate_l(top_scope_block, local_scope_block) do |scope| + # Assert no change to topscope hash + scope.get_variable_entry('a').value.should == {'a' =>1, 'b'=> 2} + end.should == {'a' => 1, 'b' => 4, 'c' => 3} + end + + it "from odd length array - error" do + top_scope_block = fqn('a').set({'a' => 1, 'b' => 2}) + local_scope_block = fqn('a').plus_set(['b', 4, 'c']) + expect { evaluate_l(top_scope_block, local_scope_block) }.to raise_error(Puppet::Pops::EvaluationError) + end + end + end + + context "access to numeric variables" do + it "without a match" do + evaluate_l(block(literal(2) + literal(2), + [var(0), var(1), var(2), var(3)])).should == [nil, nil, nil, nil] + end + + it "after a match" do + evaluate_l(block(literal('abc') =~ literal(/(a)(b)(c)/), + [var(0), var(1), var(2), var(3)])).should == ['abc', 'a', 'b', 'c'] + end + + it "after a failed match" do + evaluate_l(block(literal('abc') =~ literal(/(x)(y)(z)/), + [var(0), var(1), var(2), var(3)])).should == [nil, nil, nil, nil] + end + + it "after a match with variable referencing a non existing group" do + evaluate_l(block(literal('abc') =~ literal(/(a)(b)(c)/), + [var(0), var(1), var(2), var(3), var(4)])).should == ['abc', 'a', 'b', 'c', nil] + end + end + end + end +end From 8dbd8650e4a4cc11b51b209aabb8ce2dadca8447 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 10 Sep 2013 19:18:03 +0200 Subject: [PATCH 017/800] (#22363) Add variables tests and fix evaluation implementation. This adds testing of variables, assignment and += assignment in various forms. The + operator has been broken out to a separate method to be used by both + and +=. Corrects calls in runtime support (3x must have string variables names). Corrects problems in the rspec helper w.r.t top/local scope and making sure there is a topscope (to lookup ::x namespaced variables). --- lib/puppet/pops/evaluator/evaluator_impl.rb | 73 +++++++++++-------- lib/puppet/pops/evaluator/runtime3_support.rb | 9 ++- .../pops/evaluator/evaluator_rspec_helper.rb | 13 +++- spec/unit/pops/evaluator/variables_spec.rb | 36 ++++----- 4 files changed, 78 insertions(+), 53 deletions(-) diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index b368cdb73..c5d7eb2ed 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -103,6 +103,10 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # @return [value] # def assign_String(name, value, o, scope) + if name =~ /::/ + # Issues::CROSS_SCOPE_ASSIGNMENT + fail("Cross-namespace assignment is not allowed, cannot assign to $#{name}", o.left_expr, scope) + end set_variable(name, value, o, scope) value end @@ -205,7 +209,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # @example Puppet DSL # $a = 1 # $a += 1 - # @todo support for -= ('without' to remove from array) not yet implemented + # @todo support for -= ('without' to remove from array) concrete syntax not yet implemented # def eval_AssignmentExpression(o, scope) name, value = eval_BinaryExpression(o, scope) @@ -215,23 +219,14 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator assign(name, value, o, scope) when :'+=' - # Typecheck RHS - case value - when String - when Array - when Hash - else - # Note, Numeric is illegal since it is impossible to determine radix, better to fail and let user enter the string, or - # use numeric to string conversion with formatting. - fail("Illegal += right hand side type. Cannot append an instance of #{value.class}", o, scope) - end # if value does not exist, return RHS (note that type check has already been made so correct type is ensured) - if !variable_exists?(scope, name) + if !variable_exists?(name, scope) return value end begin - # Delegate to concatenate function to deal with check of LHS, and perform concatenation - assign(name, concatenate(get_variable_value(scope, name), value), o, scope) + # Delegate to calculate function to deal with check of LHS, and perform ´+´ as arithmetic or concatenation the + # same way as ArithmeticExpression performs `+`. + assign(name, calculate(get_variable_value(name, o, scope), value, :'+', o.left_expr, o.right_expr, scope), o, scope) rescue ArgumentError => e fail("Append assignment += failed with error: #{e.message}", o, scope) end @@ -267,10 +262,25 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator fail("Unknown arithmetic operator #{o.operator}", o, scope) end left, right = eval_BinaryExpression(o, scope) + begin + result = calculate(left, right, o.operator, o.left_expr, o.right_expr, scope) + rescue ArgumentError => e + fail(e.message, o, scope) + end + result + end - if (left.is_a?(Array) || left.is_a?(Hash)) && COLLECTION_OPERATORS.include?(o.operator) + + # Handles binary expression where lhs and rhs are array/hash or numeric and operator is +, - , *, % / << >> + # + def calculate(left, right, operator, left_o, right_o, scope) + unless ARITHMETIC_OPERATORS.include?(operator) + raise ArgumentError, "Unknown arithmetic operator #{o.operator}" + end + + if (left.is_a?(Array) || left.is_a?(Hash)) && COLLECTION_OPERATORS.include?(operator) # Handle operation on collections - case o.operator + case operator when :'+' concatenate(left, right) when :'-' @@ -278,22 +288,19 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator when :'<<' unless left.is_a?(Array) # TODO: when improving fail, pass o.left_expr - fail("Left operand in '<<' expression is not an Array", o, scope) + raise ArgumentError, "Left operand in '<<' expression is not an Array" end left + [right] end else # Handle operation on numeric - left = box_numeric(left, o, scope) - right = box_numeric(right, o, scope) + left = box_numeric(left, left_o, scope) + right = box_numeric(right, right_o, scope) begin - result = left.send(o.operator, right) + result = left.send(operator, right) rescue NoMethodError => e - fail("Operator #{o.operator} not applicable to a value of type #{left.class}", o, scope) + raise ArgumentError, "Operator #{operator} not applicable to a value of type #{left.class}" end - # puts "Arithmetic returns '#{left}' #{o.operator} '#{right}' => #{result}" - # puts "left = #{o.left_expr.class}, #{o.left_expr.value}" - # puts "right = #{o.right_expr.class}, #{o.right_expr.value}" result end end @@ -575,12 +582,8 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator fail "Internal error: a variable name should result in a String when evaluated. Got expression of #{o.expr.class} type.", o, scope end # TODO: Check for valid variable name - if variable_exists?(name, scope) - get_variable_value(name, o, scope) - else - # TODO: semantics of undefined variable in scope, this just returns what scope does == value or nil - nil - end + # TODO: semantics of undefined variable in scope, this just returns what scope does == value or nil + get_variable_value(name, o, scope) end # Evaluates double quoted strings that may contain interpolation @@ -716,8 +719,16 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # * Hash => a merge, where entries in `y` overrides # * any other => error # + # When x is something else, wrap it in an array first. + # + # When x is nil, an empty array is used instead. + # # @note to concatenate an Array, nest the array - i.e. `[1,2], [[2,3]]` # + # @overload concatenate(obj_x, obj_y) + # @param obj_x [Object] object to wrap in an array and concatenate to; see other overloaded methods for return type + # @param ary_y [Object] array to concatenate at end of `ary_x` + # @return [Object] wraps obj_x in array before using other overloaded option based on type of obj_y # @overload concatenate(ary_x, ary_y) # @param ary_x [Array] array to concatenate to # @param ary_y [Array] array to concatenate at end of `ary_x` @@ -742,6 +753,8 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # @raise [ArgumentError] when `xxx_x` is a Hash, and `xxx_y` is neither Array nor Hash. # def concatenate(x, y) + x = [] if x.nil? + x = [x] unless x.is_a?(Array) || x.is_a?(Hash) case x when Array y = case y diff --git a/lib/puppet/pops/evaluator/runtime3_support.rb b/lib/puppet/pops/evaluator/runtime3_support.rb index e4804d1cd..a317cc54b 100644 --- a/lib/puppet/pops/evaluator/runtime3_support.rb +++ b/lib/puppet/pops/evaluator/runtime3_support.rb @@ -27,20 +27,23 @@ module Puppet::Pops::Evaluator::Runtime3Support # Returns the value of the variable (nil is returned if variable has no value, or if variable does not exist) # def get_variable_value(name, o, scope) - scope.lookupvar(name) + # Puppet 3x stores all variables as strings (then converts them back to numeric with a regexp... to see if it is a match variable) + # Not ideal, scope should support numeric lookup directly instead. + # TODO: consider fixing scope + scope.lookupvar(name.to_s) end # Returns true if the variable of the given name is set in the given most nested scope. True is returned even if # variable is bound to nil. # def variable_bound?(name, scope) - scope.bound?(name) + scope.bound?(name.to_s) end # Returns true of the variable is bound to a value or nil, in the scope or it's parent scopes. # def variable_exists?(name, scope) - scope.exist?(name) + scope.exist?(name.to_s) end def set_match_data(match_data, o, scope) diff --git a/spec/unit/pops/evaluator/evaluator_rspec_helper.rb b/spec/unit/pops/evaluator/evaluator_rspec_helper.rb index a6cecd74c..eccf8aaca 100644 --- a/spec/unit/pops/evaluator/evaluator_rspec_helper.rb +++ b/spec/unit/pops/evaluator/evaluator_rspec_helper.rb @@ -17,12 +17,19 @@ module EvaluatorRspecHelper def evaluate in_top_scope, scopename="x", in_named_scope = nil, in_top_scope_again = nil, &block node = Puppet::Node.new('localhost') compiler = Puppet::Parser::Compiler.new(node) - top_scope = Puppet::Parser::Scope.new(compiler) + + # Compiler must create the top scope +# compiler.send(:evaluate_main) + + # compiler creates the top scope if one is not present + top_scope = compiler.topscope() + # top_scope = Puppet::Parser::Scope.new(compiler) evaluator = Puppet::Pops::Evaluator::EvaluatorImpl.new result = evaluator.evaluate(in_top_scope.current, top_scope) if in_named_scope other_scope = Puppet::Parser::Scope.new(compiler) + other_scope.add_namespace(scopename) result = evaluator.evaluate(in_named_scope.current, other_scope) end if in_top_scope_again @@ -44,7 +51,9 @@ module EvaluatorRspecHelper def evaluate_l in_top_scope, in_local_scope = nil, in_top_scope_again = nil, &block node = Puppet::Node.new('localhost') compiler = Puppet::Parser::Compiler.new(node) - top_scope = Puppet::Parser::Scope.new(compiler) + + # compiler creates the top scope if one is not present + top_scope = compiler.topscope() evaluator = Puppet::Pops::Evaluator::EvaluatorImpl.new result = evaluator.evaluate(in_top_scope.current, top_scope) diff --git a/spec/unit/pops/evaluator/variables_spec.rb b/spec/unit/pops/evaluator/variables_spec.rb index 403642415..79a5178f2 100644 --- a/spec/unit/pops/evaluator/variables_spec.rb +++ b/spec/unit/pops/evaluator/variables_spec.rb @@ -35,37 +35,37 @@ describe 'Puppet::Pops::Impl::EvaluatorImpl' do end it "access to global names works in top scope" do - top_scope_block = block( fqn('::a').set(literal(2)+literal(2)), var('::a')) + top_scope_block = block( fqn('a').set(literal(2)+literal(2)), var('::a')) evaluate_l(top_scope_block).should == 4 end it "access to global names works in local scope" do - top_scope_block = block( fqn('::a').set(literal(2)+literal(2)), var('::a')) + top_scope_block = block( fqn('a').set(literal(2)+literal(2))) local_scope_block = block( fqn('a').set(var('::a')+literal(2)), var('::a')) - evaluate_l(top_scope_block, local_scope_block).should == 4 + evaluate_l(top_scope_block, local_scope_block).should == 6 end it "can not change a variable value in same scope" do - expect { evaluate_l(block(fqn('a').set(10), fqn('a').set(20))) }.to raise_error(Puppet::Pops::ImmutableError) + expect { evaluate_l(block(fqn('a').set(10), fqn('a').set(20))) }.to raise_error(/Cannot reassign variable a/) end context "+= operations" do it "appending to non existing value, nil += []" do top_scope_block = fqn('b').set([1,2,3]) local_scope_block = fqn('a').plus_set([4]) - expect {evaluate_l(top_scope_block, local_scope_block)}.to raise_error(Puppet::Pops::EvaluationError) + evaluate_l(top_scope_block, local_scope_block).should == [4] end context "appending to list" do it "from list, [] += []" do top_scope_block = fqn('a').set([1,2,3]) - local_scope_block = fqn('a').plus_set([4]) + local_scope_block = block(fqn('a').plus_set([4]), fqn('a').var()) evaluate_l(top_scope_block, local_scope_block).should == [1,2,3,4] end it "from hash, [] += {a=>b}" do top_scope_block = fqn('a').set([1,2,3]) - local_scope_block = fqn('a').plus_set({'a' => 1, 'b'=>2}) + local_scope_block = block(fqn('a').plus_set({'a' => 1, 'b'=>2}), fqn('a').var()) evaluate_l(top_scope_block, local_scope_block).should satisfy {|result| # hash in 1.8.7 is not insertion order preserving, hence this hoop result == [1,2,3,['a',1],['b',2]] || result == [1,2,3,['b',2],['a',1]] @@ -74,13 +74,13 @@ describe 'Puppet::Pops::Impl::EvaluatorImpl' do it "from single value, [] += x" do top_scope_block = fqn('a').set([1,2,3]) - local_scope_block = fqn('a').plus_set(4) + local_scope_block = block(fqn('a').plus_set(4), fqn('a').var()) evaluate_l(top_scope_block, local_scope_block).should == [1,2,3,4] end it "from embedded list, [] += [[x]]" do top_scope_block = fqn('a').set([1,2,3]) - local_scope_block = fqn('a').plus_set([[4,5]]) + local_scope_block = block(fqn('a').plus_set([[4,5]]), fqn('a').var()) evaluate_l(top_scope_block, local_scope_block).should == [1,2,3,[4,5]] end end @@ -88,44 +88,44 @@ describe 'Puppet::Pops::Impl::EvaluatorImpl' do context "appending to hash" do it "from hash, {a=>b} += {x=>y}" do top_scope_block = fqn('a').set({'a' => 1, 'b' => 2}) - local_scope_block = fqn('a').plus_set({'c' => 3}) + local_scope_block = block(fqn('a').plus_set({'c' => 3}), fqn('a').var()) evaluate_l(top_scope_block, local_scope_block) do |scope| # Assert no change to top scope hash - scope.get_variable_entry('a').value.should == {'a' =>1, 'b'=> 2} + scope['a'].should == {'a' =>1, 'b'=> 2} end.should == {'a' => 1, 'b' => 2, 'c' => 3} end it "from list, {a=>b} += ['x', y]" do top_scope_block = fqn('a').set({'a' => 1, 'b' => 2}) - local_scope_block = fqn('a').plus_set(['c', 3]) + local_scope_block = block(fqn('a').plus_set(['c', 3]), fqn('a').var()) evaluate_l(top_scope_block, local_scope_block) do |scope| # Assert no change to top scope hash - scope.get_variable_entry('a').value.should == {'a' =>1, 'b'=> 2} + scope['a'].should == {'a' =>1, 'b'=> 2} end.should == {'a' => 1, 'b' => 2, 'c' => 3} end it "with overwrite from hash, {a=>b} += {a=>c}" do top_scope_block = fqn('a').set({'a' => 1, 'b' => 2}) - local_scope_block = fqn('a').plus_set({'b' => 4, 'c' => 3}) + local_scope_block = block(fqn('a').plus_set({'b' => 4, 'c' => 3}),fqn('a').var()) evaluate_l(top_scope_block, local_scope_block) do |scope| # Assert no change to top scope hash - scope.get_variable_entry('a').value.should == {'a' =>1, 'b'=> 2} + scope['a'].should == {'a' =>1, 'b'=> 2} end.should == {'a' => 1, 'b' => 4, 'c' => 3} end it "with overwrite from list, {a=>b} += ['a', c]" do top_scope_block = fqn('a').set({'a' => 1, 'b' => 2}) - local_scope_block = fqn('a').plus_set(['b', 4, 'c', 3]) + local_scope_block = block(fqn('a').plus_set(['b', 4, 'c', 3]), fqn('a').var()) evaluate_l(top_scope_block, local_scope_block) do |scope| # Assert no change to topscope hash - scope.get_variable_entry('a').value.should == {'a' =>1, 'b'=> 2} + scope['a'].should == {'a' =>1, 'b'=> 2} end.should == {'a' => 1, 'b' => 4, 'c' => 3} end it "from odd length array - error" do top_scope_block = fqn('a').set({'a' => 1, 'b' => 2}) local_scope_block = fqn('a').plus_set(['b', 4, 'c']) - expect { evaluate_l(top_scope_block, local_scope_block) }.to raise_error(Puppet::Pops::EvaluationError) + expect { evaluate_l(top_scope_block, local_scope_block) }.to raise_error(/Append assignment \+= failed with error: odd number of arguments for Hash/) end end end From 9788ff341cbd45607aa4922e1d3f00890da03dc9 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Wed, 11 Sep 2013 03:35:44 +0200 Subject: [PATCH 018/800] (#22363) Remove defunct testing of type operations The functionality was moved to streamline the type model. The operations are now implemented (and tested) as part of AccessExpression (i.e. it is a language feature to apply [] operator on types, not an intrinsic capability of a type). --- spec/unit/pops/types/type_operations_spec.rb | 78 -------------------- 1 file changed, 78 deletions(-) delete mode 100644 spec/unit/pops/types/type_operations_spec.rb diff --git a/spec/unit/pops/types/type_operations_spec.rb b/spec/unit/pops/types/type_operations_spec.rb deleted file mode 100644 index 7f0827873..000000000 --- a/spec/unit/pops/types/type_operations_spec.rb +++ /dev/null @@ -1,78 +0,0 @@ -require 'spec_helper' -require 'puppet/pops' - -describe 'operations on types' do - - describe 'Puppet::Pops::Types::PPatternType' do - it 'can create a regular expression via the [] operator' do - result = Puppet::Pops::Types::PPatternType.new()['a*'] - expect(result.class).to eql(Regexp) - expect(result).to eql(Regexp.new('a*')) - end - end - - describe 'the Puppet::Pops::Types::PCatalog subtypes' do - it 'can create an unspecific type by using the [] operator without arguments' do - x = Puppet::Pops::Types::TypeFactory.host_class() - expect(x[]).to be_the_type(Puppet::Pops::Types::TypeFactory.host_class()) - - x = Puppet::Pops::Types::TypeFactory.resource() - expect(x[]).to be_the_type(Puppet::Pops::Types::TypeFactory.resource()) - - x = Puppet::Pops::Types::TypeFactory.resource('File') - expect(x[]).to be_the_type(Puppet::Pops::Types::TypeFactory.resource('File')) - end - - it 'can create a specific class by using [name] on PHostClassType with given class name' do - x = Puppet::Pops::Types::TypeFactory.host_class() - expect(x['a']).to be_the_type(Puppet::Pops::Types::TypeFactory.host_class('a')) - end - - it 'can create a specific resource reference by using [name] on a PResourceType with given type name' do - x = Puppet::Pops::Types::TypeFactory.resource('File') - expect(x['a']).to be_the_type(Puppet::Pops::Types::TypeFactory.resource('File', 'a')) - end - - it 'can create an Array of class references' do - x = Puppet::Pops::Types::TypeFactory.host_class() - result = x['a', 'b', 'c'] - expect(result.class).to eql(Array) - expect(result[0]).to be_the_type(Puppet::Pops::Types::TypeFactory.host_class('a')) - expect(result[1]).to be_the_type(Puppet::Pops::Types::TypeFactory.host_class('b')) - expect(result[2]).to be_the_type(Puppet::Pops::Types::TypeFactory.host_class('c')) - end - - it 'can create an Array of Resource references' do - x = Puppet::Pops::Types::TypeFactory.resource('F') - result = x['a', 'b', 'c'] - expect(result.class).to eql(Array) - expect(result[0]).to be_the_type(Puppet::Pops::Types::TypeFactory.resource('F', 'a')) - expect(result[1]).to be_the_type(Puppet::Pops::Types::TypeFactory.resource('F', 'b')) - expect(result[2]).to be_the_type(Puppet::Pops::Types::TypeFactory.resource('F', 'c')) - end - - it 'checks error conditions' do - x = Puppet::Pops::Types::TypeFactory.host_class('a') - expect{x['b']}.to raise_error(/Cannot create new Class references from a specific Class reference/) - - x = Puppet::Pops::Types::TypeFactory.resource('File', 'foo') - expect{x['b']}.to raise_error(/Cannot create new Resource references from a specific Resource reference/) - - x = Puppet::Pops::Types::TypeFactory.resource() - expect{x['b']}.to raise_error(/A Resource reference without type name can not produce Resource references/) - end - end - - matcher :be_the_type do |type| - calc = Puppet::Pops::Types::TypeCalculator.new - - match do |actual| - calc.assignable?(actual, type) && calc.assignable?(type, actual) - end - - failure_message_for_should do |actual| - "expected #{calc.string(type)}, but was #{calc.string(actual)}" - end - end - -end \ No newline at end of file From 74278bc60a492c295624f849311b084ad4e819a4 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Wed, 11 Sep 2013 05:17:36 +0200 Subject: [PATCH 019/800] (#22363) Add Integer[from, to, step] feature to produce array This produces an array with values from, to with step size (default to 1). Supports descending series when from < to and step < 0. --- lib/puppet/pops/evaluator/access_operator.rb | 20 ++++++++++++ lib/puppet/pops/evaluator/evaluator_impl.rb | 1 - spec/unit/pops/evaluator/access_ops_spec.rb | 32 ++++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/lib/puppet/pops/evaluator/access_operator.rb b/lib/puppet/pops/evaluator/access_operator.rb index ad46726f1..229ad23af 100644 --- a/lib/puppet/pops/evaluator/access_operator.rb +++ b/lib/puppet/pops/evaluator/access_operator.rb @@ -75,6 +75,7 @@ class Puppet::Pops::Evaluator::AccessOperator end end + # Evaluates [] with support for one or more arguments. If more than one argument is used, the result # is an array with each lookup. # @@ -93,6 +94,25 @@ class Puppet::Pops::Evaluator::AccessOperator end end + # An integer type provides a way to create an Array of integers from, to (inclusive) (must be given), and an + # optional step at the 3d position which defaults to 1 + def access_PIntegerType(o, scope, keys) + if keys.size == 0 + return o + end + + unless keys.size.between?(2, 3) + fail("Integer[] only accepts two or three parameters (from, to, step). Got #{keys.size}", @semantic.keys, scope) + end + keys.each {|x| fail("Integer[] requires all keys to be integers", @semantic.keys, scope) unless x.is_a?(Numeric) } + from, to, step = keys + fail("Integer[] cannot step with increment of 0", @semantic.keys, scope) if step == 0 + step ||= 1 + + # Ok, so this is quite bad for very large arrays... + from.step(to, step).collect {|x| x} + end + # A Hash can create a new Hash type, one arg sets value type, two args sets key and value type in new type # It is not possible to create a collection of Hash types. # diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index c5d7eb2ed..784e0492f 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -753,7 +753,6 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # @raise [ArgumentError] when `xxx_x` is a Hash, and `xxx_y` is neither Array nor Hash. # def concatenate(x, y) - x = [] if x.nil? x = [x] unless x.is_a?(Array) || x.is_a?(Hash) case x when Array diff --git a/spec/unit/pops/evaluator/access_ops_spec.rb b/spec/unit/pops/evaluator/access_ops_spec.rb index 26526686e..8fc298168 100644 --- a/spec/unit/pops/evaluator/access_ops_spec.rb +++ b/spec/unit/pops/evaluator/access_ops_spec.rb @@ -91,6 +91,38 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do context "When applied to a type it" do let(:types) { Puppet::Pops::Types::TypeFactory } + # Integer + # + it 'produces an Array of ascending values in range from Integer[from, to]' do + expr = fqr('Integer')[1, 3] + expect(evaluate(expr)).to eql([1,2,3]) + end + + it 'produces an Array of one values in range from Integer[from, from]' do + expr = fqr('Integer')[1,1] + expect(evaluate(expr)).to eql([1]) + end + + it 'produces an Array of one values in range from Integer[from, Date: Wed, 11 Sep 2013 16:33:31 +0200 Subject: [PATCH 020/800] (#22363) Fix typos in test example titles --- spec/unit/pops/evaluator/access_ops_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/unit/pops/evaluator/access_ops_spec.rb b/spec/unit/pops/evaluator/access_ops_spec.rb index 8fc298168..1f0af97bd 100644 --- a/spec/unit/pops/evaluator/access_ops_spec.rb +++ b/spec/unit/pops/evaluator/access_ops_spec.rb @@ -98,12 +98,12 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do expect(evaluate(expr)).to eql([1,2,3]) end - it 'produces an Array of one values in range from Integer[from, from]' do + it 'produces an Array of one value in range from Integer[from, from]' do expr = fqr('Integer')[1,1] expect(evaluate(expr)).to eql([1]) end - it 'produces an Array of one values in range from Integer[from, Date: Fri, 13 Sep 2013 04:34:13 +0200 Subject: [PATCH 021/800] (#22363) Add evaluation of conditionals test and fixes This adds tests of conditional epressions; if, unless, and selector. This commit also includes handing management of 3x scope nested match scopes (resetting them to no leak to expressions after the conditional). --- lib/puppet/pops/evaluator/evaluator_impl.rb | 95 +++++---- lib/puppet/pops/evaluator/runtime3_support.rb | 11 + spec/unit/pops/evaluator/conditionals_spec.rb | 188 ++++++++++++++++++ 3 files changed, 258 insertions(+), 36 deletions(-) create mode 100644 spec/unit/pops/evaluator/conditionals_spec.rb diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index 784e0492f..dbefaa950 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -397,7 +397,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator end matched = pattern.match(left) # nil, or MatchData - set_match_data(matched, o, scope) # creates or clears ephemeral + set_match_data(matched, o, scope) # creates ephemeral # convert match result to Boolean true, or false o.operator == :'=~' ? !!matched : !matched @@ -456,24 +456,29 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # If an option matches, the result of evaluating that option is returned. # @return [Object, nil] what a matched option returns, or nil if nothing matched. # - def eval_CaseExpression o, scope - test = evaluate(o.test, scope) - result = nil - the_default = nil - if o.options.find do |co| - # the first case option that matches - if co.values.find do |c| - the_default = co.then_expr if c.is_a? Puppet::Pops::Model::LiteralDefault - is_match?(test, evaluate(c, scope), scope) + def eval_CaseExpression(o, scope) + # memo scope level before evaluating test - don't want a match in the case test to leak $n match vars + # to expressions after the case expression. + # + with_guarded_scope(scope) do + test = evaluate(o.test, scope) + result = nil + the_default = nil + if o.options.find do |co| + # the first case option that matches + if co.values.find do |c| + the_default = co.then_expr if c.is_a? Puppet::Pops::Model::LiteralDefault + is_match?(test, evaluate(c, scope), c, scope) + end + result = evaluate(co.then_expr, scope) + true # the option was picked + end end - result = evaluate(co.then_expr, scope) - true # the option was picked + result # an option was picked, and produced a result + else + evaluate(the_default, scope) # evaluate the default (should be a nop/nil) if there is no default). end end - result # an option was picked, and produced a result - else - evaluate(the_default, scope) # evaluate the default (should be a nop/nil) if there is no default). - end end # @todo not implemented - maybe not needed; this is an abstract class @@ -532,34 +537,43 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # $x ? { 10 => true, 20 => false, default => 0 } # def eval_SelectorExpression o, scope - test = evaluate(o.left_expr, scope) - selected = o.selectors.find {|s| - evaluate(s.matching_expr, scope) {|candidate| - candidate == :default || is_match?(test, candidate, scope) + # memo scope level before evaluating test - don't want a match in the case test to leak $n match vars + # to expressions after the selector expression. + # + with_guarded_scope(scope) do + test = evaluate(o.left_expr, scope) + selected = o.selectors.find {|s| + evaluate(s.matching_expr, scope) {|candidate| + candidate == :default || is_match?(test, candidate, s.matching_expr, scope) + } } - } - if selected - evaluate(selected.value_expr, scope) - else - nil + if selected + evaluate(selected.value_expr, scope) + else + nil + end end end # Evaluates Puppet DSL `if` def eval_IfExpression o, scope - if is_true?(evaluate(o.test, scope)) - evaluate(o.then_expr, scope) - else - evaluate(o.else_expr, scope) + with_guarded_scope(scope) do + if is_true?(evaluate(o.test, scope)) + evaluate(o.then_expr, scope) + else + evaluate(o.else_expr, scope) + end end end # Evaluates Puppet DSL `unless` def eval_UnlessExpression o, scope - unless is_true?(evaluate(o.test, scope)) - evaluate(o.then_expr, scope) - else - evaluate(o.else_expr, scope) + with_guarded_scope(scope) do + unless is_true?(evaluate(o.test, scope)) + evaluate(o.then_expr, scope) + else + evaluate(o.else_expr, scope) + end end end @@ -813,19 +827,28 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # of value except regular expression where a match is performed. # @todo there are implementation issues left to deal with (see source) # - def is_match? left, right, scope + def is_match? left, right, o, scope # TODO: deal with TypeError # TODO: match when left is a Number, or something strange # TODO: solution should be used in MatchExpression if right.is_a?(Regexp) matched = right.match(left) - scope.set_match_data(matched) - !!matched + set_match_data(matched, o, scope) # creates or clears ephemeral + !!matched # convert to boolean else left == right end end + def with_guarded_scope(scope) + scope_memo = get_scope_nesting_level(scope) + begin + yield + ensure + set_scope_nesting_level(scope, scope_memo) + end + end + # Translates data type name to instance of a data type. # # Maps an object to an instance of RGen::ECore::EDataType - i.e. returns diff --git a/lib/puppet/pops/evaluator/runtime3_support.rb b/lib/puppet/pops/evaluator/runtime3_support.rb index a317cc54b..8c08c2019 100644 --- a/lib/puppet/pops/evaluator/runtime3_support.rb +++ b/lib/puppet/pops/evaluator/runtime3_support.rb @@ -58,6 +58,17 @@ module Puppet::Pops::Evaluator::Runtime3Support end end + def get_scope_nesting_level(scope) + scope.ephemeral_level + end + + def set_scope_nesting_level(scope, level) + # Yup, 3x uses this method to reset the level, it also supports passing :all to destroy all + # ephemeral/local scopes - which is a sure way to create havoc. + # + scope.unset_ephemeral_var(level) + end + # Adds a relationship between the given `source` and `target` of the given `relationship_type` # @param source [Puppet:Pops::Types::PCatalogEntryType] the source end of the relationship (from) # @param target [Puppet:Pops::Types::PCatalogEntryType] the target end of the relationship (to) diff --git a/spec/unit/pops/evaluator/conditionals_spec.rb b/spec/unit/pops/evaluator/conditionals_spec.rb new file mode 100644 index 000000000..21257d429 --- /dev/null +++ b/spec/unit/pops/evaluator/conditionals_spec.rb @@ -0,0 +1,188 @@ +#! /usr/bin/env ruby +require 'spec_helper' +require 'puppet/pops' +require 'puppet/pops/evaluator/evaluator_impl' + +# relative to this spec file (./) does not work as this file is loaded by rspec +require File.join(File.dirname(__FILE__), '/evaluator_rspec_helper') + +# This file contains testing of Conditionals, if, case, unless, selector +# +describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do + include EvaluatorRspecHelper + + context "When the evaluator evaluates" do + context "an if expression" do + it 'should output the expected result when dumped' do + dump(IF(literal(true), literal(2), literal(5))).should == unindent(<<-TEXT + (if true + (then 2) + (else 5)) + TEXT + ) + end + + it 'if true {5} == 5' do + evaluate(IF(literal(true), literal(5))).should == 5 + end + + it 'if false {5} == nil' do + evaluate(IF(literal(false), literal(5))).should == nil + end + + it 'if false {2} else {5} == 5' do + evaluate(IF(literal(false), literal(2), literal(5))).should == 5 + end + + it 'if false {2} elsif true {5} == 5' do + evaluate(IF(literal(false), literal(2), IF(literal(true), literal(5)))).should == 5 + end + + it 'if false {2} elsif false {5} == nil' do + evaluate(IF(literal(false), literal(2), IF(literal(false), literal(5)))).should == nil + end + end + + context "an unless expression" do + it 'should output the expected result when dumped' do + dump(UNLESS(literal(true), literal(2), literal(5))).should == unindent(<<-TEXT + (unless true + (then 2) + (else 5)) + TEXT + ) + end + + it 'unless false {5} == 5' do + evaluate(UNLESS(literal(false), literal(5))).should == 5 + end + + it 'unless true {5} == nil' do + evaluate(UNLESS(literal(true), literal(5))).should == nil + end + + it 'unless true {2} else {5} == 5' do + evaluate(UNLESS(literal(true), literal(2), literal(5))).should == 5 + end + + it 'unless true {2} elsif true {5} == 5' do + evaluate(UNLESS(literal(true), literal(2), IF(literal(true), literal(5)))).should == 5 + end + + it 'unless true {2} elsif false {5} == nil' do + evaluate(UNLESS(literal(true), literal(2), IF(literal(false), literal(5)))).should == nil + end + end + + context "a case expression" do + it 'should output the expected result when dumped' do + dump(CASE(literal(2), + WHEN(literal(1), literal('wat')), + WHEN([literal(2), literal(3)], literal('w00t')) + )).should == unindent(<<-TEXT + (case 2 + (when (1) (then 'wat')) + (when (2 3) (then 'w00t'))) + TEXT + ) + dump(CASE(literal(2), + WHEN(literal(1), literal('wat')), + WHEN([literal(2), literal(3)], literal('w00t')) + ).default(literal(4))).should == unindent(<<-TEXT + (case 2 + (when (1) (then 'wat')) + (when (2 3) (then 'w00t')) + (when (:default) (then 4))) + TEXT + ) + end + + it "case 1 { 1 : { 'w00t'} } == 'w00t'" do + evaluate(CASE(literal(1), WHEN(literal(1), literal('w00t')))).should == 'w00t' + end + + it "case 2 { 1,2,3 : { 'w00t'} } == 'w00t'" do + evaluate(CASE(literal(2), WHEN([literal(1), literal(2), literal(3)], literal('w00t')))).should == 'w00t' + end + + it "case 2 { 1,3 : {'wat'} 2: { 'w00t'} } == 'w00t'" do + evaluate(CASE(literal(2), + WHEN([literal(1), literal(3)], literal('wat')), + WHEN(literal(2), literal('w00t')))).should == 'w00t' + end + + it "case 2 { 1,3 : {'wat'} 5: { 'wat'} default: {'w00t'}} == 'w00t'" do + evaluate(CASE(literal(2), + WHEN([literal(1), literal(3)], literal('wat')), + WHEN(literal(5), literal('wat'))).default(literal('w00t')) + ).should == 'w00t' + end + + it "case 2 { 1,3 : {'wat'} 5: { 'wat'} } == nil" do + evaluate(CASE(literal(2), + WHEN([literal(1), literal(3)], literal('wat')), + WHEN(literal(5), literal('wat'))) + ).should == nil + end + + it "case 'banana' { 1,3 : {'wat'} /.*ana.*/: { 'w00t'} } == w00t" do + evaluate(CASE(literal('banana'), + WHEN([literal(1), literal(3)], literal('wat')), + WHEN(literal(/.*ana.*/), literal('w00t'))) + ).should == 'w00t' + end + + context "with regular expressions" do + it "should set numeric variables from the match" do + evaluate(CASE(literal('banana'), + WHEN([literal(1), literal(3)], literal('wat')), + WHEN(literal(/.*(ana).*/), var(1))) + ).should == 'ana' + end + end + end + + context "select expressions" do + it 'should output the expected result when dumped' do + dump(literal(2).select( + MAP(literal(1), literal('wat')), + MAP(literal(2), literal('w00t')) + )).should == "(? 2 (1 => 'wat') (2 => 'w00t'))" + end + + it "1 ? {1 => 'w00t'} == 'w00t'" do + evaluate(literal(1).select(MAP(literal(1), literal('w00t')))).should == 'w00t' + end + + it "2 ? {1 => 'wat', 2 => 'w00t'} == 'w00t'" do + evaluate(literal(2).select( + MAP(literal(1), literal('wat')), + MAP(literal(2), literal('w00t')) + )).should == 'w00t' + end + + it "3 ? {1 => 'wat', 2 => 'wat', default => 'w00t'} == 'w00t'" do + evaluate(literal(3).select( + MAP(literal(1), literal('wat')), + MAP(literal(2), literal('wat')), + MAP(literal(:default), literal('w00t')) + )).should == 'w00t' + end + + it "3 ? {1 => 'wat', default => 'w00t', 3 => 'wat'} == 'w00t'" do + evaluate(literal(3).select( + MAP(literal(1), literal('wat')), + MAP(literal(:default), literal('w00t')), + MAP(literal(2), literal('wat')) + )).should == 'w00t' + end + + it "should set numerical variables from match" do + evaluate(literal('banana').select( + MAP(literal(1), literal('wat')), + MAP(literal(/.*(ana).*/), var(1)) + )).should == 'ana' + end + end + end +end From a66b923eb2fe297d027b4c3f05c30153e18172b1 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 15 Sep 2013 04:14:08 +0200 Subject: [PATCH 022/800] (maint) Cleanup Scope impl from unused methods & test specific code This removes several unused methods and attributes from Puppet::Parser::Scope. The only places in code using them where tests. It also removes some "convenience"/"proxy" methods used by only the Collector (better for it to do this on its own than cluttering the Scope implementation) Also refactors creating scopes for the purpose of running tests by moving that logic from Scope to PuppetSpec::Scope that should be included in examples to use the no(now renamed) method Useless and tests that test that scope behaves the wrong ! way removed. (It is always illegal to set match data variables from anything but a match, but this functionality was tested by spec tests). It was then much clearer how match scope and local scope should work. Now a LocalScope always have a nested MatchScope that is transparent until a match is set, or a nested MatchScope is created for an inner scope. This enables rewriting the evaluator logic to not leak match scopes where there is a sequence of matches at top level in a define or class body. --- lib/puppet/parser/collector.rb | 2 +- lib/puppet/parser/functions.rb | 3 +- lib/puppet/parser/scope.rb | 202 +++++++++++------- spec/lib/puppet_spec/scope.rb | 14 ++ spec/spec_helper.rb | 34 +++ spec/unit/hiera/scope_spec.rb | 8 +- spec/unit/hiera_puppet_spec.rb | 5 +- spec/unit/parser/functions/fqdn_rand_spec.rb | 5 +- .../unit/parser/functions/hiera_array_spec.rb | 7 +- spec/unit/parser/functions/hiera_hash_spec.rb | 5 +- .../parser/functions/hiera_include_spec.rb | 9 +- spec/unit/parser/functions/hiera_spec.rb | 7 +- spec/unit/parser/functions/lookup_spec.rb | 5 +- spec/unit/parser/scope_spec.rb | 119 ++++++----- .../binder/hiera2/bindings_provider_spec.rb | 5 +- .../pops/parser/evaluating_parser_spec.rb | 4 +- 16 files changed, 283 insertions(+), 151 deletions(-) create mode 100644 spec/lib/puppet_spec/scope.rb diff --git a/lib/puppet/parser/collector.rb b/lib/puppet/parser/collector.rb index 07c462b7f..a5f4cb0ff 100644 --- a/lib/puppet/parser/collector.rb +++ b/lib/puppet/parser/collector.rb @@ -103,7 +103,7 @@ class Puppet::Parser::Collector # key is '#{type}/#{name}', and host and filter. found = Puppet::Resource.indirection. - search(@type, :host => @scope.host, :filter => @equery, :scope => @scope) + search(@type, :host => @scope.compiler.node.name, :filter => @equery, :scope => @scope) found_resources = found.map {|x| x.is_a?(Puppet::Parser::Resource) ? x : x.to_resource(@scope)} diff --git a/lib/puppet/parser/functions.rb b/lib/puppet/parser/functions.rb index 360387727..ab8f7038d 100644 --- a/lib/puppet/parser/functions.rb +++ b/lib/puppet/parser/functions.rb @@ -71,7 +71,8 @@ module Puppet::Parser::Functions # :doc=>"Doubles an object, typically a number or string."} # # @example Invoke the double function from irb as is done in RSpec examples: - # >> scope = Puppet::Parser::Scope.new_for_test_harness('example') + # >> require 'puppet_spec/scope' + # >> scope = PuppetSpec::Scope.create_test_scope_for_node('example') # => Scope() # >> scope.function_double([2]) # => 4 diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb index da353b918..b14abd411 100644 --- a/lib/puppet/parser/scope.rb +++ b/lib/puppet/parser/scope.rb @@ -24,11 +24,9 @@ class Puppet::Parser::Scope Puppet::Util.logmethods(self) - include Enumerable include Puppet::Util::Errors attr_accessor :source, :resource - attr_accessor :base, :keyword - attr_accessor :top, :translated, :compiler + attr_accessor :compiler attr_accessor :parent attr_reader :namespaces @@ -36,68 +34,103 @@ class Puppet::Parser::Scope # them frequently enough to justify the extra method call. def_delegators :compiler, :catalog, :environment - # thin wrapper around an ephemeral - # symbol table. - # when a symbol - class Ephemeral - extend Forwardable - def initialize(parent=nil, local=false) - @symbols = {} + # Abstract base class for LocalScope and MatchScope + # + class Ephemeral + + attr_reader :parent + + def initialize(parent = nil) @parent = parent - @local_scope = local end - def_delegators :@symbols, :delete, :[]=, :each + def is_local_scope? + false + end def [](name) - if @symbols.include?(name) or @parent.nil? - @symbols[name] - else + if @parent @parent[name] end end def include?(name) - bound?(name) or (@parent and @parent.include?(name)) + (@parent and @parent.include?(name)) + end + + def bound?(name) + false + end + + def add_entries_to(target = {}) + @parent.add_entries_to(target) unless @parent.nil? + # do not include match data ($0-$n) + target + end + end + + class LocalScope < Ephemeral + + def initialize(parent=nil) + super parent + @symbols = {} + end + + def [](name) + if @symbols.include?(name) + @symbols[name] + else + super + end + end + + def is_local_scope? + true + end + + def []=(name, value) + @symbols[name] = value + end + + def include?(name) + bound?(name) || super + end + + def delete(name) + @symbols.delete(name) end def bound?(name) @symbols.include?(name) end - def is_local_scope? - @local_scope - end - - # @return [Ephemeral, Hash, nil] - def parent - @parent - end - def add_entries_to(target = {}) - @parent.add_entries_to(target) unless @parent.nil? - # do not return pure ephemeral ($0-$n) - if is_local_scope? - @symbols.each do |k, v| - if v == :undef - target.delete(k) - else - target[ k ] = v - end + super + @symbols.each do |k, v| + if v == :undef + target.delete(k) + else + target[ k ] = v end end target end - end + class MatchScope < Ephemeral - def initialize(parent, match_data) - super(parent, false) + attr_accessor :match_data + + def initialize(parent = nil, match_data = nil) + super parent @match_data = match_data end + def is_local_scope? + false + end + def [](name) if bound?(name) @match_data[name.to_i] @@ -106,44 +139,38 @@ class Puppet::Parser::Scope end end - def bound?(name) - # A "match variables" scope reports all numeric variables to be bound - name =~ /^\d+$/ + def include?(name) + bound?(name) or super end - end - # Initialize a new scope suitable for parser function testing. This method - # should be considered a public API for external modules. A shared spec - # helper should consume this API method. - # - # @api protected - # - def self.new_for_test_harness(node_name) - node = Puppet::Node.new(node_name) - compiler = Puppet::Parser::Compiler.new(node) - scope = new(compiler) - scope.source = Puppet::Resource::Type.new(:node, node_name) - scope.parent = compiler.topscope - scope - end + def bound?(name) + # A "match variables" scope reports all numeric variables to be bound if the scope has + # match_data. Without match data the scope is transparent. + # + @match_data && name =~ /^\d+$/ + end - def each - to_hash.each { |name, value| yield(name, value) } - end + def []=(name, value) + # TODO: Bad choice of exception + raise Puppet::ParseError, "Numerical variables cannot be changed. Attempt to set $#{name}" + end - # Proxy accessors - def host - compiler.node.name - end + def delete(name) + # TODO: Bad choice of exception + raise Puppet::ParseError, "Numerical variables cannot be deleted: Attempt to delete: $#{name}" + end + + def add_entries_to(target = {}) + # do not include match data ($0-$n) + super + end - # TODO: 19514 - this is smelly; who uses this? functions? templates? - # What about trusted facts ? Should untrusted facts be removed from facts? - # - def facts - compiler.node.facts end # Returns true if the variable of the given name has a non nil value. + # TODO: This has vague semantics - does the variable exist or not? + # use ['name'] to get nil or value, and if nil check with exist?('name') + # this include? is only useful because of checking against the boolean value false. # def include?(name) ! self[name].nil? @@ -156,6 +183,7 @@ class Puppet::Parser::Scope end # Returns true if the given name is bound in the current (most nested) scope. + # def bound?(name) effective_symtable(true).bound?(name) end @@ -232,9 +260,10 @@ class Puppet::Parser::Scope extend_with_functions_module # The symbol table for this scope. This is where we store variables. - @symtable = Ephemeral.new(nil, true) + # @symtable = Ephemeral.new(nil, true) + @symtable = LocalScope.new(nil) - @ephemeral = [ Ephemeral.new(@symtable) ] + @ephemeral = [ MatchScope.new(@symtable, nil) ] # All of the defaults set for types. It's a hash of hashes, # with the first key being the type, then the second key being @@ -326,6 +355,8 @@ class Puppet::Parser::Scope class_name = $1 variable_name = $2 lookup_qualified_variable(class_name, variable_name, options) + + # TODO: optimize with an assoc instead, this searches through scopes twice for a hit elsif table.include?(name) table[name] else @@ -504,7 +535,7 @@ class Puppet::Parser::Scope # when you need to set options. def setvar(name, value, options = {}) if name =~ /^[0-9]+$/ - raise Puppet::ParseError.new("Cannot assign to a numeric match result variable '$#{name}'") unless options[:ephemeral] + raise Puppet::ParseError.new("Cannot assign to a numeric match result variable '$#{name}'") # unless options[:ephemeral] end unless name.is_a? String raise Puppet::ParseError, "Scope variable name #{name.inspect} is a #{name.class}, not a string" @@ -568,6 +599,7 @@ class Puppet::Parser::Scope s = @ephemeral.last return s if use_ephemeral + # Why check if ephemeral is a Hash ??? Not needed, a hash cannot be a parent scope ??? while s && !(s.is_a?(Hash) || s.is_local_scope?()) s = s.parent end @@ -613,27 +645,36 @@ class Puppet::Parser::Scope end # remove ephemeral scope up to level + # TODO: Who uses :all ? Remove ?? + # def unset_ephemeral_var(level=:all) if level == :all - @ephemeral = [ Ephemeral.new(@symtable)] + @ephemeral = [ MatchScope.new(@symtable, nil)] else @ephemeral.pop(@ephemeral.size - level) end end - # check if name exists in one of the ephemeral scopes. - def ephemeral_include?(name) - @ephemeral.any? {|eph| eph.include?(name) } - end - def ephemeral_level @ephemeral.size end + # TODO: Who calls this? def new_ephemeral(local_scope = false) - @ephemeral.push(Ephemeral.new(@ephemeral.last, local_scope)) + if local_scope + @ephemeral.push(LocalScope.new(@ephemeral.last)) + else + @ephemeral.push(MatchScope.new(@ephemeral.last, nil)) + end end + # Sets match data in the most nested scope (which always is a MatchScope), it clobbers match data already set there + # + def set_match_data(match_data) + @ephemeral.last.match_data = match_data + end + + # Nests a match data scope def new_match_scope(match_data) @ephemeral.push(MatchScope.new(@ephemeral.last, match_data)) end @@ -642,8 +683,12 @@ class Puppet::Parser::Scope case match when Hash # Create local scope ephemeral and set all values from hash - new_ephemeral true + new_ephemeral(true) match.each {|k,v| setvar(k, v, :file => file, :line => line, :ephemeral => true) } + # Must always have an inner match data scope (that starts out as transparent) + # In 3x slightly wasteful, since a new nested scope is created for a match + # (TODO: Fix that problem) + new_ephemeral(false) else raise(ArgumentError,"Invalid regex match data. Got a #{match.class}") unless match.is_a?(MatchData) # Create a match ephemeral and set values from match data @@ -665,6 +710,7 @@ class Puppet::Parser::Scope environment.known_resource_types.find_definition(namespaces, type.to_s.downcase) end + def method_missing(method, *args, &block) method.to_s =~ /^function_(.*)$/ name = $1 diff --git a/spec/lib/puppet_spec/scope.rb b/spec/lib/puppet_spec/scope.rb new file mode 100644 index 000000000..c14ab4755 --- /dev/null +++ b/spec/lib/puppet_spec/scope.rb @@ -0,0 +1,14 @@ + +module PuppetSpec::Scope + # Initialize a new scope suitable for testing. + # + def create_test_scope_for_node(node_name) + node = Puppet::Node.new(node_name) + compiler = Puppet::Parser::Compiler.new(node) + scope = Puppet::Parser::Scope.new(compiler) + scope.source = Puppet::Resource::Type.new(:node, node_name) + scope.parent = compiler.topscope + scope + end + +end \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 49fa482cf..e72fecee4 100755 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -90,9 +90,22 @@ RSpec.configure do |config| config.before :all do Puppet::Test::TestHelper.before_all_tests() + if ENV['PROFILE'] == 'all' + require 'ruby-prof' + RubyProf.start + end end config.after :all do + if ENV['PROFILE'] == 'all' + require 'ruby-prof' + result = RubyProf.stop + printer = RubyProf::CallTreePrinter.new(result) + open(File.join(ENV['PROFILEOUT'],"callgrind.all.#{Time.now.to_i}.trace"), "w") do |f| + printer.print(f) + end + end + Puppet::Test::TestHelper.after_all_tests() end @@ -160,4 +173,25 @@ RSpec.configure do |config| ENV['TMPDIR'] = oldtmpdir FileUtils.rm_rf(tmpdir) if Puppet::FileSystem::File.exist?(tmpdir) && tmpdir.to_s.start_with?(oldtmpdir) end + + if ENV['PROFILE'] + require 'ruby-prof' + + def profile + result = RubyProf.profile { yield } + name = example.metadata[:full_description].downcase.gsub(/[^a-z0-9_-]/, "-").gsub(/-+/, "-") + printer = RubyProf::CallTreePrinter.new(result) + open(File.join(ENV['PROFILEOUT'],"callgrind.#{name}.#{Time.now.to_i}.trace"), "w") do |f| + printer.print(f) + end + end + + config.around(:each) do |example| + if ENV['PROFILE'] == 'each' or (example.metadata[:profile] and ENV['PROFILE']) + profile { example.run } + else + example.run + end + end + end end diff --git a/spec/unit/hiera/scope_spec.rb b/spec/unit/hiera/scope_spec.rb index a60be78a9..d53b435ce 100644 --- a/spec/unit/hiera/scope_spec.rb +++ b/spec/unit/hiera/scope_spec.rb @@ -1,8 +1,12 @@ require 'spec_helper' require 'hiera/scope' +require 'puppet_spec/scope' + describe Hiera::Scope do - let(:real) { Puppet::Parser::Scope.new_for_test_harness("test_node") } + include PuppetSpec::Scope + + let(:real) { create_test_scope_for_node("test_node") } let(:scope) { Hiera::Scope.new(real) } describe "#initialize" do @@ -57,7 +61,7 @@ describe Hiera::Scope do end it "looks for the class which includes the defined type as the calling_class" do - parent = Puppet::Parser::Scope.new_for_test_harness("parent") + parent = create_test_scope_for_node("parent") real.parent = parent parent.source = Puppet::Resource::Type.new(:hostclass, "name_of_the_class_including_the_definition", diff --git a/spec/unit/hiera_puppet_spec.rb b/spec/unit/hiera_puppet_spec.rb index 894b6e334..545e8ace1 100644 --- a/spec/unit/hiera_puppet_spec.rb +++ b/spec/unit/hiera_puppet_spec.rb @@ -1,7 +1,10 @@ require 'spec_helper' require 'hiera_puppet' +require 'puppet_spec/scope' describe 'HieraPuppet' do + include PuppetSpec::Scope + describe 'HieraPuppet#hiera_config' do let(:hiera_config_data) do { :backend => 'yaml' } @@ -71,7 +74,7 @@ describe 'HieraPuppet' do end describe 'HieraPuppet#lookup' do - let :scope do Puppet::Parser::Scope.new_for_test_harness('foo') end + let :scope do create_test_scope_for_node('foo') end before :each do Puppet[:hiera_config] = PuppetSpec::Files.tmpfile('hiera_config') diff --git a/spec/unit/parser/functions/fqdn_rand_spec.rb b/spec/unit/parser/functions/fqdn_rand_spec.rb index cef161000..49a169661 100755 --- a/spec/unit/parser/functions/fqdn_rand_spec.rb +++ b/spec/unit/parser/functions/fqdn_rand_spec.rb @@ -1,7 +1,10 @@ #! /usr/bin/env ruby require 'spec_helper' +require 'puppet_spec/scope' describe "the fqdn_rand function" do + include PuppetSpec::Scope + it "provides a random number strictly less than the given max" do fqdn_rand(3).should satisfy {|n| n.to_i < 3 } end @@ -35,7 +38,7 @@ describe "the fqdn_rand function" do host = args[:host] || '127.0.0.1' extra = args[:extra_identifier] || [] - scope = Puppet::Parser::Scope.new_for_test_harness('localhost') + scope = create_test_scope_for_node('localhost') scope.stubs(:[]).with("::fqdn").returns(host) scope.function_fqdn_rand([max] + extra) diff --git a/spec/unit/parser/functions/hiera_array_spec.rb b/spec/unit/parser/functions/hiera_array_spec.rb index e8efb727f..ca9bb5c88 100644 --- a/spec/unit/parser/functions/hiera_array_spec.rb +++ b/spec/unit/parser/functions/hiera_array_spec.rb @@ -1,11 +1,14 @@ require 'spec_helper' +require 'puppet_spec/scope' describe 'Puppet::Parser::Functions#hiera_array' do + include PuppetSpec::Scope + before :each do Puppet[:hiera_config] = PuppetSpec::Files.tmpfile('hiera_config') end - let :scope do Puppet::Parser::Scope.new_for_test_harness('foo') end + let :scope do create_test_scope_for_node('foo') end it 'should require a key argument' do expect { scope.function_hiera_array([]) }.to raise_error(ArgumentError) @@ -17,7 +20,7 @@ describe 'Puppet::Parser::Functions#hiera_array' do end it 'should use the array resolution_type' do - Hiera.any_instance.expects(:lookup).with() { |*args| args[4].should be :array }.returns([]) + Hiera.any_instance.expects(:lookup).with() { |*args| args[4].should be(:array) }.returns([]) scope.function_hiera_array(['key']) end end diff --git a/spec/unit/parser/functions/hiera_hash_spec.rb b/spec/unit/parser/functions/hiera_hash_spec.rb index a345a6c7f..9b89b2efd 100644 --- a/spec/unit/parser/functions/hiera_hash_spec.rb +++ b/spec/unit/parser/functions/hiera_hash_spec.rb @@ -1,7 +1,10 @@ require 'spec_helper' +require 'puppet_spec/scope' describe 'Puppet::Parser::Functions#hiera_hash' do - let :scope do Puppet::Parser::Scope.new_for_test_harness('foo') end + include PuppetSpec::Scope + + let :scope do create_test_scope_for_node('foo') end it 'should require a key argument' do expect { scope.function_hiera_hash([]) }.to raise_error(ArgumentError) diff --git a/spec/unit/parser/functions/hiera_include_spec.rb b/spec/unit/parser/functions/hiera_include_spec.rb index 76a2c5dec..2909e07ae 100644 --- a/spec/unit/parser/functions/hiera_include_spec.rb +++ b/spec/unit/parser/functions/hiera_include_spec.rb @@ -1,7 +1,10 @@ require 'spec_helper' +require 'puppet_spec/scope' describe 'Puppet::Parser::Functions#hiera_include' do - let :scope do Puppet::Parser::Scope.new_for_test_harness('foo') end + include PuppetSpec::Scope + + let :scope do create_test_scope_for_node('foo') end before :each do Puppet[:hiera_config] = PuppetSpec::Files.tmpfile('hiera_config') @@ -18,8 +21,8 @@ describe 'Puppet::Parser::Functions#hiera_include' do end it 'should use the array resolution_type' do - HieraPuppet.expects(:lookup).with() { |*args| args[4].should be :array }.returns(['someclass']) - expect { scope.function_hiera_include(['key']) }.to raise_error Puppet::Error, /Could not find class someclass/ + HieraPuppet.expects(:lookup).with() { |*args| args[4].should be(:array) }.returns(['someclass']) + expect { scope.function_hiera_include(['key']) }.to raise_error(Puppet::Error, /Could not find class someclass/) end it 'should call the `include` function with the classes' do diff --git a/spec/unit/parser/functions/hiera_spec.rb b/spec/unit/parser/functions/hiera_spec.rb index f22eee9d4..db31fdf82 100755 --- a/spec/unit/parser/functions/hiera_spec.rb +++ b/spec/unit/parser/functions/hiera_spec.rb @@ -1,7 +1,10 @@ require 'spec_helper' +require 'puppet_spec/scope' describe 'Puppet::Parser::Functions#hiera' do - let :scope do Puppet::Parser::Scope.new_for_test_harness('foo') end + include PuppetSpec::Scope + + let :scope do create_test_scope_for_node('foo') end it 'should require a key argument' do expect { scope.function_hiera([]) }.to raise_error(ArgumentError) @@ -13,7 +16,7 @@ describe 'Puppet::Parser::Functions#hiera' do end it 'should use the priority resolution_type' do - Hiera.any_instance.expects(:lookup).with() { |*args| args[4].should be :priority }.returns('foo_result') + Hiera.any_instance.expects(:lookup).with() { |*args| args[4].should be(:priority) }.returns('foo_result') scope.function_hiera(['key']).should == 'foo_result' end end diff --git a/spec/unit/parser/functions/lookup_spec.rb b/spec/unit/parser/functions/lookup_spec.rb index a468ba7ec..dd62926c7 100644 --- a/spec/unit/parser/functions/lookup_spec.rb +++ b/spec/unit/parser/functions/lookup_spec.rb @@ -1,8 +1,11 @@ require 'spec_helper' require 'puppet/pops' require 'stringio' +require 'puppet_spec/scope' describe "lookup function" do + include PuppetSpec::Scope + before(:each) do Puppet[:binder] = true end @@ -63,7 +66,7 @@ describe "lookup function" do def scope_with_injections_from(binder) injector = Puppet::Pops::Binder::Injector.new(binder) - scope = Puppet::Parser::Scope.new_for_test_harness('testing') + scope = create_test_scope_for_node('testing') scope.compiler.injector = injector scope diff --git a/spec/unit/parser/scope_spec.rb b/spec/unit/parser/scope_spec.rb index 0865c4c7f..4ed6b8ee8 100755 --- a/spec/unit/parser/scope_spec.rb +++ b/spec/unit/parser/scope_spec.rb @@ -1,8 +1,11 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet_spec/compiler' +require 'puppet_spec/scope' describe Puppet::Parser::Scope do + include PuppetSpec::Scope + before :each do @scope = Puppet::Parser::Scope.new( Puppet::Parser::Compiler.new(Puppet::Node.new("foo")) @@ -12,26 +15,26 @@ describe Puppet::Parser::Scope do @scope.parent = @topscope end - describe ".new_for_test_harness" do + describe "create_test_scope_for_node" do let(:node_name) { "node_name_foo" } - let(:scope) { described_class.new_for_test_harness(node_name) } + let(:scope) { create_test_scope_for_node(node_name) } it "should be a kind of Scope" do - scope.should be_a_kind_of Puppet::Parser::Scope + scope.should be_a_kind_of(Puppet::Parser::Scope) end it "should set the source to a node resource" do - scope.source.should be_a_kind_of Puppet::Resource::Type + scope.source.should be_a_kind_of(Puppet::Resource::Type) end it "should have a compiler" do - scope.compiler.should be_a_kind_of Puppet::Parser::Compiler + scope.compiler.should be_a_kind_of(Puppet::Parser::Compiler) end it "should set the parent to the compiler topscope" do - scope.parent.should be scope.compiler.topscope + scope.parent.should be(scope.compiler.topscope) end end it "should return a scope for use in a test harness" do - described_class.new_for_test_harness("node_name_foo").should be_a_kind_of Puppet::Parser::Scope + create_test_scope_for_node("node_name_foo").should be_a_kind_of(Puppet::Parser::Scope) end it "should be able to retrieve class scopes by name" do @@ -187,17 +190,17 @@ describe Puppet::Parser::Scope do @scope.should_not be_include("var") end - it "should support iteration over its variables" do - @scope["one"] = "two" - @scope["three"] = "four" - hash = {} - @scope.each { |name, value| hash[name] = value } - hash.should == {"one" => "two", "three" => "four" } - end +# it "should support iteration over its variables" do +# @scope["one"] = "two" +# @scope["three"] = "four" +# hash = {} +# @scope.each { |name, value| hash[name] = value } +# hash.should == {"one" => "two", "three" => "four" } +# end - it "should include Enumerable" do - @scope.singleton_class.ancestors.should be_include(Enumerable) - end +# it "should include Enumerable" do +# @scope.singleton_class.ancestors.should be_include(Enumerable) +# end describe "and the variable is qualified" do before :each do @@ -389,42 +392,42 @@ describe Puppet::Parser::Scope do describe "when using ephemeral variables" do it "should store the variable value" do - @scope.setvar("1", :value, :ephemeral => true) - +# @scope.setvar("1", :value, :ephemeral => true) + @scope.set_match_data({1 => :value}) @scope["1"].should == :value end - it "should remove the variable value when unset_ephemeral_var is called" do - @scope.setvar("1", :value, :ephemeral => true) + it "should remove the variable value when unset_ephemeral_var(:all) is called" do +# @scope.setvar("1", :value, :ephemeral => true) + @scope.set_match_data({1 => :value}) @scope.stubs(:parent).returns(nil) - @scope.unset_ephemeral_var + @scope.unset_ephemeral_var(:all) @scope["1"].should be_nil end - it "should not remove classic variables when unset_ephemeral_var is called" do + it "should not remove classic variables when unset_ephemeral_var(:all) is called" do @scope['myvar'] = :value1 - @scope.setvar("1", :value2, :ephemeral => true) + @scope.set_match_data({1 => :value2}) @scope.stubs(:parent).returns(nil) - @scope.unset_ephemeral_var + @scope.unset_ephemeral_var(:all) @scope["myvar"].should == :value1 end - it "should raise an error when setting it again" do - @scope.setvar("1", :value2, :ephemeral => true) + it "should raise an error when setting numerical variable" do expect { @scope.setvar("1", :value3, :ephemeral => true) - }.to raise_error(Puppet::ParseError, /Cannot reassign variable 1/) + }.to raise_error(Puppet::ParseError, /Cannot assign to a numeric match result variable/) end describe "with more than one level" do it "should prefer latest ephemeral scopes" do - @scope.setvar("0", :earliest, :ephemeral => true) + @scope.set_match_data({0 => :earliest}) @scope.new_ephemeral - @scope.setvar("0", :latest, :ephemeral => true) + @scope.set_match_data({0 => :latest}) @scope["0"].should == :latest end @@ -434,52 +437,56 @@ describe Puppet::Parser::Scope do @scope.ephemeral_level.should == 2 end - it "should check presence of an ephemeral variable accross multiple levels" do + it "should not check presence of an ephemeral variable accross multiple levels" do + # This test was testing that scope actuallys screwed up - making values from earlier matches show as if they + # where true for latest match - insanity ! @scope.new_ephemeral - @scope.setvar("1", :value1, :ephemeral => true) + @scope.set_match_data({1 => :value1}) @scope.new_ephemeral - @scope.setvar("0", :value2, :ephemeral => true) + @scope.set_match_data({0 => :value2}) @scope.new_ephemeral - @scope.ephemeral_include?("1").should be_true + @scope.include?("1").should be_false end it "should return false when an ephemeral variable doesn't exist in any ephemeral scope" do @scope.new_ephemeral - @scope.setvar("1", :value1, :ephemeral => true) + @scope.set_match_data({1 => :value1}) @scope.new_ephemeral - @scope.setvar("0", :value2, :ephemeral => true) + @scope.set_match_data({0 => :value2}) @scope.new_ephemeral - @scope.ephemeral_include?("2").should be_false + @scope.include?("2").should be_false end - it "should get ephemeral values from earlier scope when not in later" do - @scope.setvar("1", :value1, :ephemeral => true) + it "should not get ephemeral values from earlier scope when not in later" do + @scope.set_match_data({1 => :value1}) @scope.new_ephemeral - @scope.setvar("0", :value2, :ephemeral => true) - @scope["1"].should == :value1 + @scope.set_match_data({0 => :value2}) + @scope.include?("1").should be_false end - describe "when calling unset_ephemeral_var without a level" do - it "should remove all the variables values" do - @scope.setvar("1", :value1, :ephemeral => true) - @scope.new_ephemeral - @scope.setvar("1", :value2, :ephemeral => true) - - @scope.unset_ephemeral_var - - @scope["1"].should be_nil - end - end +# # TODO: Stupid test - nothing but tests use the remove all ephemeral +# describe "when calling unset_ephemeral_var without a level" do +# it "should remove all the variables values" do +# @scope.setvar("1", :value1, :ephemeral => true) +# @scope.new_ephemeral +# @scope.setvar("1", :value2, :ephemeral => true) +# +# @scope.unset_ephemeral_var +# +# @scope["1"].should be_nil +# end +# end describe "when calling unset_ephemeral_var with a level" do it "should remove ephemeral scopes up to this level" do - @scope.setvar("1", :value1, :ephemeral => true) + @scope.set_match_data({1 => :value1}) @scope.new_ephemeral - @scope.setvar("1", :value2, :ephemeral => true) + @scope.set_match_data({1 => :value2}) + level = @scope.ephemeral_level() @scope.new_ephemeral - @scope.setvar("1", :value3, :ephemeral => true) + @scope.set_match_data({1 => :value3}) - @scope.unset_ephemeral_var(2) + @scope.unset_ephemeral_var(level) @scope["1"].should == :value2 end diff --git a/spec/unit/pops/binder/hiera2/bindings_provider_spec.rb b/spec/unit/pops/binder/hiera2/bindings_provider_spec.rb index edb4c7df2..b0924efab 100644 --- a/spec/unit/pops/binder/hiera2/bindings_provider_spec.rb +++ b/spec/unit/pops/binder/hiera2/bindings_provider_spec.rb @@ -1,10 +1,13 @@ require 'spec_helper' require 'puppet/pops' require 'puppet_spec/pops' +require 'puppet_spec/scope' + describe 'The hiera2 bindings provider' do include PuppetSpec::Pops + include PuppetSpec::Scope def config_dir(config_name) File.dirname(my_fixture("#{config_name}/hiera.yaml")) @@ -18,7 +21,7 @@ describe 'The hiera2 bindings provider' do let(:node) { 'node.example.com' } let(:acceptor) { Puppet::Pops::Validation::Acceptor.new() } - let(:scope) { s = Puppet::Parser::Scope.new_for_test_harness(node); s['a'] = '42'; s['node'] = node; s } + let(:scope) { s = create_test_scope_for_node(node); s['a'] = '42'; s['node'] = node; s } let(:module_dir) { config_dir('ok') } let(:node_binder) { b = Puppet::Pops::Binder::Binder.new(); b.define_categories(Puppet::Pops::Binder::BindingsFactory.categories(['node', node])); b } let(:bindings) { Puppet::Pops::Binder::Hiera2::BindingsProvider.new('test', module_dir, acceptor).load_bindings(scope) } diff --git a/spec/unit/pops/parser/evaluating_parser_spec.rb b/spec/unit/pops/parser/evaluating_parser_spec.rb index 027b86a09..555857847 100644 --- a/spec/unit/pops/parser/evaluating_parser_spec.rb +++ b/spec/unit/pops/parser/evaluating_parser_spec.rb @@ -1,14 +1,16 @@ require 'spec_helper' require 'puppet/pops' require 'puppet_spec/pops' +require 'puppet_spec/scope' describe 'The hiera2 string evaluator' do include PuppetSpec::Pops + include PuppetSpec::Scope let(:acceptor) { Puppet::Pops::Validation::Acceptor.new() } let(:diag) { Puppet::Pops::Binder::Hiera2::DiagnosticProducer.new(acceptor) } - let(:scope) { s = Puppet::Parser::Scope.new_for_test_harness(node); s } + let(:scope) { s = create_test_scope_for_node(node); s } let(:node) { 'node.example.com' } def quote(x) From 709b8439fb5e489c201ddb20cc7136023788c887 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 15 Sep 2013 04:39:15 +0200 Subject: [PATCH 023/800] (#22363) Add examples for match result variable shadowing There were examples missing that tests evaluation of match operator. (shadow / no shadow, and if failed match alters match data or not). --- spec/unit/pops/evaluator/variables_spec.rb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/spec/unit/pops/evaluator/variables_spec.rb b/spec/unit/pops/evaluator/variables_spec.rb index 79a5178f2..06080b175 100644 --- a/spec/unit/pops/evaluator/variables_spec.rb +++ b/spec/unit/pops/evaluator/variables_spec.rb @@ -146,6 +146,20 @@ describe 'Puppet::Pops::Impl::EvaluatorImpl' do [var(0), var(1), var(2), var(3)])).should == [nil, nil, nil, nil] end + it "a failed match does not alter previous match" do + evaluate_l(block( + literal('abc') =~ literal(/(a)(b)(c)/), + literal('abc') =~ literal(/(x)(y)(z)/), + [var(0), var(1), var(2), var(3)])).should == ['abc', 'a', 'b', 'c'] + end + + it "a new match completely shadows previous match" do + evaluate_l(block( + literal('abc') =~ literal(/(a)(b)(c)/), + literal('abc') =~ literal(/(a)bc/), + [var(0), var(1), var(2), var(3)])).should == ['abc', 'a', nil, nil] + end + it "after a match with variable referencing a non existing group" do evaluate_l(block(literal('abc') =~ literal(/(a)(b)(c)/), [var(0), var(1), var(2), var(3), var(4)])).should == ['abc', 'a', 'b', 'c', nil] From 0ccf3c9406bedbc60c33716fe89e6ecd62c4259d Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 15 Sep 2013 23:38:49 +0200 Subject: [PATCH 024/800] (#22363) Add tests of Block, and Array/Hash +, -, and << operators This also fixes an issue with interpretation of conversion of array to hash using two different forms [k,v,k,v] vs. [[k,v], [k,v]] - both are now supported. --- lib/puppet/pops/evaluator/evaluator_impl.rb | 14 ++- .../pops/evaluator/basic_expressions_spec.rb | 10 ++ .../pops/evaluator/collections_ops_spec.rb | 109 ++++++++++++++++++ 3 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 spec/unit/pops/evaluator/collections_ops_spec.rb diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index dbefaa950..5f514adf2 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -780,7 +780,19 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator when Hash y = case y when Hash then y - when Array then Hash[*y] + when Array + # Hash[[a, 1, b, 2]] => {} + # Hash[a,1,b,2] => {a => 1, b => 2} + # Hash[[a,1], [b,2]] => {[a,1] => [b,2]} + # Hash[[[a,1], [b,2]]] => {a => 1, b => 2} + # Use type calcultor to determine if array is Array[Array[?]], and if so use second form + # of call + t = @@type_calculator.infer(y) + if t.element_type.is_a? Puppet::Pops::Types::PArrayType + Hash[y] + else + Hash[*y] + end else raise ArgumentError.new("Can only append Array or Hash to a Hash") end diff --git a/spec/unit/pops/evaluator/basic_expressions_spec.rb b/spec/unit/pops/evaluator/basic_expressions_spec.rb index 367128e22..6ee46ac1a 100644 --- a/spec/unit/pops/evaluator/basic_expressions_spec.rb +++ b/spec/unit/pops/evaluator/basic_expressions_spec.rb @@ -90,4 +90,14 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do evaluate(literal({:a => 1, :b => 2})[:b]).should == 2 end end + + context 'When the evaluator evaluates a Block' do + it 'an empty block evaluates to nil' do + evaluate(block()).should == nil + end + + it 'a block evaluates to its last expression' do + evaluate(block(literal(1), literal(2))).should == 2 + end + end end diff --git a/spec/unit/pops/evaluator/collections_ops_spec.rb b/spec/unit/pops/evaluator/collections_ops_spec.rb new file mode 100644 index 000000000..da317c790 --- /dev/null +++ b/spec/unit/pops/evaluator/collections_ops_spec.rb @@ -0,0 +1,109 @@ +#! /usr/bin/env ruby +require 'spec_helper' + +require 'puppet/pops' +require 'puppet/pops/evaluator/evaluator_impl' +require 'puppet/pops/types/type_factory' + + +# relative to this spec file (./) does not work as this file is loaded by rspec +require File.join(File.dirname(__FILE__), '/evaluator_rspec_helper') + +describe 'Puppet::Pops::Evaluator::EvaluatorImpl/Concat/Delete' do + include EvaluatorRspecHelper + + context 'The evaluator when operating on an Array' do + it 'concatenates another array using +' do + expect(evaluate(literal([1,2,3]) + literal([4,5]))).to eql([1,2,3,4,5]) + end + + it 'concatenates another nested array using +' do + expect(evaluate(literal([1,2,3]) + literal([[4,5]]))).to eql([1,2,3,[4,5]]) + end + + it 'concatenates a hash by converting it to array' do + expect(evaluate(literal([1,2,3]) + literal({'a' => 1, 'b'=>2}))).to eql([1,2,3,['a',1],['b',2]]) + end + + it 'concatenates a non array value with +' do + expect(evaluate(literal([1,2,3]) + literal(4))).to eql([1,2,3,4]) + end + + it 'appends another array using <<' do + expect(evaluate(literal([1,2,3]) << literal([4,5]))).to eql([1,2,3,[4,5]]) + end + + it 'appends a hash without conversion when << operator is used' do + expect(evaluate(literal([1,2,3]) << literal({'a' => 1, 'b'=>2}))).to eql([1,2,3,{'a' => 1, 'b'=>2}]) + end + + it 'appends another non array using <<' do + expect(evaluate(literal([1,2,3]) << literal(4))).to eql([1,2,3,4]) + end + + it 'computes the difference with another array using -' do + expect(evaluate(literal([1,2,3,4]) - literal([2,3]))).to eql([1,4]) + end + + it 'computes the difference with a non array using -' do + expect(evaluate(literal([1,2,3,4]) - literal(2))).to eql([1,3,4]) + end + + it 'does not recurse into nested arrays when computing diff' do + expect(evaluate(literal([1,2,3,[2],4]) - literal(2))).to eql([1,3,[2],4]) + end + + it 'can compute diff with sub arrays' do + expect(evaluate(literal([1,2,3,[2,3],4]) - literal([[2,3]]))).to eql([1,2,3,4]) + end + + it 'computes difference by removing all matching instances' do + expect(evaluate(literal([1,2,3,3,2,4,2,3]) - literal([2,3]))).to eql([1,4]) + end + + it 'computes difference with a hash by converting it to an array' do + expect(evaluate(literal([1,2,3,['a',1],['b',2]]) - literal({'a' => 1, 'b'=>2}))).to eql([1,2,3]) + end + + it 'diffs hashes when given in an array' do + expect(evaluate(literal([1,2,3,{'a'=>1,'b'=>2}]) - literal([{'a' => 1, 'b'=>2}]))).to eql([1,2,3]) + end + + it 'raises and error when LHS of << is a hash' do + expect { + evaluate(literal({'a' => 1, 'b'=>2}) << literal(1)) + }.to raise_error(/Left operand in '<<' expression is not an Array/) + end + end + + context 'The evaluator when operating on a Hash' do + it 'merges with another Hash using +' do + expect(evaluate(literal({'a' => 1, 'b'=>2}) + literal({'c' => 3}))).to eql({'a' => 1, 'b'=>2, 'c' => 3}) + end + + it 'merges RHS on top of LHS ' do + expect(evaluate(literal({'a' => 1, 'b'=>2}) + literal({'c' => 3, 'b'=>3}))).to eql({'a' => 1, 'b'=>3, 'c' => 3}) + end + + it 'merges a flat array of pairs converted to a hash' do + expect(evaluate(literal({'a' => 1, 'b'=>2}) + literal(['c', 3, 'b', 3]))).to eql({'a' => 1, 'b'=>3, 'c' => 3}) + end + + it 'merges an array converted to a hash' do + expect(evaluate(literal({'a' => 1, 'b'=>2}) + literal([['c', 3], ['b', 3]]))).to eql({'a' => 1, 'b'=>3, 'c' => 3}) + end + + it 'computes difference with another hash using the - operator' do + expect(evaluate(literal({'a' => 1, 'b'=>2}) - literal({'b' => 3}))).to eql({'a' => 1 }) + end + + it 'computes difference with an array by treating array as array of keys' do + expect(evaluate(literal({'a' => 1, 'b'=>2,'c'=>3}) - literal(['b', 'c']))).to eql({'a' => 1 }) + end + + it 'computes difference with a non array/hash by treating it as a key' do + expect(evaluate(literal({'a' => 1, 'b'=>2,'c'=>3}) - literal('c'))).to eql({'a' => 1, 'b' => 2 }) + end + end + +end From 34f10b54ecfb8de65c871693569b136405303eb4 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 16 Sep 2013 00:18:41 +0200 Subject: [PATCH 025/800] (#22363) Add support for -= operator (validate as error in puppet 3). This adds support for the -= operator (deletes) and adds validation that it is not supported in puppet 3. --- lib/puppet/pops/evaluator/evaluator_impl.rb | 6 +- lib/puppet/pops/issues.rb | 6 + lib/puppet/pops/model/ast_transformer.rb | 8 +- lib/puppet/pops/model/factory.rb | 5 + lib/puppet/pops/model/model.rb | 2 +- lib/puppet/pops/parser/egrammar.ra | 3 +- lib/puppet/pops/parser/eparser.rb | 1758 ++++++++++--------- lib/puppet/pops/parser/lexer.rb | 1 + lib/puppet/pops/validation/checker3_1.rb | 1 + 9 files changed, 916 insertions(+), 874 deletions(-) diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index 5f514adf2..ad8f5da78 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -97,7 +97,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # @example In Puppet DSL # $name = value # @param name [String] name of variable without $ - # @param value [Object] value to assign to the varible + # @param value [Object] value to assign to the variable # @param o [Puppet::Pops::Model::PopsObject] originating instruction # @param scope [Object] the runtime specific scope where evaluation should take place # @return [value] @@ -111,6 +111,10 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator value end + def assign_Number(n, value, o, scope) + fail("Cannot assign to the numeric (match result) variable: $#{name}", o.left_expr, scope) + end + # Assign values to named variables in an array. # The '$' sign is never part of the name. # @example Puppet DSL diff --git a/lib/puppet/pops/issues.rb b/lib/puppet/pops/issues.rb index 61663b167..f0d737b62 100644 --- a/lib/puppet/pops/issues.rb +++ b/lib/puppet/pops/issues.rb @@ -182,6 +182,12 @@ module Puppet::Pops::Issues "Illegal attempt to assign to #{label.a_an(semantic)} via [index/key]. Not an assignable reference" end + # For unsupported operators (e.g. -= in puppet 3). + # + UNSUPPORTED_OPERATOR = hard_issue :UNSUPPORTED_OPERATOR, :operator do + "The operator #{operator} in #{label.a_an(semantic)} is not supported in this version of the puppet language." + end + # Some expressions/statements may not produce a value (known as right-value, or rvalue). # This may vary between puppet versions. # diff --git a/lib/puppet/pops/model/ast_transformer.rb b/lib/puppet/pops/model/ast_transformer.rb index 2d4504050..eb90ca5b5 100644 --- a/lib/puppet/pops/model/ast_transformer.rb +++ b/lib/puppet/pops/model/ast_transformer.rb @@ -277,7 +277,13 @@ class Puppet::Pops::Model::AstTransformer # Assignment in AST 3.1 is to variable or hasharray accesses !!! See Bug #16116 def transform_AssignmentExpression(o) args = {:value => transform(o.right_expr) } - args[:append] = true if o.operator == :'+=' + case o.operator + when :'+=' + args[:append] = true + when :'=' + else + raise "The operator #{o.operator} is not supported by Puppet 3." + end args[:name] = case o.left_expr when Model::VariableExpression diff --git a/lib/puppet/pops/model/factory.rb b/lib/puppet/pops/model/factory.rb index 3cfa15fca..1bfa5d405 100644 --- a/lib/puppet/pops/model/factory.rb +++ b/lib/puppet/pops/model/factory.rb @@ -398,6 +398,11 @@ class Puppet::Pops::Model::Factory f_build_binary_op(Model::AssignmentExpression, :'+=', current, r) end + # Assignment -= + def minus_set(r) + f_build_binary_op(Model::AssignmentExpression, :'-=', current, r) + end + def attributes(*args) args.each {|a| current.addAttributes(build(a)) } self diff --git a/lib/puppet/pops/model/model.rb b/lib/puppet/pops/model/model.rb index 5f5302be3..3f4d9f98a 100644 --- a/lib/puppet/pops/model/model.rb +++ b/lib/puppet/pops/model/model.rb @@ -89,7 +89,7 @@ module Puppet::Pops::Model # An assignment expression assigns a value to the lval() of the left_expr. # class AssignmentExpression < BinaryExpression - has_attr 'operator', RGen::MetamodelBuilder::DataTypes::Enum.new([:'=', :'+=']), :lowerBound => 1 + has_attr 'operator', RGen::MetamodelBuilder::DataTypes::Enum.new([:'=', :'+=', :'-=']), :lowerBound => 1 end # An arithmetic expression applies an arithmetic operator on left and right expressions. diff --git a/lib/puppet/pops/parser/egrammar.ra b/lib/puppet/pops/parser/egrammar.ra index b45cd2cc3..23f6d4a63 100644 --- a/lib/puppet/pops/parser/egrammar.ra +++ b/lib/puppet/pops/parser/egrammar.ra @@ -40,7 +40,7 @@ prechigh left GREATEREQUAL GREATERTHAN LESSTHAN LESSEQUAL left AND left OR - right APPENDS EQUALS + right APPENDS DELETES EQUALS left LBRACE left SELBRACE left RBRACE @@ -113,6 +113,7 @@ expression | expression OR expression { result = val[0].or val[2] ; loc result, val[1] } | expression EQUALS expression { result = val[0].set(val[2]) ; loc result, val[1] } | expression APPENDS expression { result = val[0].plus_set(val[2]) ; loc result, val[1] } + | expression DELETES expression { result = val[0].minus_set(val[2]); loc result, val[1] } | expression QMARK selector_entries { result = val[0].select(*val[2]) ; loc result, val[0] } | LPAREN expression RPAREN { result = val[1].paren() ; loc result, val[0] } diff --git a/lib/puppet/pops/parser/eparser.rb b/lib/puppet/pops/parser/eparser.rb index 6d71e7b7e..2d005c99a 100644 --- a/lib/puppet/pops/parser/eparser.rb +++ b/lib/puppet/pops/parser/eparser.rb @@ -20,7 +20,7 @@ module Puppet module Parser class Parser < Racc::Parser -module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 699) +module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 700) # Make emacs happy # Local Variables: @@ -30,150 +30,154 @@ module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 699) ##### State transition tables begin ### clist = [ -'68,215,228,229,-126,230,202,229,238,87,88,84,79,90,290,94,262,89,-124', -'68,80,82,81,83,68,217,51,53,51,53,224,223,90,239,94,-192,89,90,93,94', -'199,89,86,85,-126,212,72,73,75,74,77,78,276,70,71,51,53,93,-124,68,69', -'202,93,117,-201,54,119,76,87,88,84,79,90,240,94,-192,89,70,71,80,82', -'81,83,68,69,59,229,59,51,53,292,212,117,109,312,119,90,93,94,112,89', -'86,85,111,-201,72,73,75,74,77,78,294,70,71,59,51,53,279,68,69,112,93', -'51,53,111,54,76,87,88,84,79,90,307,94,306,89,70,71,80,82,81,83,112,69', -'112,219,111,59,111,321,218,278,117,112,112,119,93,111,111,117,86,85', -'119,275,72,73,75,74,77,78,220,70,71,221,59,68,68,307,69,306,238,59,189', -'299,300,76,84,79,90,90,94,94,89,89,301,80,82,81,83,51,53,202,68,165', -'51,53,284,64,66,65,67,125,304,93,93,90,236,94,85,89,308,72,73,75,74', -'77,78,310,70,71,222,261,68,236,238,69,54,317,318,260,93,54,76,84,79', -'90,260,94,63,89,63,131,80,82,81,83,68,102,254,327,253,198,113,252,330', -'102,103,238,102,90,93,94,334,89,310,336,337,338,72,73,75,74,77,78,339', -'70,71,99,342,343,344,68,69,91,93,236,63,60,351,76,87,88,84,79,90,352', -'94,353,89,70,71,80,82,81,83,68,69,354,,,,,,,,,,79,90,93,94,,89,86,85', -'80,,72,73,75,74,77,78,,70,71,,,,,,69,,93,,,,,76,68,,72,73,75,74,77,78', -',70,71,,79,90,,94,69,89,,,80,,,76,68,,,,,,,,,,,,79,90,93,94,,89,,,80', -',72,73,75,74,77,78,,70,71,,,,,,69,,93,,,,,76,68,,72,73,75,74,77,78,', -'70,71,,79,90,,94,69,89,,,80,,,76,68,,,,,,,,,,,,,90,93,94,,89,,,,,72', -'73,75,74,77,78,,70,71,,,,,,69,,93,,,,,76,,,72,73,75,74,77,78,,70,71', -',,,,68,69,,,,,,,76,87,88,84,79,90,,94,,89,,,80,82,81,83,68,,,,,,,,,', -',,,90,93,94,,89,86,85,,,72,73,75,74,77,78,,70,71,,,,,,69,,93,,,,68,76', -',,72,73,75,74,77,78,,70,71,90,,94,,89,69,,,,,,68,76,,,,,,,,,,,,90,93', -'94,,89,,,,,72,73,75,74,,,,70,71,,,,,,69,,93,,,,,76,,,72,73,75,74,,,', -'70,71,,,,,68,69,,,,,,,76,87,88,84,79,90,234,94,,89,,,80,82,81,83,,,', -',,,,,,,,,,,93,,,,86,85,,,72,73,75,74,77,78,,70,71,,,,,68,69,,,,,,,76', -'87,88,84,79,90,,94,,89,,,80,82,81,83,,,,,,,,,,,,,,,93,,,,86,85,,,72', -'73,75,74,77,78,,70,71,,,,,68,69,,,,,,,76,87,88,84,79,90,,94,,89,,,80', -'82,81,83,68,,,,,,,,,,,,,90,93,94,,89,86,85,,,72,73,75,74,77,78,,70,71', -',,,,,69,,93,,,,,76,,,,,75,74,,,,70,71,,,,,68,69,,,,,,,76,87,88,84,79', -'90,,94,,89,,,80,82,81,83,68,,,,,,,,,,,,,90,93,94,,89,86,85,,,72,73,75', -'74,77,78,,70,71,,,,,,69,,93,,,,,76,,,,,75,74,,,,70,71,,,,,68,69,,,,', -',,76,87,88,84,79,90,,94,,89,,,80,82,81,83,,,,,,,,,,,,,,,93,,,,86,85', -',,72,73,75,74,77,78,,70,71,,,,,68,69,,,,,,,76,87,88,84,79,90,,94,,89', -',,80,82,81,83,,,,,,,,,,,,,,,93,,,,86,85,,,72,73,75,74,77,78,,70,71,', -',,,68,69,,,,,,,76,87,88,84,79,90,,94,,89,,,80,82,81,83,,,,,,,,,,,,,', -',93,,,,86,85,,,72,73,75,74,77,78,,70,71,,,,,68,69,,,,,,,76,87,88,84', -'79,90,,94,,89,,,80,82,81,83,,,,,,,,,,,,,,,93,,,,86,85,,,72,73,75,74', -'77,78,,70,71,,,,,68,69,,,,,,,76,87,88,84,79,90,,94,,89,,,80,82,81,83', -',,,,,,,,,,,,,,93,,,,86,85,,,72,73,75,74,77,78,,70,71,,,,,68,69,208,', -',,,,76,87,88,84,79,90,,94,,89,,,80,82,81,83,,,,,,,,,,,,,,,93,,,,86,85', -',,72,73,75,74,77,78,,70,71,,,,,68,69,207,,,,,,76,87,88,84,79,90,,94', -',89,,,80,82,81,83,,,,,,,,,,,,,,,93,,,,86,85,,,72,73,75,74,77,78,,70', -'71,,,,,68,69,206,,,,,,76,87,88,84,79,90,,94,,89,,,80,82,81,83,,,,,,', -',,,,,,,,93,,,,86,85,,,72,73,75,74,77,78,,70,71,,,,,68,69,205,,,,,,76', -'87,88,84,79,90,,94,,89,,,80,82,81,83,,,,,,,,,,,,,,,93,,,,86,85,,,72', -'73,75,74,77,78,,70,71,,,,,68,69,,,,,,,76,87,88,84,79,90,,94,,89,,194', -'80,82,81,83,,,,,,,,,,51,53,,,47,93,48,,,86,85,,,72,73,75,74,77,78,,70', -'71,13,,,,,69,38,,44,,46,96,76,45,58,54,,40,57,,,,55,12,51,53,56,,47', -'11,48,,,,,,,59,,,,,,39,,164,13,,,,,,167,184,178,185,46,179,187,180,176', -'174,,169,182,,,,55,12,188,183,181,51,53,11,,47,,48,324,,,59,,,,,186', -'168,,,,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56', -',47,11,48,313,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40', +'68,-127,204,51,53,232,217,51,53,87,88,84,79,91,286,95,-125,90,51,53', +'80,82,81,83,241,-193,51,53,278,230,219,240,51,53,231,-202,201,204,94', +'231,54,-127,86,85,54,292,72,73,75,74,77,78,264,70,71,118,-125,309,120', +'308,69,68,59,118,242,-193,120,76,89,54,87,88,84,79,91,-202,95,59,90', +'51,53,80,82,81,83,59,110,51,53,51,53,59,231,309,113,308,126,221,112', +'94,314,323,220,86,85,226,225,72,73,75,74,77,78,113,70,71,118,112,214', +'120,222,69,68,223,118,294,54,120,76,89,214,87,88,84,79,91,68,95,59,90', +'296,68,80,82,81,83,59,281,113,91,280,95,112,90,91,113,95,113,90,112', +'94,112,113,277,86,85,112,240,72,73,75,74,77,78,94,70,71,191,301,94,302', +'303,69,68,68,64,66,65,67,76,89,204,87,88,84,79,91,91,95,95,90,90,167', +'80,82,81,83,306,238,310,312,224,263,238,240,319,320,262,262,63,63,94', +'94,132,103,86,85,256,329,72,73,75,74,77,78,255,70,71,200,114,254,332', +'103,69,68,104,240,103,336,312,76,89,338,87,88,84,79,91,339,95,340,90', +'341,196,80,82,81,83,100,344,345,346,238,63,60,353,354,355,356,,,,94', +',,,86,85,,,72,73,75,74,77,78,,70,71,,,,,,69,68,,92,,,,76,89,,87,88,84', +'79,91,,95,,90,,,80,82,81,83,,,,,,,,,,,,,,,94,,,,86,85,,,72,73,75,74', +'77,78,,70,71,,,,,,69,68,,207,,,,76,89,,87,88,84,79,91,,95,,90,,,80,82', +'81,83,,,,,,,,,,,,,,,94,,,,86,85,,,72,73,75,74,77,78,,70,71,,,,,,69,68', +',208,,,,76,89,,87,88,84,79,91,,95,,90,,,80,82,81,83,,,,,,,,,,,,,,,94', +',,,86,85,,,72,73,75,74,77,78,,70,71,,,,,,69,68,,209,,,,76,89,,87,88', +'84,79,91,,95,,90,,,80,82,81,83,,,,,,,,,,,,,,,94,,,,86,85,,,72,73,75', +'74,77,78,,70,71,,,,,,69,68,,210,,,,76,89,,87,88,84,79,91,,95,,90,,,80', +'82,81,83,,,,,,,,,,,,,,,94,,,,86,85,,,72,73,75,74,77,78,,70,71,,,,,,69', +'68,,,,,,76,89,,87,88,84,79,91,,95,,90,,,80,82,81,83,,,,,,,,,,,,,,,94', +',,,86,85,,,72,73,75,74,77,78,,70,71,,,,,,69,68,,,,,,76,89,,87,88,84', +'79,91,,95,,90,,,80,82,81,83,,,,,,,,,,,,,,,94,,,,86,85,,,72,73,75,74', +'77,78,,70,71,,,,,,69,68,,,,,,76,89,,87,88,84,79,91,,95,,90,,,80,82,81', +'83,,,,,,,,,,,,,,,94,,,,86,85,,,72,73,75,74,77,78,,70,71,,,,,,69,68,', +',,,,76,89,,87,88,84,79,91,,95,,90,,,80,82,81,83,,,,,,,,,,,,,,,94,,,', +'86,85,,,72,73,75,74,77,78,,70,71,,,,,,69,68,,,,,,76,89,,87,88,84,79', +'91,,95,,90,,,80,82,81,83,,,,,,,,,,,,,,,94,,,,86,85,,,72,73,75,74,77', +'78,,70,71,,,,,,69,68,,,,,,76,89,,87,88,84,79,91,,95,,90,,,80,82,81,83', +',,,,,,,,,,,,,,94,,,,86,85,,,72,73,75,74,77,78,,70,71,,,,,,69,68,,,,', +',76,89,,87,88,84,79,91,,95,,90,,,80,82,81,83,,,,,,,,,,,,,,,94,,,,86', +'85,,,72,73,75,74,77,78,,70,71,,,,,,69,68,,,,,,76,89,,87,88,84,79,91', +',95,,90,,,80,82,81,83,,,,,,,,,,,,,,,94,,,,86,85,,,72,73,75,74,77,78', +',70,71,,,,,,69,68,,,,,,76,89,,87,88,84,79,91,236,95,,90,,,80,82,81,83', +',,,,,,,,,,,,,,94,,,,86,85,,,72,73,75,74,77,78,,70,71,,,,,,69,68,,,,', +',76,89,,87,88,84,79,91,,95,,90,,,80,82,81,83,68,,,,,,,,,,,,,91,94,95', +',90,86,85,,,72,73,75,74,77,78,,70,71,,,,,,69,,94,,,,68,76,89,,,,75,74', +',,,70,71,91,,95,,90,69,,68,,,,,76,,,,,,,,91,,95,,90,94,,,,,,68,,72,73', +'75,74,,,,70,71,,,91,94,95,69,90,,,,,,76,75,74,,68,,70,71,,,,,,69,,94', +',91,,95,76,90,,72,73,75,74,,68,,70,71,,,,,,69,,,,91,94,95,76,90,,,,', +'72,73,75,74,77,78,,70,71,,,,,,69,,94,,,,,76,68,,72,73,75,74,77,78,,70', +'71,,79,91,,95,69,90,,,80,,,76,68,,,,,,,,,,,,79,91,94,95,,90,,,80,,72', +'73,75,74,77,78,,70,71,,,,,,69,,94,,,,,76,68,,72,73,75,74,77,78,,70,71', +',79,91,,95,69,90,,,80,,,76,68,,,,,,,,,,,,79,91,94,95,,90,,,80,,72,73', +'75,74,77,78,,70,71,,,,,,69,,94,,,,,76,,,72,73,75,74,77,78,,70,71,,,68', +',,69,,,,,,,76,84,79,91,,95,,90,,,80,82,81,83,,,,,68,,,,,,,,,,94,,,91', +',95,,90,72,73,75,74,77,78,,70,71,,,68,,,69,,,,,,94,76,84,79,91,,95,', +'90,,,80,82,81,83,70,71,,,,,,69,,51,53,,,47,94,48,,,,85,,,72,73,75,74', +'77,78,,70,71,13,,,,,69,38,,44,,46,97,76,45,58,54,,40,57,,,,55,12,51', +'53,56,,47,11,48,,,,,,,59,,,,,,39,,166,13,,,,,,169,186,180,187,46,181', +'189,182,178,176,,171,184,,,,55,12,190,185,183,51,53,11,,47,,48,315,', +',59,,,,,188,170,,,,,,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12', +'51,53,56,68,47,11,48,,,,,,,59,,,91,,95,39,90,,13,,,,,,38,,44,,46,97', +',45,58,54,,40,57,94,,,55,12,51,53,56,68,47,11,48,,,,70,71,,59,,,91,69', +'95,39,90,,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,94,,,55,12,51,53,56', +',47,11,48,,,,70,71,,59,,,,69,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,', +'40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44', +',46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39', +',,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48', +',,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51', +'53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,', +'40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44', +',46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39', +',,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48', +',,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51', +'53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,', +'40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44', +',46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39', +',,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48', +',,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51', +'53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,', +'40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44', +',46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39', +',,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48', +',,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51', +'53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,', +'40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44', +',46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39', +',,13,,,,,,169,186,180,187,46,181,189,182,178,176,,171,184,,,,55,12,190', +'185,183,51,53,11,,47,,48,,,,59,,,,,188,170,,,,,,13,,,,,,38,,44,,46,97', +',45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,', +',,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,', +',59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51,53', +'56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,,40', '57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46', -'96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13', -',,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,', -',,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53', -'56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40', -'57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46', -'96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13', -',,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,', -',,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53', -'56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40', -'57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46', -'96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13', -',,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,', -',,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53', -'56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40', -'57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46', -'96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13', -',,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,', -',,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53', -'56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40', -'57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46', -'96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13', -',,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,', -',,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53', -'56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,167,184,178,185,46,179,187,180', -'176,174,,169,182,,,,55,12,188,183,181,51,53,11,,47,,48,,,,59,,,,,186', -'168,,,,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47', -'11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55', -'12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58', -'54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38', -',44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,', -',,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47', -'11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55', -'12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58', -'54,,40,57,43,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,196,,', -',,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59', -',,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,', -'47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,', -',,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,204,,,,,38,,44,,46', -'96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13', -',,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47,11,48,,', -',,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51', -'53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,', -'40,57,43,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44', -',46,42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,', -'39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11', -'48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12', -'51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54', -',40,57,,,,55,12,,,56,51,53,11,,47,283,48,,,,59,,,,,,39,,,,,,13,,,,,', -'38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,326,,,,,', -'59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53', -'56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40', -'57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46', -'96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,266,,,,,,59,,,,,,39', +'97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13', +',,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,', +',,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51,53', +'56,,47,11,48,,,,,,,59,,,,,,39,,,13,198,,,,,38,,44,,46,97,,45,58,54,', +'40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44', +',46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39', +',,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48', +',,,,,,59,,,,,,39,,,13,206,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12', +'51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54', +',40,57,43,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,', +'44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47,11,48,,,,,,,59,,', +',,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56,', +'47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43', +',,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97', +',45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,', +',,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,', +',59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,,,56,51', +'53,11,,47,285,48,,,,59,,,,,,39,,,,,,13,,,,,,38,,44,,46,97,,45,58,54', +',40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44', +',46,42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47,11,48,326,,,,,,59,,', +',,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56,', +'47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,', +',,55,12,51,53,56,,47,11,48,268,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46', +'42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47,11,48,328,,,,,,59,,,,,,39', ',,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47,11', -'48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55', -'12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58', -'54,,40,57,,,,55,12,51,53,56,,47,11,48,258,,,,,,59,,,,,,39,,,13,,,,,', -'38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47,11,48,,,,,,,59', -',,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,', -'47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,', -',,55,12,,,56,51,53,11,,47,123,48,,,,59,,,,,,39,,,,,,13,,,,,,38,,44,', -'46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39', -',,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48', -',,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51', -'53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,', -'40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44', -',46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39', -',,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48', -',,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51', -'53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,', -'40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44', -',46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,341,,,,,,59,,,,', -',39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47', -'11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55', -'12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58', +'48,266,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,', +',55,12,51,53,56,,47,11,48,260,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42', +',45,58,54,,40,57,43,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13', +',,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47,11,48,,', +',,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51', +'53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,', +'40,57,,,,55,12,,,56,51,53,11,,47,124,48,,,,59,,,,,,39,,,,,,13,,,,,,38', +',44,,46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,', +',,39,,,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47', +'11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55', +'12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58', '54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38', -',44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,346,,,,,,59', +',44,,46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,', +',,39,,,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47', +'11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55', +'12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58', +'54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38', +',44,,46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,343,,,,,,59', ',,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56', -',47,11,48,348,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40', +',47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,,40,57', +',,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97', +',45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,', +',,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,348,', +',,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51', +'53,56,,47,11,48,350,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54', +',40,57,43,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,', +'44,,46,42,,45,58,54,61,40,57,43,,,55,12,51,53,56,,47,11,48,352,,,,,', +'59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53', +'56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40', '57,43,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,', -'46,42,,45,58,54,61,40,57,43,,,55,12,51,53,56,,47,11,48,350,,,,,,59,', -',,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56', -',47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57', -'43,,,55,12,51,53,56,,47,11,48,264,,,,,,59,,,,,,39,,,13,,,,,,38,,44,', -'46,42,,45,58,54,,40,57,43,,,55,12,,,56,,,11,,,,248,184,247,185,59,245', -'187,249,243,242,39,244,246,,,,,,188,183,250,248,184,247,185,,245,187', -'249,243,242,,244,246,,,186,251,,188,183,250,248,184,247,185,,245,187', -'249,243,242,,244,246,,,186,251,,188,183,250,,,,,,,,,,,,,,,,186,251' ] - racc_action_table = arr = ::Array.new(4874, nil) +'46,97,,45,58,54,,40,57,,,,55,12,,,56,,,11,,,,250,186,249,187,59,247', +'189,251,245,244,39,246,248,,,,,,190,185,252,250,186,249,187,,247,189', +'251,245,244,,246,248,,,188,253,,190,185,252,250,186,249,187,,247,189', +'251,245,244,,246,248,,,188,253,,190,185,252,,,,,,,,,,,,,,,,188,253' ] + racc_action_table = arr = ::Array.new(5016, nil) idx = 0 clist.each do |str| str.split(',', -1).each do |i| @@ -183,167 +187,171 @@ clist = [ end clist = [ -'161,115,138,203,176,161,102,138,291,161,161,161,161,161,231,161,203', -'161,174,139,161,161,161,161,148,115,215,215,71,71,130,130,139,177,139', -'182,139,148,161,148,102,148,161,161,176,110,161,161,161,161,161,161', -'212,161,161,217,217,139,174,160,161,212,148,215,181,71,215,161,160,160', -'160,160,160,177,160,182,160,148,148,160,160,160,160,147,148,215,267', -'71,70,70,235,114,217,42,267,217,147,160,147,96,147,160,160,96,181,160', -'160,160,160,160,160,237,160,160,217,180,180,214,159,160,42,147,45,45', -'42,70,160,159,159,159,159,159,264,159,264,159,147,147,159,159,159,159', -'275,147,179,122,275,70,179,275,122,213,180,44,178,180,159,44,178,45', -'159,159,45,210,159,159,159,159,159,159,124,159,159,124,180,158,97,304', -'159,304,241,45,92,255,257,159,158,158,158,97,158,97,158,97,259,158,158', -'158,158,221,221,260,95,90,48,48,221,7,7,7,7,48,263,158,97,95,209,95', -'158,95,265,158,158,158,158,158,158,266,158,158,127,202,157,270,271,158', -'221,272,273,200,95,48,158,157,157,157,277,157,132,157,62,60,157,157', -'157,157,146,196,195,289,193,101,43,191,298,299,37,171,36,146,157,146', -'307,146,308,310,311,315,157,157,157,157,157,157,316,157,157,35,322,323', -'325,10,157,10,146,170,5,1,340,157,10,10,10,10,10,345,10,347,10,146,146', -'10,10,10,10,156,146,349,,,,,,,,,,156,156,10,156,,156,10,10,156,,10,10', -'10,10,10,10,,10,10,,,,,,10,,156,,,,,10,155,,156,156,156,156,156,156', -',156,156,,155,155,,155,156,155,,,155,,,156,154,,,,,,,,,,,,154,154,155', -'154,,154,,,154,,155,155,155,155,155,155,,155,155,,,,,,155,,154,,,,,155', -'153,,154,154,154,154,154,154,,154,154,,153,153,,153,154,153,,,153,,', -'154,152,,,,,,,,,,,,,152,153,152,,152,,,,,153,153,153,153,153,153,,153', -'153,,,,,,153,,152,,,,,153,,,152,152,152,152,152,152,,152,152,,,,,303', -'152,,,,,,,152,303,303,303,303,303,,303,,303,,,303,303,303,303,151,,', -',,,,,,,,,,151,303,151,,151,303,303,,,303,303,303,303,303,303,,303,303', -',,,,,303,,151,,,,150,303,,,151,151,151,151,151,151,,151,151,150,,150', -',150,151,,,,,,149,151,,,,,,,,,,,,149,150,149,,149,,,,,150,150,150,150', -',,,150,150,,,,,,150,,149,,,,,150,,,149,149,149,149,,,,149,149,,,,,166', -'149,,,,,,,149,166,166,166,166,166,166,166,,166,,,166,166,166,166,,,', -',,,,,,,,,,,166,,,,166,166,,,166,166,166,166,166,166,,166,166,,,,,297', -'166,,,,,,,166,297,297,297,297,297,,297,,297,,,297,297,297,297,,,,,,', -',,,,,,,,297,,,,297,297,,,297,297,297,297,297,297,,297,297,,,,,296,297', -',,,,,,297,296,296,296,296,296,,296,,296,,,296,296,296,296,145,,,,,,', -',,,,,,145,296,145,,145,296,296,,,296,296,296,296,296,296,,296,296,,', -',,,296,,145,,,,,296,,,,,145,145,,,,145,145,,,,,288,145,,,,,,,145,288', -'288,288,288,288,,288,,288,,,288,288,288,288,144,,,,,,,,,,,,,144,288', -'144,,144,288,288,,,288,288,288,288,288,288,,288,288,,,,,,288,,144,,', -',,288,,,,,144,144,,,,144,144,,,,,286,144,,,,,,,144,286,286,286,286,286', -',286,,286,,,286,286,286,286,,,,,,,,,,,,,,,286,,,,286,286,,,286,286,286', -'286,286,286,,286,286,,,,,190,286,,,,,,,286,190,190,190,190,190,,190', -',190,,,190,190,190,190,,,,,,,,,,,,,,,190,,,,190,190,,,190,190,190,190', -'190,190,,190,190,,,,,282,190,,,,,,,190,282,282,282,282,282,,282,,282', -',,282,282,282,282,,,,,,,,,,,,,,,282,,,,282,282,,,282,282,282,282,282', -'282,,282,282,,,,,128,282,,,,,,,282,128,128,128,128,128,,128,,128,,,128', -'128,128,128,,,,,,,,,,,,,,,128,,,,128,128,,,128,128,128,128,128,128,', -'128,128,,,,,121,128,,,,,,,128,121,121,121,121,121,,121,,121,,,121,121', -'121,121,,,,,,,,,,,,,,,121,,,,121,121,,,121,121,121,121,121,121,,121', -'121,,,,,108,121,108,,,,,,121,108,108,108,108,108,,108,,108,,,108,108', -'108,108,,,,,,,,,,,,,,,108,,,,108,108,,,108,108,108,108,108,108,,108', -'108,,,,,107,108,107,,,,,,108,107,107,107,107,107,,107,,107,,,107,107', -'107,107,,,,,,,,,,,,,,,107,,,,107,107,,,107,107,107,107,107,107,,107', -'107,,,,,106,107,106,,,,,,107,106,106,106,106,106,,106,,106,,,106,106', -'106,106,,,,,,,,,,,,,,,106,,,,106,106,,,106,106,106,106,106,106,,106', -'106,,,,,104,106,104,,,,,,106,104,104,104,104,104,,104,,104,,,104,104', -'104,104,,,,,,,,,,,,,,,104,,,,104,104,,,104,104,104,104,104,104,,104', -'104,,,,,98,104,,,,,,,104,98,98,98,98,98,,98,,98,,98,98,98,98,98,,,,', -',,,,,89,89,,,89,98,89,,,98,98,,,98,98,98,98,98,98,,98,98,89,,,,,98,89', -',89,,89,89,98,89,89,89,,89,89,,,,89,89,208,208,89,,208,89,208,,,,,,', -'89,,,,,,89,,89,208,,,,,,208,208,208,208,208,208,208,208,208,208,,208', -'208,,,,208,208,208,208,208,278,278,208,,278,,278,278,,,208,,,,,208,208', -',,,,,278,,,,,,278,,278,,278,278,,278,278,278,,278,278,278,,,278,278', -'268,268,278,,268,278,268,268,,,,,,278,,,,,,278,,,268,,,,,,268,,268,', -'268,268,,268,268,268,,268,268,,,,268,268,72,72,268,,72,268,72,,,,,,', -'268,,,,,,268,,,72,,,,,,72,,72,,72,72,,72,72,72,,72,72,,,,72,72,73,73', -'72,,73,72,73,,,,,,,72,,,,,,72,,,73,,,,,,73,,73,,73,73,,73,73,73,,73', -'73,,,,73,73,74,74,73,,74,73,74,,,,,,,73,,,,,,73,,,74,,,,,,74,,74,,74', -'74,,74,74,74,,74,74,,,,74,74,75,75,74,,75,74,75,,,,,,,74,,,,,,74,,,75', -',,,,,75,,75,,75,75,,75,75,75,,75,75,,,,75,75,76,76,75,,76,75,76,,,,', -',,75,,,,,,75,,,76,,,,,,76,,76,,76,76,,76,76,76,,76,76,,,,76,76,77,77', -'76,,77,76,77,,,,,,,76,,,,,,76,,,77,,,,,,77,,77,,77,77,,77,77,77,,77', -'77,,,,77,77,78,78,77,,78,77,78,,,,,,,77,,,,,,77,,,78,,,,,,78,,78,,78', -'78,,78,78,78,,78,78,,,,78,78,79,79,78,,79,78,79,,,,,,,78,,,,,,78,,,79', -',,,,,79,,79,,79,79,,79,79,79,,79,79,,,,79,79,80,80,79,,80,79,80,,,,', -',,79,,,,,,79,,,80,,,,,,80,,80,,80,80,,80,80,80,,80,80,,,,80,80,81,81', -'80,,81,80,81,,,,,,,80,,,,,,80,,,81,,,,,,81,,81,,81,81,,81,81,81,,81', -'81,,,,81,81,82,82,81,,82,81,82,,,,,,,81,,,,,,81,,,82,,,,,,82,,82,,82', -'82,,82,82,82,,82,82,,,,82,82,83,83,82,,83,82,83,,,,,,,82,,,,,,82,,,83', -',,,,,83,,83,,83,83,,83,83,83,,83,83,,,,83,83,84,84,83,,84,83,84,,,,', -',,83,,,,,,83,,,84,,,,,,84,,84,,84,84,,84,84,84,,84,84,,,,84,84,85,85', -'84,,85,84,85,,,,,,,84,,,,,,84,,,85,,,,,,85,,85,,85,85,,85,85,85,,85', -'85,,,,85,85,86,86,85,,86,85,86,,,,,,,85,,,,,,85,,,86,,,,,,86,,86,,86', -'86,,86,86,86,,86,86,,,,86,86,87,87,86,,87,86,87,,,,,,,86,,,,,,86,,,87', -',,,,,87,,87,,87,87,,87,87,87,,87,87,,,,87,87,88,88,87,,88,87,88,,,,', -',,87,,,,,,87,,,88,,,,,,88,,88,,88,88,,88,88,88,,88,88,,,,88,88,68,68', -'88,,68,88,68,,,,,,,88,,,,,,88,,,68,,,,,,68,,68,,68,68,,68,68,68,,68', -'68,,,,68,68,261,261,68,,261,68,261,,,,,,,68,,,,,,68,,,261,,,,,,261,', -'261,,261,261,,261,261,261,,261,261,,,,261,261,91,91,261,,91,261,91,', -',,,,,261,,,,,,261,,,91,,,,,,91,91,91,91,91,91,91,91,91,91,,91,91,,,', -'91,91,91,91,91,254,254,91,,254,,254,,,,91,,,,,91,91,,,,,,254,,,,,,254', -',254,,254,254,,254,254,254,,254,254,,,,254,254,93,93,254,,93,254,93', -',,,,,,254,,,,,,254,,,93,,,,,,93,,93,,93,93,,93,93,93,,93,93,,,,93,93', -'94,94,93,,94,93,94,,,,,,,93,,,,,,93,,,94,,,,,,94,,94,,94,94,,94,94,94', -',94,94,,,,94,94,240,240,94,,240,94,240,,,,,,,94,,,,,,94,,,240,,,,,,240', -',240,,240,240,,240,240,240,,240,240,,,,240,240,239,239,240,,239,240', -'239,,,,,,,240,,,,,,240,,,239,,,,,,239,,239,,239,239,,239,239,239,,239', -'239,,,,239,239,236,236,239,,236,239,236,,,,,,,239,,,,,,239,,,236,,,', -',,236,,236,,236,236,,236,236,236,,236,236,,,,236,236,67,67,236,,67,236', -'67,,,,,,,236,,,,,,236,,,67,,,,,,67,,67,,67,67,,67,67,67,,67,67,67,,', -'67,67,99,99,67,,99,67,99,,,,,,,67,,,,,,67,,,99,99,,,,,99,,99,,99,99', -',99,99,99,,99,99,,,,99,99,230,230,99,,230,99,230,,,,,,,99,,,,,,99,,', -'230,,,,,,230,,230,,230,230,,230,230,230,,230,230,,,,230,230,229,229', -'230,,229,230,229,,,,,,,230,,,,,,230,,,229,,,,,,229,,229,,229,229,,229', -'229,229,,229,229,,,,229,229,103,103,229,,103,229,103,,,,,,,229,,,,,', -'229,,,103,103,,,,,103,,103,,103,103,,103,103,103,,103,103,,,,103,103', -'66,66,103,,66,103,66,,,,,,,103,,,,,,103,,,66,,,,,,66,,66,,66,66,,66', -'66,66,,66,66,66,,,66,66,65,65,66,,65,66,65,,,,,,,66,,,,,,66,,,65,,,', -',,65,,65,,65,65,,65,65,65,,65,65,65,,,65,65,64,64,65,,64,65,64,,,,,', -',65,,,,,,65,,,64,,,,,,64,,64,,64,64,,64,64,64,,64,64,64,,,64,64,63,63', -'64,,63,64,63,,,,,,,64,,,,,,64,,,63,,,,,,63,,63,,63,63,,63,63,63,,63', -'63,63,,,63,63,109,109,63,,109,63,109,,,,,,,63,,,,,,63,,,109,,,,,,109', -',109,,109,109,,109,109,109,,109,109,,,,109,109,227,227,109,,227,109', -'227,,,,,,,109,,,,,,109,,,227,,,,,,227,,227,,227,227,,227,227,227,,227', -'227,,,,227,227,222,222,227,,222,227,222,,,,,,,227,,,,,,227,,,222,,,', -',,222,,222,,222,222,,222,222,222,,222,222,,,,222,222,,,222,218,218,222', -',218,218,218,,,,222,,,,,,222,,,,,,218,,,,,,218,,218,,218,218,,218,218', -'218,,218,218,,,,218,218,279,279,218,,279,218,279,279,,,,,,218,,,,,,218', -',,279,,,,,,279,,279,,279,279,,279,279,279,,279,279,279,,,279,279,69', -'69,279,,69,279,69,,,,,,,279,,,,,,279,,,69,,,,,,69,,69,,69,69,,69,69', -'69,,69,69,,,,69,69,207,207,69,,207,69,207,,,,,,,69,,,,,,69,,,207,,,', -',,207,,207,,207,207,,207,207,207,,207,207,,,,207,207,206,206,207,,206', -'207,206,206,,,,,,207,,,,,,207,,,206,,,,,,206,,206,,206,206,,206,206', -'206,,206,206,206,,,206,206,61,61,206,,61,206,61,,,,,,,206,,,,,,206,', -',61,,,,,,61,,61,,61,61,,61,61,61,,61,61,61,,,61,61,164,164,61,,164,61', -'164,,,,,,,61,,,,,,61,,,164,,,,,,164,,164,,164,164,,164,164,164,,164', -'164,,,,164,164,198,198,164,,198,164,198,198,,,,,,164,,,,,,164,,,198', -',,,,,198,,198,,198,198,,198,198,198,,198,198,198,,,198,198,52,52,198', -',52,198,52,,,,,,,198,,,,,,198,,,52,,,,,,52,,52,,52,52,,52,52,52,,52', -'52,,,,52,52,169,169,52,,169,52,169,,,,,,,52,,,,,,52,,,169,,,,,,169,', -'169,,169,169,,169,169,169,,169,169,,,,169,169,,,169,47,47,169,,47,47', -'47,,,,169,,,,,,169,,,,,,47,,,,,,47,,47,,47,47,,47,47,47,,47,47,,,,47', -'47,290,290,47,,290,47,290,,,,,,,47,,,,,,47,,,290,,,,,,290,,290,,290', -'290,,290,290,290,,290,290,,,,290,290,168,168,290,,168,290,168,,,,,,', -'290,,,,,,290,,,168,,,,,,168,,168,,168,168,,168,168,168,,168,168,,,,168', -'168,167,167,168,,167,168,167,,,,,,,168,,,,,,168,,,167,,,,,,167,,167', -',167,167,,167,167,167,,167,167,,,,167,167,41,41,167,,41,167,41,,,,,', -',167,,,,,,167,,,41,,,,,,41,,41,,41,41,,41,41,41,,41,41,,,,41,41,40,40', -'41,,40,41,40,,,,,,,41,,,,,,41,,,40,,,,,,40,,40,,40,40,,40,40,40,,40', -'40,,,,40,40,39,39,40,,39,40,39,,,,,,,40,,,,,,40,,,39,,,,,,39,,39,,39', -'39,,39,39,39,,39,39,,,,39,39,38,38,39,,38,39,38,,,,,,,39,,,,,,39,,,38', -',,,,,38,,38,,38,38,,38,38,38,,38,38,,,,38,38,306,306,38,,306,38,306', -',,,,,,38,,,,,,38,,,306,,,,,,306,,306,,306,306,,306,306,306,,306,306', -',,,306,306,318,318,306,,318,306,318,318,,,,,,306,,,,,,306,,,318,,,,', -',318,,318,,318,318,,318,318,318,,318,318,318,,,318,318,13,13,318,,13', -'318,13,,,,,,,318,,,,,,318,,,13,,,,,,13,,13,,13,13,,13,13,13,,13,13,', -',,13,13,12,12,13,,12,13,12,,,,,,,13,,,,,,13,,,12,,,,,,12,,12,,12,12', -',12,12,12,,12,12,,,,12,12,11,11,12,,11,12,11,,,,,,,12,,,,,,12,,,11,', -',,,,11,,11,,11,11,,11,11,11,,11,11,,,,11,11,334,334,11,,334,11,334,334', -',,,,,11,,,,,,11,,,334,,,,,,334,,334,,334,334,,334,334,334,,334,334,334', -',,334,334,336,336,334,,336,334,336,336,,,,,,334,,,,,,334,,,336,,,,,', -'336,,336,,336,336,,336,336,336,,336,336,336,,,336,336,4,4,336,,4,336', -'4,,,,,,,336,,,,,,336,,,4,,,,,,4,,4,,4,4,,4,4,4,4,4,4,4,,,4,4,337,337', -'4,,337,4,337,337,,,,,,4,,,,,,4,,,337,,,,,,337,,337,,337,337,,337,337', -'337,,337,337,337,,,337,337,0,0,337,,0,337,0,,,,,,,337,,,,,,337,,,0,', -',,,,0,,0,,0,0,,0,0,0,,0,0,0,,,0,0,205,205,0,,205,0,205,205,,,,,,0,,', -',,,0,,,205,,,,,,205,,205,,205,205,,205,205,205,,205,205,205,,,205,205', -',,205,,,205,,,,233,233,233,233,205,233,233,233,233,233,205,233,233,', -',,,,233,233,233,189,189,189,189,,189,189,189,189,189,,189,189,,,233', -'233,,189,189,189,238,238,238,238,,238,238,238,238,238,,238,238,,,189', -'189,,238,238,238,,,,,,,,,,,,,,,,238,238' ] - racc_action_check = arr = ::Array.new(4874, nil) +'163,178,103,71,71,163,116,223,223,163,163,163,163,163,223,163,176,163', +'217,217,163,163,163,163,179,184,219,219,214,139,116,293,70,70,139,183', +'103,214,163,205,71,178,163,163,223,233,163,163,163,163,163,163,205,163', +'163,217,176,266,217,266,163,162,71,219,179,184,219,163,163,70,162,162', +'162,162,162,183,162,217,162,182,182,162,162,162,162,219,42,45,45,48', +'48,70,269,306,277,306,48,123,277,162,269,277,123,162,162,131,131,162', +'162,162,162,162,162,42,162,162,182,42,111,182,125,162,161,125,45,237', +'48,45,162,162,115,161,161,161,161,161,140,161,182,161,239,98,161,161', +'161,161,45,216,97,140,215,140,97,140,98,181,98,44,98,181,161,44,180', +'212,161,161,180,243,161,161,161,161,161,161,140,161,161,93,257,98,259', +'261,161,160,96,7,7,7,7,161,161,262,160,160,160,160,160,96,160,96,160', +'96,91,160,160,160,160,265,211,267,268,128,204,272,273,274,275,202,279', +'133,62,160,96,60,198,160,160,197,291,160,160,160,160,160,160,195,160', +'160,102,43,193,300,301,160,99,37,173,36,309,310,160,160,312,99,99,99', +'99,99,313,99,317,99,318,99,99,99,99,99,35,324,325,327,172,5,1,342,347', +'349,351,,,,99,,,,99,99,,,99,99,99,99,99,99,,99,99,,,,,,99,10,,10,,,', +'99,99,,10,10,10,10,10,,10,,10,,,10,10,10,10,,,,,,,,,,,,,,,10,,,,10,10', +',,10,10,10,10,10,10,,10,10,,,,,,10,105,,105,,,,10,10,,105,105,105,105', +'105,,105,,105,,,105,105,105,105,,,,,,,,,,,,,,,105,,,,105,105,,,105,105', +'105,105,105,105,,105,105,,,,,,105,107,,107,,,,105,105,,107,107,107,107', +'107,,107,,107,,,107,107,107,107,,,,,,,,,,,,,,,107,,,,107,107,,,107,107', +'107,107,107,107,,107,107,,,,,,107,108,,108,,,,107,107,,108,108,108,108', +'108,,108,,108,,,108,108,108,108,,,,,,,,,,,,,,,108,,,,108,108,,,108,108', +'108,108,108,108,,108,108,,,,,,108,109,,109,,,,108,108,,109,109,109,109', +'109,,109,,109,,,109,109,109,109,,,,,,,,,,,,,,,109,,,,109,109,,,109,109', +'109,109,109,109,,109,109,,,,,,109,122,,,,,,109,109,,122,122,122,122', +'122,,122,,122,,,122,122,122,122,,,,,,,,,,,,,,,122,,,,122,122,,,122,122', +'122,122,122,122,,122,122,,,,,,122,305,,,,,,122,122,,305,305,305,305', +'305,,305,,305,,,305,305,305,305,,,,,,,,,,,,,,,305,,,,305,305,,,305,305', +'305,305,305,305,,305,305,,,,,,305,129,,,,,,305,305,,129,129,129,129', +'129,,129,,129,,,129,129,129,129,,,,,,,,,,,,,,,129,,,,129,129,,,129,129', +'129,129,129,129,,129,129,,,,,,129,192,,,,,,129,129,,192,192,192,192', +'192,,192,,192,,,192,192,192,192,,,,,,,,,,,,,,,192,,,,192,192,,,192,192', +'192,192,192,192,,192,192,,,,,,192,284,,,,,,192,192,,284,284,284,284', +'284,,284,,284,,,284,284,284,284,,,,,,,,,,,,,,,284,,,,284,284,,,284,284', +'284,284,284,284,,284,284,,,,,,284,288,,,,,,284,284,,288,288,288,288', +'288,,288,,288,,,288,288,288,288,,,,,,,,,,,,,,,288,,,,288,288,,,288,288', +'288,288,288,288,,288,288,,,,,,288,290,,,,,,288,288,,290,290,290,290', +'290,,290,,290,,,290,290,290,290,,,,,,,,,,,,,,,290,,,,290,290,,,290,290', +'290,290,290,290,,290,290,,,,,,290,298,,,,,,290,290,,298,298,298,298', +'298,,298,,298,,,298,298,298,298,,,,,,,,,,,,,,,298,,,,298,298,,,298,298', +'298,298,298,298,,298,298,,,,,,298,168,,,,,,298,298,,168,168,168,168', +'168,168,168,,168,,,168,168,168,168,,,,,,,,,,,,,,,168,,,,168,168,,,168', +'168,168,168,168,168,,168,168,,,,,,168,299,,,,,,168,168,,299,299,299', +'299,299,,299,,299,,,299,299,299,299,146,,,,,,,,,,,,,146,299,146,,146', +'299,299,,,299,299,299,299,299,299,,299,299,,,,,,299,,146,,,,150,299', +'299,,,,146,146,,,,146,146,150,,150,,150,146,,145,,,,,146,,,,,,,,145', +',145,,145,150,,,,,,151,,150,150,150,150,,,,150,150,,,151,145,151,150', +'151,,,,,,150,145,145,,152,,145,145,,,,,,145,,151,,152,,152,145,152,', +'151,151,151,151,,153,,151,151,,,,,,151,,,,153,152,153,151,153,,,,,152', +'152,152,152,152,152,,152,152,,,,,,152,,153,,,,,152,154,,153,153,153', +'153,153,153,,153,153,,154,154,,154,153,154,,,154,,,153,155,,,,,,,,,', +',,155,155,154,155,,155,,,155,,154,154,154,154,154,154,,154,154,,,,,', +'154,,155,,,,,154,156,,155,155,155,155,155,155,,155,155,,156,156,,156', +'155,156,,,156,,,155,157,,,,,,,,,,,,157,157,156,157,,157,,,157,,156,156', +'156,156,156,156,,156,156,,,,,,156,,157,,,,,156,,,157,157,157,157,157', +'157,,157,157,,,158,,,157,,,,,,,157,158,158,158,,158,,158,,,158,158,158', +'158,,,,,149,,,,,,,,,,158,,,149,,149,,149,158,158,158,158,158,158,,158', +'158,,,159,,,158,,,,,,149,158,159,159,159,,159,,159,,,159,159,159,159', +'149,149,,,,,,149,,90,90,,,90,159,90,,,,159,,,159,159,159,159,159,159', +',159,159,90,,,,,159,90,,90,,90,90,159,90,90,90,,90,90,,,,90,90,210,210', +'90,,210,90,210,,,,,,,90,,,,,,90,,90,210,,,,,,210,210,210,210,210,210', +'210,210,210,210,,210,210,,,,210,210,210,210,210,270,270,210,,270,,270', +'270,,,210,,,,,210,210,,,,,,270,,,,,,270,,270,,270,270,,270,270,270,', +'270,270,,,,270,270,72,72,270,148,72,270,72,,,,,,,270,,,148,,148,270', +'148,,72,,,,,,72,,72,,72,72,,72,72,72,,72,72,148,,,72,72,73,73,72,147', +'73,72,73,,,,148,148,,72,,,147,148,147,72,147,,73,,,,,,73,,73,,73,73', +',73,73,73,,73,73,147,,,73,73,74,74,73,,74,73,74,,,,147,147,,73,,,,147', +',73,,,74,,,,,,74,,74,,74,74,,74,74,74,,74,74,,,,74,74,75,75,74,,75,74', +'75,,,,,,,74,,,,,,74,,,75,,,,,,75,,75,,75,75,,75,75,75,,75,75,,,,75,75', +'76,76,75,,76,75,76,,,,,,,75,,,,,,75,,,76,,,,,,76,,76,,76,76,,76,76,76', +',76,76,,,,76,76,77,77,76,,77,76,77,,,,,,,76,,,,,,76,,,77,,,,,,77,,77', +',77,77,,77,77,77,,77,77,,,,77,77,78,78,77,,78,77,78,,,,,,,77,,,,,,77', +',,78,,,,,,78,,78,,78,78,,78,78,78,,78,78,,,,78,78,79,79,78,,79,78,79', +',,,,,,78,,,,,,78,,,79,,,,,,79,,79,,79,79,,79,79,79,,79,79,,,,79,79,80', +'80,79,,80,79,80,,,,,,,79,,,,,,79,,,80,,,,,,80,,80,,80,80,,80,80,80,', +'80,80,,,,80,80,81,81,80,,81,80,81,,,,,,,80,,,,,,80,,,81,,,,,,81,,81', +',81,81,,81,81,81,,81,81,,,,81,81,82,82,81,,82,81,82,,,,,,,81,,,,,,81', +',,82,,,,,,82,,82,,82,82,,82,82,82,,82,82,,,,82,82,83,83,82,,83,82,83', +',,,,,,82,,,,,,82,,,83,,,,,,83,,83,,83,83,,83,83,83,,83,83,,,,83,83,84', +'84,83,,84,83,84,,,,,,,83,,,,,,83,,,84,,,,,,84,,84,,84,84,,84,84,84,', +'84,84,,,,84,84,85,85,84,,85,84,85,,,,,,,84,,,,,,84,,,85,,,,,,85,,85', +',85,85,,85,85,85,,85,85,,,,85,85,86,86,85,,86,85,86,,,,,,,85,,,,,,85', +',,86,,,,,,86,,86,,86,86,,86,86,86,,86,86,,,,86,86,166,166,86,,166,86', +'166,,,,,,,86,,,,,,86,,,166,,,,,,166,,166,,166,166,,166,166,166,,166', +'166,,,,166,166,88,88,166,,88,166,88,,,,,,,166,,,,,,166,,,88,,,,,,88', +',88,,88,88,,88,88,88,,88,88,,,,88,88,89,89,88,,89,88,89,,,,,,,88,,,', +',,88,,,89,,,,,,89,,89,,89,89,,89,89,89,,89,89,,,,89,89,69,69,89,,69', +'89,69,,,,,,,89,,,,,,89,,,69,,,,,,69,,69,,69,69,,69,69,69,,69,69,,,,69', +'69,263,263,69,,263,69,263,,,,,,,69,,,,,,69,,,263,,,,,,263,,263,,263', +'263,,263,263,263,,263,263,,,,263,263,92,92,263,,92,263,92,,,,,,,263', +',,,,,263,,,92,,,,,,92,92,92,92,92,92,92,92,92,92,,92,92,,,,92,92,92', +'92,92,256,256,92,,256,,256,,,,92,,,,,92,92,,,,,,256,,,,,,256,,256,,256', +'256,,256,256,256,,256,256,,,,256,256,94,94,256,,94,256,94,,,,,,,256', +',,,,,256,,,94,,,,,,94,,94,,94,94,,94,94,94,,94,94,,,,94,94,95,95,94', +',95,94,95,,,,,,,94,,,,,,94,,,95,,,,,,95,,95,,95,95,,95,95,95,,95,95', +',,,95,95,242,242,95,,242,95,242,,,,,,,95,,,,,,95,,,242,,,,,,242,,242', +',242,242,,242,242,242,,242,242,,,,242,242,241,241,242,,241,242,241,', +',,,,,242,,,,,,242,,,241,,,,,,241,,241,,241,241,,241,241,241,,241,241', +',,,241,241,238,238,241,,238,241,238,,,,,,,241,,,,,,241,,,238,,,,,,238', +',238,,238,238,,238,238,238,,238,238,,,,238,238,68,68,238,,68,238,68', +',,,,,,238,,,,,,238,,,68,,,,,,68,,68,,68,68,,68,68,68,,68,68,,,,68,68', +'100,100,68,,100,68,100,,,,,,,68,,,,,,68,,,100,100,,,,,100,,100,,100', +'100,,100,100,100,,100,100,,,,100,100,232,232,100,,232,100,232,,,,,,', +'100,,,,,,100,,,232,,,,,,232,,232,,232,232,,232,232,232,,232,232,,,,232', +'232,231,231,232,,231,232,231,,,,,,,232,,,,,,232,,,231,,,,,,231,,231', +',231,231,,231,231,231,,231,231,,,,231,231,104,104,231,,104,231,104,', +',,,,,231,,,,,,231,,,104,104,,,,,104,,104,,104,104,,104,104,104,,104', +'104,,,,104,104,67,67,104,,67,104,67,,,,,,,104,,,,,,104,,,67,,,,,,67', +',67,,67,67,,67,67,67,,67,67,67,,,67,67,66,66,67,,66,67,66,,,,,,,67,', +',,,,67,,,66,,,,,,66,,66,,66,66,,66,66,66,,66,66,66,,,66,66,65,65,66', +',65,66,65,,,,,,,66,,,,,,66,,,65,,,,,,65,,65,,65,65,,65,65,65,,65,65', +'65,,,65,65,64,64,65,,64,65,64,,,,,,,65,,,,,,65,,,64,,,,,,64,,64,,64', +'64,,64,64,64,,64,64,64,,,64,64,110,110,64,,110,64,110,,,,,,,64,,,,,', +'64,,,110,,,,,,110,,110,,110,110,,110,110,110,,110,110,,,,110,110,229', +'229,110,,229,110,229,,,,,,,110,,,,,,110,,,229,,,,,,229,,229,,229,229', +',229,229,229,,229,229,,,,229,229,224,224,229,,224,229,224,,,,,,,229', +',,,,,229,,,224,,,,,,224,,224,,224,224,,224,224,224,,224,224,,,,224,224', +',,224,220,220,224,,220,220,220,,,,224,,,,,,224,,,,,,220,,,,,,220,,220', +',220,220,,220,220,220,,220,220,,,,220,220,63,63,220,,63,220,63,,,,,', +',220,,,,,,220,,,63,,,,,,63,,63,,63,63,,63,63,63,,63,63,63,,,63,63,280', +'280,63,,280,63,280,280,,,,,,63,,,,,,63,,,280,,,,,,280,,280,,280,280', +',280,280,280,,280,280,280,,,280,280,209,209,280,,209,280,209,,,,,,,280', +',,,,,280,,,209,,,,,,209,,209,,209,209,,209,209,209,,209,209,,,,209,209', +'208,208,209,,208,209,208,208,,,,,,209,,,,,,209,,,208,,,,,,208,,208,', +'208,208,,208,208,208,,208,208,208,,,208,208,281,281,208,,281,208,281', +'281,,,,,,208,,,,,,208,,,281,,,,,,281,,281,,281,281,,281,281,281,,281', +'281,281,,,281,281,207,207,281,,207,281,207,207,,,,,,281,,,,,,281,,,207', +',,,,,207,,207,,207,207,,207,207,207,,207,207,207,,,207,207,200,200,207', +',200,207,200,200,,,,,,207,,,,,,207,,,200,,,,,,200,,200,,200,200,,200', +'200,200,,200,200,200,,,200,200,61,61,200,,61,200,61,,,,,,,200,,,,,,200', +',,61,,,,,,61,,61,,61,61,,61,61,61,,61,61,61,,,61,61,171,171,61,,171', +'61,171,,,,,,,61,,,,,,61,,,171,,,,,,171,,171,,171,171,,171,171,171,,171', +'171,,,,171,171,52,52,171,,52,171,52,,,,,,,171,,,,,,171,,,52,,,,,,52', +',52,,52,52,,52,52,52,,52,52,,,,52,52,,,52,47,47,52,,47,47,47,,,,52,', +',,,,52,,,,,,47,,,,,,47,,47,,47,47,,47,47,47,,47,47,,,,47,47,170,170', +'47,,170,47,170,,,,,,,47,,,,,,47,,,170,,,,,,170,,170,,170,170,,170,170', +'170,,170,170,,,,170,170,169,169,170,,169,170,169,,,,,,,170,,,,,,170', +',,169,,,,,,169,,169,,169,169,,169,169,169,,169,169,,,,169,169,292,292', +'169,,292,169,292,,,,,,,169,,,,,,169,,,292,,,,,,292,,292,,292,292,,292', +'292,292,,292,292,,,,292,292,41,41,292,,41,292,41,,,,,,,292,,,,,,292', +',,41,,,,,,41,,41,,41,41,,41,41,41,,41,41,,,,41,41,40,40,41,,40,41,40', +',,,,,,41,,,,,,41,,,40,,,,,,40,,40,,40,40,,40,40,40,,40,40,,,,40,40,39', +'39,40,,39,40,39,,,,,,,40,,,,,,40,,,39,,,,,,39,,39,,39,39,,39,39,39,', +'39,39,,,,39,39,38,38,39,,38,39,38,,,,,,,39,,,,,,39,,,38,,,,,,38,,38', +',38,38,,38,38,38,,38,38,,,,38,38,308,308,38,,308,38,308,,,,,,,38,,,', +',,38,,,308,,,,,,308,,308,,308,308,,308,308,308,,308,308,,,,308,308,320', +'320,308,,320,308,320,320,,,,,,308,,,,,,308,,,320,,,,,,320,,320,,320', +'320,,320,320,320,,320,320,320,,,320,320,13,13,320,,13,320,13,,,,,,,320', +',,,,,320,,,13,,,,,,13,,13,,13,13,,13,13,13,,13,13,,,,13,13,12,12,13', +',12,13,12,,,,,,,13,,,,,,13,,,12,,,,,,12,,12,,12,12,,12,12,12,,12,12', +',,,12,12,11,11,12,,11,12,11,,,,,,,12,,,,,,12,,,11,,,,,,11,,11,,11,11', +',11,11,11,,11,11,,,,11,11,336,336,11,,336,11,336,336,,,,,,11,,,,,,11', +',,336,,,,,,336,,336,,336,336,,336,336,336,,336,336,336,,,336,336,338', +'338,336,,338,336,338,338,,,,,,336,,,,,,336,,,338,,,,,,338,,338,,338', +'338,,338,338,338,,338,338,338,,,338,338,4,4,338,,4,338,4,,,,,,,338,', +',,,,338,,,4,,,,,,4,,4,,4,4,,4,4,4,4,4,4,4,,,4,4,339,339,4,,339,4,339', +'339,,,,,,4,,,,,,4,,,339,,,,,,339,,339,,339,339,,339,339,339,,339,339', +'339,,,339,339,0,0,339,,0,339,0,,,,,,,339,,,,,,339,,,0,,,,,,0,,0,,0,0', +',0,0,0,,0,0,0,,,0,0,87,87,0,,87,0,87,,,,,,,0,,,,,,0,,,87,,,,,,87,,87', +',87,87,,87,87,87,,87,87,,,,87,87,,,87,,,87,,,,235,235,235,235,87,235', +'235,235,235,235,87,235,235,,,,,,235,235,235,191,191,191,191,,191,191', +'191,191,191,,191,191,,,235,235,,191,191,191,240,240,240,240,,240,240', +'240,240,240,,240,240,,,191,191,,240,240,240,,,,,,,,,,,,,,,,240,240' ] + racc_action_check = arr = ::Array.new(5016, nil) idx = 0 clist.each do |str| str.split(',', -1).each do |i| @@ -353,228 +361,219 @@ clist = [ end racc_action_pointer = [ - 4691, 297, nil, nil, 4599, 284, nil, 145, nil, nil, - 285, 4461, 4415, 4369, nil, nil, nil, nil, nil, nil, + 4833, 274, nil, nil, 4741, 261, nil, 123, nil, nil, + 299, 4603, 4557, 4511, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, 263, 200, 242, 4231, 4185, - 4139, 4093, 85, 219, 118, 120, nil, 3909, 202, nil, - nil, nil, 3814, nil, nil, nil, nil, nil, nil, nil, - 251, 3676, 238, 3259, 3213, 3167, 3121, 2891, 2474, 3538, - 86, 26, 1692, 1738, 1784, 1830, 1876, 1922, 1968, 2014, - 2060, 2106, 2152, 2198, 2244, 2290, 2336, 2382, 2428, 1505, - 164, 2566, 174, 2661, 2707, 196, 64, 170, 1468, 2937, - nil, 253, -28, 3075, 1409, nil, 1350, 1291, 1232, 3305, - 21, nil, nil, nil, 67, -11, nil, nil, nil, nil, - nil, 1173, 138, nil, 161, nil, nil, 219, 1114, nil, - 26, nil, 236, nil, nil, nil, nil, nil, -5, 13, - nil, nil, nil, nil, 878, 795, 250, 77, 18, 594, - 570, 528, 445, 421, 377, 353, 309, 226, 169, 112, - 53, -6, nil, nil, 3722, nil, 653, 4047, 4001, 3860, - 255, 255, nil, nil, 7, nil, -7, 22, 119, 109, - 113, 53, 24, nil, nil, nil, nil, nil, nil, 4785, - 996, 218, nil, 238, nil, 246, 189, nil, 3768, nil, - 227, nil, 216, -9, nil, 4737, 3630, 3584, 1551, 176, - 127, nil, 27, 143, 109, 24, nil, 53, 3446, nil, - nil, 197, 3397, nil, nil, nil, nil, 3351, nil, 3029, - 2983, 2, nil, 4764, nil, 81, 2845, 102, 4806, 2799, - 2753, 168, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, 2615, 158, nil, 175, nil, 126, - 167, 2520, nil, 203, 101, 211, 196, 74, 1646, nil, - 193, 222, 228, 230, nil, 107, nil, 234, 1600, 3492, - nil, nil, 1055, nil, nil, nil, 937, nil, 854, 250, - 3955, -4, nil, nil, nil, nil, 771, 712, 255, 197, - nil, nil, nil, 504, 146, nil, 4277, 264, 243, nil, - 267, 268, nil, nil, nil, 268, 275, nil, 4323, nil, - nil, nil, 263, 280, nil, 281, nil, nil, nil, nil, - nil, nil, nil, nil, 4507, nil, 4553, 4645, nil, nil, - 289, nil, nil, nil, nil, 296, nil, 298, nil, 308, - nil, nil, nil, nil, nil ] + nil, nil, nil, nil, nil, 244, 179, 221, 4373, 4327, + 4281, 4235, 78, 196, 122, 85, nil, 4051, 87, nil, + nil, nil, 4002, nil, nil, nil, nil, nil, nil, nil, + 223, 3910, 208, 3588, 3355, 3309, 3263, 3217, 2987, 2570, + 30, 1, 1742, 1788, 1834, 1880, 1926, 1972, 2018, 2064, + 2110, 2156, 2202, 2248, 2294, 2340, 2386, 4879, 2478, 2524, + 1601, 163, 2662, 169, 2757, 2803, 178, 113, 135, 238, + 3033, nil, 230, -32, 3171, 360, nil, 421, 482, 543, + 3401, 94, nil, nil, nil, 106, -6, nil, nil, nil, + nil, nil, 604, 90, nil, 111, nil, nil, 200, 726, + nil, 101, nil, 207, nil, nil, nil, nil, nil, 22, + 130, nil, nil, nil, nil, 1239, 1177, 1787, 1741, 1535, + 1219, 1263, 1290, 1314, 1358, 1382, 1426, 1450, 1507, 1564, + 177, 116, 55, -6, nil, nil, 2432, nil, 1092, 4143, + 4097, 3956, 232, 234, nil, nil, 5, nil, -10, 13, + 127, 120, 77, 24, 14, nil, nil, nil, nil, nil, + nil, 4927, 787, 195, nil, 213, nil, 215, 156, nil, + 3864, nil, 205, nil, 197, 27, nil, 3818, 3726, 3680, + 1647, 168, 127, nil, 3, 142, 139, 16, nil, 24, + 3542, nil, nil, 5, 3493, nil, nil, nil, nil, 3447, + nil, 3125, 3079, 33, nil, 4906, nil, 116, 2941, 131, + 4948, 2895, 2849, 155, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, 2711, 153, nil, 171, + nil, 113, 157, 2616, nil, 198, 26, 200, 179, 80, + 1696, nil, 173, 202, 206, 208, nil, 59, nil, 206, + 3634, 3772, nil, nil, 848, nil, nil, nil, 909, nil, + 970, 219, 4189, 19, nil, nil, nil, nil, 1031, 1153, + 232, 174, nil, nil, nil, 665, 62, nil, 4419, 240, + 218, nil, 244, 250, nil, nil, nil, 251, 253, nil, + 4465, nil, nil, nil, 244, 261, nil, 262, nil, nil, + nil, nil, nil, nil, nil, nil, 4649, nil, 4695, 4787, + nil, nil, 266, nil, nil, nil, nil, 267, nil, 268, + nil, 269, nil, nil, nil, nil, nil ] racc_action_default = [ - -203, -204, -1, -2, -3, -4, -7, -9, -10, -15, - -103, -204, -204, -204, -43, -44, -45, -46, -47, -48, - -49, -50, -51, -52, -53, -54, -55, -56, -57, -58, - -59, -60, -61, -62, -63, -68, -69, -73, -204, -204, - -204, -204, -204, -113, -204, -204, -158, -204, -204, -168, - -169, -170, -204, -172, -179, -180, -181, -182, -183, -184, - -204, -204, -6, -204, -204, -204, -204, -204, -204, -204, - -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, - -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, - -204, -121, -116, -203, -203, -27, -204, -34, -204, -204, - -70, -204, -204, -204, -204, -80, -204, -204, -204, -204, - -203, -147, -148, -114, -203, -203, -139, -141, -142, -143, - -144, -41, -204, -161, -204, -164, -165, -204, -176, -171, - -204, 355, -5, -8, -11, -12, -13, -14, -204, -17, - -18, -156, -157, -19, -20, -21, -22, -23, -24, -25, - -26, -28, -29, -30, -31, -32, -33, -35, -36, -37, - -38, -204, -39, -98, -204, -74, -204, -196, -202, -190, - -187, -185, -111, -122, -179, -125, -183, -204, -193, -191, - -199, -181, -182, -189, -194, -195, -197, -198, -200, -121, - -120, -204, -119, -204, -40, -185, -65, -75, -204, -78, - -185, -152, -155, -204, -72, -204, -204, -204, -121, -187, - -203, -149, -204, -204, -204, -204, -145, -204, -204, -159, - -162, -204, -204, -173, -174, -175, -177, -204, -16, -204, - -204, -185, -100, -121, -110, -204, -188, -204, -186, -204, - -204, -185, -124, -126, -190, -191, -192, -193, -196, -199, - -201, -202, -117, -118, -186, -204, -67, -204, -77, -204, - -186, -204, -71, -204, -83, -204, -89, -204, -204, -93, - -187, -185, -204, -204, -133, -204, -150, -185, -204, -204, - -140, -146, -42, -160, -163, -166, -167, -178, -102, -204, - -186, -185, -106, -112, -107, -123, -127, -128, -204, -64, - -76, -79, -153, -154, -83, -82, -204, -204, -89, -88, - -204, -204, -97, -92, -94, -204, -204, -108, -204, -134, - -135, -136, -204, -204, -130, -204, -138, -99, -101, -109, - -115, -66, -81, -84, -204, -87, -204, -204, -104, -105, - -204, -132, -151, -129, -137, -204, -86, -204, -91, -204, - -96, -131, -85, -90, -95 ] + -204, -205, -1, -2, -3, -4, -7, -9, -10, -15, + -104, -205, -205, -205, -44, -45, -46, -47, -48, -49, + -50, -51, -52, -53, -54, -55, -56, -57, -58, -59, + -60, -61, -62, -63, -64, -69, -70, -74, -205, -205, + -205, -205, -205, -114, -205, -205, -159, -205, -205, -169, + -170, -171, -205, -173, -180, -181, -182, -183, -184, -185, + -205, -205, -6, -205, -205, -205, -205, -205, -205, -205, + -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, + -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, + -205, -205, -122, -117, -204, -204, -27, -205, -34, -205, + -205, -71, -205, -205, -205, -205, -81, -205, -205, -205, + -205, -204, -148, -149, -115, -204, -204, -140, -142, -143, + -144, -145, -42, -205, -162, -205, -165, -166, -205, -177, + -172, -205, 357, -5, -8, -11, -12, -13, -14, -205, + -17, -18, -157, -158, -19, -20, -21, -22, -23, -24, + -25, -26, -28, -29, -30, -31, -32, -33, -35, -36, + -37, -38, -39, -205, -40, -99, -205, -75, -205, -197, + -203, -191, -188, -186, -112, -123, -180, -126, -184, -205, + -194, -192, -200, -182, -183, -190, -195, -196, -198, -199, + -201, -122, -121, -205, -120, -205, -41, -186, -66, -76, + -205, -79, -186, -153, -156, -205, -73, -205, -205, -205, + -122, -188, -204, -150, -205, -205, -205, -205, -146, -205, + -205, -160, -163, -205, -205, -174, -175, -176, -178, -205, + -16, -205, -205, -186, -101, -122, -111, -205, -189, -205, + -187, -205, -205, -186, -125, -127, -191, -192, -193, -194, + -197, -200, -202, -203, -118, -119, -187, -205, -68, -205, + -78, -205, -187, -205, -72, -205, -84, -205, -90, -205, + -205, -94, -188, -186, -205, -205, -134, -205, -151, -186, + -205, -205, -141, -147, -43, -161, -164, -167, -168, -179, + -103, -205, -187, -186, -107, -113, -108, -124, -128, -129, + -205, -65, -77, -80, -154, -155, -84, -83, -205, -205, + -90, -89, -205, -205, -98, -93, -95, -205, -205, -109, + -205, -135, -136, -137, -205, -205, -131, -205, -139, -100, + -102, -110, -116, -67, -82, -85, -205, -88, -205, -205, + -105, -106, -205, -133, -152, -130, -138, -205, -87, -205, + -92, -205, -97, -132, -86, -91, -96 ] racc_goto_table = [ - 2, 3, 100, 95, 97, 98, 114, 163, 129, 126, - 170, 171, 127, 200, 118, 305, 309, 120, 235, 210, - 280, 311, 281, 213, 237, 231, 269, 293, 209, 233, - 104, 106, 107, 108, 142, 142, 62, 140, 143, 121, - 191, 193, 141, 141, 128, 268, 295, 333, 255, 134, - 135, 136, 137, 259, 197, 332, 273, 272, 335, 319, - 121, 139, 214, 162, 144, 145, 146, 147, 148, 149, - 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, - 160, 161, 232, 166, 289, 190, 190, 314, 302, 122, - 124, 121, 133, 132, 298, 121, 1, 226, 227, 225, - nil, 166, nil, nil, nil, nil, nil, nil, nil, 241, - 138, 211, nil, nil, nil, 211, 216, nil, 315, nil, - nil, nil, nil, 277, 316, nil, nil, 270, 271, nil, - 322, nil, nil, nil, nil, nil, nil, nil, nil, nil, - 114, 195, nil, nil, 329, 203, nil, nil, nil, 118, - nil, nil, 120, 291, nil, nil, 161, nil, nil, 104, - 106, 107, 256, nil, nil, nil, nil, nil, nil, nil, + 2, 3, 101, 96, 98, 99, 115, 165, 130, 127, + 173, 172, 128, 202, 119, 307, 313, 121, 237, 212, + 311, 233, 282, 215, 283, 239, 295, 271, 235, 211, + 105, 107, 108, 109, 143, 143, 62, 123, 270, 122, + 141, 144, 142, 142, 129, 135, 136, 137, 138, 257, + 193, 195, 297, 335, 261, 334, 199, 274, 139, 275, + 122, 140, 337, 321, 145, 146, 147, 148, 149, 150, + 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, + 161, 162, 163, 234, 168, 291, 192, 192, 316, 216, + 197, 164, 122, 133, 205, 300, 122, 304, 125, 134, + 1, 228, 168, 229, 227, nil, nil, nil, nil, 243, + nil, nil, 213, nil, nil, nil, 213, 218, 317, nil, + nil, nil, nil, nil, 279, 318, nil, nil, 273, 272, + nil, 324, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, 115, nil, nil, 331, nil, nil, nil, nil, + nil, 119, nil, 293, 121, nil, nil, nil, 163, nil, + nil, 105, 107, 108, 258, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, 285, 287, 118, 127, 118, 120, nil, 120, - nil, nil, nil, nil, nil, nil, nil, nil, 257, 121, - 166, nil, nil, nil, nil, 263, 265, nil, 328, nil, - 282, 274, nil, nil, 286, nil, nil, nil, nil, 128, - nil, 282, 288, nil, nil, nil, nil, nil, 166, nil, - nil, 296, 297, nil, nil, nil, nil, 320, nil, nil, - nil, nil, nil, nil, nil, nil, 282, nil, nil, nil, - nil, nil, nil, 303, nil, nil, nil, nil, nil, nil, - 121, nil, nil, nil, nil, 331, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, 323, 325, - nil, nil, 161, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, 104, nil, + nil, nil, nil, nil, 287, 289, 119, 128, 119, 121, + nil, 121, nil, nil, nil, nil, nil, nil, nil, nil, + 259, 122, 168, nil, nil, nil, nil, 265, 267, 330, + nil, nil, 284, 276, nil, nil, 288, nil, nil, nil, + nil, 129, nil, 284, 290, nil, nil, nil, nil, nil, + 168, nil, nil, 298, 299, nil, nil, nil, nil, 322, + nil, nil, nil, nil, nil, nil, nil, nil, 284, nil, + nil, nil, nil, nil, nil, 305, nil, nil, nil, nil, + nil, nil, 122, nil, nil, nil, nil, 333, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, 340, nil, + 325, 327, nil, nil, 163, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, 345, nil, 347, 349 ] + 105, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + 342, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, 347, nil, 349, 351 ] racc_goto_check = [ 2, 3, 37, 9, 9, 9, 62, 49, 75, 71, - 52, 54, 31, 42, 35, 44, 45, 30, 53, 63, - 68, 48, 68, 63, 36, 50, 47, 55, 52, 56, - 9, 9, 9, 9, 31, 31, 5, 12, 12, 9, - 58, 58, 30, 30, 9, 46, 59, 43, 36, 7, - 7, 7, 7, 36, 41, 44, 64, 53, 45, 65, - 9, 9, 67, 13, 9, 9, 9, 9, 9, 9, + 54, 52, 31, 42, 35, 44, 48, 30, 53, 63, + 45, 50, 68, 63, 68, 36, 55, 47, 56, 52, + 9, 9, 9, 9, 31, 31, 5, 11, 46, 9, + 12, 12, 30, 30, 9, 7, 7, 7, 7, 36, + 58, 58, 59, 43, 36, 44, 41, 53, 11, 64, + 9, 9, 45, 65, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 49, 9, 36, 9, 9, 47, 69, 11, - 70, 9, 6, 5, 36, 9, 1, 76, 77, 79, - nil, 9, nil, nil, nil, nil, nil, nil, nil, 54, - 11, 3, nil, nil, nil, 3, 3, nil, 53, nil, - nil, nil, nil, 42, 36, nil, nil, 52, 54, nil, - 36, nil, nil, nil, nil, nil, nil, nil, nil, nil, - 62, 11, nil, nil, 36, 11, nil, nil, nil, 35, - nil, nil, 30, 54, nil, nil, 9, nil, nil, 9, - 9, 9, 37, nil, nil, nil, nil, nil, nil, nil, + 9, 9, 9, 49, 9, 36, 9, 9, 47, 67, + 11, 13, 9, 5, 11, 36, 9, 69, 70, 6, + 1, 76, 9, 77, 79, nil, nil, nil, nil, 54, + nil, nil, 3, nil, nil, nil, 3, 3, 53, nil, + nil, nil, nil, nil, 42, 36, nil, nil, 54, 52, + nil, 36, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, 62, nil, nil, 36, nil, nil, nil, nil, + nil, 35, nil, 54, 30, nil, nil, nil, 9, nil, + nil, 9, 9, 9, 37, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, 71, 75, 35, 31, 35, 30, nil, 30, - nil, nil, nil, nil, nil, nil, nil, nil, 2, 9, - 9, nil, nil, nil, nil, 2, 2, nil, 49, nil, - 9, 3, nil, nil, 9, nil, nil, nil, nil, 9, - nil, 9, 9, nil, nil, nil, nil, nil, 9, nil, - nil, 9, 9, nil, nil, nil, nil, 62, nil, nil, - nil, nil, nil, nil, nil, nil, 9, nil, nil, nil, - nil, nil, nil, 9, nil, nil, nil, nil, nil, nil, - 9, nil, nil, nil, nil, 37, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, 2, 2, - nil, nil, 9, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, 71, 75, 35, 31, 35, 30, + nil, 30, nil, nil, nil, nil, nil, nil, nil, nil, + 2, 9, 9, nil, nil, nil, nil, 2, 2, 49, + nil, nil, 9, 3, nil, nil, 9, nil, nil, nil, + nil, 9, nil, 9, 9, nil, nil, nil, nil, nil, + 9, nil, nil, 9, 9, nil, nil, nil, nil, 62, nil, nil, nil, nil, nil, nil, nil, nil, 9, nil, + nil, nil, nil, nil, nil, 9, nil, nil, nil, nil, + nil, nil, 9, nil, nil, nil, nil, 37, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, 2, nil, + 2, 2, nil, nil, 9, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, 2, nil, 2, 2 ] + 9, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + 2, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, 2, nil, 2, 2 ] racc_goto_pointer = [ - nil, 96, 0, 1, nil, 32, 29, -15, nil, -8, - nil, 42, -33, -26, nil, nil, nil, nil, nil, nil, + nil, 100, 0, 1, nil, 32, 36, -19, nil, -8, + nil, -10, -30, 1, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - -28, -36, nil, nil, nil, -31, -147, -34, nil, nil, - nil, -47, -89, -259, -249, -250, -162, -181, -246, -82, - -139, nil, -81, -152, -80, -209, -137, nil, -53, -192, - nil, nil, -38, -91, -154, -216, nil, -53, -195, -172, - 42, -39, nil, nil, nil, -44, -33, -32, nil, -31 ] + -28, -36, nil, nil, nil, -31, -148, -34, nil, nil, + nil, -46, -90, -255, -251, -248, -171, -182, -253, -83, + -145, nil, -81, -154, -82, -212, -140, nil, -44, -188, + nil, nil, -38, -92, -153, -214, nil, -27, -195, -165, + 50, -39, nil, nil, nil, -44, -30, -28, nil, -27 ] racc_goto_default = [ - nil, nil, nil, 192, 4, 5, 6, 7, 8, 10, - 9, 267, nil, nil, 14, 35, 15, 16, 17, 18, + nil, nil, nil, 194, 4, 5, 6, 7, 8, 10, + 9, 269, nil, nil, 14, 35, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, nil, nil, 36, 37, - 101, nil, nil, 105, nil, nil, nil, nil, nil, nil, - nil, 41, nil, nil, nil, 172, nil, 92, nil, 173, - 177, 175, 110, nil, nil, nil, 115, nil, 116, 201, - nil, nil, 49, 50, 52, nil, nil, nil, 130, nil ] + 102, nil, nil, 106, nil, nil, nil, nil, nil, nil, + nil, 41, nil, nil, nil, 174, nil, 93, nil, 175, + 179, 177, 111, nil, nil, nil, 116, nil, 117, 203, + nil, nil, 49, 50, 52, nil, nil, nil, 131, nil ] racc_reduce_table = [ 0, 0, :racc_error, - 1, 77, :_reduce_1, - 1, 77, :_reduce_none, - 1, 78, :_reduce_3, - 1, 80, :_reduce_4, - 3, 80, :_reduce_5, - 2, 80, :_reduce_6, - 1, 81, :_reduce_7, - 3, 81, :_reduce_8, - 1, 82, :_reduce_none, - 1, 83, :_reduce_10, - 3, 83, :_reduce_11, - 3, 83, :_reduce_12, - 3, 83, :_reduce_13, - 3, 83, :_reduce_14, - 1, 85, :_reduce_none, - 4, 85, :_reduce_16, - 3, 85, :_reduce_17, - 3, 85, :_reduce_18, - 3, 85, :_reduce_19, - 3, 85, :_reduce_20, - 3, 85, :_reduce_21, - 3, 85, :_reduce_22, - 3, 85, :_reduce_23, - 3, 85, :_reduce_24, - 3, 85, :_reduce_25, - 3, 85, :_reduce_26, - 2, 85, :_reduce_27, - 3, 85, :_reduce_28, - 3, 85, :_reduce_29, - 3, 85, :_reduce_30, - 3, 85, :_reduce_31, - 3, 85, :_reduce_32, - 3, 85, :_reduce_33, - 2, 85, :_reduce_34, - 3, 85, :_reduce_35, - 3, 85, :_reduce_36, - 3, 85, :_reduce_37, - 3, 85, :_reduce_38, - 3, 85, :_reduce_39, - 3, 85, :_reduce_40, - 1, 87, :_reduce_41, - 3, 87, :_reduce_42, + 1, 78, :_reduce_1, + 1, 78, :_reduce_none, + 1, 79, :_reduce_3, + 1, 81, :_reduce_4, + 3, 81, :_reduce_5, + 2, 81, :_reduce_6, + 1, 82, :_reduce_7, + 3, 82, :_reduce_8, + 1, 83, :_reduce_none, + 1, 84, :_reduce_10, + 3, 84, :_reduce_11, + 3, 84, :_reduce_12, + 3, 84, :_reduce_13, + 3, 84, :_reduce_14, 1, 86, :_reduce_none, - 1, 91, :_reduce_none, - 1, 91, :_reduce_none, - 1, 91, :_reduce_none, - 1, 91, :_reduce_none, - 1, 91, :_reduce_none, - 1, 91, :_reduce_none, - 1, 91, :_reduce_none, - 1, 91, :_reduce_none, - 1, 91, :_reduce_none, - 1, 91, :_reduce_none, + 4, 86, :_reduce_16, + 3, 86, :_reduce_17, + 3, 86, :_reduce_18, + 3, 86, :_reduce_19, + 3, 86, :_reduce_20, + 3, 86, :_reduce_21, + 3, 86, :_reduce_22, + 3, 86, :_reduce_23, + 3, 86, :_reduce_24, + 3, 86, :_reduce_25, + 3, 86, :_reduce_26, + 2, 86, :_reduce_27, + 3, 86, :_reduce_28, + 3, 86, :_reduce_29, + 3, 86, :_reduce_30, + 3, 86, :_reduce_31, + 3, 86, :_reduce_32, + 3, 86, :_reduce_33, + 2, 86, :_reduce_34, + 3, 86, :_reduce_35, + 3, 86, :_reduce_36, + 3, 86, :_reduce_37, + 3, 86, :_reduce_38, + 3, 86, :_reduce_39, + 3, 86, :_reduce_40, + 3, 86, :_reduce_41, + 1, 88, :_reduce_42, + 3, 88, :_reduce_43, + 1, 87, :_reduce_none, 1, 92, :_reduce_none, 1, 92, :_reduce_none, 1, 92, :_reduce_none, @@ -583,152 +582,162 @@ racc_reduce_table = [ 1, 92, :_reduce_none, 1, 92, :_reduce_none, 1, 92, :_reduce_none, - 1, 107, :_reduce_62, - 1, 107, :_reduce_63, - 5, 90, :_reduce_64, - 3, 90, :_reduce_65, - 6, 90, :_reduce_66, - 4, 90, :_reduce_67, - 1, 90, :_reduce_68, - 1, 94, :_reduce_69, - 2, 94, :_reduce_70, - 4, 114, :_reduce_71, - 3, 114, :_reduce_72, - 1, 114, :_reduce_73, - 3, 115, :_reduce_74, - 2, 113, :_reduce_75, - 3, 117, :_reduce_76, - 2, 117, :_reduce_77, - 2, 116, :_reduce_78, - 4, 116, :_reduce_79, - 2, 97, :_reduce_80, - 5, 119, :_reduce_81, - 4, 119, :_reduce_82, - 0, 120, :_reduce_none, - 2, 120, :_reduce_84, - 4, 120, :_reduce_85, - 3, 120, :_reduce_86, - 6, 98, :_reduce_87, - 5, 98, :_reduce_88, + 1, 92, :_reduce_none, + 1, 92, :_reduce_none, + 1, 93, :_reduce_none, + 1, 93, :_reduce_none, + 1, 93, :_reduce_none, + 1, 93, :_reduce_none, + 1, 93, :_reduce_none, + 1, 93, :_reduce_none, + 1, 93, :_reduce_none, + 1, 93, :_reduce_none, + 1, 108, :_reduce_63, + 1, 108, :_reduce_64, + 5, 91, :_reduce_65, + 3, 91, :_reduce_66, + 6, 91, :_reduce_67, + 4, 91, :_reduce_68, + 1, 91, :_reduce_69, + 1, 95, :_reduce_70, + 2, 95, :_reduce_71, + 4, 115, :_reduce_72, + 3, 115, :_reduce_73, + 1, 115, :_reduce_74, + 3, 116, :_reduce_75, + 2, 114, :_reduce_76, + 3, 118, :_reduce_77, + 2, 118, :_reduce_78, + 2, 117, :_reduce_79, + 4, 117, :_reduce_80, + 2, 98, :_reduce_81, + 5, 120, :_reduce_82, + 4, 120, :_reduce_83, 0, 121, :_reduce_none, - 4, 121, :_reduce_90, - 3, 121, :_reduce_91, - 5, 96, :_reduce_92, - 1, 122, :_reduce_93, - 2, 122, :_reduce_94, - 5, 123, :_reduce_95, - 4, 123, :_reduce_96, - 1, 124, :_reduce_97, - 1, 89, :_reduce_none, - 4, 89, :_reduce_99, - 1, 126, :_reduce_100, - 3, 126, :_reduce_101, - 3, 125, :_reduce_102, - 1, 84, :_reduce_103, - 6, 84, :_reduce_104, - 6, 84, :_reduce_105, - 5, 84, :_reduce_106, - 5, 84, :_reduce_107, - 5, 84, :_reduce_108, - 4, 131, :_reduce_109, - 1, 132, :_reduce_110, - 1, 128, :_reduce_111, - 3, 128, :_reduce_112, - 1, 127, :_reduce_113, - 2, 127, :_reduce_114, - 6, 95, :_reduce_115, - 2, 95, :_reduce_116, - 3, 133, :_reduce_117, - 3, 133, :_reduce_118, - 1, 134, :_reduce_none, - 1, 134, :_reduce_none, - 0, 130, :_reduce_121, - 1, 130, :_reduce_122, - 3, 130, :_reduce_123, - 1, 136, :_reduce_none, - 1, 136, :_reduce_none, - 1, 136, :_reduce_none, - 3, 135, :_reduce_127, - 3, 135, :_reduce_128, - 6, 99, :_reduce_129, - 5, 99, :_reduce_130, - 7, 100, :_reduce_131, - 6, 100, :_reduce_132, - 1, 140, :_reduce_none, - 2, 140, :_reduce_134, + 2, 121, :_reduce_85, + 4, 121, :_reduce_86, + 3, 121, :_reduce_87, + 6, 99, :_reduce_88, + 5, 99, :_reduce_89, + 0, 122, :_reduce_none, + 4, 122, :_reduce_91, + 3, 122, :_reduce_92, + 5, 97, :_reduce_93, + 1, 123, :_reduce_94, + 2, 123, :_reduce_95, + 5, 124, :_reduce_96, + 4, 124, :_reduce_97, + 1, 125, :_reduce_98, + 1, 90, :_reduce_none, + 4, 90, :_reduce_100, + 1, 127, :_reduce_101, + 3, 127, :_reduce_102, + 3, 126, :_reduce_103, + 1, 85, :_reduce_104, + 6, 85, :_reduce_105, + 6, 85, :_reduce_106, + 5, 85, :_reduce_107, + 5, 85, :_reduce_108, + 5, 85, :_reduce_109, + 4, 132, :_reduce_110, + 1, 133, :_reduce_111, + 1, 129, :_reduce_112, + 3, 129, :_reduce_113, + 1, 128, :_reduce_114, + 2, 128, :_reduce_115, + 6, 96, :_reduce_116, + 2, 96, :_reduce_117, + 3, 134, :_reduce_118, + 3, 134, :_reduce_119, + 1, 135, :_reduce_none, + 1, 135, :_reduce_none, + 0, 131, :_reduce_122, + 1, 131, :_reduce_123, + 3, 131, :_reduce_124, + 1, 137, :_reduce_none, + 1, 137, :_reduce_none, + 1, 137, :_reduce_none, + 3, 136, :_reduce_128, + 3, 136, :_reduce_129, + 6, 100, :_reduce_130, + 5, 100, :_reduce_131, + 7, 101, :_reduce_132, + 6, 101, :_reduce_133, 1, 141, :_reduce_none, - 1, 141, :_reduce_none, - 6, 101, :_reduce_137, - 5, 101, :_reduce_138, - 1, 142, :_reduce_139, - 3, 142, :_reduce_140, - 1, 144, :_reduce_141, - 1, 144, :_reduce_142, - 1, 144, :_reduce_143, + 2, 141, :_reduce_135, + 1, 142, :_reduce_none, + 1, 142, :_reduce_none, + 6, 102, :_reduce_138, + 5, 102, :_reduce_139, + 1, 143, :_reduce_140, + 3, 143, :_reduce_141, + 1, 145, :_reduce_142, + 1, 145, :_reduce_143, + 1, 145, :_reduce_144, + 1, 145, :_reduce_none, 1, 144, :_reduce_none, - 1, 143, :_reduce_none, - 2, 143, :_reduce_146, - 1, 138, :_reduce_147, - 1, 138, :_reduce_148, + 2, 144, :_reduce_147, + 1, 139, :_reduce_148, 1, 139, :_reduce_149, - 2, 139, :_reduce_150, - 4, 139, :_reduce_151, - 1, 118, :_reduce_152, - 3, 118, :_reduce_153, - 3, 145, :_reduce_154, - 1, 145, :_reduce_155, - 1, 88, :_reduce_none, - 1, 88, :_reduce_none, - 1, 93, :_reduce_158, - 3, 102, :_reduce_159, - 4, 102, :_reduce_160, - 2, 102, :_reduce_161, - 3, 105, :_reduce_162, - 4, 105, :_reduce_163, - 2, 105, :_reduce_164, - 1, 146, :_reduce_165, - 3, 146, :_reduce_166, + 1, 140, :_reduce_150, + 2, 140, :_reduce_151, + 4, 140, :_reduce_152, + 1, 119, :_reduce_153, + 3, 119, :_reduce_154, + 3, 146, :_reduce_155, + 1, 146, :_reduce_156, + 1, 89, :_reduce_none, + 1, 89, :_reduce_none, + 1, 94, :_reduce_159, + 3, 103, :_reduce_160, + 4, 103, :_reduce_161, + 2, 103, :_reduce_162, + 3, 106, :_reduce_163, + 4, 106, :_reduce_164, + 2, 106, :_reduce_165, + 1, 147, :_reduce_166, 3, 147, :_reduce_167, - 1, 111, :_reduce_none, - 1, 111, :_reduce_none, - 1, 148, :_reduce_170, - 2, 149, :_reduce_171, - 1, 150, :_reduce_172, - 1, 152, :_reduce_173, + 3, 148, :_reduce_168, + 1, 112, :_reduce_none, + 1, 112, :_reduce_none, + 1, 149, :_reduce_171, + 2, 150, :_reduce_172, + 1, 151, :_reduce_173, 1, 153, :_reduce_174, - 2, 151, :_reduce_175, - 1, 154, :_reduce_176, + 1, 154, :_reduce_175, + 2, 152, :_reduce_176, 1, 155, :_reduce_177, - 2, 155, :_reduce_178, - 1, 110, :_reduce_179, - 1, 108, :_reduce_180, + 1, 156, :_reduce_178, + 2, 156, :_reduce_179, + 1, 111, :_reduce_180, 1, 109, :_reduce_181, - 1, 104, :_reduce_182, - 1, 103, :_reduce_183, - 1, 106, :_reduce_184, - 0, 112, :_reduce_none, - 1, 112, :_reduce_186, - 0, 129, :_reduce_none, - 1, 129, :_reduce_none, - 1, 137, :_reduce_none, - 1, 137, :_reduce_none, - 1, 137, :_reduce_none, - 1, 137, :_reduce_none, - 1, 137, :_reduce_none, - 1, 137, :_reduce_none, - 1, 137, :_reduce_none, - 1, 137, :_reduce_none, - 1, 137, :_reduce_none, - 1, 137, :_reduce_none, - 1, 137, :_reduce_none, - 1, 137, :_reduce_none, - 1, 137, :_reduce_none, - 1, 137, :_reduce_none, - 0, 79, :_reduce_203 ] + 1, 110, :_reduce_182, + 1, 105, :_reduce_183, + 1, 104, :_reduce_184, + 1, 107, :_reduce_185, + 0, 113, :_reduce_none, + 1, 113, :_reduce_187, + 0, 130, :_reduce_none, + 1, 130, :_reduce_none, + 1, 138, :_reduce_none, + 1, 138, :_reduce_none, + 1, 138, :_reduce_none, + 1, 138, :_reduce_none, + 1, 138, :_reduce_none, + 1, 138, :_reduce_none, + 1, 138, :_reduce_none, + 1, 138, :_reduce_none, + 1, 138, :_reduce_none, + 1, 138, :_reduce_none, + 1, 138, :_reduce_none, + 1, 138, :_reduce_none, + 1, 138, :_reduce_none, + 1, 138, :_reduce_none, + 0, 80, :_reduce_204 ] -racc_reduce_n = 204 +racc_reduce_n = 205 -racc_shift_n = 355 +racc_shift_n = 357 racc_token_table = { false => 0, @@ -805,10 +814,11 @@ racc_token_table = { :HIGH => 71, :CALL => 72, :MODULO => 73, - :TITLE_COLON => 74, - :CASE_COLON => 75 } + :DELETES => 74, + :TITLE_COLON => 75, + :CASE_COLON => 76 } -racc_nt_base = 76 +racc_nt_base = 77 racc_use_result_var = true @@ -903,6 +913,7 @@ Racc_token_to_s_table = [ "HIGH", "CALL", "MODULO", + "DELETES", "TITLE_COLON", "CASE_COLON", "$start", @@ -1245,33 +1256,38 @@ module_eval(<<'.,.,', 'egrammar.ra', 114) module_eval(<<'.,.,', 'egrammar.ra', 115) def _reduce_39(val, _values, result) - result = val[0].select(*val[2]) ; loc result, val[0] + result = val[0].minus_set(val[2]); loc result, val[1] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 116) def _reduce_40(val, _values, result) - result = val[1].paren() ; loc result, val[0] + result = val[0].select(*val[2]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 124) +module_eval(<<'.,.,', 'egrammar.ra', 117) def _reduce_41(val, _values, result) - result = [val[0]] + result = val[1].paren() ; loc result, val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 125) def _reduce_42(val, _values, result) - result = val[0].push(val[2]) + result = [val[0]] result end .,., -# reduce 43 omitted +module_eval(<<'.,.,', 'egrammar.ra', 126) + def _reduce_43(val, _values, result) + result = val[0].push(val[2]) + result + end +.,., # reduce 44 omitted @@ -1309,12 +1325,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 125) # reduce 61 omitted -module_eval(<<'.,.,', 'egrammar.ra', 155) - def _reduce_62(val, _values, result) - result = val[0] - result - end -.,., +# reduce 62 omitted module_eval(<<'.,.,', 'egrammar.ra', 156) def _reduce_63(val, _values, result) @@ -1323,8 +1334,15 @@ module_eval(<<'.,.,', 'egrammar.ra', 156) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 164) +module_eval(<<'.,.,', 'egrammar.ra', 157) def _reduce_64(val, _values, result) + result = val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 165) + def _reduce_65(val, _values, result) result = Factory.CALL_NAMED(val[0], true, val[2]) loc result, val[0], val[4] @@ -1332,8 +1350,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 164) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 168) - def _reduce_65(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 169) + def _reduce_66(val, _values, result) result = Factory.CALL_NAMED(val[0], true, []) loc result, val[0], val[2] @@ -1341,8 +1359,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 168) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 172) - def _reduce_66(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 173) + def _reduce_67(val, _values, result) result = Factory.CALL_NAMED(val[0], true, val[2]) loc result, val[0], val[4] result.lambda = val[5] @@ -1351,8 +1369,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 172) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 177) - def _reduce_67(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 178) + def _reduce_68(val, _values, result) result = Factory.CALL_NAMED(val[0], true, []) loc result, val[0], val[2] result.lambda = val[3] @@ -1361,14 +1379,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 177) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 181) - def _reduce_68(val, _values, result) - result = val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 186) +module_eval(<<'.,.,', 'egrammar.ra', 182) def _reduce_69(val, _values, result) result = val[0] result @@ -1377,34 +1388,41 @@ module_eval(<<'.,.,', 'egrammar.ra', 186) module_eval(<<'.,.,', 'egrammar.ra', 187) def _reduce_70(val, _values, result) - result = val[0]; val[0].lambda = val[1] + result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 190) +module_eval(<<'.,.,', 'egrammar.ra', 188) def _reduce_71(val, _values, result) - result = Factory.CALL_METHOD(val[0], val[2]); loc result, val[1], val[3] + result = val[0]; val[0].lambda = val[1] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 191) def _reduce_72(val, _values, result) - result = Factory.CALL_METHOD(val[0], []); loc result, val[1], val[3] + result = Factory.CALL_METHOD(val[0], val[2]); loc result, val[1], val[3] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 192) def _reduce_73(val, _values, result) + result = Factory.CALL_METHOD(val[0], []); loc result, val[1], val[3] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 193) + def _reduce_74(val, _values, result) result = Factory.CALL_METHOD(val[0], []); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 197) - def _reduce_74(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 198) + def _reduce_75(val, _values, result) result = val[0].dot(Factory.fqn(val[2][:value])) loc result, val[1], val[2] @@ -1412,8 +1430,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 197) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 209) - def _reduce_75(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 210) + def _reduce_76(val, _values, result) result = Factory.LAMBDA(val[0], val[1]) # loc result, val[1] # TODO @@ -1421,36 +1439,36 @@ module_eval(<<'.,.,', 'egrammar.ra', 209) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 214) - def _reduce_76(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 215) + def _reduce_77(val, _values, result) result = val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 215) - def _reduce_77(val, _values, result) - result = nil - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 219) +module_eval(<<'.,.,', 'egrammar.ra', 216) def _reduce_78(val, _values, result) - result = [] + result = nil result end .,., module_eval(<<'.,.,', 'egrammar.ra', 220) def _reduce_79(val, _values, result) + result = [] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 221) + def _reduce_80(val, _values, result) result = val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 230) - def _reduce_80(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 231) + def _reduce_81(val, _values, result) result = val[1] loc(result, val[0], val[1]) @@ -1458,8 +1476,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 230) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 237) - def _reduce_81(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 238) + def _reduce_82(val, _values, result) result = Factory.IF(val[0], Factory.block_or_expression(*val[2]), val[4]) loc(result, val[0], (val[4] ? val[4] : val[3])) @@ -1467,8 +1485,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 237) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 241) - def _reduce_82(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 242) + def _reduce_83(val, _values, result) result = Factory.IF(val[0], nil, val[3]) loc(result, val[0], (val[3] ? val[3] : val[2])) @@ -1476,10 +1494,10 @@ module_eval(<<'.,.,', 'egrammar.ra', 241) end .,., -# reduce 83 omitted +# reduce 84 omitted -module_eval(<<'.,.,', 'egrammar.ra', 249) - def _reduce_84(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 250) + def _reduce_85(val, _values, result) result = val[1] loc(result, val[0], val[1]) @@ -1487,8 +1505,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 249) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 253) - def _reduce_85(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 254) + def _reduce_86(val, _values, result) result = Factory.block_or_expression(*val[2]) loc result, val[0], val[3] @@ -1496,16 +1514,16 @@ module_eval(<<'.,.,', 'egrammar.ra', 253) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 257) - def _reduce_86(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 258) + def _reduce_87(val, _values, result) result = nil # don't think a nop is needed here either result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 266) - def _reduce_87(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 267) + def _reduce_88(val, _values, result) result = Factory.UNLESS(val[1], Factory.block_or_expression(*val[3]), val[5]) loc result, val[0], val[4] @@ -1513,8 +1531,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 266) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 270) - def _reduce_88(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 271) + def _reduce_89(val, _values, result) result = Factory.UNLESS(val[1], nil, nil) loc result, val[0], val[4] @@ -1522,10 +1540,10 @@ module_eval(<<'.,.,', 'egrammar.ra', 270) end .,., -# reduce 89 omitted +# reduce 90 omitted -module_eval(<<'.,.,', 'egrammar.ra', 280) - def _reduce_90(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 281) + def _reduce_91(val, _values, result) result = Factory.block_or_expression(*val[2]) loc result, val[0], val[3] @@ -1533,16 +1551,16 @@ module_eval(<<'.,.,', 'egrammar.ra', 280) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 284) - def _reduce_91(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 285) + def _reduce_92(val, _values, result) result = nil # don't think a nop is needed here either result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 292) - def _reduce_92(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 293) + def _reduce_93(val, _values, result) result = Factory.CASE(val[1], *val[3]) loc result, val[0], val[4] @@ -1550,22 +1568,22 @@ module_eval(<<'.,.,', 'egrammar.ra', 292) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 298) - def _reduce_93(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 299) + def _reduce_94(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 299) - def _reduce_94(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 300) + def _reduce_95(val, _values, result) result = val[0].push val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 304) - def _reduce_95(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 305) + def _reduce_96(val, _values, result) result = Factory.WHEN(val[0], val[3]) loc result, val[1], val[4] @@ -1573,8 +1591,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 304) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 308) - def _reduce_96(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 309) + def _reduce_97(val, _values, result) result = Factory.WHEN(val[0], nil) loc result, val[1], val[3] @@ -1582,54 +1600,54 @@ module_eval(<<'.,.,', 'egrammar.ra', 308) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 312) - def _reduce_97(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 313) + def _reduce_98(val, _values, result) result = val[0] result end .,., -# reduce 98 omitted +# reduce 99 omitted -module_eval(<<'.,.,', 'egrammar.ra', 323) - def _reduce_99(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 324) + def _reduce_100(val, _values, result) result = val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 328) - def _reduce_100(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 329) + def _reduce_101(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 329) - def _reduce_101(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 330) + def _reduce_102(val, _values, result) result = val[0].push val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 334) - def _reduce_102(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 335) + def _reduce_103(val, _values, result) result = Factory.MAP(val[0], val[2]) ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 350) - def _reduce_103(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 351) + def _reduce_104(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 353) - def _reduce_104(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 354) + def _reduce_105(val, _values, result) result = case Factory.resource_shape(val[1]) when :resource, :class tmp = Factory.RESOURCE(Factory.fqn(token_text(val[1])), val[3]) @@ -1648,8 +1666,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 353) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 368) - def _reduce_105(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 369) + def _reduce_106(val, _values, result) result = case Factory.resource_shape(val[1]) when :resource, :class error "Defaults are not virtualizable" @@ -1665,8 +1683,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 368) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 380) - def _reduce_106(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 381) + def _reduce_107(val, _values, result) result = case Factory.resource_shape(val[0]) when :resource, :class Factory.RESOURCE(Factory.fqn(token_text(val[0])), val[2]) @@ -1683,8 +1701,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 380) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 393) - def _reduce_107(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 394) + def _reduce_108(val, _values, result) result = case Factory.resource_shape(val[0]) when :resource, :class # This catches deprecated syntax. @@ -1703,8 +1721,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 393) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 408) - def _reduce_108(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 409) + def _reduce_109(val, _values, result) result = Factory.RESOURCE(Factory.fqn(token_text(val[0])), val[2]) loc result, val[0], val[4] @@ -1712,50 +1730,50 @@ module_eval(<<'.,.,', 'egrammar.ra', 408) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 413) - def _reduce_109(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 414) + def _reduce_110(val, _values, result) result = Factory.RESOURCE_BODY(val[0], val[2]) result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 415) - def _reduce_110(val, _values, result) - result = val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 418) +module_eval(<<'.,.,', 'egrammar.ra', 416) def _reduce_111(val, _values, result) - result = [val[0]] + result = val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 419) def _reduce_112(val, _values, result) - result = val[0].push val[2] + result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 424) +module_eval(<<'.,.,', 'egrammar.ra', 420) def _reduce_113(val, _values, result) - result = :virtual + result = val[0].push val[2] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 425) def _reduce_114(val, _values, result) + result = :virtual + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 426) + def _reduce_115(val, _values, result) result = :exported result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 437) - def _reduce_115(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 438) + def _reduce_116(val, _values, result) result = Factory.COLLECT(val[0], val[1], val[3]) loc result, val[0], val[5] @@ -1763,8 +1781,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 437) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 441) - def _reduce_116(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 442) + def _reduce_117(val, _values, result) result = Factory.COLLECT(val[0], val[1], []) loc result, val[0], val[1] @@ -1772,53 +1790,53 @@ module_eval(<<'.,.,', 'egrammar.ra', 441) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 446) - def _reduce_117(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 447) + def _reduce_118(val, _values, result) result = Factory.VIRTUAL_QUERY(val[1]) ; loc result, val[0], val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 447) - def _reduce_118(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 448) + def _reduce_119(val, _values, result) result = Factory.EXPORTED_QUERY(val[1]) ; loc result, val[0], val[2] result end .,., -# reduce 119 omitted - # reduce 120 omitted -module_eval(<<'.,.,', 'egrammar.ra', 460) - def _reduce_121(val, _values, result) - result = [] - result - end -.,., +# reduce 121 omitted module_eval(<<'.,.,', 'egrammar.ra', 461) def _reduce_122(val, _values, result) - result = [val[0]] + result = [] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 462) def _reduce_123(val, _values, result) + result = [val[0]] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 463) + def _reduce_124(val, _values, result) result = val[0].push(val[2]) result end .,., -# reduce 124 omitted - # reduce 125 omitted # reduce 126 omitted -module_eval(<<'.,.,', 'egrammar.ra', 478) - def _reduce_127(val, _values, result) +# reduce 127 omitted + +module_eval(<<'.,.,', 'egrammar.ra', 479) + def _reduce_128(val, _values, result) result = Factory.ATTRIBUTE_OP(val[0][:value], :'=>', val[2]) loc result, val[0], val[2] @@ -1826,8 +1844,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 478) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 482) - def _reduce_128(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 483) + def _reduce_129(val, _values, result) result = Factory.ATTRIBUTE_OP(val[0][:value], :'+>', val[2]) loc result, val[0], val[2] @@ -1835,8 +1853,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 482) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 492) - def _reduce_129(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 493) + def _reduce_130(val, _values, result) result = Factory.DEFINITION(classname(val[1][:value]), val[2], val[4]) loc result, val[0], val[5] @lexer.indefine = false @@ -1845,8 +1863,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 492) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 497) - def _reduce_130(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 498) + def _reduce_131(val, _values, result) result = Factory.DEFINITION(classname(val[1][:value]), val[2], nil) loc result, val[0], val[4] @lexer.indefine = false @@ -1855,8 +1873,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 497) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 512) - def _reduce_131(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 513) + def _reduce_132(val, _values, result) @lexer.namepop result = Factory.HOSTCLASS(classname(val[1][:value]), val[2], token_text(val[3]), val[5]) loc result, val[0], val[6] @@ -1865,8 +1883,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 512) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 517) - def _reduce_132(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 518) + def _reduce_133(val, _values, result) @lexer.namepop result = Factory.HOSTCLASS(classname(val[1][:value]), val[2], token_text(val[3]), nil) loc result, val[0], val[5] @@ -1875,21 +1893,21 @@ module_eval(<<'.,.,', 'egrammar.ra', 517) end .,., -# reduce 133 omitted +# reduce 134 omitted -module_eval(<<'.,.,', 'egrammar.ra', 525) - def _reduce_134(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 526) + def _reduce_135(val, _values, result) result = val[1] result end .,., -# reduce 135 omitted - # reduce 136 omitted -module_eval(<<'.,.,', 'egrammar.ra', 542) - def _reduce_137(val, _values, result) +# reduce 137 omitted + +module_eval(<<'.,.,', 'egrammar.ra', 543) + def _reduce_138(val, _values, result) result = Factory.NODE(val[1], val[2], val[4]) loc result, val[0], val[5] @@ -1897,8 +1915,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 542) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 546) - def _reduce_138(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 547) + def _reduce_139(val, _values, result) result = Factory.NODE(val[1], val[2], nil) loc result, val[0], val[4] @@ -1906,55 +1924,48 @@ module_eval(<<'.,.,', 'egrammar.ra', 546) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 556) - def _reduce_139(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 557) + def _reduce_140(val, _values, result) result = [result] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 557) - def _reduce_140(val, _values, result) - result = val[0].push(val[2]) - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 562) +module_eval(<<'.,.,', 'egrammar.ra', 558) def _reduce_141(val, _values, result) - result = Factory.fqn(val[0][:value]); loc result, val[0] + result = val[0].push(val[2]) result end .,., module_eval(<<'.,.,', 'egrammar.ra', 563) def _reduce_142(val, _values, result) - result = val[0] + result = Factory.fqn(val[0][:value]); loc result, val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 564) def _reduce_143(val, _values, result) + result = val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 565) + def _reduce_144(val, _values, result) result = Factory.literal(:default); loc result, val[0] result end .,., -# reduce 144 omitted - # reduce 145 omitted -module_eval(<<'.,.,', 'egrammar.ra', 570) - def _reduce_146(val, _values, result) - result = val[1] - result - end -.,., +# reduce 146 omitted -module_eval(<<'.,.,', 'egrammar.ra', 575) +module_eval(<<'.,.,', 'egrammar.ra', 571) def _reduce_147(val, _values, result) - result = val[0] + result = val[1] result end .,., @@ -1966,9 +1977,9 @@ module_eval(<<'.,.,', 'egrammar.ra', 576) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 580) +module_eval(<<'.,.,', 'egrammar.ra', 577) def _reduce_149(val, _values, result) - result = [] + result = val[0] result end .,., @@ -1982,134 +1993,134 @@ module_eval(<<'.,.,', 'egrammar.ra', 581) module_eval(<<'.,.,', 'egrammar.ra', 582) def _reduce_151(val, _values, result) - result = val[1] + result = [] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 586) +module_eval(<<'.,.,', 'egrammar.ra', 583) def _reduce_152(val, _values, result) - result = [val[0]] + result = val[1] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 587) def _reduce_153(val, _values, result) - result = val[0].push(val[2]) + result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 591) +module_eval(<<'.,.,', 'egrammar.ra', 588) def _reduce_154(val, _values, result) - result = Factory.PARAM(val[0][:value], val[2]) ; loc result, val[0] + result = val[0].push(val[2]) result end .,., module_eval(<<'.,.,', 'egrammar.ra', 592) def _reduce_155(val, _values, result) + result = Factory.PARAM(val[0][:value], val[2]) ; loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 593) + def _reduce_156(val, _values, result) result = Factory.PARAM(val[0][:value]); loc result, val[0] result end .,., -# reduce 156 omitted - # reduce 157 omitted -module_eval(<<'.,.,', 'egrammar.ra', 605) - def _reduce_158(val, _values, result) - result = Factory.fqn(val[0][:value]).var ; loc result, val[0] - result - end -.,., +# reduce 158 omitted -module_eval(<<'.,.,', 'egrammar.ra', 611) +module_eval(<<'.,.,', 'egrammar.ra', 606) def _reduce_159(val, _values, result) - result = Factory.LIST(val[1]); loc result, val[0], val[2] + result = Factory.fqn(val[0][:value]).var ; loc result, val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 612) def _reduce_160(val, _values, result) - result = Factory.LIST(val[1]); loc result, val[0], val[3] + result = Factory.LIST(val[1]); loc result, val[0], val[2] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 613) def _reduce_161(val, _values, result) - result = Factory.literal([]) ; loc result, val[0] + result = Factory.LIST(val[1]); loc result, val[0], val[3] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 616) +module_eval(<<'.,.,', 'egrammar.ra', 614) def _reduce_162(val, _values, result) - result = Factory.HASH(val[1]); loc result, val[0], val[2] + result = Factory.literal([]) ; loc result, val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 617) def _reduce_163(val, _values, result) - result = Factory.HASH(val[1]); loc result, val[0], val[3] + result = Factory.HASH(val[1]); loc result, val[0], val[2] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 618) def _reduce_164(val, _values, result) - result = Factory.literal({}) ; loc result, val[0], val[3] + result = Factory.HASH(val[1]); loc result, val[0], val[3] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 621) +module_eval(<<'.,.,', 'egrammar.ra', 619) def _reduce_165(val, _values, result) - result = [val[0]] + result = Factory.literal({}) ; loc result, val[0], val[3] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 622) def _reduce_166(val, _values, result) + result = [val[0]] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 623) + def _reduce_167(val, _values, result) result = val[0].push val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 625) - def _reduce_167(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 626) + def _reduce_168(val, _values, result) result = Factory.KEY_ENTRY(val[0], val[2]); loc result, val[1] result end .,., -# reduce 168 omitted - # reduce 169 omitted -module_eval(<<'.,.,', 'egrammar.ra', 631) - def _reduce_170(val, _values, result) - result = Factory.literal(val[0][:value]) ; loc result, val[0] - result - end -.,., +# reduce 170 omitted module_eval(<<'.,.,', 'egrammar.ra', 632) def _reduce_171(val, _values, result) - result = Factory.string(val[0], *val[1]) ; loc result, val[0], val[1][-1] + result = Factory.literal(val[0][:value]) ; loc result, val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 633) def _reduce_172(val, _values, result) - result = Factory.literal(val[0][:value]); loc result, val[0] + result = Factory.string(val[0], *val[1]) ; loc result, val[0], val[1][-1] result end .,., @@ -2130,85 +2141,90 @@ module_eval(<<'.,.,', 'egrammar.ra', 635) module_eval(<<'.,.,', 'egrammar.ra', 636) def _reduce_175(val, _values, result) - result = [val[0]] + val[1] + result = Factory.literal(val[0][:value]); loc result, val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 637) def _reduce_176(val, _values, result) - result = Factory.TEXT(val[0]) + result = [val[0]] + val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 640) +module_eval(<<'.,.,', 'egrammar.ra', 638) def _reduce_177(val, _values, result) - result = [val[0]] + result = Factory.TEXT(val[0]) result end .,., module_eval(<<'.,.,', 'egrammar.ra', 641) def _reduce_178(val, _values, result) - result = [val[0]] + val[1] + result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 643) +module_eval(<<'.,.,', 'egrammar.ra', 642) def _reduce_179(val, _values, result) - result = Factory.QNAME_OR_NUMBER(val[0][:value]) ; loc result, val[0] + result = [val[0]] + val[1] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 644) def _reduce_180(val, _values, result) - result = Factory.QREF(val[0][:value]) ; loc result, val[0] + result = Factory.QNAME_OR_NUMBER(val[0][:value]) ; loc result, val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 645) def _reduce_181(val, _values, result) - result = Factory.literal(:undef); loc result, val[0] + result = Factory.QREF(val[0][:value]) ; loc result, val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 646) def _reduce_182(val, _values, result) + result = Factory.literal(:undef); loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 647) + def _reduce_183(val, _values, result) result = Factory.literal(:default); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 651) - def _reduce_183(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 652) + def _reduce_184(val, _values, result) result = Factory.literal(val[0][:value]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 654) - def _reduce_184(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 655) + def _reduce_185(val, _values, result) result = Factory.literal(val[0][:value]); loc result, val[0] result end .,., -# reduce 185 omitted +# reduce 186 omitted -module_eval(<<'.,.,', 'egrammar.ra', 660) - def _reduce_186(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 661) + def _reduce_187(val, _values, result) result = nil result end .,., -# reduce 187 omitted - # reduce 188 omitted # reduce 189 omitted @@ -2239,8 +2255,10 @@ module_eval(<<'.,.,', 'egrammar.ra', 660) # reduce 202 omitted -module_eval(<<'.,.,', 'egrammar.ra', 683) - def _reduce_203(val, _values, result) +# reduce 203 omitted + +module_eval(<<'.,.,', 'egrammar.ra', 684) + def _reduce_204(val, _values, result) result = nil result end diff --git a/lib/puppet/pops/parser/lexer.rb b/lib/puppet/pops/parser/lexer.rb index 994c4643f..060d501c1 100644 --- a/lib/puppet/pops/parser/lexer.rb +++ b/lib/puppet/pops/parser/lexer.rb @@ -154,6 +154,7 @@ class Puppet::Pops::Parser::Lexer ')' => :RPAREN, '=' => :EQUALS, '+=' => :APPENDS, + '-=' => :DELETES, '==' => :ISEQUAL, '>=' => :GREATEREQUAL, '>' => :GREATERTHAN, diff --git a/lib/puppet/pops/validation/checker3_1.rb b/lib/puppet/pops/validation/checker3_1.rb index 839df5044..be277aba6 100644 --- a/lib/puppet/pops/validation/checker3_1.rb +++ b/lib/puppet/pops/validation/checker3_1.rb @@ -142,6 +142,7 @@ class Puppet::Pops::Validation::Checker3_1 end def check_AssignmentExpression(o) + acceptor.accept(Issues::UNSUPPORTED_OPERATOR, o, {:operator => o.operator}) unless [:'=', :'+='].include? o.operator assign(o.left_expr) rvalue(o.right_expr) end From 3c1c376ab62b02dc97748cdb45a6b60bdbb7bfc4 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 16 Sep 2013 00:22:31 +0200 Subject: [PATCH 026/800] (#22363) Add validation test for -= (UNSUPPORTED_OPERATOR) in Puppet 3 --- spec/unit/pops/validator/validator_spec.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/unit/pops/validator/validator_spec.rb b/spec/unit/pops/validator/validator_spec.rb index d54d2ca61..16052d2cc 100644 --- a/spec/unit/pops/validator/validator_spec.rb +++ b/spec/unit/pops/validator/validator_spec.rb @@ -28,4 +28,8 @@ describe "validating 3x" do expect(validate(fqn('AAA').var())).to have_issue(Puppet::Pops::Issues::ILLEGAL_NAME) end + it 'should raise error for -= assignment' do + expect(validate(fqn('aaa').minus_set(2))).to have_issue(Puppet::Pops::Issues::UNSUPPORTED_OPERATOR) + end + end \ No newline at end of file From d1bd7e7aa2007ed3152b5e6a6de528ceed04ca99 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 16 Sep 2013 00:44:01 +0200 Subject: [PATCH 027/800] (#22363) Add examples for -= and += (parser) and fix issues in evaluator This adds more examples for -= and += for the parser and fixes issues in the evaluator when evaluating -=. --- lib/puppet/pops/evaluator/evaluator_impl.rb | 10 ++++---- spec/unit/pops/evaluator/variables_spec.rb | 24 +++++++++++++++++++ spec/unit/pops/parser/lexer_spec.rb | 1 + .../parser/parse_basic_expressions_spec.rb | 8 +++++++ 4 files changed, 37 insertions(+), 6 deletions(-) diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index ad8f5da78..e28e731a1 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -219,7 +219,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator name, value = eval_BinaryExpression(o, scope) case o.operator - when :'=' # normal assignment + when :'=' # regular assignment assign(name, value, o, scope) when :'+=' @@ -235,20 +235,18 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator fail("Append assignment += failed with error: #{e.message}", o, scope) end - # TODO: Add concrete syntax for this in lexer and parser - # when :'-=' # If an attempt is made to delete values from something that does not exists, the value is :undef (it is guaranteed to not # include any values the user wants deleted anyway :-) # - if !variable_exists?(scope, name) + if !variable_exists?(name, scope) return nil end begin # Delegate to delete function to deal with check of LHS, and perform deletion - assign(name, delete(get_variable_value(scope, name), value), o, scope) + assign(name, delete(get_variable_value(name, o, scope), value), o, scope) rescue ArgumentError => e - fail("Without assignment -= failed with error: #{e.message}", o, scope) + fail("'Without' assignment -= failed with error: #{e.message}", o, scope) end else fail("Unknown assignment operator: '#{o.operator}'.", o, scope) diff --git a/spec/unit/pops/evaluator/variables_spec.rb b/spec/unit/pops/evaluator/variables_spec.rb index 06080b175..0896d7c72 100644 --- a/spec/unit/pops/evaluator/variables_spec.rb +++ b/spec/unit/pops/evaluator/variables_spec.rb @@ -49,7 +49,31 @@ describe 'Puppet::Pops::Impl::EvaluatorImpl' do expect { evaluate_l(block(fqn('a').set(10), fqn('a').set(20))) }.to raise_error(/Cannot reassign variable a/) end + context "-= operations" do + # Also see collections_ops_spec.rb where delete via - is fully tested, here only the + # the -= operation itself is tested (there are many combinations) + # + it 'deleting from non existing value produces nil, nil -= ?' do + top_scope_block = fqn('b').set([1,2,3]) + local_scope_block = fqn('a').minus_set([4]) + evaluate_l(top_scope_block, local_scope_block).should == nil + end + + it 'deletes from a list' do + top_scope_block = fqn('a').set([1,2,3]) + local_scope_block = block(fqn('a').minus_set([2]), fqn('a').var()) + evaluate_l(top_scope_block, local_scope_block).should == [1,3] + end + + it 'deletes from a hash' do + top_scope_block = fqn('a').set({'a'=>1,'b'=>2,'c'=>3}) + local_scope_block = block(fqn('a').minus_set('b'), fqn('a').var()) + evaluate_l(top_scope_block, local_scope_block).should == {'a'=>1,'c'=>3} + end + end + context "+= operations" do + # Also see collections_ops_spec.rb where concatenation via + is fully tested it "appending to non existing value, nil += []" do top_scope_block = fqn('b').set([1,2,3]) local_scope_block = fqn('a').plus_set([4]) diff --git a/spec/unit/pops/parser/lexer_spec.rb b/spec/unit/pops/parser/lexer_spec.rb index 71010c033..4eaf39e6d 100755 --- a/spec/unit/pops/parser/lexer_spec.rb +++ b/spec/unit/pops/parser/lexer_spec.rb @@ -179,6 +179,7 @@ describe Puppet::Pops::Parser::Lexer::TOKENS do :FARROW => '=>', :PARROW => '+>', :APPENDS => '+=', + :DELETES => '-=', :PLUS => '+', :MINUS => '-', :DIV => '/', diff --git a/spec/unit/pops/parser/parse_basic_expressions_spec.rb b/spec/unit/pops/parser/parse_basic_expressions_spec.rb index 6560e62e7..74079b2e3 100644 --- a/spec/unit/pops/parser/parse_basic_expressions_spec.rb +++ b/spec/unit/pops/parser/parse_basic_expressions_spec.rb @@ -179,6 +179,14 @@ describe "egrammar parsing basic expressions" do dump(parse("$a = 10")).should == "(= $a 10)" end + it "Should allow append assignment" do + dump(parse("$a += 10")).should == "(+= $a 10)" + end + + it "Should allow without assignment" do + dump(parse("$a -= 10")).should == "(-= $a 10)" + end + it "Should allow chained assignment" do dump(parse("$a = $b = 10")).should == "(= $a (= $b 10))" end From 51ef75ecbb7cb53d13f7429114b690cd6fa00237 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 16 Sep 2013 18:59:39 +0200 Subject: [PATCH 028/800] (#22363) Add evaluation of calls / lambdas --- lib/puppet/parser/ast/lambda.rb | 5 + lib/puppet/parser/functions/each.rb | 2 +- lib/puppet/parser/functions/filter.rb | 2 +- lib/puppet/parser/functions/lookup.rb | 4 +- lib/puppet/parser/functions/reduce.rb | 2 +- lib/puppet/parser/functions/slice.rb | 2 +- lib/puppet/pops/evaluator/closure.rb | 28 +++++ lib/puppet/pops/evaluator/evaluator_impl.rb | 113 +++++++++++++++++- lib/puppet/pops/evaluator/runtime3_support.rb | 35 ++++++ 9 files changed, 184 insertions(+), 9 deletions(-) create mode 100644 lib/puppet/pops/evaluator/closure.rb diff --git a/lib/puppet/parser/ast/lambda.rb b/lib/puppet/parser/ast/lambda.rb index 976f817cd..74ffa710c 100644 --- a/lib/puppet/parser/ast/lambda.rb +++ b/lib/puppet/parser/ast/lambda.rb @@ -122,5 +122,10 @@ class Puppet::Parser::AST result << "| ... }" result.join('') end + + # marker method checked with respond_to :puppet_lambda + def puppet_lambda() + true + end end end diff --git a/lib/puppet/parser/functions/each.rb b/lib/puppet/parser/functions/each.rb index 3a522098d..70d8cb2bc 100644 --- a/lib/puppet/parser/functions/each.rb +++ b/lib/puppet/parser/functions/each.rb @@ -82,7 +82,7 @@ Puppet::Parser::Functions::newfunction( raise ArgumentError, ("each(): wrong number of arguments (#{args.length}; must be 2)") if args.length != 2 receiver = args[0] pblock = args[1] - raise ArgumentError, ("each(): wrong argument type (#{args[1].class}; must be a parameterized block.") unless pblock.is_a? Puppet::Parser::AST::Lambda + raise ArgumentError, ("each(): wrong argument type (#{args[1].class}; must be a parameterized block.") unless pblock.respond_to?(:puppet_lambda) case receiver when Array diff --git a/lib/puppet/parser/functions/filter.rb b/lib/puppet/parser/functions/filter.rb index 7894fa48f..ca17a386d 100644 --- a/lib/puppet/parser/functions/filter.rb +++ b/lib/puppet/parser/functions/filter.rb @@ -32,7 +32,7 @@ Puppet::Parser::Functions::newfunction( receiver = args[0] pblock = args[1] - raise ArgumentError, ("filter(): wrong argument type (#{pblock.class}; must be a parameterized block.") unless pblock.is_a? Puppet::Parser::AST::Lambda + raise ArgumentError, ("reject(): wrong argument type (#{pblock.class}; must be a parameterized block.") unless pblock.respond_to?(:puppet_lambda) case receiver when Array diff --git a/lib/puppet/parser/functions/lookup.rb b/lib/puppet/parser/functions/lookup.rb index 47ab719a0..800c9bd3d 100644 --- a/lib/puppet/parser/functions/lookup.rb +++ b/lib/puppet/parser/functions/lookup.rb @@ -33,8 +33,8 @@ ENDHEREDOC raise Puppet::ParseError, "The lookup function is only available with settings --binder true, or --parser future" end type_parser = Puppet::Pops::Types::TypeParser.new - pblock = args[-1] if args[-1].is_a?(Puppet::Parser::AST::Lambda) - type_name = args[1] unless args[1].is_a?(Puppet::Parser::AST::Lambda) + pblock = args[-1] if args[-1].respond_to?(:puppet_lambda) + type_name = args[1] unless args[1].respond_to?(:puppet_lambda) type = type_parser.parse( type_name || "Data") result = compiler.injector.lookup(self, type, args[0]) if pblock diff --git a/lib/puppet/parser/functions/reduce.rb b/lib/puppet/parser/functions/reduce.rb index afa321058..824f564c4 100644 --- a/lib/puppet/parser/functions/reduce.rb +++ b/lib/puppet/parser/functions/reduce.rb @@ -61,7 +61,7 @@ Puppet::Parser::Functions::newfunction( else raise ArgumentError, ("reduce(): wrong number of arguments (#{args.length}; must be 2 or 3)") end - unless pblock.is_a? Puppet::Parser::AST::Lambda + unless pblock.respond_to?(:puppet_lambda) raise ArgumentError, ("reduce(): wrong argument type (#{args[1].class}; must be a parameterized block.") end receiver = args[0] diff --git a/lib/puppet/parser/functions/slice.rb b/lib/puppet/parser/functions/slice.rb index 505ab7261..f721c984f 100644 --- a/lib/puppet/parser/functions/slice.rb +++ b/lib/puppet/parser/functions/slice.rb @@ -84,7 +84,7 @@ Puppet::Parser::Functions::newfunction( # the block is optional, ok if nil, function then produces an array pblock = args[2] - raise ArgumentError, ("slice(): wrong argument type (#{args[2].class}; must be a parameterized block.") unless pblock.is_a?(Puppet::Parser::AST::Lambda) || args.length == 2 + raise ArgumentError, ("slice(): wrong argument type (#{args[2].class}; must be a parameterized block.") unless pblock.respond_to?(:puppet_lambda) || args.length == 2 case receiver when Array diff --git a/lib/puppet/pops/evaluator/closure.rb b/lib/puppet/pops/evaluator/closure.rb new file mode 100644 index 000000000..0e7232654 --- /dev/null +++ b/lib/puppet/pops/evaluator/closure.rb @@ -0,0 +1,28 @@ + + +class Puppet::Evaluator::Lambda + attr_reader :evaluator + attr_reader :model + attr_reader :enclosing_scope + + def initialize(evaluator, model, scope) + @evaluator = evaluator + @model = model + @enclosing_scope = scope + end + + # marker method checked with respond_to :puppet_lambda + def puppet_lambda() + true + end + + # compatible with 3x AST::Lambda + def call(scope, *args) + @evaluator.call(self, args, @enclosing_scope) + end + + # incompatible with 3x except that it is an array of the same size + def parameters() + @model.parameters || [] + end +end diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index e28e731a1..1dbf436ce 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -33,12 +33,12 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # TODO: Change these to class variables to get caching. @@eval_visitor ||= Puppet::Pops::Visitor.new(self, "eval", 1, 1) @@assign_visitor ||= Puppet::Pops::Visitor.new(self, "assign", 3, 3) + @@call_visitor ||= Puppet::Pops::Visitor.new(self, "call", 3, 3) @@type_calculator ||= Puppet::Pops::Types::TypeCalculator.new() @@type_parser ||= Puppet::Pops::Types::TypeParser.new() @@compare_operator ||= Puppet::Pops::Evaluator::CompareOperator.new() - @@call_operator ||= Puppet::Pops::Evaluator::CallOperator.new() @@relationship_operator ||= Puppet::Pops::Evaluator::RelationshipOperator.new() end @@ -90,6 +90,74 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator @@assign_visitor.visit_this(self, target, value, o, scope) end + # Call a closure - Can only be called with a closure (for now), may be refactored later + # to also handle other types of calls (function calls are also handled by CallNamedFunction and CallMethod, they + # could create similar objects to Closure, wait until other types of defines are instantiated - they may bhave + # as special cases of calls - i.e. 'new') + # + # @raise ArgumentError, if there are to many or too few arguments + # @raise ArgumentError, if given closure is not a Puppet::Pops::Evaluator::Closure + # + def call(closure, args, scope, &block) + raise ArgumentError, "Can only call a Lambda" unless closure.is_a?(Puppet::Pops::Evaluator::Closure) + pblock = closure.model + parameters = pblock.parameters || [] + + raise ArgumentError, "Too many arguments: #{args.size} for #{parameters.size}" unless args.size <= parameters.size + + # associate values with parameters + merged = parameters.zip(args) + # calculate missing arguments + missing = parameters.slice(args.size, parameters.size - args.size).select {|p| e.value.nil? } + unless missing.empty? + optional = parameters.count { |p| !p.value.nil? } + raise ArgumentError, "Too few arguments; #{args.size} for #{optional > 0 ? ' min ' : ''}#{parameters.size - optional}" + end + + evaluated = merged.collect do |m| + # m can be one of + # m = [Parameter{name => "name", value => nil], "given"] + # | [Parameter{name => "name", value => Expression}, "given"] + # + # "given" is always an optional entry. If a parameter was provided then + # the entry will be in the array, otherwise the m array will be a + # single element.a = [] + given_argument = m[1] + argument_name = m[0].name + default_expression = m[0].value + + value = if default_expression + evaluate(default_expression, scope) + else + given_argument + end + [argument_name, value] + end + + # Store the evaluated name => value associations in a new inner/local/ephemeral scope + # (This is made complicated due to the fact that the implementation of scope is overloaded with + # functionality and an inner ephemeral scope must be used (as opposed to just pushing a local scope + # on a scope "stack"). + + # Ensure variable exists with nil value if error occurs. + # Some ruby implementations does not like creating variable on return + result = nil + begin + scope_memo = get_scope_nesting_level(scope) + # change to create local scope_from - cannot give it file and line - that is the place of the call, not + # "here" + create_local_scope_from(Hash[evaluated], scope) + result = evaluate(pblock.body, scope) + ensure + set_scope_nesting_level(scope, scope_memo) + end + if block_given + block.call(result) + else + result + end + end + protected # Assign value to named variable. @@ -525,8 +593,6 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # NodeDefinition < Expression # HostClassDefinition < NamedDefinition - # CallExpression < Expression - # CallNamedFunctionExpression < CallExpression # TypeReference < Expression # InstanceReferences < TypeReference # ResourceExpression < Expression @@ -535,6 +601,47 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # ResourceOverrideExpression < Expression # NamedAccessExpression < Expression + # Puppet 3.1 AST only supports calling a function by name (it is not possible to produce a function + # that is then called). TODO- should puppet 4 accept this? It is very powerful in combiantion with + # custom functions in puppet language. + # + # rval_required (for an expression) + # functor_expr (lhs - the "name" expression) + # arguments - list of arguments + # + def eval_CallNamedFunctionExpression(o, scope) + # The functor expression is not evaluated, it is not possible to select the function to call + # via an expression like $a() + fail("Unacceptable expression for name of function", o, scope) unless o.functor_expr.is_a? Puppet::Pops::Model::QualifiedName + name = o.functor_expr.value + assert_function_available(name, o, scope) + evaluated_arguments = o.arguments.collect {|arg| evaluate(arg, scope) } + # rval_required = o.rval_required # TODO: is this really needed - it can just return nil for a function that is not rval + # wrap lambda in a callable block if it is present + evaluated_arguments << Puppet::Evaluator::Lambda.new(self, o.lambda) if o.lambda + call_function(name, evaluated_arguments, o, scope) do |result| + # prevent functions that are not r-value from leaking its return value + rvalue_function?(name, o, scope) ? result : nil + end + end + + # Evaluation of CallMethodExpression handles a NamedAccessExpression functor (receiver.function_name) + # + def eval_CallMethodExpression(o, scope) + fail("Unacceptable expression for name of function", o.functor_expr, scope) unless o.functor_expr.is_a? Puppet::Pops::Model::NamedAccessExpression + receiver = evaluate(o.functor_expr.left_expr, scope) + name = o.right_expr + fail("Unacceptable expression for name of function/method", name, scope) unless name.is_a? Puppet::Pops::Model::QualifiedName + name = name.value # the string function name + assert_function_available(name, o, scope) + evaluated_arguments = [receiver] + (o.arguments || []).collect {|arg| evaluate(arg, scope) } + evaluated_arguments << Puppet::Evaluator::Lambda.new(self, o.lambda) if o.lambda + call_function(name, evaluated_arguments, o, scope) do |result| + # prevent functions that are not r-value from leaking its return value + rvalue_function?(name, o, scope) ? result : nil + end + end + # @example # $x ? { 10 => true, 20 => false, default => 0 } # diff --git a/lib/puppet/pops/evaluator/runtime3_support.rb b/lib/puppet/pops/evaluator/runtime3_support.rb index 8c08c2019..d8318d434 100644 --- a/lib/puppet/pops/evaluator/runtime3_support.rb +++ b/lib/puppet/pops/evaluator/runtime3_support.rb @@ -58,6 +58,23 @@ module Puppet::Pops::Evaluator::Runtime3Support end end + # Creates a local scope with vairalbes set from a hash of variable name to value + # + def create_local_scope_from(hash, scope) + # two dummy values are needed since the scope tries to give an error message (can not happen in this + # case - it is just wrong, the error should be reported by the caller who knows in more detail where it + # is in the source. + # + raise ArgumentError, "Internal error - attempt to create a local scope without a hash" unless hash.is_a?(Hash) + scope.ephemeral_from(hash) + end + + # Creates a nested match scope + def create_match_scope_from(scope) + # Create a transparent match scope (for future matches) + scope.new_match_scope(nil) + end + def get_scope_nesting_level(scope) scope.ephemeral_level end @@ -146,6 +163,24 @@ module Puppet::Pops::Evaluator::Runtime3Support n end + # Asserts that the given function name resolves to an available function. The function is loaded + # as a side effect. Fails if the function does not exist. + # + def assert_function_available(name, o, scope) + fail("Unknown function #{name}", o, scope) unless Puppet::Parser::Functions.function(name) + end + + def call_function(name, args, o, scope) + # Should arguments be mapped from :undef to '' (3x functions expects this - but it is bad) + mapped_args = args.map {|a| a == :undef ? '' : a } + scope.send("function_#{name}", mapped_args) + end + + # Returns true if the function produces a value + def rvalue_function?(name, o, scope) + Puppet::Parser::Functions.rvalue?(name) + end + # This is the same type of "truth" as used in the current Puppet DSL. # def is_true? o From 299b3248ce2296163f535382e0ddaf46ebe5362b Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Wed, 2 Oct 2013 02:48:12 +0200 Subject: [PATCH 029/800] (maint) Remove TODO comment (fixed thing), and yardoc text typos --- lib/puppet/pops/evaluator/evaluator_impl.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index 1dbf436ce..246f03f1e 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -30,7 +30,6 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator include Puppet::Pops::Evaluator::Runtime3Support def initialize - # TODO: Change these to class variables to get caching. @@eval_visitor ||= Puppet::Pops::Visitor.new(self, "eval", 1, 1) @@assign_visitor ||= Puppet::Pops::Visitor.new(self, "assign", 3, 3) @@call_visitor ||= Puppet::Pops::Visitor.new(self, "call", 3, 3) @@ -92,7 +91,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # Call a closure - Can only be called with a closure (for now), may be refactored later # to also handle other types of calls (function calls are also handled by CallNamedFunction and CallMethod, they - # could create similar objects to Closure, wait until other types of defines are instantiated - they may bhave + # could create similar objects to Closure, wait until other types of defines are instantiated - they may behave # as special cases of calls - i.e. 'new') # # @raise ArgumentError, if there are to many or too few arguments From 28ccec29896283143c772ee34a6dee553daf4813 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 5 Oct 2013 23:01:33 +0200 Subject: [PATCH 030/800] (perf) Add optimized versions of Visitorv.visit_this_n When using an exact number of arguments, it is 30% faster to call a dedicated method than one that takes variable number arguments and then checks min/max args passed. This matters in tight loops. Calls with 0 to 3 exact parameters can thus be optimized. --- lib/puppet/pops/visitor.rb | 45 +++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/lib/puppet/pops/visitor.rb b/lib/puppet/pops/visitor.rb index d5526ac8b..eb1ea31d7 100644 --- a/lib/puppet/pops/visitor.rb +++ b/lib/puppet/pops/visitor.rb @@ -37,9 +37,7 @@ class Puppet::Pops::Visitor return receiver.send(method_name, thing, *args) else thing.class.ancestors().each do |ancestor| - method_name = :"#{@message}_#{ancestor.name.split("::").last}" - # DEBUG OUTPUT - # puts "Visitor checking: #{receiver.class}.#{method_name}, responds to: #{@receiver.respond_to? method_name}" + method_name = :"#{@message}_#{ancestor.name.split(/::/).last}" next unless receiver.respond_to? method_name @cache[thing.class] = method_name return receiver.send(method_name, thing, *args) @@ -47,4 +45,45 @@ class Puppet::Pops::Visitor end raise "Visitor Error: the configured receiver (#{receiver.class}) can't handle instance of: #{thing.class}" end + + # Visit an explicit receiver with 0 args + # (This is ~30% faster than calling the general method) + # + def visit_this_0(receiver, thing) + if method_name = @cache[thing.class] + return receiver.send(method_name, thing) + end + visit_this(receiver, thing) + end + + # Visit an explicit receiver with 1 args + # (This is ~30% faster than calling the general method) + # + def visit_this_1(receiver, thing, arg) + if method_name = @cache[thing.class] + return receiver.send(method_name, thing, arg) + end + visit_this(receiver, thing, arg) + end + + # Visit an explicit receiver with 2 args + # (This is ~30% faster than calling th general method) + # + def visit_this_2(receiver, thing, arg1, arg2) + if method_name = @cache[thing.class] + return receiver.send(method_name, thing, arg1, arg2) + end + visit_this(receiver, thing, arg1, arg2) + end + + # Visit an explicit receiver with 3 args + # (This is ~30% faster than calling the general method) + # + def visit_this_3(receiver, thing, arg1, arg2, arg3) + if method_name = @cache[thing.class] + return receiver.send(method_name, thing, arg1, arg2, arg3) + end + visit_this(receiver, thing, arg1, arg2, arg3) + end + end From 35807f6f5f95b310ac9f328c01256338b1ba595a Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 5 Oct 2013 23:04:42 +0200 Subject: [PATCH 031/800] (perf) Change TypeCalculator to use optimized visitor calls. --- lib/puppet/pops/types/type_calculator.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/puppet/pops/types/type_calculator.rb b/lib/puppet/pops/types/type_calculator.rb index 62c43cdf3..922ec7ea4 100644 --- a/lib/puppet/pops/types/type_calculator.rb +++ b/lib/puppet/pops/types/type_calculator.rb @@ -139,7 +139,7 @@ class Puppet::Pops::Types::TypeCalculator t2 = type(t2) end - @@assignable_visitor.visit_this(self, t, t2) + @@assignable_visitor.visit_this_1(self, t, t2) end # Answers 'what is the Puppet Type corresponding to the given Ruby class' @@ -187,7 +187,7 @@ class Puppet::Pops::Types::TypeCalculator # @api public # def infer(o) - @@infer_visitor.visit_this(self, o) + @@infer_visitor.visit_this_0(self, o) end # Answers 'is o an instance of type t' @@ -320,7 +320,7 @@ class Puppet::Pops::Types::TypeCalculator # @api public # def string(t) - @@string_visitor.visit_this(self, t) + @@string_visitor.visit_this_0(self, t) end From 89bc985ad8be21ba5b16dbca0d917dfff2bb419d Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 10 Oct 2013 00:03:52 +0200 Subject: [PATCH 032/800] (perf) Make lexer 30% faster --- lib/puppet/pops.rb | 1 + lib/puppet/pops/parser/lexer.rb | 270 +++++++++------------------- lib/puppet/pops/parser/locator.rb | 205 +++++++++++++++++++++ lib/puppet/pops/visitor.rb | 3 +- spec/unit/pops/parser/lexer_spec.rb | 117 ++++-------- 5 files changed, 320 insertions(+), 276 deletions(-) create mode 100644 lib/puppet/pops/parser/locator.rb diff --git a/lib/puppet/pops.rb b/lib/puppet/pops.rb index 65d7d11cb..a5df72043 100644 --- a/lib/puppet/pops.rb +++ b/lib/puppet/pops.rb @@ -71,6 +71,7 @@ module Puppet module Parser require 'puppet/pops/parser/eparser' require 'puppet/pops/parser/parser_support' + require 'puppet/pops/parser/locator' require 'puppet/pops/parser/lexer' require 'puppet/pops/parser/evaluating_parser' end diff --git a/lib/puppet/pops/parser/lexer.rb b/lib/puppet/pops/parser/lexer.rb index 060d501c1..c3a34410f 100644 --- a/lib/puppet/pops/parser/lexer.rb +++ b/lib/puppet/pops/parser/lexer.rb @@ -58,8 +58,7 @@ class Puppet::Pops::Parser::Lexer end # @return [Boolean] if the token is acceptable in the given context or not. - # this implementation always returns true. - # @param context [Hash] ? ? ? + # @param context [Hash] the lexing context # def acceptable?(context={}) @acceptable_when.call(context) @@ -214,13 +213,13 @@ class Puppet::Pops::Parser::Lexer REGEX_INTRODUCING_TOKENS.include? context[:after] end - DASHED_VARIABLES_ALLOWED = Proc.new do |context| - Puppet[:allow_variables_with_dashes] - end - - VARIABLE_AND_DASHES_ALLOWED = Proc.new do |context| - Contextual::DASHED_VARIABLES_ALLOWED.call(context) and TOKENS[:VARIABLE].acceptable?(context) - end +# DASHED_VARIABLES_ALLOWED = Proc.new do |context| +# Puppet[:allow_variables_with_dashes] +# end +# +# VARIABLE_AND_DASHES_ALLOWED = Proc.new do |context| +# Contextual::DASHED_VARIABLES_ALLOWED.call(context) and TOKENS[:VARIABLE].acceptable?(context) +# end end # Numbers are treated separately from names, so that they may contain dots. @@ -253,14 +252,14 @@ class Puppet::Pops::Parser::Lexer end TOKENS.add_token :COMMENT, %r{#.*}, :skip => true do |lexer,value| - value.sub!(/# ?/,'') - [self, value] +# value.sub!(/# ?/,'') + [self, ""] end TOKENS.add_token :MLCOMMENT, %r{/\*(.*?)\*/}m, :skip => true do |lexer, value| - value.sub!(/^\/\* ?/,'') - value.sub!(/ ?\*\/$/,'') - [self,value] +# value.sub!(/^\/\* ?/,'') +# value.sub!(/ ?\*\/$/,'') + [self, ""] end TOKENS.add_token :REGEX, %r{/[^/\n]*/} do |lexer, value| @@ -313,30 +312,10 @@ class Puppet::Pops::Parser::Lexer end end - TOKENS.add_token :DOLLAR_VAR_WITH_DASH, %r{\$(?:::)?(?:[-\w]+::)*[-\w]+} do |lexer, value| - lexer.warn_if_variable_has_hyphen(value) - - [TOKENS[:VARIABLE], value[1..-1]] - end - TOKENS[:DOLLAR_VAR_WITH_DASH].acceptable_when Contextual::DASHED_VARIABLES_ALLOWED - TOKENS.add_token :DOLLAR_VAR, %r{\$(::)?(\w+::)*\w+} do |lexer, value| [TOKENS[:VARIABLE],value[1..-1]] end - TOKENS.add_token :VARIABLE_WITH_DASH, %r{(?:::)?(?:[-\w]+::)*[-\w]+} do |lexer, value| - lexer.warn_if_variable_has_hyphen(value) - # If the varname (following $, or ${ is followed by (, it is a function call, and not a variable - # reference. - # - if lexer.match?(%r{[ \t\r]*\(}) - [TOKENS[:NAME],value] - else - [TOKENS[:VARIABLE], value] - end - end - TOKENS[:VARIABLE_WITH_DASH].acceptable_when Contextual::VARIABLE_AND_DASHES_ALLOWED - TOKENS.add_token :VARIABLE, %r{(::)?(\w+::)*\w+} do |lexer, value| # If the varname (following $, or ${ is followed by (, it is a function call, and not a variable # reference. @@ -420,8 +399,8 @@ class Puppet::Pops::Parser::Lexer def file=(file) @file = file contents = Puppet::FileSystem::File.exist?(file) ? File.read(file) : "" - @scanner = StringScanner.new(contents) - @locator = Locator.new(contents, multibyte?) + @scanner = StringScanner.new(contents.freeze) + @locator = Puppet::Pops::Parser::Locator.locator(contents) end def_delegator :@token_queue, :shift, :shift_token @@ -432,9 +411,13 @@ class Puppet::Pops::Parser::Lexer # tries, where it is otherwise the number of string token we have. Also, # the lookups are optimized hash lookups, instead of regex scans. # - s = @scanner.peek(3) + _scn = @scanner + s = _scn.peek(3) token = TOKENS.lookup(s[0,3]) || TOKENS.lookup(s[0,2]) || TOKENS.lookup(s[0,1]) - [ token, token && @scanner.scan(token.regex) ] + unless token + return [nil, nil] + end + [ token, _scn.scan(token.regex) ] end # Find the next token that matches a regex. We look for these first. @@ -444,8 +427,10 @@ class Puppet::Pops::Parser::Lexer # I tried optimizing based on the first char, but it had # a slightly negative affect and was a good bit more complicated. + _lxc = @lexing_context + _scn = @scanner TOKENS.regex_tokens.each do |token| - if length = @scanner.match?(token.regex) and token.acceptable?(lexing_context) + if length = _scn.match?(token.regex) and token.acceptable?(_lxc) # We've found a longer match if length > best_length best_length = length @@ -454,7 +439,7 @@ class Puppet::Pops::Parser::Lexer end end - return best_token, @scanner.scan(best_token.regex) if best_token + return best_token, _scn.scan(best_token.regex) if best_token end # Find the next token, returning the string and the token. @@ -462,8 +447,10 @@ class Puppet::Pops::Parser::Lexer shift_token || find_regex_token || find_string_token end + MULTIBYTE = Puppet::Pops::Parser::Locator::MULTIBYTE + SKIPPATTERN = MULTIBYTE ? %r{[[:blank:]\r]+} : %r{[ \t\r]+} + def initialize - @multibyte = init_multibyte initvars end @@ -477,21 +464,6 @@ class Puppet::Pops::Parser::Lexer end end - # Returns true if ruby version >= 1.9.3 since regexp supports multi-byte matches and expanded - # character categories like [[:blank:]]. - # - # This implementation will fail if there are more than 255 minor or micro versions of ruby - # - def init_multibyte - numver = RUBY_VERSION.split(".").collect {|s| s.to_i } - return true if (numver[0] << 16 | numver[1] << 8 | numver[2]) >= (1 << 16 | 9 << 8 | 3) - false - end - - def multibyte? - @multibyte - end - def initvars @previous_token = nil @scanner = nil @@ -500,13 +472,6 @@ class Puppet::Pops::Parser::Lexer # AAARRGGGG! okay, regexes in ruby are bloody annoying # no one else has "\n" =~ /\s/ - if multibyte? - # Skip all kinds of space, and CR, but not newlines - @skip = %r{[[:blank:]\r]+} - else - @skip = %r{[ \t\r]+} - end - @namestack = [] @token_queue = [] @indefine = false @@ -527,7 +492,7 @@ class Puppet::Pops::Parser::Lexer # return token, value if value.is_a? Hash - skip if token.skip_text + @scanner.skip(SKIPPATTERN) if token.skip_text return if token.skip @@ -540,26 +505,22 @@ class Puppet::Pops::Parser::Lexer # If the conversion performed the munging/positioning return token, value if value.is_a? Hash - pos_hash = position_in_source - pos_hash[:value] = value - - # Add one to pos, first char on line is 1 - return token, pos_hash + return token, positioned_value(value) end # Returns a hash with the current position in source based on the current lexing context # - def position_in_source - pos = @locator.pos_on_line(lexing_context[:offset]) - offset = @locator.char_offset(lexing_context[:offset]) - length = @locator.char_length(lexing_context[:offset], lexing_context[:end_offset]) - start_line = @locator.line_for_offset(lexing_context[:offset]) - - return { :line => start_line, :pos => pos, :offset => offset, :length => length} + def positioned_value(value) + { + :value => value, + :locator => @locator, + :offset => @lexing_context[:offset], + :end_offset => @lexing_context[:end_offset] + } end def pos - @locator.pos_on_line(lexing_context[:offset]) + @locator.pos_on_line(@lexing_context[:offset]) end # Handling the namespace stack @@ -576,58 +537,67 @@ class Puppet::Pops::Parser::Lexer end def_delegator :@scanner, :rest + + LBRACE_CHAR = '{' + # this is the heart of the lexer def scan + _scn = @scanner #Puppet.debug("entering scan") - lex_error "Internal Error: No string or file given to lexer to process." unless @scanner + lex_error "Internal Error: No string or file given to lexer to process." unless _scn # Skip any initial whitespace. - skip + _scn.skip(SKIPPATTERN) + _lbrace = '{'.freeze # faster to compare against a frozen string in - until token_queue.empty? and @scanner.eos? do - offset = @scanner.pos + until token_queue.empty? and _scn.eos? do + offset = _scn.pos matched_token, value = find_token - end_offset = @scanner.pos + end_offset = _scn.pos # error out if we didn't match anything at all - lex_error "Could not match #{@scanner.rest[/^(\S+|\s+|.*)/]}" unless matched_token + lex_error "Could not match #{_scn.rest[/^(\S+|\s+|.*)/]}" unless matched_token newline = matched_token.name == :RETURN - lexing_context[:start_of_line] = newline - lexing_context[:offset] = offset - lexing_context[:end_offset] = end_offset + _lxc = @lexing_context + _lxc[:start_of_line] = newline + _lxc[:offset] = offset + _lxc[:end_offset] = end_offset final_token, token_value = munge_token(matched_token, value) # update end position since munging may have moved the end offset - lexing_context[:end_offset] = @scanner.pos + _lxc[:end_offset] = _scn.pos unless final_token - skip + _scn.skip(SKIPPATTERN) next end - lexing_context[:after] = final_token.name unless newline + _lxc[:after] = final_token.name unless newline if final_token.name == :DQPRE - lexing_context[:interpolation_stack] << lexing_context[:brace_count] + _lxc[:interpolation_stack] << _lxc[:brace_count] elsif final_token.name == :DQPOST - lexing_context[:interpolation_stack].pop + _lxc[:interpolation_stack].pop end value = token_value[:value] + _expected = @expected if match = @@pairs[value] and final_token.name != :DQUOTE and final_token.name != :SQUOTE - @expected << match - elsif exp = @expected[-1] and exp == value and final_token.name != :DQUOTE and final_token.name != :SQUOTE - @expected.pop + _expected << match + elsif exp = _expected[-1] and exp == value and final_token.name != :DQUOTE and final_token.name != :SQUOTE + _expected.pop end yield [final_token.name, token_value] - if @previous_token - namestack(value) if @previous_token.name == :CLASS and value != '{' + _prv = @previous_token + if _prv + namestack(value) if _prv.name == :CLASS and value != LBRACE_CHAR - if @previous_token.name == :DEFINE + # TODO: Lexer has no business dealing with this - it is semantic + if _prv.name == :DEFINE if indefine? msg = "Cannot nest definition #{value} inside #{@indefine}" self.indefine = false @@ -638,7 +608,7 @@ class Puppet::Pops::Parser::Lexer end end @previous_token = final_token - skip + _scn.skip(SKIPPATTERN) end # Cannot reset @scanner to nil here - it is needed to answer questions about context after # completed parsing. @@ -649,11 +619,6 @@ class Puppet::Pops::Parser::Lexer yield [false,false] end - # Skip any skipchars in our remaining string. - def skip - @scanner.skip(@skip) - end - def match? r @scanner.match?(r) end @@ -722,9 +687,10 @@ class Puppet::Pops::Parser::Lexer # Advanced after '{' if this is in expression ${} interpolation braced = terminator == '$' && @scanner.scan(/\{/) # make offset to end_ofset be the length of the pre expression string including its start and terminating chars - lexing_context[:end_offset] = @scanner.pos + lxc = @lexing_context + lxc[:end_offset] = @scanner.pos - token_queue << [TOKENS[token_type[terminator]],position_in_source().merge!({:value => preamble+value})] + token_queue << [TOKENS[token_type[terminator]],positioned_value(preamble+value)] variable_regex = if Puppet[:allow_variables_with_dashes] TOKENS[:VARIABLE_WITH_DASH].regex else @@ -736,18 +702,18 @@ class Puppet::Pops::Parser::Lexer tmp_offset = @scanner.pos if var_name = @scanner.scan(variable_regex) - lexing_context[:offset] = tmp_offset - lexing_context[:end_offset] = @scanner.pos + lxc[:offset] = tmp_offset + lxc[:end_offset] = @scanner.pos warn_if_variable_has_hyphen(var_name) # If the varname after ${ is followed by (, it is a function call, and not a variable # reference. # if braced && @scanner.match?(%r{[ \t\r]*\(}) - token_queue << [TOKENS[:NAME], position_in_source().merge!({:value=>var_name})] + token_queue << [TOKENS[:NAME], positioned_value(var_name)] else - token_queue << [TOKENS[:VARIABLE],position_in_source().merge!({:value=>var_name})] + token_queue << [TOKENS[:VARIABLE],positioned_value(var_name)] end - lexing_context[:offset] = @scanner.pos + lxc[:offset] = @scanner.pos tokenize_interpolated_string(DQ_continuation_token_types) else tokenize_interpolated_string(token_type, replace_false_start_with_text(terminator)) @@ -766,8 +732,8 @@ class Puppet::Pops::Parser::Lexer # just parse a string, not a whole file def string=(string) - @scanner = StringScanner.new(string) - @locator = Locator.new(string, multibyte?) + @scanner = StringScanner.new(string.freeze) + @locator = Puppet::Pops::Parser::Locator.locator(string) end def warn_if_variable_has_hyphen(var_name) @@ -781,83 +747,7 @@ class Puppet::Pops::Parser::Lexer # consumed. # def line - return 1 unless lexing_context && locator - locator.line_for_offset(lexing_context[:end_offset]) - end - - # Helper class that keeps track of where line breaks are located and can answer questions about positions. - # - class Locator - attr_reader :line_index - attr_reader :string - - # Create a locator based on a content string, and a boolean indicating if ruby version support multi-byte strings - # or not. - # - def initialize(string, multibyte) - @string = string - @multibyte = multibyte - compute_line_index - end - - # Returns whether this a ruby version that supports multi-byte strings or not - # - def multibyte? - @multibyte - end - - # Computes the start offset for each line. - # - def compute_line_index - scanner = StringScanner.new(@string) - result = [0] # first line starts at 0 - while scanner.scan_until(/\n/) - result << scanner.pos - end - @line_index = result - end - - # Returns the line number (first line is 1) for the given offset - def line_for_offset(offset) - if line_nbr = line_index.index {|x| x > offset} - return line_nbr - end - # If not found it is after last - return line_index.size - end - - # Returns the offset on line (first offset on a line is 0). - # - def offset_on_line(offset) - line_offset = line_index[line_for_offset(offset)-1] - if multibyte? - @string.byteslice(line_offset, offset-line_offset).length - else - offset - line_offset - end - end - - # Returns the position on line (first position on a line is 1) - def pos_on_line(offset) - offset_on_line(offset) +1 - end - - # Returns the character offset for a given byte offset - def char_offset(byte_offset) - if multibyte? - @string.byteslice(0, byte_offset).length - else - byte_offset - end - end - - # Returns the length measured in number of characters from the given start and end byte offseta - def char_length(offset, end_offset) - if multibyte? - @string.byteslice(offset, end_offset - offset).length - else - end_offset - offset - end - end + return 1 unless @lexing_context && locator + locator.line_for_offset(@lexing_context[:end_offset]) end end diff --git a/lib/puppet/pops/parser/locator.rb b/lib/puppet/pops/parser/locator.rb new file mode 100644 index 000000000..851249a2a --- /dev/null +++ b/lib/puppet/pops/parser/locator.rb @@ -0,0 +1,205 @@ +# Helper class that keeps track of where line breaks are located and can answer questions about positions. +# +class Puppet::Pops::Parser::Locator + + RUBY_1_9_3 = (1 << 16 | 9 << 8 | 3) + RUBY_2_0_0 = (2 << 16 | 0 << 8 | 0) + RUBYVER_ARRAY = RUBY_VERSION.split(".").collect {|s| s.to_i } + RUBYVER = (RUBYVER_ARRAY[0] << 16 | RUBYVER_ARRAY[1] << 8 | RUBYVER_ARRAY[2]) + + # Computes a symbol representing which ruby runtime this is running on + # This implementation will fail if there are more than 255 minor or micro versions of ruby + # + def self.locator_version + if RUBYVER >= RUBY_2_0_0 + :ruby20 + elsif RUBYVER >= RUBY_1_9_3 + :ruby19 + else + :ruby18 + end + end + LOCATOR_VERSION = locator_version + + # Constant set to true if multibyte is supported (includes multibyte extended regular expressions) + MULTIBYTE = !!(LOCATOR_VERSION == :ruby19 || LOCATOR_VERSION == :ruby20) + + def self.locator(string) + case LOCATOR_VERSION + when :ruby20, :ruby19 + Locator19.new(string) + else + Locator18.new(string) + end + end + + # Returns the position on line (first position on a line is 1) + def pos_on_line(offset) + end + + # Returns the line number (first line is 1) for the given offset + def line_for_offset(offset) + end + + # Returns the position to use given a string scanner (the support for multibyte varies + # depending on Ruby version). + # + def scanner_pos(scanner) + end + + # Returns the offset on line (first offset on a line is 0). + # + def offset_on_line(offset) + end + + # Returns the character offset for a given reported offset + def char_offset(byte_offset) + end + + # Returns the length measured in number of characters from the given start and end reported offseta + def char_length(offset, end_offset) + end + + private + + class AbstractLocator < Puppet::Pops::Parser::Locator + attr_accessor :line_index + attr_accessor :string + attr_accessor :prev_offset + attr_accessor :prev_line + + # Create a locator based on a content string, and a boolean indicating if ruby version support multi-byte strings + # or not. + # + def initialize(string) + @string = string.freeze + @prev_offset = nil + @prev_line = nil + compute_line_index + end + + # Returns the position on line (first position on a line is 1) + def pos_on_line(offset) + offset_on_line(offset) +1 + end + + def to_location_hash(reported_offset, end_offset) + pos = pos_on_line(reported_offset) + offset = char_offset(reported_offset) + length = char_length(reported_offset, end_offset) + start_line = line_for_offset(reported_offset) + { :line => start_line, :pos => pos, :offset => offset, :length => length} + end + + # Returns the index of the smallest item for which the item > the given value + # This is a min binary search. Although written in Ruby it is only slightly slower than + # the corresponding method in C in Ruby 2.0.0 - the main benefit to use this method over + # the Ruby C version is that it returns the index (not the value) which means there is not need + # to have an additional structure to get the index (or record the index in the structure). This + # saes both memory and CPU. It also does not require passing a block that is called since this + # method is specialized to search the line index. + # + def ary_bsearch_i(ary, value) + low = 0 + high = ary.length + mid = nil + smaller = false + satisfied = false + v = nil + + while low < high do + mid = low + ((high - low) / 2) + v = (ary[mid] > value) + if v == true + satisfied = true + smaller = true + elsif !v + smaller = false + else + raise TypeError, "wrong argument, must be boolean or nil, got '#{v.class}'" + end + + if smaller + high = mid + else + low = mid + 1; + end + end + + return nil if low == ary.length + return nil if !satisfied + return low + end + + # Common impl for 18 and 19 since scanner is byte based + def compute_line_index + scanner = StringScanner.new(string) + result = [0] # first line starts at 0 + while scanner.scan_until(/\n/) + result << scanner.pos + end + self.line_index = result + end + + # Returns the line number (first line is 1) for the given offset + def line_for_offset(offset) + if prev_offset == offset + # use cache + return prev_line + end + if line_nbr = ary_bsearch_i(line_index, offset) + # cache + prev_offset = offset + prev_line = line_nbr + return line_nbr + end + # If not found it is after last + # clear cache + prev_offset = prev_line = nil + return line_index.size + end + end + + class Locator18 < AbstractLocator + + def offset_on_line(offset) + line_offset = line_index[ line_for_offset(offset)-1 ] + offset - line_offset + end + + def char_offset(char_offset) + char_offset + end + + def char_length(offset, end_offset) + end_offset - offset + end + + end + + # This implementation is for Ruby19 and Ruby20. It uses byteslice to get strings from byte based offsets. + # For Ruby20 this is faster than using the Stringscanner.charpos method (byteslice outperforms it, when + # strings are frozen). + # + class Locator19 < AbstractLocator + + # Returns the offset on line (first offset on a line is 0). + # Ruby 19 is multibyte but has no character position methods, must use byteslice + def offset_on_line(offset) + line_offset = line_index[ line_for_offset(offset)-1 ] + string.byteslice(line_offset, offset-line_offset).length + end + + # Returns the character offset for a given byte offset + # Ruby 19 is multibyte but has no character position methods, must use byteslice + def char_offset(byte_offset) + string.byteslice(0, byte_offset).length + end + + # Returns the length measured in number of characters from the given start and end byte offseta + def char_length(offset, end_offset) + string.byteslice(offset, end_offset - offset).length + end + end + +end \ No newline at end of file diff --git a/lib/puppet/pops/visitor.rb b/lib/puppet/pops/visitor.rb index eb1ea31d7..460fa80d6 100644 --- a/lib/puppet/pops/visitor.rb +++ b/lib/puppet/pops/visitor.rb @@ -38,11 +38,12 @@ class Puppet::Pops::Visitor else thing.class.ancestors().each do |ancestor| method_name = :"#{@message}_#{ancestor.name.split(/::/).last}" - next unless receiver.respond_to? method_name + next unless receiver.respond_to?(method_name, true) @cache[thing.class] = method_name return receiver.send(method_name, thing, *args) end end + require 'byebug'; byebug raise "Visitor Error: the configured receiver (#{receiver.class}) can't handle instance of: #{thing.class}" end diff --git a/spec/unit/pops/parser/lexer_spec.rb b/spec/unit/pops/parser/lexer_spec.rb index 4eaf39e6d..071450c41 100755 --- a/spec/unit/pops/parser/lexer_spec.rb +++ b/spec/unit/pops/parser/lexer_spec.rb @@ -16,7 +16,20 @@ module EgrammarLexerSpec def self.tokens_scanned_from(s) lexer = Puppet::Pops::Parser::Lexer.new lexer.string = s - lexer.fullscan[0..-2] + tokens = lexer.fullscan[0..-2] + tokens.map do |t| + key = t[0] + options = t[1] + if options[:locator] + # unresolved locations needs to be resolved for tests that check positioning + [key, + options[:locator].to_location_hash( + options[:offset], + options[:end_offset]).merge({:value => options[:value]}) ] + else + t + end + end end end @@ -350,8 +363,11 @@ describe Puppet::Pops::Parser::Lexer::TOKENS[:COMMENT] do @token.skip?.should be_true end - it "'s block should return the comment without the #" do - @token.convert(@lexer,"# this is a comment")[1].should == "this is a comment" + it "'s block should return the comment without any text" do + # This is a silly test, the original tested that the comments was processed, but + # all comments are skipped anyway, and never collected for documentation. + # + @token.convert(@lexer,"# this is a comment")[1].should == "" end end @@ -383,9 +399,12 @@ describe Puppet::Pops::Parser::Lexer::TOKENS[:MLCOMMENT] do end it "'s block should return the comment without the comment marks" do + # This is a silly test, the original tested that the comments was processed, but + # all comments are skipped anyway, and never collected for documentation. + # @lexer.stubs(:line=).with(0) - @token.convert(@lexer,"/* this is a comment */")[1].should == "this is a comment" + @token.convert(@lexer,"/* this is a comment */")[1].should == "" end end @@ -444,63 +463,10 @@ describe Puppet::Pops::Parser::Lexer::TOKENS[:VARIABLE] do end describe "the horrible deprecation / compatibility variables with dashes" do - ENamesWithDashes = %w{f- f-o -f f::-o f::o- f::o-o} - - { Puppet::Pops::Parser::Lexer::TOKENS[:DOLLAR_VAR_WITH_DASH] => '$', - Puppet::Pops::Parser::Lexer::TOKENS[:VARIABLE_WITH_DASH] => '' - }.each do |token, prefix| - describe token do - its(:skip_text) { should be_false } - - context "when compatibly is disabled" do - before :each do Puppet[:allow_variables_with_dashes] = false end - Puppet::Pops::Parser::Lexer::TOKENS.each do |name, value| - it "should be unacceptable after #{name}" do - token.acceptable?(:after => name).should be_false - end - end - - # Yes, this should still *match*, just not be acceptable. - ENamesWithDashes.each do |name| - ["", "::"].each do |global_scope| - var = prefix + global_scope + name - it "should match #{var.inspect}" do - subject.regex.match(var).to_a.should == [var] - end - end - end - end - - context "when compatibility is enabled" do - before :each do Puppet[:allow_variables_with_dashes] = true end - - it "should be acceptable after DQPRE" do - token.acceptable?(:after => :DQPRE).should be_true - end - - ENamesWithDashes.each do |name| - ["", "::"].each do |global_scope| - var = prefix + global_scope + name - it "should match #{var.inspect}" do - subject.regex.match(var).to_a.should == [var] - end - end - end - end - end - end context "deprecation warnings" do before :each do Puppet[:allow_variables_with_dashes] = true end - it "should match a top level variable" do - Puppet.expects(:deprecation_warning).once - - EgrammarLexerSpec.tokens_scanned_from('$foo-bar').should == [ - [:VARIABLE, {:value=>"foo-bar", :line=>1, :pos=>1, :offset=>0, :length=>8}] - ] - end - it "does not warn about a variable without a dash" do Puppet.expects(:deprecation_warning).never @@ -516,34 +482,6 @@ describe "the horrible deprecation / compatibility variables with dashes" do [:NAME, {:value=>"foo-bar", :line=>1, :pos=>1, :offset=>0, :length=>7}] ] end - - it "warns about reference to variable" do - Puppet.expects(:deprecation_warning).once - - EgrammarLexerSpec.tokens_scanned_from('$::foo-bar::baz-quux').should == [ - [:VARIABLE, {:value=>"::foo-bar::baz-quux", :line=>1, :pos=>1, :offset=>0, :length=>20}] - ] - end - - it "warns about reference to variable interpolated in a string" do - Puppet.expects(:deprecation_warning).once - - EgrammarLexerSpec.tokens_scanned_from('"$::foo-bar::baz-quux"').should == [ - [:DQPRE, {:value=>"", :line=>1, :pos=>1, :offset=>0, :length=>2}], # length since preamble includes start and terminator - [:VARIABLE, {:value=>"::foo-bar::baz-quux", :line=>1, :pos=>3, :offset=>2, :length=>19}], - [:DQPOST, {:value=>"", :line=>1, :pos=>22, :offset=>21, :length=>1}], - ] - end - - it "warns about reference to variable interpolated in a string as an expression" do - Puppet.expects(:deprecation_warning).once - - EgrammarLexerSpec.tokens_scanned_from('"${::foo-bar::baz-quux}"').should == [ - [:DQPRE, {:value=>"", :line=>1, :pos=>1, :offset=>0, :length=>3}], - [:VARIABLE, {:value=>"::foo-bar::baz-quux", :line=>1, :pos=>4, :offset=>3, :length=>19}], - [:DQPOST, {:value=>"", :line=>1, :pos=>23, :offset=>22, :length=>2}], - ] - end end end @@ -900,3 +838,12 @@ describe "when lexing interpolation detailed positioning should be correct" do ) end end + +describe "when benchmarked" do + it "should not take longer than x sec", :profile => true do + lexer = Puppet::Pops::Parser::Lexer.new +# code = 'if true { 10 + 10 } else { "interpolate ${foo} andn stuff" }' + code = 'if true \n{\n 10 + 10\n }\n else\n {\n "interpolate ${foo} andn stuff"\n }\n' + puts Benchmark.measure {10000.times {lexer.string = code; lexer.fullscan }} + end +end From 37c602c544a9e57818d6e76c1f6cb6dca7b817e7 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 14 Oct 2013 03:22:37 +0200 Subject: [PATCH 033/800] (perf) Add Lexer2 - a faster Lexer replacement The Lexer2 (not yet used by the parser) performs >3x faster on lexing of 10 lines of mixed code including simple expression interpolation than the original lexer (which does not keep track of detailed positions). --- lib/puppet/pops/parser/heredoc_support.rb | 190 ++++++ .../pops/parser/interpolation_support.rb | 189 ++++++ lib/puppet/pops/parser/lexer.rb | 6 +- lib/puppet/pops/parser/lexer2.rb | 639 ++++++++++++++++++ lib/puppet/pops/parser/lexer_support.rb | 96 +++ lib/puppet/pops/parser/locator.rb | 26 +- lib/puppet/pops/parser/slurp_support.rb | 95 +++ spec/lib/matchers/match_tokens2.rb | 74 ++ spec/unit/pops/parser/lexer2_spec.rb | 304 +++++++++ 9 files changed, 1605 insertions(+), 14 deletions(-) create mode 100644 lib/puppet/pops/parser/heredoc_support.rb create mode 100644 lib/puppet/pops/parser/interpolation_support.rb create mode 100644 lib/puppet/pops/parser/lexer2.rb create mode 100644 lib/puppet/pops/parser/lexer_support.rb create mode 100644 lib/puppet/pops/parser/slurp_support.rb create mode 100644 spec/lib/matchers/match_tokens2.rb create mode 100644 spec/unit/pops/parser/lexer2_spec.rb diff --git a/lib/puppet/pops/parser/heredoc_support.rb b/lib/puppet/pops/parser/heredoc_support.rb new file mode 100644 index 000000000..0683b91be --- /dev/null +++ b/lib/puppet/pops/parser/heredoc_support.rb @@ -0,0 +1,190 @@ +module Puppet::Pops::Parser::HeredocSupport + + # Pattern for heredoc `@(endtag[:syntax][/escapes]) + # Produces groups for endtag (group 1), syntax (group 2), and escapes (group 3) + # + PATTERN_HEREDOC = %r{@\(([^:/\r\n\)]+)(?::[:blank:]*([a-z][a-zA-Z0-9_+]+)[:blank:]*)?(?:/((?:\w|[$])*)[:blank:]*)?\)} + + + def heredoc + scn = @scanner + ctx = @lexing_context + locator = @locator + before = scn.pos + + # scanner is at position before @( + # find end of the heredoc spec + str = scn.scan_until(/\)/) || lexer.lex_error_with_pos("Unclosed parenthesis after '@(' followed by '#{followed_by}'") + pos_after_heredoc = scn.pos + + # Note: allows '+' as separator in syntax, but this needs validation as empty segments are not allowed + unless md = str.match(PATTERN_HEREDOC) + lex_error_with_pos("Invalid syntax in heredoc expected @(endtag[:syntax][/escapes])") + end + endtag = md[1] + syntax = md[2] || '' + escapes = md[3] + + endtag.strip! + + # Is this a dq string style heredoc? (endtag enclosed in "") + if endtag =~ /^"(.*)"$/ + dqstring_style = true + endtag = $1.strip + end + + lexer.lex_error_with_pos("Missing endtag in heredoc") unless endtag.length >= 1 + + resulting_escapes = [] + if escapes + escapes = "trnsuL$" if escapes.length < 1 + + escapes = escapes.split('') + unless escapes.length == escapes.uniq.length + lex_error_with_pos("An escape char for @() may only appear once. Got '#{escapes.join(', ')}") + end + resulting_escapes = ["\\"] + escapes.each do |e| + case e + when "t", "r", "n", "s", "u", "$" + resulting_escapes << e + when "L" + resulting_escapes += ["\n", "\r\n"] + else + lex_error(positioned_message("Invalid heredoc escape char. Only t, r, n, s, u, L, $ allowed. Got '#{e}'")) + end + end + end + + # Produce a heredoc token to make the syntax available to the grammar + enqueue_completed([:HEREDOC, syntax, pos_after_heredoc - before], before) + + # If this is the second or subsequent heredoc on the line, the lexing context's :newline_jump contains + # the position after the \n where the next heredoc text should scan. If not set, this is the first + # and it should start scanning after the first found \n (or if not found == error). + + if ctx[:newline_jump] + scn.pos = lexing_context[:newline_jump] + else + scn.scan_until(/\n/) || lex_error_with_pos("Heredoc without any following lines of text") + end + # offset 0 for the heredoc, and its line number + heredoc_offset = scn.pos + heredoc_line = locator.line_for_offset(heredoc_offset)-1 + + # Compute message to emit if there is no end (to make it refer to the opening heredoc position). + eof_message = positioned_message("Heredoc without end-tagged line") + + # Text from this position (+ lexing contexts offset for any preceding heredoc) is heredoc until a line + # that terminates the heredoc is found. + + # (Endline in EBNF form): WS* ('|' WS*)? ('-' WS*)? endtag WS* \r? (\n|$) + endline_pattern = /([[:blank:]]*)(?:([|])[[:blank:]]*)?(?:(\-)[[:blank:]]*)?#{Regexp.escape(endtag)}[[:blank:]]*\r?(?:\n|\z)/ + lines = [] + while !scn.eos? do + one_line = scn.scan_until(/(?:\n|\z)/) || lexer.lex_error(eof_message) + if md = one_line.match(endline_pattern) + leading = md[1] + has_margin = md[2] == '|' + remove_break = md[3] == '-' + # Record position where next heredoc (from same line as current @()) should start scanning for content + ctx[:newline_jump] = scn.pos + + # Process captured lines - remove leading, and trailing newline + str = heredoc_text(lines, leading, has_margin, remove_break) + + # Use a new lexer instance configured with a sub-locator to enable correct positioning + sublexer = self.class.new() + locator = SubLocator.sub_locator(str, locator.file, heredoc_line, heredoc_offset, leading.length()) + sublexer.lex_unquoted_string(str, locator, resulting_escapes, dqstring_style) + sublexer.interpolate_uq_to(self) + # Continue scan after @(...) + scn.pos = pos_after_heredoc + return + else + lines << one_line + end + end + lex_error(eof_message) + end + + # Produces the heredoc text string given the individual (unprocessed) lines as an array. + # @param lines [Array] unprocessed lines of text in the heredoc w/o terminating line + # @param leading [String] the leading text up (up to pipe or other terminating char) + # @param has_margin [Boolean] if the left margin should be adjusted as indicated by `leading` + # @param remove_break [Boolean] if the line break (\r?\n) at the end of the last line should be removed or not + # + def heredoc_text(lines, leading, has_margin, remove_break) + if has_margin + leading_pattern = /^#{Regexp.escape(leading)}/ + lines = lines.collect {|s| s.gsub(leading_pattern, '') } + end + result = lines.join('') + result.gsub!(/\r?\n$/, '') if remove_break + result + end + + # A Sublocator locates a concrete locator (subspace) in a virtual space. + # The `leading_line_count` is the (virtual) number of lines preceding the first line in the concrete locator. + # The `leading_offset` is the (virtual) byte offset of the first byte in the concrete locator. + # The `leading_line_offset` is the (virtual) offset / margin in characters for each line. + # + # This illustrates characters in the sublocator (`.`) inside the subspace (`X`): + # + # 1:XXXXXXXX + # 2:XXXX.... .. ... .. + # 3:XXXX. . .... .. + # 4:XXXX............ + # + # This sublocator would be configured with leading_line_count = 1, + # leading_offset=8, and leading_line_offset=4 + # + # Note that leading_offset must be the same for all lines and measured in characters. + # + class SubLocator < Puppet::Pops::Parser::Locator + def self.sub_locator(string, file, leading_line_count, leading_offset, leading_line_offset) + self.new(Puppet::Pops::Parser::Locator.locator(string, file), + leading_line_count, + leading_offset, + leading_line_offset) + end + + def initialize(locator, leading_line_count, leading_offset, leading_line_offset) + @locator = locator + @leading_line_count = leading_line_count + @leading_offset = leading_offset + @leading_line_offset = leading_line_offset + end + + def file + @locator.file + end + + def string + @locator.string + end + + # Given offset is offset in the subspace + def line_for_offset(offset) + @locator.line_for_offset(offset) + @leading_line_count + end + + # Given offset is offset in the subspace + def offset_on_line(offset) + @locator.offset_on_line + @leading_line_offset + end + + # Given offset is offset in the subspace + def char_offset(offset) + effective_line = @locator.line_for_offset(offset) + locator.char_offset(offset) + (effective_line * @leading_line_offset) + @leading_offset + end + + # Given offsets are offsets in the subspace + def char_length(offset, end_offset) + effective_line = @locator.line_for_offset(end_offset) - @locator.line_for_offset(offset) + locator.char_length(offset, end_offset) + (effective_line * @leading_line_offset) + end + end + +end \ No newline at end of file diff --git a/lib/puppet/pops/parser/interpolation_support.rb b/lib/puppet/pops/parser/interpolation_support.rb new file mode 100644 index 000000000..1529065b0 --- /dev/null +++ b/lib/puppet/pops/parser/interpolation_support.rb @@ -0,0 +1,189 @@ +# This module is an integral part of the Lexer. +# It defines interpolation support +# PERFORMANCE NOTE: There are 4 very similar methods in this module that are designed to be as +# performant as possible. While it is possible to parameterize them into one common method, the overhead +# of passing parameters and evaluating conditional logic has a negative impact on performance. +# +module Puppet::Pops::Parser::InterpolationSupport + + PATTERN_VARIABLE = %r{(::)?(\w+::)*\w+} + + # This is the starting point for a double quoted string with possible interpolation + # The structure mimics that of the grammar. + # The logic is explicit (where the former implementation used parameters/strucures) given to a + # generic handler. + # (This is both easier to understand and faster). + # + def interpolate_dq + scn = @scanner + ctx = @lexing_context + before = scn.pos + # skip the leading " by doing a scan since the slurp_dqstring uses last matched when there is an error + scn.scan(/"/) + value,terminator = slurp_dqstring() + text = value + after = scn.pos + while true + case terminator + when '"' + # simple case, there was no interpolation, return directly + return emit_completed([:STRING, text, scn.pos-before], before) + when '${' + count = ctx[:brace_count] + ctx[:brace_count] += 1 + # The ${ terminator is counted towards the string part + enqueue_completed([:DQPRE, text, scn.pos-before], before) + # Lex expression tokens until a closing (balanced) brace count is reached + enqueue_until count + break + when '$' + if varname = scn.scan(PATTERN_VARIABLE) + # The $ is counted towards the variable + enqueue_completed([:DQPRE, text, after-before-1], before) + enqueue_completed([:VARIABLE, varname, scn.pos - after + 1], after -1) + break + else + # false $ variable start + text += value + value,terminator = slurp_dqstring() + after = scn.pos + end + end + end + interpolate_tail_dq + # return the first enqueued token and shift the queue + @token_queue.shift + end + + def interpolate_tail_dq + scn = @scanner + ctx = @lexing_context + before = scn.pos + value,terminator = slurp_dqstring + text = value + after = scn.pos + while true + case terminator + when '"' + # simple case, there was no further interpolation, return directly + enqueue_completed([:DQPOST, text, scn.pos-before], before) + return + when '${' + count = ctx[:brace_count] + ctx[:brace_count] += 1 + # The ${ terminator is counted towards the string part + enqueue_completed([:DQMID, text, scn.pos-before], before) + # Lex expression tokens until a closing (balanced) brace count is reached + enqueue_until count + break + when '$' + if varname = scn.scan(PATTERN_VARIABLE) + # The $ is counted towards the variable + enqueue_completed([:DQMID, text, after-before-1], before) + enqueue_completed([:VARIABLE, varname, scn.pos - after +1], after -1) + break + else + # false $ variable start + text += value + value,terminator = self.send(slurpfunc) + after = scn.pos + end + end + end + interpolate_tail_dq + end + + # This is the starting point for a un-quoted string with possible interpolation + # The logic is explicit (where the former implementation used parameters/strucures) given to a + # generic handler. + # (This is both easier to understand and faster). + # + def interpolate_uq + scn = @scanner + ctx = @lexing_context + before = scn.pos + value,terminator = slurp_uqstring() + text = value + after = scn.pos + while true + case terminator + when '' + # simple case, there was no interpolation, return directly + enqueue_completed([:STRING, text, scn.pos-before], before) + return + when '${' + count = ctx[:brace_count] + ctx[:brace_count] += 1 + # The ${ terminator is counted towards the string part + enqueue_completed([:DQPRE, text, scn.pos-before], before) + # Lex expression tokens until a closing (balanced) brace count is reached + enqueue_until count + break + when '$' + if varname = scn.scan(PATTERN_VARIABLE) + # The $ is counted towards the variable + enqueue_completed([:DQPRE, text, after-before-1], before) + enqueue_completed([:VARIABLE, varname, scn.pos - after + 1], after -1) + break + else + # false $ variable start + text += value + value,terminator = slurp_uqstring() + after = scn.pos + end + end + end + interpolate_tail_uq + nil + end + + def interpolate_tail_uq + scn = @scanner + ctx = @lexing_context + before = scn.pos + value,terminator = slurp_uqstring + text = value + after = scn.pos + while true + case terminator + when '' + # simple case, there was no further interpolation, return directly + enqueue_completed([:DQPOST, text, scn.pos-before], before) + return + when '${' + count = ctx[:brace_count] + ctx[:brace_count] += 1 + # The ${ terminator is counted towards the string part + enqueue_completed([:DQMID, text, scn.pos-before], before) + # Lex expression tokens until a closing (balanced) brace count is reached + enqueue_until count + break + when '$' + if varname = scn.scan(PATTERN_VARIABLE) + # The $ is counted towards the variable + enqueue_completed([:DQMID, text, after-before-1], before) + enqueue_completed([:VARIABLE, varname, scn.pos - after +1], after -1) + break + else + # false $ variable start + text += value + value,terminator = slurp_uqstring + after = scn.pos + end + end + end + interpolate_tail_uq + end + + # Interpolates unquoted string and transfers the result to the given lexer + # (This is used when a second lexer instance is used to lex a substring) + # + def interpolate_uq_to(lexer) + interpolate_uq + queue = @token_queue + until queue.empty? do + lexer.enqueue(queue.shift) + end + end + +end \ No newline at end of file diff --git a/lib/puppet/pops/parser/lexer.rb b/lib/puppet/pops/parser/lexer.rb index c3a34410f..39e3c4099 100644 --- a/lib/puppet/pops/parser/lexer.rb +++ b/lib/puppet/pops/parser/lexer.rb @@ -400,7 +400,7 @@ class Puppet::Pops::Parser::Lexer @file = file contents = Puppet::FileSystem::File.exist?(file) ? File.read(file) : "" @scanner = StringScanner.new(contents.freeze) - @locator = Puppet::Pops::Parser::Locator.locator(contents) + @locator = Puppet::Pops::Parser::Locator.locator(contents, file) end def_delegator :@token_queue, :shift, :shift_token @@ -731,9 +731,9 @@ class Puppet::Pops::Parser::Lexer end # just parse a string, not a whole file - def string=(string) + def string=(string, path='') @scanner = StringScanner.new(string.freeze) - @locator = Puppet::Pops::Parser::Locator.locator(string) + @locator = Puppet::Pops::Parser::Locator.locator(string, path) end def warn_if_variable_has_hyphen(var_name) diff --git a/lib/puppet/pops/parser/lexer2.rb b/lib/puppet/pops/parser/lexer2.rb new file mode 100644 index 000000000..e09ddf2c5 --- /dev/null +++ b/lib/puppet/pops/parser/lexer2.rb @@ -0,0 +1,639 @@ +# A sample small lexer to try performance of ruby +# This lexer looks like what a "lex" like tool would output, but it is handwritten + +# TODO: template support + +# Old returns tokens [:KEY, value, { locator = } +# Could return [[token], locator] +# or Token.new([token], locator) with the same API x[0] = token_symbol, x[1] = self, x[:key] = (:value, :file, :line, :pos) etc + +require 'strscan' +require 'puppet/pops/parser/lexer_support' +require 'puppet/pops/parser/heredoc_support' +require 'puppet/pops/parser/interpolation_support' +require 'puppet/pops/parser/slurp_support' + +class Lexer2 + include Puppet::Pops::Parser::LexerSupport + include Puppet::Pops::Parser::HeredocSupport + include Puppet::Pops::Parser::InterpolationSupport + include Puppet::Pops::Parser::SlurpSupport + + # ALl tokens ahve three slots, the token name (a Symbol), the token text (String), and a token text length. + # All operator and punctuation tokens reuse singleton arrays Tokens that require unique values create + # a unique array per token. + # PEFORMANCE NOTES: + # This construct reduces the amount of object that needs to be created for operators and punctuation. + # The length is pre-calculated for all singleton tokens. The length is used both to signal the length of + # the token, and to advance the scanner position (without having to advance it with a scan(regexp)). + # + TOKEN_LBRACK = [:LBRACK, '['.freeze, 1].freeze + TOKEN_RBRACK = [:RBRACK, ']'.freeze, 1].freeze + TOKEN_LBRACE = [:LBRACE, '{'.freeze, 1].freeze + TOKEN_RBRACE = [:RBRACE, '}'.freeze, 1].freeze + TOKEN_SELBRACE = [:SELBRACE, '{'.freeze, 1].freeze + TOKEN_LAMBDA = [:LAMBDA, '{'.freeze, 1].freeze # should be removed + TOKEN_LPAREN = [:LPAREN, '('.freeze, 1].freeze + TOKEN_RPAREN = [:RPAREN, ')'.freeze, 1].freeze + + TOKEN_EQUALS = [:EQUALS, '='.freeze, 1].freeze + TOKEN_APPENDS = [:APPENDS, '+='.freeze, 2].freeze + TOKEN_DELETES = [:DELETES, '-='.freeze, 2].freeze + + TOKEN_ISEQUAL = [:ISEQUAL, '=='.freeze, 2].freeze + TOKEN_NOTEQUAL = [:NOTEQUAL, '!='.freeze, 2].freeze + TOKEN_MATCH = [:MATCH, '=~'.freeze, 2].freeze + TOKEN_NOMATCH = [:NOMATCH, '!~'.freeze, 2].freeze + TOKEN_GREATEREQUAL = [:GREATEREQUAL, '>='.freeze, 2].freeze + TOKEN_GREATERTHAN = [:GREATERTHAN, '>'.freeze, 1].freeze + TOKEN_LESSEQUAL = [:LESSEQUAL, '<='.freeze, 2].freeze + TOKEN_LESSTHAN = [:LESSTHAN, '<'.freeze, 1].freeze + + TOKEN_FARROW = [:FARROW, '=>'.freeze, 2].freeze + TOKEN_PARROW = [:PARROW, '+>'.freeze, 2].freeze + + TOKEN_LSHIFT = [:LSHIFT, '<<'.freeze, 2].freeze + TOKEN_LLCOLLECT = [:LLCOLLECT, '<<|'.freeze, 3].freeze + TOKEN_LCOLLECT = [:LCOLLECT, '<|'.freeze, 2].freeze + + TOKEN_RSHIFT = [:RSHIFT, '>>'.freeze, 2].freeze + TOKEN_RRCOLLECT = [:RRCOLLECT, '|>>'.freeze, 3].freeze + TOKEN_RCOLLECT = [:RCOLLECT, '|>'.freeze, 2].freeze + + TOKEN_PLUS = [:PLUS, '+'.freeze, 1].freeze + TOKEN_MINUS = [:MINUS, '-'.freeze, 1].freeze + TOKEN_DIV = [:DIV, '/'.freeze, 1].freeze + TOKEN_TIMES = [:TIMES, '*'.freeze, 1].freeze + TOKEN_MODULO = [:MODULO, '%'.freeze, 1].freeze + + TOKEN_NOT = [:NOT, '!'.freeze, 1].freeze + TOKEN_DOT = [:DOT, '.'.freeze, 1].freeze + TOKEN_PIPE = [:PIPE, '|'.freeze, 1].freeze + TOKEN_AT = [:AT , '@'.freeze, 1].freeze + TOKEN_COLON = [:COLON, ':'.freeze, 1].freeze + TOKEN_COMMA = [:COMMA, ','.freeze, 1].freeze + TOKEN_SEMIC = [:SEMIC, ';'.freeze, 1].freeze + TOKEN_QMARK = [:QMARK, '?'.freeze, 1].freeze + TOKEN_TILDE = [:TILDE, '~'.freeze, 1].freeze # lexed but not an operator in Puppet + + TOKEN_REGEXP = [:REGEXP, nil, 0].freeze + + TOKEN_IN_EDGE = [:IN_EDGE, '->'.freeze, 2].freeze + TOKEN_IN_EDGE_SUB = [:IN_EDGE_SUB, '~>'.freeze, 2].freeze + TOKEN_OUT_EDGE = [:OUT_EDGE, '<-'.freeze, 2].freeze + TOKEN_OUT_EDGE_SUB = [:OUT_EDGE_SUB, '<~'.freeze, 2].freeze + + # Tokens that are always unique to what has been lexed + TOKEN_STRING = [:STRING, nil, 0].freeze + TOKEN_DQPRE = [:DQPRE, nil, 0].freeze + TOKEN_DQMID = [:DQPRE, nil, 0].freeze + TOKEN_DQPOS = [:DQPRE, nil, 0].freeze + TOKEN_NUMBER = [:NUMBER, nil, 0].freeze + TOKEN_VARIABLE = [:VARIABLE, nil, 1].freeze + TOKEN_VARIABLE_EMPTY = [:VARIABLE, ''.freeze, 1].freeze + + # This is used for unrecognized tokens, will always be a single character. This particular instance + # is not used, but is kept here for documentation purposes. + TOKEN_OTHER = [:OTHER, nil, 0] + + # Keywords are all singleton tokens with pre calculated lengths. + # Booleans are pre-calculated (rather than evaluating the strings "false" "true" repeatedly. + # + KEYWORDS = { + "case" => [:CASE, 'case', 4], + "class" => [:CLASS, 'class', 5], + "default" => [:DEFAULT, 'default', 7], + "define" => [:DEFINE, 'define', 6], + "if" => [:IF, 'if', 2], + "elsif" => [:ELSIF, 'elsif', 5], + "else" => [:ELSE, 'else', 4], + "inherits" => [:INHERITS,'inherits', 8], + "node" => [:NODE, 'node', 4], + "and" => [:AND, 'and', 3], + "or" => [:OR, 'or', 2], + "undef" => [:UNDEF, 'undef', 5], + "false" => [:BOOLEAN, false, 5], + "true" => [:BOOLEAN, true, 4], + "in" => [:IN, 'in', 2], + "unless" => [:UNLESS, 'unless', 6], + } + KEYWORDS.each {|k,v| v[1].freeze; v.freeze } + KEYWORDS.freeze + + PATTERN_WS = %r{[[:blank:]\r]+} + + # The single line comment includes the line ending. + PATTERN_COMMENT = %r{#.*\r?} + PATTERN_MLCOMMENT = %r{/\*(.*?)\*/}m + + PATTERN_REGEX = %r{/[^/\n]*/} + PATTERN_REGEX_END = %r{/} + PATTERN_REGEX_A = %r{\A/} # for replacement to "" + PATTERN_REGEX_Z = %r{/\Z} # for replacement to "" + PATTERN_REGEX_ESC = %r{\\/} # for replacement to "/" + + # The 3x patterns: + # PATTERN_CLASSREF = %r{((::){0,1}[A-Z][-\w]*)+} + # PATTERN_NAME = %r{((::)?[a-z0-9][-\w]*)(::[a-z0-9][-\w]*)*} + + # The NAME and CLASSREF in 4x are strict. Each segment must start with + # a letter a-z and may not contain dashes anyway (\w includes letters, digits and _). + # + PATTERN_CLASSREF = %r{((::){0,1}[A-Z][\w]*)+} + PATTERN_NAME = %r{((::)?[a-z][-\w]*)(::[a-z][\w]*)*} + + PATTERN_DOLLAR_VAR = %r{\$(::)?(\w+::)*\w+} + PATTERN_NUMBER = %r{\b(?:0[xX][0-9A-Fa-f]+|0?\d+(?:\.\d+)?(?:[eE]-?\d+)?)\b} + + + # PERFORMANCE NOTE: + # Comparison against a frozen string is faster (than unfrozen). + # + STRING_BSLASH_BSLASH = '\\'.freeze + + def initialize + # Nothing is needed here since a lex starts with a call to lex_string, or lex_file which both + # resets the instance variables by calling initvars. + end + + # Convenience method, and for compatibility with older lexer. Use the lex_string instead which allows + # passing the path to use without first having to call file= (which reads the file if it exists). + # (Bad form to use overloading of assignment operator for something that is not really an assignment. Also, + # overloading of = does not allow passing more than one argument). + # + def string=(string) + lex_string(string, '') + end + + def lex_string(string, path='') + initvars + @scanner = StringScanner.new(string) + @locator = Puppet::Pops::Parser::Locator.locator(string, path) + end + + # Lexes an unquoted string. + # @param string [String] the string to lex + # @param locator [Puppet::Pops::Parser::Locator] the locator to use (a default is used if nil is given) + # @param escapes [Array] array of character strings representing the escape sequences to transform + # @param interpolate [Boolean] whether interpolation of expressions should be made or not. + # + def lex_unquoted_string(string, locator, escapes, interpolate) + initvars + @scanner = StringScanner.new(string) + @locator = locator || Puppet::Pops::Parser::Locator.locator(string, '') + @lexing_context[:escapes] = escapes || UQ_ESCAPES + @lexing_context[:uq_slurp_pattern] = (interpolate || !escapes.empty?) ? SLURP_UQ_PATTERN : SLURP_ALL_PATTERN + end + + # Convenience method, and for compatibility with older lexer. Use the lex_file instead. + # (Bad form to use overloading of assignment operator for something that is not really an assignment). + # + def file=(file) + lex_file(file) + end + + # Initializes lexing of the content of the given file. An empty string is used if the file does not exist. + # + def lex_file(file) + initvars + contents = File.exists?(file) ? File.read(file) : "" + @scanner = StringScanner.new(contents.freeze) + @locator = Puppet::Pops::Parser::Locator.locator(contents, file) + end + + def initvars + @token_queue = [] + @lexing_context = { + :brace_count => 0, + :after => nil, + } + # NOTE: additional keys are used; :escapes, :uq_slurp_pattern, :newline_jump + end + + # Scans all of the content and returns it in an array + # Note that the terminating [false, false] token is included in the result. + # + def fullscan + result = [] + scan {|token, value| result.push([token, value]) } + result + end + + # A block must be passed to scan. It will be called with two arguments, a symbol for the token, + # and an instance of LexerSupport::TokenValue + # PERFORMANCE NOTE: The TokenValue is designed to reduce the amount of garbage / termporary data + # and to only convert the lexer's internal tokens on demand. It is slightly mroe costly to create an + # instance of a class defined in Ruby than an Array or Hash, but the gain is much bigger since transformation + # logic is avoided for many of its memebers (most are never used (e.g. line/pos information which is only of + # value in general for error messages, and for some expressions (which the lexer does not know about). + # + def scan + # PERFORMANCE note: it is faster to access local variables than instance variables. + # This makes a small but notable difference since instance member access is avoided for + # every token in the lexed content. + # + scn = @scanner + ctx = @lexing_context + queue = @token_queue + + lex_error "Internal Error: No string or file given to lexer to process." unless scn + + scn.skip(PATTERN_WS) + + # This is the lexer's main loop + until queue.empty? && scn.eos? do + if token = queue.shift || lex_token + yield [ ctx[:after] = token[0], token[1] ] + end + end + + # Signals end of input + yield [false, false] + end + + # This lexes one token at the current position of the scanner. + # PERFORMANCE NOTE: Any change to this logic should be performance measured. + # + def lex_token + # Using three char look ahead (may be faster to do 2 char look ahead since only 2 tokens require a third + scn = @scanner + ctx = @lexing_context + before = @scanner.pos + + # A look ahead of 3 characters is used since the longest operator ambiguity is resolved at that point. + # PERFORMANCE NOTE: It is faster to peek once and use three separate variables for lookahead 0, 1 and 2. + # + la = scn.peek(3) + return nil if la.empty? + + # Ruby 1.8.7 requires using offset and length (or integers are returned. + # PERFORMANCE NOTE. + # It is slightly faster to use these local variables than accessing la[0], la[1] etc. in ruby 1.9.3 + # But not big enough to warrant two completely different implementations. + # + la0 = la[0,1] + la1 = la[1,1] + la2 = la[2,1] + + # PERFORMANCE NOTE: + # A case when, where all the cases are literal values is the fastest way to map from data to code. + # It is much faster than using a hash with lambdas, hash with symbol used to then invoke send etc. + # This case statement is evaluated for most character positions in puppet source, and great care must + # be taken to not introduce performance regressions. + # + case la0 + + when '.' + emit(TOKEN_DOT, before) + + when ',' + emit(TOKEN_COMMA, before) + + when '[' + emit(TOKEN_LBRACK, before) + + when ']' + emit(TOKEN_RBRACK, before) + + when '(' + emit(TOKEN_LPAREN, before) + + when ')' + emit(TOKEN_RPAREN, before) + + when ';' + emit(TOKEN_SEMIC, before) + + when '?' + emit(TOKEN_QMARK, before) + + when '*' + emit(TOKEN_TIMES, before) + + when '%' + emit(TOKEN_MODULO, before) + + when '{' + # The lexer needs to help the parser since the technology used cannot deal with + # lookahead of same token with different precedence. This is solved by making left brace + # after ? into a separate token. + # + ctx[:brace_count] += 1 + emit(if ctx[:after] == :QMARK + TOKEN_SELBRACE + else + TOKEN_LBRACE + end, before) + + when '}' + ctx[:brace_count] -= 1 + emit(TOKEN_RBRACE, before) + + # TOKENS @, @@, @( + when '@' + case la1 + when '@' + emit(TOKEN_ATAT, before) # TODO; Check if this is good for the grammar + when '(' + heredoc + else + emit(TOKEN_AT, before) + end + + # TOKENS |, |>, |>> + when '|' + emit(case la1 + when '>' + la2 == '>' ? TOKEN_RRCOLLECT : TOKEN_RCOLLECT + else + TOKEN_PIPE + end, before) + + # TOKENS =, =>, ==, =~ + when '=' + emit(case la1 + when '=' + TOKEN_ISEQUAL + when '>' + TOKEN_FARROW + when '~' + TOKEN_MATCH + else + TOKEN_EQUALS + end, before) + + # TOKENS '+', '+=', and '+>' + when '+' + emit(case la1 + when '=' + TOKEN_APPENDS + when '>' + TOKEN_PARROW + else + TOKEN_PLUS + end, before) + + # TOKENS '-', '->' + when '-' + emit(case la1 + when '>' + TOKEN_IN_EDGE + when '=' + TOKEN_DELETES + else + TOKEN_MINUS + end, before) + + # TOKENS !, !=, !~ + when '!' + emit(case la1 + when '=' + TOKEN_NOTEQUAL + when '~' + TOKEN_NOMATCH + else + TOKEN_NOT + end, before) + + # TOKENS ~>, ~ + when '~' + emit(la1 == '>' ? TOKEN_IN_EDGE_SUB : TOKEN_TILDE, before) + + when '#' + scn.skip(PATTERN_COMMENT) + nil + + # TOKENS '/', '/*' and '/ regexp /' + when '/' + case la1 + when '*' + scn.skip(PATTERN_MLCOMMENT) + nil + + else + # regexp position is a regexp, else a div + if regexp_acceptable? && value = scn.scan(PATTERN_REGEX) + # Ensure an escaped / was not matched + while value[-2..-2] == STRING_BSLASH_BSLASH # i.e. \\ + value += scn.scan_until(PATTERN_REGEX_END) + end + regex = value.sub(PATTERN_REGEX_A, '').sub(PATTERN_REGEX_Z, '').gsub(PATTERN_REGEX_ESC, '/') + emit_completed([:REGEX, Regexp.new(regex), scn.pos-before], before) + else + emit(TOKEN_DIV, before) + end + end + + # TOKENS <, <=, <|, <<|, <<, <-, <~ + when '<' + emit(case la1 + when '<' + if la2 == '|' + TOKEN_LLCOLLECT + else + TOKEN_LSHIFT + end + when '=' + TOKEN_LESSEQUAL + when '|' + TOKEN_LCOLLECT + when '-' + TOKEN_OUT_EDGE + when '~' + TOKEN_OUT_EDGE_SUB + else + TOKEN_LESSTHAN + end, before) + + # TOKENS >, >=, >> + when '>' + emit(case la1 + when '>' + TOKEN_RSHIFT + when '=' + TOKEN_GREATEREQUAL + else + TOKEN_GREATERTHAN + end, before) + + # TOKENS :, ::CLASSREF, ::NAME + when ':' + if la1 == ':' + before = scn.pos + # PERFORMANCE NOTE: This could potentially be speeded up by using a case/when listing all + # upper case letters. Alternatively, the 'A', and 'Z' comparisons may be faster if they are + # frozen. + # + if la2 >= 'A' && la2 <= 'Z' + # CLASSREF or error + value = scn.scan(PATTERN_CLASSREF) + if value + after = scn.pos + emit_completed([:CLASSREF, value, after-before], before) + else + # move to faulty position ('::' was ok) + scn.pos = scn.pos + 3 + lex_error_with_pos("Illegal fully qualified class reference") + end + else + # NAME or error + value = scn.scan(PATTERN_NAME) + if value + emit_completed([:NAME, value, scn.pos-before], before) + else + # move to faulty position ('::' was ok) + scn.pos = scn.pos + 2 + lex_error_with_pos("Illegal fully qualified name") + end + end + else + emit(TOKEN_COLON, before) + end + + when '$' + if value = scn.scan(PATTERN_DOLLAR_VAR) + emit_completed([:VARIABLE, value, scn.pos - before], before) + else + # consume the $ and let higher layer complain about the error instead of getting a syntax error + emit(TOKEN_VARIABLE_EMPTY, before) + end + + when '"' + # Recursive string interpolation, 'interpolate' either returns a STRING token, or + # a DQPRE with the rest of the string's tokens placed in the @token_queue + interpolate_dq + + when "'" + emit_completed([:STRING, slurp_sqstring, before-scn.pos], before) + + when '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' + value = scn.scan(PATTERN_NUMBER) + if value + length = scn.pos - before + assert_numeric(value, length) + emit_completed([:NUMBER, value, length], before) + else + # move to faulty position ([0-9] was ok) + scn.pos = scn.pos + 1 + lex_error_with_pos("Illegal number") + end + + when 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'w', 'x', 'y', 'z' + value = scn.scan(PATTERN_NAME) + if value + emit_completed(KEYWORDS[value] || [:NAME, value, scn.pos - before], before) + else + # move to faulty position ([a-z] was ok) + scn.pos = scn.pos + 1 + lex_error_with_pos("Illegal name") + end + + when 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'W', 'X', 'Y', 'Z' + value = scn.scan(PATTERN_CLASSREF) + if value + emit_completed([:CLASSREF, value, scn.pos - before], before) + else + # move to faulty position ([A-Z] was ok) + scn.pos = scn.pos + 1 + lex_error_with_pos("Illegal class reference") + end + + when "\n" + # If heredoc_cont is in effect there are heredoc text lines to skip over + # otherwise just skip the newline. + # + if ctx[:newline_jump] + scn.pos = ctx[:newline_jump] + ctx[:newline_jump] = nil + else + scn.pos += 1 + end + return nil + + when ' ', "\t", "\r" + scn.skip(PATTERN_WS) + return nil + + else + # In case of unicode spaces of various kinds that are captured by a regexp, but not by the + # simpler case expression above (not worth handling those special cases with better performance). + if scn.skip(PATTERN_WS) + nil + else + # "unrecognized char" + emit([:OTHER, la0, 1], before) + end + end + end + + # Emits (produces) a token [:tokensymbol, TokenValue] and moves the scanner's position past the token + # + def emit(token, byte_offset) + @scanner.pos = byte_offset + token[2] + [token[0], TokenValue.new(token, byte_offset, @locator)] + end + + # Emits the completed token on the form [:tokensymbol, TokenValue. This method does not alter + # the scanner's position. + # + def emit_completed(token, byte_offset) + [token[0], TokenValue.new(token, byte_offset, @locator)] + end + + # Enqueues a completed token at the given offset + def enqueue_completed(token, byte_offset) + @token_queue << emit_completed(token, byte_offset) + end + + # Allows subprocessors for heredoc etc to enqueue tokens that are tokenized by a different lexer instance + # + def enqueue(emitted_token) + @token_queue << emitted_token + end + + # Enqueues lexed tokens until either end of input, or the given brace_count is reached + # + def enqueue_until brace_count + scn = @scanner + ctx = @lexing_context + queue = @token_queue + + scn.skip(PATTERN_WS) + + until scn.eos? do + if token = lex_token + token_name = token[0] + ctx[:after] = token_name + return if token_name == :RBRACE && ctx[:brace_count] == brace_count + queue << token + else + scn.skip(PATTERN_WS) + end + end + end + + # Answers after which tokens it is acceptable to lex a regular expression. + # PERFORMANCE NOTE: + # It may be beneficial to turn this into a hash with default value of true for missing entries. + # A case expression with literal values will however create a hash internally. Since a reference is + # always needed to the hash, this access is almost as costly as a method call. + # + def regexp_acceptable? + case @lexing_context[:after] + + # Ends of (potential) R-value generating expressions + when :RPAREN, :RBRACK, :RBRACE, :RRCOLLECT, :RCOLLECT + false + + # Operands (that can be followed by DIV (even if illegal in grammar) + when :NAME, :CLASSREF, :NUMBER, :STRING, :BOOLEAN, :DQPRE, :DQMID, :DQPOST, :HEREDOC, :REGEX + false + + else + true + end + end + +end diff --git a/lib/puppet/pops/parser/lexer_support.rb b/lib/puppet/pops/parser/lexer_support.rb new file mode 100644 index 000000000..24c7c8c0c --- /dev/null +++ b/lib/puppet/pops/parser/lexer_support.rb @@ -0,0 +1,96 @@ +# This is an integral part of the Lexer. It is broken out into a separate module +# for maintainability of the code, and making the various parts of the lexer focused. +# +module Puppet::Pops::Parser::LexerSupport + + # Formats given message by appending file, line and position if available. + def positioned_message msg + result = [msg] + file = @locator.file + line = @locator.line_for_offset(@scanner.pos) + pos = @locator.pos_on_line(@scanner.pos) + + result << "in file #{file}" if file && file.is_a?(String) && !file.empty? + result << "at line #{line}:#{pos}" + result.join(" ") + end + + # Returns "" if at end of input, else the following 5 characters with \n \r \t escaped + def followed_by + return "" if @scanner.eos? + result = @scanner.rest[0,5] + "..." + result.gsub!("\t", '\t') + result.gsub!("\n", '\n') + result.gsub!("\r", '\r') + result + end + + # Returns a quoted string using " or ' depending on the given a strings's content + def format_quote(q) + if q == "'" + '"\'"' + else + "'#{q}'" + end + end + + # Raises a Puppet::LexError with the given message + def lex_error msg + raise Puppet::LexError.new(msg) + end + + # Raises a Puppet::LexError with the given message + def lex_error_with_pos msg + raise Puppet::LexError.new(positioned_message(msg)) + end + + # Asserts that the given string value is a float, or an integer in decimal, octal or hex form. + # An error is raised if the given value does not comply. + # + def assert_numeric(value, length) + if value =~ /^0[xX].*$/ + lex_error_with_pos("Not a valid hex number #{value}", length) unless value =~ /^0[xX][0-9A-Fa-f]+$/ + + elsif value =~ /^0[^.].*$/ + lex_error_with_pos("Not a valid octal number #{value}", length) unless value =~ /^0[0-7]+$/ + + else + lex_error_with_pos("Not a valid decimal number #{value}", length) unless value =~ /0?\d+(?:\.\d+)?(?:[eE]-?\d+)?/ + end + end + + # A TokenValue keeps track of the token symbol, the lexed text for the token, its length + # and its position in its source container. There is a cost associated with computing the + # line and position on line information. + # + class TokenValue + def initialize(token_array, offset, locator) + @token_array = token_array + @offset = offset + @locator = locator + end + def [](key) + case key + when :value + @token_array[1] + when :file + @locator.file + when :line + @locator.line_for_offset(@offset) + when :pos + @locator.pos_on_line(@offset) + when :length + @token_array[2] + when :locator + @locator + else + nil + end + end + + # TODO: Make this comparable for testing + # vs symbolic, vs array with symbol and non hash, array with symbol and hash) + # + end + +end \ No newline at end of file diff --git a/lib/puppet/pops/parser/locator.rb b/lib/puppet/pops/parser/locator.rb index 851249a2a..ef0d2f4f4 100644 --- a/lib/puppet/pops/parser/locator.rb +++ b/lib/puppet/pops/parser/locator.rb @@ -24,15 +24,23 @@ class Puppet::Pops::Parser::Locator # Constant set to true if multibyte is supported (includes multibyte extended regular expressions) MULTIBYTE = !!(LOCATOR_VERSION == :ruby19 || LOCATOR_VERSION == :ruby20) - def self.locator(string) + def self.locator(string, file) case LOCATOR_VERSION when :ruby20, :ruby19 - Locator19.new(string) + Locator19.new(string, file) else - Locator18.new(string) + Locator18.new(string, file) end end + # Returns the file name associated with the string content + def file + end + + # Returns the string content + def string + end + # Returns the position on line (first position on a line is 1) def pos_on_line(offset) end @@ -41,12 +49,6 @@ class Puppet::Pops::Parser::Locator def line_for_offset(offset) end - # Returns the position to use given a string scanner (the support for multibyte varies - # depending on Ruby version). - # - def scanner_pos(scanner) - end - # Returns the offset on line (first offset on a line is 0). # def offset_on_line(offset) @@ -67,12 +69,15 @@ class Puppet::Pops::Parser::Locator attr_accessor :string attr_accessor :prev_offset attr_accessor :prev_line + attr_reader :string + attr_reader :file # Create a locator based on a content string, and a boolean indicating if ruby version support multi-byte strings # or not. # - def initialize(string) + def initialize(string, file) @string = string.freeze + @file = file.freeze @prev_offset = nil @prev_line = nil compute_line_index @@ -201,5 +206,4 @@ class Puppet::Pops::Parser::Locator string.byteslice(offset, end_offset - offset).length end end - end \ No newline at end of file diff --git a/lib/puppet/pops/parser/slurp_support.rb b/lib/puppet/pops/parser/slurp_support.rb new file mode 100644 index 000000000..adf0d62eb --- /dev/null +++ b/lib/puppet/pops/parser/slurp_support.rb @@ -0,0 +1,95 @@ +# This module is an integral part of the Lexer. +# It defines the string slurping behavior - finding the string and non string parts in interpolated +# strings, translating escape sequences in strings to their single character equivalence. +# +# PERFORMANCE NOTE: The various kinds of slurping could be made even more generic, but requires +# additional parameter passing and evaluation of conditional logic. +# TODO: More detailed performance analysis of excessive character escaping and interpolation. +# +module Puppet::Pops::Parser::SlurpSupport + + SLURP_SQ_PATTERN = /(?:[^\\]|^|[^\\])(?:[\\]{2})*[']/ + SLURP_DQ_PATTERN = /(?:[^\\]|^|[^\\])(?:[\\]{2})*(["]|[$]\{?)/ + SLURP_UQ_PATTERN = /(?:[^\\]|^|[^\\])(?:[\\]{2})*([$]\{?|\z)/ + SLURP_ALL_PATTERN = /.*(\z)/ + SQ_ESCAPES = %w{ ' } + DQ_ESCAPES = %w{ \\ $ ' " r n t s u}+["\r\n", "\n"] + UQ_ESCAPES = %w{ \\ $ r n t s u}+["\r\n", "\n"] + + def slurp_sqstring + # skip the leading ' + @scanner.pos += 1 + str = slurp(@scanner, SLURP_SQ_PATTERN, SQ_ESCAPES, :ignore_invalid_escapes) || lex_error_with_pos("Unclosed quote after \"'\" followed by '#{followed_by}'") + str[0..-2] # strip closing "'" from result + end + + def slurp_dqstring + scn = @scanner + last = scn.matched + str = slurp(scn, SLURP_DQ_PATTERN, DQ_ESCAPES, false) + unless str + lex_error_with_pos("Unclosed quote after #{format_quote(last)} followed by '#{followed_by}'") + end + + # Terminator may be a single char '"', '$', or two characters '${' group match 1 (scn[1]) from the last slurp holds this + terminator = scn[1] + [str[0..(-1 - terminator.length)], terminator] + end + + # Copy from old lexer - can do much better + def slurp_uqstring + scn = @scanner + last = scn.matched + ignore = true + str = slurp(scn, @lexing_context[:uq_slurp_pattern], @lexing_context[:escapes], :ignore_invalid_escapes) + + # Terminator may be a single char '$', two characters '${', or empty string '' at the end of intput. + # Group match 1 holds this. + # The exceptional case is found by looking at the subgroup 1 of the most recent match made by the scanner (i.e. @scanner[1]). + # This is the last match made by the slurp method (having called scan_until on the scanner). + # If there is a terminating character is must be stripped and returned separately. + # + terminator = scn[1] + [str[0..(-1 - terminator.length)], terminator] + end + + # Slurps a string from the given scanner until the given pattern and then replaces any escaped + # characters given by escapes into their control-character equivalent or in case of line breaks, replaces the + # pattern \r?\n with an empty string. + # The returned string contains the terminating character. Returns nil if the scanner can not scan until the given + # pattern. + # + def slurp(scanner, pattern, escapes, ignore_invalid_escapes) + str = scanner.scan_until(pattern) || return + + # Process unicode escapes first as they require getting 4 hex digits + # If later a \u is found it is warned not to be a unicode escape + if escapes.include?('u') + str.gsub!(/\\u([\da-fAF]{4})/m) { + [$1.hex].pack("U") + } + end + + str.gsub!(/\\([^\r\n]|(?:\r?\n))/m) { + ch = $1 + if escapes.include? ch + case ch + when 'r' ; "\r" + when 'n' ; "\n" + when 't' ; "\t" + when 's' ; " " + when 'u' + Puppet.warning(positioned_message("Unicode escape '\\u' was not followed by 4 hex digits")) + "\\u" + when "\n" ; '' + when "\r\n"; '' + else ch + end + else + Puppet.warning(positioned_message("Unrecognized escape sequence '\\#{ch}'")) unless ignore_invalid_escapes + "\\#{ch}" + end + } + str + end +end \ No newline at end of file diff --git a/spec/lib/matchers/match_tokens2.rb b/spec/lib/matchers/match_tokens2.rb new file mode 100644 index 000000000..55c70dd89 --- /dev/null +++ b/spec/lib/matchers/match_tokens2.rb @@ -0,0 +1,74 @@ +# Matches tokens produced by lexer +# The given exepected is one or more entries where an entry is one of +# - a token symbol +# - an Array with a token symbol and the text value +# - an Array with a token symbol and a Hash specifying all attributes of the token +# - nil (ignore) +# +RSpec::Matchers.define :match_tokens2 do | *expected | + match do | actual | + expected.zip(actual).all? do |e, a| + compare(e, a) + end + end + + def failure_message_for_should + msg = ["Expected (#{expected.size}):"] + expected.each {|e| msg << e.to_s } + + zipped = expected.zip(actual) + msg << "\nGot (#{actual.size}):" + actual.each_with_index do |e, idx| + if zipped[idx] + zipped_expected = zipped[idx][0] + zipped_actual = zipped[idx][1] + + prefix = compare(zipped_expected, zipped_actual) ? ' ' : '*' + msg2 = ["#{prefix}["] + msg2 << e[0].to_s + msg2 << ', ' + if e[1] == false + msg2 << 'false' + else + msg2 << e[1][:value].to_s.dump + end + # If expectation has options, output them + if zipped_expected.is_a?(Array) && zipped_expected[2] && zipped_expected[2].is_a?(Hash) + msg2 << ", {" + msg3 = [] + zipped_expected[2].each do |k,v| + prefix = e[1][k] != v ? "*" : '' + msg3 << "#{prefix}:#{k}=>#{e[1][k]}" + end + msg2 << msg3.join(", ") + msg2 << "}" + end + msg2 << ']' + msg << msg2.join('') + end + end + msg.join("\n") + end + + def compare(e, a) + # if expected ends before actual + return true if !e + + # If actual ends before expected + return false if !a + + # Simple - only expect token to match + return true if a[0] == e + + # Expect value and optional attributes to match + if e.is_a? Array + # tokens must match + return false unless a[0] == e[0] + if e[2].is_a?(Hash) + e[2].each {|k,v| return false unless a[1][k] == v } + end + return (a[1] == e[1] || (a[1][:value] == e[1])) + end + false + end +end diff --git a/spec/unit/pops/parser/lexer2_spec.rb b/spec/unit/pops/parser/lexer2_spec.rb new file mode 100644 index 000000000..1df98785c --- /dev/null +++ b/spec/unit/pops/parser/lexer2_spec.rb @@ -0,0 +1,304 @@ +require 'spec_helper' +require 'matchers/match_tokens2' +require 'puppet/pops' +require 'puppet/pops/parser/lexer2' + +module EgrammarLexer2Spec + def tokens_scanned_from(s) + lexer = Lexer2.new + lexer.string = s + tokens = lexer.fullscan[0..-2] + end +end + +describe 'Lexer2' do + include EgrammarLexer2Spec + + { + :LBRACK => '[', + :RBRACK => ']', + :LBRACE => '{', + :RBRACE => '}', + :LPAREN => '(', + :RPAREN => ')', + :EQUALS => '=', + :ISEQUAL => '==', + :GREATEREQUAL => '>=', + :GREATERTHAN => '>', + :LESSTHAN => '<', + :LESSEQUAL => '<=', + :NOTEQUAL => '!=', + :NOT => '!', + :COMMA => ',', + :DOT => '.', + :COLON => ':', + :AT => '@', + :LLCOLLECT => '<<|', + :RRCOLLECT => '|>>', + :LCOLLECT => '<|', + :RCOLLECT => '|>', + :SEMIC => ';', + :QMARK => '?', + :OTHER => '\\', + :FARROW => '=>', + :PARROW => '+>', + :APPENDS => '+=', + :DELETES => '-=', + :PLUS => '+', + :MINUS => '-', + :DIV => '/', + :TIMES => '*', + :LSHIFT => '<<', + :RSHIFT => '>>', + :MATCH => '=~', + :NOMATCH => '!~', + :IN_EDGE => '->', + :OUT_EDGE => '<-', + :IN_EDGE_SUB => '~>', + :OUT_EDGE_SUB => '<~', + :PIPE => '|', + }.each do |name, string| + it "should lex a token named #{name.to_s}" do + tokens_scanned_from(string).should match_tokens2(name) + end + end + + { + "case" => :CASE, + "class" => :CLASS, + "default" => :DEFAULT, + "define" => :DEFINE, +# "import" => :IMPORT, # done as a function in egrammar + "if" => :IF, + "elsif" => :ELSIF, + "else" => :ELSE, + "inherits" => :INHERITS, + "node" => :NODE, + "and" => :AND, + "or" => :OR, + "undef" => :UNDEF, + "false" => :BOOLEAN, + "true" => :BOOLEAN, + "in" => :IN, + "unless" => :UNLESS, + }.each do |string, name| + it "should lex a keyword from '#{string}'" do + tokens_scanned_from(string).should match_tokens2(name) + end + end + + # TODO: Complete with all edge cases + [ 'A', 'A::B', '::A', '::A::B',].each do |string| + it "should lex a CLASSREF on the form '#{string}'" do + tokens_scanned_from(string).should match_tokens2([:CLASSREF, string]) + end + end + + # TODO: Complete with all edge cases + [ 'a', 'a::b', '::a', '::a::b',].each do |string| + it "should lex a NAME on the form '#{string}'" do + tokens_scanned_from(string).should match_tokens2([:NAME, string]) + end + end + + { 'false'=>false, 'true'=>true}.each do |string, value| + it "should lex a BOOLEAN on the form '#{string}'" do + tokens_scanned_from(string).should match_tokens2([:BOOLEAN, value]) + end + end + + [ '0', '1', '2982383139'].each do |string| + it "should lex a decimal integer NUMBER on the form '#{string}'" do + tokens_scanned_from(string).should match_tokens2([:NUMBER, string]) + end + end + + [ '0.0', '0.1', '0.2982383139', '29823.235', '10e23', '10e-23', '1.234e23'].each do |string| + it "should lex a decimal floating point NUMBER on the form '#{string}'" do + tokens_scanned_from(string).should match_tokens2([:NUMBER, string]) + end + end + + [ '00', '01', '0123', '0777'].each do |string| + it "should lex an octal integer NUMBER on the form '#{string}'" do + tokens_scanned_from(string).should match_tokens2([:NUMBER, string]) + end + end + + [ '0x0', '0x1', '0xa', '0xA', '0xabcdef', '0xABCDEF'].each do |string| + it "should lex an hex integer NUMBER on the form '#{string}'" do + tokens_scanned_from(string).should match_tokens2([:NUMBER, string]) + end + end + + { "''" => '', + "'a'" => 'a', + "'a\\'b'" =>"a'b", + "'a\\r\\n\\t\\s\\$\\\"\\\\b'" => "a\\r\\n\\t\\s\\$\\\"\\\\b" + }.each do |source, expected| + it "should lex a single quoted STRING on the form #{source}" do + tokens_scanned_from(source).should match_tokens2([:STRING, expected]) + end + end + + { '""' => '', + '"a"' => 'a', + '"a\'b"' => "a'b", + }.each do |source, expected| + it "should lex a double quoted STRING on the form #{source}" do + tokens_scanned_from(source).should match_tokens2([:STRING, expected]) + end + end + + { '"a$x b"' => [[:DQPRE, 'a', {:line => 1, :pos=>1, :length=>2 }], + [:VARIABLE, 'x', {:line => 1, :pos=>3, :length=>2 }], + [:DQPOST, ' b', {:line => 1, :pos=>5, :length=>3 }]], + + '"a$x.b"' => [[:DQPRE, 'a', {:line => 1, :pos=>1, :length=>2 }], + [:VARIABLE, 'x', {:line => 1, :pos=>3, :length=>2 }], + [:DQPOST, '.b', {:line => 1, :pos=>5, :length=>3 }]], + + '"$x.b"' => [[:DQPRE, '', {:line => 1, :pos=>1, :length=>1 }], + [:VARIABLE, 'x', {:line => 1, :pos=>2, :length=>2 }], + [:DQPOST, '.b', {:line => 1, :pos=>4, :length=>3 }]], + + '"a$x"' => [[:DQPRE, 'a', {:line => 1, :pos=>1, :length=>2 }], + [:VARIABLE, 'x', {:line => 1, :pos=>3, :length=>2 }], + [:DQPOST, '', {:line => 1, :pos=>5, :length=>1 }]], + }.each do |source, expected| + it "should lex an interpolated variable 'x' from #{source}" do + tokens_scanned_from(source).should match_tokens2(*expected) + end + end + + it "skips whitepsace" do + tokens_scanned_from(" if if if ").should match_tokens2(:IF, :IF, :IF) + tokens_scanned_from(" if \n\r\t\nif if ").should match_tokens2(:IF, :IF, :IF) + end + + it "skips single line comments" do + tokens_scanned_from("if # comment\nif").should match_tokens2(:IF, :IF) + end + + ["if /* comment */\nif", + "if /* comment\n */\nif", + "if /*\n comment\n */\nif", + ].each do |source| + it "skips multi line comments" do + tokens_scanned_from(source).should match_tokens2(:IF, :IF) + end + end + + { "=~" => [:MATCH, "=~ /./"], + "!~" => [:NOMATCH, "!~ /./"], + "," => [:COMMA, ", /./"], + "(" => [:LPAREN, "( /./"], + "[" => [:LBRACK, "[ /./"], + "{" => [:LBRACE, "{ /./"], + "+" => [:PLUS, "+ /./"], + "-" => [:MINUS, "- /./"], + "*" => [:TIMES, "* /./"], + ";" => [:SEMIC, "; /./"], + }.each do |token, entry| + it "should lex regexp after '#{token}'" do + tokens_scanned_from(entry[1]).should match_tokens2(entry[0], :REGEX) + end + end + + { "1" => ["1 /./", [:NUMBER, :DIV, :DOT, :DIV]], + "'a'" => ["'a' /./", [:STRING, :DIV, :DOT, :DIV]], + "true" => ["true /./", [:BOOLEAN, :DIV, :DOT, :DIV]], + "false" => ["false /./", [:BOOLEAN, :DIV, :DOT, :DIV]], + "/./" => ["/./ /./", [:REGEX, :DIV, :DOT, :DIV]], + "a" => ["a /./", [:NAME, :DIV, :DOT, :DIV]], + "A" => ["A /./", [:CLASSREF, :DIV, :DOT, :DIV]], + ")" => [") /./", [:RPAREN, :DIV, :DOT, :DIV]], + "}" => ["} /./", [:RBRACE, :DIV, :DOT, :DIV]], + "]" => ["] /./", [:RBRACK, :DIV, :DOT, :DIV]], + "|>" => ["|> /./", [:RCOLLECT, :DIV, :DOT, :DIV]], + "|>>" => ["|>> /./", [:RRCOLLECT, :DIV, :DOT, :DIV]], + '"a$a"' => ['"a$a" /./', [:DQPRE, :VARIABLE, :DQPOST, :DIV, :DOT, :DIV]], + }.each do |token, entry| + it "should not lex regexp after '#{token}'" do + tokens_scanned_from(entry[ 0 ]).should match_tokens2(*entry[ 1 ]) + end + end + +# TODO: Tricky, and heredoc not supported yet +# it "should not lex regexp after heredoc" do +# tokens_scanned_from("1 / /./").should match_tokens2(:NUMBER, :DIV, :REGEX) +# end + + it "should lex regexp at beginning of input" do + tokens_scanned_from(" /./").should match_tokens2(:REGEX) + end + + it "should lex regexp right of div" do + tokens_scanned_from("1 / /./").should match_tokens2(:NUMBER, :DIV, :REGEX) + end + + context 'when lexer lexes heredoc' do + it 'lexes tag, syntax and escapes, margin and right trim' do + code = <<-CODE + @(END:syntax/t) + Tex\\tt\\n + |- END + CODE + tokens_scanned_from(code).should match_tokens2([:HEREDOC, 'syntax'], [:STRING, "Tex\tt\\n"]) + end + + it 'lexes "tag", syntax and escapes, margin, right trim and interpolation' do + code = <<-CODE + @("END":syntax/t) + Tex\\tt\\n$var After + |- END + CODE + tokens_scanned_from(code).should match_tokens2( + [:HEREDOC, 'syntax'], + [:DQPRE, "Tex\tt\\n"], + [:VARIABLE, "var"], + [:DQPOST, " After"] + ) + end + end + + it 'should support unicode characters' do + code = <<-CODE + "x\\u2713y" + CODE + if Puppet::Pops::Parser::Locator::RUBYVER < Puppet::Pops::Parser::Locator::RUBY_1_9_3 + # Ruby 1.8.7 reports the multibyte char as several octal characters + tokens_scanned_from(code).should match_tokens2([:STRING, "x\342\234\223y"]) + else + # >= Ruby 1.9.3 reports \u + tokens_scanned_from(code).should match_tokens2([:STRING, "x\u2713y"]) + end + end + +end + +describe "when benchmarked" do + + it "Lexer 2", :profile => true do + lexer = Lexer2.new + code = 'if true \n{\n 10 + 10\n }\n else\n {\n "interpolate ${foo} and stuff"\n }\n' + m = Benchmark.measure {10000.times {lexer.string = code; lexer.fullscan }} + puts "Lexer2: #{m}" + end + +# it "Pops Optimized", :profile => true do +# lexer = Puppet::Pops::Parser::Lexer.new +# # code = 'if true { 10 + 10 } else { "interpolate ${foo} andn stuff" }' +# code = 'if true \n{\n 10 + 10\n }\n else\n {\n "interpolate ${foo} and stuff"\n }\n' +# m = Benchmark.measure {10000.times {lexer.string = code; lexer.fullscan }} +# puts "Pops O: #{m}" +# end +# +# it "Original", :profile => true do +# lexer = Puppet::Parser::Lexer.new +# # code = 'if true { 10 + 10 } else { "interpolate ${foo} andn stuff" }' +# code = 'if true \n{\n 10 + 10\n }\n else\n {\n "interpolate ${foo} and stuff"\n }\n' +# m = Benchmark.measure {10000.times {lexer.string = code; lexer.fullscan }} +# puts "Original: #{m}" +# end +end \ No newline at end of file From 872f0d7aef4ce3ad410af1c9fefc08e4a01dbdfb Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 20 Oct 2013 23:09:29 +0200 Subject: [PATCH 034/800] (#20516) Add support for epp templates in new lexer This adds support for epp templates to the new Lexer2 without affecting the overall performance of the lexer. This does not add the full epp template feature, only the support in the lexer and the EppParser that shows how the lexer is used to perform epp lexing when parsing. This commit also refacores method names for error handling since the most common way to report a lexer error is to add location information. --- lib/puppet/pops/parser/epp_parser.rb | 52 +++++ lib/puppet/pops/parser/epp_support.rb | 244 ++++++++++++++++++++++ lib/puppet/pops/parser/heredoc_support.rb | 16 +- lib/puppet/pops/parser/lexer2.rb | 82 +++++--- lib/puppet/pops/parser/lexer_support.rb | 18 +- lib/puppet/pops/parser/slurp_support.rb | 4 +- spec/unit/pops/parser/lexer2_spec.rb | 94 ++++++++- 7 files changed, 457 insertions(+), 53 deletions(-) create mode 100644 lib/puppet/pops/parser/epp_parser.rb create mode 100644 lib/puppet/pops/parser/epp_support.rb diff --git a/lib/puppet/pops/parser/epp_parser.rb b/lib/puppet/pops/parser/epp_parser.rb new file mode 100644 index 000000000..15dcd4fe5 --- /dev/null +++ b/lib/puppet/pops/parser/epp_parser.rb @@ -0,0 +1,52 @@ +# The EppParser is a specialized Puppet Parser that starts parsing in Epp Text mode +class Puppet::Pops::Parser::EppParser < Puppet::Pops::Parser::Parser + + # Initializes the epp parser support by creating a new instance of {Puppet::Pops::Parser::Lexer} + # configured to start in Epp Lexing mode. + # @return [void] + # + def initvars + self.lexer = Puppet::Pops::Parser::Lexer.new({:mode => :epp}) + end + + # Parses a file expected to contain epp text/DSL logic. + def parse_file(file) + unless FileTest.exist?(file) + unless file =~ /\.epp$/ + file = file + ".epp" + end + end + @lexer.file = file + _parse() + end + + # Performs the parsing and returns the resulting model. + # The lexer holds state, and this is setup with {#parse_string}, or {#parse_file}. + # + # TODO: Drop support for parsing a ruby file this way (should be done where it is decided + # which file to load/run (i.e. loaders), and initial file to run + # TODO: deal with options containing origin (i.e. parsing a string from externally known location). + # TODO: should return the model, not a Hostclass + # + # @api private + # + def _parse() + begin + @yydebug = false + main = yyparse(@lexer,:scan_epp) + # #Commented out now because this hides problems in the racc grammar while developing + # # TODO include this when test coverage is good enough. + # rescue Puppet::ParseError => except + # except.line ||= @lexer.line + # except.file ||= @lexer.file + # except.pos ||= @lexer.pos + # raise except + # rescue => except + # raise Puppet::ParseError.new(except.message, @lexer.file, @lexer.line, @lexer.pos, except) + end + main.record_origin(@lexer.file) if main + return main + ensure + @lexer.clear + end +end diff --git a/lib/puppet/pops/parser/epp_support.rb b/lib/puppet/pops/parser/epp_support.rb new file mode 100644 index 000000000..3d1013690 --- /dev/null +++ b/lib/puppet/pops/parser/epp_support.rb @@ -0,0 +1,244 @@ +# This module is an integral part of the Lexer. +# It handles scanning of EPP (Embedded Puppet), a form of string/expression interpolation similar to ERB. +# +require 'strscan' +module Puppet::Pops::Parser::EppSupport + + TOKEN_RENDER_STRING = [:RENDER_STRING, nil, 0] + TOKEN_RENDER_EXPR = [:RENDER_EXPR, nil, 0] + + # Scans all of the content and returns it in an array + # Note that the terminating [false, false] token is included in the result. + # + def fullscan_epp + result = [] + scan_epp {|token, value| result.push([token, value]) } + result + end + + # A block must be passed to scan. It will be called with two arguments, a symbol for the token, + # and an instance of LexerSupport::TokenValue + # PERFORMANCE NOTE: The TokenValue is designed to reduce the amount of garbage / termporary data + # and to only convert the lexer's internal tokens on demand. It is slightly mroe costly to create an + # instance of a class defined in Ruby than an Array or Hash, but the gain is much bigger since transformation + # logic is avoided for many of its memebers (most are never used (e.g. line/pos information which is only of + # value in general for error messages, and for some expressions (which the lexer does not know about). + # + def scan_epp + # PERFORMANCE note: it is faster to access local variables than instance variables. + # This makes a small but notable difference since instance member access is avoided for + # every token in the lexed content. + # + scn = @scanner + ctx = @lexing_context + queue = @token_queue + + lex_error "Internal Error: No string or file given to lexer to process." unless scn + + ctx[:epp_mode] = :text + interpolate_epp + + # This is the lexer's main loop + until queue.empty? && scn.eos? do + if token = queue.shift || lex_token + yield [ ctx[:after] = token[0], token[1] ] + end + end + if ctx[:epp_position] + lex_error("Unbalanced epp tag, reached without closing tag.", ctx[:epp_position]) + end + + # Signals end of input + yield [false, false] + end + + def interpolate_epp(skip_leading=false) + scn = @scanner + ctx = @lexing_context + eppscanner = EppScanner.new(scn) + before = scn.pos + s = eppscanner.scan(skip_leading) + + case eppscanner.mode + when :text + # Should be at end of scan, or something is terribly wrong + lex_error("Internal error: template scanner returns text mode and is not and end of input") unless @scanner.eos? + if s + # s may be nil if scanned text ends with an epp tag (i.e. no trailing text). + enqueue_completed([:RENDER_STRING, s, scn.pos - before], before) + end + ctx[:epp_open_position] = nil + # do nothing else, scanner is at the end + + when :error + lex_error(eppscanner.message()) + + when :epp + # It is meaningless to render empty string segments, and it is harmful to do this at + # the start of the scan as it prevents specification of parameters with <%- ($x, $y) -%> + # + if s && s.length > 0 + enqueue_completed([:RENDER_STRING, s, scn.pos - before], before) + end + # switch epp_mode to general (embedded) pp logic (non rendered result) + ctx[:epp_mode] = :epp + ctx[:epp_open_position] = scn.pos + + when :expr + # It is meaningless to render an empty string segment + if s && s.length > 0 + enqueue_completed([:RENDER_STRING, s, scn.pos - before], before) + end + enqueue_completed(TOKEN_RENDER_EXPR, before) + # switch mode to "epp expr interpolation" + ctx[:epp_mode] = :expr + ctx[:epp_open_position] = scn.pos + else + lex_error("Internal Error, Unknown mode #{eppscanner.mode} returned by template scanner") + end + nil + end + + # A scanner specialized in processing text with embedded EPP (Embedded Puppet) tags. + # The scanner is initialized with a StringScanner which it mutates as scanning takes place. + # The intent is to use one instance of EppScanner per wanted scan, and this instance represents + # the state after the scan. + # + # @example Sample usage + # a = "some text <% pp code %> some more text" + # scan = StringScanner.new(a) + # eppscan = EppScanner.new(scan) + # str = eppscan.scan + # eppscan.mode # => :epp + # eppscan.lines # => 0 + # eppscan + # + # The scanner supports + # * scanning text until <%, <%-, <%= + # * while scanning text: + # * tokens <%% and %%> are translated to <% and %> respetively and is returned as text. + # * tokens <%# and %> (or ending with -%>) and the enclosed text is a comment and is not included in the returned text + # * text following a comment that ends with -%> gets trailing whitespace (up to and including a line break) trimmed + # and this whitespace is not included in the returned text. + # * The continuation {#mode} is set to one of: + # * `:epp` - for a <% token + # * `:expr` - for a <%= token + # * `:text` - when there was no continuation mode (e.g. when input ends with text) + # * ':error` - if the tokens are unbalanced (reaching the end without a closing matching token). An error message + # is then also available via the method {#message}. + # + # Note that the intent is to use this specialized scanner to scan the text parts, when continuation mode is `:epp` or `:expr` + # the pp lexer should advance scanning (using the string scanner) until it reaches and consumes a `-%>` or '%>´ token. If it + # finds a `-%> token it should pass this on as a `skip_leading` parameter when it performs the next {#scan}. + # + class EppScanner + # The original scanner used by the lexer/container using EppScanner + attr_reader :scanner + + # The resulting mode after the scan. + # The mode is one of `:text` (the initial mode), `:epp` embedded code (no output), `:expr` (embedded + # expression), or `:error` + # + attr_reader :mode + + # An error message if `mode == :error`, `nil` otherwise. + attr_reader :message + + # If the first scan should skip leading whitespace (typically detected by the pp lexer when the + # pp mode end-token is found (i.e. `-%>`) and then passed on to the scanner. + # + attr_reader :skip_leading + + # Creates an EppScanner based on a StringScanner that represents the state where EppScanner should start scanning. + # The given scanner will be mutated (i.e. position moved) to reflect the EppScanner's end state after a scan. + # + def initialize(scanner) + @scanner = scanner + end + + # Scans from the current position in the configured scanner, advances this scanner's position until the end + # of the input, or to the first position after a mode switching token (`<%`, `<%-` or `<%=`). Number of processed + # lines and continuation mode can be obtained via {#lines}, and {#mode}. + # + # @return [String, nil] the scanned and processed text, or nil if at the end of the input. + # + def scan(skip_leading=false) + @mode = :text + @skip_leading = skip_leading + + return nil if scanner.eos? + s = "" + until scanner.eos? + part = @scanner.scan_until(/(<%)|\z/) + if @skip_leading + part.gsub!(/^[ \t]*\r?\n?/,'') + @skip_leading = false + end + # The spec for %%> is to transform it into a literal %>. This is done here, as %%> otherwise would go + # undetected in text mode. (i.e. it is not really necessary to escape %> with %%> in text mode unless + # adding checks stating that a literal %> is illegal in text (unbalanced). + # + part.gsub!(/%%>/, '%>') + s += part + case @scanner.peek(1) + when "" + # at the end + # if s ends with <% then this is an error (unbalanced <% %>) + if s.end_with? "<%" + @mode = :error + @message = "Unbalanced embedded expression - opening <% and reaching end of input" + else + mode = :epp + end + return s + + when "-" + # trim trailing whitespace on same line from accumulated s + # return text and signal switch to pp mode + @scanner.getch # drop the - + s.gsub!(/\r?\n?[ \t]*<%\z/, '') + @mode = :epp + return s + + when "%" + # verbatim text + # keep the scanned <%, and continue scanning after skipping one % + # (i.e. do nothing here) + @scanner.getch # drop the % to get a literal <% in the output + + when "=" + # expression + # return text and signal switch to expression mode + # drop the scanned <%, and skip past -%>, or %>, but also skip %%> + @scanner.getch # drop the = + s.slice!(-2..-1) + @mode = :expr + return s + + when "#" + # template comment + # drop the scanned <%, and skip past -%>, or %>, but also skip %%> + s.slice!(-2..-1) + + # unless there is an immediate termination i.e. <%#%> scan for the next %> that is not + # preceded by a % (i.e. skip %%>) + part = scanner.scan_until(/[^%]%>/) + unless part + @message = "Reaching end after opening <%# without seeing %>" + @mode = :error + return s + end + @skip_leading = true if part.end_with?("-%>") + # Continue scanning for more text + + else + # Switch to pp after having removed the <% + s.slice!(-2..-1) + @mode = :epp + return s + end + end + end + end + +end \ No newline at end of file diff --git a/lib/puppet/pops/parser/heredoc_support.rb b/lib/puppet/pops/parser/heredoc_support.rb index 0683b91be..eda606569 100644 --- a/lib/puppet/pops/parser/heredoc_support.rb +++ b/lib/puppet/pops/parser/heredoc_support.rb @@ -14,12 +14,12 @@ module Puppet::Pops::Parser::HeredocSupport # scanner is at position before @( # find end of the heredoc spec - str = scn.scan_until(/\)/) || lexer.lex_error_with_pos("Unclosed parenthesis after '@(' followed by '#{followed_by}'") + str = scn.scan_until(/\)/) || lexer.lex_error("Unclosed parenthesis after '@(' followed by '#{followed_by}'") pos_after_heredoc = scn.pos # Note: allows '+' as separator in syntax, but this needs validation as empty segments are not allowed unless md = str.match(PATTERN_HEREDOC) - lex_error_with_pos("Invalid syntax in heredoc expected @(endtag[:syntax][/escapes])") + lex_error("Invalid syntax in heredoc expected @(endtag[:syntax][/escapes])") end endtag = md[1] syntax = md[2] || '' @@ -33,7 +33,7 @@ module Puppet::Pops::Parser::HeredocSupport endtag = $1.strip end - lexer.lex_error_with_pos("Missing endtag in heredoc") unless endtag.length >= 1 + lexer.lex_error("Missing endtag in heredoc") unless endtag.length >= 1 resulting_escapes = [] if escapes @@ -41,7 +41,7 @@ module Puppet::Pops::Parser::HeredocSupport escapes = escapes.split('') unless escapes.length == escapes.uniq.length - lex_error_with_pos("An escape char for @() may only appear once. Got '#{escapes.join(', ')}") + lex_error("An escape char for @() may only appear once. Got '#{escapes.join(', ')}") end resulting_escapes = ["\\"] escapes.each do |e| @@ -51,7 +51,7 @@ module Puppet::Pops::Parser::HeredocSupport when "L" resulting_escapes += ["\n", "\r\n"] else - lex_error(positioned_message("Invalid heredoc escape char. Only t, r, n, s, u, L, $ allowed. Got '#{e}'")) + lex_error("Invalid heredoc escape char. Only t, r, n, s, u, L, $ allowed. Got '#{e}'") end end end @@ -66,7 +66,7 @@ module Puppet::Pops::Parser::HeredocSupport if ctx[:newline_jump] scn.pos = lexing_context[:newline_jump] else - scn.scan_until(/\n/) || lex_error_with_pos("Heredoc without any following lines of text") + scn.scan_until(/\n/) || lex_error("Heredoc without any following lines of text") end # offset 0 for the heredoc, and its line number heredoc_offset = scn.pos @@ -82,7 +82,7 @@ module Puppet::Pops::Parser::HeredocSupport endline_pattern = /([[:blank:]]*)(?:([|])[[:blank:]]*)?(?:(\-)[[:blank:]]*)?#{Regexp.escape(endtag)}[[:blank:]]*\r?(?:\n|\z)/ lines = [] while !scn.eos? do - one_line = scn.scan_until(/(?:\n|\z)/) || lexer.lex_error(eof_message) + one_line = scn.scan_until(/(?:\n|\z)/) || lexer.lex_error_without_pos(eof_message) if md = one_line.match(endline_pattern) leading = md[1] has_margin = md[2] == '|' @@ -105,7 +105,7 @@ module Puppet::Pops::Parser::HeredocSupport lines << one_line end end - lex_error(eof_message) + lex_error_without_pos(eof_message) end # Produces the heredoc text string given the individual (unprocessed) lines as an array. diff --git a/lib/puppet/pops/parser/lexer2.rb b/lib/puppet/pops/parser/lexer2.rb index e09ddf2c5..bd558f57d 100644 --- a/lib/puppet/pops/parser/lexer2.rb +++ b/lib/puppet/pops/parser/lexer2.rb @@ -1,8 +1,6 @@ -# A sample small lexer to try performance of ruby -# This lexer looks like what a "lex" like tool would output, but it is handwritten - -# TODO: template support - +# The Lexer is responsbile for turning source text into tokens. +# This version is a performance enhanced lexer (in comparison to the 3.x and earlier "future parser" lexer. +# # Old returns tokens [:KEY, value, { locator = } # Could return [[token], locator] # or Token.new([token], locator) with the same API x[0] = token_symbol, x[1] = self, x[:key] = (:value, :file, :line, :pos) etc @@ -11,6 +9,7 @@ require 'strscan' require 'puppet/pops/parser/lexer_support' require 'puppet/pops/parser/heredoc_support' require 'puppet/pops/parser/interpolation_support' +require 'puppet/pops/parser/epp_support' require 'puppet/pops/parser/slurp_support' class Lexer2 @@ -18,11 +17,13 @@ class Lexer2 include Puppet::Pops::Parser::HeredocSupport include Puppet::Pops::Parser::InterpolationSupport include Puppet::Pops::Parser::SlurpSupport + include Puppet::Pops::Parser::EppSupport - # ALl tokens ahve three slots, the token name (a Symbol), the token text (String), and a token text length. + # ALl tokens have three slots, the token name (a Symbol), the token text (String), and a token text length. # All operator and punctuation tokens reuse singleton arrays Tokens that require unique values create # a unique array per token. - # PEFORMANCE NOTES: + # + # PEFORMANCE NOTES: # This construct reduces the amount of object that needs to be created for operators and punctuation. # The length is pre-calculated for all singleton tokens. The length is used both to signal the length of # the token, and to advance the scanner position (without having to advance it with a scan(regexp)). @@ -92,6 +93,12 @@ class Lexer2 TOKEN_VARIABLE = [:VARIABLE, nil, 1].freeze TOKEN_VARIABLE_EMPTY = [:VARIABLE, ''.freeze, 1].freeze + # Tokens that start HEREDOC and EPP, both have syntax as an argument. + # These tokens are always unique to what has been lexed. + # + TOKEN_HEREDOC = [:HEREDOC, nil, 0].freeze + TOKEN_EPPSTART = [:EPPSTART, nil, 0].freeze + # This is used for unrecognized tokens, will always be a single character. This particular instance # is not used, but is kept here for documentation purposes. TOKEN_OTHER = [:OTHER, nil, 0] @@ -137,7 +144,7 @@ class Lexer2 # PATTERN_NAME = %r{((::)?[a-z0-9][-\w]*)(::[a-z0-9][-\w]*)*} # The NAME and CLASSREF in 4x are strict. Each segment must start with - # a letter a-z and may not contain dashes anyway (\w includes letters, digits and _). + # a letter a-z and may not contain dashes (\w includes letters, digits and _). # PATTERN_CLASSREF = %r{((::){0,1}[A-Z][\w]*)+} PATTERN_NAME = %r{((::)?[a-z][-\w]*)(::[a-z][\w]*)*} @@ -151,9 +158,17 @@ class Lexer2 # STRING_BSLASH_BSLASH = '\\'.freeze - def initialize - # Nothing is needed here since a lex starts with a call to lex_string, or lex_file which both - # resets the instance variables by calling initvars. + def initialize() + end + + # Clears the lexer state (it is not required to call this as it will be garbage collected + # and the next lex call (lex_string, lex_file) will reset the internal state. + # + def clear() + # not really needed, but if someone wants to ensure garbage is collected as early as possible + @scanner = nil + @locator = nil + @lexing_context = nil end # Convenience method, and for compatibility with older lexer. Use the lex_string instead which allows @@ -203,11 +218,11 @@ class Lexer2 def initvars @token_queue = [] + # NOTE: additional keys are used; :escapes, :uq_slurp_pattern, :newline_jump, :epp_* @lexing_context = { :brace_count => 0, :after => nil, } - # NOTE: additional keys are used; :escapes, :uq_slurp_pattern, :newline_jump end # Scans all of the content and returns it in an array @@ -236,7 +251,7 @@ class Lexer2 ctx = @lexing_context queue = @token_queue - lex_error "Internal Error: No string or file given to lexer to process." unless scn + lex_error_without_pos("Internal Error: No string or file given to lexer to process.") unless scn scn.skip(PATTERN_WS) @@ -311,7 +326,13 @@ class Lexer2 emit(TOKEN_TIMES, before) when '%' - emit(TOKEN_MODULO, before) + if la1 == '>' && ctx[:epp_mode] + scn.pos += 2 + ctx[:epp_mode] = :text + interpolate_epp + else + emit(TOKEN_MODULO, before) + end when '{' # The lexer needs to help the parser since the technology used cannot deal with @@ -373,16 +394,21 @@ class Lexer2 TOKEN_PLUS end, before) - # TOKENS '-', '->' + # TOKENS '-', '->', and epp '-%>' (end of interpolation with trim) when '-' - emit(case la1 - when '>' - TOKEN_IN_EDGE - when '=' - TOKEN_DELETES - else - TOKEN_MINUS - end, before) + if ctx[:epp_mode] && la1 == '%' && la2 == '>' + scn.pos += 3 + interpolate_epp(:with_trim) + else + emit(case la1 + when '>' + TOKEN_IN_EDGE + when '=' + TOKEN_DELETES + else + TOKEN_MINUS + end, before) + end # TOKENS !, !=, !~ when '!' @@ -473,7 +499,7 @@ class Lexer2 else # move to faulty position ('::' was ok) scn.pos = scn.pos + 3 - lex_error_with_pos("Illegal fully qualified class reference") + lex_error("Illegal fully qualified class reference") end else # NAME or error @@ -483,7 +509,7 @@ class Lexer2 else # move to faulty position ('::' was ok) scn.pos = scn.pos + 2 - lex_error_with_pos("Illegal fully qualified name") + lex_error("Illegal fully qualified name") end end else @@ -515,7 +541,7 @@ class Lexer2 else # move to faulty position ([0-9] was ok) scn.pos = scn.pos + 1 - lex_error_with_pos("Illegal number") + lex_error("Illegal number") end when 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', @@ -526,7 +552,7 @@ class Lexer2 else # move to faulty position ([a-z] was ok) scn.pos = scn.pos + 1 - lex_error_with_pos("Illegal name") + lex_error("Illegal name") end when 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', @@ -537,7 +563,7 @@ class Lexer2 else # move to faulty position ([A-Z] was ok) scn.pos = scn.pos + 1 - lex_error_with_pos("Illegal class reference") + lex_error("Illegal class reference") end when "\n" diff --git a/lib/puppet/pops/parser/lexer_support.rb b/lib/puppet/pops/parser/lexer_support.rb index 24c7c8c0c..3cfd5288c 100644 --- a/lib/puppet/pops/parser/lexer_support.rb +++ b/lib/puppet/pops/parser/lexer_support.rb @@ -4,11 +4,11 @@ module Puppet::Pops::Parser::LexerSupport # Formats given message by appending file, line and position if available. - def positioned_message msg + def positioned_message(msg, pos = nil) result = [msg] file = @locator.file - line = @locator.line_for_offset(@scanner.pos) - pos = @locator.pos_on_line(@scanner.pos) + line = @locator.line_for_offset(pos || @scanner.pos) + pos = @locator.pos_on_line(pos || @scanner.pos) result << "in file #{file}" if file && file.is_a?(String) && !file.empty? result << "at line #{line}:#{pos}" @@ -35,13 +35,13 @@ module Puppet::Pops::Parser::LexerSupport end # Raises a Puppet::LexError with the given message - def lex_error msg + def lex_error_without_pos msg raise Puppet::LexError.new(msg) end # Raises a Puppet::LexError with the given message - def lex_error_with_pos msg - raise Puppet::LexError.new(positioned_message(msg)) + def lex_error(msg, pos=nil) + raise Puppet::LexError.new(positioned_message(msg, pos)) end # Asserts that the given string value is a float, or an integer in decimal, octal or hex form. @@ -49,13 +49,13 @@ module Puppet::Pops::Parser::LexerSupport # def assert_numeric(value, length) if value =~ /^0[xX].*$/ - lex_error_with_pos("Not a valid hex number #{value}", length) unless value =~ /^0[xX][0-9A-Fa-f]+$/ + lex_error("Not a valid hex number #{value}", length) unless value =~ /^0[xX][0-9A-Fa-f]+$/ elsif value =~ /^0[^.].*$/ - lex_error_with_pos("Not a valid octal number #{value}", length) unless value =~ /^0[0-7]+$/ + lex_error("Not a valid octal number #{value}", length) unless value =~ /^0[0-7]+$/ else - lex_error_with_pos("Not a valid decimal number #{value}", length) unless value =~ /0?\d+(?:\.\d+)?(?:[eE]-?\d+)?/ + lex_error("Not a valid decimal number #{value}", length) unless value =~ /0?\d+(?:\.\d+)?(?:[eE]-?\d+)?/ end end diff --git a/lib/puppet/pops/parser/slurp_support.rb b/lib/puppet/pops/parser/slurp_support.rb index adf0d62eb..0f258c83b 100644 --- a/lib/puppet/pops/parser/slurp_support.rb +++ b/lib/puppet/pops/parser/slurp_support.rb @@ -19,7 +19,7 @@ module Puppet::Pops::Parser::SlurpSupport def slurp_sqstring # skip the leading ' @scanner.pos += 1 - str = slurp(@scanner, SLURP_SQ_PATTERN, SQ_ESCAPES, :ignore_invalid_escapes) || lex_error_with_pos("Unclosed quote after \"'\" followed by '#{followed_by}'") + str = slurp(@scanner, SLURP_SQ_PATTERN, SQ_ESCAPES, :ignore_invalid_escapes) || lex_error("Unclosed quote after \"'\" followed by '#{followed_by}'") str[0..-2] # strip closing "'" from result end @@ -28,7 +28,7 @@ module Puppet::Pops::Parser::SlurpSupport last = scn.matched str = slurp(scn, SLURP_DQ_PATTERN, DQ_ESCAPES, false) unless str - lex_error_with_pos("Unclosed quote after #{format_quote(last)} followed by '#{followed_by}'") + lex_error("Unclosed quote after #{format_quote(last)} followed by '#{followed_by}'") end # Terminator may be a single char '"', '$', or two characters '${' group match 1 (scn[1]) from the last slurp holds this diff --git a/spec/unit/pops/parser/lexer2_spec.rb b/spec/unit/pops/parser/lexer2_spec.rb index 1df98785c..1dd19d7f2 100644 --- a/spec/unit/pops/parser/lexer2_spec.rb +++ b/spec/unit/pops/parser/lexer2_spec.rb @@ -9,6 +9,12 @@ module EgrammarLexer2Spec lexer.string = s tokens = lexer.fullscan[0..-2] end + + def epp_tokens_scanned_from(s) + lexer = Lexer2.new + lexer.string = s + tokens = lexer.fullscan_epp[0..-2] + end end describe 'Lexer2' do @@ -275,16 +281,92 @@ describe 'Lexer2' do end end + context 'when lexing epp' do + it 'epp can contain just text' do + code = <<-CODE + This is just text + CODE + epp_tokens_scanned_from(code).should match_tokens2([:RENDER_STRING, " This is just text\n"]) + end + + it 'epp can contain text with interpolated rendered expressions' do + code = <<-CODE + This is <%= $x %> just text + CODE + epp_tokens_scanned_from(code).should match_tokens2( + [:RENDER_STRING, " This is "], + [:RENDER_EXPR, nil], + [:VARIABLE, "$x"], + [:RENDER_STRING, " just text\n"] + ) + end + + it 'epp can contain text with expressions that are not rendered' do + code = <<-CODE + This is <% $x=10 %> just text + CODE + epp_tokens_scanned_from(code).should match_tokens2( + [:RENDER_STRING, " This is "], + [:VARIABLE, "$x"], + :EQUALS, + [:NUMBER, "10"], + [:RENDER_STRING, " just text\n"] + ) + end + + it 'epp can skip leading space in tail text' do + code = <<-CODE + This is <% $x=10 -%> + just text + CODE + epp_tokens_scanned_from(code).should match_tokens2( + [:RENDER_STRING, " This is "], + [:VARIABLE, "$x"], + :EQUALS, + [:NUMBER, "10"], + [:RENDER_STRING, "just text\n"] + ) + end + + it 'epp can skip comments' do + code = <<-CODE + This is <% $x=10 -%> + <%# This is an epp comment -%> + just text + CODE + epp_tokens_scanned_from(code).should match_tokens2( + [:RENDER_STRING, " This is "], + [:VARIABLE, "$x"], + :EQUALS, + [:NUMBER, "10"], + [:RENDER_STRING, "just text\n"] + ) + end + + it 'epp can escape epp tags' do + code = <<-CODE + This is <% $x=10 -%> + <%% this is escaped epp %%> + CODE + epp_tokens_scanned_from(code).should match_tokens2( + [:RENDER_STRING, " This is "], + [:VARIABLE, "$x"], + :EQUALS, + [:NUMBER, "10"], + [:RENDER_STRING, "<% this is escaped epp %>\n"] + ) + end + end end describe "when benchmarked" do - it "Lexer 2", :profile => true do - lexer = Lexer2.new - code = 'if true \n{\n 10 + 10\n }\n else\n {\n "interpolate ${foo} and stuff"\n }\n' - m = Benchmark.measure {10000.times {lexer.string = code; lexer.fullscan }} - puts "Lexer2: #{m}" - end +# it "Lexer 2", :profile => true do +# lexer = Lexer2.new +# code = 'if true \n{\n 10 + 10\n }\n else\n {\n "interpolate ${foo} and stuff"\n }\n' +# m = Benchmark.measure {10000.times {lexer.string = code; lexer.fullscan }} +# puts "Lexer2: #{m}" +# end # it "Pops Optimized", :profile => true do # lexer = Puppet::Pops::Parser::Lexer.new From 39e097b145241e28ed16f2004760665353b03ee8 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 20 Oct 2013 23:14:14 +0200 Subject: [PATCH 035/800] (maint) Improve output from match_tokens2. The output did not include':' for the "Got" tokens. --- spec/lib/matchers/match_tokens2.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/lib/matchers/match_tokens2.rb b/spec/lib/matchers/match_tokens2.rb index 55c70dd89..c1872e68f 100644 --- a/spec/lib/matchers/match_tokens2.rb +++ b/spec/lib/matchers/match_tokens2.rb @@ -24,7 +24,7 @@ RSpec::Matchers.define :match_tokens2 do | *expected | zipped_actual = zipped[idx][1] prefix = compare(zipped_expected, zipped_actual) ? ' ' : '*' - msg2 = ["#{prefix}["] + msg2 = ["#{prefix}[:"] msg2 << e[0].to_s msg2 << ', ' if e[1] == false From 4adaaf7ed6f7bce0b996a95cb9225a9ca338f865 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 20 Oct 2013 23:24:39 +0200 Subject: [PATCH 036/800] (maint) Fix problem after adding flag to respond_tp? call The extra flag was not handled by the parser factory. (The flag is required for Ruby 2.0 since behavior has changed). --- lib/puppet/pops/model/factory.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/puppet/pops/model/factory.rb b/lib/puppet/pops/model/factory.rb index 1bfa5d405..72d38ad6c 100644 --- a/lib/puppet/pops/model/factory.rb +++ b/lib/puppet/pops/model/factory.rb @@ -417,8 +417,8 @@ class Puppet::Pops::Model::Factory end end - def respond_to?(meth) - current.respond_to?(meth) || super + def respond_to?(meth, include_all=false) + current.respond_to?(meth, include_all) || super end # Records the position (start -> end) and computes the resulting length. From 2989f40350ffe96792337370652871a0acd87583 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 20 Oct 2013 23:29:56 +0200 Subject: [PATCH 037/800] (maint) Move Lexer2 to the correct namespace (Puppet::Pops::Parser) It was named Lexer2 in the gloval namespace. --- lib/puppet/pops.rb | 1 + lib/puppet/pops/parser/lexer2.rb | 2 +- spec/unit/pops/parser/lexer2_spec.rb | 4 ++-- spec/unit/pops/parser/lexer_spec.rb | 12 ++++++------ 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/puppet/pops.rb b/lib/puppet/pops.rb index a5df72043..129d4e9d2 100644 --- a/lib/puppet/pops.rb +++ b/lib/puppet/pops.rb @@ -73,6 +73,7 @@ module Puppet require 'puppet/pops/parser/parser_support' require 'puppet/pops/parser/locator' require 'puppet/pops/parser/lexer' + require 'puppet/pops/parser/lexer2' require 'puppet/pops/parser/evaluating_parser' end diff --git a/lib/puppet/pops/parser/lexer2.rb b/lib/puppet/pops/parser/lexer2.rb index bd558f57d..7be7470e0 100644 --- a/lib/puppet/pops/parser/lexer2.rb +++ b/lib/puppet/pops/parser/lexer2.rb @@ -12,7 +12,7 @@ require 'puppet/pops/parser/interpolation_support' require 'puppet/pops/parser/epp_support' require 'puppet/pops/parser/slurp_support' -class Lexer2 +class Puppet::Pops::Parser::Lexer2 include Puppet::Pops::Parser::LexerSupport include Puppet::Pops::Parser::HeredocSupport include Puppet::Pops::Parser::InterpolationSupport diff --git a/spec/unit/pops/parser/lexer2_spec.rb b/spec/unit/pops/parser/lexer2_spec.rb index 1dd19d7f2..e8f7ce144 100644 --- a/spec/unit/pops/parser/lexer2_spec.rb +++ b/spec/unit/pops/parser/lexer2_spec.rb @@ -5,13 +5,13 @@ require 'puppet/pops/parser/lexer2' module EgrammarLexer2Spec def tokens_scanned_from(s) - lexer = Lexer2.new + lexer = Puppet::Pops::Parser::Lexer2.new lexer.string = s tokens = lexer.fullscan[0..-2] end def epp_tokens_scanned_from(s) - lexer = Lexer2.new + lexer = Puppet::Pops::Parser::Lexer2.new lexer.string = s tokens = lexer.fullscan_epp[0..-2] end diff --git a/spec/unit/pops/parser/lexer_spec.rb b/spec/unit/pops/parser/lexer_spec.rb index 071450c41..fc616fdc6 100755 --- a/spec/unit/pops/parser/lexer_spec.rb +++ b/spec/unit/pops/parser/lexer_spec.rb @@ -840,10 +840,10 @@ describe "when lexing interpolation detailed positioning should be correct" do end describe "when benchmarked" do - it "should not take longer than x sec", :profile => true do - lexer = Puppet::Pops::Parser::Lexer.new -# code = 'if true { 10 + 10 } else { "interpolate ${foo} andn stuff" }' - code = 'if true \n{\n 10 + 10\n }\n else\n {\n "interpolate ${foo} andn stuff"\n }\n' - puts Benchmark.measure {10000.times {lexer.string = code; lexer.fullscan }} - end +# it "should not take longer than x sec", :profile => true do +# lexer = Puppet::Pops::Parser::Lexer.new +## code = 'if true { 10 + 10 } else { "interpolate ${foo} andn stuff" }' +# code = 'if true \n{\n 10 + 10\n }\n else\n {\n "interpolate ${foo} andn stuff"\n }\n' +# puts Benchmark.measure {10000.times {lexer.string = code; lexer.fullscan }} +# end end From 1a544914978f70e84d361414d704955014ac9c89 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 21 Oct 2013 16:21:56 +0200 Subject: [PATCH 038/800] (perf) Make future parser use new faster lexer This makes the future parser use the new faster lexer. As a consequence features provided by the old lexer has now moved to the parser (keeping track of namestack). Locator information is still not optimized wrt the parser as the tokens/locator information are adapter to the "old" way (room for performance improvements). Tests are updated as the new lexer is both more restrictive, and in some case more relaxed (where regular expressions may occur). --- lib/puppet/pops/model/factory.rb | 76 +- lib/puppet/pops/parser/egrammar.ra | 75 +- lib/puppet/pops/parser/eparser.rb | 2172 +++++++++-------- .../pops/parser/interpolation_support.rb | 38 + lib/puppet/pops/parser/lexer2.rb | 93 +- lib/puppet/pops/parser/lexer_support.rb | 6 + lib/puppet/pops/parser/parser_support.rb | 48 +- lib/puppet/pops/visitor.rb | 1 - spec/unit/pops/parser/lexer2_spec.rb | 14 +- .../parser/parse_basic_expressions_spec.rb | 25 +- .../unit/pops/parser/parse_containers_spec.rb | 22 +- .../transform_basic_expressions_spec.rb | 8 +- .../pops/transformer/transform_calls_spec.rb | 4 +- .../transformer/transform_containers_spec.rb | 16 +- 14 files changed, 1404 insertions(+), 1194 deletions(-) diff --git a/lib/puppet/pops/model/factory.rb b/lib/puppet/pops/model/factory.rb index 72d38ad6c..0f44bce7b 100644 --- a/lib/puppet/pops/model/factory.rb +++ b/lib/puppet/pops/model/factory.rb @@ -15,6 +15,7 @@ class Puppet::Pops::Model::Factory # Shared build_visitor, since there are many instances of Factory being used @@build_visitor = Puppet::Pops::Visitor.new(self, "build") + @@interpolation_visitor = Puppet::Pops::Visitor.new(self, "interpolate") # Initialize a factory with a single object, or a class with arguments applied to build of # created instance # @@ -32,6 +33,16 @@ class Puppet::Pops::Model::Factory end end + # Polymorphic inerpolate + def interpolate() + begin + @@interpolation_visitor.visit_this(self, current) + rescue =>e + # require 'debugger'; debugger # enable this when in trouble... + raise e + end + end + # Building of Model classes def build_ArithmeticExpression(o, op, a, b) @@ -549,7 +560,7 @@ class Puppet::Pops::Model::Factory end def self.TEXT(expr) - new(Model::TextExpression, expr) + new(Model::TextExpression, new(expr).interpolate) end # TODO: This is the same a fqn factory method, don't know if callers to fqn and QNAME can live with the @@ -559,6 +570,15 @@ class Puppet::Pops::Model::Factory new(Model::QualifiedName, name) end + def self.NUMBER(name) + if n_radix = Puppet::Pops::Utils.to_n_with_radix(name) + new(Model::LiteralNumber, *n_radix) + else + # Bad number should already have been caught by lexer - this should never happen + raise ArgumentError, "Internal Error, NUMBER token does not contain a valid number, #{name}" + end + end + # Convert input string to either a qualified name, or a LiteralNumber with radix # def self.QNAME_OR_NUMBER(name) @@ -810,6 +830,60 @@ class Puppet::Pops::Model::Factory build(o.new(), *args) end + def interpolate_Factory(o) + interpolate(o.current) + end + + def interpolate_LiteralNumber(o) + # TODO + # convert number to name using correct radix + # convert name to variable + end + + def interpolate_Object(o) + o + end + + def interpolate_QualifiedName(o) + self.class.new(o).var + end + + # rewrite left expression to variable if it is name, number, and recurse if it is an access expression + # this is for interpolation support in new lexer (${NAME}, ${NAME[}}, ${NUMBER}, ${NUMBER[]} - all + # other expressions requires variables to be preceded with $ + # + def interpolate_AccessExpression(o) + if is_interop_rewriteable?(o.left_expr) + o.left_expr = to_ops(self.class.new(o.left_expr).interpolate) + end + o + end + + def interpolate_NamedAccessExpression(o) + if is_interop_rewriteable?(o.left_expr) + o.left_expr = to_ops(self.class.new(o.left_expr).interpolate) + end + o + end + + # Rewrite method calls on the form ${x.each ...} to ${$x.each} + def interpolate_CallMethodExpression(o) + if is_interop_rewriteable?(o.functor_expr) + o.functor_expr = to_ops(self.class.new(o.functor_expr).interpolate) + end + o + end + + def is_interop_rewriteable?(o) + case o + when Model::LiteralNumber, Model::AccessExpression, Model::QualifiedName, + Model::NamedAccessExpression, Model::CallMethodExpression + true + else + false + end + end + # Checks if the object is already a model object, or build it def to_ops(o, *args) if o.kind_of?(Model::PopsObject) diff --git a/lib/puppet/pops/parser/egrammar.ra b/lib/puppet/pops/parser/egrammar.ra index 23f6d4a63..da84502d5 100644 --- a/lib/puppet/pops/parser/egrammar.ra +++ b/lib/puppet/pops/parser/egrammar.ra @@ -10,11 +10,12 @@ token FALSE EQUALS APPENDS LESSEQUAL NOTEQUAL DOT COLON LLCOLLECT RRCOLLECT token QMARK LPAREN RPAREN ISEQUAL GREATEREQUAL GREATERTHAN LESSTHAN token IF ELSE token DEFINE ELSIF VARIABLE CLASS INHERITS NODE BOOLEAN -token NAME SEMIC CASE DEFAULT AT LCOLLECT RCOLLECT CLASSREF +token NAME SEMIC CASE DEFAULT AT ATAT LCOLLECT RCOLLECT CLASSREF token NOT OR AND UNDEF PARROW PLUS MINUS TIMES DIV LSHIFT RSHIFT UMINUS token MATCH NOMATCH REGEX IN_EDGE OUT_EDGE IN_EDGE_SUB OUT_EDGE_SUB token IN UNLESS PIPE -token SELBRACE +token LAMBDA SELBRACE +token NUMBER token LOW prechigh @@ -23,7 +24,7 @@ prechigh left PIPE left LPAREN left RPAREN - left AT + left AT ATAT left DOT left CALL left LBRACK @@ -150,6 +151,7 @@ literal_expression | hash | regex | text_or_name =LOW # resolves hash key ambiguity (racc W U require this?) + | number | type | undef @@ -358,24 +360,20 @@ resource_expression tmp.form = val[0] tmp when :defaults - error "A resource default can not be virtual or exported" + error val[1], "A resource default can not be virtual or exported" when :override - error "A resource override can not be virtual or exported" + error val[1], "A resource override can not be virtual or exported" else - error "Expression is not valid as a resource, resource-default, or resource-override" + error val[1], "Expression is not valid as a resource, resource-default, or resource-override" end loc result, val[1], val[4] } | at expression LBRACE attribute_operations endcomma RBRACE { result = case Factory.resource_shape(val[1]) - when :resource, :class - error "Defaults are not virtualizable" - when :defaults - error "Defaults are not virtualizable" - when :override - error "Defaults are not virtualizable" + when :resource, :class, :defaults, :override + error val[1], "Defaults are not virtualizable" else - error "Expression is not valid as a resource, resource-default, or resource-override" + error val[1], "Expression is not valid as a resource, resource-default, or resource-override" end } | expression LBRACE resourceinstances endsemi RBRACE { @@ -383,11 +381,11 @@ resource_expression when :resource, :class Factory.RESOURCE(Factory.fqn(token_text(val[0])), val[2]) when :defaults - error "A resource default can not specify a resource name" + error val[1], "A resource default can not specify a resource name" when :override - error "A resource override does not allow override of name of resource" + error val[1], "A resource override does not allow override of name of resource" else - error "Expression is not valid as a resource, resource-default, or resource-override" + error val[1], "Expression is not valid as a resource, resource-default, or resource-override" end loc result, val[0], val[4] } @@ -395,14 +393,14 @@ resource_expression result = case Factory.resource_shape(val[0]) when :resource, :class # This catches deprecated syntax. - error "All resource specifications require names" + error val[1], "All resource specifications require names" when :defaults Factory.RESOURCE_DEFAULTS(val[0], val[2]) when :override # This was only done for override in original - TODO shuld it be here at all Factory.RESOURCE_OVERRIDE(val[0], val[2]) else - error "Expression is not valid as a resource, resource-default, or resource-override" + error val[0], "Expression is not valid as a resource, resource-default, or resource-override" end loc result, val[0], val[4] } @@ -425,6 +423,7 @@ resource_expression at : AT { result = :virtual } | AT AT { result = :exported } + | ATAT { result = :exported } #---COLLECTION # @@ -490,16 +489,19 @@ attribute_operations # Produces Model::Definition # definition_expression - : DEFINE classname parameter_list LBRACE statements RBRACE { + : DEFINE classname parameter_list LBRACE opt_statements RBRACE { result = Factory.DEFINITION(classname(val[1][:value]), val[2], val[4]) loc result, val[0], val[5] - @lexer.indefine = false - } - | DEFINE classname parameter_list LBRACE RBRACE { - result = Factory.DEFINITION(classname(val[1][:value]), val[2], nil) - loc result, val[0], val[4] - @lexer.indefine = false + # New lexer does not keep track of this, this is done in validation + if @lexer.respond_to?(:'indefine=') + @lexer.indefine = false + end } +# | DEFINE classname parameter_list LBRACE RBRACE { +# result = Factory.DEFINITION(classname(val[1][:value]), val[2], nil) +# loc result, val[0], val[4] +# @lexer.indefine = false +# } #---HOSTCLASS # ORIGINAL COMMENT: Our class gets defined in the parent namespace, not our own. @@ -510,16 +512,22 @@ definition_expression # Produces Model::HostClassDefinition # hostclass_expression - : CLASS classname parameter_list classparent LBRACE statements RBRACE { - @lexer.namepop + : CLASS stacked_classname parameter_list classparent LBRACE opt_statements RBRACE { + namepop result = Factory.HOSTCLASS(classname(val[1][:value]), val[2], token_text(val[3]), val[5]) loc result, val[0], val[6] } - | CLASS classname parameter_list classparent LBRACE RBRACE { - @lexer.namepop - result = Factory.HOSTCLASS(classname(val[1][:value]), val[2], token_text(val[3]), nil) - loc result, val[0], val[5] - } +# | CLASS classname parameter_list classparent LBRACE RBRACE { +# result = Factory.HOSTCLASS(classname(val[1][:value]), val[2], token_text(val[3]), nil) +# loc result, val[0], val[5] +# } + + stacked_classname + : classname { namestack(val[0][:value]) ; result = val[0] } + + opt_statements + : statements + | nil # Produces String, name or nil result classparent @@ -575,7 +583,7 @@ node_definition_expression # String result classname : NAME { result = val[0] } - | CLASS { result = val[0] } + | CLASS { error val[0], "'class' is not a valid classname" } # Produces Array parameter_list @@ -642,6 +650,7 @@ dqtail : dqpost { result = [val[0]] } | dqmid dqrval { result = [val[0]] + val[1] } +number : NUMBER { result = Factory.NUMBER(val[0][:value]) ; loc result, val[0] } name : NAME { result = Factory.QNAME_OR_NUMBER(val[0][:value]) ; loc result, val[0] } type : CLASSREF { result = Factory.QREF(val[0][:value]) ; loc result, val[0] } undef : UNDEF { result = Factory.literal(:undef); loc result, val[0] } diff --git a/lib/puppet/pops/parser/eparser.rb b/lib/puppet/pops/parser/eparser.rb index 2d005c99a..caa157b3d 100644 --- a/lib/puppet/pops/parser/eparser.rb +++ b/lib/puppet/pops/parser/eparser.rb @@ -20,7 +20,7 @@ module Puppet module Parser class Parser < Racc::Parser -module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 700) +module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 709) # Make emacs happy # Local Variables: @@ -30,154 +30,158 @@ module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 700) ##### State transition tables begin ### clist = [ -'68,-127,204,51,53,232,217,51,53,87,88,84,79,91,286,95,-125,90,51,53', -'80,82,81,83,241,-193,51,53,278,230,219,240,51,53,231,-202,201,204,94', -'231,54,-127,86,85,54,292,72,73,75,74,77,78,264,70,71,118,-125,309,120', -'308,69,68,59,118,242,-193,120,76,89,54,87,88,84,79,91,-202,95,59,90', -'51,53,80,82,81,83,59,110,51,53,51,53,59,231,309,113,308,126,221,112', -'94,314,323,220,86,85,226,225,72,73,75,74,77,78,113,70,71,118,112,214', -'120,222,69,68,223,118,294,54,120,76,89,214,87,88,84,79,91,68,95,59,90', -'296,68,80,82,81,83,59,281,113,91,280,95,112,90,91,113,95,113,90,112', -'94,112,113,277,86,85,112,240,72,73,75,74,77,78,94,70,71,191,301,94,302', -'303,69,68,68,64,66,65,67,76,89,204,87,88,84,79,91,91,95,95,90,90,167', -'80,82,81,83,306,238,310,312,224,263,238,240,319,320,262,262,63,63,94', -'94,132,103,86,85,256,329,72,73,75,74,77,78,255,70,71,200,114,254,332', -'103,69,68,104,240,103,336,312,76,89,338,87,88,84,79,91,339,95,340,90', -'341,196,80,82,81,83,100,344,345,346,238,63,60,353,354,355,356,,,,94', -',,,86,85,,,72,73,75,74,77,78,,70,71,,,,,,69,68,,92,,,,76,89,,87,88,84', -'79,91,,95,,90,,,80,82,81,83,,,,,,,,,,,,,,,94,,,,86,85,,,72,73,75,74', -'77,78,,70,71,,,,,,69,68,,207,,,,76,89,,87,88,84,79,91,,95,,90,,,80,82', -'81,83,,,,,,,,,,,,,,,94,,,,86,85,,,72,73,75,74,77,78,,70,71,,,,,,69,68', -',208,,,,76,89,,87,88,84,79,91,,95,,90,,,80,82,81,83,,,,,,,,,,,,,,,94', -',,,86,85,,,72,73,75,74,77,78,,70,71,,,,,,69,68,,209,,,,76,89,,87,88', -'84,79,91,,95,,90,,,80,82,81,83,,,,,,,,,,,,,,,94,,,,86,85,,,72,73,75', -'74,77,78,,70,71,,,,,,69,68,,210,,,,76,89,,87,88,84,79,91,,95,,90,,,80', -'82,81,83,,,,,,,,,,,,,,,94,,,,86,85,,,72,73,75,74,77,78,,70,71,,,,,,69', -'68,,,,,,76,89,,87,88,84,79,91,,95,,90,,,80,82,81,83,,,,,,,,,,,,,,,94', -',,,86,85,,,72,73,75,74,77,78,,70,71,,,,,,69,68,,,,,,76,89,,87,88,84', -'79,91,,95,,90,,,80,82,81,83,,,,,,,,,,,,,,,94,,,,86,85,,,72,73,75,74', -'77,78,,70,71,,,,,,69,68,,,,,,76,89,,87,88,84,79,91,,95,,90,,,80,82,81', -'83,,,,,,,,,,,,,,,94,,,,86,85,,,72,73,75,74,77,78,,70,71,,,,,,69,68,', -',,,,76,89,,87,88,84,79,91,,95,,90,,,80,82,81,83,,,,,,,,,,,,,,,94,,,', -'86,85,,,72,73,75,74,77,78,,70,71,,,,,,69,68,,,,,,76,89,,87,88,84,79', -'91,,95,,90,,,80,82,81,83,,,,,,,,,,,,,,,94,,,,86,85,,,72,73,75,74,77', -'78,,70,71,,,,,,69,68,,,,,,76,89,,87,88,84,79,91,,95,,90,,,80,82,81,83', -',,,,,,,,,,,,,,94,,,,86,85,,,72,73,75,74,77,78,,70,71,,,,,,69,68,,,,', -',76,89,,87,88,84,79,91,,95,,90,,,80,82,81,83,,,,,,,,,,,,,,,94,,,,86', -'85,,,72,73,75,74,77,78,,70,71,,,,,,69,68,,,,,,76,89,,87,88,84,79,91', -',95,,90,,,80,82,81,83,,,,,,,,,,,,,,,94,,,,86,85,,,72,73,75,74,77,78', -',70,71,,,,,,69,68,,,,,,76,89,,87,88,84,79,91,236,95,,90,,,80,82,81,83', -',,,,,,,,,,,,,,94,,,,86,85,,,72,73,75,74,77,78,,70,71,,,,,,69,68,,,,', -',76,89,,87,88,84,79,91,,95,,90,,,80,82,81,83,68,,,,,,,,,,,,,91,94,95', -',90,86,85,,,72,73,75,74,77,78,,70,71,,,,,,69,,94,,,,68,76,89,,,,75,74', -',,,70,71,91,,95,,90,69,,68,,,,,76,,,,,,,,91,,95,,90,94,,,,,,68,,72,73', -'75,74,,,,70,71,,,91,94,95,69,90,,,,,,76,75,74,,68,,70,71,,,,,,69,,94', -',91,,95,76,90,,72,73,75,74,,68,,70,71,,,,,,69,,,,91,94,95,76,90,,,,', -'72,73,75,74,77,78,,70,71,,,,,,69,,94,,,,,76,68,,72,73,75,74,77,78,,70', -'71,,79,91,,95,69,90,,,80,,,76,68,,,,,,,,,,,,79,91,94,95,,90,,,80,,72', -'73,75,74,77,78,,70,71,,,,,,69,,94,,,,,76,68,,72,73,75,74,77,78,,70,71', -',79,91,,95,69,90,,,80,,,76,68,,,,,,,,,,,,79,91,94,95,,90,,,80,,72,73', -'75,74,77,78,,70,71,,,,,,69,,94,,,,,76,,,72,73,75,74,77,78,,70,71,,,68', -',,69,,,,,,,76,84,79,91,,95,,90,,,80,82,81,83,,,,,68,,,,,,,,,,94,,,91', -',95,,90,72,73,75,74,77,78,,70,71,,,68,,,69,,,,,,94,76,84,79,91,,95,', -'90,,,80,82,81,83,70,71,,,,,,69,,51,53,,,47,94,48,,,,85,,,72,73,75,74', -'77,78,,70,71,13,,,,,69,38,,44,,46,97,76,45,58,54,,40,57,,,,55,12,51', -'53,56,,47,11,48,,,,,,,59,,,,,,39,,166,13,,,,,,169,186,180,187,46,181', -'189,182,178,176,,171,184,,,,55,12,190,185,183,51,53,11,,47,,48,315,', -',59,,,,,188,170,,,,,,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12', -'51,53,56,68,47,11,48,,,,,,,59,,,91,,95,39,90,,13,,,,,,38,,44,,46,97', -',45,58,54,,40,57,94,,,55,12,51,53,56,68,47,11,48,,,,70,71,,59,,,91,69', -'95,39,90,,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,94,,,55,12,51,53,56', -',47,11,48,,,,70,71,,59,,,,69,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,', -'40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44', -',46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39', -',,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48', -',,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51', -'53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,', -'40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44', -',46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39', -',,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48', -',,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51', -'53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,', -'40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44', -',46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39', -',,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48', -',,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51', -'53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,', -'40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44', -',46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39', -',,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48', -',,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51', -'53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,', -'40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44', -',46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39', -',,13,,,,,,169,186,180,187,46,181,189,182,178,176,,171,184,,,,55,12,190', -'185,183,51,53,11,,47,,48,,,,59,,,,,188,170,,,,,,13,,,,,,38,,44,,46,97', -',45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,', -',,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,', -',59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51,53', -'56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,,40', -'57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46', -'97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13', -',,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,', -',,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51,53', -'56,,47,11,48,,,,,,,59,,,,,,39,,,13,198,,,,,38,,44,,46,97,,45,58,54,', -'40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44', -',46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39', -',,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48', -',,,,,,59,,,,,,39,,,13,206,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12', -'51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54', -',40,57,43,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,', -'44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47,11,48,,,,,,,59,,', -',,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56,', -'47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43', -',,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97', -',45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,', -',,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,', -',59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,,,56,51', -'53,11,,47,285,48,,,,59,,,,,,39,,,,,,13,,,,,,38,,44,,46,97,,45,58,54', -',40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44', -',46,42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47,11,48,326,,,,,,59,,', -',,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56,', -'47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,', -',,55,12,51,53,56,,47,11,48,268,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46', -'42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47,11,48,328,,,,,,59,,,,,,39', -',,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47,11', -'48,266,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,', -',55,12,51,53,56,,47,11,48,260,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42', -',45,58,54,,40,57,43,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13', -',,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47,11,48,,', -',,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51', -'53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,', -'40,57,,,,55,12,,,56,51,53,11,,47,124,48,,,,59,,,,,,39,,,,,,13,,,,,,38', -',44,,46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,', -',,39,,,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47', -'11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55', -'12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58', -'54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38', -',44,,46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,', -',,39,,,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47', -'11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,,40,57,,,,55', -'12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58', -'54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38', -',44,,46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,343,,,,,,59', -',,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56', -',47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97,,45,58,54,,40,57', -',,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,97', -',45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,', -',,,,38,,44,,46,97,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,348,', -',,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51', -'53,56,,47,11,48,350,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54', -',40,57,43,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,', -'44,,46,42,,45,58,54,61,40,57,43,,,55,12,51,53,56,,47,11,48,352,,,,,', -'59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53', -'56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40', -'57,43,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,', -'46,97,,45,58,54,,40,57,,,,55,12,,,56,,,11,,,,250,186,249,187,59,247', -'189,251,245,244,39,246,248,,,,,,190,185,252,250,186,249,187,,247,189', -'251,245,244,,246,248,,,188,253,,190,185,252,250,186,249,187,,247,189', -'251,245,244,,246,248,,,188,253,,190,185,252,,,,,,,,,,,,,,,,188,253' ] - racc_action_table = arr = ::Array.new(5016, nil) +'71,-129,208,235,-127,236,235,113,244,90,91,87,82,94,318,98,268,93,245', +'-197,83,85,84,86,-206,296,282,53,55,53,55,53,55,226,117,208,227,205', +'116,97,53,55,-129,89,88,-127,218,75,76,78,77,80,81,234,73,74,53,55,235', +'246,-197,72,298,71,122,-206,122,124,57,124,79,92,90,91,87,82,94,57,98', +'313,93,312,71,83,85,84,86,62,71,62,117,62,218,122,116,94,124,98,221', +'93,62,94,97,98,225,93,89,88,300,224,75,76,78,77,80,81,62,73,74,53,55', +'97,223,285,72,117,71,97,313,116,312,284,327,79,92,90,91,87,82,94,117', +'98,281,93,116,71,83,85,84,86,230,229,244,117,53,55,122,116,94,124,98', +'130,93,117,195,97,305,116,306,89,88,307,208,75,76,78,77,80,81,62,73', +'74,171,310,97,53,55,72,242,71,314,57,290,316,228,267,79,92,90,91,87', +'82,94,242,98,244,93,323,324,83,85,84,86,67,69,68,70,266,266,66,66,136', +'57,106,260,334,259,204,97,118,258,337,89,88,106,107,75,76,78,77,80,81', +'244,73,74,106,341,316,343,344,72,345,71,346,103,348,349,350,242,79,92', +'90,91,87,82,94,240,98,66,93,63,357,83,85,84,86,358,359,360,,,,,,,,,', +',,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,95,,,,,79,92', +'90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75', +'76,78,77,80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93', +',,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74', +',,,,,72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,', +',,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,,,,,,79', +'92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,', +',75,76,78,77,80,81,,73,74,,,,,,72,,71,,214,,,,,79,92,90,91,87,82,94', +',98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81', +',73,74,,,,,,72,,71,,213,,,,,79,92,90,91,87,82,94,,98,,93,,,83,85,84', +'86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71', +',,,,,,79,92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,', +',,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,212,,,,,79,92,90,91', +'87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78', +'77,80,81,,73,74,,,,,,72,,71,,211,,,,,79,92,90,91,87,82,94,,98,,93,,', +'83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,', +',,,72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93,,200,83,85,84,86,,,,,,', +',,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,,,,,,79', +'92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,', +',75,76,78,77,80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90,91,87,82,94,,98', +',93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73', +'74,,,,,,72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,', +',,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,,,,,', +'79,92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88', +',,75,76,78,77,80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90,91,87,82,94,71', +'98,,93,,71,83,85,84,86,,,,94,,98,,93,94,,98,,93,,,97,,,,89,88,,,75,76', +'78,77,80,81,97,73,74,53,55,97,,49,72,50,,,,,,73,74,79,92,,73,74,72,', +',13,,72,,,,173,190,184,191,48,185,193,186,182,180,,175,188,,,,,58,12', +'194,189,187,53,55,11,,49,,50,,,,62,,,,,192,174,,,,56,,13,,,,,,39,,46', +',48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,71,49,11,50,,,,,,,62,,,94', +',98,40,93,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,97,,,58,12,53', +'55,59,,49,11,50,,,,73,74,,62,,,,72,,40,,,13,56,,,,,39,,46,,48,100,,47', +'61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,', +',,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,333', +',,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58', +'12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47', +'61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13', +'56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11', +'50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45', +',,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43', +',47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,', +',13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49', +'11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,', +',,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48', +'100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,', +',13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49', +'11,50,319,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60', +',,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48', +'100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,', +',13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11', +'50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,', +'58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100', +',47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13', +'56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50', +',,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58', +'12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47', +'61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,', +',,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,', +',,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53', +'55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57', +',41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39', +',46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,', +',,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59', +',49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41', +'60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46', +',48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,', +'40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49', +'11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,', +',,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48', +'100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,', +',13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11', +'50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,', +'58,12,,,59,53,55,11,,49,,50,,,,62,,,,,,40,,,170,56,,13,,,,,,39,,46,', +'48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40', +',,13,56,,,,,173,190,184,191,48,185,193,186,182,180,,175,188,,,,,58,12', +'194,189,187,53,55,11,,49,,50,,,,62,,,,,192,174,,,,56,,13,,,,,,39,,46', +',48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,', +'40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49', +'11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,', +',,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48', +'100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,', +',13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11', +'50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,', +'58,12,53,55,59,,49,11,50,,,,,,,62,71,,,,,40,,,13,56,,,,94,39,98,46,93', +'48,100,,47,61,57,,41,60,,,,,58,12,,,59,53,55,11,97,49,,50,,,,62,,,78', +'77,,40,,73,74,56,,13,202,,72,,,39,,46,,48,100,79,47,61,57,,41,60,,,', +',58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100', +',47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,71,,,,,40,,,13', +'56,,,,94,39,98,46,93,48,100,,47,61,57,,41,60,,,,,58,12,,,59,53,55,11', +'97,49,,50,,,,62,,,78,77,,40,,73,74,56,,13,210,,72,,,39,,46,,48,100,79', +'47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56', +',,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,', +',,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12', +'53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61', +'57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,', +'39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,,,,', +',,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53', +'55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57', +',41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,71,,,,,40,,,13,56,,,,94', +'39,98,46,93,48,100,,47,61,57,,41,60,,,,,58,12,,,59,53,55,11,97,49,289', +'50,,,,62,75,76,78,77,,40,,73,74,56,,13,,,72,,,39,,46,,48,100,79,47,61', +'57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,71,,,,,40,,,13,56,,', +',94,39,98,46,93,48,100,,47,61,57,,41,60,,,,,58,12,,,59,53,55,11,97,49', +'128,50,,,,62,75,76,78,77,,40,,73,74,56,,13,,,72,,,39,,46,,48,100,79', +'47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56', +',,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,272', +',,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58', +'12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47', +'61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,270,,,,,,62,,,,,,40,,,13,56', +',,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50', +'264,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45', +',,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100', +',47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13', +'56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50', +',,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58', +'12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47', +'61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,352,,,,,,62,,,,,,40,,,13,56', +',,,,39,,46,,48,43,,47,61,57,71,41,60,44,45,,,58,12,,,59,,94,11,98,,93', +',,,,62,,,71,,,40,,,,56,,,,,,94,97,98,,93,,,,,75,76,78,77,80,81,,73,74', +',,,,,72,,,97,,,,,,79,,75,76,78,77,80,81,,73,74,71,,,,,72,,,,,,,82,94', +'79,98,,93,,,83,,,,,71,,,,,,,,,,,,82,94,97,98,,93,,,83,,75,76,78,77,80', +'81,,73,74,,,,,,72,,,97,,,,,,79,,75,76,78,77,80,81,,73,74,71,,,,,72,', +',,,,,82,94,79,98,,93,,,83,,,,,71,,,,,,,,,,,,82,94,97,98,,93,,,83,,75', +'76,78,77,80,81,,73,74,,,,,,72,,,97,,,,,,79,,75,76,78,77,80,81,,73,74', +'71,,,,,72,,,,,,87,82,94,79,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,', +',,,71,,75,76,78,77,80,81,,73,74,87,82,94,,98,72,93,,,83,85,84,86,,79', +',,,,,,,,,,53,55,,97,49,,50,354,88,,,75,76,78,77,80,81,,73,74,,,13,,', +'72,,,39,,46,,48,43,79,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11', +'50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,64,41,60,44', +'45,,,58,12,53,55,59,,49,11,50,356,,,,,,62,,,,,,40,,,13,56,,,,,39,,46', +',48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,,,,,,,62,,', +',,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55', +'59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,', +'41,60,,,,,58,12,,,59,,,11,,,,254,190,253,191,62,251,193,255,249,248', +'40,250,252,,56,,,,,194,189,256,254,190,253,191,,251,193,255,249,248', +',250,252,,,192,257,,,194,189,256,254,190,253,191,,251,193,255,249,248', +',250,252,,,192,257,,,194,189,256,,,,,,,,,,,,,,,,192,257' ] + racc_action_table = arr = ::Array.new(5065, nil) idx = 0 clist.each do |str| str.split(',', -1).each do |i| @@ -187,171 +191,176 @@ clist = [ end clist = [ -'163,178,103,71,71,163,116,223,223,163,163,163,163,163,223,163,176,163', -'217,217,163,163,163,163,179,184,219,219,214,139,116,293,70,70,139,183', -'103,214,163,205,71,178,163,163,223,233,163,163,163,163,163,163,205,163', -'163,217,176,266,217,266,163,162,71,219,179,184,219,163,163,70,162,162', -'162,162,162,183,162,217,162,182,182,162,162,162,162,219,42,45,45,48', -'48,70,269,306,277,306,48,123,277,162,269,277,123,162,162,131,131,162', -'162,162,162,162,162,42,162,162,182,42,111,182,125,162,161,125,45,237', -'48,45,162,162,115,161,161,161,161,161,140,161,182,161,239,98,161,161', -'161,161,45,216,97,140,215,140,97,140,98,181,98,44,98,181,161,44,180', -'212,161,161,180,243,161,161,161,161,161,161,140,161,161,93,257,98,259', -'261,161,160,96,7,7,7,7,161,161,262,160,160,160,160,160,96,160,96,160', -'96,91,160,160,160,160,265,211,267,268,128,204,272,273,274,275,202,279', -'133,62,160,96,60,198,160,160,197,291,160,160,160,160,160,160,195,160', -'160,102,43,193,300,301,160,99,37,173,36,309,310,160,160,312,99,99,99', -'99,99,313,99,317,99,318,99,99,99,99,99,35,324,325,327,172,5,1,342,347', -'349,351,,,,99,,,,99,99,,,99,99,99,99,99,99,,99,99,,,,,,99,10,,10,,,', -'99,99,,10,10,10,10,10,,10,,10,,,10,10,10,10,,,,,,,,,,,,,,,10,,,,10,10', -',,10,10,10,10,10,10,,10,10,,,,,,10,105,,105,,,,10,10,,105,105,105,105', -'105,,105,,105,,,105,105,105,105,,,,,,,,,,,,,,,105,,,,105,105,,,105,105', -'105,105,105,105,,105,105,,,,,,105,107,,107,,,,105,105,,107,107,107,107', -'107,,107,,107,,,107,107,107,107,,,,,,,,,,,,,,,107,,,,107,107,,,107,107', -'107,107,107,107,,107,107,,,,,,107,108,,108,,,,107,107,,108,108,108,108', -'108,,108,,108,,,108,108,108,108,,,,,,,,,,,,,,,108,,,,108,108,,,108,108', -'108,108,108,108,,108,108,,,,,,108,109,,109,,,,108,108,,109,109,109,109', -'109,,109,,109,,,109,109,109,109,,,,,,,,,,,,,,,109,,,,109,109,,,109,109', -'109,109,109,109,,109,109,,,,,,109,122,,,,,,109,109,,122,122,122,122', -'122,,122,,122,,,122,122,122,122,,,,,,,,,,,,,,,122,,,,122,122,,,122,122', -'122,122,122,122,,122,122,,,,,,122,305,,,,,,122,122,,305,305,305,305', -'305,,305,,305,,,305,305,305,305,,,,,,,,,,,,,,,305,,,,305,305,,,305,305', -'305,305,305,305,,305,305,,,,,,305,129,,,,,,305,305,,129,129,129,129', -'129,,129,,129,,,129,129,129,129,,,,,,,,,,,,,,,129,,,,129,129,,,129,129', -'129,129,129,129,,129,129,,,,,,129,192,,,,,,129,129,,192,192,192,192', -'192,,192,,192,,,192,192,192,192,,,,,,,,,,,,,,,192,,,,192,192,,,192,192', -'192,192,192,192,,192,192,,,,,,192,284,,,,,,192,192,,284,284,284,284', -'284,,284,,284,,,284,284,284,284,,,,,,,,,,,,,,,284,,,,284,284,,,284,284', -'284,284,284,284,,284,284,,,,,,284,288,,,,,,284,284,,288,288,288,288', -'288,,288,,288,,,288,288,288,288,,,,,,,,,,,,,,,288,,,,288,288,,,288,288', -'288,288,288,288,,288,288,,,,,,288,290,,,,,,288,288,,290,290,290,290', -'290,,290,,290,,,290,290,290,290,,,,,,,,,,,,,,,290,,,,290,290,,,290,290', -'290,290,290,290,,290,290,,,,,,290,298,,,,,,290,290,,298,298,298,298', -'298,,298,,298,,,298,298,298,298,,,,,,,,,,,,,,,298,,,,298,298,,,298,298', -'298,298,298,298,,298,298,,,,,,298,168,,,,,,298,298,,168,168,168,168', -'168,168,168,,168,,,168,168,168,168,,,,,,,,,,,,,,,168,,,,168,168,,,168', -'168,168,168,168,168,,168,168,,,,,,168,299,,,,,,168,168,,299,299,299', -'299,299,,299,,299,,,299,299,299,299,146,,,,,,,,,,,,,146,299,146,,146', -'299,299,,,299,299,299,299,299,299,,299,299,,,,,,299,,146,,,,150,299', -'299,,,,146,146,,,,146,146,150,,150,,150,146,,145,,,,,146,,,,,,,,145', -',145,,145,150,,,,,,151,,150,150,150,150,,,,150,150,,,151,145,151,150', -'151,,,,,,150,145,145,,152,,145,145,,,,,,145,,151,,152,,152,145,152,', -'151,151,151,151,,153,,151,151,,,,,,151,,,,153,152,153,151,153,,,,,152', -'152,152,152,152,152,,152,152,,,,,,152,,153,,,,,152,154,,153,153,153', -'153,153,153,,153,153,,154,154,,154,153,154,,,154,,,153,155,,,,,,,,,', -',,155,155,154,155,,155,,,155,,154,154,154,154,154,154,,154,154,,,,,', -'154,,155,,,,,154,156,,155,155,155,155,155,155,,155,155,,156,156,,156', -'155,156,,,156,,,155,157,,,,,,,,,,,,157,157,156,157,,157,,,157,,156,156', -'156,156,156,156,,156,156,,,,,,156,,157,,,,,156,,,157,157,157,157,157', -'157,,157,157,,,158,,,157,,,,,,,157,158,158,158,,158,,158,,,158,158,158', -'158,,,,,149,,,,,,,,,,158,,,149,,149,,149,158,158,158,158,158,158,,158', -'158,,,159,,,158,,,,,,149,158,159,159,159,,159,,159,,,159,159,159,159', -'149,149,,,,,,149,,90,90,,,90,159,90,,,,159,,,159,159,159,159,159,159', -',159,159,90,,,,,159,90,,90,,90,90,159,90,90,90,,90,90,,,,90,90,210,210', -'90,,210,90,210,,,,,,,90,,,,,,90,,90,210,,,,,,210,210,210,210,210,210', -'210,210,210,210,,210,210,,,,210,210,210,210,210,270,270,210,,270,,270', -'270,,,210,,,,,210,210,,,,,,270,,,,,,270,,270,,270,270,,270,270,270,', -'270,270,,,,270,270,72,72,270,148,72,270,72,,,,,,,270,,,148,,148,270', -'148,,72,,,,,,72,,72,,72,72,,72,72,72,,72,72,148,,,72,72,73,73,72,147', -'73,72,73,,,,148,148,,72,,,147,148,147,72,147,,73,,,,,,73,,73,,73,73', -',73,73,73,,73,73,147,,,73,73,74,74,73,,74,73,74,,,,147,147,,73,,,,147', -',73,,,74,,,,,,74,,74,,74,74,,74,74,74,,74,74,,,,74,74,75,75,74,,75,74', -'75,,,,,,,74,,,,,,74,,,75,,,,,,75,,75,,75,75,,75,75,75,,75,75,,,,75,75', -'76,76,75,,76,75,76,,,,,,,75,,,,,,75,,,76,,,,,,76,,76,,76,76,,76,76,76', -',76,76,,,,76,76,77,77,76,,77,76,77,,,,,,,76,,,,,,76,,,77,,,,,,77,,77', -',77,77,,77,77,77,,77,77,,,,77,77,78,78,77,,78,77,78,,,,,,,77,,,,,,77', -',,78,,,,,,78,,78,,78,78,,78,78,78,,78,78,,,,78,78,79,79,78,,79,78,79', -',,,,,,78,,,,,,78,,,79,,,,,,79,,79,,79,79,,79,79,79,,79,79,,,,79,79,80', -'80,79,,80,79,80,,,,,,,79,,,,,,79,,,80,,,,,,80,,80,,80,80,,80,80,80,', -'80,80,,,,80,80,81,81,80,,81,80,81,,,,,,,80,,,,,,80,,,81,,,,,,81,,81', -',81,81,,81,81,81,,81,81,,,,81,81,82,82,81,,82,81,82,,,,,,,81,,,,,,81', -',,82,,,,,,82,,82,,82,82,,82,82,82,,82,82,,,,82,82,83,83,82,,83,82,83', -',,,,,,82,,,,,,82,,,83,,,,,,83,,83,,83,83,,83,83,83,,83,83,,,,83,83,84', -'84,83,,84,83,84,,,,,,,83,,,,,,83,,,84,,,,,,84,,84,,84,84,,84,84,84,', -'84,84,,,,84,84,85,85,84,,85,84,85,,,,,,,84,,,,,,84,,,85,,,,,,85,,85', -',85,85,,85,85,85,,85,85,,,,85,85,86,86,85,,86,85,86,,,,,,,85,,,,,,85', -',,86,,,,,,86,,86,,86,86,,86,86,86,,86,86,,,,86,86,166,166,86,,166,86', -'166,,,,,,,86,,,,,,86,,,166,,,,,,166,,166,,166,166,,166,166,166,,166', -'166,,,,166,166,88,88,166,,88,166,88,,,,,,,166,,,,,,166,,,88,,,,,,88', -',88,,88,88,,88,88,88,,88,88,,,,88,88,89,89,88,,89,88,89,,,,,,,88,,,', -',,88,,,89,,,,,,89,,89,,89,89,,89,89,89,,89,89,,,,89,89,69,69,89,,69', -'89,69,,,,,,,89,,,,,,89,,,69,,,,,,69,,69,,69,69,,69,69,69,,69,69,,,,69', -'69,263,263,69,,263,69,263,,,,,,,69,,,,,,69,,,263,,,,,,263,,263,,263', -'263,,263,263,263,,263,263,,,,263,263,92,92,263,,92,263,92,,,,,,,263', -',,,,,263,,,92,,,,,,92,92,92,92,92,92,92,92,92,92,,92,92,,,,92,92,92', -'92,92,256,256,92,,256,,256,,,,92,,,,,92,92,,,,,,256,,,,,,256,,256,,256', -'256,,256,256,256,,256,256,,,,256,256,94,94,256,,94,256,94,,,,,,,256', -',,,,,256,,,94,,,,,,94,,94,,94,94,,94,94,94,,94,94,,,,94,94,95,95,94', -',95,94,95,,,,,,,94,,,,,,94,,,95,,,,,,95,,95,,95,95,,95,95,95,,95,95', -',,,95,95,242,242,95,,242,95,242,,,,,,,95,,,,,,95,,,242,,,,,,242,,242', -',242,242,,242,242,242,,242,242,,,,242,242,241,241,242,,241,242,241,', -',,,,,242,,,,,,242,,,241,,,,,,241,,241,,241,241,,241,241,241,,241,241', -',,,241,241,238,238,241,,238,241,238,,,,,,,241,,,,,,241,,,238,,,,,,238', -',238,,238,238,,238,238,238,,238,238,,,,238,238,68,68,238,,68,238,68', -',,,,,,238,,,,,,238,,,68,,,,,,68,,68,,68,68,,68,68,68,,68,68,,,,68,68', -'100,100,68,,100,68,100,,,,,,,68,,,,,,68,,,100,100,,,,,100,,100,,100', -'100,,100,100,100,,100,100,,,,100,100,232,232,100,,232,100,232,,,,,,', -'100,,,,,,100,,,232,,,,,,232,,232,,232,232,,232,232,232,,232,232,,,,232', -'232,231,231,232,,231,232,231,,,,,,,232,,,,,,232,,,231,,,,,,231,,231', -',231,231,,231,231,231,,231,231,,,,231,231,104,104,231,,104,231,104,', -',,,,,231,,,,,,231,,,104,104,,,,,104,,104,,104,104,,104,104,104,,104', -'104,,,,104,104,67,67,104,,67,104,67,,,,,,,104,,,,,,104,,,67,,,,,,67', -',67,,67,67,,67,67,67,,67,67,67,,,67,67,66,66,67,,66,67,66,,,,,,,67,', -',,,,67,,,66,,,,,,66,,66,,66,66,,66,66,66,,66,66,66,,,66,66,65,65,66', -',65,66,65,,,,,,,66,,,,,,66,,,65,,,,,,65,,65,,65,65,,65,65,65,,65,65', -'65,,,65,65,64,64,65,,64,65,64,,,,,,,65,,,,,,65,,,64,,,,,,64,,64,,64', -'64,,64,64,64,,64,64,64,,,64,64,110,110,64,,110,64,110,,,,,,,64,,,,,', -'64,,,110,,,,,,110,,110,,110,110,,110,110,110,,110,110,,,,110,110,229', -'229,110,,229,110,229,,,,,,,110,,,,,,110,,,229,,,,,,229,,229,,229,229', -',229,229,229,,229,229,,,,229,229,224,224,229,,224,229,224,,,,,,,229', -',,,,,229,,,224,,,,,,224,,224,,224,224,,224,224,224,,224,224,,,,224,224', -',,224,220,220,224,,220,220,220,,,,224,,,,,,224,,,,,,220,,,,,,220,,220', -',220,220,,220,220,220,,220,220,,,,220,220,63,63,220,,63,220,63,,,,,', -',220,,,,,,220,,,63,,,,,,63,,63,,63,63,,63,63,63,,63,63,63,,,63,63,280', -'280,63,,280,63,280,280,,,,,,63,,,,,,63,,,280,,,,,,280,,280,,280,280', -',280,280,280,,280,280,280,,,280,280,209,209,280,,209,280,209,,,,,,,280', -',,,,,280,,,209,,,,,,209,,209,,209,209,,209,209,209,,209,209,,,,209,209', -'208,208,209,,208,209,208,208,,,,,,209,,,,,,209,,,208,,,,,,208,,208,', -'208,208,,208,208,208,,208,208,208,,,208,208,281,281,208,,281,208,281', -'281,,,,,,208,,,,,,208,,,281,,,,,,281,,281,,281,281,,281,281,281,,281', -'281,281,,,281,281,207,207,281,,207,281,207,207,,,,,,281,,,,,,281,,,207', -',,,,,207,,207,,207,207,,207,207,207,,207,207,207,,,207,207,200,200,207', -',200,207,200,200,,,,,,207,,,,,,207,,,200,,,,,,200,,200,,200,200,,200', -'200,200,,200,200,200,,,200,200,61,61,200,,61,200,61,,,,,,,200,,,,,,200', -',,61,,,,,,61,,61,,61,61,,61,61,61,,61,61,61,,,61,61,171,171,61,,171', -'61,171,,,,,,,61,,,,,,61,,,171,,,,,,171,,171,,171,171,,171,171,171,,171', -'171,,,,171,171,52,52,171,,52,171,52,,,,,,,171,,,,,,171,,,52,,,,,,52', -',52,,52,52,,52,52,52,,52,52,,,,52,52,,,52,47,47,52,,47,47,47,,,,52,', -',,,,52,,,,,,47,,,,,,47,,47,,47,47,,47,47,47,,47,47,,,,47,47,170,170', -'47,,170,47,170,,,,,,,47,,,,,,47,,,170,,,,,,170,,170,,170,170,,170,170', -'170,,170,170,,,,170,170,169,169,170,,169,170,169,,,,,,,170,,,,,,170', -',,169,,,,,,169,,169,,169,169,,169,169,169,,169,169,,,,169,169,292,292', -'169,,292,169,292,,,,,,,169,,,,,,169,,,292,,,,,,292,,292,,292,292,,292', -'292,292,,292,292,,,,292,292,41,41,292,,41,292,41,,,,,,,292,,,,,,292', -',,41,,,,,,41,,41,,41,41,,41,41,41,,41,41,,,,41,41,40,40,41,,40,41,40', -',,,,,,41,,,,,,41,,,40,,,,,,40,,40,,40,40,,40,40,40,,40,40,,,,40,40,39', -'39,40,,39,40,39,,,,,,,40,,,,,,40,,,39,,,,,,39,,39,,39,39,,39,39,39,', -'39,39,,,,39,39,38,38,39,,38,39,38,,,,,,,39,,,,,,39,,,38,,,,,,38,,38', -',38,38,,38,38,38,,38,38,,,,38,38,308,308,38,,308,38,308,,,,,,,38,,,', -',,38,,,308,,,,,,308,,308,,308,308,,308,308,308,,308,308,,,,308,308,320', -'320,308,,320,308,320,320,,,,,,308,,,,,,308,,,320,,,,,,320,,320,,320', -'320,,320,320,320,,320,320,320,,,320,320,13,13,320,,13,320,13,,,,,,,320', -',,,,,320,,,13,,,,,,13,,13,,13,13,,13,13,13,,13,13,,,,13,13,12,12,13', -',12,13,12,,,,,,,13,,,,,,13,,,12,,,,,,12,,12,,12,12,,12,12,12,,12,12', -',,,12,12,11,11,12,,11,12,11,,,,,,,12,,,,,,12,,,11,,,,,,11,,11,,11,11', -',11,11,11,,11,11,,,,11,11,336,336,11,,336,11,336,336,,,,,,11,,,,,,11', -',,336,,,,,,336,,336,,336,336,,336,336,336,,336,336,336,,,336,336,338', -'338,336,,338,336,338,338,,,,,,336,,,,,,336,,,338,,,,,,338,,338,,338', -'338,,338,338,338,,338,338,338,,,338,338,4,4,338,,4,338,4,,,,,,,338,', -',,,,338,,,4,,,,,,4,,4,,4,4,,4,4,4,4,4,4,4,,,4,4,339,339,4,,339,4,339', -'339,,,,,,4,,,,,,4,,,339,,,,,,339,,339,,339,339,,339,339,339,,339,339', -'339,,,339,339,0,0,339,,0,339,0,,,,,,,339,,,,,,339,,,0,,,,,,0,,0,,0,0', -',0,0,0,,0,0,0,,,0,0,87,87,0,,87,0,87,,,,,,,0,,,,,,0,,,87,,,,,,87,,87', -',87,87,,87,87,87,,87,87,,,,87,87,,,87,,,87,,,,235,235,235,235,87,235', -'235,235,235,235,87,235,235,,,,,,235,235,235,191,191,191,191,,191,191', -'191,191,191,,191,191,,,235,235,,191,191,191,240,240,240,240,,240,240', -'240,240,240,,240,240,,,191,191,,240,240,240,,,,,,,,,,,,,,,,240,240' ] - racc_action_check = arr = ::Array.new(5016, nil) +'167,182,106,209,180,167,273,43,297,167,167,167,167,167,273,167,209,167', +'183,188,167,167,167,167,187,237,218,221,221,223,223,74,74,129,43,218', +'129,106,43,167,73,73,182,167,167,180,114,167,167,167,167,167,167,143', +'167,167,186,186,143,183,188,167,241,166,221,187,223,221,74,223,167,167', +'166,166,166,166,166,73,166,270,166,270,144,166,166,166,166,221,101,223', +'100,74,119,186,100,144,186,144,120,144,73,101,166,101,127,101,166,166', +'243,127,166,166,166,166,166,166,186,166,166,47,47,144,120,220,166,281', +'165,101,310,281,310,219,281,166,166,165,165,165,165,165,185,165,216', +'165,185,99,165,165,165,165,135,135,247,46,50,50,47,46,99,47,99,50,99', +'184,96,165,261,184,263,165,165,265,266,165,165,165,165,165,165,47,165', +'165,94,269,99,227,227,165,215,164,271,50,227,272,132,208,165,165,164', +'164,164,164,164,276,164,277,164,278,279,164,164,164,164,7,7,7,7,206', +'283,137,65,63,227,202,201,295,199,105,164,44,197,304,164,164,305,38', +'164,164,164,164,164,164,177,164,164,37,313,314,316,317,164,321,172,322', +'36,328,329,332,176,164,164,172,172,172,172,172,172,172,5,172,1,347,172', +'172,172,172,351,353,355,,,,,,,,,,,,,172,,,,172,172,,,172,172,172,172', +'172,172,,172,172,,,,,,172,,10,,10,,,,,172,172,10,10,10,10,10,,10,,10', +',,10,10,10,10,,,,,,,,,,,,,,,,10,,,,10,10,,,10,10,10,10,10,10,,10,10', +',,,,,10,,196,,,,,,,10,10,196,196,196,196,196,,196,,196,,,196,196,196', +'196,,,,,,,,,,,,,,,,196,,,,196,196,,,196,196,196,196,196,196,,196,196', +',,,,,196,,133,,,,,,,196,196,133,133,133,133,133,,133,,133,,,133,133', +'133,133,,,,,,,,,,,,,,,,133,,,,133,133,,,133,133,133,133,133,133,,133', +'133,,,,,,133,,126,,,,,,,133,133,126,126,126,126,126,,126,,126,,,126', +'126,126,126,,,,,,,,,,,,,,,,126,,,,126,126,,,126,126,126,126,126,126', +',126,126,,,,,,126,,112,,112,,,,,126,126,112,112,112,112,112,,112,,112', +',,112,112,112,112,,,,,,,,,,,,,,,,112,,,,112,112,,,112,112,112,112,112', +'112,,112,112,,,,,,112,,111,,111,,,,,112,112,111,111,111,111,111,,111', +',111,,,111,111,111,111,,,,,,,,,,,,,,,,111,,,,111,111,,,111,111,111,111', +'111,111,,111,111,,,,,,111,,309,,,,,,,111,111,309,309,309,309,309,,309', +',309,,,309,309,309,309,,,,,,,,,,,,,,,,309,,,,309,309,,,309,309,309,309', +'309,309,,309,309,,,,,,309,,110,,110,,,,,309,309,110,110,110,110,110', +',110,,110,,,110,110,110,110,,,,,,,,,,,,,,,,110,,,,110,110,,,110,110', +'110,110,110,110,,110,110,,,,,,110,,108,,108,,,,,110,110,108,108,108', +'108,108,,108,,108,,,108,108,108,108,,,,,,,,,,,,,,,,108,,,,108,108,,', +'108,108,108,108,108,108,,108,108,,,,,,108,,102,,,,,,,108,108,102,102', +'102,102,102,,102,,102,,102,102,102,102,102,,,,,,,,,,,,,,,,102,,,,102', +'102,,,102,102,102,102,102,102,,102,102,,,,,,102,,288,,,,,,,102,102,288', +'288,288,288,288,,288,,288,,,288,288,288,288,,,,,,,,,,,,,,,,288,,,,288', +'288,,,288,288,288,288,288,288,,288,288,,,,,,288,,303,,,,,,,288,288,303', +'303,303,303,303,,303,,303,,,303,303,303,303,,,,,,,,,,,,,,,,303,,,,303', +'303,,,303,303,303,303,303,303,,303,303,,,,,,303,,302,,,,,,,303,303,302', +'302,302,302,302,,302,,302,,,302,302,302,302,,,,,,,,,,,,,,,,302,,,,302', +'302,,,302,302,302,302,302,302,,302,302,,,,,,302,,292,,,,,,,302,302,292', +'292,292,292,292,,292,,292,,,292,292,292,292,,,,,,,,,,,,,,,,292,,,,292', +'292,,,292,292,292,292,292,292,,292,292,,,,,,292,,294,,,,,,,292,292,294', +'294,294,294,294,153,294,,294,,152,294,294,294,294,,,,153,,153,,153,152', +',152,,152,,,294,,,,294,294,,,294,294,294,294,294,294,153,294,294,214', +'214,152,,214,294,214,,,,,,153,153,294,294,,152,152,153,,,214,,152,,', +',214,214,214,214,214,214,214,214,214,214,,214,214,,,,,214,214,214,214', +'214,296,296,214,,296,,296,,,,214,,,,,214,214,,,,214,,296,,,,,,296,,296', +',296,296,,296,296,296,,296,296,,,,,296,296,54,54,296,151,54,296,54,', +',,,,,296,,,151,,151,296,151,,54,296,,,,,54,,54,,54,54,,54,54,54,,54', +'54,,151,,,54,54,42,42,54,,42,54,42,,,,151,151,,54,,,,151,,54,,,42,54', +',,,,42,,42,,42,42,,42,42,42,,42,42,,,,,42,42,64,64,42,,64,42,64,,,,', +',,42,,,,,,42,,,64,42,,,,,64,,64,,64,64,,64,64,64,,64,64,64,64,,,64,64', +'285,285,64,,285,64,285,285,,,,,,64,,,,,,64,,,285,64,,,,,285,,285,,285', +'285,,285,285,285,,285,285,285,285,,,285,285,66,66,285,,66,285,66,,,', +',,,285,,,,,,285,,,66,285,,,,,66,,66,,66,66,,66,66,66,,66,66,66,66,,', +'66,66,67,67,66,,67,66,67,,,,,,,66,,,,,,66,,,67,66,,,,,67,,67,,67,67', +',67,67,67,,67,67,67,67,,,67,67,68,68,67,,68,67,68,,,,,,,67,,,,,,67,', +',68,67,,,,,68,,68,,68,68,,68,68,68,,68,68,68,68,,,68,68,69,69,68,,69', +'68,69,,,,,,,68,,,,,,68,,,69,68,,,,,69,,69,,69,69,,69,69,69,,69,69,69', +'69,,,69,69,70,70,69,,70,69,70,,,,,,,69,,,,,,69,,,70,69,,,,,70,,70,,70', +'70,,70,70,70,,70,70,70,70,,,70,70,71,71,70,,71,70,71,,,,,,,70,,,,,,70', +',,71,70,,,,,71,,71,,71,71,,71,71,71,,71,71,,,,,71,71,72,72,71,,72,71', +'72,,,,,,,71,,,,,,71,,,72,71,,,,,72,,72,,72,72,,72,72,72,,72,72,,,,,72', +'72,284,284,72,,284,72,284,,,,,,,72,,,,,,72,,,284,72,,,,,284,,284,,284', +'284,,284,284,284,,284,284,284,284,,,284,284,274,274,284,,274,284,274', +'274,,,,,,284,,,,,,284,,,274,284,,,,,274,,274,,274,274,,274,274,274,', +'274,274,,,,,274,274,75,75,274,,75,274,75,,,,,,,274,,,,,,274,,,75,274', +',,,,75,,75,,75,75,,75,75,75,,75,75,,,,,75,75,76,76,75,,76,75,76,,,,', +',,75,,,,,,75,,,76,75,,,,,76,,76,,76,76,,76,76,76,,76,76,,,,,76,76,77', +'77,76,,77,76,77,,,,,,,76,,,,,,76,,,77,76,,,,,77,,77,,77,77,,77,77,77', +',77,77,,,,,77,77,78,78,77,,78,77,78,,,,,,,77,,,,,,77,,,78,77,,,,,78', +',78,,78,78,,78,78,78,,78,78,,,,,78,78,79,79,78,,79,78,79,,,,,,,78,,', +',,,78,,,79,78,,,,,79,,79,,79,79,,79,79,79,,79,79,,,,,79,79,80,80,79', +',80,79,80,,,,,,,79,,,,,,79,,,80,79,,,,,80,,80,,80,80,,80,80,80,,80,80', +',,,,80,80,81,81,80,,81,80,81,,,,,,,80,,,,,,80,,,81,80,,,,,81,,81,,81', +'81,,81,81,81,,81,81,,,,,81,81,82,82,81,,82,81,82,,,,,,,81,,,,,,81,,', +'82,81,,,,,82,,82,,82,82,,82,82,82,,82,82,,,,,82,82,83,83,82,,83,82,83', +',,,,,,82,,,,,,82,,,83,82,,,,,83,,83,,83,83,,83,83,83,,83,83,,,,,83,83', +'84,84,83,,84,83,84,,,,,,,83,,,,,,83,,,84,83,,,,,84,,84,,84,84,,84,84', +'84,,84,84,,,,,84,84,85,85,84,,85,84,85,,,,,,,84,,,,,,84,,,85,84,,,,', +'85,,85,,85,85,,85,85,85,,85,85,,,,,85,85,86,86,85,,86,85,86,,,,,,,85', +',,,,,85,,,86,85,,,,,86,,86,,86,86,,86,86,86,,86,86,,,,,86,86,87,87,86', +',87,86,87,,,,,,,86,,,,,,86,,,87,86,,,,,87,,87,,87,87,,87,87,87,,87,87', +',,,,87,87,88,88,87,,88,87,88,,,,,,,87,,,,,,87,,,88,87,,,,,88,,88,,88', +'88,,88,88,88,,88,88,,,,,88,88,89,89,88,,89,88,89,,,,,,,88,,,,,,88,,', +'89,88,,,,,89,,89,,89,89,,89,89,89,,89,89,,,,,89,89,170,170,89,,170,89', +'170,,,,,,,89,,,,,,89,,,170,89,,,,,170,,170,,170,170,,170,170,170,,170', +'170,,,,,170,170,91,91,170,,91,170,91,,,,,,,170,,,,,,170,,,91,170,,,', +',91,,91,,91,91,,91,91,91,,91,91,,,,,91,91,92,92,91,,92,91,92,,,,,,,91', +',,,,,91,,,92,91,,,,,92,,92,,92,92,,92,92,92,,92,92,,,,,92,92,93,93,92', +',93,92,93,,,,,,,92,,,,,,92,,,93,92,,,,,93,,93,,93,93,,93,93,93,,93,93', +',,,,93,93,,,93,267,267,93,,267,,267,,,,93,,,,,,93,,,93,93,,267,,,,,', +'267,,267,,267,267,,267,267,267,,267,267,,,,,267,267,95,95,267,,95,267', +'95,,,,,,,267,,,,,,267,,,95,267,,,,,95,95,95,95,95,95,95,95,95,95,,95', +'95,,,,,95,95,95,95,95,260,260,95,,260,,260,,,,95,,,,,95,95,,,,95,,260', +',,,,,260,,260,,260,260,,260,260,260,,260,260,,,,,260,260,97,97,260,', +'97,260,97,,,,,,,260,,,,,,260,,,97,260,,,,,97,,97,,97,97,,97,97,97,,97', +'97,,,,,97,97,98,98,97,,98,97,98,,,,,,,97,,,,,,97,,,98,97,,,,,98,,98', +',98,98,,98,98,98,,98,98,,,,,98,98,246,246,98,,246,98,246,,,,,,,98,,', +',,,98,,,246,98,,,,,246,,246,,246,246,,246,246,246,,246,246,,,,,246,246', +'245,245,246,,245,246,245,,,,,,,246,,,,,,246,,,245,246,,,,,245,,245,', +'245,245,,245,245,245,,245,245,,,,,245,245,242,242,245,,242,245,242,', +',,,,,245,,,,,,245,,,242,245,,,,,242,,242,,242,242,,242,242,242,,242', +'242,,,,,242,242,41,41,242,,41,242,41,,,,,,,242,149,,,,,242,,,41,242', +',,,149,41,149,41,149,41,41,,41,41,41,,41,41,,,,,41,41,,,41,103,103,41', +'149,103,,103,,,,41,,,149,149,,41,,149,149,41,,103,103,,149,,,103,,103', +',103,103,149,103,103,103,,103,103,,,,,103,103,236,236,103,,236,103,236', +',,,,,,103,,,,,,103,,,236,103,,,,,236,,236,,236,236,,236,236,236,,236', +'236,,,,,236,236,235,235,236,,235,236,235,,,,,,,236,150,,,,,236,,,235', +'236,,,,150,235,150,235,150,235,235,,235,235,235,,235,235,,,,,235,235', +',,235,107,107,235,150,107,,107,,,,235,,,150,150,,235,,150,150,235,,107', +'107,,150,,,107,,107,,107,107,150,107,107,107,,107,107,,,,,107,107,40', +'40,107,,40,107,40,,,,,,,107,,,,,,107,,,40,107,,,,,40,,40,,40,40,,40', +'40,40,,40,40,,,,,40,40,39,39,40,,39,40,39,,,,,,,40,,,,,,40,,,39,40,', +',,,39,,39,,39,39,,39,39,39,,39,39,,,,,39,39,312,312,39,,312,39,312,', +',,,,,39,,,,,,39,,,312,39,,,,,312,,312,,312,312,,312,312,312,,312,312', +',,,,312,312,324,324,312,,324,312,324,,,,,,,312,,,,,,312,,,324,312,,', +',,324,,324,,324,324,,324,324,324,,324,324,324,324,,,324,324,113,113', +'324,,113,324,113,,,,,,,324,,,,,,324,,,113,324,,,,,113,,113,,113,113', +',113,113,113,,113,113,,,,,113,113,233,233,113,,233,113,233,,,,,,,113', +',,,,,113,,,233,113,,,,,233,,233,,233,233,,233,233,233,,233,233,,,,,233', +'233,228,228,233,,228,233,228,,,,,,,233,154,,,,,233,,,228,233,,,,154', +'228,154,228,154,228,228,,228,228,228,,228,228,,,,,228,228,,,228,224', +'224,228,154,224,224,224,,,,228,154,154,154,154,,228,,154,154,228,,224', +',,154,,,224,,224,,224,224,154,224,224,224,,224,224,,,,,224,224,13,13', +'224,,13,224,13,,,,,,,224,155,,,,,224,,,13,224,,,,155,13,155,13,155,13', +'13,,13,13,13,,13,13,,,,,13,13,,,13,49,49,13,155,49,49,49,,,,13,155,155', +'155,155,,13,,155,155,13,,49,,,155,,,49,,49,,49,49,155,49,49,49,,49,49', +',,,,49,49,213,213,49,,213,49,213,,,,,,,49,,,,,,49,,,213,49,,,,,213,', +'213,,213,213,,213,213,213,,213,213,,,,,213,213,212,212,213,,212,213', +'212,212,,,,,,213,,,,,,213,,,212,213,,,,,212,,212,,212,212,,212,212,212', +',212,212,212,212,,,212,212,12,12,212,,12,212,12,,,,,,,212,,,,,,212,', +',12,212,,,,,12,,12,,12,12,,12,12,12,,12,12,,,,,12,12,211,211,12,,211', +'12,211,211,,,,,,12,,,,,,12,,,211,12,,,,,211,,211,,211,211,,211,211,211', +',211,211,211,211,,,211,211,204,204,211,,204,211,204,204,,,,,,211,,,', +',,211,,,204,211,,,,,204,,204,,204,204,,204,204,204,,204,204,204,204', +',,204,204,11,11,204,,11,204,11,,,,,,,204,,,,,,204,,,11,204,,,,,11,,11', +',11,11,,11,11,11,,11,11,,,,,11,11,175,175,11,,175,11,175,,,,,,,11,,', +',,,11,,,175,11,,,,,175,,175,,175,175,,175,175,175,,175,175,,,,,175,175', +'174,174,175,,174,175,174,,,,,,,175,,,,,,175,,,174,175,,,,,174,,174,', +'174,174,,174,174,174,,174,174,,,,,174,174,173,173,174,,173,174,173,', +',,,,,174,,,,,,174,,,173,174,,,,,173,,173,,173,173,,173,173,173,,173', +'173,,,,,173,173,341,341,173,,341,173,341,341,,,,,,173,,,,,,173,,,341', +'173,,,,,341,,341,,341,341,,341,341,341,156,341,341,341,341,,,341,341', +',,341,,156,341,156,,156,,,,,341,,,157,,,341,,,,341,,,,,,157,156,157', +',157,,,,,156,156,156,156,156,156,,156,156,,,,,,156,,,157,,,,,,156,,157', +'157,157,157,157,157,,157,157,158,,,,,157,,,,,,,158,158,157,158,,158', +',,158,,,,,159,,,,,,,,,,,,159,159,158,159,,159,,,159,,158,158,158,158', +'158,158,,158,158,,,,,,158,,,159,,,,,,158,,159,159,159,159,159,159,,159', +'159,160,,,,,159,,,,,,,160,160,159,160,,160,,,160,,,,,161,,,,,,,,,,,', +'161,161,160,161,,161,,,161,,160,160,160,160,160,160,,160,160,,,,,,160', +',,161,,,,,,160,,161,161,161,161,161,161,,161,161,162,,,,,161,,,,,,162', +'162,162,161,162,,162,,,162,162,162,162,,,,,,,,,,,,,,,,162,,,,,,163,', +'162,162,162,162,162,162,,162,162,163,163,163,,163,162,163,,,163,163', +'163,163,,162,,,,,,,,,,,343,343,,163,343,,343,343,163,,,163,163,163,163', +'163,163,,163,163,,,343,,,163,,,343,,343,,343,343,163,343,343,343,,343', +'343,343,343,,,343,343,4,4,343,,4,343,4,,,,,,,343,,,,,,343,,,4,343,,', +',,4,,4,,4,4,,4,4,4,4,4,4,4,4,,,4,4,344,344,4,,344,4,344,344,,,,,,4,', +',,,,4,,,344,4,,,,,344,,344,,344,344,,344,344,344,,344,344,344,344,,', +'344,344,0,0,344,,0,344,0,,,,,,,344,,,,,,344,,,0,344,,,,,0,,0,,0,0,,0', +'0,0,,0,0,0,0,,,0,0,90,90,0,,90,0,90,,,,,,,0,,,,,,0,,,90,0,,,,,90,,90', +',90,90,,90,90,90,,90,90,,,,,90,90,,,90,,,90,,,,244,244,244,244,90,244', +'244,244,244,244,90,244,244,,90,,,,,244,244,244,239,239,239,239,,239', +'239,239,239,239,,239,239,,,244,244,,,239,239,239,195,195,195,195,,195', +'195,195,195,195,,195,195,,,239,239,,,195,195,195,,,,,,,,,,,,,,,,195', +'195' ] + racc_action_check = arr = ::Array.new(5065, nil) idx = 0 clist.each do |str| str.split(',', -1).each do |i| @@ -361,383 +370,393 @@ clist = [ end racc_action_pointer = [ - 4833, 274, nil, nil, 4741, 261, nil, 123, nil, nil, - 299, 4603, 4557, 4511, nil, nil, nil, nil, nil, nil, + 4877, 270, nil, nil, 4783, 256, nil, 150, nil, nil, + 309, 4186, 4045, 3854, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, 244, 179, 221, 4373, 4327, - 4281, 4235, 78, 196, 122, 85, nil, 4051, 87, nil, - nil, nil, 4002, nil, nil, nil, nil, nil, nil, nil, - 223, 3910, 208, 3588, 3355, 3309, 3263, 3217, 2987, 2570, - 30, 1, 1742, 1788, 1834, 1880, 1926, 1972, 2018, 2064, - 2110, 2156, 2202, 2248, 2294, 2340, 2386, 4879, 2478, 2524, - 1601, 163, 2662, 169, 2757, 2803, 178, 113, 135, 238, - 3033, nil, 230, -32, 3171, 360, nil, 421, 482, 543, - 3401, 94, nil, nil, nil, 106, -6, nil, nil, nil, - nil, nil, 604, 90, nil, 111, nil, nil, 200, 726, - nil, 101, nil, 207, nil, nil, nil, nil, nil, 22, - 130, nil, nil, nil, nil, 1239, 1177, 1787, 1741, 1535, - 1219, 1263, 1290, 1314, 1358, 1382, 1426, 1450, 1507, 1564, - 177, 116, 55, -6, nil, nil, 2432, nil, 1092, 4143, - 4097, 3956, 232, 234, nil, nil, 5, nil, -10, 13, - 127, 120, 77, 24, 14, nil, nil, nil, nil, nil, - nil, 4927, 787, 195, nil, 213, nil, 215, 156, nil, - 3864, nil, 205, nil, 197, 27, nil, 3818, 3726, 3680, - 1647, 168, 127, nil, 3, 142, 139, 16, nil, 24, - 3542, nil, nil, 5, 3493, nil, nil, nil, nil, 3447, - nil, 3125, 3079, 33, nil, 4906, nil, 116, 2941, 131, - 4948, 2895, 2849, 155, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, 2711, 153, nil, 171, - nil, 113, 157, 2616, nil, 198, 26, 200, 179, 80, - 1696, nil, 173, 202, 206, 208, nil, 59, nil, 206, - 3634, 3772, nil, nil, 848, nil, nil, nil, 909, nil, - 970, 219, 4189, 19, nil, nil, nil, nil, 1031, 1153, - 232, 174, nil, nil, nil, 665, 62, nil, 4419, 240, - 218, nil, 244, 250, nil, nil, nil, 251, 253, nil, - 4465, nil, nil, nil, 244, 261, nil, 262, nil, nil, - nil, nil, nil, nil, nil, nil, 4649, nil, 4695, 4787, - nil, nil, 266, nil, nil, nil, nil, 267, nil, 268, - nil, 269, nil, nil, nil, nil, nil ] + nil, nil, nil, nil, nil, nil, 230, 176, 211, 3522, + 3475, 3234, 1395, -1, 186, nil, 118, 117, nil, 3904, + 152, nil, nil, nil, 1348, nil, nil, nil, nil, nil, + nil, nil, nil, 221, 1442, 208, 1536, 1583, 1630, 1677, + 1724, 1771, 1818, 38, 29, 1959, 2006, 2053, 2100, 2147, + 2194, 2241, 2288, 2335, 2382, 2429, 2476, 2523, 2570, 2617, + 4924, 2711, 2758, 2805, 143, 2902, 156, 2999, 3046, 139, + 55, 82, 876, 3284, nil, 219, -32, 3428, 813, nil, + 750, 624, 561, 3663, 22, nil, nil, nil, nil, 68, + 86, nil, nil, nil, nil, nil, 498, 97, nil, 24, + nil, nil, 183, 435, nil, 146, nil, 207, nil, nil, + nil, nil, nil, 46, 76, nil, nil, nil, nil, 3244, + 3388, 1347, 1210, 1205, 3767, 3864, 4408, 4433, 4489, 4514, + 4570, 4595, 4651, 4696, 183, 120, 57, -6, nil, nil, + 2664, nil, 246, 4327, 4280, 4233, 218, 230, nil, nil, + -7, nil, -10, 7, 128, 105, 54, 13, 8, nil, + nil, nil, nil, nil, nil, 4996, 372, 184, nil, 204, + nil, 212, 154, nil, 4139, nil, 205, nil, 180, -9, + nil, 4092, 3998, 3951, 1251, 148, 106, nil, 1, 123, + 115, 25, nil, 27, 3807, nil, nil, 183, 3757, nil, + nil, nil, nil, 3710, nil, 3378, 3331, 13, nil, 4974, + nil, 53, 3187, 99, 4952, 3140, 3093, 140, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + 2952, 141, nil, 159, nil, 102, 138, 2855, nil, 174, + 48, 181, 162, -6, 1912, nil, 163, 193, 198, 200, + nil, 90, nil, 206, 1865, 1489, nil, nil, 939, nil, + nil, nil, 1128, nil, 1191, 216, 1301, -4, nil, nil, + nil, nil, 1065, 1002, 222, 165, nil, nil, nil, 687, + 97, nil, 3569, 238, 216, nil, 240, 241, nil, nil, + nil, 242, 244, nil, 3616, nil, nil, nil, 230, 247, + nil, nil, 248, nil, nil, nil, nil, nil, nil, nil, + nil, 4374, nil, 4736, 4830, nil, nil, 262, nil, nil, + nil, 267, nil, 268, nil, 269, nil, nil, nil, nil, + nil ] racc_action_default = [ - -204, -205, -1, -2, -3, -4, -7, -9, -10, -15, - -104, -205, -205, -205, -44, -45, -46, -47, -48, -49, + -208, -209, -1, -2, -3, -4, -7, -9, -10, -15, + -105, -209, -209, -209, -44, -45, -46, -47, -48, -49, -50, -51, -52, -53, -54, -55, -56, -57, -58, -59, - -60, -61, -62, -63, -64, -69, -70, -74, -205, -205, - -205, -205, -205, -114, -205, -205, -159, -205, -205, -169, - -170, -171, -205, -173, -180, -181, -182, -183, -184, -185, - -205, -205, -6, -205, -205, -205, -205, -205, -205, -205, - -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, - -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, - -205, -205, -122, -117, -204, -204, -27, -205, -34, -205, - -205, -71, -205, -205, -205, -205, -81, -205, -205, -205, - -205, -204, -148, -149, -115, -204, -204, -140, -142, -143, - -144, -145, -42, -205, -162, -205, -165, -166, -205, -177, - -172, -205, 357, -5, -8, -11, -12, -13, -14, -205, - -17, -18, -157, -158, -19, -20, -21, -22, -23, -24, - -25, -26, -28, -29, -30, -31, -32, -33, -35, -36, - -37, -38, -39, -205, -40, -99, -205, -75, -205, -197, - -203, -191, -188, -186, -112, -123, -180, -126, -184, -205, - -194, -192, -200, -182, -183, -190, -195, -196, -198, -199, - -201, -122, -121, -205, -120, -205, -41, -186, -66, -76, - -205, -79, -186, -153, -156, -205, -73, -205, -205, -205, - -122, -188, -204, -150, -205, -205, -205, -205, -146, -205, - -205, -160, -163, -205, -205, -174, -175, -176, -178, -205, - -16, -205, -205, -186, -101, -122, -111, -205, -189, -205, - -187, -205, -205, -186, -125, -127, -191, -192, -193, -194, - -197, -200, -202, -203, -118, -119, -187, -205, -68, -205, - -78, -205, -187, -205, -72, -205, -84, -205, -90, -205, - -205, -94, -188, -186, -205, -205, -134, -205, -151, -186, - -205, -205, -141, -147, -43, -161, -164, -167, -168, -179, - -103, -205, -187, -186, -107, -113, -108, -124, -128, -129, - -205, -65, -77, -80, -154, -155, -84, -83, -205, -205, - -90, -89, -205, -205, -98, -93, -95, -205, -205, -109, - -205, -135, -136, -137, -205, -205, -131, -205, -139, -100, - -102, -110, -116, -67, -82, -85, -205, -88, -205, -205, - -105, -106, -205, -133, -152, -130, -138, -205, -87, -205, - -92, -205, -97, -132, -86, -91, -96 ] + -60, -61, -62, -63, -64, -65, -70, -71, -75, -209, + -209, -209, -209, -209, -115, -117, -209, -209, -162, -209, + -209, -172, -173, -174, -209, -176, -183, -184, -185, -186, + -187, -188, -189, -209, -209, -6, -209, -209, -209, -209, + -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, + -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, + -209, -209, -209, -209, -209, -124, -119, -208, -208, -27, + -209, -34, -209, -209, -72, -209, -209, -209, -209, -82, + -209, -209, -209, -209, -208, -134, -151, -152, -116, -208, + -208, -143, -145, -146, -147, -148, -42, -209, -165, -209, + -168, -169, -209, -180, -175, -209, 361, -5, -8, -11, + -12, -13, -14, -209, -17, -18, -160, -161, -19, -20, + -21, -22, -23, -24, -25, -26, -28, -29, -30, -31, + -32, -33, -35, -36, -37, -38, -39, -209, -40, -100, + -209, -76, -209, -201, -207, -195, -192, -190, -113, -125, + -184, -128, -188, -209, -198, -196, -204, -186, -187, -194, + -199, -200, -202, -203, -205, -124, -123, -209, -122, -209, + -41, -190, -67, -77, -209, -80, -190, -156, -159, -209, + -74, -209, -209, -209, -124, -192, -208, -153, -209, -209, + -209, -209, -149, -209, -209, -163, -166, -209, -209, -177, + -178, -179, -181, -209, -16, -209, -209, -190, -102, -124, + -112, -209, -193, -209, -191, -209, -209, -190, -127, -129, + -195, -196, -197, -198, -201, -204, -206, -207, -120, -121, + -191, -209, -69, -209, -79, -209, -191, -209, -73, -209, + -85, -209, -91, -209, -209, -95, -192, -190, -209, -209, + -137, -209, -154, -190, -208, -209, -144, -150, -43, -164, + -167, -170, -171, -182, -104, -209, -191, -190, -108, -114, + -109, -126, -130, -131, -209, -66, -78, -81, -157, -158, + -85, -84, -209, -209, -91, -90, -209, -209, -99, -94, + -96, -209, -209, -110, -208, -138, -139, -140, -209, -209, + -135, -136, -209, -142, -101, -103, -111, -118, -68, -83, + -86, -209, -89, -209, -209, -106, -107, -209, -155, -132, + -141, -209, -88, -209, -93, -209, -98, -133, -87, -92, + -97 ] racc_goto_table = [ - 2, 3, 101, 96, 98, 99, 115, 165, 130, 127, - 173, 172, 128, 202, 119, 307, 313, 121, 237, 212, - 311, 233, 282, 215, 283, 239, 295, 271, 235, 211, - 105, 107, 108, 109, 143, 143, 62, 123, 270, 122, - 141, 144, 142, 142, 129, 135, 136, 137, 138, 257, - 193, 195, 297, 335, 261, 334, 199, 274, 139, 275, - 122, 140, 337, 321, 145, 146, 147, 148, 149, 150, - 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, - 161, 162, 163, 234, 168, 291, 192, 192, 316, 216, - 197, 164, 122, 133, 205, 300, 122, 304, 125, 134, - 1, 228, 168, 229, 227, nil, nil, nil, nil, 243, - nil, nil, 213, nil, nil, nil, 213, 218, 317, nil, - nil, nil, nil, nil, 279, 318, nil, nil, 273, 272, - nil, 324, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, 115, nil, nil, 331, nil, nil, nil, nil, - nil, 119, nil, 293, 121, nil, nil, nil, 163, nil, - nil, 105, 107, 108, 258, nil, nil, nil, nil, nil, + 2, 119, 3, 99, 101, 102, 104, 134, 131, 177, + 176, 169, 132, 206, 241, 315, 125, 123, 311, 329, + 216, 139, 140, 141, 142, 219, 243, 286, 215, 287, + 275, 108, 110, 111, 112, 147, 147, 65, 197, 199, + 127, 126, 146, 146, 145, 148, 133, 1, 299, 239, + 261, 237, 301, 278, 317, 265, 274, 342, 339, 347, + 340, 279, 143, 126, 144, 325, 220, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, + 162, 163, 164, 165, 166, 167, 295, 172, 238, 196, + 196, 320, 203, 308, 201, 126, 304, 137, 209, 126, + 129, 168, 138, 232, 233, 172, 231, nil, nil, 247, + nil, nil, nil, nil, 321, nil, 217, nil, nil, nil, + nil, 217, 222, nil, nil, 283, 322, nil, 277, 276, + nil, nil, 328, nil, nil, nil, nil, nil, nil, 119, + nil, nil, nil, nil, nil, nil, 336, nil, nil, nil, + nil, nil, nil, 297, nil, 125, 123, nil, nil, nil, + nil, nil, 167, nil, nil, 108, 110, 111, nil, nil, + nil, 262, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, 291, 293, nil, nil, 132, + 125, 123, 125, 123, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, 263, 126, 172, nil, nil, nil, + nil, 269, 271, nil, 335, nil, 288, nil, 280, nil, + 292, nil, nil, nil, nil, 133, nil, 288, 294, nil, + nil, nil, nil, nil, 172, nil, 326, 302, 303, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, 287, 289, 119, 128, 119, 121, - nil, 121, nil, nil, nil, nil, nil, nil, nil, nil, - 259, 122, 168, nil, nil, nil, nil, 265, 267, 330, - nil, nil, 284, 276, nil, nil, 288, nil, nil, nil, - nil, 129, nil, 284, 290, nil, nil, nil, nil, nil, - 168, nil, nil, 298, 299, nil, nil, nil, nil, 322, - nil, nil, nil, nil, nil, nil, nil, nil, 284, nil, - nil, nil, nil, nil, nil, 305, nil, nil, nil, nil, - nil, nil, 122, nil, nil, nil, nil, 333, nil, nil, + nil, nil, 288, nil, nil, nil, nil, nil, nil, 309, + nil, nil, nil, nil, nil, nil, 126, nil, nil, nil, + nil, nil, nil, nil, 338, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, 332, 331, nil, 167, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - 325, 327, nil, nil, 163, nil, nil, nil, nil, nil, + nil, nil, nil, nil, 108, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - 105, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, 331, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - 342, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, 347, nil, 349, 351 ] + nil, 351, nil, 353, 355 ] racc_goto_check = [ - 2, 3, 37, 9, 9, 9, 62, 49, 75, 71, - 54, 52, 31, 42, 35, 44, 48, 30, 53, 63, - 45, 50, 68, 63, 68, 36, 55, 47, 56, 52, - 9, 9, 9, 9, 31, 31, 5, 11, 46, 9, - 12, 12, 30, 30, 9, 7, 7, 7, 7, 36, - 58, 58, 59, 43, 36, 44, 41, 53, 11, 64, - 9, 9, 45, 65, 9, 9, 9, 9, 9, 9, + 2, 63, 3, 9, 9, 9, 38, 78, 74, 55, + 53, 50, 31, 43, 54, 46, 30, 36, 45, 65, + 64, 7, 7, 7, 7, 64, 37, 71, 53, 71, + 48, 9, 9, 9, 9, 31, 31, 5, 59, 59, + 11, 9, 30, 30, 12, 12, 9, 1, 56, 57, + 37, 51, 60, 54, 49, 37, 47, 46, 45, 65, + 44, 67, 11, 9, 9, 68, 70, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 49, 9, 36, 9, 9, 47, 67, - 11, 13, 9, 5, 11, 36, 9, 69, 70, 6, - 1, 76, 9, 77, 79, nil, nil, nil, nil, 54, - nil, nil, 3, nil, nil, nil, 3, 3, 53, nil, - nil, nil, nil, nil, 42, 36, nil, nil, 54, 52, - nil, 36, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, 62, nil, nil, 36, nil, nil, nil, nil, - nil, 35, nil, 54, 30, nil, nil, nil, 9, nil, - nil, 9, 9, 9, 37, nil, nil, nil, nil, nil, + 9, 9, 9, 9, 9, 9, 37, 9, 50, 9, + 9, 48, 42, 72, 11, 9, 37, 5, 11, 9, + 73, 13, 6, 79, 80, 9, 82, nil, nil, 55, + nil, nil, nil, nil, 54, nil, 3, nil, nil, nil, + nil, 3, 3, nil, nil, 43, 37, nil, 55, 53, + nil, nil, 37, nil, nil, nil, nil, nil, nil, 63, + nil, nil, nil, nil, nil, nil, 37, nil, nil, nil, + nil, nil, nil, 55, nil, 30, 36, nil, nil, nil, + nil, nil, 9, nil, nil, 9, 9, 9, nil, nil, + nil, 38, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, 74, 78, nil, nil, 31, + 30, 36, 30, 36, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, 2, 9, 9, nil, nil, nil, + nil, 2, 2, nil, 50, nil, 9, nil, 3, nil, + 9, nil, nil, nil, nil, 9, nil, 9, 9, nil, + nil, nil, nil, nil, 9, nil, 63, 9, 9, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, 71, 75, 35, 31, 35, 30, - nil, 30, nil, nil, nil, nil, nil, nil, nil, nil, - 2, 9, 9, nil, nil, nil, nil, 2, 2, 49, - nil, nil, 9, 3, nil, nil, 9, nil, nil, nil, - nil, 9, nil, 9, 9, nil, nil, nil, nil, nil, - 9, nil, nil, 9, 9, nil, nil, nil, nil, 62, - nil, nil, nil, nil, nil, nil, nil, nil, 9, nil, - nil, nil, nil, nil, nil, 9, nil, nil, nil, nil, - nil, nil, 9, nil, nil, nil, nil, 37, nil, nil, + nil, nil, 9, nil, nil, nil, nil, nil, nil, 9, + nil, nil, nil, nil, nil, nil, 9, nil, nil, nil, + nil, nil, nil, nil, 38, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, 2, 3, nil, 9, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - 2, 2, nil, nil, 9, nil, nil, nil, nil, nil, + nil, nil, nil, nil, 9, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - 9, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, 3, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - 2, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, 2, nil, 2, 2 ] + nil, 2, nil, 2, 2 ] racc_goto_pointer = [ - nil, 100, 0, 1, nil, 32, 36, -19, nil, -8, - nil, -10, -30, 1, nil, nil, nil, nil, nil, nil, + nil, 47, 0, 2, nil, 33, 36, -46, nil, -8, + nil, -9, -29, 8, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - -28, -36, nil, nil, nil, -31, -148, -34, nil, nil, - nil, -46, -90, -255, -251, -248, -171, -182, -253, -83, - -145, nil, -81, -154, -82, -212, -140, nil, -44, -188, - nil, nil, -38, -92, -153, -214, nil, -27, -195, -165, - 50, -39, nil, nil, nil, -44, -30, -28, nil, -27 ] + -31, -38, nil, nil, nil, nil, -30, -151, -31, nil, + nil, nil, -13, -93, -252, -252, -257, -157, -183, -219, + -82, -119, nil, -85, -162, -86, -194, -123, nil, -59, + -192, nil, nil, -45, -94, -265, nil, -155, -216, nil, + -54, -194, -173, 50, -42, nil, nil, nil, -47, -32, + -31, nil, -29 ] racc_goto_default = [ - nil, nil, nil, 194, 4, 5, 6, 7, 8, 10, - 9, 269, nil, nil, 14, 35, 15, 16, 17, 18, + nil, nil, 330, 198, 4, 5, 6, 7, 8, 10, + 9, 273, nil, nil, 14, 36, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, - 29, 30, 31, 32, 33, 34, nil, nil, 36, 37, - 102, nil, nil, 106, nil, nil, nil, nil, nil, nil, - nil, 41, nil, nil, nil, 174, nil, 93, nil, 175, - 179, 177, 111, nil, nil, nil, 116, nil, 117, 203, - nil, nil, 49, 50, 52, nil, nil, nil, 131, nil ] + 29, 30, 31, 32, 33, 34, 35, nil, nil, 37, + 38, 105, nil, nil, 109, nil, nil, nil, nil, nil, + nil, nil, 42, nil, nil, nil, 178, nil, 96, nil, + 179, 183, 181, 115, nil, nil, 114, nil, nil, 120, + nil, 121, 207, nil, nil, 51, 52, 54, nil, nil, + nil, 135, nil ] racc_reduce_table = [ 0, 0, :racc_error, - 1, 78, :_reduce_1, - 1, 78, :_reduce_none, - 1, 79, :_reduce_3, - 1, 81, :_reduce_4, - 3, 81, :_reduce_5, - 2, 81, :_reduce_6, - 1, 82, :_reduce_7, - 3, 82, :_reduce_8, - 1, 83, :_reduce_none, - 1, 84, :_reduce_10, - 3, 84, :_reduce_11, - 3, 84, :_reduce_12, - 3, 84, :_reduce_13, - 3, 84, :_reduce_14, + 1, 81, :_reduce_1, + 1, 81, :_reduce_none, + 1, 82, :_reduce_3, + 1, 84, :_reduce_4, + 3, 84, :_reduce_5, + 2, 84, :_reduce_6, + 1, 85, :_reduce_7, + 3, 85, :_reduce_8, 1, 86, :_reduce_none, - 4, 86, :_reduce_16, - 3, 86, :_reduce_17, - 3, 86, :_reduce_18, - 3, 86, :_reduce_19, - 3, 86, :_reduce_20, - 3, 86, :_reduce_21, - 3, 86, :_reduce_22, - 3, 86, :_reduce_23, - 3, 86, :_reduce_24, - 3, 86, :_reduce_25, - 3, 86, :_reduce_26, - 2, 86, :_reduce_27, - 3, 86, :_reduce_28, - 3, 86, :_reduce_29, - 3, 86, :_reduce_30, - 3, 86, :_reduce_31, - 3, 86, :_reduce_32, - 3, 86, :_reduce_33, - 2, 86, :_reduce_34, - 3, 86, :_reduce_35, - 3, 86, :_reduce_36, - 3, 86, :_reduce_37, - 3, 86, :_reduce_38, - 3, 86, :_reduce_39, - 3, 86, :_reduce_40, - 3, 86, :_reduce_41, - 1, 88, :_reduce_42, - 3, 88, :_reduce_43, - 1, 87, :_reduce_none, - 1, 92, :_reduce_none, - 1, 92, :_reduce_none, - 1, 92, :_reduce_none, - 1, 92, :_reduce_none, - 1, 92, :_reduce_none, - 1, 92, :_reduce_none, - 1, 92, :_reduce_none, - 1, 92, :_reduce_none, - 1, 92, :_reduce_none, - 1, 92, :_reduce_none, - 1, 93, :_reduce_none, - 1, 93, :_reduce_none, - 1, 93, :_reduce_none, - 1, 93, :_reduce_none, - 1, 93, :_reduce_none, - 1, 93, :_reduce_none, - 1, 93, :_reduce_none, - 1, 93, :_reduce_none, - 1, 108, :_reduce_63, - 1, 108, :_reduce_64, - 5, 91, :_reduce_65, - 3, 91, :_reduce_66, - 6, 91, :_reduce_67, - 4, 91, :_reduce_68, - 1, 91, :_reduce_69, - 1, 95, :_reduce_70, - 2, 95, :_reduce_71, - 4, 115, :_reduce_72, - 3, 115, :_reduce_73, - 1, 115, :_reduce_74, - 3, 116, :_reduce_75, - 2, 114, :_reduce_76, - 3, 118, :_reduce_77, - 2, 118, :_reduce_78, - 2, 117, :_reduce_79, - 4, 117, :_reduce_80, - 2, 98, :_reduce_81, - 5, 120, :_reduce_82, - 4, 120, :_reduce_83, - 0, 121, :_reduce_none, - 2, 121, :_reduce_85, - 4, 121, :_reduce_86, - 3, 121, :_reduce_87, - 6, 99, :_reduce_88, - 5, 99, :_reduce_89, - 0, 122, :_reduce_none, - 4, 122, :_reduce_91, - 3, 122, :_reduce_92, - 5, 97, :_reduce_93, - 1, 123, :_reduce_94, - 2, 123, :_reduce_95, - 5, 124, :_reduce_96, - 4, 124, :_reduce_97, - 1, 125, :_reduce_98, + 1, 87, :_reduce_10, + 3, 87, :_reduce_11, + 3, 87, :_reduce_12, + 3, 87, :_reduce_13, + 3, 87, :_reduce_14, + 1, 89, :_reduce_none, + 4, 89, :_reduce_16, + 3, 89, :_reduce_17, + 3, 89, :_reduce_18, + 3, 89, :_reduce_19, + 3, 89, :_reduce_20, + 3, 89, :_reduce_21, + 3, 89, :_reduce_22, + 3, 89, :_reduce_23, + 3, 89, :_reduce_24, + 3, 89, :_reduce_25, + 3, 89, :_reduce_26, + 2, 89, :_reduce_27, + 3, 89, :_reduce_28, + 3, 89, :_reduce_29, + 3, 89, :_reduce_30, + 3, 89, :_reduce_31, + 3, 89, :_reduce_32, + 3, 89, :_reduce_33, + 2, 89, :_reduce_34, + 3, 89, :_reduce_35, + 3, 89, :_reduce_36, + 3, 89, :_reduce_37, + 3, 89, :_reduce_38, + 3, 89, :_reduce_39, + 3, 89, :_reduce_40, + 3, 89, :_reduce_41, + 1, 91, :_reduce_42, + 3, 91, :_reduce_43, 1, 90, :_reduce_none, - 4, 90, :_reduce_100, - 1, 127, :_reduce_101, - 3, 127, :_reduce_102, - 3, 126, :_reduce_103, - 1, 85, :_reduce_104, - 6, 85, :_reduce_105, - 6, 85, :_reduce_106, - 5, 85, :_reduce_107, - 5, 85, :_reduce_108, - 5, 85, :_reduce_109, - 4, 132, :_reduce_110, - 1, 133, :_reduce_111, - 1, 129, :_reduce_112, - 3, 129, :_reduce_113, - 1, 128, :_reduce_114, - 2, 128, :_reduce_115, - 6, 96, :_reduce_116, - 2, 96, :_reduce_117, - 3, 134, :_reduce_118, - 3, 134, :_reduce_119, - 1, 135, :_reduce_none, - 1, 135, :_reduce_none, - 0, 131, :_reduce_122, - 1, 131, :_reduce_123, - 3, 131, :_reduce_124, - 1, 137, :_reduce_none, - 1, 137, :_reduce_none, - 1, 137, :_reduce_none, - 3, 136, :_reduce_128, - 3, 136, :_reduce_129, - 6, 100, :_reduce_130, - 5, 100, :_reduce_131, - 7, 101, :_reduce_132, - 6, 101, :_reduce_133, + 1, 95, :_reduce_none, + 1, 95, :_reduce_none, + 1, 95, :_reduce_none, + 1, 95, :_reduce_none, + 1, 95, :_reduce_none, + 1, 95, :_reduce_none, + 1, 95, :_reduce_none, + 1, 95, :_reduce_none, + 1, 95, :_reduce_none, + 1, 95, :_reduce_none, + 1, 96, :_reduce_none, + 1, 96, :_reduce_none, + 1, 96, :_reduce_none, + 1, 96, :_reduce_none, + 1, 96, :_reduce_none, + 1, 96, :_reduce_none, + 1, 96, :_reduce_none, + 1, 96, :_reduce_none, + 1, 96, :_reduce_none, + 1, 111, :_reduce_64, + 1, 111, :_reduce_65, + 5, 94, :_reduce_66, + 3, 94, :_reduce_67, + 6, 94, :_reduce_68, + 4, 94, :_reduce_69, + 1, 94, :_reduce_70, + 1, 98, :_reduce_71, + 2, 98, :_reduce_72, + 4, 119, :_reduce_73, + 3, 119, :_reduce_74, + 1, 119, :_reduce_75, + 3, 120, :_reduce_76, + 2, 118, :_reduce_77, + 3, 122, :_reduce_78, + 2, 122, :_reduce_79, + 2, 121, :_reduce_80, + 4, 121, :_reduce_81, + 2, 101, :_reduce_82, + 5, 124, :_reduce_83, + 4, 124, :_reduce_84, + 0, 125, :_reduce_none, + 2, 125, :_reduce_86, + 4, 125, :_reduce_87, + 3, 125, :_reduce_88, + 6, 102, :_reduce_89, + 5, 102, :_reduce_90, + 0, 126, :_reduce_none, + 4, 126, :_reduce_92, + 3, 126, :_reduce_93, + 5, 100, :_reduce_94, + 1, 127, :_reduce_95, + 2, 127, :_reduce_96, + 5, 128, :_reduce_97, + 4, 128, :_reduce_98, + 1, 129, :_reduce_99, + 1, 93, :_reduce_none, + 4, 93, :_reduce_101, + 1, 131, :_reduce_102, + 3, 131, :_reduce_103, + 3, 130, :_reduce_104, + 1, 88, :_reduce_105, + 6, 88, :_reduce_106, + 6, 88, :_reduce_107, + 5, 88, :_reduce_108, + 5, 88, :_reduce_109, + 5, 88, :_reduce_110, + 4, 136, :_reduce_111, + 1, 137, :_reduce_112, + 1, 133, :_reduce_113, + 3, 133, :_reduce_114, + 1, 132, :_reduce_115, + 2, 132, :_reduce_116, + 1, 132, :_reduce_117, + 6, 99, :_reduce_118, + 2, 99, :_reduce_119, + 3, 138, :_reduce_120, + 3, 138, :_reduce_121, + 1, 139, :_reduce_none, + 1, 139, :_reduce_none, + 0, 135, :_reduce_124, + 1, 135, :_reduce_125, + 3, 135, :_reduce_126, 1, 141, :_reduce_none, - 2, 141, :_reduce_135, - 1, 142, :_reduce_none, - 1, 142, :_reduce_none, - 6, 102, :_reduce_138, - 5, 102, :_reduce_139, - 1, 143, :_reduce_140, - 3, 143, :_reduce_141, - 1, 145, :_reduce_142, - 1, 145, :_reduce_143, - 1, 145, :_reduce_144, + 1, 141, :_reduce_none, + 1, 141, :_reduce_none, + 3, 140, :_reduce_130, + 3, 140, :_reduce_131, + 6, 103, :_reduce_132, + 7, 104, :_reduce_133, + 1, 146, :_reduce_134, 1, 145, :_reduce_none, - 1, 144, :_reduce_none, - 2, 144, :_reduce_147, - 1, 139, :_reduce_148, - 1, 139, :_reduce_149, - 1, 140, :_reduce_150, - 2, 140, :_reduce_151, - 4, 140, :_reduce_152, - 1, 119, :_reduce_153, - 3, 119, :_reduce_154, - 3, 146, :_reduce_155, - 1, 146, :_reduce_156, - 1, 89, :_reduce_none, - 1, 89, :_reduce_none, - 1, 94, :_reduce_159, - 3, 103, :_reduce_160, - 4, 103, :_reduce_161, - 2, 103, :_reduce_162, + 1, 145, :_reduce_none, + 1, 147, :_reduce_none, + 2, 147, :_reduce_138, + 1, 148, :_reduce_none, + 1, 148, :_reduce_none, + 6, 105, :_reduce_141, + 5, 105, :_reduce_142, + 1, 149, :_reduce_143, + 3, 149, :_reduce_144, + 1, 151, :_reduce_145, + 1, 151, :_reduce_146, + 1, 151, :_reduce_147, + 1, 151, :_reduce_none, + 1, 150, :_reduce_none, + 2, 150, :_reduce_150, + 1, 143, :_reduce_151, + 1, 143, :_reduce_152, + 1, 144, :_reduce_153, + 2, 144, :_reduce_154, + 4, 144, :_reduce_155, + 1, 123, :_reduce_156, + 3, 123, :_reduce_157, + 3, 152, :_reduce_158, + 1, 152, :_reduce_159, + 1, 92, :_reduce_none, + 1, 92, :_reduce_none, + 1, 97, :_reduce_162, 3, 106, :_reduce_163, 4, 106, :_reduce_164, 2, 106, :_reduce_165, - 1, 147, :_reduce_166, - 3, 147, :_reduce_167, - 3, 148, :_reduce_168, - 1, 112, :_reduce_none, - 1, 112, :_reduce_none, - 1, 149, :_reduce_171, - 2, 150, :_reduce_172, - 1, 151, :_reduce_173, - 1, 153, :_reduce_174, - 1, 154, :_reduce_175, - 2, 152, :_reduce_176, - 1, 155, :_reduce_177, - 1, 156, :_reduce_178, - 2, 156, :_reduce_179, - 1, 111, :_reduce_180, - 1, 109, :_reduce_181, - 1, 110, :_reduce_182, - 1, 105, :_reduce_183, - 1, 104, :_reduce_184, - 1, 107, :_reduce_185, - 0, 113, :_reduce_none, - 1, 113, :_reduce_187, - 0, 130, :_reduce_none, - 1, 130, :_reduce_none, - 1, 138, :_reduce_none, - 1, 138, :_reduce_none, - 1, 138, :_reduce_none, - 1, 138, :_reduce_none, - 1, 138, :_reduce_none, - 1, 138, :_reduce_none, - 1, 138, :_reduce_none, - 1, 138, :_reduce_none, - 1, 138, :_reduce_none, - 1, 138, :_reduce_none, - 1, 138, :_reduce_none, - 1, 138, :_reduce_none, - 1, 138, :_reduce_none, - 1, 138, :_reduce_none, - 0, 80, :_reduce_204 ] + 3, 109, :_reduce_166, + 4, 109, :_reduce_167, + 2, 109, :_reduce_168, + 1, 153, :_reduce_169, + 3, 153, :_reduce_170, + 3, 154, :_reduce_171, + 1, 116, :_reduce_none, + 1, 116, :_reduce_none, + 1, 155, :_reduce_174, + 2, 156, :_reduce_175, + 1, 157, :_reduce_176, + 1, 159, :_reduce_177, + 1, 160, :_reduce_178, + 2, 158, :_reduce_179, + 1, 161, :_reduce_180, + 1, 162, :_reduce_181, + 2, 162, :_reduce_182, + 1, 112, :_reduce_183, + 1, 115, :_reduce_184, + 1, 113, :_reduce_185, + 1, 114, :_reduce_186, + 1, 108, :_reduce_187, + 1, 107, :_reduce_188, + 1, 110, :_reduce_189, + 0, 117, :_reduce_none, + 1, 117, :_reduce_191, + 0, 134, :_reduce_none, + 1, 134, :_reduce_none, + 1, 142, :_reduce_none, + 1, 142, :_reduce_none, + 1, 142, :_reduce_none, + 1, 142, :_reduce_none, + 1, 142, :_reduce_none, + 1, 142, :_reduce_none, + 1, 142, :_reduce_none, + 1, 142, :_reduce_none, + 1, 142, :_reduce_none, + 1, 142, :_reduce_none, + 1, 142, :_reduce_none, + 1, 142, :_reduce_none, + 1, 142, :_reduce_none, + 1, 142, :_reduce_none, + 0, 83, :_reduce_208 ] -racc_reduce_n = 205 +racc_reduce_n = 209 -racc_shift_n = 357 +racc_shift_n = 361 racc_token_table = { false => 0, @@ -784,41 +803,44 @@ racc_token_table = { :CASE => 41, :DEFAULT => 42, :AT => 43, - :LCOLLECT => 44, - :RCOLLECT => 45, - :CLASSREF => 46, - :NOT => 47, - :OR => 48, - :AND => 49, - :UNDEF => 50, - :PARROW => 51, - :PLUS => 52, - :MINUS => 53, - :TIMES => 54, - :DIV => 55, - :LSHIFT => 56, - :RSHIFT => 57, - :UMINUS => 58, - :MATCH => 59, - :NOMATCH => 60, - :REGEX => 61, - :IN_EDGE => 62, - :OUT_EDGE => 63, - :IN_EDGE_SUB => 64, - :OUT_EDGE_SUB => 65, - :IN => 66, - :UNLESS => 67, - :PIPE => 68, - :SELBRACE => 69, - :LOW => 70, - :HIGH => 71, - :CALL => 72, - :MODULO => 73, - :DELETES => 74, - :TITLE_COLON => 75, - :CASE_COLON => 76 } + :ATAT => 44, + :LCOLLECT => 45, + :RCOLLECT => 46, + :CLASSREF => 47, + :NOT => 48, + :OR => 49, + :AND => 50, + :UNDEF => 51, + :PARROW => 52, + :PLUS => 53, + :MINUS => 54, + :TIMES => 55, + :DIV => 56, + :LSHIFT => 57, + :RSHIFT => 58, + :UMINUS => 59, + :MATCH => 60, + :NOMATCH => 61, + :REGEX => 62, + :IN_EDGE => 63, + :OUT_EDGE => 64, + :IN_EDGE_SUB => 65, + :OUT_EDGE_SUB => 66, + :IN => 67, + :UNLESS => 68, + :PIPE => 69, + :LAMBDA => 70, + :SELBRACE => 71, + :NUMBER => 72, + :LOW => 73, + :HIGH => 74, + :CALL => 75, + :MODULO => 76, + :DELETES => 77, + :TITLE_COLON => 78, + :CASE_COLON => 79 } -racc_nt_base = 77 +racc_nt_base = 80 racc_use_result_var = true @@ -883,6 +905,7 @@ Racc_token_to_s_table = [ "CASE", "DEFAULT", "AT", + "ATAT", "LCOLLECT", "RCOLLECT", "CLASSREF", @@ -908,7 +931,9 @@ Racc_token_to_s_table = [ "IN", "UNLESS", "PIPE", + "LAMBDA", "SELBRACE", + "NUMBER", "LOW", "HIGH", "CALL", @@ -948,6 +973,7 @@ Racc_token_to_s_table = [ "hash", "regex", "text_or_name", + "number", "type", "undef", "name", @@ -980,6 +1006,8 @@ Racc_token_to_s_table = [ "keyword", "classname", "parameter_list", + "opt_statements", + "stacked_classname", "classparent", "classnameordefault", "hostnames", @@ -1003,7 +1031,7 @@ Racc_debug_parser = false # reduce 0 omitted -module_eval(<<'.,.,', 'egrammar.ra', 57) +module_eval(<<'.,.,', 'egrammar.ra', 58) def _reduce_1(val, _values, result) result = Factory.block_or_expression(*val[0]) result @@ -1012,42 +1040,42 @@ module_eval(<<'.,.,', 'egrammar.ra', 57) # reduce 2 omitted -module_eval(<<'.,.,', 'egrammar.ra', 62) +module_eval(<<'.,.,', 'egrammar.ra', 63) def _reduce_3(val, _values, result) result = transform_calls(val[0]) result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 68) +module_eval(<<'.,.,', 'egrammar.ra', 69) def _reduce_4(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 69) +module_eval(<<'.,.,', 'egrammar.ra', 70) def _reduce_5(val, _values, result) result = val[0].push val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 70) +module_eval(<<'.,.,', 'egrammar.ra', 71) def _reduce_6(val, _values, result) result = val[0].push val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 74) +module_eval(<<'.,.,', 'egrammar.ra', 75) def _reduce_7(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 75) +module_eval(<<'.,.,', 'egrammar.ra', 76) def _reduce_8(val, _values, result) result = aryfy(val[0]).push val[2] result @@ -1056,35 +1084,35 @@ module_eval(<<'.,.,', 'egrammar.ra', 75) # reduce 9 omitted -module_eval(<<'.,.,', 'egrammar.ra', 81) +module_eval(<<'.,.,', 'egrammar.ra', 82) def _reduce_10(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 82) +module_eval(<<'.,.,', 'egrammar.ra', 83) def _reduce_11(val, _values, result) result = val[0].relop(val[1][:value], val[2]); loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 83) +module_eval(<<'.,.,', 'egrammar.ra', 84) def _reduce_12(val, _values, result) result = val[0].relop(val[1][:value], val[2]); loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 84) +module_eval(<<'.,.,', 'egrammar.ra', 85) def _reduce_13(val, _values, result) result = val[0].relop(val[1][:value], val[2]); loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 85) +module_eval(<<'.,.,', 'egrammar.ra', 86) def _reduce_14(val, _values, result) result = val[0].relop(val[1][:value], val[2]); loc result, val[1] result @@ -1093,196 +1121,196 @@ module_eval(<<'.,.,', 'egrammar.ra', 85) # reduce 15 omitted -module_eval(<<'.,.,', 'egrammar.ra', 92) +module_eval(<<'.,.,', 'egrammar.ra', 93) def _reduce_16(val, _values, result) result = val[0][*val[2]] ; loc result, val[0], val[3] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 93) +module_eval(<<'.,.,', 'egrammar.ra', 94) def _reduce_17(val, _values, result) result = val[0].in val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 94) +module_eval(<<'.,.,', 'egrammar.ra', 95) def _reduce_18(val, _values, result) result = val[0] =~ val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 95) +module_eval(<<'.,.,', 'egrammar.ra', 96) def _reduce_19(val, _values, result) result = val[0].mne val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 96) +module_eval(<<'.,.,', 'egrammar.ra', 97) def _reduce_20(val, _values, result) result = val[0] + val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 97) +module_eval(<<'.,.,', 'egrammar.ra', 98) def _reduce_21(val, _values, result) result = val[0] - val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 98) +module_eval(<<'.,.,', 'egrammar.ra', 99) def _reduce_22(val, _values, result) result = val[0] / val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 99) +module_eval(<<'.,.,', 'egrammar.ra', 100) def _reduce_23(val, _values, result) result = val[0] * val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 100) +module_eval(<<'.,.,', 'egrammar.ra', 101) def _reduce_24(val, _values, result) result = val[0] % val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 101) +module_eval(<<'.,.,', 'egrammar.ra', 102) def _reduce_25(val, _values, result) result = val[0] << val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 102) +module_eval(<<'.,.,', 'egrammar.ra', 103) def _reduce_26(val, _values, result) result = val[0] >> val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 103) +module_eval(<<'.,.,', 'egrammar.ra', 104) def _reduce_27(val, _values, result) result = val[1].minus() ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 104) +module_eval(<<'.,.,', 'egrammar.ra', 105) def _reduce_28(val, _values, result) result = val[0].ne val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 105) +module_eval(<<'.,.,', 'egrammar.ra', 106) def _reduce_29(val, _values, result) result = val[0] == val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 106) +module_eval(<<'.,.,', 'egrammar.ra', 107) def _reduce_30(val, _values, result) result = val[0] > val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 107) +module_eval(<<'.,.,', 'egrammar.ra', 108) def _reduce_31(val, _values, result) result = val[0] >= val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 108) +module_eval(<<'.,.,', 'egrammar.ra', 109) def _reduce_32(val, _values, result) result = val[0] < val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 109) +module_eval(<<'.,.,', 'egrammar.ra', 110) def _reduce_33(val, _values, result) result = val[0] <= val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 110) +module_eval(<<'.,.,', 'egrammar.ra', 111) def _reduce_34(val, _values, result) result = val[1].not ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 111) +module_eval(<<'.,.,', 'egrammar.ra', 112) def _reduce_35(val, _values, result) result = val[0].and val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 112) +module_eval(<<'.,.,', 'egrammar.ra', 113) def _reduce_36(val, _values, result) result = val[0].or val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 113) +module_eval(<<'.,.,', 'egrammar.ra', 114) def _reduce_37(val, _values, result) result = val[0].set(val[2]) ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 114) +module_eval(<<'.,.,', 'egrammar.ra', 115) def _reduce_38(val, _values, result) result = val[0].plus_set(val[2]) ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 115) +module_eval(<<'.,.,', 'egrammar.ra', 116) def _reduce_39(val, _values, result) result = val[0].minus_set(val[2]); loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 116) +module_eval(<<'.,.,', 'egrammar.ra', 117) def _reduce_40(val, _values, result) result = val[0].select(*val[2]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 117) +module_eval(<<'.,.,', 'egrammar.ra', 118) def _reduce_41(val, _values, result) result = val[1].paren() ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 125) +module_eval(<<'.,.,', 'egrammar.ra', 126) def _reduce_42(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 126) +module_eval(<<'.,.,', 'egrammar.ra', 127) def _reduce_43(val, _values, result) result = val[0].push(val[2]) result @@ -1327,22 +1355,24 @@ module_eval(<<'.,.,', 'egrammar.ra', 126) # reduce 62 omitted -module_eval(<<'.,.,', 'egrammar.ra', 156) - def _reduce_63(val, _values, result) - result = val[0] - result - end -.,., +# reduce 63 omitted -module_eval(<<'.,.,', 'egrammar.ra', 157) +module_eval(<<'.,.,', 'egrammar.ra', 158) def _reduce_64(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 165) +module_eval(<<'.,.,', 'egrammar.ra', 159) def _reduce_65(val, _values, result) + result = val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 167) + def _reduce_66(val, _values, result) result = Factory.CALL_NAMED(val[0], true, val[2]) loc result, val[0], val[4] @@ -1350,8 +1380,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 165) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 169) - def _reduce_66(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 171) + def _reduce_67(val, _values, result) result = Factory.CALL_NAMED(val[0], true, []) loc result, val[0], val[2] @@ -1359,8 +1389,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 169) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 173) - def _reduce_67(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 175) + def _reduce_68(val, _values, result) result = Factory.CALL_NAMED(val[0], true, val[2]) loc result, val[0], val[4] result.lambda = val[5] @@ -1369,8 +1399,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 173) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 178) - def _reduce_68(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 180) + def _reduce_69(val, _values, result) result = Factory.CALL_NAMED(val[0], true, []) loc result, val[0], val[2] result.lambda = val[3] @@ -1379,50 +1409,50 @@ module_eval(<<'.,.,', 'egrammar.ra', 178) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 182) - def _reduce_69(val, _values, result) - result = val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 187) +module_eval(<<'.,.,', 'egrammar.ra', 184) def _reduce_70(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 188) +module_eval(<<'.,.,', 'egrammar.ra', 189) def _reduce_71(val, _values, result) + result = val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 190) + def _reduce_72(val, _values, result) result = val[0]; val[0].lambda = val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 191) - def _reduce_72(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 193) + def _reduce_73(val, _values, result) result = Factory.CALL_METHOD(val[0], val[2]); loc result, val[1], val[3] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 192) - def _reduce_73(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 194) + def _reduce_74(val, _values, result) result = Factory.CALL_METHOD(val[0], []); loc result, val[1], val[3] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 193) - def _reduce_74(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 195) + def _reduce_75(val, _values, result) result = Factory.CALL_METHOD(val[0], []); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 198) - def _reduce_75(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 200) + def _reduce_76(val, _values, result) result = val[0].dot(Factory.fqn(val[2][:value])) loc result, val[1], val[2] @@ -1430,8 +1460,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 198) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 210) - def _reduce_76(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 212) + def _reduce_77(val, _values, result) result = Factory.LAMBDA(val[0], val[1]) # loc result, val[1] # TODO @@ -1439,36 +1469,36 @@ module_eval(<<'.,.,', 'egrammar.ra', 210) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 215) - def _reduce_77(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 217) + def _reduce_78(val, _values, result) result = val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 216) - def _reduce_78(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 218) + def _reduce_79(val, _values, result) result = nil result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 220) - def _reduce_79(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 222) + def _reduce_80(val, _values, result) result = [] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 221) - def _reduce_80(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 223) + def _reduce_81(val, _values, result) result = val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 231) - def _reduce_81(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 233) + def _reduce_82(val, _values, result) result = val[1] loc(result, val[0], val[1]) @@ -1476,8 +1506,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 231) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 238) - def _reduce_82(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 240) + def _reduce_83(val, _values, result) result = Factory.IF(val[0], Factory.block_or_expression(*val[2]), val[4]) loc(result, val[0], (val[4] ? val[4] : val[3])) @@ -1485,8 +1515,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 238) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 242) - def _reduce_83(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 244) + def _reduce_84(val, _values, result) result = Factory.IF(val[0], nil, val[3]) loc(result, val[0], (val[3] ? val[3] : val[2])) @@ -1494,10 +1524,10 @@ module_eval(<<'.,.,', 'egrammar.ra', 242) end .,., -# reduce 84 omitted +# reduce 85 omitted -module_eval(<<'.,.,', 'egrammar.ra', 250) - def _reduce_85(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 252) + def _reduce_86(val, _values, result) result = val[1] loc(result, val[0], val[1]) @@ -1505,8 +1535,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 250) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 254) - def _reduce_86(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 256) + def _reduce_87(val, _values, result) result = Factory.block_or_expression(*val[2]) loc result, val[0], val[3] @@ -1514,16 +1544,16 @@ module_eval(<<'.,.,', 'egrammar.ra', 254) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 258) - def _reduce_87(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 260) + def _reduce_88(val, _values, result) result = nil # don't think a nop is needed here either result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 267) - def _reduce_88(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 269) + def _reduce_89(val, _values, result) result = Factory.UNLESS(val[1], Factory.block_or_expression(*val[3]), val[5]) loc result, val[0], val[4] @@ -1531,8 +1561,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 267) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 271) - def _reduce_89(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 273) + def _reduce_90(val, _values, result) result = Factory.UNLESS(val[1], nil, nil) loc result, val[0], val[4] @@ -1540,10 +1570,10 @@ module_eval(<<'.,.,', 'egrammar.ra', 271) end .,., -# reduce 90 omitted +# reduce 91 omitted -module_eval(<<'.,.,', 'egrammar.ra', 281) - def _reduce_91(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 283) + def _reduce_92(val, _values, result) result = Factory.block_or_expression(*val[2]) loc result, val[0], val[3] @@ -1551,16 +1581,16 @@ module_eval(<<'.,.,', 'egrammar.ra', 281) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 285) - def _reduce_92(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 287) + def _reduce_93(val, _values, result) result = nil # don't think a nop is needed here either result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 293) - def _reduce_93(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 295) + def _reduce_94(val, _values, result) result = Factory.CASE(val[1], *val[3]) loc result, val[0], val[4] @@ -1568,22 +1598,22 @@ module_eval(<<'.,.,', 'egrammar.ra', 293) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 299) - def _reduce_94(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 301) + def _reduce_95(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 300) - def _reduce_95(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 302) + def _reduce_96(val, _values, result) result = val[0].push val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 305) - def _reduce_96(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 307) + def _reduce_97(val, _values, result) result = Factory.WHEN(val[0], val[3]) loc result, val[1], val[4] @@ -1591,8 +1621,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 305) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 309) - def _reduce_97(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 311) + def _reduce_98(val, _values, result) result = Factory.WHEN(val[0], nil) loc result, val[1], val[3] @@ -1600,65 +1630,65 @@ module_eval(<<'.,.,', 'egrammar.ra', 309) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 313) - def _reduce_98(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 315) + def _reduce_99(val, _values, result) result = val[0] result end .,., -# reduce 99 omitted +# reduce 100 omitted -module_eval(<<'.,.,', 'egrammar.ra', 324) - def _reduce_100(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 326) + def _reduce_101(val, _values, result) result = val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 329) - def _reduce_101(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 331) + def _reduce_102(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 330) - def _reduce_102(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 332) + def _reduce_103(val, _values, result) result = val[0].push val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 335) - def _reduce_103(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 337) + def _reduce_104(val, _values, result) result = Factory.MAP(val[0], val[2]) ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 351) - def _reduce_104(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 353) + def _reduce_105(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 354) - def _reduce_105(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 356) + def _reduce_106(val, _values, result) result = case Factory.resource_shape(val[1]) when :resource, :class tmp = Factory.RESOURCE(Factory.fqn(token_text(val[1])), val[3]) tmp.form = val[0] tmp when :defaults - error "A resource default can not be virtual or exported" + error val[1], "A resource default can not be virtual or exported" when :override - error "A resource override can not be virtual or exported" + error val[1], "A resource override can not be virtual or exported" else - error "Expression is not valid as a resource, resource-default, or resource-override" + error val[1], "Expression is not valid as a resource, resource-default, or resource-override" end loc result, val[1], val[4] @@ -1666,34 +1696,30 @@ module_eval(<<'.,.,', 'egrammar.ra', 354) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 369) - def _reduce_106(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 371) + def _reduce_107(val, _values, result) result = case Factory.resource_shape(val[1]) - when :resource, :class - error "Defaults are not virtualizable" - when :defaults - error "Defaults are not virtualizable" - when :override - error "Defaults are not virtualizable" + when :resource, :class, :defaults, :override + error val[1], "Defaults are not virtualizable" else - error "Expression is not valid as a resource, resource-default, or resource-override" + error val[1], "Expression is not valid as a resource, resource-default, or resource-override" end result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 381) - def _reduce_107(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 379) + def _reduce_108(val, _values, result) result = case Factory.resource_shape(val[0]) when :resource, :class Factory.RESOURCE(Factory.fqn(token_text(val[0])), val[2]) when :defaults - error "A resource default can not specify a resource name" + error val[1], "A resource default can not specify a resource name" when :override - error "A resource override does not allow override of name of resource" + error val[1], "A resource override does not allow override of name of resource" else - error "Expression is not valid as a resource, resource-default, or resource-override" + error val[1], "Expression is not valid as a resource, resource-default, or resource-override" end loc result, val[0], val[4] @@ -1701,19 +1727,19 @@ module_eval(<<'.,.,', 'egrammar.ra', 381) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 394) - def _reduce_108(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 392) + def _reduce_109(val, _values, result) result = case Factory.resource_shape(val[0]) when :resource, :class # This catches deprecated syntax. - error "All resource specifications require names" + error val[1], "All resource specifications require names" when :defaults Factory.RESOURCE_DEFAULTS(val[0], val[2]) when :override # This was only done for override in original - TODO shuld it be here at all Factory.RESOURCE_OVERRIDE(val[0], val[2]) else - error "Expression is not valid as a resource, resource-default, or resource-override" + error val[0], "Expression is not valid as a resource, resource-default, or resource-override" end loc result, val[0], val[4] @@ -1721,8 +1747,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 394) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 409) - def _reduce_109(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 407) + def _reduce_110(val, _values, result) result = Factory.RESOURCE(Factory.fqn(token_text(val[0])), val[2]) loc result, val[0], val[4] @@ -1730,50 +1756,57 @@ module_eval(<<'.,.,', 'egrammar.ra', 409) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 414) - def _reduce_110(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 412) + def _reduce_111(val, _values, result) result = Factory.RESOURCE_BODY(val[0], val[2]) result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 416) - def _reduce_111(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 414) + def _reduce_112(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 419) - def _reduce_112(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 417) + def _reduce_113(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 420) - def _reduce_113(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 418) + def _reduce_114(val, _values, result) result = val[0].push val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 425) - def _reduce_114(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 423) + def _reduce_115(val, _values, result) result = :virtual result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 426) - def _reduce_115(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 424) + def _reduce_116(val, _values, result) result = :exported result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 438) - def _reduce_116(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 425) + def _reduce_117(val, _values, result) + result = :exported + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 437) + def _reduce_118(val, _values, result) result = Factory.COLLECT(val[0], val[1], val[3]) loc result, val[0], val[5] @@ -1781,8 +1814,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 438) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 442) - def _reduce_117(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 441) + def _reduce_119(val, _values, result) result = Factory.COLLECT(val[0], val[1], []) loc result, val[0], val[1] @@ -1790,53 +1823,53 @@ module_eval(<<'.,.,', 'egrammar.ra', 442) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 447) - def _reduce_118(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 446) + def _reduce_120(val, _values, result) result = Factory.VIRTUAL_QUERY(val[1]) ; loc result, val[0], val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 448) - def _reduce_119(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 447) + def _reduce_121(val, _values, result) result = Factory.EXPORTED_QUERY(val[1]) ; loc result, val[0], val[2] result end .,., -# reduce 120 omitted +# reduce 122 omitted -# reduce 121 omitted +# reduce 123 omitted -module_eval(<<'.,.,', 'egrammar.ra', 461) - def _reduce_122(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 460) + def _reduce_124(val, _values, result) result = [] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 462) - def _reduce_123(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 461) + def _reduce_125(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 463) - def _reduce_124(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 462) + def _reduce_126(val, _values, result) result = val[0].push(val[2]) result end .,., -# reduce 125 omitted - -# reduce 126 omitted - # reduce 127 omitted -module_eval(<<'.,.,', 'egrammar.ra', 479) - def _reduce_128(val, _values, result) +# reduce 128 omitted + +# reduce 129 omitted + +module_eval(<<'.,.,', 'egrammar.ra', 478) + def _reduce_130(val, _values, result) result = Factory.ATTRIBUTE_OP(val[0][:value], :'=>', val[2]) loc result, val[0], val[2] @@ -1844,8 +1877,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 479) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 483) - def _reduce_129(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 482) + def _reduce_131(val, _values, result) result = Factory.ATTRIBUTE_OP(val[0][:value], :'+>', val[2]) loc result, val[0], val[2] @@ -1853,29 +1886,22 @@ module_eval(<<'.,.,', 'egrammar.ra', 483) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 493) - def _reduce_130(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 492) + def _reduce_132(val, _values, result) result = Factory.DEFINITION(classname(val[1][:value]), val[2], val[4]) loc result, val[0], val[5] - @lexer.indefine = false + # New lexer does not keep track of this, this is done in validation + if @lexer.respond_to?(:'indefine=') + @lexer.indefine = false + end result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 498) - def _reduce_131(val, _values, result) - result = Factory.DEFINITION(classname(val[1][:value]), val[2], nil) - loc result, val[0], val[4] - @lexer.indefine = false - - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 513) - def _reduce_132(val, _values, result) - @lexer.namepop +module_eval(<<'.,.,', 'egrammar.ra', 515) + def _reduce_133(val, _values, result) + namepop result = Factory.HOSTCLASS(classname(val[1][:value]), val[2], token_text(val[3]), val[5]) loc result, val[0], val[6] @@ -1883,31 +1909,32 @@ module_eval(<<'.,.,', 'egrammar.ra', 513) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 518) - def _reduce_133(val, _values, result) - @lexer.namepop - result = Factory.HOSTCLASS(classname(val[1][:value]), val[2], token_text(val[3]), nil) - loc result, val[0], val[5] - +module_eval(<<'.,.,', 'egrammar.ra', 525) + def _reduce_134(val, _values, result) + namestack(val[0][:value]) ; result = val[0] result end .,., -# reduce 134 omitted - -module_eval(<<'.,.,', 'egrammar.ra', 526) - def _reduce_135(val, _values, result) - result = val[1] - result - end -.,., +# reduce 135 omitted # reduce 136 omitted # reduce 137 omitted -module_eval(<<'.,.,', 'egrammar.ra', 543) +module_eval(<<'.,.,', 'egrammar.ra', 534) def _reduce_138(val, _values, result) + result = val[1] + result + end +.,., + +# reduce 139 omitted + +# reduce 140 omitted + +module_eval(<<'.,.,', 'egrammar.ra', 551) + def _reduce_141(val, _values, result) result = Factory.NODE(val[1], val[2], val[4]) loc result, val[0], val[5] @@ -1915,8 +1942,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 543) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 547) - def _reduce_139(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 555) + def _reduce_142(val, _values, result) result = Factory.NODE(val[1], val[2], nil) loc result, val[0], val[4] @@ -1924,315 +1951,314 @@ module_eval(<<'.,.,', 'egrammar.ra', 547) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 557) - def _reduce_140(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 565) + def _reduce_143(val, _values, result) result = [result] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 558) - def _reduce_141(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 566) + def _reduce_144(val, _values, result) result = val[0].push(val[2]) result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 563) - def _reduce_142(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 571) + def _reduce_145(val, _values, result) result = Factory.fqn(val[0][:value]); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 564) - def _reduce_143(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 572) + def _reduce_146(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 565) - def _reduce_144(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 573) + def _reduce_147(val, _values, result) result = Factory.literal(:default); loc result, val[0] result end .,., -# reduce 145 omitted +# reduce 148 omitted -# reduce 146 omitted +# reduce 149 omitted -module_eval(<<'.,.,', 'egrammar.ra', 571) - def _reduce_147(val, _values, result) - result = val[1] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 576) - def _reduce_148(val, _values, result) - result = val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 577) - def _reduce_149(val, _values, result) - result = val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 581) +module_eval(<<'.,.,', 'egrammar.ra', 579) def _reduce_150(val, _values, result) - result = [] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 582) - def _reduce_151(val, _values, result) - result = [] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 583) - def _reduce_152(val, _values, result) result = val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 587) +module_eval(<<'.,.,', 'egrammar.ra', 584) + def _reduce_151(val, _values, result) + result = val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 585) + def _reduce_152(val, _values, result) + error val[0], "'class' is not a valid classname" + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 589) def _reduce_153(val, _values, result) + result = [] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 590) + def _reduce_154(val, _values, result) + result = [] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 591) + def _reduce_155(val, _values, result) + result = val[1] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 595) + def _reduce_156(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 588) - def _reduce_154(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 596) + def _reduce_157(val, _values, result) result = val[0].push(val[2]) result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 592) - def _reduce_155(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 600) + def _reduce_158(val, _values, result) result = Factory.PARAM(val[0][:value], val[2]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 593) - def _reduce_156(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 601) + def _reduce_159(val, _values, result) result = Factory.PARAM(val[0][:value]); loc result, val[0] result end .,., -# reduce 157 omitted +# reduce 160 omitted -# reduce 158 omitted +# reduce 161 omitted -module_eval(<<'.,.,', 'egrammar.ra', 606) - def _reduce_159(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 614) + def _reduce_162(val, _values, result) result = Factory.fqn(val[0][:value]).var ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 612) - def _reduce_160(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 620) + def _reduce_163(val, _values, result) result = Factory.LIST(val[1]); loc result, val[0], val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 613) - def _reduce_161(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 621) + def _reduce_164(val, _values, result) result = Factory.LIST(val[1]); loc result, val[0], val[3] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 614) - def _reduce_162(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 622) + def _reduce_165(val, _values, result) result = Factory.literal([]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 617) - def _reduce_163(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 625) + def _reduce_166(val, _values, result) result = Factory.HASH(val[1]); loc result, val[0], val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 618) - def _reduce_164(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 626) + def _reduce_167(val, _values, result) result = Factory.HASH(val[1]); loc result, val[0], val[3] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 619) - def _reduce_165(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 627) + def _reduce_168(val, _values, result) result = Factory.literal({}) ; loc result, val[0], val[3] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 622) - def _reduce_166(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 630) + def _reduce_169(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 623) - def _reduce_167(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 631) + def _reduce_170(val, _values, result) result = val[0].push val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 626) - def _reduce_168(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 634) + def _reduce_171(val, _values, result) result = Factory.KEY_ENTRY(val[0], val[2]); loc result, val[1] result end .,., -# reduce 169 omitted +# reduce 172 omitted -# reduce 170 omitted +# reduce 173 omitted -module_eval(<<'.,.,', 'egrammar.ra', 632) - def _reduce_171(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 640) + def _reduce_174(val, _values, result) result = Factory.literal(val[0][:value]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 633) - def _reduce_172(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 641) + def _reduce_175(val, _values, result) result = Factory.string(val[0], *val[1]) ; loc result, val[0], val[1][-1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 634) - def _reduce_173(val, _values, result) - result = Factory.literal(val[0][:value]); loc result, val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 635) - def _reduce_174(val, _values, result) - result = Factory.literal(val[0][:value]); loc result, val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 636) - def _reduce_175(val, _values, result) - result = Factory.literal(val[0][:value]); loc result, val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 637) - def _reduce_176(val, _values, result) - result = [val[0]] + val[1] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 638) - def _reduce_177(val, _values, result) - result = Factory.TEXT(val[0]) - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 641) - def _reduce_178(val, _values, result) - result = [val[0]] - result - end -.,., - module_eval(<<'.,.,', 'egrammar.ra', 642) + def _reduce_176(val, _values, result) + result = Factory.literal(val[0][:value]); loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 643) + def _reduce_177(val, _values, result) + result = Factory.literal(val[0][:value]); loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 644) + def _reduce_178(val, _values, result) + result = Factory.literal(val[0][:value]); loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 645) def _reduce_179(val, _values, result) result = [val[0]] + val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 644) - def _reduce_180(val, _values, result) - result = Factory.QNAME_OR_NUMBER(val[0][:value]) ; loc result, val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 645) - def _reduce_181(val, _values, result) - result = Factory.QREF(val[0][:value]) ; loc result, val[0] - result - end -.,., - module_eval(<<'.,.,', 'egrammar.ra', 646) - def _reduce_182(val, _values, result) - result = Factory.literal(:undef); loc result, val[0] + def _reduce_180(val, _values, result) + result = Factory.TEXT(val[0]) result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 647) - def _reduce_183(val, _values, result) - result = Factory.literal(:default); loc result, val[0] +module_eval(<<'.,.,', 'egrammar.ra', 649) + def _reduce_181(val, _values, result) + result = [val[0]] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 650) + def _reduce_182(val, _values, result) + result = [val[0]] + val[1] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 652) + def _reduce_183(val, _values, result) + result = Factory.NUMBER(val[0][:value]) ; loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 653) def _reduce_184(val, _values, result) - result = Factory.literal(val[0][:value]) ; loc result, val[0] + result = Factory.QNAME_OR_NUMBER(val[0][:value]) ; loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 654) + def _reduce_185(val, _values, result) + result = Factory.QREF(val[0][:value]) ; loc result, val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 655) - def _reduce_185(val, _values, result) + def _reduce_186(val, _values, result) + result = Factory.literal(:undef); loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 656) + def _reduce_187(val, _values, result) + result = Factory.literal(:default); loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 661) + def _reduce_188(val, _values, result) + result = Factory.literal(val[0][:value]) ; loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 664) + def _reduce_189(val, _values, result) result = Factory.literal(val[0][:value]); loc result, val[0] result end .,., -# reduce 186 omitted +# reduce 190 omitted -module_eval(<<'.,.,', 'egrammar.ra', 661) - def _reduce_187(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 670) + def _reduce_191(val, _values, result) result = nil result end .,., -# reduce 188 omitted - -# reduce 189 omitted - -# reduce 190 omitted - -# reduce 191 omitted - # reduce 192 omitted # reduce 193 omitted @@ -2257,8 +2283,16 @@ module_eval(<<'.,.,', 'egrammar.ra', 661) # reduce 203 omitted -module_eval(<<'.,.,', 'egrammar.ra', 684) - def _reduce_204(val, _values, result) +# reduce 204 omitted + +# reduce 205 omitted + +# reduce 206 omitted + +# reduce 207 omitted + +module_eval(<<'.,.,', 'egrammar.ra', 693) + def _reduce_208(val, _values, result) result = nil result end diff --git a/lib/puppet/pops/parser/interpolation_support.rb b/lib/puppet/pops/parser/interpolation_support.rb index 1529065b0..696709acb 100644 --- a/lib/puppet/pops/parser/interpolation_support.rb +++ b/lib/puppet/pops/parser/interpolation_support.rb @@ -175,6 +175,44 @@ module Puppet::Pops::Parser::InterpolationSupport interpolate_tail_uq end + # Enqueues lexed tokens until either end of input, or the given brace_count is reached + # + def enqueue_until brace_count + scn = @scanner + ctx = @lexing_context + queue = @token_queue + + scn.skip(self.class::PATTERN_WS) + queue_size = queue.size + until scn.eos? do + if token = lex_token + token_name = token[0] + ctx[:after] = token_name + if token_name == :RBRACE && ctx[:brace_count] == brace_count + if queue.size - queue_size == 1 + # Single token is subject to replacement + queue[-1] = transform_to_variable(queue[-1]) + end + return + end + queue << token + else + scn.skip(self.class::PATTERN_WS) + end + end + end + + def transform_to_variable(token) + token_name = token[0] + if [:NUMBER, :NAME].include?(token_name) || self.class::KEYWORD_NAMES[token_name] + t = token[1] + ta = t.token_array + [:VARIABLE, self.class::TokenValue.new([:VARIABLE, ta[1], ta[2]], t.offset, t.locator)] + else + token + end + end + # Interpolates unquoted string and transfers the result to the given lexer # (This is used when a second lexer instance is used to lex a substring) # diff --git a/lib/puppet/pops/parser/lexer2.rb b/lib/puppet/pops/parser/lexer2.rb index 7be7470e0..04de1e485 100644 --- a/lib/puppet/pops/parser/lexer2.rb +++ b/lib/puppet/pops/parser/lexer2.rb @@ -1,6 +1,6 @@ # The Lexer is responsbile for turning source text into tokens. # This version is a performance enhanced lexer (in comparison to the 3.x and earlier "future parser" lexer. -# +# # Old returns tokens [:KEY, value, { locator = } # Could return [[token], locator] # or Token.new([token], locator) with the same API x[0] = token_symbol, x[1] = self, x[:key] = (:value, :file, :line, :pos) etc @@ -71,6 +71,7 @@ class Puppet::Pops::Parser::Lexer2 TOKEN_DOT = [:DOT, '.'.freeze, 1].freeze TOKEN_PIPE = [:PIPE, '|'.freeze, 1].freeze TOKEN_AT = [:AT , '@'.freeze, 1].freeze + TOKEN_ATAT = [:ATAT , '@@'.freeze, 2].freeze TOKEN_COLON = [:COLON, ':'.freeze, 1].freeze TOKEN_COMMA = [:COMMA, ','.freeze, 1].freeze TOKEN_SEMIC = [:SEMIC, ';'.freeze, 1].freeze @@ -105,7 +106,7 @@ class Puppet::Pops::Parser::Lexer2 # Keywords are all singleton tokens with pre calculated lengths. # Booleans are pre-calculated (rather than evaluating the strings "false" "true" repeatedly. - # + # KEYWORDS = { "case" => [:CASE, 'case', 4], "class" => [:CLASS, 'class', 5], @@ -127,6 +128,11 @@ class Puppet::Pops::Parser::Lexer2 KEYWORDS.each {|k,v| v[1].freeze; v.freeze } KEYWORDS.freeze + # Reverse lookup of keyword name to string + KEYWORD_NAMES = {} + KEYWORDS.each {|k, v| KEYWORD_NAMES[v[0]] = k } + KEYWORD_NAMES.freeze + PATTERN_WS = %r{[[:blank:]\r]+} # The single line comment includes the line ending. @@ -143,7 +149,7 @@ class Puppet::Pops::Parser::Lexer2 # PATTERN_CLASSREF = %r{((::){0,1}[A-Z][-\w]*)+} # PATTERN_NAME = %r{((::)?[a-z0-9][-\w]*)(::[a-z0-9][-\w]*)*} - # The NAME and CLASSREF in 4x are strict. Each segment must start with + # The NAME and CLASSREF in 4x are strict. Each segment must start with # a letter a-z and may not contain dashes (\w includes letters, digits and _). # PATTERN_CLASSREF = %r{((::){0,1}[A-Z][\w]*)+} @@ -152,12 +158,13 @@ class Puppet::Pops::Parser::Lexer2 PATTERN_DOLLAR_VAR = %r{\$(::)?(\w+::)*\w+} PATTERN_NUMBER = %r{\b(?:0[xX][0-9A-Fa-f]+|0?\d+(?:\.\d+)?(?:[eE]-?\d+)?)\b} - # PERFORMANCE NOTE: # Comparison against a frozen string is faster (than unfrozen). # STRING_BSLASH_BSLASH = '\\'.freeze + attr_reader :locator + def initialize() end @@ -207,6 +214,13 @@ class Puppet::Pops::Parser::Lexer2 lex_file(file) end + # TODO: This method should not be used, callers should get the locator since it is most likely required to + # compute line, position etc given offsets. + # + def file + @locator ? @locator.file : nil + end + # Initializes lexing of the content of the given file. An empty string is used if the file does not exist. # def lex_file(file) @@ -350,7 +364,7 @@ class Puppet::Pops::Parser::Lexer2 ctx[:brace_count] -= 1 emit(TOKEN_RBRACE, before) - # TOKENS @, @@, @( + # TOKENS @, @@, @( when '@' case la1 when '@' @@ -361,7 +375,7 @@ class Puppet::Pops::Parser::Lexer2 emit(TOKEN_AT, before) end - # TOKENS |, |>, |>> + # TOKENS |, |>, |>> when '|' emit(case la1 when '>' @@ -370,7 +384,7 @@ class Puppet::Pops::Parser::Lexer2 TOKEN_PIPE end, before) - # TOKENS =, =>, ==, =~ + # TOKENS =, =>, ==, =~ when '=' emit(case la1 when '=' @@ -383,34 +397,34 @@ class Puppet::Pops::Parser::Lexer2 TOKEN_EQUALS end, before) - # TOKENS '+', '+=', and '+>' + # TOKENS '+', '+=', and '+>' when '+' emit(case la1 when '=' TOKEN_APPENDS when '>' - TOKEN_PARROW + TOKEN_PARROW else - TOKEN_PLUS + TOKEN_PLUS end, before) - # TOKENS '-', '->', and epp '-%>' (end of interpolation with trim) + # TOKENS '-', '->', and epp '-%>' (end of interpolation with trim) when '-' if ctx[:epp_mode] && la1 == '%' && la2 == '>' scn.pos += 3 interpolate_epp(:with_trim) else emit(case la1 - when '>' - TOKEN_IN_EDGE - when '=' - TOKEN_DELETES - else - TOKEN_MINUS - end, before) + when '>' + TOKEN_IN_EDGE + when '=' + TOKEN_DELETES + else + TOKEN_MINUS + end, before) end - # TOKENS !, !=, !~ + # TOKENS !, !=, !~ when '!' emit(case la1 when '=' @@ -421,7 +435,7 @@ class Puppet::Pops::Parser::Lexer2 TOKEN_NOT end, before) - # TOKENS ~>, ~ + # TOKENS ~>, ~ when '~' emit(la1 == '>' ? TOKEN_IN_EDGE_SUB : TOKEN_TILDE, before) @@ -429,7 +443,7 @@ class Puppet::Pops::Parser::Lexer2 scn.skip(PATTERN_COMMENT) nil - # TOKENS '/', '/*' and '/ regexp /' + # TOKENS '/', '/*' and '/ regexp /' when '/' case la1 when '*' @@ -450,7 +464,7 @@ class Puppet::Pops::Parser::Lexer2 end end - # TOKENS <, <=, <|, <<|, <<, <-, <~ + # TOKENS <, <=, <|, <<|, <<, <-, <~ when '<' emit(case la1 when '<' @@ -471,7 +485,7 @@ class Puppet::Pops::Parser::Lexer2 TOKEN_LESSTHAN end, before) - # TOKENS >, >=, >> + # TOKENS >, >=, >> when '>' emit(case la1 when '>' @@ -482,7 +496,7 @@ class Puppet::Pops::Parser::Lexer2 TOKEN_GREATERTHAN end, before) - # TOKENS :, ::CLASSREF, ::NAME + # TOKENS :, ::CLASSREF, ::NAME when ':' if la1 == ':' before = scn.pos @@ -518,7 +532,7 @@ class Puppet::Pops::Parser::Lexer2 when '$' if value = scn.scan(PATTERN_DOLLAR_VAR) - emit_completed([:VARIABLE, value, scn.pos - before], before) + emit_completed([:VARIABLE, value[1..-1], scn.pos - before], before) else # consume the $ and let higher layer complain about the error instead of getting a syntax error emit(TOKEN_VARIABLE_EMPTY, before) @@ -545,7 +559,7 @@ class Puppet::Pops::Parser::Lexer2 end when 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'w', 'x', 'y', 'z' + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' value = scn.scan(PATTERN_NAME) if value emit_completed(KEYWORDS[value] || [:NAME, value, scn.pos - before], before) @@ -556,7 +570,7 @@ class Puppet::Pops::Parser::Lexer2 end when 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'W', 'X', 'Y', 'Z' + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' value = scn.scan(PATTERN_CLASSREF) if value emit_completed([:CLASSREF, value, scn.pos - before], before) @@ -569,7 +583,7 @@ class Puppet::Pops::Parser::Lexer2 when "\n" # If heredoc_cont is in effect there are heredoc text lines to skip over # otherwise just skip the newline. - # + # if ctx[:newline_jump] scn.pos = ctx[:newline_jump] ctx[:newline_jump] = nil @@ -619,27 +633,6 @@ class Puppet::Pops::Parser::Lexer2 @token_queue << emitted_token end - # Enqueues lexed tokens until either end of input, or the given brace_count is reached - # - def enqueue_until brace_count - scn = @scanner - ctx = @lexing_context - queue = @token_queue - - scn.skip(PATTERN_WS) - - until scn.eos? do - if token = lex_token - token_name = token[0] - ctx[:after] = token_name - return if token_name == :RBRACE && ctx[:brace_count] == brace_count - queue << token - else - scn.skip(PATTERN_WS) - end - end - end - # Answers after which tokens it is acceptable to lex a regular expression. # PERFORMANCE NOTE: # It may be beneficial to turn this into a hash with default value of true for missing entries. @@ -653,7 +646,7 @@ class Puppet::Pops::Parser::Lexer2 when :RPAREN, :RBRACK, :RBRACE, :RRCOLLECT, :RCOLLECT false - # Operands (that can be followed by DIV (even if illegal in grammar) + # Operands (that can be followed by DIV (even if illegal in grammar) when :NAME, :CLASSREF, :NUMBER, :STRING, :BOOLEAN, :DQPRE, :DQMID, :DQPOST, :HEREDOC, :REGEX false diff --git a/lib/puppet/pops/parser/lexer_support.rb b/lib/puppet/pops/parser/lexer_support.rb index 3cfd5288c..3c3b87bc8 100644 --- a/lib/puppet/pops/parser/lexer_support.rb +++ b/lib/puppet/pops/parser/lexer_support.rb @@ -64,6 +64,10 @@ module Puppet::Pops::Parser::LexerSupport # line and position on line information. # class TokenValue + attr_reader :token_array + attr_reader :offset + attr_reader :locator + def initialize(token_array, offset, locator) @token_array = token_array @offset = offset @@ -83,6 +87,8 @@ module Puppet::Pops::Parser::LexerSupport @token_array[2] when :locator @locator + when :offset + @offset else nil end diff --git a/lib/puppet/pops/parser/parser_support.rb b/lib/puppet/pops/parser/parser_support.rb index a22de7161..cd50cb38a 100644 --- a/lib/puppet/pops/parser/parser_support.rb +++ b/lib/puppet/pops/parser/parser_support.rb @@ -42,7 +42,7 @@ class Puppet::Pops::Parser::Parser # before evaluation-time. # def classname(name) - [@lexer.namespace, name].join("::").sub(/^::/, '') + [namespace, name].join("::").sub(/^::/, '') end # Reinitializes variables (i.e. creates a new lexer instance @@ -52,11 +52,11 @@ class Puppet::Pops::Parser::Parser end # Raises a Parse error. - def error(message, options = {}) + def error(value, message, options = {}) except = Puppet::ParseError.new(message) - except.line = options[:line] || @lexer.line - except.file = options[:file] || @lexer.file - except.pos = options[:pos] || @lexer.pos + except.line = options[:line] || value[:line] + except.file = options[:file] || value[:file] # @lexer.file + except.pos = options[:pos] || value[:pos] # @lexer.pos raise except end @@ -84,7 +84,8 @@ class Puppet::Pops::Parser::Parser # @return [void] # def initvars - @lexer = Puppet::Pops::Parser::Lexer.new + @lexer = Puppet::Pops::Parser::Lexer2.new + @namestack = [] end # This is a callback from the generated grammar (when an error occurs while parsing) @@ -93,11 +94,11 @@ class Puppet::Pops::Parser::Parser # def on_error(token,value,stack) if token == 0 # denotes end of file - value = 'end of file' + value_at = 'end of file' else - value = "'#{value[:value]}'" + value_at = "'#{value[:value]}'" end - error = "Syntax error at #{value}" + error = "Syntax error at #{value_at}" # The 'expected' is only of value at end of input, otherwise any parse error involving a # start of a pair will be reported as expecting the close of the pair - e.g. "$x.each |$x {", would @@ -105,14 +106,19 @@ class Puppet::Pops::Parser::Parser # Real "expected" tokens are very difficult to compute (would require parsing of racc output data). Output of the stack # could help, but can require extensive backtracking and produce many options. # - if token == 0 && brace = @lexer.expected - error += "; expected '#{brace}'" - end + # The lexer should handle the "expected instead of end of file for strings, and interpolation", other exåectancies + # must be handled by the grammar. The lexer may have enqueued tokens far ahead - the lexer's opinion about this + # is not trustworthy. + # +# if token == 0 && brace = @lexer.expected +# error += "; expected '#{brace}'" +# end except = Puppet::ParseError.new(error) - except.line = @lexer.line - except.file = @lexer.file if @lexer.file - except.pos = @lexer.pos + except.line = value[:line] unless token == 0 + path = value[:file] + except.file = path if path.is_a?(String) && !path.empty? + except.pos = value[:pos] unless token == 0 raise except end @@ -163,6 +169,18 @@ class Puppet::Pops::Parser::Parser o end + def namespace + @namestack.join('::') + end + + def namestack(name) + @namestack << name + end + + def namepop() + @namestack.pop + end + # Transforms an array of expressions containing literal name expressions to calls if followed by an # expression, or expression list # diff --git a/lib/puppet/pops/visitor.rb b/lib/puppet/pops/visitor.rb index 460fa80d6..baae887f7 100644 --- a/lib/puppet/pops/visitor.rb +++ b/lib/puppet/pops/visitor.rb @@ -43,7 +43,6 @@ class Puppet::Pops::Visitor return receiver.send(method_name, thing, *args) end end - require 'byebug'; byebug raise "Visitor Error: the configured receiver (#{receiver.class}) can't handle instance of: #{thing.class}" end diff --git a/spec/unit/pops/parser/lexer2_spec.rb b/spec/unit/pops/parser/lexer2_spec.rb index e8f7ce144..1c5970a2f 100644 --- a/spec/unit/pops/parser/lexer2_spec.rb +++ b/spec/unit/pops/parser/lexer2_spec.rb @@ -230,6 +230,10 @@ describe 'Lexer2' do end end + it 'should lex assignment' do + tokens_scanned_from("$a = 10").should match_tokens2([:VARIABLE, "a"], :EQUALS, [:NUMBER, '10']) + end + # TODO: Tricky, and heredoc not supported yet # it "should not lex regexp after heredoc" do # tokens_scanned_from("1 / /./").should match_tokens2(:NUMBER, :DIV, :REGEX) @@ -296,7 +300,7 @@ describe 'Lexer2' do epp_tokens_scanned_from(code).should match_tokens2( [:RENDER_STRING, " This is "], [:RENDER_EXPR, nil], - [:VARIABLE, "$x"], + [:VARIABLE, "x"], [:RENDER_STRING, " just text\n"] ) end @@ -307,7 +311,7 @@ describe 'Lexer2' do CODE epp_tokens_scanned_from(code).should match_tokens2( [:RENDER_STRING, " This is "], - [:VARIABLE, "$x"], + [:VARIABLE, "x"], :EQUALS, [:NUMBER, "10"], [:RENDER_STRING, " just text\n"] @@ -321,7 +325,7 @@ describe 'Lexer2' do CODE epp_tokens_scanned_from(code).should match_tokens2( [:RENDER_STRING, " This is "], - [:VARIABLE, "$x"], + [:VARIABLE, "x"], :EQUALS, [:NUMBER, "10"], [:RENDER_STRING, "just text\n"] @@ -336,7 +340,7 @@ describe 'Lexer2' do CODE epp_tokens_scanned_from(code).should match_tokens2( [:RENDER_STRING, " This is "], - [:VARIABLE, "$x"], + [:VARIABLE, "x"], :EQUALS, [:NUMBER, "10"], [:RENDER_STRING, "just text\n"] @@ -350,7 +354,7 @@ describe 'Lexer2' do CODE epp_tokens_scanned_from(code).should match_tokens2( [:RENDER_STRING, " This is "], - [:VARIABLE, "$x"], + [:VARIABLE, "x"], :EQUALS, [:NUMBER, "10"], [:RENDER_STRING, "<% this is escaped epp %>\n"] diff --git a/spec/unit/pops/parser/parse_basic_expressions_spec.rb b/spec/unit/pops/parser/parse_basic_expressions_spec.rb index 74079b2e3..f5aeb2a29 100644 --- a/spec/unit/pops/parser/parse_basic_expressions_spec.rb +++ b/spec/unit/pops/parser/parse_basic_expressions_spec.rb @@ -118,11 +118,11 @@ describe "egrammar parsing basic expressions" do context "of regular expressions (parse errors)" do # Not supported in concrete syntax it "$a = /.*/ == /.*/" do - expect { parse("$a = /.*/ == /.*/") }.to raise_error(Puppet::ParseError) + dump(parse("$a = /.*/ == /.*/")).should == "(= $a (== /.*/ /.*/))" end it "$a = /.*/ != /a.*/" do - expect { parse("$a = /.*/ != /.*/") }.to raise_error(Puppet::ParseError) + dump(parse("$a = /.*/ != /.*/")).should == "(= $a (!= /.*/ /.*/))" end end end @@ -249,8 +249,25 @@ describe "egrammar parsing basic expressions" do dump(parse("$a = \"yo${var}yo\"")).should == "(= $a (cat 'yo' (str $var) 'yo'))" end - it "should interpolate any expression in a text expression, \"${var*2}\"" do - dump(parse("$a = \"yo${var+2}yo\"")).should == "(= $a (cat 'yo' (str (+ $var 2)) 'yo'))" + it "should interpolate any expression in a text expression, \"${$var*2}\"" do + dump(parse("$a = \"yo${$var+2}yo\"")).should == "(= $a (cat 'yo' (str (+ $var 2)) 'yo'))" + end + + it "should not interpolate names as variable in expression, \"${notvar*2}\"" do + dump(parse("$a = \"yo${notvar+2}yo\"")).should == "(= $a (cat 'yo' (str (+ notvar 2)) 'yo'))" + end + + it "should interpolate name as variable in access expression, \"${var[0]}\"" do + dump(parse("$a = \"yo${var[0]}yo\"")).should == "(= $a (cat 'yo' (str (slice $var 0)) 'yo'))" + end + + it "should interpolate name as variable in method call, \"${var.foo}\"" do + dump(parse("$a = \"yo${$var.foo}yo\"")).should == "(= $a (cat 'yo' (str (call-method (. $var foo))) 'yo'))" + end + + it "should interpolate name as variable in method call, \"${var.foo}\"" do + dump(parse("$a = \"yo${var.foo}yo\"")).should == "(= $a (cat 'yo' (str (call-method (. $var foo))) 'yo'))" + dump(parse("$a = \"yo${var.foo.bar}yo\"")).should == "(= $a (cat 'yo' (str (call-method (. (call-method (. $var foo)) bar))) 'yo'))" end end end diff --git a/spec/unit/pops/parser/parse_containers_spec.rb b/spec/unit/pops/parser/parse_containers_spec.rb index a6b9f2022..28778c308 100644 --- a/spec/unit/pops/parser/parse_containers_spec.rb +++ b/spec/unit/pops/parser/parse_containers_spec.rb @@ -57,7 +57,9 @@ describe "egrammar parsing containers" do # a very confusing effect when resolving relative names, getting the global hardwired "Class" # instead of some foo::class etc. # This is allowed in 3.x. - dump(parse("class class {}")).should == "(class class ())" + expect { + dump(parse("class class {}")).should == "(class class ())" + }.to raise_error(/not a valid classname/) end it "class default {} # a class named 'default'" do @@ -72,7 +74,9 @@ describe "egrammar parsing containers" do end it "class class inherits default {} # inherits default", :broken => true do - dump(parse("class class inherits default {}")).should == "(class class (inherits default) ())" + expect { + dump(parse("class class inherits default {}")).should == "(class class (inherits default) ())" + }.to raise_error(/not a valid classname/) end it "class class inherits default {} # inherits default" do @@ -80,12 +84,16 @@ describe "egrammar parsing containers" do # this because a class is named at parse time (since class evaluation is lazy, the model must have the # full class name for nested classes - only, it gets this wrong when a class is named "class" - or at least # I think it is wrong.) - # + # + expect { dump(parse("class class inherits default {}")).should == "(class class::class (inherits default) ())" + }.to raise_error(/not a valid classname/) end it "class foo inherits class" do - dump(parse("class foo inherits class {}")).should == "(class foo (inherits class) ())" + expect { + dump(parse("class foo inherits class {}")).should == "(class foo (inherits class) ())" + }.to raise_error(/not a valid classname/) end end end @@ -119,7 +127,9 @@ describe "egrammar parsing containers" do it "define class {} # a define named 'class'" do # This is weird because Class already exists, and instantiating this define will probably not # work - dump(parse("define class {}")).should == "(define class ())" + expect { + dump(parse("define class {}")).should == "(define class ())" + }.to raise_error(/not a valid classname/) end it "define default {} # a define named 'default'" do @@ -165,7 +175,7 @@ describe "egrammar parsing containers" do end it "node wat inherits /apache.*/ {}" do - expect { parse("node wat inherits /apache.*/ {}")}.to raise_error(Puppet::ParseError) + dump(parse("node wat inherits /apache.*/ {}")).should == "(node (matches wat) (parent /apache.*/) ())" end it "node foo inherits bar {$a = 10 $b = 20}" do diff --git a/spec/unit/pops/transformer/transform_basic_expressions_spec.rb b/spec/unit/pops/transformer/transform_basic_expressions_spec.rb index 11a2c76f5..dda35002c 100644 --- a/spec/unit/pops/transformer/transform_basic_expressions_spec.rb +++ b/spec/unit/pops/transformer/transform_basic_expressions_spec.rb @@ -117,13 +117,13 @@ describe "transformation to Puppet AST for basic expressions" do end context "of regular expressions (parse errors)" do - # Not supported in concrete syntax + # Not supported in concrete syntax (until Lexer2) it "$a = /.*/ == /.*/" do - expect { parse("$a = /.*/ == /.*/") }.to raise_error(Puppet::ParseError) + astdump(parse("$a = /.*/ == /.*/")).should == "(= $a (== /.*/ /.*/))" end it "$a = /.*/ != /a.*/" do - expect { parse("$a = /.*/ != /.*/") }.to raise_error(Puppet::ParseError) + astdump(parse("$a = /.*/ != /.*/")).should == "(= $a (!= /.*/ /.*/))" end end end @@ -237,7 +237,7 @@ describe "transformation to Puppet AST for basic expressions" do end it "should interpolate any expression in a text expression, \"${var*2}\"" do - astdump(parse("$a = \"yo${var+2}yo\"")).should == "(= $a (cat 'yo' (str (+ $var 2)) 'yo'))" + astdump(parse("$a = \"yo${$var+2}yo\"")).should == "(= $a (cat 'yo' (str (+ $var 2)) 'yo'))" end end end diff --git a/spec/unit/pops/transformer/transform_calls_spec.rb b/spec/unit/pops/transformer/transform_calls_spec.rb index 969a2d7b8..735ba771e 100644 --- a/spec/unit/pops/transformer/transform_calls_spec.rb +++ b/spec/unit/pops/transformer/transform_calls_spec.rb @@ -60,7 +60,7 @@ describe "transformation to Puppet AST for function calls" do astdump(parse("$a.foo")).should == "(call-method (. $a foo))" end - it "$a.foo ||{ }" do + it "$a.foo || { }" do astdump(parse("$a.foo || { }")).should == "(call-method (. $a foo) (lambda ()))" end @@ -68,7 +68,7 @@ describe "transformation to Puppet AST for function calls" do astdump(parse("$a.foo || {[]}")).should == "(call-method (. $a foo) (lambda (block ([]))))" end - it "$a.foo {|$x| }" do + it "$a.foo |$x| { }" do astdump(parse("$a.foo |$x| { }")).should == "(call-method (. $a foo) (lambda (parameters x) ()))" end diff --git a/spec/unit/pops/transformer/transform_containers_spec.rb b/spec/unit/pops/transformer/transform_containers_spec.rb index 8c65e2bcc..c57998eed 100644 --- a/spec/unit/pops/transformer/transform_containers_spec.rb +++ b/spec/unit/pops/transformer/transform_containers_spec.rb @@ -57,7 +57,9 @@ describe "transformation to Puppet AST for containers" do # a very confusing effect when resolving relative names, getting the global hardwired "Class" # instead of some foo::class etc. # This is allowed in 3.x. + expect { astdump(parse("class class {}")).should == "(class class ())" + }.to raise_error(/is not a valid classname/) end it "class default {} # a class named 'default'" do @@ -81,11 +83,15 @@ describe "transformation to Puppet AST for containers" do # full class name for nested classes - only, it gets this wrong when a class is named "class" - or at least # I think it is wrong.) # - astdump(parse("class class inherits default {}")).should == "(class class::class (inherits default) ())" + expect { + astdump(parse("class class inherits default {}")).should == "(class class::class (inherits default) ())" + }.to raise_error(/is not a valid classname/) end it "class foo inherits class" do - astdump(parse("class foo inherits class {}")).should == "(class foo (inherits class) ())" + expect { + astdump(parse("class foo inherits class {}")).should == "(class foo (inherits class) ())" + }.to raise_error(/is not a valid classname/) end end end @@ -119,7 +125,9 @@ describe "transformation to Puppet AST for containers" do it "define class {} # a define named 'class'" do # This is weird because Class already exists, and instantiating this define will probably not # work - astdump(parse("define class {}")).should == "(define class ())" + expect { + astdump(parse("define class {}")).should == "(define class ())" + }.to raise_error(/is not a valid classname/) end it "define default {} # a define named 'default'" do @@ -171,7 +179,7 @@ describe "transformation to Puppet AST for containers" do end it "node wat inherits /apache.*/ {}" do - expect { parse("node wat inherits /apache.*/ {}")}.to raise_error(Puppet::ParseError) + astdump(parse("node wat inherits /apache.*/ {}")).should == "(node (matches 'wat') (parent /apache.*/) ())" end it "node foo inherits bar {$a = 10 $b = 20}" do From 7770c2312dacba13121a7a15dd30fd05955171d8 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Wed, 23 Oct 2013 22:29:33 +0200 Subject: [PATCH 039/800] (maint) Add test examples for nested naming of classes There were no tests for naming of nested classes (the naming needs to take place during parsing). --- spec/unit/pops/parser/parse_containers_spec.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/spec/unit/pops/parser/parse_containers_spec.rb b/spec/unit/pops/parser/parse_containers_spec.rb index 28778c308..49ae0be9a 100644 --- a/spec/unit/pops/parser/parse_containers_spec.rb +++ b/spec/unit/pops/parser/parse_containers_spec.rb @@ -23,6 +23,10 @@ describe "egrammar parsing containers" do dump(parse("class foo {}")).should == "(class foo ())" end + it "class foo { class bar {} }" do + dump(parse("class foo { class bar {}}")).should == "(class foo (block (class foo::bar ())))" + end + it "class foo::bar {}" do dump(parse("class foo::bar {}")).should == "(class foo::bar ())" end @@ -103,6 +107,15 @@ describe "egrammar parsing containers" do dump(parse("define foo {}")).should == "(define foo ())" end + it "class foo { define bar {}}" do + dump(parse("class foo {define bar {}}")).should == "(class foo (block (define foo::bar ())))" + end + + it "define foo { define bar {}}" do + # This is illegal, but handled as part of validation + dump(parse("define foo { define bar {}}")).should == "(define foo (block (define bar ())))" + end + it "define foo::bar {}" do dump(parse("define foo::bar {}")).should == "(define foo::bar ())" end From 37650695ad5a749c5c6511262daeefcae69dfdcc Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 24 Oct 2013 00:31:23 +0200 Subject: [PATCH 040/800] (maint) Fix parsing hostnames containing '.' as sequence instead of name There was a problem with NAME no longer allowing '.' in names (it is only supported to enable a node to use a node match based on NAME. This changes the parser to parse a node name such as kermit.example.com as a sequence of names separated by dot. --- lib/puppet/pops/model/factory.rb | 14 + lib/puppet/pops/parser/egrammar.ra | 30 +- lib/puppet/pops/parser/eparser.rb | 1237 +++++++++-------- .../unit/pops/parser/parse_containers_spec.rb | 22 +- 4 files changed, 669 insertions(+), 634 deletions(-) diff --git a/lib/puppet/pops/model/factory.rb b/lib/puppet/pops/model/factory.rb index 0f44bce7b..4d9dcddaf 100644 --- a/lib/puppet/pops/model/factory.rb +++ b/lib/puppet/pops/model/factory.rb @@ -892,4 +892,18 @@ class Puppet::Pops::Model::Factory build(o, *args) end end + + def self.concat(*args) + new(args.map do |e| + e = e.current if e.is_a?(self) + case e + when Model::LiteralString + e.value + when String + e + else + raise ArgumentError, "can only concatenate strings, got #{e.class}" + end + end.join('')) + end end diff --git a/lib/puppet/pops/parser/egrammar.ra b/lib/puppet/pops/parser/egrammar.ra index da84502d5..d161d33d7 100644 --- a/lib/puppet/pops/parser/egrammar.ra +++ b/lib/puppet/pops/parser/egrammar.ra @@ -497,31 +497,22 @@ definition_expression @lexer.indefine = false end } -# | DEFINE classname parameter_list LBRACE RBRACE { -# result = Factory.DEFINITION(classname(val[1][:value]), val[2], nil) -# loc result, val[0], val[4] -# @lexer.indefine = false -# } #---HOSTCLASS -# ORIGINAL COMMENT: Our class gets defined in the parent namespace, not our own. -# WAT ??! This is way odd; should get its complete name, classnames do not nest -# Seems like the call to classname makes use of the name scope -# (This is uneccesary, since the parent name is known when evaluating) # # Produces Model::HostClassDefinition # hostclass_expression : CLASS stacked_classname parameter_list classparent LBRACE opt_statements RBRACE { + # Remove this class' name from the namestack as all nested classes have been parsed namepop result = Factory.HOSTCLASS(classname(val[1][:value]), val[2], token_text(val[3]), val[5]) loc result, val[0], val[6] } -# | CLASS classname parameter_list classparent LBRACE RBRACE { -# result = Factory.HOSTCLASS(classname(val[1][:value]), val[2], token_text(val[3]), nil) -# loc result, val[0], val[5] -# } + # Record the classname so nested classes gets a fully qualified name at parse-time + # This is a separate rule since racc does not support intermediate actions. + # stacked_classname : classname { namestack(val[0][:value]) ; result = val[0] } @@ -569,18 +560,23 @@ node_definition_expression # Produces a LiteralExpression (string, :default, or regexp) # String with interpolation is validated for better error message hostname - : NAME { result = Factory.fqn(val[0][:value]); loc result, val[0] } - | quotedtext { result = val[0] } + : dotted_name { result = val[0]; loc result, val[0] } + | quotedtext { result = val[0] } | DEFAULT { result = Factory.literal(:default); loc result, val[0] } | regex + dotted_name + : NAME { result = Factory.literal(val[0][:value]); loc result, val[0] } + | dotted_name DOT NAME { result = Factory.concat(val[0], '.', val[2][:value]); loc result, val[0], val[2] } + # Produces Expression, since hostname is an Expression nodeparent : nil | INHERITS hostname { result = val[1] } -#---NAMES AND PARAMTERS COMMON TO SEVERAL RULES -# String result +#---NAMES AND PARAMETERS COMMON TO SEVERAL RULES +# Produces String +# classname : NAME { result = val[0] } | CLASS { error val[0], "'class' is not a valid classname" } diff --git a/lib/puppet/pops/parser/eparser.rb b/lib/puppet/pops/parser/eparser.rb index caa157b3d..b1e7b415a 100644 --- a/lib/puppet/pops/parser/eparser.rb +++ b/lib/puppet/pops/parser/eparser.rb @@ -20,7 +20,7 @@ module Puppet module Parser class Parser < Racc::Parser -module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 709) +module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 705) # Make emacs happy # Local Variables: @@ -30,26 +30,26 @@ module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 709) ##### State transition tables begin ### clist = [ -'71,-129,208,235,-127,236,235,113,244,90,91,87,82,94,318,98,268,93,245', -'-197,83,85,84,86,-206,296,282,53,55,53,55,53,55,226,117,208,227,205', -'116,97,53,55,-129,89,88,-127,218,75,76,78,77,80,81,234,73,74,53,55,235', -'246,-197,72,298,71,122,-206,122,124,57,124,79,92,90,91,87,82,94,57,98', -'313,93,312,71,83,85,84,86,62,71,62,117,62,218,122,116,94,124,98,221', -'93,62,94,97,98,225,93,89,88,300,224,75,76,78,77,80,81,62,73,74,53,55', -'97,223,285,72,117,71,97,313,116,312,284,327,79,92,90,91,87,82,94,117', -'98,281,93,116,71,83,85,84,86,230,229,244,117,53,55,122,116,94,124,98', -'130,93,117,195,97,305,116,306,89,88,307,208,75,76,78,77,80,81,62,73', -'74,171,310,97,53,55,72,242,71,314,57,290,316,228,267,79,92,90,91,87', -'82,94,242,98,244,93,323,324,83,85,84,86,67,69,68,70,266,266,66,66,136', -'57,106,260,334,259,204,97,118,258,337,89,88,106,107,75,76,78,77,80,81', -'244,73,74,106,341,316,343,344,72,345,71,346,103,348,349,350,242,79,92', -'90,91,87,82,94,240,98,66,93,63,357,83,85,84,86,358,359,360,,,,,,,,,', -',,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,95,,,,,79,92', -'90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75', -'76,78,77,80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93', -',,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74', -',,,,,72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,', -',,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,,,,,,79', +'71,209,227,237,113,53,55,226,246,90,91,87,82,94,219,98,270,93,247,-127', +'83,85,84,86,284,53,55,53,55,53,55,117,293,209,228,116,206,229,-199,97', +'53,55,126,89,88,124,205,75,76,78,77,80,81,-208,73,74,53,55,-129,248', +'-127,72,57,71,126,62,57,124,53,55,79,92,90,91,87,82,94,57,98,-199,93', +'299,237,83,85,84,86,62,117,62,321,290,116,126,-208,222,124,117,236,-129', +'62,116,97,237,330,126,89,88,124,301,75,76,78,77,80,81,62,73,74,224,287', +'117,53,55,72,116,71,71,62,131,316,303,315,79,92,90,91,87,82,94,94,98', +'98,93,93,71,83,85,84,86,117,117,232,231,116,116,286,283,94,57,98,316', +'93,315,244,97,97,219,196,89,88,308,309,75,76,78,77,80,81,310,73,74,209', +'172,97,313,225,72,317,71,67,69,68,70,319,230,79,92,90,91,87,82,94,242', +'98,269,93,71,244,83,85,84,86,246,326,327,268,268,106,66,94,137,98,262', +'93,261,337,260,97,246,118,66,89,88,340,106,75,76,78,77,80,81,107,73', +'74,246,97,106,344,319,72,346,71,347,348,349,103,351,352,79,92,90,91', +'87,82,94,353,98,244,93,66,63,83,85,84,86,360,361,362,363,,,,,,,,,,,', +'97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,95,,,,,79,92,90', +'91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76', +'78,77,80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93,,', +'83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,', +',,,72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,', +',,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,215,,,,,79', '92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,', ',75,76,78,77,80,81,,73,74,,,,,,72,,71,,214,,,,,79,92,90,91,87,82,94', ',98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81', @@ -58,9 +58,9 @@ clist = [ ',,,,,,79,92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,', ',,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,212,,,,,79,92,90,91', '87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78', -'77,80,81,,73,74,,,,,,72,,71,,211,,,,,79,92,90,91,87,82,94,,98,,93,,', +'77,80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93,,201', '83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,', -',,,72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93,,200,83,85,84,86,,,,,,', +',,,72,,71,,,,,238,,79,92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,', ',,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,,,,,,79', '92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,', ',75,76,78,77,80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90,91,87,82,94,,98', @@ -71,29 +71,26 @@ clist = [ ',,75,76,78,77,80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90,91,87,82,94,71', '98,,93,,71,83,85,84,86,,,,94,,98,,93,94,,98,,93,,,97,,,,89,88,,,75,76', '78,77,80,81,97,73,74,53,55,97,,49,72,50,,,,,,73,74,79,92,,73,74,72,', -',13,,72,,,,173,190,184,191,48,185,193,186,182,180,,175,188,,,,,58,12', -'194,189,187,53,55,11,,49,,50,,,,62,,,,,192,174,,,,56,,13,,,,,,39,,46', -',48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,71,49,11,50,,,,,,,62,,,94', -',98,40,93,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,97,,,58,12,53', -'55,59,,49,11,50,,,,73,74,,62,,,,72,,40,,,13,56,,,,,39,,46,,48,100,,47', -'61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,', -',,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,333', -',,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58', -'12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47', -'61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13', -'56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11', -'50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45', -',,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43', +',13,,72,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,71,49', +'11,50,,,,,,,62,,,94,,98,40,93,,13,56,,,,,39,,46,,48,100,,47,61,57,,41', +'60,,97,,,58,12,53,55,59,,49,11,50,,,,73,74,,62,,,,72,,40,,,13,56,,,', +',39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,', +'62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53', +'55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57', +',41,60,44,45,,,58,12,53,55,59,,49,11,50,336,,,,,,62,,,,,,40,,,13,56', +',,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50', +',,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,', +'58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43', ',47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,', ',13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49', -'11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,', -',,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48', -'100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,', -',13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49', -'11,50,319,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60', -',,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48', -'100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,', -',13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11', +'11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44', +'45,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48', +'43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40', +',,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11', +'50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,', +'58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43', +',47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,322,,,,,,62,,,,,,40', +',,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11', '50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,', '58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100', ',47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13', @@ -113,13 +110,16 @@ clist = [ '40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49', '11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,', ',,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48', -'100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,', -',13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11', +'100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,71,,,,,40', +',,13,56,,,,94,39,98,46,93,48,100,,47,61,57,,41,60,,,,,58,12,,,59,53', +'55,11,97,49,129,50,,,,62,,,78,77,,40,,73,74,56,,13,,,72,,,39,,46,,48', +'100,79,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40', +',,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11', '50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,', -'58,12,,,59,53,55,11,,49,,50,,,,62,,,,,,40,,,170,56,,13,,,,,,39,,46,', +'58,12,,,59,53,55,11,,49,,50,,,,62,,,,,,40,,,171,56,,13,,,,,,39,,46,', '48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40', -',,13,56,,,,,173,190,184,191,48,185,193,186,182,180,,175,188,,,,,58,12', -'194,189,187,53,55,11,,49,,50,,,,62,,,,,192,174,,,,56,,13,,,,,,39,,46', +',,13,56,,,,,174,191,185,192,48,186,194,187,183,181,,176,189,,,,,58,12', +'195,190,188,53,55,11,,49,,50,,,,62,,,,,193,175,,,,56,,13,,,,,,39,,46', ',48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,', '40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49', '11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,', @@ -129,58 +129,58 @@ clist = [ '50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,', '58,12,53,55,59,,49,11,50,,,,,,,62,71,,,,,40,,,13,56,,,,94,39,98,46,93', '48,100,,47,61,57,,41,60,,,,,58,12,,,59,53,55,11,97,49,,50,,,,62,,,78', -'77,,40,,73,74,56,,13,202,,72,,,39,,46,,48,100,79,47,61,57,,41,60,,,', +'77,,40,,73,74,56,,13,203,,72,,,39,,46,,48,100,79,47,61,57,,41,60,,,', ',58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100', ',47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,71,,,,,40,,,13', '56,,,,94,39,98,46,93,48,100,,47,61,57,,41,60,,,,,58,12,,,59,53,55,11', -'97,49,,50,,,,62,,,78,77,,40,,73,74,56,,13,210,,72,,,39,,46,,48,100,79', -'47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56', -',,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,', -',,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12', -'53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61', -'57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,', -'39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,,,,', -',,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53', -'55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57', -',41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,71,,,,,40,,,13,56,,,,94', -'39,98,46,93,48,100,,47,61,57,,41,60,,,,,58,12,,,59,53,55,11,97,49,289', -'50,,,,62,75,76,78,77,,40,,73,74,56,,13,,,72,,,39,,46,,48,100,79,47,61', -'57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,71,,,,,40,,,13,56,,', -',94,39,98,46,93,48,100,,47,61,57,,41,60,,,,,58,12,,,59,53,55,11,97,49', -'128,50,,,,62,75,76,78,77,,40,,73,74,56,,13,,,72,,,39,,46,,48,100,79', -'47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56', -',,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,272', -',,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58', -'12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47', -'61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,270,,,,,,62,,,,,,40,,,13,56', -',,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50', -'264,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45', -',,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100', -',47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13', +'97,49,,50,,,,62,75,76,78,77,,40,,73,74,56,,13,211,,72,,,39,,46,,48,100', +'79,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13', +'56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50', +',,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58', +'12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47', +'61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13', '56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50', ',,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58', '12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47', -'61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,352,,,,,,62,,,,,,40,,,13,56', -',,,,39,,46,,48,43,,47,61,57,71,41,60,44,45,,,58,12,,,59,,94,11,98,,93', -',,,,62,,,71,,,40,,,,56,,,,,,94,97,98,,93,,,,,75,76,78,77,80,81,,73,74', -',,,,,72,,,97,,,,,,79,,75,76,78,77,80,81,,73,74,71,,,,,72,,,,,,,82,94', -'79,98,,93,,,83,,,,,71,,,,,,,,,,,,82,94,97,98,,93,,,83,,75,76,78,77,80', -'81,,73,74,,,,,,72,,,97,,,,,,79,,75,76,78,77,80,81,,73,74,71,,,,,72,', -',,,,,82,94,79,98,,93,,,83,,,,,71,,,,,,,,,,,,82,94,97,98,,93,,,83,,75', -'76,78,77,80,81,,73,74,,,,,,72,,,97,,,,,,79,,75,76,78,77,80,81,,73,74', -'71,,,,,72,,,,,,87,82,94,79,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,', -',,,71,,75,76,78,77,80,81,,73,74,87,82,94,,98,72,93,,,83,85,84,86,,79', -',,,,,,,,,,53,55,,97,49,,50,354,88,,,75,76,78,77,80,81,,73,74,,,13,,', -'72,,,39,,46,,48,43,79,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11', -'50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,64,41,60,44', -'45,,,58,12,53,55,59,,49,11,50,356,,,,,,62,,,,,,40,,,13,56,,,,,39,,46', +'61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,71,,,,,40,,,13,56', +',,,94,39,98,46,93,48,100,,47,61,57,,41,60,,,,,58,12,,,59,53,55,11,97', +'49,292,50,,,,62,75,76,78,77,,40,,73,74,56,,13,,,72,,,39,,46,,48,100', +'79,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13', +'56,,,,,174,191,185,192,48,186,194,187,183,181,,176,189,,,,,58,12,195', +'190,188,53,55,11,,49,,50,,,,62,,,,,193,175,,,,56,,13,,,,,,39,,46,,48', +'100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,', +',13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11', +'50,274,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44', +'45,,,58,12,53,55,59,,49,11,50,272,,,,,,62,,,,,,40,,,13,56,,,,,39,,46', ',48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,,,,,,,62,,', -',,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55', -'59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,', -'41,60,,,,,58,12,,,59,,,11,,,,254,190,253,191,62,251,193,255,249,248', -'40,250,252,,56,,,,,194,189,256,254,190,253,191,,251,193,255,249,248', -',250,252,,,192,257,,,194,189,256,254,190,253,191,,251,193,255,249,248', -',250,252,,,192,257,,,194,189,256,,,,,,,,,,,,,,,,192,257' ] +',,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59', +',49,11,50,266,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41', +'60,44,45,,,58,12,53,55,59,,49,11,50,355,,,,,,62,,,,,,40,,,13,56,,,,', +'39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,,,,', +',,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53', +'55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57', +',41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39', +',46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,357,,,,,,62', +',,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53', +'55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57', +'71,41,60,,,,,58,12,,,59,,94,11,98,,93,,,,,62,,,71,,,40,,,,56,,,,,,94', +'97,98,,93,,,,,75,76,78,77,80,81,,73,74,,,,,,72,,,97,,,,,,79,,75,76,78', +'77,80,81,,73,74,71,,,,,72,,,,,,,82,94,79,98,,93,,,83,,,,,71,,,,,,,,', +',,,82,94,97,98,,93,,,83,,75,76,78,77,80,81,,73,74,,,,,,72,,,97,,,,,', +'79,,75,76,78,77,80,81,,73,74,71,,,,,72,,,,,,,82,94,79,98,,93,,,83,,', +',,71,,,,,,,,,,,,82,94,97,98,,93,,,83,,75,76,78,77,80,81,,73,74,,,,,', +'72,,,97,,,,,,79,,75,76,78,77,80,81,,73,74,71,,,,,72,,,,,,87,82,94,79', +'98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,,,71,,75,76,78,77,80,81,,73', +'74,87,82,94,,98,72,93,,,83,85,84,86,,79,,,,,,,,,,,53,55,,97,49,,50,', +'88,,,75,76,78,77,80,81,,73,74,,,13,,,72,,,39,,46,,48,43,79,47,61,57', +'64,41,60,44,45,,,58,12,53,55,59,,49,11,50,359,,,,,,62,,,,,,40,,,13,56', +',,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50', +',,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,', +'58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100', +',47,61,57,,41,60,,,,,58,12,,,59,,,11,,,,256,191,255,192,62,253,194,257', +'251,250,40,252,254,,56,,,,,195,190,258,256,191,255,192,,253,194,257', +'251,250,,252,254,,,193,259,,,195,190,258,256,191,255,192,,253,194,257', +'251,250,,252,254,,,193,259,,,195,190,258,,,,,,,,,,,,,,,,193,259' ] racc_action_table = arr = ::Array.new(5065, nil) idx = 0 clist.each do |str| @@ -191,175 +191,174 @@ clist = [ end clist = [ -'167,182,106,209,180,167,273,43,297,167,167,167,167,167,273,167,209,167', -'183,188,167,167,167,167,187,237,218,221,221,223,223,74,74,129,43,218', -'129,106,43,167,73,73,182,167,167,180,114,167,167,167,167,167,167,143', -'167,167,186,186,143,183,188,167,241,166,221,187,223,221,74,223,167,167', -'166,166,166,166,166,73,166,270,166,270,144,166,166,166,166,221,101,223', -'100,74,119,186,100,144,186,144,120,144,73,101,166,101,127,101,166,166', -'243,127,166,166,166,166,166,166,186,166,166,47,47,144,120,220,166,281', -'165,101,310,281,310,219,281,166,166,165,165,165,165,165,185,165,216', -'165,185,99,165,165,165,165,135,135,247,46,50,50,47,46,99,47,99,50,99', -'184,96,165,261,184,263,165,165,265,266,165,165,165,165,165,165,47,165', -'165,94,269,99,227,227,165,215,164,271,50,227,272,132,208,165,165,164', -'164,164,164,164,276,164,277,164,278,279,164,164,164,164,7,7,7,7,206', -'283,137,65,63,227,202,201,295,199,105,164,44,197,304,164,164,305,38', -'164,164,164,164,164,164,177,164,164,37,313,314,316,317,164,321,172,322', -'36,328,329,332,176,164,164,172,172,172,172,172,172,172,5,172,1,347,172', -'172,172,172,351,353,355,,,,,,,,,,,,,172,,,,172,172,,,172,172,172,172', -'172,172,,172,172,,,,,,172,,10,,10,,,,,172,172,10,10,10,10,10,,10,,10', +'167,106,128,210,43,222,222,128,249,167,167,167,167,167,119,167,210,167', +'184,181,167,167,167,167,219,229,229,224,224,74,74,43,229,219,130,43', +'106,130,189,167,73,73,222,167,167,222,105,167,167,167,167,167,167,188', +'167,167,187,187,183,184,181,167,229,166,224,222,74,224,47,47,167,167', +'166,166,166,166,166,73,166,189,166,239,275,166,166,166,166,224,186,74', +'275,225,186,187,188,120,187,283,144,183,73,283,166,144,283,47,166,166', +'47,243,166,166,166,166,166,166,187,166,166,120,221,185,50,50,166,185', +'165,101,47,50,272,245,272,166,166,165,165,165,165,165,101,165,101,165', +'101,145,165,165,165,165,100,46,136,136,100,46,220,217,145,50,145,313', +'145,313,216,165,101,114,96,165,165,263,265,165,165,165,165,165,165,267', +'165,165,268,94,145,271,122,165,273,173,7,7,7,7,274,133,165,165,173,173', +'173,173,173,173,173,209,173,99,278,173,173,173,173,279,280,281,207,285', +'203,65,99,63,99,202,99,200,298,198,173,300,44,138,173,173,307,308,173', +'173,173,173,173,173,38,173,173,178,99,37,316,317,173,319,197,320,324', +'325,36,331,332,173,173,197,197,197,197,197,335,197,177,197,5,1,197,197', +'197,197,350,354,356,358,,,,,,,,,,,,197,,,,197,197,,,197,197,197,197', +'197,197,,197,197,,,,,,197,,10,,10,,,,,197,197,10,10,10,10,10,,10,,10', ',,10,10,10,10,,,,,,,,,,,,,,,,10,,,,10,10,,,10,10,10,10,10,10,,10,10', -',,,,,10,,196,,,,,,,10,10,196,196,196,196,196,,196,,196,,,196,196,196', -'196,,,,,,,,,,,,,,,,196,,,,196,196,,,196,196,196,196,196,196,,196,196', -',,,,,196,,133,,,,,,,196,196,133,133,133,133,133,,133,,133,,,133,133', -'133,133,,,,,,,,,,,,,,,,133,,,,133,133,,,133,133,133,133,133,133,,133', -'133,,,,,,133,,126,,,,,,,133,133,126,126,126,126,126,,126,,126,,,126', -'126,126,126,,,,,,,,,,,,,,,,126,,,,126,126,,,126,126,126,126,126,126', -',126,126,,,,,,126,,112,,112,,,,,126,126,112,112,112,112,112,,112,,112', -',,112,112,112,112,,,,,,,,,,,,,,,,112,,,,112,112,,,112,112,112,112,112', -'112,,112,112,,,,,,112,,111,,111,,,,,112,112,111,111,111,111,111,,111', -',111,,,111,111,111,111,,,,,,,,,,,,,,,,111,,,,111,111,,,111,111,111,111', -'111,111,,111,111,,,,,,111,,309,,,,,,,111,111,309,309,309,309,309,,309', -',309,,,309,309,309,309,,,,,,,,,,,,,,,,309,,,,309,309,,,309,309,309,309', -'309,309,,309,309,,,,,,309,,110,,110,,,,,309,309,110,110,110,110,110', -',110,,110,,,110,110,110,110,,,,,,,,,,,,,,,,110,,,,110,110,,,110,110', -'110,110,110,110,,110,110,,,,,,110,,108,,108,,,,,110,110,108,108,108', -'108,108,,108,,108,,,108,108,108,108,,,,,,,,,,,,,,,,108,,,,108,108,,', -'108,108,108,108,108,108,,108,108,,,,,,108,,102,,,,,,,108,108,102,102', -'102,102,102,,102,,102,,102,102,102,102,102,,,,,,,,,,,,,,,,102,,,,102', -'102,,,102,102,102,102,102,102,,102,102,,,,,,102,,288,,,,,,,102,102,288', -'288,288,288,288,,288,,288,,,288,288,288,288,,,,,,,,,,,,,,,,288,,,,288', -'288,,,288,288,288,288,288,288,,288,288,,,,,,288,,303,,,,,,,288,288,303', -'303,303,303,303,,303,,303,,,303,303,303,303,,,,,,,,,,,,,,,,303,,,,303', -'303,,,303,303,303,303,303,303,,303,303,,,,,,303,,302,,,,,,,303,303,302', -'302,302,302,302,,302,,302,,,302,302,302,302,,,,,,,,,,,,,,,,302,,,,302', -'302,,,302,302,302,302,302,302,,302,302,,,,,,302,,292,,,,,,,302,302,292', -'292,292,292,292,,292,,292,,,292,292,292,292,,,,,,,,,,,,,,,,292,,,,292', -'292,,,292,292,292,292,292,292,,292,292,,,,,,292,,294,,,,,,,292,292,294', -'294,294,294,294,153,294,,294,,152,294,294,294,294,,,,153,,153,,153,152', -',152,,152,,,294,,,,294,294,,,294,294,294,294,294,294,153,294,294,214', -'214,152,,214,294,214,,,,,,153,153,294,294,,152,152,153,,,214,,152,,', -',214,214,214,214,214,214,214,214,214,214,,214,214,,,,,214,214,214,214', -'214,296,296,214,,296,,296,,,,214,,,,,214,214,,,,214,,296,,,,,,296,,296', -',296,296,,296,296,296,,296,296,,,,,296,296,54,54,296,151,54,296,54,', -',,,,,296,,,151,,151,296,151,,54,296,,,,,54,,54,,54,54,,54,54,54,,54', -'54,,151,,,54,54,42,42,54,,42,54,42,,,,151,151,,54,,,,151,,54,,,42,54', -',,,,42,,42,,42,42,,42,42,42,,42,42,,,,,42,42,64,64,42,,64,42,64,,,,', -',,42,,,,,,42,,,64,42,,,,,64,,64,,64,64,,64,64,64,,64,64,64,64,,,64,64', -'285,285,64,,285,64,285,285,,,,,,64,,,,,,64,,,285,64,,,,,285,,285,,285', -'285,,285,285,285,,285,285,285,285,,,285,285,66,66,285,,66,285,66,,,', -',,,285,,,,,,285,,,66,285,,,,,66,,66,,66,66,,66,66,66,,66,66,66,66,,', -'66,66,67,67,66,,67,66,67,,,,,,,66,,,,,,66,,,67,66,,,,,67,,67,,67,67', -',67,67,67,,67,67,67,67,,,67,67,68,68,67,,68,67,68,,,,,,,67,,,,,,67,', -',68,67,,,,,68,,68,,68,68,,68,68,68,,68,68,68,68,,,68,68,69,69,68,,69', -'68,69,,,,,,,68,,,,,,68,,,69,68,,,,,69,,69,,69,69,,69,69,69,,69,69,69', -'69,,,69,69,70,70,69,,70,69,70,,,,,,,69,,,,,,69,,,70,69,,,,,70,,70,,70', -'70,,70,70,70,,70,70,70,70,,,70,70,71,71,70,,71,70,71,,,,,,,70,,,,,,70', -',,71,70,,,,,71,,71,,71,71,,71,71,71,,71,71,,,,,71,71,72,72,71,,72,71', -'72,,,,,,,71,,,,,,71,,,72,71,,,,,72,,72,,72,72,,72,72,72,,72,72,,,,,72', -'72,284,284,72,,284,72,284,,,,,,,72,,,,,,72,,,284,72,,,,,284,,284,,284', -'284,,284,284,284,,284,284,284,284,,,284,284,274,274,284,,274,284,274', -'274,,,,,,284,,,,,,284,,,274,284,,,,,274,,274,,274,274,,274,274,274,', -'274,274,,,,,274,274,75,75,274,,75,274,75,,,,,,,274,,,,,,274,,,75,274', -',,,,75,,75,,75,75,,75,75,75,,75,75,,,,,75,75,76,76,75,,76,75,76,,,,', -',,75,,,,,,75,,,76,75,,,,,76,,76,,76,76,,76,76,76,,76,76,,,,,76,76,77', -'77,76,,77,76,77,,,,,,,76,,,,,,76,,,77,76,,,,,77,,77,,77,77,,77,77,77', -',77,77,,,,,77,77,78,78,77,,78,77,78,,,,,,,77,,,,,,77,,,78,77,,,,,78', -',78,,78,78,,78,78,78,,78,78,,,,,78,78,79,79,78,,79,78,79,,,,,,,78,,', -',,,78,,,79,78,,,,,79,,79,,79,79,,79,79,79,,79,79,,,,,79,79,80,80,79', -',80,79,80,,,,,,,79,,,,,,79,,,80,79,,,,,80,,80,,80,80,,80,80,80,,80,80', -',,,,80,80,81,81,80,,81,80,81,,,,,,,80,,,,,,80,,,81,80,,,,,81,,81,,81', -'81,,81,81,81,,81,81,,,,,81,81,82,82,81,,82,81,82,,,,,,,81,,,,,,81,,', -'82,81,,,,,82,,82,,82,82,,82,82,82,,82,82,,,,,82,82,83,83,82,,83,82,83', -',,,,,,82,,,,,,82,,,83,82,,,,,83,,83,,83,83,,83,83,83,,83,83,,,,,83,83', -'84,84,83,,84,83,84,,,,,,,83,,,,,,83,,,84,83,,,,,84,,84,,84,84,,84,84', -'84,,84,84,,,,,84,84,85,85,84,,85,84,85,,,,,,,84,,,,,,84,,,85,84,,,,', -'85,,85,,85,85,,85,85,85,,85,85,,,,,85,85,86,86,85,,86,85,86,,,,,,,85', -',,,,,85,,,86,85,,,,,86,,86,,86,86,,86,86,86,,86,86,,,,,86,86,87,87,86', -',87,86,87,,,,,,,86,,,,,,86,,,87,86,,,,,87,,87,,87,87,,87,87,87,,87,87', -',,,,87,87,88,88,87,,88,87,88,,,,,,,87,,,,,,87,,,88,87,,,,,88,,88,,88', -'88,,88,88,88,,88,88,,,,,88,88,89,89,88,,89,88,89,,,,,,,88,,,,,,88,,', -'89,88,,,,,89,,89,,89,89,,89,89,89,,89,89,,,,,89,89,170,170,89,,170,89', -'170,,,,,,,89,,,,,,89,,,170,89,,,,,170,,170,,170,170,,170,170,170,,170', -'170,,,,,170,170,91,91,170,,91,170,91,,,,,,,170,,,,,,170,,,91,170,,,', -',91,,91,,91,91,,91,91,91,,91,91,,,,,91,91,92,92,91,,92,91,92,,,,,,,91', -',,,,,91,,,92,91,,,,,92,,92,,92,92,,92,92,92,,92,92,,,,,92,92,93,93,92', -',93,92,93,,,,,,,92,,,,,,92,,,93,92,,,,,93,,93,,93,93,,93,93,93,,93,93', -',,,,93,93,,,93,267,267,93,,267,,267,,,,93,,,,,,93,,,93,93,,267,,,,,', -'267,,267,,267,267,,267,267,267,,267,267,,,,,267,267,95,95,267,,95,267', -'95,,,,,,,267,,,,,,267,,,95,267,,,,,95,95,95,95,95,95,95,95,95,95,,95', -'95,,,,,95,95,95,95,95,260,260,95,,260,,260,,,,95,,,,,95,95,,,,95,,260', -',,,,,260,,260,,260,260,,260,260,260,,260,260,,,,,260,260,97,97,260,', -'97,260,97,,,,,,,260,,,,,,260,,,97,260,,,,,97,,97,,97,97,,97,97,97,,97', -'97,,,,,97,97,98,98,97,,98,97,98,,,,,,,97,,,,,,97,,,98,97,,,,,98,,98', -',98,98,,98,98,98,,98,98,,,,,98,98,246,246,98,,246,98,246,,,,,,,98,,', -',,,98,,,246,98,,,,,246,,246,,246,246,,246,246,246,,246,246,,,,,246,246', -'245,245,246,,245,246,245,,,,,,,246,,,,,,246,,,245,246,,,,,245,,245,', -'245,245,,245,245,245,,245,245,,,,,245,245,242,242,245,,242,245,242,', -',,,,,245,,,,,,245,,,242,245,,,,,242,,242,,242,242,,242,242,242,,242', -'242,,,,,242,242,41,41,242,,41,242,41,,,,,,,242,149,,,,,242,,,41,242', -',,,149,41,149,41,149,41,41,,41,41,41,,41,41,,,,,41,41,,,41,103,103,41', -'149,103,,103,,,,41,,,149,149,,41,,149,149,41,,103,103,,149,,,103,,103', -',103,103,149,103,103,103,,103,103,,,,,103,103,236,236,103,,236,103,236', -',,,,,,103,,,,,,103,,,236,103,,,,,236,,236,,236,236,,236,236,236,,236', -'236,,,,,236,236,235,235,236,,235,236,235,,,,,,,236,150,,,,,236,,,235', -'236,,,,150,235,150,235,150,235,235,,235,235,235,,235,235,,,,,235,235', -',,235,107,107,235,150,107,,107,,,,235,,,150,150,,235,,150,150,235,,107', -'107,,150,,,107,,107,,107,107,150,107,107,107,,107,107,,,,,107,107,40', -'40,107,,40,107,40,,,,,,,107,,,,,,107,,,40,107,,,,,40,,40,,40,40,,40', -'40,40,,40,40,,,,,40,40,39,39,40,,39,40,39,,,,,,,40,,,,,,40,,,39,40,', -',,,39,,39,,39,39,,39,39,39,,39,39,,,,,39,39,312,312,39,,312,39,312,', -',,,,,39,,,,,,39,,,312,39,,,,,312,,312,,312,312,,312,312,312,,312,312', -',,,,312,312,324,324,312,,324,312,324,,,,,,,312,,,,,,312,,,324,312,,', -',,324,,324,,324,324,,324,324,324,,324,324,324,324,,,324,324,113,113', -'324,,113,324,113,,,,,,,324,,,,,,324,,,113,324,,,,,113,,113,,113,113', -',113,113,113,,113,113,,,,,113,113,233,233,113,,233,113,233,,,,,,,113', -',,,,,113,,,233,113,,,,,233,,233,,233,233,,233,233,233,,233,233,,,,,233', -'233,228,228,233,,228,233,228,,,,,,,233,154,,,,,233,,,228,233,,,,154', -'228,154,228,154,228,228,,228,228,228,,228,228,,,,,228,228,,,228,224', -'224,228,154,224,224,224,,,,228,154,154,154,154,,228,,154,154,228,,224', -',,154,,,224,,224,,224,224,154,224,224,224,,224,224,,,,,224,224,13,13', -'224,,13,224,13,,,,,,,224,155,,,,,224,,,13,224,,,,155,13,155,13,155,13', -'13,,13,13,13,,13,13,,,,,13,13,,,13,49,49,13,155,49,49,49,,,,13,155,155', -'155,155,,13,,155,155,13,,49,,,155,,,49,,49,,49,49,155,49,49,49,,49,49', -',,,,49,49,213,213,49,,213,49,213,,,,,,,49,,,,,,49,,,213,49,,,,,213,', -'213,,213,213,,213,213,213,,213,213,,,,,213,213,212,212,213,,212,213', +',,,,,10,,134,,,,,,,10,10,134,134,134,134,134,,134,,134,,,134,134,134', +'134,,,,,,,,,,,,,,,,134,,,,134,134,,,134,134,134,134,134,134,,134,134', +',,,,,134,,127,,,,,,,134,134,127,127,127,127,127,,127,,127,,,127,127', +'127,127,,,,,,,,,,,,,,,,127,,,,127,127,,,127,127,127,127,127,127,,127', +'127,,,,,,127,,112,,112,,,,,127,127,112,112,112,112,112,,112,,112,,,112', +'112,112,112,,,,,,,,,,,,,,,,112,,,,112,112,,,112,112,112,112,112,112', +',112,112,,,,,,112,,111,,111,,,,,112,112,111,111,111,111,111,,111,,111', +',,111,111,111,111,,,,,,,,,,,,,,,,111,,,,111,111,,,111,111,111,111,111', +'111,,111,111,,,,,,111,,110,,110,,,,,111,111,110,110,110,110,110,,110', +',110,,,110,110,110,110,,,,,,,,,,,,,,,,110,,,,110,110,,,110,110,110,110', +'110,110,,110,110,,,,,,110,,312,,,,,,,110,110,312,312,312,312,312,,312', +',312,,,312,312,312,312,,,,,,,,,,,,,,,,312,,,,312,312,,,312,312,312,312', +'312,312,,312,312,,,,,,312,,108,,108,,,,,312,312,108,108,108,108,108', +',108,,108,,,108,108,108,108,,,,,,,,,,,,,,,,108,,,,108,108,,,108,108', +'108,108,108,108,,108,108,,,,,,108,,102,,,,,,,108,108,102,102,102,102', +'102,,102,,102,,102,102,102,102,102,,,,,,,,,,,,,,,,102,,,,102,102,,,102', +'102,102,102,102,102,,102,102,,,,,,102,,168,,,,,168,,102,102,168,168', +'168,168,168,,168,,168,,,168,168,168,168,,,,,,,,,,,,,,,,168,,,,168,168', +',,168,168,168,168,168,168,,168,168,,,,,,168,,291,,,,,,,168,168,291,291', +'291,291,291,,291,,291,,,291,291,291,291,,,,,,,,,,,,,,,,291,,,,291,291', +',,291,291,291,291,291,291,,291,291,,,,,,291,,306,,,,,,,291,291,306,306', +'306,306,306,,306,,306,,,306,306,306,306,,,,,,,,,,,,,,,,306,,,,306,306', +',,306,306,306,306,306,306,,306,306,,,,,,306,,305,,,,,,,306,306,305,305', +'305,305,305,,305,,305,,,305,305,305,305,,,,,,,,,,,,,,,,305,,,,305,305', +',,305,305,305,305,305,305,,305,305,,,,,,305,,295,,,,,,,305,305,295,295', +'295,295,295,,295,,295,,,295,295,295,295,,,,,,,,,,,,,,,,295,,,,295,295', +',,295,295,295,295,295,295,,295,295,,,,,,295,,297,,,,,,,295,295,297,297', +'297,297,297,154,297,,297,,153,297,297,297,297,,,,154,,154,,154,153,', +'153,,153,,,297,,,,297,297,,,297,297,297,297,297,297,154,297,297,91,91', +'153,,91,297,91,,,,,,154,154,297,297,,153,153,154,,,91,,153,,,,91,,91', +',91,91,,91,91,91,,91,91,,,,,91,91,299,299,91,152,299,91,299,,,,,,,91', +',,152,,152,91,152,,299,91,,,,,299,,299,,299,299,,299,299,299,,299,299', +',152,,,299,299,54,54,299,,54,299,54,,,,152,152,,299,,,,152,,299,,,54', +'299,,,,,54,,54,,54,54,,54,54,54,,54,54,,,,,54,54,42,42,54,,42,54,42', +',,,,,,54,,,,,,54,,,42,54,,,,,42,,42,,42,42,,42,42,42,,42,42,,,,,42,42', +'64,64,42,,64,42,64,,,,,,,42,,,,,,42,,,64,42,,,,,64,,64,,64,64,,64,64', +'64,,64,64,64,64,,,64,64,287,287,64,,287,64,287,287,,,,,,64,,,,,,64,', +',287,64,,,,,287,,287,,287,287,,287,287,287,,287,287,287,287,,,287,287', +'66,66,287,,66,287,66,,,,,,,287,,,,,,287,,,66,287,,,,,66,,66,,66,66,', +'66,66,66,,66,66,66,66,,,66,66,67,67,66,,67,66,67,,,,,,,66,,,,,,66,,', +'67,66,,,,,67,,67,,67,67,,67,67,67,,67,67,67,67,,,67,67,68,68,67,,68', +'67,68,,,,,,,67,,,,,,67,,,68,67,,,,,68,,68,,68,68,,68,68,68,,68,68,68', +'68,,,68,68,69,69,68,,69,68,69,,,,,,,68,,,,,,68,,,69,68,,,,,69,,69,,69', +'69,,69,69,69,,69,69,69,69,,,69,69,70,70,69,,70,69,70,,,,,,,69,,,,,,69', +',,70,69,,,,,70,,70,,70,70,,70,70,70,,70,70,70,70,,,70,70,71,71,70,,71', +'70,71,,,,,,,70,,,,,,70,,,71,70,,,,,71,,71,,71,71,,71,71,71,,71,71,,', +',,71,71,72,72,71,,72,71,72,,,,,,,71,,,,,,71,,,72,71,,,,,72,,72,,72,72', +',72,72,72,,72,72,,,,,72,72,286,286,72,,286,72,286,,,,,,,72,,,,,,72,', +',286,72,,,,,286,,286,,286,286,,286,286,286,,286,286,286,286,,,286,286', +'276,276,286,,276,286,276,276,,,,,,286,,,,,,286,,,276,286,,,,,276,,276', +',276,276,,276,276,276,,276,276,,,,,276,276,75,75,276,,75,276,75,,,,', +',,276,,,,,,276,,,75,276,,,,,75,,75,,75,75,,75,75,75,,75,75,,,,,75,75', +'76,76,75,,76,75,76,,,,,,,75,,,,,,75,,,76,75,,,,,76,,76,,76,76,,76,76', +'76,,76,76,,,,,76,76,77,77,76,,77,76,77,,,,,,,76,,,,,,76,,,77,76,,,,', +'77,,77,,77,77,,77,77,77,,77,77,,,,,77,77,78,78,77,,78,77,78,,,,,,,77', +',,,,,77,,,78,77,,,,,78,,78,,78,78,,78,78,78,,78,78,,,,,78,78,79,79,78', +',79,78,79,,,,,,,78,,,,,,78,,,79,78,,,,,79,,79,,79,79,,79,79,79,,79,79', +',,,,79,79,80,80,79,,80,79,80,,,,,,,79,,,,,,79,,,80,79,,,,,80,,80,,80', +'80,,80,80,80,,80,80,,,,,80,80,81,81,80,,81,80,81,,,,,,,80,,,,,,80,,', +'81,80,,,,,81,,81,,81,81,,81,81,81,,81,81,,,,,81,81,82,82,81,,82,81,82', +',,,,,,81,,,,,,81,,,82,81,,,,,82,,82,,82,82,,82,82,82,,82,82,,,,,82,82', +'83,83,82,,83,82,83,,,,,,,82,,,,,,82,,,83,82,,,,,83,,83,,83,83,,83,83', +'83,,83,83,,,,,83,83,84,84,83,,84,83,84,,,,,,,83,,,,,,83,,,84,83,,,,', +'84,,84,,84,84,,84,84,84,,84,84,,,,,84,84,85,85,84,,85,84,85,,,,,,,84', +',,,,,84,,,85,84,,,,,85,,85,,85,85,,85,85,85,,85,85,,,,,85,85,86,86,85', +',86,85,86,,,,,,,85,,,,,,85,,,86,85,,,,,86,,86,,86,86,,86,86,86,,86,86', +',,,,86,86,87,87,86,,87,86,87,,,,,,,86,,,,,,86,,,87,86,,,,,87,,87,,87', +'87,,87,87,87,,87,87,,,,,87,87,88,88,87,,88,87,88,,,,,,,87,,,,,,87,,', +'88,87,,,,,88,,88,,88,88,,88,88,88,,88,88,,,,,88,88,89,89,88,,89,88,89', +',,,,,,88,,,,,,88,,,89,88,,,,,89,,89,,89,89,,89,89,89,,89,89,,,,,89,89', +'90,90,89,,90,89,90,,,,,,,89,150,,,,,89,,,90,89,,,,150,90,150,90,150', +'90,90,,90,90,90,,90,90,,,,,90,90,,,90,49,49,90,150,49,49,49,,,,90,,', +'150,150,,90,,150,150,90,,49,,,150,,,49,,49,,49,49,150,49,49,49,,49,49', +',,,,49,49,92,92,49,,92,49,92,,,,,,,49,,,,,,49,,,92,49,,,,,92,,92,,92', +'92,,92,92,92,,92,92,,,,,92,92,93,93,92,,93,92,93,,,,,,,92,,,,,,92,,', +'93,92,,,,,93,,93,,93,93,,93,93,93,,93,93,,,,,93,93,,,93,269,269,93,', +'269,,269,,,,93,,,,,,93,,,93,93,,269,,,,,,269,,269,,269,269,,269,269', +'269,,269,269,,,,,269,269,95,95,269,,95,269,95,,,,,,,269,,,,,,269,,,95', +'269,,,,,95,95,95,95,95,95,95,95,95,95,,95,95,,,,,95,95,95,95,95,262', +'262,95,,262,,262,,,,95,,,,,95,95,,,,95,,262,,,,,,262,,262,,262,262,', +'262,262,262,,262,262,,,,,262,262,97,97,262,,97,262,97,,,,,,,262,,,,', +',262,,,97,262,,,,,97,,97,,97,97,,97,97,97,,97,97,,,,,97,97,98,98,97', +',98,97,98,,,,,,,97,,,,,,97,,,98,97,,,,,98,,98,,98,98,,98,98,98,,98,98', +',,,,98,98,248,248,98,,248,98,248,,,,,,,98,,,,,,98,,,248,98,,,,,248,', +'248,,248,248,,248,248,248,,248,248,,,,,248,248,41,41,248,,41,248,41', +',,,,,,248,,,,,,248,,,41,248,,,,,41,,41,,41,41,,41,41,41,,41,41,,,,,41', +'41,244,244,41,,244,41,244,,,,,,,41,,,,,,41,,,244,41,,,,,244,,244,,244', +'244,,244,244,244,,244,244,,,,,244,244,40,40,244,,40,244,40,,,,,,,244', +'151,,,,,244,,,40,244,,,,151,40,151,40,151,40,40,,40,40,40,,40,40,,,', +',40,40,,,40,103,103,40,151,103,,103,,,,40,,,151,151,,40,,151,151,40', +',103,103,,151,,,103,,103,,103,103,151,103,103,103,,103,103,,,,,103,103', +'238,238,103,,238,103,238,,,,,,,103,,,,,,103,,,238,103,,,,,238,,238,', +'238,238,,238,238,238,,238,238,,,,,238,238,237,237,238,,237,238,237,', +',,,,,238,155,,,,,238,,,237,238,,,,155,237,155,237,155,237,237,,237,237', +'237,,237,237,,,,,237,237,,,237,107,107,237,155,107,,107,,,,237,155,155', +'155,155,,237,,155,155,237,,107,107,,155,,,107,,107,,107,107,155,107', +'107,107,,107,107,,,,,107,107,39,39,107,,39,107,39,,,,,,,107,,,,,,107', +',,39,107,,,,,39,,39,,39,39,,39,39,39,,39,39,,,,,39,39,315,315,39,,315', +'39,315,,,,,,,39,,,,,,39,,,315,39,,,,,315,,315,,315,315,,315,315,315', +',315,315,,,,,315,315,327,327,315,,327,315,327,,,,,,,315,,,,,,315,,,327', +'315,,,,,327,,327,,327,327,,327,327,327,,327,327,327,327,,,327,327,13', +'13,327,,13,327,13,,,,,,,327,,,,,,327,,,13,327,,,,,13,,13,,13,13,,13', +'13,13,,13,13,,,,,13,13,113,113,13,,113,13,113,,,,,,,13,,,,,,13,,,113', +'13,,,,,113,,113,,113,113,,113,113,113,,113,113,,,,,113,113,235,235,113', +',235,113,235,,,,,,,113,,,,,,113,,,235,113,,,,,235,,235,,235,235,,235', +'235,235,,235,235,,,,,235,235,230,230,235,,230,235,230,,,,,,,235,156', +',,,,235,,,230,235,,,,156,230,156,230,156,230,230,,230,230,230,,230,230', +',,,,230,230,,,230,226,226,230,156,226,226,226,,,,230,156,156,156,156', +',230,,156,156,230,,226,,,156,,,226,,226,,226,226,156,226,226,226,,226', +'226,,,,,226,226,215,215,226,,215,226,215,,,,,,,226,,,,,,226,,,215,226', +',,,,215,215,215,215,215,215,215,215,215,215,,215,215,,,,,215,215,215', +'215,215,12,12,215,,12,,12,,,,215,,,,,215,215,,,,215,,12,,,,,,12,,12', +',12,12,,12,12,12,,12,12,,,,,12,12,214,214,12,,214,12,214,,,,,,,12,,', +',,,12,,,214,12,,,,,214,,214,,214,214,,214,214,214,,214,214,,,,,214,214', +'213,213,214,,213,214,213,213,,,,,,214,,,,,,214,,,213,214,,,,,213,,213', +',213,213,,213,213,213,,213,213,213,213,,,213,213,212,212,213,,212,213', '212,212,,,,,,213,,,,,,213,,,212,213,,,,,212,,212,,212,212,,212,212,212', -',212,212,212,212,,,212,212,12,12,212,,12,212,12,,,,,,,212,,,,,,212,', -',12,212,,,,,12,,12,,12,12,,12,12,12,,12,12,,,,,12,12,211,211,12,,211', -'12,211,211,,,,,,12,,,,,,12,,,211,12,,,,,211,,211,,211,211,,211,211,211', -',211,211,211,211,,,211,211,204,204,211,,204,211,204,204,,,,,,211,,,', -',,211,,,204,211,,,,,204,,204,,204,204,,204,204,204,,204,204,204,204', -',,204,204,11,11,204,,11,204,11,,,,,,,204,,,,,,204,,,11,204,,,,,11,,11', -',11,11,,11,11,11,,11,11,,,,,11,11,175,175,11,,175,11,175,,,,,,,11,,', -',,,11,,,175,11,,,,,175,,175,,175,175,,175,175,175,,175,175,,,,,175,175', -'174,174,175,,174,175,174,,,,,,,175,,,,,,175,,,174,175,,,,,174,,174,', -'174,174,,174,174,174,,174,174,,,,,174,174,173,173,174,,173,174,173,', -',,,,,174,,,,,,174,,,173,174,,,,,173,,173,,173,173,,173,173,173,,173', -'173,,,,,173,173,341,341,173,,341,173,341,341,,,,,,173,,,,,,173,,,341', -'173,,,,,341,,341,,341,341,,341,341,341,156,341,341,341,341,,,341,341', -',,341,,156,341,156,,156,,,,,341,,,157,,,341,,,,341,,,,,,157,156,157', -',157,,,,,156,156,156,156,156,156,,156,156,,,,,,156,,,157,,,,,,156,,157', -'157,157,157,157,157,,157,157,158,,,,,157,,,,,,,158,158,157,158,,158', -',,158,,,,,159,,,,,,,,,,,,159,159,158,159,,159,,,159,,158,158,158,158', -'158,158,,158,158,,,,,,158,,,159,,,,,,158,,159,159,159,159,159,159,,159', -'159,160,,,,,159,,,,,,,160,160,159,160,,160,,,160,,,,,161,,,,,,,,,,,', -'161,161,160,161,,161,,,161,,160,160,160,160,160,160,,160,160,,,,,,160', -',,161,,,,,,160,,161,161,161,161,161,161,,161,161,162,,,,,161,,,,,,162', -'162,162,161,162,,162,,,162,162,162,162,,,,,,,,,,,,,,,,162,,,,,,163,', -'162,162,162,162,162,162,,162,162,163,163,163,,163,162,163,,,163,163', -'163,163,,162,,,,,,,,,,,343,343,,163,343,,343,343,163,,,163,163,163,163', -'163,163,,163,163,,,343,,,163,,,343,,343,,343,343,163,343,343,343,,343', -'343,343,343,,,343,343,4,4,343,,4,343,4,,,,,,,343,,,,,,343,,,4,343,,', -',,4,,4,,4,4,,4,4,4,4,4,4,4,4,,,4,4,344,344,4,,344,4,344,344,,,,,,4,', -',,,,4,,,344,4,,,,,344,,344,,344,344,,344,344,344,,344,344,344,344,,', -'344,344,0,0,344,,0,344,0,,,,,,,344,,,,,,344,,,0,344,,,,,0,,0,,0,0,,0', -'0,0,,0,0,0,0,,,0,0,90,90,0,,90,0,90,,,,,,,0,,,,,,0,,,90,0,,,,,90,,90', -',90,90,,90,90,90,,90,90,,,,,90,90,,,90,,,90,,,,244,244,244,244,90,244', -'244,244,244,244,90,244,244,,90,,,,,244,244,244,239,239,239,239,,239', -'239,239,239,239,,239,239,,,244,244,,,239,239,239,195,195,195,195,,195', -'195,195,195,195,,195,195,,,239,239,,,195,195,195,,,,,,,,,,,,,,,,195', -'195' ] +',212,212,212,212,,,212,212,11,11,212,,11,212,11,,,,,,,212,,,,,,212,', +',11,212,,,,,11,,11,,11,11,,11,11,11,,11,11,,,,,11,11,205,205,11,,205', +'11,205,205,,,,,,11,,,,,,11,,,205,11,,,,,205,,205,,205,205,,205,205,205', +',205,205,205,205,,,205,205,344,344,205,,344,205,344,344,,,,,,205,,,', +',,205,,,344,205,,,,,344,,344,,344,344,,344,344,344,,344,344,344,344', +',,344,344,176,176,344,,176,344,176,,,,,,,344,,,,,,344,,,176,344,,,,', +'176,,176,,176,176,,176,176,176,,176,176,,,,,176,176,175,175,176,,175', +'176,175,,,,,,,176,,,,,,176,,,175,176,,,,,175,,175,,175,175,,175,175', +'175,,175,175,,,,,175,175,174,174,175,,174,175,174,,,,,,,175,,,,,,175', +',,174,175,,,,,174,,174,,174,174,,174,174,174,,174,174,,,,,174,174,346', +'346,174,,346,174,346,346,,,,,,174,,,,,,174,,,346,174,,,,,346,,346,,346', +'346,,346,346,346,,346,346,346,346,,,346,346,171,171,346,,171,346,171', +',,,,,,346,,,,,,346,,,171,346,,,,,171,,171,,171,171,,171,171,171,157', +'171,171,,,,,171,171,,,171,,157,171,157,,157,,,,,171,,,158,,,171,,,,171', +',,,,,158,157,158,,158,,,,,157,157,157,157,157,157,,157,157,,,,,,157', +',,158,,,,,,157,,158,158,158,158,158,158,,158,158,159,,,,,158,,,,,,,159', +'159,158,159,,159,,,159,,,,,160,,,,,,,,,,,,160,160,159,160,,160,,,160', +',159,159,159,159,159,159,,159,159,,,,,,159,,,160,,,,,,159,,160,160,160', +'160,160,160,,160,160,161,,,,,160,,,,,,,161,161,160,161,,161,,,161,,', +',,162,,,,,,,,,,,,162,162,161,162,,162,,,162,,161,161,161,161,161,161', +',161,161,,,,,,161,,,162,,,,,,161,,162,162,162,162,162,162,,162,162,163', +',,,,162,,,,,,163,163,163,162,163,,163,,,163,163,163,163,,,,,,,,,,,,', +',,,163,,,,,,164,,163,163,163,163,163,163,,163,163,164,164,164,,164,163', +'164,,,164,164,164,164,,163,,,,,,,,,,,4,4,,164,4,,4,,164,,,164,164,164', +'164,164,164,,164,164,,,4,,,164,,,4,,4,,4,4,164,4,4,4,4,4,4,4,4,,,4,4', +'347,347,4,,347,4,347,347,,,,,,4,,,,,,4,,,347,4,,,,,347,,347,,347,347', +',347,347,347,,347,347,347,347,,,347,347,0,0,347,,0,347,0,,,,,,,347,', +',,,,347,,,0,347,,,,,0,,0,,0,0,,0,0,0,,0,0,0,0,,,0,0,247,247,0,,247,0', +'247,,,,,,,0,,,,,,0,,,247,0,,,,,247,,247,,247,247,,247,247,247,,247,247', +',,,,247,247,,,247,,,247,,,,241,241,241,241,247,241,241,241,241,241,247', +'241,241,,247,,,,,241,241,241,246,246,246,246,,246,246,246,246,246,,246', +'246,,,241,241,,,246,246,246,196,196,196,196,,196,196,196,196,196,,196', +'196,,,246,246,,,196,196,196,,,,,,,,,,,,,,,,196,196' ] racc_action_check = arr = ::Array.new(5065, nil) idx = 0 clist.each do |str| @@ -370,178 +369,178 @@ clist = [ end racc_action_pointer = [ - 4877, 270, nil, nil, 4783, 256, nil, 150, nil, nil, - 309, 4186, 4045, 3854, nil, nil, nil, nil, nil, nil, + 4877, 271, nil, nil, 4783, 258, nil, 127, nil, nil, + 309, 4092, 3904, 3616, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, 230, 176, 211, 3522, - 3475, 3234, 1395, -1, 186, nil, 118, 117, nil, 3904, - 152, nil, nil, nil, 1348, nil, nil, nil, nil, nil, - nil, nil, nil, 221, 1442, 208, 1536, 1583, 1630, 1677, - 1724, 1771, 1818, 38, 29, 1959, 2006, 2053, 2100, 2147, - 2194, 2241, 2288, 2335, 2382, 2429, 2476, 2523, 2570, 2617, - 4924, 2711, 2758, 2805, 143, 2902, 156, 2999, 3046, 139, - 55, 82, 876, 3284, nil, 219, -32, 3428, 813, nil, - 750, 624, 561, 3663, 22, nil, nil, nil, nil, 68, - 86, nil, nil, nil, nil, nil, 498, 97, nil, 24, - nil, nil, 183, 435, nil, 146, nil, 207, nil, nil, - nil, nil, nil, 46, 76, nil, nil, nil, nil, 3244, - 3388, 1347, 1210, 1205, 3767, 3864, 4408, 4433, 4489, 4514, - 4570, 4595, 4651, 4696, 183, 120, 57, -6, nil, nil, - 2664, nil, 246, 4327, 4280, 4233, 218, 230, nil, nil, - -7, nil, -10, 7, 128, 105, 54, 13, 8, nil, - nil, nil, nil, nil, nil, 4996, 372, 184, nil, 204, - nil, 212, 154, nil, 4139, nil, 205, nil, 180, -9, - nil, 4092, 3998, 3951, 1251, 148, 106, nil, 1, 123, - 115, 25, nil, 27, 3807, nil, nil, 183, 3757, nil, - nil, nil, nil, 3710, nil, 3378, 3331, 13, nil, 4974, - nil, 53, 3187, 99, 4952, 3140, 3093, 140, nil, nil, + nil, nil, nil, nil, nil, nil, 232, 178, 218, 3475, + 3234, 3140, 1392, -4, 187, nil, 116, 66, nil, 2711, + 120, nil, nil, nil, 1345, nil, nil, nil, nil, nil, + nil, nil, nil, 221, 1439, 207, 1533, 1580, 1627, 1674, + 1721, 1768, 1815, 38, 27, 1956, 2003, 2050, 2097, 2144, + 2191, 2238, 2285, 2332, 2379, 2426, 2473, 2520, 2567, 2614, + 2661, 1251, 2758, 2805, 144, 2902, 160, 2999, 3046, 201, + 115, 121, 813, 3284, nil, 38, -33, 3428, 750, nil, + 624, 561, 498, 3663, 143, nil, nil, nil, nil, -10, + 83, nil, 167, nil, nil, nil, nil, 435, -5, nil, + 25, nil, nil, 184, 372, nil, 148, nil, 219, nil, + nil, nil, nil, nil, 91, 139, nil, nil, nil, nil, + 2671, 3244, 1297, 1210, 1205, 3388, 3767, 4455, 4480, 4536, + 4561, 4617, 4642, 4698, 4743, 120, 57, -6, 876, nil, + nil, 4421, nil, 183, 4327, 4280, 4233, 228, 233, nil, + nil, 8, nil, 47, 7, 86, 53, 54, 42, 27, + nil, nil, nil, nil, nil, nil, 4996, 246, 181, nil, + 203, nil, 211, 149, nil, 4139, nil, 204, nil, 190, + -9, nil, 4045, 3998, 3951, 3854, 124, 121, nil, -1, + 148, 112, 3, nil, 25, 52, 3807, nil, nil, 23, + 3757, nil, nil, nil, nil, 3710, nil, 3378, 3331, 69, + nil, 4952, nil, 100, 3187, 122, 4974, 4924, 3093, -4, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - 2952, 141, nil, 159, nil, 102, 138, 2855, nil, 174, - 48, 181, 162, -6, 1912, nil, 163, 193, 198, 200, - nil, 90, nil, 206, 1865, 1489, nil, nil, 939, nil, - nil, nil, 1128, nil, 1191, 216, 1301, -4, nil, nil, - nil, nil, 1065, 1002, 222, 165, nil, nil, nil, 687, - 97, nil, 3569, 238, 216, nil, 240, 241, nil, nil, - nil, 242, 244, nil, 3616, nil, nil, nil, 230, 247, - nil, nil, 248, nil, nil, nil, nil, nil, nil, nil, - nil, 4374, nil, 4736, 4830, nil, nil, 262, nil, nil, - nil, 267, nil, 268, nil, 269, nil, nil, nil, nil, - nil ] + nil, nil, 2952, 146, nil, 163, nil, 110, 148, 2855, + nil, 176, 99, 179, 163, 70, 1909, nil, 168, 201, + 205, 207, nil, 62, nil, 205, 1862, 1486, nil, nil, + nil, 939, nil, nil, nil, 1128, nil, 1191, 217, 1298, + 217, nil, nil, nil, nil, 1065, 1002, 225, 166, nil, + nil, nil, 687, 130, nil, 3522, 240, 218, nil, 243, + 245, nil, nil, nil, 245, 246, nil, 3569, nil, nil, + nil, 232, 249, nil, nil, 257, nil, nil, nil, nil, + nil, nil, nil, nil, 4186, nil, 4374, 4830, nil, nil, + 267, nil, nil, nil, 268, nil, 269, nil, 270, nil, + nil, nil, nil, nil ] racc_action_default = [ - -208, -209, -1, -2, -3, -4, -7, -9, -10, -15, - -105, -209, -209, -209, -44, -45, -46, -47, -48, -49, + -210, -211, -1, -2, -3, -4, -7, -9, -10, -15, + -105, -211, -211, -211, -44, -45, -46, -47, -48, -49, -50, -51, -52, -53, -54, -55, -56, -57, -58, -59, - -60, -61, -62, -63, -64, -65, -70, -71, -75, -209, - -209, -209, -209, -209, -115, -117, -209, -209, -162, -209, - -209, -172, -173, -174, -209, -176, -183, -184, -185, -186, - -187, -188, -189, -209, -209, -6, -209, -209, -209, -209, - -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, - -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, - -209, -209, -209, -209, -209, -124, -119, -208, -208, -27, - -209, -34, -209, -209, -72, -209, -209, -209, -209, -82, - -209, -209, -209, -209, -208, -134, -151, -152, -116, -208, - -208, -143, -145, -146, -147, -148, -42, -209, -165, -209, - -168, -169, -209, -180, -175, -209, 361, -5, -8, -11, - -12, -13, -14, -209, -17, -18, -160, -161, -19, -20, - -21, -22, -23, -24, -25, -26, -28, -29, -30, -31, - -32, -33, -35, -36, -37, -38, -39, -209, -40, -100, - -209, -76, -209, -201, -207, -195, -192, -190, -113, -125, - -184, -128, -188, -209, -198, -196, -204, -186, -187, -194, - -199, -200, -202, -203, -205, -124, -123, -209, -122, -209, - -41, -190, -67, -77, -209, -80, -190, -156, -159, -209, - -74, -209, -209, -209, -124, -192, -208, -153, -209, -209, - -209, -209, -149, -209, -209, -163, -166, -209, -209, -177, - -178, -179, -181, -209, -16, -209, -209, -190, -102, -124, - -112, -209, -193, -209, -191, -209, -209, -190, -127, -129, - -195, -196, -197, -198, -201, -204, -206, -207, -120, -121, - -191, -209, -69, -209, -79, -209, -191, -209, -73, -209, - -85, -209, -91, -209, -209, -95, -192, -190, -209, -209, - -137, -209, -154, -190, -208, -209, -144, -150, -43, -164, - -167, -170, -171, -182, -104, -209, -191, -190, -108, -114, - -109, -126, -130, -131, -209, -66, -78, -81, -157, -158, - -85, -84, -209, -209, -91, -90, -209, -209, -99, -94, - -96, -209, -209, -110, -208, -138, -139, -140, -209, -209, - -135, -136, -209, -142, -101, -103, -111, -118, -68, -83, - -86, -209, -89, -209, -209, -106, -107, -209, -155, -132, - -141, -209, -88, -209, -93, -209, -98, -133, -87, -92, - -97 ] + -60, -61, -62, -63, -64, -65, -70, -71, -75, -211, + -211, -211, -211, -211, -115, -117, -211, -211, -164, -211, + -211, -174, -175, -176, -211, -178, -185, -186, -187, -188, + -189, -190, -191, -211, -211, -6, -211, -211, -211, -211, + -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, + -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, + -211, -211, -211, -211, -211, -124, -119, -210, -210, -27, + -211, -34, -211, -211, -72, -211, -211, -211, -211, -82, + -211, -211, -211, -211, -210, -134, -153, -154, -116, -210, + -210, -143, -145, -146, -147, -148, -149, -42, -211, -167, + -211, -170, -171, -211, -182, -177, -211, 364, -5, -8, + -11, -12, -13, -14, -211, -17, -18, -162, -163, -19, + -20, -21, -22, -23, -24, -25, -26, -28, -29, -30, + -31, -32, -33, -35, -36, -37, -38, -39, -211, -40, + -100, -211, -76, -211, -203, -209, -197, -194, -192, -113, + -125, -186, -128, -190, -211, -200, -198, -206, -188, -189, + -196, -201, -202, -204, -205, -207, -124, -123, -211, -122, + -211, -41, -192, -67, -77, -211, -80, -192, -158, -161, + -211, -74, -211, -211, -211, -124, -194, -210, -155, -211, + -211, -211, -211, -151, -211, -211, -211, -165, -168, -211, + -211, -179, -180, -181, -183, -211, -16, -211, -211, -192, + -102, -124, -112, -211, -195, -211, -193, -211, -211, -192, + -127, -129, -197, -198, -199, -200, -203, -206, -208, -209, + -120, -121, -193, -211, -69, -211, -79, -211, -193, -211, + -73, -211, -85, -211, -91, -211, -211, -95, -194, -192, + -211, -211, -137, -211, -156, -192, -210, -211, -144, -152, + -150, -43, -166, -169, -172, -173, -184, -104, -211, -193, + -192, -108, -114, -109, -126, -130, -131, -211, -66, -78, + -81, -159, -160, -85, -84, -211, -211, -91, -90, -211, + -211, -99, -94, -96, -211, -211, -110, -210, -138, -139, + -140, -211, -211, -135, -136, -211, -142, -101, -103, -111, + -118, -68, -83, -86, -211, -89, -211, -211, -106, -107, + -211, -157, -132, -141, -211, -88, -211, -93, -211, -98, + -133, -87, -92, -97 ] racc_goto_table = [ - 2, 119, 3, 99, 101, 102, 104, 134, 131, 177, - 176, 169, 132, 206, 241, 315, 125, 123, 311, 329, - 216, 139, 140, 141, 142, 219, 243, 286, 215, 287, - 275, 108, 110, 111, 112, 147, 147, 65, 197, 199, - 127, 126, 146, 146, 145, 148, 133, 1, 299, 239, - 261, 237, 301, 278, 317, 265, 274, 342, 339, 347, - 340, 279, 143, 126, 144, 325, 220, 149, 150, 151, - 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, - 162, 163, 164, 165, 166, 167, 295, 172, 238, 196, - 196, 320, 203, 308, 201, 126, 304, 137, 209, 126, - 129, 168, 138, 232, 233, 172, 231, nil, nil, 247, - nil, nil, nil, nil, 321, nil, 217, nil, nil, nil, - nil, 217, 222, nil, nil, 283, 322, nil, 277, 276, - nil, nil, 328, nil, nil, nil, nil, nil, nil, 119, - nil, nil, nil, nil, nil, nil, 336, nil, nil, nil, - nil, nil, nil, 297, nil, 125, 123, nil, nil, nil, - nil, nil, 167, nil, nil, 108, 110, 111, nil, nil, - nil, 262, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, 291, 293, nil, nil, 132, - 125, 123, 125, 123, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, 263, 126, 172, nil, nil, nil, - nil, 269, 271, nil, 335, nil, 288, nil, 280, nil, - 292, nil, nil, nil, nil, 133, nil, 288, 294, nil, - nil, nil, nil, nil, 172, nil, 326, 302, 303, nil, + 2, 119, 3, 99, 101, 102, 104, 135, 170, 123, + 132, 207, 133, 178, 243, 318, 314, 125, 217, 177, + 288, 332, 289, 220, 198, 200, 277, 146, 149, 1, + 245, 108, 110, 111, 112, 148, 148, 216, 65, 128, + 302, 127, 241, 147, 147, 239, 134, 140, 141, 142, + 143, 304, 320, 280, 263, 276, 343, 342, 345, 267, + 281, 144, 350, 127, 145, 328, 221, 150, 151, 152, + 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, + 163, 164, 165, 166, 167, 168, 240, 173, 323, 197, + 197, 298, 204, 202, 311, 127, 130, 210, 138, 127, + 169, 307, 139, 234, 235, 173, 233, nil, nil, nil, + nil, nil, nil, nil, 249, 324, 218, nil, nil, nil, + nil, 218, 223, nil, 285, nil, nil, nil, nil, nil, + nil, 325, nil, 279, nil, nil, nil, 331, nil, 278, + 119, nil, nil, nil, nil, nil, nil, nil, nil, 123, + nil, nil, 339, nil, nil, nil, nil, 125, nil, 300, + nil, nil, nil, 168, nil, nil, 108, 110, 111, nil, + nil, nil, 264, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, 123, nil, 123, nil, 296, 294, + nil, 133, 125, nil, 125, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, 265, 127, 173, nil, nil, + nil, nil, 271, 273, 338, nil, nil, nil, 291, 282, + nil, nil, 295, nil, nil, nil, nil, 134, nil, 291, + 297, nil, nil, nil, nil, nil, 173, nil, 329, 305, + 306, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, 291, nil, nil, nil, nil, nil, + nil, 312, nil, nil, nil, nil, nil, nil, 127, nil, + nil, nil, nil, nil, nil, nil, nil, 341, nil, nil, + nil, nil, nil, nil, nil, nil, nil, 335, 334, nil, + nil, 168, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, 108, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, 288, nil, nil, nil, nil, nil, nil, 309, - nil, nil, nil, nil, nil, nil, 126, nil, nil, nil, - nil, nil, nil, nil, 338, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, 332, 331, nil, 167, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, 334, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, 108, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, 331, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, 351, nil, 353, 355 ] + nil, nil, nil, nil, 354, nil, 356, 358 ] racc_goto_check = [ - 2, 63, 3, 9, 9, 9, 38, 78, 74, 55, - 53, 50, 31, 43, 54, 46, 30, 36, 45, 65, - 64, 7, 7, 7, 7, 64, 37, 71, 53, 71, - 48, 9, 9, 9, 9, 31, 31, 5, 59, 59, - 11, 9, 30, 30, 12, 12, 9, 1, 56, 57, - 37, 51, 60, 54, 49, 37, 47, 46, 45, 65, - 44, 67, 11, 9, 9, 68, 70, 9, 9, 9, + 2, 63, 3, 9, 9, 9, 38, 79, 50, 36, + 75, 43, 31, 55, 54, 46, 45, 30, 64, 53, + 71, 65, 71, 64, 59, 59, 48, 12, 12, 1, + 37, 9, 9, 9, 9, 31, 31, 53, 5, 11, + 56, 9, 57, 30, 30, 51, 9, 7, 7, 7, + 7, 60, 49, 54, 37, 47, 44, 45, 46, 37, + 67, 11, 65, 9, 9, 68, 70, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 37, 9, 50, 9, - 9, 48, 42, 72, 11, 9, 37, 5, 11, 9, - 73, 13, 6, 79, 80, 9, 82, nil, nil, 55, - nil, nil, nil, nil, 54, nil, 3, nil, nil, nil, - nil, 3, 3, nil, nil, 43, 37, nil, 55, 53, - nil, nil, 37, nil, nil, nil, nil, nil, nil, 63, - nil, nil, nil, nil, nil, nil, 37, nil, nil, nil, - nil, nil, nil, 55, nil, 30, 36, nil, nil, nil, - nil, nil, 9, nil, nil, 9, 9, 9, nil, nil, - nil, 38, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, 74, 78, nil, nil, 31, - 30, 36, 30, 36, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, 2, 9, 9, nil, nil, nil, - nil, 2, 2, nil, 50, nil, 9, nil, 3, nil, - 9, nil, nil, nil, nil, 9, nil, 9, 9, nil, - nil, nil, nil, nil, 9, nil, 63, 9, 9, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, 9, nil, nil, nil, nil, nil, nil, 9, - nil, nil, nil, nil, nil, nil, 9, nil, nil, nil, - nil, nil, nil, nil, 38, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, 2, 3, nil, 9, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + 9, 9, 9, 9, 9, 9, 50, 9, 48, 9, + 9, 37, 42, 11, 73, 9, 74, 11, 5, 9, + 13, 37, 6, 80, 81, 9, 83, nil, nil, nil, + nil, nil, nil, nil, 55, 54, 3, nil, nil, nil, + nil, 3, 3, nil, 43, nil, nil, nil, nil, nil, + nil, 37, nil, 55, nil, nil, nil, 37, nil, 53, + 63, nil, nil, nil, nil, nil, nil, nil, nil, 36, + nil, nil, 37, nil, nil, nil, nil, 30, nil, 55, + nil, nil, nil, 9, nil, nil, 9, 9, 9, nil, + nil, nil, 38, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, 36, nil, 36, nil, 79, 75, + nil, 31, 30, nil, 30, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, 2, 9, 9, nil, nil, + nil, nil, 2, 2, 50, nil, nil, nil, 9, 3, + nil, nil, 9, nil, nil, nil, nil, 9, nil, 9, + 9, nil, nil, nil, nil, nil, 9, nil, 63, 9, + 9, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 9, nil, nil, nil, nil, nil, + nil, 9, nil, nil, nil, nil, nil, nil, 9, nil, + nil, nil, nil, nil, nil, nil, nil, 38, nil, nil, + nil, nil, nil, nil, nil, nil, nil, 2, 3, nil, + nil, 9, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, 9, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, 3, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, 3, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, 2, nil, 2, 2 ] + nil, nil, nil, nil, 2, nil, 2, 2 ] racc_goto_pointer = [ - nil, 47, 0, 2, nil, 33, 36, -46, nil, -8, - nil, -9, -29, 8, nil, nil, nil, nil, nil, nil, + nil, 29, 0, 2, nil, 34, 36, -20, nil, -8, + nil, -10, -46, 7, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - -31, -38, nil, nil, nil, nil, -30, -151, -31, nil, - nil, nil, -13, -93, -252, -252, -257, -157, -183, -219, - -82, -119, nil, -85, -162, -86, -194, -123, nil, -59, - -192, nil, nil, -45, -94, -265, nil, -155, -216, nil, - -54, -194, -173, 50, -42, nil, nil, nil, -47, -32, - -31, nil, -29 ] + -30, -38, nil, nil, nil, nil, -38, -148, -31, nil, + nil, nil, -13, -95, -259, -256, -259, -159, -188, -223, + -85, -126, nil, -76, -163, -82, -204, -131, nil, -73, + -195, nil, nil, -45, -96, -265, nil, -157, -218, nil, + -54, -202, nil, -174, 46, -40, nil, nil, nil, -47, + -33, -32, nil, -30 ] racc_goto_default = [ - nil, nil, 330, 198, 4, 5, 6, 7, 8, 10, - 9, 273, nil, nil, 14, 36, 15, 16, 17, 18, + nil, nil, 333, 199, 4, 5, 6, 7, 8, 10, + 9, 275, nil, nil, 14, 36, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, nil, nil, 37, 38, 105, nil, nil, 109, nil, nil, nil, nil, nil, - nil, nil, 42, nil, nil, nil, 178, nil, 96, nil, - 179, 183, 181, 115, nil, nil, 114, nil, nil, 120, - nil, 121, 207, nil, nil, 51, 52, 54, nil, nil, - nil, 135, nil ] + nil, nil, 42, nil, nil, nil, 179, nil, 96, nil, + 180, 184, 182, 115, nil, nil, 114, nil, nil, 120, + nil, 121, 122, 208, nil, nil, 51, 52, 54, nil, + nil, nil, 136, nil ] racc_reduce_table = [ 0, 0, :racc_error, @@ -693,49 +692,51 @@ racc_reduce_table = [ 1, 151, :_reduce_146, 1, 151, :_reduce_147, 1, 151, :_reduce_none, + 1, 152, :_reduce_149, + 3, 152, :_reduce_150, 1, 150, :_reduce_none, - 2, 150, :_reduce_150, - 1, 143, :_reduce_151, - 1, 143, :_reduce_152, - 1, 144, :_reduce_153, - 2, 144, :_reduce_154, - 4, 144, :_reduce_155, - 1, 123, :_reduce_156, - 3, 123, :_reduce_157, - 3, 152, :_reduce_158, - 1, 152, :_reduce_159, + 2, 150, :_reduce_152, + 1, 143, :_reduce_153, + 1, 143, :_reduce_154, + 1, 144, :_reduce_155, + 2, 144, :_reduce_156, + 4, 144, :_reduce_157, + 1, 123, :_reduce_158, + 3, 123, :_reduce_159, + 3, 153, :_reduce_160, + 1, 153, :_reduce_161, 1, 92, :_reduce_none, 1, 92, :_reduce_none, - 1, 97, :_reduce_162, - 3, 106, :_reduce_163, - 4, 106, :_reduce_164, - 2, 106, :_reduce_165, - 3, 109, :_reduce_166, - 4, 109, :_reduce_167, - 2, 109, :_reduce_168, - 1, 153, :_reduce_169, - 3, 153, :_reduce_170, - 3, 154, :_reduce_171, + 1, 97, :_reduce_164, + 3, 106, :_reduce_165, + 4, 106, :_reduce_166, + 2, 106, :_reduce_167, + 3, 109, :_reduce_168, + 4, 109, :_reduce_169, + 2, 109, :_reduce_170, + 1, 154, :_reduce_171, + 3, 154, :_reduce_172, + 3, 155, :_reduce_173, 1, 116, :_reduce_none, 1, 116, :_reduce_none, - 1, 155, :_reduce_174, - 2, 156, :_reduce_175, - 1, 157, :_reduce_176, - 1, 159, :_reduce_177, - 1, 160, :_reduce_178, - 2, 158, :_reduce_179, + 1, 156, :_reduce_176, + 2, 157, :_reduce_177, + 1, 158, :_reduce_178, + 1, 160, :_reduce_179, 1, 161, :_reduce_180, - 1, 162, :_reduce_181, - 2, 162, :_reduce_182, - 1, 112, :_reduce_183, - 1, 115, :_reduce_184, - 1, 113, :_reduce_185, - 1, 114, :_reduce_186, - 1, 108, :_reduce_187, - 1, 107, :_reduce_188, - 1, 110, :_reduce_189, + 2, 159, :_reduce_181, + 1, 162, :_reduce_182, + 1, 163, :_reduce_183, + 2, 163, :_reduce_184, + 1, 112, :_reduce_185, + 1, 115, :_reduce_186, + 1, 113, :_reduce_187, + 1, 114, :_reduce_188, + 1, 108, :_reduce_189, + 1, 107, :_reduce_190, + 1, 110, :_reduce_191, 0, 117, :_reduce_none, - 1, 117, :_reduce_191, + 1, 117, :_reduce_193, 0, 134, :_reduce_none, 1, 134, :_reduce_none, 1, 142, :_reduce_none, @@ -752,11 +753,11 @@ racc_reduce_table = [ 1, 142, :_reduce_none, 1, 142, :_reduce_none, 1, 142, :_reduce_none, - 0, 83, :_reduce_208 ] + 0, 83, :_reduce_210 ] -racc_reduce_n = 209 +racc_reduce_n = 211 -racc_shift_n = 361 +racc_shift_n = 364 racc_token_table = { false => 0, @@ -1013,6 +1014,7 @@ Racc_token_to_s_table = [ "hostnames", "nodeparent", "hostname", + "dotted_name", "parameter", "hashpairs", "hashpair", @@ -1899,9 +1901,10 @@ module_eval(<<'.,.,', 'egrammar.ra', 492) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 515) +module_eval(<<'.,.,', 'egrammar.ra', 506) def _reduce_133(val, _values, result) - namepop + # Remove this class' name from the namestack as all nested classes have been parsed + namepop result = Factory.HOSTCLASS(classname(val[1][:value]), val[2], token_text(val[3]), val[5]) loc result, val[0], val[6] @@ -1909,7 +1912,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 515) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 525) +module_eval(<<'.,.,', 'egrammar.ra', 516) def _reduce_134(val, _values, result) namestack(val[0][:value]) ; result = val[0] result @@ -1922,7 +1925,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 525) # reduce 137 omitted -module_eval(<<'.,.,', 'egrammar.ra', 534) +module_eval(<<'.,.,', 'egrammar.ra', 525) def _reduce_138(val, _values, result) result = val[1] result @@ -1933,7 +1936,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 534) # reduce 140 omitted -module_eval(<<'.,.,', 'egrammar.ra', 551) +module_eval(<<'.,.,', 'egrammar.ra', 542) def _reduce_141(val, _values, result) result = Factory.NODE(val[1], val[2], val[4]) loc result, val[0], val[5] @@ -1942,7 +1945,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 551) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 555) +module_eval(<<'.,.,', 'egrammar.ra', 546) def _reduce_142(val, _values, result) result = Factory.NODE(val[1], val[2], nil) loc result, val[0], val[4] @@ -1951,35 +1954,35 @@ module_eval(<<'.,.,', 'egrammar.ra', 555) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 565) +module_eval(<<'.,.,', 'egrammar.ra', 556) def _reduce_143(val, _values, result) result = [result] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 566) +module_eval(<<'.,.,', 'egrammar.ra', 557) def _reduce_144(val, _values, result) result = val[0].push(val[2]) result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 571) +module_eval(<<'.,.,', 'egrammar.ra', 562) def _reduce_145(val, _values, result) - result = Factory.fqn(val[0][:value]); loc result, val[0] + result = val[0]; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 572) +module_eval(<<'.,.,', 'egrammar.ra', 563) def _reduce_146(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 573) +module_eval(<<'.,.,', 'egrammar.ra', 564) def _reduce_147(val, _values, result) result = Factory.literal(:default); loc result, val[0] result @@ -1988,280 +1991,290 @@ module_eval(<<'.,.,', 'egrammar.ra', 573) # reduce 148 omitted -# reduce 149 omitted +module_eval(<<'.,.,', 'egrammar.ra', 568) + def _reduce_149(val, _values, result) + result = Factory.literal(val[0][:value]); loc result, val[0] + result + end +.,., -module_eval(<<'.,.,', 'egrammar.ra', 579) +module_eval(<<'.,.,', 'egrammar.ra', 569) def _reduce_150(val, _values, result) + result = Factory.concat(val[0], '.', val[2][:value]); loc result, val[0], val[2] + result + end +.,., + +# reduce 151 omitted + +module_eval(<<'.,.,', 'egrammar.ra', 574) + def _reduce_152(val, _values, result) result = val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 584) - def _reduce_151(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 580) + def _reduce_153(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 585) - def _reduce_152(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 581) + def _reduce_154(val, _values, result) error val[0], "'class' is not a valid classname" result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 589) - def _reduce_153(val, _values, result) - result = [] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 590) - def _reduce_154(val, _values, result) - result = [] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 591) +module_eval(<<'.,.,', 'egrammar.ra', 585) def _reduce_155(val, _values, result) + result = [] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 586) + def _reduce_156(val, _values, result) + result = [] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 587) + def _reduce_157(val, _values, result) result = val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 595) - def _reduce_156(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 591) + def _reduce_158(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 596) - def _reduce_157(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 592) + def _reduce_159(val, _values, result) result = val[0].push(val[2]) result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 600) - def _reduce_158(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 596) + def _reduce_160(val, _values, result) result = Factory.PARAM(val[0][:value], val[2]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 601) - def _reduce_159(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 597) + def _reduce_161(val, _values, result) result = Factory.PARAM(val[0][:value]); loc result, val[0] result end .,., -# reduce 160 omitted +# reduce 162 omitted -# reduce 161 omitted +# reduce 163 omitted -module_eval(<<'.,.,', 'egrammar.ra', 614) - def _reduce_162(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 610) + def _reduce_164(val, _values, result) result = Factory.fqn(val[0][:value]).var ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 620) - def _reduce_163(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 616) + def _reduce_165(val, _values, result) result = Factory.LIST(val[1]); loc result, val[0], val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 621) - def _reduce_164(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 617) + def _reduce_166(val, _values, result) result = Factory.LIST(val[1]); loc result, val[0], val[3] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 622) - def _reduce_165(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 618) + def _reduce_167(val, _values, result) result = Factory.literal([]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 625) - def _reduce_166(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 621) + def _reduce_168(val, _values, result) result = Factory.HASH(val[1]); loc result, val[0], val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 626) - def _reduce_167(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 622) + def _reduce_169(val, _values, result) result = Factory.HASH(val[1]); loc result, val[0], val[3] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 627) - def _reduce_168(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 623) + def _reduce_170(val, _values, result) result = Factory.literal({}) ; loc result, val[0], val[3] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 630) - def _reduce_169(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 626) + def _reduce_171(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 631) - def _reduce_170(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 627) + def _reduce_172(val, _values, result) result = val[0].push val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 634) - def _reduce_171(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 630) + def _reduce_173(val, _values, result) result = Factory.KEY_ENTRY(val[0], val[2]); loc result, val[1] result end .,., -# reduce 172 omitted +# reduce 174 omitted -# reduce 173 omitted +# reduce 175 omitted -module_eval(<<'.,.,', 'egrammar.ra', 640) - def _reduce_174(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 636) + def _reduce_176(val, _values, result) result = Factory.literal(val[0][:value]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 641) - def _reduce_175(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 637) + def _reduce_177(val, _values, result) result = Factory.string(val[0], *val[1]) ; loc result, val[0], val[1][-1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 642) - def _reduce_176(val, _values, result) - result = Factory.literal(val[0][:value]); loc result, val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 643) - def _reduce_177(val, _values, result) - result = Factory.literal(val[0][:value]); loc result, val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 644) +module_eval(<<'.,.,', 'egrammar.ra', 638) def _reduce_178(val, _values, result) result = Factory.literal(val[0][:value]); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 645) +module_eval(<<'.,.,', 'egrammar.ra', 639) def _reduce_179(val, _values, result) - result = [val[0]] + val[1] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 646) - def _reduce_180(val, _values, result) - result = Factory.TEXT(val[0]) - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 649) - def _reduce_181(val, _values, result) - result = [val[0]] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 650) - def _reduce_182(val, _values, result) - result = [val[0]] + val[1] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 652) - def _reduce_183(val, _values, result) - result = Factory.NUMBER(val[0][:value]) ; loc result, val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 653) - def _reduce_184(val, _values, result) - result = Factory.QNAME_OR_NUMBER(val[0][:value]) ; loc result, val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 654) - def _reduce_185(val, _values, result) - result = Factory.QREF(val[0][:value]) ; loc result, val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 655) - def _reduce_186(val, _values, result) - result = Factory.literal(:undef); loc result, val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 656) - def _reduce_187(val, _values, result) - result = Factory.literal(:default); loc result, val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 661) - def _reduce_188(val, _values, result) - result = Factory.literal(val[0][:value]) ; loc result, val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 664) - def _reduce_189(val, _values, result) result = Factory.literal(val[0][:value]); loc result, val[0] result end .,., -# reduce 190 omitted +module_eval(<<'.,.,', 'egrammar.ra', 640) + def _reduce_180(val, _values, result) + result = Factory.literal(val[0][:value]); loc result, val[0] + result + end +.,., -module_eval(<<'.,.,', 'egrammar.ra', 670) +module_eval(<<'.,.,', 'egrammar.ra', 641) + def _reduce_181(val, _values, result) + result = [val[0]] + val[1] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 642) + def _reduce_182(val, _values, result) + result = Factory.TEXT(val[0]) + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 645) + def _reduce_183(val, _values, result) + result = [val[0]] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 646) + def _reduce_184(val, _values, result) + result = [val[0]] + val[1] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 648) + def _reduce_185(val, _values, result) + result = Factory.NUMBER(val[0][:value]) ; loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 649) + def _reduce_186(val, _values, result) + result = Factory.QNAME_OR_NUMBER(val[0][:value]) ; loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 650) + def _reduce_187(val, _values, result) + result = Factory.QREF(val[0][:value]) ; loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 651) + def _reduce_188(val, _values, result) + result = Factory.literal(:undef); loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 652) + def _reduce_189(val, _values, result) + result = Factory.literal(:default); loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 657) + def _reduce_190(val, _values, result) + result = Factory.literal(val[0][:value]) ; loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 660) def _reduce_191(val, _values, result) - result = nil + result = Factory.literal(val[0][:value]); loc result, val[0] result end .,., # reduce 192 omitted -# reduce 193 omitted +module_eval(<<'.,.,', 'egrammar.ra', 666) + def _reduce_193(val, _values, result) + result = nil + result + end +.,., # reduce 194 omitted @@ -2291,8 +2304,12 @@ module_eval(<<'.,.,', 'egrammar.ra', 670) # reduce 207 omitted -module_eval(<<'.,.,', 'egrammar.ra', 693) - def _reduce_208(val, _values, result) +# reduce 208 omitted + +# reduce 209 omitted + +module_eval(<<'.,.,', 'egrammar.ra', 689) + def _reduce_210(val, _values, result) result = nil result end diff --git a/spec/unit/pops/parser/parse_containers_spec.rb b/spec/unit/pops/parser/parse_containers_spec.rb index 49ae0be9a..57d6efee9 100644 --- a/spec/unit/pops/parser/parse_containers_spec.rb +++ b/spec/unit/pops/parser/parse_containers_spec.rb @@ -156,11 +156,19 @@ describe "egrammar parsing containers" do context "When parsing node" do it "node foo {}" do - dump(parse("node foo {}")).should == "(node (matches foo) ())" + dump(parse("node foo {}")).should == "(node (matches 'foo') ())" + end + + it "node kermit.example.com {}" do + dump(parse("node kermit.example.com {}")).should == "(node (matches 'kermit.example.com') ())" + end + + it "node kermit . example . com {}" do + dump(parse("node kermit . example . com {}")).should == "(node (matches 'kermit.example.com') ())" end it "node foo, x::bar, default {}" do - dump(parse("node foo, x::bar, default {}")).should == "(node (matches foo x::bar :default) ())" + dump(parse("node foo, x::bar, default {}")).should == "(node (matches 'foo' 'x::bar' :default) ())" end it "node 'foo' {}" do @@ -168,15 +176,15 @@ describe "egrammar parsing containers" do end it "node foo inherits x::bar {}" do - dump(parse("node foo inherits x::bar {}")).should == "(node (matches foo) (parent x::bar) ())" + dump(parse("node foo inherits x::bar {}")).should == "(node (matches 'foo') (parent 'x::bar') ())" end it "node foo inherits 'bar' {}" do - dump(parse("node foo inherits 'bar' {}")).should == "(node (matches foo) (parent 'bar') ())" + dump(parse("node foo inherits 'bar' {}")).should == "(node (matches 'foo') (parent 'bar') ())" end it "node foo inherits default {}" do - dump(parse("node foo inherits default {}")).should == "(node (matches foo) (parent :default) ())" + dump(parse("node foo inherits default {}")).should == "(node (matches 'foo') (parent :default) ())" end it "node /web.*/ {}" do @@ -188,11 +196,11 @@ describe "egrammar parsing containers" do end it "node wat inherits /apache.*/ {}" do - dump(parse("node wat inherits /apache.*/ {}")).should == "(node (matches wat) (parent /apache.*/) ())" + dump(parse("node wat inherits /apache.*/ {}")).should == "(node (matches 'wat') (parent /apache.*/) ())" end it "node foo inherits bar {$a = 10 $b = 20}" do - dump(parse("node foo inherits bar {$a = 10 $b = 20}")).should == "(node (matches foo) (parent bar) (block (= $a 10) (= $b 20)))" + dump(parse("node foo inherits bar {$a = 10 $b = 20}")).should == "(node (matches 'foo') (parent 'bar') (block (= $a 10) (= $b 20)))" end end end From fd7525c3a6dbc9cb9ef3446d793379251e2666b0 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 24 Oct 2013 05:27:09 +0200 Subject: [PATCH 041/800] (#22962) Remove alternative lambda syntaxes from tests --- .../pops/parser/evaluating_parser_spec.rb | 19 +++++++++++++++++++ spec/unit/pops/parser/parser_spec.rb | 18 ++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/spec/unit/pops/parser/evaluating_parser_spec.rb b/spec/unit/pops/parser/evaluating_parser_spec.rb index 555857847..eef809c32 100644 --- a/spec/unit/pops/parser/evaluating_parser_spec.rb +++ b/spec/unit/pops/parser/evaluating_parser_spec.rb @@ -87,4 +87,23 @@ describe 'The hiera2 string evaluator' do test_interpolate("\\s", ' ') end end + +# describe "when benchmarked" do +# +# it "Pops Parser", :profile => true do +# code = 'if true +#{ +#$a = 10 + 10 +#} +#else +#{ +#$a = "interpolate ${foo} and stuff" +#} +#' +# parser = Puppet::Pops::Parser::EvaluatingParser.new() +# m = Benchmark.measure { 10000.times { parser.parse_string(code) }} +# puts "Parser: #{m}" +# end +# end + end diff --git a/spec/unit/pops/parser/parser_spec.rb b/spec/unit/pops/parser/parser_spec.rb index e29410681..98e39e0b7 100644 --- a/spec/unit/pops/parser/parser_spec.rb +++ b/spec/unit/pops/parser/parser_spec.rb @@ -12,4 +12,22 @@ describe Puppet::Pops::Parser::Parser do model = parser.parse_string("$a = 10").current model.class.should == Puppet::Pops::Model::AssignmentExpression end + +# describe "when benchmarked" do +# +# it "Pops Parser", :profile => true do +# code = 'if true +#{ +#10 + 10 +#} +#else +#{ +#"interpolate ${foo} and stuff" +#} +#' +# parser = Puppet::Pops::Parser::Parser.new() +# m = Benchmark.measure { 10000.times { parser.parse_string(code) }} +# puts "Parser: #{m}" +# end +# end end From 02e9223e35e21b31588432d99a59b7c91f020039 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 24 Oct 2013 17:43:53 +0200 Subject: [PATCH 042/800] (perf) Speed up containment calculation by 2x This speeds up getting all contents of a model by a factor 2. The performance is gained by not calling the general routines since they call through many layers with enumeration, filtering and copy & concat until they reach an answer. The optimized version inlines as much as possible and keeps a cache of resulting getters per class. (The getters that produce the content). --- lib/puppet/pops/containment.rb | 48 ++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/lib/puppet/pops/containment.rb b/lib/puppet/pops/containment.rb index a019044b0..b28695dc9 100644 --- a/lib/puppet/pops/containment.rb +++ b/lib/puppet/pops/containment.rb @@ -1,5 +1,6 @@ # FIXME: This module should be updated when a newer version of RGen (>0.6.2) adds required meta model "e-method" supports. # +require 'rgen/ecore/ecore' module Puppet::Pops::Containment # Returns Enumerable, thus allowing # some_element.eAllContents each {|contained| } @@ -13,6 +14,7 @@ module Puppet::Pops::Containment include Enumerable def initialize o @element = o + @@cache ||= {} end def each &block @@ -25,13 +27,49 @@ module Puppet::Pops::Containment end def eAllContents(element, &block) - element.class.ecore.eAllReferences.select{|r| r.containment}.each do |r| - children = element.getGenericAsArray(r.name) - children.each do |c| - block.call(c) - eAllContents(c, &block) + # This method is performance critical and code has been manually in-lined. + # Resist the urge to make this pretty. + # The slow way is element.eAllContainments.each {|c| element.getGenericsAsArray(c.name) } + # + (@@cache[element.class] || all_containment_getters(element)).each do |r| + children = element.send(r) + if children.is_a?(Array) + children.each do |c| + yield c + eAllContents(c, &block) + end + elsif !children.nil? + yield children + eAllContents(children, &block) end end end + + private + + def all_containment_getters(element) + elem_class = element.class + containments = [] + collect_getters(elem_class.ecore, containments) + @@cache[elem_class] = containments + end + + def collect_getters(eclass, containments) + eclass.eStructuralFeatures.select {|r| r.is_a?(RGen::ECore::EReference) && r.containment}.each do |r| + n = r.name + containments << :"get#{n[0..0].upcase + ( n[1..-1] || "" )}" + end + eclass.eSuperTypes.each do |t| + if cached = @@cache[ t.instanceClass ] + containments.concat(cached) + else + super_containments = [] + collect_getters(t, super_containments) + @@cache[ t.instanceClass ] = super_containments + containments.concat(super_containments) + end + end + end + end end From cd7f8ad8c54f7ecffbd0d6fa3ede89efce6fabca Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 24 Oct 2013 17:45:47 +0200 Subject: [PATCH 043/800] (maint) Allow any expression as hash key in parser As opposed to just strings or names. (There is the need to at least support NUMBER, but this generalizes the rule to an expression). --- lib/puppet/pops/parser/egrammar.ra | 2 +- lib/puppet/pops/parser/eparser.rb | 742 +++++++++++++++-------------- 2 files changed, 377 insertions(+), 367 deletions(-) diff --git a/lib/puppet/pops/parser/egrammar.ra b/lib/puppet/pops/parser/egrammar.ra index d161d33d7..c7fc20dc9 100644 --- a/lib/puppet/pops/parser/egrammar.ra +++ b/lib/puppet/pops/parser/egrammar.ra @@ -628,7 +628,7 @@ hash | hashpairs COMMA hashpair { result = val[0].push val[2] } hashpair - : text_or_name FARROW expression { result = Factory.KEY_ENTRY(val[0], val[2]); loc result, val[1] } + : expression FARROW expression { result = Factory.KEY_ENTRY(val[0], val[2]); loc result, val[1] } quotedtext : string diff --git a/lib/puppet/pops/parser/eparser.rb b/lib/puppet/pops/parser/eparser.rb index b1e7b415a..691fd250d 100644 --- a/lib/puppet/pops/parser/eparser.rb +++ b/lib/puppet/pops/parser/eparser.rb @@ -30,67 +30,72 @@ module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 705) ##### State transition tables begin ### clist = [ -'71,209,227,237,113,53,55,226,246,90,91,87,82,94,219,98,270,93,247,-127', -'83,85,84,86,284,53,55,53,55,53,55,117,293,209,228,116,206,229,-199,97', -'53,55,126,89,88,124,205,75,76,78,77,80,81,-208,73,74,53,55,-129,248', -'-127,72,57,71,126,62,57,124,53,55,79,92,90,91,87,82,94,57,98,-199,93', -'299,237,83,85,84,86,62,117,62,321,290,116,126,-208,222,124,117,236,-129', -'62,116,97,237,330,126,89,88,124,301,75,76,78,77,80,81,62,73,74,224,287', -'117,53,55,72,116,71,71,62,131,316,303,315,79,92,90,91,87,82,94,94,98', -'98,93,93,71,83,85,84,86,117,117,232,231,116,116,286,283,94,57,98,316', -'93,315,244,97,97,219,196,89,88,308,309,75,76,78,77,80,81,310,73,74,209', -'172,97,313,225,72,317,71,67,69,68,70,319,230,79,92,90,91,87,82,94,242', -'98,269,93,71,244,83,85,84,86,246,326,327,268,268,106,66,94,137,98,262', -'93,261,337,260,97,246,118,66,89,88,340,106,75,76,78,77,80,81,107,73', -'74,246,97,106,344,319,72,346,71,347,348,349,103,351,352,79,92,90,91', -'87,82,94,353,98,244,93,66,63,83,85,84,86,360,361,362,363,,,,,,,,,,,', -'97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,95,,,,,79,92,90', -'91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76', -'78,77,80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93,,', -'83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,', -',,,72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,', -',,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,215,,,,,79', +'71,247,209,222,53,55,316,113,315,90,91,87,82,94,242,98,284,93,-208,-199', +'83,85,84,86,-129,209,-127,224,237,53,55,117,232,231,117,116,321,206', +'116,97,237,57,248,89,88,53,55,75,76,78,77,80,81,270,73,74,53,55,117', +'-208,-199,72,116,71,62,-129,126,-127,238,124,79,92,90,91,87,82,94,117', +'98,344,93,116,57,83,85,84,86,283,117,62,53,55,116,126,227,330,124,117', +'236,226,299,116,97,237,244,62,89,88,301,205,75,76,78,77,80,81,62,73', +'74,53,55,316,229,315,72,230,71,126,303,290,124,219,66,79,92,90,91,87', +'82,94,246,98,269,93,308,71,83,85,84,86,62,71,67,69,68,70,126,309,94', +'124,98,310,93,209,94,97,98,196,93,89,88,313,286,75,76,78,77,80,81,62', +'73,74,317,319,97,268,172,72,244,71,97,246,326,327,106,268,79,92,90,91', +'87,82,94,71,98,262,93,261,66,83,85,84,86,137,260,337,94,246,98,246,93', +'118,244,340,106,107,225,106,97,219,319,346,89,88,347,348,75,76,78,77', +'80,81,97,73,74,349,103,351,352,353,72,287,71,66,63,360,361,362,363,79', '92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,', -',75,76,78,77,80,81,,73,74,,,,,,72,,71,,214,,,,,79,92,90,91,87,82,94', -',98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81', -',73,74,,,,,,72,,71,,213,,,,,79,92,90,91,87,82,94,,98,,93,,,83,85,84', +',75,76,78,77,80,81,,73,74,,,,,,72,,71,,95,,,,,79,92,90,91,87,82,94,', +'98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81', +',73,74,,,,,,72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93,,,83,85,84,86', +',,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,', +',,,,79,92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,', +'89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,,,,228,,79,92,90,91,87', +'82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77', +'80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93,,,83,85', +'84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72', +',71,,215,,,,,79,92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,', +',,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90', +'91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76', +'78,77,80,81,,73,74,,,,,,72,,71,,214,,,,,79,92,90,91,87,82,94,,98,,93', +',,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74', +',,,,,72,,71,,213,,,,,79,92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,', +',,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,212,', +',,,79,92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89', +'88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90,91,87,82,94', +',98,,93,,201,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80', +'81,,73,74,,,,,,72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93,,,83,85,84', '86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71', ',,,,,,79,92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,', -',,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,212,,,,,79,92,90,91', -'87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78', -'77,80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93,,201', -'83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,', -',,,72,,71,,,,,238,,79,92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,', -',,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,,,,,,79', -'92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,', -',75,76,78,77,80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90,91,87,82,94,,98', -',93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73', -'74,,,,,,72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,', -',,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,,,,,', -'79,92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88', -',,75,76,78,77,80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90,91,87,82,94,71', -'98,,93,,71,83,85,84,86,,,,94,,98,,93,94,,98,,93,,,97,,,,89,88,,,75,76', -'78,77,80,81,97,73,74,53,55,97,,49,72,50,,,,,,73,74,79,92,,73,74,72,', -',13,,72,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,71,49', -'11,50,,,,,,,62,,,94,,98,40,93,,13,56,,,,,39,,46,,48,100,,47,61,57,,41', -'60,,97,,,58,12,53,55,59,,49,11,50,,,,73,74,,62,,,,72,,40,,,13,56,,,', -',39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,', -'62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53', -'55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57', -',41,60,44,45,,,58,12,53,55,59,,49,11,50,336,,,,,,62,,,,,,40,,,13,56', -',,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50', +',,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90,91,87', +'82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77', +'80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93,,,83,85', +'84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72', +',71,,,,,,,79,92,90,91,87,82,94,71,98,,93,,71,83,85,84,86,,,,94,,98,', +'93,94,,98,,93,,,97,,,,89,88,,,75,76,78,77,80,81,97,73,74,53,55,97,,49', +'72,50,,,,,,73,74,79,92,,73,74,72,,,13,,72,,,,174,191,185,192,48,186', +'194,187,183,181,,176,189,,,,,58,12,195,190,188,53,55,11,,49,,50,,,,62', +',,,71,193,175,,,,56,,13,,,,,94,39,98,46,93,48,100,,47,61,57,,41,60,', +',,,58,12,,,59,53,55,11,97,49,129,50,,,,62,,,78,77,,40,,73,74,56,,13', +',,72,,,39,,46,,48,100,79,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50', ',,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,', -'58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43', +'58,12,53,55,59,71,49,11,50,,,,,,,62,,,94,,98,40,93,,13,56,,,,,39,,46', +',48,100,,47,61,57,,41,60,,97,,,58,12,53,55,59,,49,11,50,,,,73,74,,62', +',,,72,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53', +'55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57', +',41,60,44,45,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,', +',39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,,,', +',,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58', +'12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47', +'61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13', +'56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50', +',,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58', +'12,53,55,59,,49,11,50,336,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43', ',47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,', ',13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49', -'11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44', -'45,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48', -'43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40', -',,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11', -'50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,', -'58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43', -',47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,322,,,,,,62,,,,,,40', -',,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11', +'11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,', +',,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48', +'100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,', +',13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11', '50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,', '58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100', ',47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13', @@ -110,78 +115,78 @@ clist = [ '40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49', '11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,', ',,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48', -'100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,71,,,,,40', -',,13,56,,,,94,39,98,46,93,48,100,,47,61,57,,41,60,,,,,58,12,,,59,53', -'55,11,97,49,129,50,,,,62,,,78,77,,40,,73,74,56,,13,,,72,,,39,,46,,48', -'100,79,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40', -',,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11', -'50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,', -'58,12,,,59,53,55,11,,49,,50,,,,62,,,,,,40,,,171,56,,13,,,,,,39,,46,', -'48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40', -',,13,56,,,,,174,191,185,192,48,186,194,187,183,181,,176,189,,,,,58,12', -'195,190,188,53,55,11,,49,,50,,,,62,,,,,193,175,,,,56,,13,,,,,,39,,46', -',48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,', -'40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49', -'11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,', -',,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48', '100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,', -',13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11', -'50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,', -'58,12,53,55,59,,49,11,50,,,,,,,62,71,,,,,40,,,13,56,,,,94,39,98,46,93', -'48,100,,47,61,57,,41,60,,,,,58,12,,,59,53,55,11,97,49,,50,,,,62,,,78', -'77,,40,,73,74,56,,13,203,,72,,,39,,46,,48,100,79,47,61,57,,41,60,,,', -',58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100', -',47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,71,,,,,40,,,13', -'56,,,,94,39,98,46,93,48,100,,47,61,57,,41,60,,,,,58,12,,,59,53,55,11', -'97,49,,50,,,,62,75,76,78,77,,40,,73,74,56,,13,211,,72,,,39,,46,,48,100', -'79,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13', -'56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50', -',,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58', -'12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47', -'61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13', -'56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50', -',,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58', -'12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47', -'61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,71,,,,,40,,,13,56', -',,,94,39,98,46,93,48,100,,47,61,57,,41,60,,,,,58,12,,,59,53,55,11,97', -'49,292,50,,,,62,75,76,78,77,,40,,73,74,56,,13,,,72,,,39,,46,,48,100', -'79,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13', -'56,,,,,174,191,185,192,48,186,194,187,183,181,,176,189,,,,,58,12,195', -'190,188,53,55,11,,49,,50,,,,62,,,,,193,175,,,,56,,13,,,,,,39,,46,,48', -'100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,', -',13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11', -'50,274,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44', -'45,,,58,12,53,55,59,,49,11,50,272,,,,,,62,,,,,,40,,,13,56,,,,,39,,46', -',48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,,,,,,,62,,', +',13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,,,59,53,55,11,', +'49,,50,322,,,62,,,,,,40,,,171,56,,13,,,,,,39,,46,,48,100,,47,61,57,', +'41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,174', +'191,185,192,48,186,194,187,183,181,,176,189,,,,,58,12,195,190,188,53', +'55,11,,49,,50,,,,62,,,,,193,175,,,,56,,13,,,,,,39,,46,,48,100,,47,61', +'57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,', +'39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62', +',,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55', +'59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,', +'41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,', +'46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,', ',,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59', -',49,11,50,266,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41', -'60,44,45,,,58,12,53,55,59,,49,11,50,355,,,,,,62,,,,,,40,,,13,56,,,,', -'39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,,,,', +',49,11,50,,,,,,,62,71,,,,,40,,,13,56,,,,94,39,98,46,93,48,100,,47,61', +'57,,41,60,,,,,58,12,,,59,53,55,11,97,49,,50,,,,62,,,78,77,,40,,73,74', +'56,,13,203,,72,,,39,,46,,48,100,79,47,61,57,,41,60,,,,,58,12,53,55,59', +',49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41', +'60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,71,,,,,40,,,13,56,,,,94,39', +'98,46,93,48,100,,47,61,57,,41,60,,,,,58,12,,,59,53,55,11,97,49,,50,', +',,62,75,76,78,77,,40,,73,74,56,,13,211,,72,,,39,,46,,48,100,79,47,61', +'57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,', +'39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62', +',,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55', +'59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,', +'41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,', +'46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,', +',,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59', +',49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41', +'60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46', +',48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,294,,,,,,62,,', +',,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59', +',49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41', +'60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,71,,,,,40,,,13,56,,,,94,39', +'98,46,93,48,43,,47,61,57,,41,60,44,45,,,58,12,,,59,53,55,11,97,49,292', +'50,,,,62,75,76,78,77,,40,,73,74,56,,13,,,72,,,39,,46,,48,100,79,47,61', +'57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,', +'39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,132,,,', ',,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53', '55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57', ',41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39', -',46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,357,,,,,,62', +',46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,274,,,,,,62', ',,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53', -'55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57', -'71,41,60,,,,,58,12,,,59,,94,11,98,,93,,,,,62,,,71,,,40,,,,56,,,,,,94', -'97,98,,93,,,,,75,76,78,77,80,81,,73,74,,,,,,72,,,97,,,,,,79,,75,76,78', -'77,80,81,,73,74,71,,,,,72,,,,,,,82,94,79,98,,93,,,83,,,,,71,,,,,,,,', -',,,82,94,97,98,,93,,,83,,75,76,78,77,80,81,,73,74,,,,,,72,,,97,,,,,', -'79,,75,76,78,77,80,81,,73,74,71,,,,,72,,,,,,,82,94,79,98,,93,,,83,,', -',,71,,,,,,,,,,,,82,94,97,98,,93,,,83,,75,76,78,77,80,81,,73,74,,,,,', -'72,,,97,,,,,,79,,75,76,78,77,80,81,,73,74,71,,,,,72,,,,,,87,82,94,79', -'98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,,,71,,75,76,78,77,80,81,,73', -'74,87,82,94,,98,72,93,,,83,85,84,86,,79,,,,,,,,,,,53,55,,97,49,,50,', -'88,,,75,76,78,77,80,81,,73,74,,,13,,,72,,,39,,46,,48,43,79,47,61,57', -'64,41,60,44,45,,,58,12,53,55,59,,49,11,50,359,,,,,,62,,,,,,40,,,13,56', -',,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50', -',,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,', +'55,59,,49,11,50,272,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61', +'57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,266,,,,,,62,,,,,,40,,,13', +'56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11', +'50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,', '58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100', -',47,61,57,,41,60,,,,,58,12,,,59,,,11,,,,256,191,255,192,62,253,194,257', -'251,250,40,252,254,,56,,,,,195,190,258,256,191,255,192,,253,194,257', -'251,250,,252,254,,,193,259,,,195,190,258,256,191,255,192,,253,194,257', -'251,250,,252,254,,,193,259,,,195,190,258,,,,,,,,,,,,,,,,193,259' ] - racc_action_table = arr = ::Array.new(5065, nil) +',47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13', +'56,,,,,39,,46,,48,100,,47,61,57,71,41,60,,,,,58,12,,,59,,94,11,98,,93', +',,,,62,,,71,,,40,,,,56,,,,,,94,97,98,,93,,,,,75,76,78,77,80,81,,73,74', +',,,,,72,,,97,,,,,,79,,75,76,78,77,80,81,,73,74,71,,,,,72,,,,,,,82,94', +'79,98,,93,,,83,,,,,71,,,,,,,,,,,,82,94,97,98,,93,,,83,,75,76,78,77,80', +'81,,73,74,,,,,,72,,,97,,,,,,79,,75,76,78,77,80,81,,73,74,71,,,,,72,', +',,,,,82,94,79,98,,93,,,83,,,,,71,,,,,,,,,,,,82,94,97,98,,93,,,83,,75', +'76,78,77,80,81,,73,74,,,,,,72,,,97,,,,,,79,,75,76,78,77,80,81,,73,74', +'71,,,,,72,,,,,,87,82,94,79,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,', +',,,71,,75,76,78,77,80,81,,73,74,87,82,94,,98,72,93,,,83,85,84,86,,79', +',,,,,,,,,,53,55,,97,49,,50,355,88,,,75,76,78,77,80,81,,73,74,,,13,,', +'72,,,39,,46,,48,43,79,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11', +'50,357,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44', +'45,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48', +'43,,47,61,57,64,41,60,44,45,,,58,12,53,55,59,,49,11,50,359,,,,,,62,', +',,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55', +'59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,', +'41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,', +'46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,,,,,,,62', +',,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,,,59,', +',11,,,,256,191,255,192,62,253,194,257,251,250,40,252,254,,56,,,,,195', +'190,258,256,191,255,192,,253,194,257,251,250,,252,254,,,193,259,,,195', +'190,258,256,191,255,192,,253,194,257,251,250,,252,254,,,193,259,,,195', +'190,258,,,,,,,,,,,,,,,,193,259' ] + racc_action_table = arr = ::Array.new(5222, nil) idx = 0 clist.each do |str| str.split(',', -1).each do |i| @@ -191,175 +196,180 @@ clist = [ end clist = [ -'167,106,128,210,43,222,222,128,249,167,167,167,167,167,119,167,210,167', -'184,181,167,167,167,167,219,229,229,224,224,74,74,43,229,219,130,43', -'106,130,189,167,73,73,222,167,167,222,105,167,167,167,167,167,167,188', -'167,167,187,187,183,184,181,167,229,166,224,222,74,224,47,47,167,167', -'166,166,166,166,166,73,166,189,166,239,275,166,166,166,166,224,186,74', -'275,225,186,187,188,120,187,283,144,183,73,283,166,144,283,47,166,166', -'47,243,166,166,166,166,166,166,187,166,166,120,221,185,50,50,166,185', -'165,101,47,50,272,245,272,166,166,165,165,165,165,165,101,165,101,165', -'101,145,165,165,165,165,100,46,136,136,100,46,220,217,145,50,145,313', -'145,313,216,165,101,114,96,165,165,263,265,165,165,165,165,165,165,267', -'165,165,268,94,145,271,122,165,273,173,7,7,7,7,274,133,165,165,173,173', -'173,173,173,173,173,209,173,99,278,173,173,173,173,279,280,281,207,285', -'203,65,99,63,99,202,99,200,298,198,173,300,44,138,173,173,307,308,173', -'173,173,173,173,173,38,173,173,178,99,37,316,317,173,319,197,320,324', -'325,36,331,332,173,173,197,197,197,197,197,335,197,177,197,5,1,197,197', -'197,197,350,354,356,358,,,,,,,,,,,,197,,,,197,197,,,197,197,197,197', -'197,197,,197,197,,,,,,197,,10,,10,,,,,197,197,10,10,10,10,10,,10,,10', -',,10,10,10,10,,,,,,,,,,,,,,,,10,,,,10,10,,,10,10,10,10,10,10,,10,10', -',,,,,10,,134,,,,,,,10,10,134,134,134,134,134,,134,,134,,,134,134,134', -'134,,,,,,,,,,,,,,,,134,,,,134,134,,,134,134,134,134,134,134,,134,134', -',,,,,134,,127,,,,,,,134,134,127,127,127,127,127,,127,,127,,,127,127', -'127,127,,,,,,,,,,,,,,,,127,,,,127,127,,,127,127,127,127,127,127,,127', -'127,,,,,,127,,112,,112,,,,,127,127,112,112,112,112,112,,112,,112,,,112', -'112,112,112,,,,,,,,,,,,,,,,112,,,,112,112,,,112,112,112,112,112,112', -',112,112,,,,,,112,,111,,111,,,,,112,112,111,111,111,111,111,,111,,111', -',,111,111,111,111,,,,,,,,,,,,,,,,111,,,,111,111,,,111,111,111,111,111', -'111,,111,111,,,,,,111,,110,,110,,,,,111,111,110,110,110,110,110,,110', -',110,,,110,110,110,110,,,,,,,,,,,,,,,,110,,,,110,110,,,110,110,110,110', -'110,110,,110,110,,,,,,110,,312,,,,,,,110,110,312,312,312,312,312,,312', -',312,,,312,312,312,312,,,,,,,,,,,,,,,,312,,,,312,312,,,312,312,312,312', -'312,312,,312,312,,,,,,312,,108,,108,,,,,312,312,108,108,108,108,108', -',108,,108,,,108,108,108,108,,,,,,,,,,,,,,,,108,,,,108,108,,,108,108', -'108,108,108,108,,108,108,,,,,,108,,102,,,,,,,108,108,102,102,102,102', -'102,,102,,102,,102,102,102,102,102,,,,,,,,,,,,,,,,102,,,,102,102,,,102', -'102,102,102,102,102,,102,102,,,,,,102,,168,,,,,168,,102,102,168,168', -'168,168,168,,168,,168,,,168,168,168,168,,,,,,,,,,,,,,,,168,,,,168,168', -',,168,168,168,168,168,168,,168,168,,,,,,168,,291,,,,,,,168,168,291,291', -'291,291,291,,291,,291,,,291,291,291,291,,,,,,,,,,,,,,,,291,,,,291,291', -',,291,291,291,291,291,291,,291,291,,,,,,291,,306,,,,,,,291,291,306,306', -'306,306,306,,306,,306,,,306,306,306,306,,,,,,,,,,,,,,,,306,,,,306,306', -',,306,306,306,306,306,306,,306,306,,,,,,306,,305,,,,,,,306,306,305,305', -'305,305,305,,305,,305,,,305,305,305,305,,,,,,,,,,,,,,,,305,,,,305,305', -',,305,305,305,305,305,305,,305,305,,,,,,305,,295,,,,,,,305,305,295,295', -'295,295,295,,295,,295,,,295,295,295,295,,,,,,,,,,,,,,,,295,,,,295,295', -',,295,295,295,295,295,295,,295,295,,,,,,295,,297,,,,,,,295,295,297,297', -'297,297,297,154,297,,297,,153,297,297,297,297,,,,154,,154,,154,153,', -'153,,153,,,297,,,,297,297,,,297,297,297,297,297,297,154,297,297,91,91', -'153,,91,297,91,,,,,,154,154,297,297,,153,153,154,,,91,,153,,,,91,,91', -',91,91,,91,91,91,,91,91,,,,,91,91,299,299,91,152,299,91,299,,,,,,,91', -',,152,,152,91,152,,299,91,,,,,299,,299,,299,299,,299,299,299,,299,299', -',152,,,299,299,54,54,299,,54,299,54,,,,152,152,,299,,,,152,,299,,,54', -'299,,,,,54,,54,,54,54,,54,54,54,,54,54,,,,,54,54,42,42,54,,42,54,42', -',,,,,,54,,,,,,54,,,42,54,,,,,42,,42,,42,42,,42,42,42,,42,42,,,,,42,42', -'64,64,42,,64,42,64,,,,,,,42,,,,,,42,,,64,42,,,,,64,,64,,64,64,,64,64', -'64,,64,64,64,64,,,64,64,287,287,64,,287,64,287,287,,,,,,64,,,,,,64,', -',287,64,,,,,287,,287,,287,287,,287,287,287,,287,287,287,287,,,287,287', -'66,66,287,,66,287,66,,,,,,,287,,,,,,287,,,66,287,,,,,66,,66,,66,66,', -'66,66,66,,66,66,66,66,,,66,66,67,67,66,,67,66,67,,,,,,,66,,,,,,66,,', -'67,66,,,,,67,,67,,67,67,,67,67,67,,67,67,67,67,,,67,67,68,68,67,,68', -'67,68,,,,,,,67,,,,,,67,,,68,67,,,,,68,,68,,68,68,,68,68,68,,68,68,68', -'68,,,68,68,69,69,68,,69,68,69,,,,,,,68,,,,,,68,,,69,68,,,,,69,,69,,69', -'69,,69,69,69,,69,69,69,69,,,69,69,70,70,69,,70,69,70,,,,,,,69,,,,,,69', -',,70,69,,,,,70,,70,,70,70,,70,70,70,,70,70,70,70,,,70,70,71,71,70,,71', -'70,71,,,,,,,70,,,,,,70,,,71,70,,,,,71,,71,,71,71,,71,71,71,,71,71,,', -',,71,71,72,72,71,,72,71,72,,,,,,,71,,,,,,71,,,72,71,,,,,72,,72,,72,72', -',72,72,72,,72,72,,,,,72,72,286,286,72,,286,72,286,,,,,,,72,,,,,,72,', -',286,72,,,,,286,,286,,286,286,,286,286,286,,286,286,286,286,,,286,286', -'276,276,286,,276,286,276,276,,,,,,286,,,,,,286,,,276,286,,,,,276,,276', -',276,276,,276,276,276,,276,276,,,,,276,276,75,75,276,,75,276,75,,,,', -',,276,,,,,,276,,,75,276,,,,,75,,75,,75,75,,75,75,75,,75,75,,,,,75,75', -'76,76,75,,76,75,76,,,,,,,75,,,,,,75,,,76,75,,,,,76,,76,,76,76,,76,76', -'76,,76,76,,,,,76,76,77,77,76,,77,76,77,,,,,,,76,,,,,,76,,,77,76,,,,', -'77,,77,,77,77,,77,77,77,,77,77,,,,,77,77,78,78,77,,78,77,78,,,,,,,77', -',,,,,77,,,78,77,,,,,78,,78,,78,78,,78,78,78,,78,78,,,,,78,78,79,79,78', -',79,78,79,,,,,,,78,,,,,,78,,,79,78,,,,,79,,79,,79,79,,79,79,79,,79,79', -',,,,79,79,80,80,79,,80,79,80,,,,,,,79,,,,,,79,,,80,79,,,,,80,,80,,80', -'80,,80,80,80,,80,80,,,,,80,80,81,81,80,,81,80,81,,,,,,,80,,,,,,80,,', -'81,80,,,,,81,,81,,81,81,,81,81,81,,81,81,,,,,81,81,82,82,81,,82,81,82', -',,,,,,81,,,,,,81,,,82,81,,,,,82,,82,,82,82,,82,82,82,,82,82,,,,,82,82', -'83,83,82,,83,82,83,,,,,,,82,,,,,,82,,,83,82,,,,,83,,83,,83,83,,83,83', -'83,,83,83,,,,,83,83,84,84,83,,84,83,84,,,,,,,83,,,,,,83,,,84,83,,,,', -'84,,84,,84,84,,84,84,84,,84,84,,,,,84,84,85,85,84,,85,84,85,,,,,,,84', -',,,,,84,,,85,84,,,,,85,,85,,85,85,,85,85,85,,85,85,,,,,85,85,86,86,85', -',86,85,86,,,,,,,85,,,,,,85,,,86,85,,,,,86,,86,,86,86,,86,86,86,,86,86', -',,,,86,86,87,87,86,,87,86,87,,,,,,,86,,,,,,86,,,87,86,,,,,87,,87,,87', -'87,,87,87,87,,87,87,,,,,87,87,88,88,87,,88,87,88,,,,,,,87,,,,,,87,,', -'88,87,,,,,88,,88,,88,88,,88,88,88,,88,88,,,,,88,88,89,89,88,,89,88,89', -',,,,,,88,,,,,,88,,,89,88,,,,,89,,89,,89,89,,89,89,89,,89,89,,,,,89,89', -'90,90,89,,90,89,90,,,,,,,89,150,,,,,89,,,90,89,,,,150,90,150,90,150', -'90,90,,90,90,90,,90,90,,,,,90,90,,,90,49,49,90,150,49,49,49,,,,90,,', -'150,150,,90,,150,150,90,,49,,,150,,,49,,49,,49,49,150,49,49,49,,49,49', -',,,,49,49,92,92,49,,92,49,92,,,,,,,49,,,,,,49,,,92,49,,,,,92,,92,,92', +'173,184,106,120,74,74,272,43,272,173,173,173,173,173,173,173,219,173', +'188,189,173,173,173,173,183,219,181,120,275,222,222,186,136,136,43,186', +'275,106,43,173,210,74,184,173,173,73,73,173,173,173,173,173,173,210', +'173,173,187,187,185,188,189,173,185,168,74,183,222,181,168,222,173,173', +'168,168,168,168,168,100,168,316,168,100,73,168,168,168,168,217,283,222', +'47,47,283,187,128,283,187,46,144,128,239,46,168,144,216,73,168,168,243', +'105,168,168,168,168,168,168,187,168,168,224,224,313,131,313,168,131', +'167,47,245,225,47,119,138,168,168,167,167,167,167,167,249,167,209,167', +'263,145,167,167,167,167,47,101,7,7,7,7,224,265,145,224,145,267,145,268', +'101,167,101,96,101,167,167,271,220,167,167,167,167,167,167,224,167,167', +'273,274,145,207,94,167,278,166,101,279,280,281,203,285,167,167,166,166', +'166,166,166,99,166,202,166,200,65,166,166,166,166,63,198,298,99,178', +'99,300,99,44,177,307,308,38,122,37,166,114,317,319,166,166,320,324,166', +'166,166,166,166,166,99,166,166,325,36,331,332,335,166,221,165,5,1,350', +'354,356,358,166,166,165,165,165,165,165,,165,,165,,,165,165,165,165', +',,,,,,,,,,,,,,,165,,,,165,165,,,165,165,165,165,165,165,,165,165,,,', +',,165,,10,,10,,,,,165,165,10,10,10,10,10,,10,,10,,,10,10,10,10,,,,,', +',,,,,,,,,,10,,,,10,10,,,10,10,10,10,10,10,,10,10,,,,,,10,,197,,,,,,', +'10,10,197,197,197,197,197,,197,,197,,,197,197,197,197,,,,,,,,,,,,,,', +',197,,,,197,197,,,197,197,197,197,197,197,,197,197,,,,,,197,,134,,,', +',,,197,197,134,134,134,134,134,,134,,134,,,134,134,134,134,,,,,,,,,', +',,,,,,134,,,,134,134,,,134,134,134,134,134,134,,134,134,,,,,,134,,130', +',,,,130,,134,134,130,130,130,130,130,,130,,130,,,130,130,130,130,,,', +',,,,,,,,,,,,130,,,,130,130,,,130,130,130,130,130,130,,130,130,,,,,,130', +',127,,,,,,,130,130,127,127,127,127,127,,127,,127,,,127,127,127,127,', +',,,,,,,,,,,,,,127,,,,127,127,,,127,127,127,127,127,127,,127,127,,,,', +',127,,112,,112,,,,,127,127,112,112,112,112,112,,112,,112,,,112,112,112', +'112,,,,,,,,,,,,,,,,112,,,,112,112,,,112,112,112,112,112,112,,112,112', +',,,,,112,,312,,,,,,,112,112,312,312,312,312,312,,312,,312,,,312,312', +'312,312,,,,,,,,,,,,,,,,312,,,,312,312,,,312,312,312,312,312,312,,312', +'312,,,,,,312,,111,,111,,,,,312,312,111,111,111,111,111,,111,,111,,,111', +'111,111,111,,,,,,,,,,,,,,,,111,,,,111,111,,,111,111,111,111,111,111', +',111,111,,,,,,111,,110,,110,,,,,111,111,110,110,110,110,110,,110,,110', +',,110,110,110,110,,,,,,,,,,,,,,,,110,,,,110,110,,,110,110,110,110,110', +'110,,110,110,,,,,,110,,108,,108,,,,,110,110,108,108,108,108,108,,108', +',108,,,108,108,108,108,,,,,,,,,,,,,,,,108,,,,108,108,,,108,108,108,108', +'108,108,,108,108,,,,,,108,,102,,,,,,,108,108,102,102,102,102,102,,102', +',102,,102,102,102,102,102,,,,,,,,,,,,,,,,102,,,,102,102,,,102,102,102', +'102,102,102,,102,102,,,,,,102,,306,,,,,,,102,102,306,306,306,306,306', +',306,,306,,,306,306,306,306,,,,,,,,,,,,,,,,306,,,,306,306,,,306,306', +'306,306,306,306,,306,306,,,,,,306,,305,,,,,,,306,306,305,305,305,305', +'305,,305,,305,,,305,305,305,305,,,,,,,,,,,,,,,,305,,,,305,305,,,305', +'305,305,305,305,305,,305,305,,,,,,305,,291,,,,,,,305,305,291,291,291', +'291,291,,291,,291,,,291,291,291,291,,,,,,,,,,,,,,,,291,,,,291,291,,', +'291,291,291,291,291,291,,291,291,,,,,,291,,297,,,,,,,291,291,297,297', +'297,297,297,,297,,297,,,297,297,297,297,,,,,,,,,,,,,,,,297,,,,297,297', +',,297,297,297,297,297,297,,297,297,,,,,,297,,293,,,,,,,297,297,293,293', +'293,293,293,154,293,,293,,153,293,293,293,293,,,,154,,154,,154,153,', +'153,,153,,,293,,,,293,293,,,293,293,293,293,293,293,154,293,293,215', +'215,153,,215,293,215,,,,,,154,154,293,293,,153,153,154,,,215,,153,,', +',215,215,215,215,215,215,215,215,215,215,,215,215,,,,,215,215,215,215', +'215,54,54,215,,54,,54,,,,215,,,,150,215,215,,,,215,,54,,,,,150,54,150', +'54,150,54,54,,54,54,54,,54,54,,,,,54,54,,,54,49,49,54,150,49,49,49,', +',,54,,,150,150,,54,,150,150,54,,49,,,150,,,49,,49,,49,49,150,49,49,49', +',49,49,,,,,49,49,64,64,49,,64,49,64,,,,,,,49,,,,,,49,,,64,49,,,,,64', +',64,,64,64,,64,64,64,,64,64,64,64,,,64,64,299,299,64,152,299,64,299', +',,,,,,64,,,152,,152,64,152,,299,64,,,,,299,,299,,299,299,,299,299,299', +',299,299,,152,,,299,299,66,66,299,,66,299,66,,,,152,152,,299,,,,152', +',299,,,66,299,,,,,66,,66,,66,66,,66,66,66,,66,66,66,66,,,66,66,67,67', +'66,,67,66,67,,,,,,,66,,,,,,66,,,67,66,,,,,67,,67,,67,67,,67,67,67,,67', +'67,67,67,,,67,67,68,68,67,,68,67,68,,,,,,,67,,,,,,67,,,68,67,,,,,68', +',68,,68,68,,68,68,68,,68,68,68,68,,,68,68,69,69,68,,69,68,69,,,,,,,68', +',,,,,68,,,69,68,,,,,69,,69,,69,69,,69,69,69,,69,69,69,69,,,69,69,70', +'70,69,,70,69,70,,,,,,,69,,,,,,69,,,70,69,,,,,70,,70,,70,70,,70,70,70', +',70,70,70,70,,,70,70,71,71,70,,71,70,71,,,,,,,70,,,,,,70,,,71,70,,,', +',71,,71,,71,71,,71,71,71,,71,71,,,,,71,71,72,72,71,,72,71,72,,,,,,,71', +',,,,,71,,,72,71,,,,,72,,72,,72,72,,72,72,72,,72,72,,,,,72,72,287,287', +'72,,287,72,287,287,,,,,,72,,,,,,72,,,287,72,,,,,287,,287,,287,287,,287', +'287,287,,287,287,287,287,,,287,287,286,286,287,,286,287,286,,,,,,,287', +',,,,,287,,,286,287,,,,,286,,286,,286,286,,286,286,286,,286,286,286,286', +',,286,286,75,75,286,,75,286,75,,,,,,,286,,,,,,286,,,75,286,,,,,75,,75', +',75,75,,75,75,75,,75,75,,,,,75,75,76,76,75,,76,75,76,,,,,,,75,,,,,,75', +',,76,75,,,,,76,,76,,76,76,,76,76,76,,76,76,,,,,76,76,77,77,76,,77,76', +'77,,,,,,,76,,,,,,76,,,77,76,,,,,77,,77,,77,77,,77,77,77,,77,77,,,,,77', +'77,78,78,77,,78,77,78,,,,,,,77,,,,,,77,,,78,77,,,,,78,,78,,78,78,,78', +'78,78,,78,78,,,,,78,78,79,79,78,,79,78,79,,,,,,,78,,,,,,78,,,79,78,', +',,,79,,79,,79,79,,79,79,79,,79,79,,,,,79,79,80,80,79,,80,79,80,,,,,', +',79,,,,,,79,,,80,79,,,,,80,,80,,80,80,,80,80,80,,80,80,,,,,80,80,81', +'81,80,,81,80,81,,,,,,,80,,,,,,80,,,81,80,,,,,81,,81,,81,81,,81,81,81', +',81,81,,,,,81,81,82,82,81,,82,81,82,,,,,,,81,,,,,,81,,,82,81,,,,,82', +',82,,82,82,,82,82,82,,82,82,,,,,82,82,83,83,82,,83,82,83,,,,,,,82,,', +',,,82,,,83,82,,,,,83,,83,,83,83,,83,83,83,,83,83,,,,,83,83,84,84,83', +',84,83,84,,,,,,,83,,,,,,83,,,84,83,,,,,84,,84,,84,84,,84,84,84,,84,84', +',,,,84,84,85,85,84,,85,84,85,,,,,,,84,,,,,,84,,,85,84,,,,,85,,85,,85', +'85,,85,85,85,,85,85,,,,,85,85,86,86,85,,86,85,86,,,,,,,85,,,,,,85,,', +'86,85,,,,,86,,86,,86,86,,86,86,86,,86,86,,,,,86,86,87,87,86,,87,86,87', +',,,,,,86,,,,,,86,,,87,86,,,,,87,,87,,87,87,,87,87,87,,87,87,,,,,87,87', +'88,88,87,,88,87,88,,,,,,,87,,,,,,87,,,88,87,,,,,88,,88,,88,88,,88,88', +'88,,88,88,,,,,88,88,89,89,88,,89,88,89,,,,,,,88,,,,,,88,,,89,88,,,,', +'89,,89,,89,89,,89,89,89,,89,89,,,,,89,89,90,90,89,,90,89,90,,,,,,,89', +',,,,,89,,,90,89,,,,,90,,90,,90,90,,90,90,90,,90,90,,,,,90,90,91,91,90', +',91,90,91,,,,,,,90,,,,,,90,,,91,90,,,,,91,,91,,91,91,,91,91,91,,91,91', +',,,,91,91,92,92,91,,92,91,92,,,,,,,91,,,,,,91,,,92,91,,,,,92,,92,,92', '92,,92,92,92,,92,92,,,,,92,92,93,93,92,,93,92,93,,,,,,,92,,,,,,92,,', -'93,92,,,,,93,,93,,93,93,,93,93,93,,93,93,,,,,93,93,,,93,269,269,93,', -'269,,269,,,,93,,,,,,93,,,93,93,,269,,,,,,269,,269,,269,269,,269,269', -'269,,269,269,,,,,269,269,95,95,269,,95,269,95,,,,,,,269,,,,,,269,,,95', -'269,,,,,95,95,95,95,95,95,95,95,95,95,,95,95,,,,,95,95,95,95,95,262', -'262,95,,262,,262,,,,95,,,,,95,95,,,,95,,262,,,,,,262,,262,,262,262,', -'262,262,262,,262,262,,,,,262,262,97,97,262,,97,262,97,,,,,,,262,,,,', -',262,,,97,262,,,,,97,,97,,97,97,,97,97,97,,97,97,,,,,97,97,98,98,97', +'93,92,,,,,93,,93,,93,93,,93,93,93,,93,93,,,,,93,93,,,93,276,276,93,', +'276,,276,276,,,93,,,,,,93,,,93,93,,276,,,,,,276,,276,,276,276,,276,276', +'276,,276,276,,,,,276,276,95,95,276,,95,276,95,,,,,,,276,,,,,,276,,,95', +'276,,,,,95,95,95,95,95,95,95,95,95,95,,95,95,,,,,95,95,95,95,95,269', +'269,95,,269,,269,,,,95,,,,,95,95,,,,95,,269,,,,,,269,,269,,269,269,', +'269,269,269,,269,269,,,,,269,269,97,97,269,,97,269,97,,,,,,,269,,,,', +',269,,,97,269,,,,,97,,97,,97,97,,97,97,97,,97,97,,,,,97,97,98,98,97', ',98,97,98,,,,,,,97,,,,,,97,,,98,97,,,,,98,,98,,98,98,,98,98,98,,98,98', -',,,,98,98,248,248,98,,248,98,248,,,,,,,98,,,,,,98,,,248,98,,,,,248,', -'248,,248,248,,248,248,248,,248,248,,,,,248,248,41,41,248,,41,248,41', -',,,,,,248,,,,,,248,,,41,248,,,,,41,,41,,41,41,,41,41,41,,41,41,,,,,41', -'41,244,244,41,,244,41,244,,,,,,,41,,,,,,41,,,244,41,,,,,244,,244,,244', -'244,,244,244,244,,244,244,,,,,244,244,40,40,244,,40,244,40,,,,,,,244', -'151,,,,,244,,,40,244,,,,151,40,151,40,151,40,40,,40,40,40,,40,40,,,', -',40,40,,,40,103,103,40,151,103,,103,,,,40,,,151,151,,40,,151,151,40', -',103,103,,151,,,103,,103,,103,103,151,103,103,103,,103,103,,,,,103,103', -'238,238,103,,238,103,238,,,,,,,103,,,,,,103,,,238,103,,,,,238,,238,', -'238,238,,238,238,238,,238,238,,,,,238,238,237,237,238,,237,238,237,', -',,,,,238,155,,,,,238,,,237,238,,,,155,237,155,237,155,237,237,,237,237', -'237,,237,237,,,,,237,237,,,237,107,107,237,155,107,,107,,,,237,155,155', -'155,155,,237,,155,155,237,,107,107,,155,,,107,,107,,107,107,155,107', -'107,107,,107,107,,,,,107,107,39,39,107,,39,107,39,,,,,,,107,,,,,,107', -',,39,107,,,,,39,,39,,39,39,,39,39,39,,39,39,,,,,39,39,315,315,39,,315', -'39,315,,,,,,,39,,,,,,39,,,315,39,,,,,315,,315,,315,315,,315,315,315', -',315,315,,,,,315,315,327,327,315,,327,315,327,,,,,,,315,,,,,,315,,,327', -'315,,,,,327,,327,,327,327,,327,327,327,,327,327,327,327,,,327,327,13', -'13,327,,13,327,13,,,,,,,327,,,,,,327,,,13,327,,,,,13,,13,,13,13,,13', -'13,13,,13,13,,,,,13,13,113,113,13,,113,13,113,,,,,,,13,,,,,,13,,,113', -'13,,,,,113,,113,,113,113,,113,113,113,,113,113,,,,,113,113,235,235,113', -',235,113,235,,,,,,,113,,,,,,113,,,235,113,,,,,235,,235,,235,235,,235', -'235,235,,235,235,,,,,235,235,230,230,235,,230,235,230,,,,,,,235,156', -',,,,235,,,230,235,,,,156,230,156,230,156,230,230,,230,230,230,,230,230', -',,,,230,230,,,230,226,226,230,156,226,226,226,,,,230,156,156,156,156', -',230,,156,156,230,,226,,,156,,,226,,226,,226,226,156,226,226,226,,226', -'226,,,,,226,226,215,215,226,,215,226,215,,,,,,,226,,,,,,226,,,215,226', -',,,,215,215,215,215,215,215,215,215,215,215,,215,215,,,,,215,215,215', -'215,215,12,12,215,,12,,12,,,,215,,,,,215,215,,,,215,,12,,,,,,12,,12', -',12,12,,12,12,12,,12,12,,,,,12,12,214,214,12,,214,12,214,,,,,,,12,,', -',,,12,,,214,12,,,,,214,,214,,214,214,,214,214,214,,214,214,,,,,214,214', -'213,213,214,,213,214,213,213,,,,,,214,,,,,,214,,,213,214,,,,,213,,213', -',213,213,,213,213,213,,213,213,213,213,,,213,213,212,212,213,,212,213', -'212,212,,,,,,213,,,,,,213,,,212,213,,,,,212,,212,,212,212,,212,212,212', -',212,212,212,212,,,212,212,11,11,212,,11,212,11,,,,,,,212,,,,,,212,', -',11,212,,,,,11,,11,,11,11,,11,11,11,,11,11,,,,,11,11,205,205,11,,205', -'11,205,205,,,,,,11,,,,,,11,,,205,11,,,,,205,,205,,205,205,,205,205,205', -',205,205,205,205,,,205,205,344,344,205,,344,205,344,344,,,,,,205,,,', -',,205,,,344,205,,,,,344,,344,,344,344,,344,344,344,,344,344,344,344', -',,344,344,176,176,344,,176,344,176,,,,,,,344,,,,,,344,,,176,344,,,,', -'176,,176,,176,176,,176,176,176,,176,176,,,,,176,176,175,175,176,,175', -'176,175,,,,,,,176,,,,,,176,,,175,176,,,,,175,,175,,175,175,,175,175', -'175,,175,175,,,,,175,175,174,174,175,,174,175,174,,,,,,,175,,,,,,175', -',,174,175,,,,,174,,174,,174,174,,174,174,174,,174,174,,,,,174,174,346', -'346,174,,346,174,346,346,,,,,,174,,,,,,174,,,346,174,,,,,346,,346,,346', -'346,,346,346,346,,346,346,346,346,,,346,346,171,171,346,,171,346,171', -',,,,,,346,,,,,,346,,,171,346,,,,,171,,171,,171,171,,171,171,171,157', -'171,171,,,,,171,171,,,171,,157,171,157,,157,,,,,171,,,158,,,171,,,,171', -',,,,,158,157,158,,158,,,,,157,157,157,157,157,157,,157,157,,,,,,157', -',,158,,,,,,157,,158,158,158,158,158,158,,158,158,159,,,,,158,,,,,,,159', -'159,158,159,,159,,,159,,,,,160,,,,,,,,,,,,160,160,159,160,,160,,,160', -',159,159,159,159,159,159,,159,159,,,,,,159,,,160,,,,,,159,,160,160,160', -'160,160,160,,160,160,161,,,,,160,,,,,,,161,161,160,161,,161,,,161,,', -',,162,,,,,,,,,,,,162,162,161,162,,162,,,162,,161,161,161,161,161,161', -',161,161,,,,,,161,,,162,,,,,,161,,162,162,162,162,162,162,,162,162,163', -',,,,162,,,,,,163,163,163,162,163,,163,,,163,163,163,163,,,,,,,,,,,,', -',,,163,,,,,,164,,163,163,163,163,163,163,,163,163,164,164,164,,164,163', -'164,,,164,164,164,164,,163,,,,,,,,,,,4,4,,164,4,,4,,164,,,164,164,164', -'164,164,164,,164,164,,,4,,,164,,,4,,4,,4,4,164,4,4,4,4,4,4,4,4,,,4,4', -'347,347,4,,347,4,347,347,,,,,,4,,,,,,4,,,347,4,,,,,347,,347,,347,347', -',347,347,347,,347,347,347,347,,,347,347,0,0,347,,0,347,0,,,,,,,347,', -',,,,347,,,0,347,,,,,0,,0,,0,0,,0,0,0,,0,0,0,0,,,0,0,247,247,0,,247,0', -'247,,,,,,,0,,,,,,0,,,247,0,,,,,247,,247,,247,247,,247,247,247,,247,247', -',,,,247,247,,,247,,,247,,,,241,241,241,241,247,241,241,241,241,241,247', -'241,241,,247,,,,,241,241,241,246,246,246,246,,246,246,246,246,246,,246', -'246,,,241,241,,,246,246,246,196,196,196,196,,196,196,196,196,196,,196', -'196,,,246,246,,,196,196,196,,,,,,,,,,,,,,,,196,196' ] - racc_action_check = arr = ::Array.new(5065, nil) +',,,,98,98,262,262,98,,262,98,262,,,,,,,98,,,,,,98,,,262,98,,,,,262,', +'262,,262,262,,262,262,262,,262,262,,,,,262,262,248,248,262,,248,262', +'248,,,,,,,262,,,,,,262,,,248,262,,,,,248,,248,,248,248,,248,248,248', +',248,248,,,,,248,248,174,174,248,,174,248,174,,,,,,,248,,,,,,248,,,174', +'248,,,,,174,,174,,174,174,,174,174,174,,174,174,,,,,174,174,42,42,174', +',42,174,42,,,,,,,174,151,,,,,174,,,42,174,,,,151,42,151,42,151,42,42', +',42,42,42,,42,42,,,,,42,42,,,42,103,103,42,151,103,,103,,,,42,,,151', +'151,,42,,151,151,42,,103,103,,151,,,103,,103,,103,103,151,103,103,103', +',103,103,,,,,103,103,244,244,103,,244,103,244,,,,,,,103,,,,,,103,,,244', +'103,,,,,244,,244,,244,244,,244,244,244,,244,244,,,,,244,244,238,238', +'244,,238,244,238,,,,,,,244,155,,,,,244,,,238,244,,,,155,238,155,238', +'155,238,238,,238,238,238,,238,238,,,,,238,238,,,238,107,107,238,155', +'107,,107,,,,238,155,155,155,155,,238,,155,155,238,,107,107,,155,,,107', +',107,,107,107,155,107,107,107,,107,107,,,,,107,107,41,41,107,,41,107', +'41,,,,,,,107,,,,,,107,,,41,107,,,,,41,,41,,41,41,,41,41,41,,41,41,,', +',,41,41,40,40,41,,40,41,40,,,,,,,41,,,,,,41,,,40,41,,,,,40,,40,,40,40', +',40,40,40,,40,40,,,,,40,40,39,39,40,,39,40,39,,,,,,,40,,,,,,40,,,39', +'40,,,,,39,,39,,39,39,,39,39,39,,39,39,,,,,39,39,315,315,39,,315,39,315', +',,,,,,39,,,,,,39,,,315,39,,,,,315,,315,,315,315,,315,315,315,,315,315', +',,,,315,315,113,113,315,,113,315,113,,,,,,,315,,,,,,315,,,113,315,,', +',,113,,113,,113,113,,113,113,113,,113,113,,,,,113,113,237,237,113,,237', +'113,237,,,,,,,113,,,,,,113,,,237,113,,,,,237,,237,,237,237,,237,237', +'237,,237,237,,,,,237,237,235,235,237,,235,237,235,,,,,,,237,,,,,,237', +',,235,237,,,,,235,,235,,235,235,,235,235,235,,235,235,,,,,235,235,230', +'230,235,,230,235,230,230,,,,,,235,,,,,,235,,,230,235,,,,,230,,230,,230', +'230,,230,230,230,,230,230,,,,,230,230,228,228,230,,228,230,228,,,,,', +',230,,,,,,230,,,228,230,,,,,228,,228,,228,228,,228,228,228,,228,228', +',,,,228,228,327,327,228,,327,228,327,,,,,,,228,156,,,,,228,,,327,228', +',,,156,327,156,327,156,327,327,,327,327,327,,327,327,327,327,,,327,327', +',,327,226,226,327,156,226,226,226,,,,327,156,156,156,156,,327,,156,156', +'327,,226,,,156,,,226,,226,,226,226,156,226,226,226,,226,226,,,,,226', +'226,13,13,226,,13,226,13,,,,,,,226,,,,,,226,,,13,226,,,,,13,,13,,13', +'13,,13,13,13,,13,13,,,,,13,13,50,50,13,,50,13,50,50,,,,,,13,,,,,,13', +',,50,13,,,,,50,,50,,50,50,,50,50,50,,50,50,,,,,50,50,12,12,50,,12,50', +'12,,,,,,,50,,,,,,50,,,12,50,,,,,12,,12,,12,12,,12,12,12,,12,12,,,,,12', +'12,214,214,12,,214,12,214,,,,,,,12,,,,,,12,,,214,12,,,,,214,,214,,214', +'214,,214,214,214,,214,214,,,,,214,214,213,213,214,,213,214,213,213,', +',,,,214,,,,,,214,,,213,214,,,,,213,,213,,213,213,,213,213,213,,213,213', +'213,213,,,213,213,212,212,213,,212,213,212,212,,,,,,213,,,,,,213,,,212', +'213,,,,,212,,212,,212,212,,212,212,212,,212,212,212,212,,,212,212,205', +'205,212,,205,212,205,205,,,,,,212,,,,,,212,,,205,212,,,,,205,,205,,205', +'205,,205,205,205,,205,205,205,205,,,205,205,11,11,205,,11,205,11,,,', +',,,205,,,,,,205,,,11,205,,,,,11,,11,,11,11,,11,11,11,,11,11,,,,,11,11', +'176,176,11,,176,11,176,,,,,,,11,,,,,,11,,,176,11,,,,,176,,176,,176,176', +',176,176,176,,176,176,,,,,176,176,175,175,176,,175,176,175,,,,,,,176', +',,,,,176,,,175,176,,,,,175,,175,,175,175,,175,175,175,157,175,175,,', +',,175,175,,,175,,157,175,157,,157,,,,,175,,,158,,,175,,,,175,,,,,,158', +'157,158,,158,,,,,157,157,157,157,157,157,,157,157,,,,,,157,,,158,,,', +',,157,,158,158,158,158,158,158,,158,158,159,,,,,158,,,,,,,159,159,158', +'159,,159,,,159,,,,,160,,,,,,,,,,,,160,160,159,160,,160,,,160,,159,159', +'159,159,159,159,,159,159,,,,,,159,,,160,,,,,,159,,160,160,160,160,160', +'160,,160,160,161,,,,,160,,,,,,,161,161,160,161,,161,,,161,,,,,162,,', +',,,,,,,,,162,162,161,162,,162,,,162,,161,161,161,161,161,161,,161,161', +',,,,,161,,,162,,,,,,161,,162,162,162,162,162,162,,162,162,163,,,,,162', +',,,,,163,163,163,162,163,,163,,,163,163,163,163,,,,,,,,,,,,,,,,163,', +',,,,164,,163,163,163,163,163,163,,163,163,164,164,164,,164,163,164,', +',164,164,164,164,,163,,,,,,,,,,,344,344,,164,344,,344,344,164,,,164', +'164,164,164,164,164,,164,164,,,344,,,164,,,344,,344,,344,344,164,344', +'344,344,,344,344,344,344,,,344,344,346,346,344,,346,344,346,346,,,,', +',344,,,,,,344,,,346,344,,,,,346,,346,,346,346,,346,346,346,,346,346', +'346,346,,,346,346,4,4,346,,4,346,4,,,,,,,346,,,,,,346,,,4,346,,,,,4', +',4,,4,4,,4,4,4,4,4,4,4,4,,,4,4,347,347,4,,347,4,347,347,,,,,,4,,,,,', +'4,,,347,4,,,,,347,,347,,347,347,,347,347,347,,347,347,347,347,,,347', +'347,171,171,347,,171,347,171,,,,,,,347,,,,,,347,,,171,347,,,,,171,,171', +',171,171,,171,171,171,,171,171,,,,,171,171,0,0,171,,0,171,0,,,,,,,171', +',,,,,171,,,0,171,,,,,0,,0,,0,0,,0,0,0,,0,0,0,0,,,0,0,247,247,0,,247', +'0,247,,,,,,,0,,,,,,0,,,247,0,,,,,247,,247,,247,247,,247,247,247,,247', +'247,,,,,247,247,,,247,,,247,,,,241,241,241,241,247,241,241,241,241,241', +'247,241,241,,247,,,,,241,241,241,246,246,246,246,,246,246,246,246,246', +',246,246,,,241,241,,,246,246,246,196,196,196,196,,196,196,196,196,196', +',196,196,,,246,246,,,196,196,196,,,,,,,,,,,,,,,,196,196' ] + racc_action_check = arr = ::Array.new(5222, nil) idx = 0 clist.each do |str| str.split(',', -1).each do |i| @@ -369,42 +379,42 @@ clist = [ end racc_action_pointer = [ - 4877, 271, nil, nil, 4783, 258, nil, 127, nil, nil, - 309, 4092, 3904, 3616, nil, nil, nil, nil, nil, nil, + 5034, 254, nil, nil, 4893, 241, nil, 89, nil, nil, + 309, 4343, 4108, 4014, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, 232, 178, 218, 3475, - 3234, 3140, 1392, -4, 187, nil, 116, 66, nil, 2711, - 120, nil, nil, nil, 1345, nil, nil, nil, nil, nil, - nil, nil, nil, 221, 1439, 207, 1533, 1580, 1627, 1674, - 1721, 1768, 1815, 38, 27, 1956, 2003, 2050, 2097, 2144, - 2191, 2238, 2285, 2332, 2379, 2426, 2473, 2520, 2567, 2614, - 2661, 1251, 2758, 2805, 144, 2902, 160, 2999, 3046, 201, - 115, 121, 813, 3284, nil, 38, -33, 3428, 750, nil, - 624, 561, 498, 3663, 143, nil, nil, nil, nil, -10, - 83, nil, 167, nil, nil, nil, nil, 435, -5, nil, - 25, nil, nil, 184, 372, nil, 148, nil, 219, nil, + nil, nil, nil, nil, nil, nil, 222, 158, 201, 3588, + 3541, 3494, 3253, -1, 178, nil, 62, 88, nil, 1414, + 4061, nil, nil, nil, 1364, nil, nil, nil, nil, nil, + nil, nil, nil, 213, 1461, 196, 1555, 1602, 1649, 1696, + 1743, 1790, 1837, 43, 2, 1978, 2025, 2072, 2119, 2166, + 2213, 2260, 2307, 2354, 2401, 2448, 2495, 2542, 2589, 2636, + 2683, 2730, 2777, 2824, 147, 2921, 159, 3018, 3065, 197, + 42, 145, 939, 3303, nil, 101, -32, 3447, 876, nil, + 813, 750, 624, 3682, 205, nil, nil, nil, nil, 107, + -9, nil, 207, nil, nil, nil, nil, 561, 87, nil, + 498, 113, nil, nil, 435, nil, 28, nil, 120, nil, nil, nil, nil, nil, 91, 139, nil, nil, nil, nil, - 2671, 3244, 1297, 1210, 1205, 3388, 3767, 4455, 4480, 4536, - 4561, 4617, 4642, 4698, 4743, 120, 57, -6, 876, nil, - nil, 4421, nil, 183, 4327, 4280, 4233, 228, 233, nil, - nil, 8, nil, 47, 7, 86, 53, 54, 42, 27, - nil, nil, nil, nil, nil, nil, 4996, 246, 181, nil, - 203, nil, 211, 149, nil, 4139, nil, 204, nil, 190, - -9, nil, 4045, 3998, 3951, 3854, 124, 121, nil, -1, - 148, 112, 3, nil, 25, 52, 3807, nil, nil, 23, - 3757, nil, nil, nil, nil, 3710, nil, 3378, 3331, 69, - nil, 4952, nil, 100, 3187, 122, 4974, 4924, 3093, -4, + 1374, 3263, 1507, 1273, 1268, 3407, 3927, 4471, 4496, 4552, + 4577, 4633, 4658, 4714, 4759, 246, 183, 120, 57, nil, + nil, 4987, nil, -6, 3206, 4437, 4390, 182, 205, nil, + nil, 15, nil, 13, -10, 23, -4, 54, 7, 8, + nil, nil, nil, nil, nil, nil, 5153, 372, 168, nil, + 185, nil, 193, 125, nil, 4296, nil, 173, nil, 127, + 28, nil, 4249, 4202, 4155, 1314, 64, 51, nil, -9, + 164, 243, 27, nil, 117, 90, 3967, nil, 3870, nil, + 3823, nil, nil, nil, nil, 3776, nil, 3729, 3397, 88, + nil, 5109, nil, 99, 3350, 119, 5131, 5081, 3159, 128, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, 2952, 146, nil, 163, nil, 110, 148, 2855, - nil, 176, 99, 179, 163, 70, 1909, nil, 168, 201, - 205, 207, nil, 62, nil, 205, 1862, 1486, nil, nil, - nil, 939, nil, nil, nil, 1128, nil, 1191, 217, 1298, - 217, nil, nil, nil, nil, 1065, 1002, 225, 166, nil, - nil, nil, 687, 130, nil, 3522, 240, 218, nil, 243, - 245, nil, nil, nil, 245, 246, nil, 3569, nil, nil, - nil, 232, 249, nil, nil, 257, nil, nil, nil, nil, - nil, nil, nil, nil, 4186, nil, 4374, 4830, nil, nil, - 267, nil, nil, nil, 268, nil, 269, nil, 270, nil, + nil, nil, 3112, 119, nil, 148, nil, 92, 129, 2971, + nil, 162, -25, 173, 152, 16, 2874, nil, 148, 179, + 183, 185, nil, 53, nil, 183, 1931, 1884, nil, nil, + nil, 1128, nil, 1254, nil, nil, nil, 1191, 206, 1508, + 207, nil, nil, nil, nil, 1065, 1002, 214, 155, nil, + nil, nil, 687, 90, nil, 3635, 71, 199, nil, 223, + 226, nil, nil, nil, 226, 236, nil, 3917, nil, nil, + nil, 222, 239, nil, nil, 240, nil, nil, nil, nil, + nil, nil, nil, nil, 4799, nil, 4846, 4940, nil, nil, + 246, nil, nil, nil, 247, nil, 248, nil, 249, nil, nil, nil, nil, nil ] racc_action_default = [ @@ -421,7 +431,7 @@ racc_action_default = [ -211, -34, -211, -211, -72, -211, -211, -211, -211, -82, -211, -211, -211, -211, -210, -134, -153, -154, -116, -210, -210, -143, -145, -146, -147, -148, -149, -42, -211, -167, - -211, -170, -171, -211, -182, -177, -211, 364, -5, -8, + -211, -211, -170, -171, -182, -177, -211, 364, -5, -8, -11, -12, -13, -14, -211, -17, -18, -162, -163, -19, -20, -21, -22, -23, -24, -25, -26, -28, -29, -30, -31, -32, -33, -35, -36, -37, -38, -39, -211, -40, @@ -430,14 +440,14 @@ racc_action_default = [ -196, -201, -202, -204, -205, -207, -124, -123, -211, -122, -211, -41, -192, -67, -77, -211, -80, -192, -158, -161, -211, -74, -211, -211, -211, -124, -194, -210, -155, -211, - -211, -211, -211, -151, -211, -211, -211, -165, -168, -211, + -211, -211, -211, -151, -211, -211, -211, -165, -211, -168, -211, -179, -180, -181, -183, -211, -16, -211, -211, -192, -102, -124, -112, -211, -195, -211, -193, -211, -211, -192, -127, -129, -197, -198, -199, -200, -203, -206, -208, -209, -120, -121, -193, -211, -69, -211, -79, -211, -193, -211, -73, -211, -85, -211, -91, -211, -211, -95, -194, -192, -211, -211, -137, -211, -156, -192, -210, -211, -144, -152, - -150, -43, -166, -169, -172, -173, -184, -104, -211, -193, + -150, -43, -166, -173, -169, -172, -184, -104, -211, -193, -192, -108, -114, -109, -126, -130, -131, -211, -66, -78, -81, -159, -160, -85, -84, -211, -211, -91, -90, -211, -211, -99, -94, -96, -211, -211, -110, -210, -138, -139, @@ -447,29 +457,29 @@ racc_action_default = [ -133, -87, -92, -97 ] racc_goto_table = [ - 2, 119, 3, 99, 101, 102, 104, 135, 170, 123, - 132, 207, 133, 178, 243, 318, 314, 125, 217, 177, - 288, 332, 289, 220, 198, 200, 277, 146, 149, 1, - 245, 108, 110, 111, 112, 148, 148, 216, 65, 128, - 302, 127, 241, 147, 147, 239, 134, 140, 141, 142, - 143, 304, 320, 280, 263, 276, 343, 342, 345, 267, - 281, 144, 350, 127, 145, 328, 221, 150, 151, 152, + 2, 119, 3, 99, 101, 102, 104, 135, 170, 133, + 123, 177, 178, 207, 243, 318, 314, 125, 332, 217, + 140, 141, 142, 143, 220, 1, 288, 245, 289, 216, + 277, 108, 110, 111, 112, 302, 65, 198, 200, 128, + 241, 127, 130, 147, 147, 239, 134, 146, 149, 148, + 148, 263, 304, 280, 320, 276, 267, 342, 345, 350, + 343, 144, 281, 127, 145, 328, 221, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, - 163, 164, 165, 166, 167, 168, 240, 173, 323, 197, - 197, 298, 204, 202, 311, 127, 130, 210, 138, 127, - 169, 307, 139, 234, 235, 173, 233, nil, nil, nil, - nil, nil, nil, nil, 249, 324, 218, nil, nil, nil, - nil, 218, 223, nil, 285, nil, nil, nil, nil, nil, - nil, 325, nil, 279, nil, nil, nil, 331, nil, 278, - 119, nil, nil, nil, nil, nil, nil, nil, nil, 123, - nil, nil, 339, nil, nil, nil, nil, 125, nil, 300, + 163, 164, 165, 166, 167, 168, 240, 173, 298, 197, + 197, 204, 323, 202, 311, 127, 138, 210, 307, 127, + 131, 169, 139, 234, 235, 173, 233, nil, nil, nil, + nil, nil, nil, 249, nil, 324, 218, nil, nil, nil, + nil, 218, 223, nil, nil, nil, 285, nil, 325, nil, + nil, 278, 279, nil, 331, nil, nil, nil, nil, nil, + 119, nil, nil, nil, nil, nil, nil, nil, nil, 339, + 123, nil, nil, nil, nil, nil, nil, 125, 300, nil, nil, nil, nil, 168, nil, nil, 108, 110, 111, nil, nil, nil, 264, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, 123, nil, 123, nil, 296, 294, - nil, 133, 125, nil, 125, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, 123, nil, 123, 296, 295, + nil, nil, 125, nil, 125, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 265, 127, 173, nil, nil, nil, nil, 271, 273, 338, nil, nil, nil, 291, 282, - nil, nil, 295, nil, nil, nil, nil, 134, nil, 291, + 293, nil, 130, nil, nil, nil, nil, 134, nil, 291, 297, nil, nil, nil, nil, nil, 173, nil, 329, 305, 306, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 291, nil, nil, nil, nil, nil, @@ -484,29 +494,29 @@ racc_goto_table = [ nil, nil, nil, nil, 354, nil, 356, 358 ] racc_goto_check = [ - 2, 63, 3, 9, 9, 9, 38, 79, 50, 36, - 75, 43, 31, 55, 54, 46, 45, 30, 64, 53, - 71, 65, 71, 64, 59, 59, 48, 12, 12, 1, - 37, 9, 9, 9, 9, 31, 31, 53, 5, 11, - 56, 9, 57, 30, 30, 51, 9, 7, 7, 7, - 7, 60, 49, 54, 37, 47, 44, 45, 46, 37, - 67, 11, 65, 9, 9, 68, 70, 9, 9, 9, + 2, 63, 3, 9, 9, 9, 38, 79, 50, 75, + 36, 53, 55, 43, 54, 46, 45, 30, 65, 64, + 7, 7, 7, 7, 64, 1, 71, 37, 71, 53, + 48, 9, 9, 9, 9, 56, 5, 59, 59, 11, + 57, 9, 9, 30, 30, 51, 9, 12, 12, 31, + 31, 37, 60, 54, 49, 47, 37, 45, 46, 65, + 44, 11, 67, 9, 9, 68, 70, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 50, 9, 48, 9, - 9, 37, 42, 11, 73, 9, 74, 11, 5, 9, - 13, 37, 6, 80, 81, 9, 83, nil, nil, nil, - nil, nil, nil, nil, 55, 54, 3, nil, nil, nil, - nil, 3, 3, nil, 43, nil, nil, nil, nil, nil, - nil, 37, nil, 55, nil, nil, nil, 37, nil, 53, - 63, nil, nil, nil, nil, nil, nil, nil, nil, 36, - nil, nil, 37, nil, nil, nil, nil, 30, nil, 55, + 9, 9, 9, 9, 9, 9, 50, 9, 37, 9, + 9, 42, 48, 11, 73, 9, 5, 11, 37, 9, + 74, 13, 6, 80, 81, 9, 83, nil, nil, nil, + nil, nil, nil, 55, nil, 54, 3, nil, nil, nil, + nil, 3, 3, nil, nil, nil, 43, nil, 37, nil, + nil, 53, 55, nil, 37, nil, nil, nil, nil, nil, + 63, nil, nil, nil, nil, nil, nil, nil, nil, 37, + 36, nil, nil, nil, nil, nil, nil, 30, 55, nil, nil, nil, nil, 9, nil, nil, 9, 9, 9, nil, nil, nil, 38, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, 36, nil, 36, nil, 79, 75, - nil, 31, 30, nil, 30, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, 36, nil, 36, 79, 75, + nil, nil, 30, nil, 30, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 2, 9, 9, nil, nil, nil, nil, 2, 2, 50, nil, nil, nil, 9, 3, - nil, nil, 9, nil, nil, nil, nil, 9, nil, 9, + 9, nil, 9, nil, nil, nil, nil, 9, nil, 9, 9, nil, nil, nil, nil, nil, 9, nil, 63, 9, 9, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 9, nil, nil, nil, nil, nil, @@ -521,14 +531,14 @@ racc_goto_check = [ nil, nil, nil, nil, 2, nil, 2, 2 ] racc_goto_pointer = [ - nil, 29, 0, 2, nil, 34, 36, -20, nil, -8, - nil, -10, -46, 7, nil, nil, nil, nil, nil, nil, + nil, 25, 0, 2, nil, 32, 36, -47, nil, -8, + nil, -10, -26, 8, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - -30, -38, nil, nil, nil, nil, -38, -148, -31, nil, - nil, nil, -13, -95, -259, -256, -259, -159, -188, -223, - -85, -126, nil, -76, -163, -82, -204, -131, nil, -73, - -195, nil, nil, -45, -96, -265, nil, -157, -218, nil, - -54, -202, nil, -174, 46, -40, nil, nil, nil, -47, + -30, -24, nil, nil, nil, nil, -37, -151, -31, nil, + nil, nil, -14, -93, -255, -256, -259, -159, -184, -221, + -85, -126, nil, -84, -163, -83, -209, -133, nil, -60, + -194, nil, nil, -45, -95, -268, nil, -155, -218, nil, + -54, -196, nil, -174, 50, -41, nil, nil, nil, -47, -33, -32, nil, -30 ] racc_goto_default = [ From a0bca5ee9f2fc10fc3e2cd24a0007307b5570ef9 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 24 Oct 2013 17:46:52 +0200 Subject: [PATCH 044/800] (maint) Improve handling of loc/sourcepos calculation in parser support There where many checks if being at end of input (one was also missing). --- lib/puppet/pops/parser/parser_support.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/puppet/pops/parser/parser_support.rb b/lib/puppet/pops/parser/parser_support.rb index cd50cb38a..4ca8984a5 100644 --- a/lib/puppet/pops/parser/parser_support.rb +++ b/lib/puppet/pops/parser/parser_support.rb @@ -115,10 +115,15 @@ class Puppet::Pops::Parser::Parser # end except = Puppet::ParseError.new(error) - except.line = value[:line] unless token == 0 - path = value[:file] + if token != 0 + path = value[:file] + except.line = value[:line] + except.pos = value[:pos] + else + # At end of input, use what the lexer thinks is the source file + path = lexer.file + end except.file = path if path.is_a?(String) && !path.empty? - except.pos = value[:pos] unless token == 0 raise except end From 8ca589f431560ed67f38850056dee115d071cb9f Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 24 Oct 2013 17:47:21 +0200 Subject: [PATCH 045/800] (perf) Include perf benchmark for parse + validate --- .../pops/parser/evaluating_parser_spec.rb | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/spec/unit/pops/parser/evaluating_parser_spec.rb b/spec/unit/pops/parser/evaluating_parser_spec.rb index eef809c32..76a5e5628 100644 --- a/spec/unit/pops/parser/evaluating_parser_spec.rb +++ b/spec/unit/pops/parser/evaluating_parser_spec.rb @@ -88,22 +88,22 @@ describe 'The hiera2 string evaluator' do end end -# describe "when benchmarked" do -# -# it "Pops Parser", :profile => true do -# code = 'if true -#{ -#$a = 10 + 10 -#} -#else -#{ -#$a = "interpolate ${foo} and stuff" -#} -#' -# parser = Puppet::Pops::Parser::EvaluatingParser.new() -# m = Benchmark.measure { 10000.times { parser.parse_string(code) }} -# puts "Parser: #{m}" -# end -# end + describe "when benchmarked" do + + it "Pops Parser", :profile => true do + code = 'if true +{ +$a = 10 + 10 +} +else +{ +$a = "interpolate ${foo} and stuff" +} +' + parser = Puppet::Pops::Parser::EvaluatingParser.new() + m = Benchmark.measure { 10000.times { parser.parse_string(code) }} + puts "Parser: #{m}" + end + end end From 8a154ab2867796891a6432a63aca684b223771da Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 25 Oct 2013 04:54:22 +0200 Subject: [PATCH 046/800] (perf) Make location calculation lazy and pass all tests A class (and subclasses) called Locatable were added to delay the computation of location as long as possible. SourcePosAdapter was modified to hold a Locatable instead of individual measure (line, pos, etc.). Factory was modified to make use of the new Locator when creating source positions. It also got some additional performance tweaks. The egrammar had a bug where it cause recursive lazy location resolution. A located result should not be located again since it resolves to itself. Lexer support was modified to make use of Locatabel. Transformer spec used an old SourcePosAdapter and required changing. --- lib/puppet/pops.rb | 1 + lib/puppet/pops/adapters.rb | 59 +++++--- lib/puppet/pops/model/ast_transformer.rb | 6 + lib/puppet/pops/model/factory.rb | 63 +++++--- lib/puppet/pops/parser/egrammar.ra | 2 +- lib/puppet/pops/parser/eparser.rb | 2 +- lib/puppet/pops/parser/lexer_support.rb | 7 +- lib/puppet/pops/parser/locatable.rb | 137 ++++++++++++++++++ lib/puppet/pops/parser/parser_support.rb | 36 ++--- .../evaluator/string_interpolation_spec.rb | 2 +- spec/unit/pops/model/ast_transformer_spec.rb | 11 +- 11 files changed, 256 insertions(+), 70 deletions(-) create mode 100644 lib/puppet/pops/parser/locatable.rb diff --git a/lib/puppet/pops.rb b/lib/puppet/pops.rb index 129d4e9d2..ffc4ccff6 100644 --- a/lib/puppet/pops.rb +++ b/lib/puppet/pops.rb @@ -72,6 +72,7 @@ module Puppet require 'puppet/pops/parser/eparser' require 'puppet/pops/parser/parser_support' require 'puppet/pops/parser/locator' + require 'puppet/pops/parser/locatable' require 'puppet/pops/parser/lexer' require 'puppet/pops/parser/lexer2' require 'puppet/pops/parser/evaluating_parser' diff --git a/lib/puppet/pops/adapters.rb b/lib/puppet/pops/adapters.rb index 13e53845c..111fb9971 100644 --- a/lib/puppet/pops/adapters.rb +++ b/lib/puppet/pops/adapters.rb @@ -22,32 +22,57 @@ module Puppet::Pops::Adapters attr_accessor :origin end - # A SourcePosAdapter describes a position relative to an origin. (Typically an {OriginAdapter} is - # associated with the root of a model. This origin has a URI to the resource, and a line number. - # The offset in the SourcePosAdapter is then relative to this origin. - # (This somewhat complex structure makes it possible to correctly refer to a source position + # A SourcePosAdapter describes hold a reference to something *locateable* (a position in source text). + # This is represented by an instance of Puppet::Pops::Parser::Locateable (it has an offset, a length, and + # a Puppet::Pops::Parser::Locator) that are used together to provide derived information (line, and position + # on line). + # This somewhat complex structure makes it possible to correctly refer to a source position # in source that is embedded in some resource; a parser only sees the embedded snippet of source text - # and does not know where it was embedded). + # and does not know where it was embedded. It also enables lazy evaluation of source positions (they are + # rarely needed - typically just when there is an error to report. + # + # @note It is relatively expensive to compute line and postion on line - it is not something that + # should be done for every token or model object. # # @see Puppet::Pops::Utils#find_adapter # class SourcePosAdapter < Puppet::Pops::Adaptable::Adapter - # @return [Fixnum] The start line in source starting from 1 - attr_accessor :line + attr_accessor :locatable - # @return [Fixnum] The position on the start_line (in characters) starting from 0 - attr_accessor :pos + def locator + locatable.locator + end - # @return [Fixnum] The (start) offset of source text characters - # (starting from 0) representing the adapted object. - # Value may be nil - attr_accessor :offset + def offset + locatable.offset + end - # @return [Fixnum] The length (count) of characters of source text - # representing the adapted object from the origin. Not including any - # trailing whitespace. - attr_accessor :length + def length + locatable.length + end + # Produces the line number for the given offset. + # @note This is an expensive operation + # + def line + locatable.locator.line_for_offset(offset) + end + + # Produces the position on the line of the given offset. + # @note This is an expensive operation + # + def pos + locatable.locator.pos_on_line(offset) + end + + # Extracts the text represented by this source position (the string is obtained from the locator) + def extract_text + locatable.locator.string.slice(offset, length) + end + + # Extracts the text represented by this source position from a given string (which needs to be identical + # to what is held in the locator - why is this needed ? + # TODO: def extract_text_from_string(string) string.slice(offset, length) end diff --git a/lib/puppet/pops/model/ast_transformer.rb b/lib/puppet/pops/model/ast_transformer.rb index eb90ca5b5..1a0a14ca3 100644 --- a/lib/puppet/pops/model/ast_transformer.rb +++ b/lib/puppet/pops/model/ast_transformer.rb @@ -33,6 +33,12 @@ class Puppet::Pops::Model::AstTransformer klass.new(merge_location(hash, o)) end + # THIS IS AN EXPENSIVE OPERATION + # The 3x AST requires line, pos etc. to be recorded directly in the AST nodes and this information + # must be computed. + # (Newer implementation only computes the information that is actually needed; typically when raising an + # exception). + # def merge_location(hash, o) if o pos = {} diff --git a/lib/puppet/pops/model/factory.rb b/lib/puppet/pops/model/factory.rb index 4d9dcddaf..7a47b1595 100644 --- a/lib/puppet/pops/model/factory.rb +++ b/lib/puppet/pops/model/factory.rb @@ -19,8 +19,15 @@ class Puppet::Pops::Model::Factory # Initialize a factory with a single object, or a class with arguments applied to build of # created instance # - def initialize popsobj, *args - @current = to_ops(popsobj, *args) + def initialize o, *args + @current = case o + when Model::PopsObject + o + when Puppet::Pops::Model::Factory + o.current + else + build(o, *args) + end end # Polymorphic build @@ -33,7 +40,7 @@ class Puppet::Pops::Model::Factory end end - # Polymorphic inerpolate + # Polymorphic interpolate def interpolate() begin @@interpolation_visitor.visit_this(self, current) @@ -434,15 +441,21 @@ class Puppet::Pops::Model::Factory # Records the position (start -> end) and computes the resulting length. # - def record_position(start_pos, end_pos) + def record_position(start_locatable, end_locatable) Puppet::Pops::Adapters::SourcePosAdapter.adapt(current) do |a| - a.line = start_pos.line - a.offset = start_pos.offset - a.pos = start_pos.pos - a.length = start_pos.length - if(end_pos.offset && end_pos.length) - a.length = end_pos.offset + end_pos.length - start_pos.offset + if start_locatable && end_locatable + a.locatable = Puppet::Pops::Parser::Locatable::Range.new(start_locatable, end_locatable) + else + a.locatable = Puppet::Pops::Parser::Locatable::Lazy.new(start_locatable) end + +# a.line = start_pos.line +# a.offset = start_pos.offset +# a.pos = start_pos.pos +# a.length = start_pos.length +# if(end_pos.offset && end_pos.length) +# a.length = end_pos.offset + end_pos.length - start_pos.offset +# end end self end @@ -466,18 +479,19 @@ class Puppet::Pops::Model::Factory Puppet::Pops::Adapters::SourcePosAdapter.adapt(current) end - # Returns documentation string, or nil if not available - # @return [String, nil] associated documentation if available - def doc() - a = Puppet::Pops::Adapters::SourcePosAdapter.adapt(current) - return a.documentation if a - nil - end - - def doc=(doc_string) - a = Puppet::Pops::Adapters::SourcePosAdapter.adapt(current) - a.documentation = doc_string - end +# UNUSED AND WRONG +# # Returns documentation string, or nil if not available +# # @return [String, nil] associated documentation if available +# def doc() +# a = Puppet::Pops::Adapters::Source PosAdapter.adapt(current) +# return a.documentation if a +# nil +# end +# +# def doc=(doc_string) +# a = Puppet::Pops::Adapters::Source PosAdapter.adapt(current) +# a.documentation = doc_string +# end # Returns symbolic information about an expected share of a resource expression given the LHS of a resource expr. # @@ -886,8 +900,11 @@ class Puppet::Pops::Model::Factory # Checks if the object is already a model object, or build it def to_ops(o, *args) - if o.kind_of?(Model::PopsObject) + case o + when Model::PopsObject o + when Puppet::Pops::Model::Factory + o.current else build(o, *args) end diff --git a/lib/puppet/pops/parser/egrammar.ra b/lib/puppet/pops/parser/egrammar.ra index c7fc20dc9..6465d8a44 100644 --- a/lib/puppet/pops/parser/egrammar.ra +++ b/lib/puppet/pops/parser/egrammar.ra @@ -560,7 +560,7 @@ node_definition_expression # Produces a LiteralExpression (string, :default, or regexp) # String with interpolation is validated for better error message hostname - : dotted_name { result = val[0]; loc result, val[0] } + : dotted_name { result = val[0] } | quotedtext { result = val[0] } | DEFAULT { result = Factory.literal(:default); loc result, val[0] } | regex diff --git a/lib/puppet/pops/parser/eparser.rb b/lib/puppet/pops/parser/eparser.rb index 691fd250d..7e6144f5b 100644 --- a/lib/puppet/pops/parser/eparser.rb +++ b/lib/puppet/pops/parser/eparser.rb @@ -1980,7 +1980,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 557) module_eval(<<'.,.,', 'egrammar.ra', 562) def _reduce_145(val, _values, result) - result = val[0]; loc result, val[0] + result = val[0] result end .,., diff --git a/lib/puppet/pops/parser/lexer_support.rb b/lib/puppet/pops/parser/lexer_support.rb index 3c3b87bc8..22c5703ab 100644 --- a/lib/puppet/pops/parser/lexer_support.rb +++ b/lib/puppet/pops/parser/lexer_support.rb @@ -63,7 +63,7 @@ module Puppet::Pops::Parser::LexerSupport # and its position in its source container. There is a cost associated with computing the # line and position on line information. # - class TokenValue + class TokenValue < Puppet::Pops::Parser::Locatable attr_reader :token_array attr_reader :offset attr_reader :locator @@ -73,6 +73,11 @@ module Puppet::Pops::Parser::LexerSupport @offset = offset @locator = locator end + + def length + @token_array[2] + end + def [](key) case key when :value diff --git a/lib/puppet/pops/parser/locatable.rb b/lib/puppet/pops/parser/locatable.rb new file mode 100644 index 000000000..604668603 --- /dev/null +++ b/lib/puppet/pops/parser/locatable.rb @@ -0,0 +1,137 @@ +# Interface for something that is "locateable" +# It holds a reference to a Locator which can compute line number, position on line etc. +# The basic information is offset and length. +class Puppet::Pops::Parser::Locatable + # The locator for this locatable + def locator + end + + # The offset in the locator's content + def offset + end + + # The length in the locator from the given offset + def length + end + + # The file (if given) of the locator's content + def file + locator.file + end + + # This class is useful for testing + class Fixed < Puppet::Pops::Parser::Locatable + attr_reader :offset + attr_reader :pos + attr_reader :line + attr_reader :length + + def initialize(line, column, offset, length) + @line = line + @pos = column + @offset = offset + @length = length + end + + def locator + self + end + + def line_for_offset(offset) + @line + end + + def pos_on_line(offset) + @pos + end + end + + class Lazy < Puppet::Pops::Parser::Locatable + attr_reader :morphed + + def initialize(loc_reference) + @ref = loc_reference # some object from which location information can be derived + @morphed = false + end + + def locator + ensure_morphed + @ref.locator + end + + def offset + ensure_morphed + @ref.offset + end + + def length + ensure_morphed + @ref.length + end + + def ensure_morphed + unless @morphed + @morphed = true + @ref = morph(@ref) or raise ArgumentError.new("Internal Error: Range not given something locatable in 'from'") + end + end + + def morph(o) + # the object may be a Locatable (= done), a PopsObject (find its source pos and locator), or + # a factory wrapping a pops object + # + o = o.current if o.is_a? Puppet::Pops::Model::Factory + case o + when Puppet::Pops::Model::PopsObject + adapter = Puppet::Pops::Adapters::SourcePosAdapter.get(o) + adapter.nil? ? nil : adapter.locatable + when Puppet::Pops::Parser::Locatable + o + else + raise ArgumentError, "InternalError: Locator Range can not handle an instance of #{o.class}" + end + end + + end + # Combines two Locators into a range. The given from locator must have smaller offset, but + # may overlap with given to-locator. + # + class Range < Puppet::Pops::Parser::Locatable::Lazy + attr_reader :from + attr_reader :to + attr_reader :morphed + + def initialize(from, to) + @from = from + @to = to + @morphed = false + end + + def locator + ensure_morphed + @from.locator + end + + def offset + ensure_morphed + @from.offset + end + + def length + ensure_morphed + @length ||= @to.offset - @from.offset + @to.length + end + + def ensure_morphed + return if @morphed + @morphed = true + @from = morph(@from) or raise ArgumentError.new("Internal Error: Range not given something locatable in 'from'") + if @to.nil? + @to = @from # i.e. no range if @to is nil + else + @to = morph(@to) or raise ArgumentError.new("Internal Error: Range not given something locatable in 'to'") + end + end + + end +end diff --git a/lib/puppet/pops/parser/parser_support.rb b/lib/puppet/pops/parser/parser_support.rb index 4ca8984a5..dfff7b15c 100644 --- a/lib/puppet/pops/parser/parser_support.rb +++ b/lib/puppet/pops/parser/parser_support.rb @@ -141,8 +141,8 @@ class Puppet::Pops::Parser::Parser # @return [Puppet::Pops::Model::Factory] the given factory # @api private # - def loc(factory, start_token, end_token = nil) - factory.record_position(sourcepos(start_token), sourcepos(end_token)) + def loc(factory, start_locateable, end_locateable = nil) + factory.record_position(start_locateable, end_locateable) end # Associate documentation with the factory wrapped model object. @@ -152,22 +152,22 @@ class Puppet::Pops::Parser::Parser factory.doc = doc_string end - def sourcepos(o) - if !o - Puppet::Pops::Adapters::SourcePosAdapter.new - elsif o.is_a? Puppet::Pops::Model::Factory - # It is a built model element with loc set returns start at pos 0 - o.loc - else - loc = Puppet::Pops::Adapters::SourcePosAdapter.new - # It must be a token - loc.line = o[:line] - loc.pos = o[:pos] - loc.offset = o[:offset] - loc.length = o[:length] - loc - end - end +# def sourcepos(o) +# if !o +# Puppet::Pops::Adapters::SourcePosAdapter.new +# elsif o.is_a? Puppet::Pops::Model::Factory +# # It is a built model element with loc set returns start at pos 0 +# o.loc +# else +# loc = Puppet::Pops::Adapters::SourcePosAdapter.new +# # It must be a token +# loc.line = o[:line] +# loc.pos = o[:pos] +# loc.offset = o[:offset] +# loc.length = o[:length] +# loc +# end +# end def aryfy(o) o = [o] unless o.is_a?(Array) diff --git a/spec/unit/pops/evaluator/string_interpolation_spec.rb b/spec/unit/pops/evaluator/string_interpolation_spec.rb index f1018fcf8..35d0e529e 100644 --- a/spec/unit/pops/evaluator/string_interpolation_spec.rb +++ b/spec/unit/pops/evaluator/string_interpolation_spec.rb @@ -27,7 +27,7 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do evaluate(a_block).should == "value is 10 yo" end - it "should interpolate any expression in a text expression, \"${var*2}\"" do + it "should interpolate any expression in a text expression, \"${$var*2}\"" do a_block = block(fqn('a').set(5), string('value is ', text(var(fqn('a')) * 2) , ' yo')) evaluate(a_block).should == "value is 10 yo" end diff --git a/spec/unit/pops/model/ast_transformer_spec.rb b/spec/unit/pops/model/ast_transformer_spec.rb index 969e944de..bf38463bc 100644 --- a/spec/unit/pops/model/ast_transformer_spec.rb +++ b/spec/unit/pops/model/ast_transformer_spec.rb @@ -55,7 +55,7 @@ describe Puppet::Pops::Model::AstTransformer do it "preserves the file location" do model = literal(1) - model.record_position(location(3, 1, 10), location(3, 2, 11)) + model.record_position(location(3, 1, 10, 1), nil) ast = transform(model) @@ -68,12 +68,7 @@ describe Puppet::Pops::Model::AstTransformer do transformer.transform(model) end - def location(line, column, offset) - position = Puppet::Pops::Adapters::SourcePosAdapter.new - position.line = line - position.pos = column - position.offset = offset - - position + def location(line, column, offset, length) + Puppet::Pops::Parser::Locatable::Fixed.new(line, column, offset, length) end end From 4b3438d03bdc5207f01442d59d6e907a694a577f Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 25 Oct 2013 04:55:54 +0200 Subject: [PATCH 047/800] (maint) Remove dead / unused and broken code in Factory There were a dead part of the logic trying to deal with associating documentation with model objects (only using sourcepos adapter instead of doc adapter). --- lib/puppet/pops/model/factory.rb | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/lib/puppet/pops/model/factory.rb b/lib/puppet/pops/model/factory.rb index 7a47b1595..86d02ad08 100644 --- a/lib/puppet/pops/model/factory.rb +++ b/lib/puppet/pops/model/factory.rb @@ -479,20 +479,6 @@ class Puppet::Pops::Model::Factory Puppet::Pops::Adapters::SourcePosAdapter.adapt(current) end -# UNUSED AND WRONG -# # Returns documentation string, or nil if not available -# # @return [String, nil] associated documentation if available -# def doc() -# a = Puppet::Pops::Adapters::Source PosAdapter.adapt(current) -# return a.documentation if a -# nil -# end -# -# def doc=(doc_string) -# a = Puppet::Pops::Adapters::Source PosAdapter.adapt(current) -# a.documentation = doc_string -# end - # Returns symbolic information about an expected share of a resource expression given the LHS of a resource expr. # # * `name { }` => `:resource`, create a resource of the given type From a930dbf65d6341632cdc1f9e358c692ed2076850 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 25 Oct 2013 18:44:24 +0200 Subject: [PATCH 048/800] (maint) Add use of new evaluator in specialization of EvaluatingParser This makes it easier to test the new evaluator as it is being developed. Add tests using the Transitional impl. --- lib/puppet/pops/evaluator/evaluator_impl.rb | 5 + lib/puppet/pops/parser/evaluating_parser.rb | 12 ++ .../pops/evaluator/evaluating_parser_spec.rb | 104 ++++++++++++++++++ .../pops/parser/evaluating_parser_spec.rb | 2 +- 4 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 spec/unit/pops/evaluator/evaluating_parser_spec.rb diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index 246f03f1e..949e50f27 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -218,6 +218,10 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator fail("An object of type #{o.class} is not assignable", o, scope) end + def eval_Factory(o, scope) + evaluate(o.current, scope) + end + # Evaluates any object not evaluated to something else to itself. def eval_Object o, scope o @@ -343,6 +347,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # Handles binary expression where lhs and rhs are array/hash or numeric and operator is +, - , *, % / << >> # def calculate(left, right, operator, left_o, right_o, scope) + require 'debugger'; debugger unless ARITHMETIC_OPERATORS.include?(operator) raise ArgumentError, "Unknown arithmetic operator #{o.operator}" end diff --git a/lib/puppet/pops/parser/evaluating_parser.rb b/lib/puppet/pops/parser/evaluating_parser.rb index 3a6cdbb66..5d3aba875 100644 --- a/lib/puppet/pops/parser/evaluating_parser.rb +++ b/lib/puppet/pops/parser/evaluating_parser.rb @@ -31,6 +31,7 @@ class Puppet::Pops::Parser::EvaluatingParser end def evaluate_string(scope, s, file_source='unknown') + require 'debugger'; debugger evaluate(scope, parse_string(s, file_source)) end @@ -159,4 +160,15 @@ class Puppet::Pops::Parser::EvaluatingParser escaped << p unless p.nil? escaped << '"' end + + # This is a temporary solution to making it possible to use the new evaluator. The main class + # will eventually have this behavior instead of using transformation to Puppet 3.x AST + class Transitional < Puppet::Pops::Parser::EvaluatingParser + + def evaluate(scope, model) + return nil unless model + Puppet::Pops::Evaluator::EvaluatorImpl.new().evaluate(model, scope) + end + + end end diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb new file mode 100644 index 000000000..0e3c51aec --- /dev/null +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -0,0 +1,104 @@ +#! /usr/bin/env ruby +require 'spec_helper' + +require 'puppet/pops' +require 'puppet/pops/evaluator/evaluator_impl' +require 'puppet_spec/pops' +require 'puppet_spec/scope' + + +# relative to this spec file (./) does not work as this file is loaded by rspec +#require File.join(File.dirname(__FILE__), '/evaluator_rspec_helper') + +describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do + include PuppetSpec::Pops + include PuppetSpec::Scope + + let(:parser) { Puppet::Pops::Parser::EvaluatingParser::Transitional.new } + let(:node) { 'node.example.com' } + let(:scope) { s = create_test_scope_for_node(node); s } + + context "When the evaluator performs arithmetic" do + context "on Integers" do + { "2+2" => 4, + "2 + 2" => 4, + "7 - 3" => 4, + "6 * 3" => 18, + "6 / 3" => 2, + "6 % 3" => 0, + "10 % 3" => 1, + "-(6/3)" => -2, + "-6/3 " => -2, + "8 >> 1" => 4, + "8 << 1" => 16, + }.each do |source, result| + it "should parse and evaluate the expression '#{source}' to #{result}" do + parser.evaluate_string(scope, source, __FILE__).should == result + end + end + + context "on Floats" do + { + "2.2 + 2.2" => 4.4, + "7.7 - 3.3" => 4.4, + "6.1 * 3.1" => 18.91, + "6.6 / 3.3" => 2.0, + "6.6 % 3.3" => 0.0, + "10.0 % 3.0" => 1.0, + "-(6.0/3.0)" => -2.0, + "-6.0/3.0 " => -2.0, + }.each do |source, result| + it "should parse and evaluate the expression '#{source}' to #{result}" do + parser.evaluate_string(scope, source, __FILE__).should == result + end + end + + { + "3.14 << 2" => :error, + "3.14 >> 2" => :error, + }.each do |source, result| + it "should parse and raise error for '#{source}'" do + expect { parser.evaluate_string(scope, source, __FILE__) }.to raise_error(Puppet::ParseError) + end + end + + end +# it "3.14 << 2 == error" do; expect { evaluate(literal(3.14) << literal(2))}.to raise_error(Puppet::ParseError); end +# it "3.14 >> 2 == error" do; expect { evaluate(literal(3.14) >> literal(2))}.to raise_error(Puppet::ParseError); end + end + end +end +# context "on strings requiring boxing to Numeric" do +# it "'2' + '2' == 4" do +# evaluate(literal('2') + literal('2')).should == 4 +# end +# +# it "'2.2' + '2.2' == 4.4" do +# evaluate(literal('2.2') + literal('2.2')).should == 4.4 +# end +# +# it "'0xF7' + '0x8' == 0xFF" do +# evaluate(literal('0xF7') + literal('0x8')).should == 0xFF +# end +# +# it "'0367' + '010' == 0xFF" do +# evaluate(literal('0367') + literal('010')).should == 0xFF +# end +# +# it "'0888' + '010' == error" do +# expect { evaluate(literal('0888') + literal('010'))}.to raise_error(Puppet::ParseError) +# end +# +# it "'0xWTF' + '010' == error" do +# expect { evaluate(literal('0xWTF') + literal('010'))}.to raise_error(Puppet::ParseError) +# end +# +# it "'0x12.3' + '010' == error" do +# expect { evaluate(literal('0x12.3') + literal('010'))}.to raise_error(Puppet::ParseError) +# end +# +# it "'012.3' + '0.3' == 12.6 (not error, floats can start with 0)" do +# evaluate(literal('012.3') + literal('010')) == 12.6 +# end +# end +# end diff --git a/spec/unit/pops/parser/evaluating_parser_spec.rb b/spec/unit/pops/parser/evaluating_parser_spec.rb index 76a5e5628..b2db895ad 100644 --- a/spec/unit/pops/parser/evaluating_parser_spec.rb +++ b/spec/unit/pops/parser/evaluating_parser_spec.rb @@ -3,7 +3,7 @@ require 'puppet/pops' require 'puppet_spec/pops' require 'puppet_spec/scope' -describe 'The hiera2 string evaluator' do +describe 'The Evaluating Parser' do include PuppetSpec::Pops include PuppetSpec::Scope From 07b39ddbbf4f6bf799fc00b266e0f852027581eb Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 26 Oct 2013 04:39:34 +0200 Subject: [PATCH 049/800] (#22363) Add parse / validate / evaluate tests, fix evaluator issues This adds a comprehensive parse/validate/evaluate test for the future evaluator. Several parsing and evaluation issues were found and corrected. --- lib/puppet/pops/evaluator/compare_operator.rb | 2 +- lib/puppet/pops/evaluator/evaluator_impl.rb | 39 +- lib/puppet/pops/parser/egrammar.ra | 12 +- lib/puppet/pops/parser/eparser.rb | 1300 ++++++++--------- lib/puppet/pops/parser/evaluating_parser.rb | 2 - lib/puppet/pops/parser/lexer2.rb | 9 +- .../pops/evaluator/arithmetic_ops_spec.rb | 4 +- spec/unit/pops/evaluator/conditionals_spec.rb | 2 + .../pops/evaluator/evaluating_parser_spec.rb | 473 +++++- spec/unit/pops/parser/lexer2_spec.rb | 1 - 10 files changed, 1118 insertions(+), 726 deletions(-) diff --git a/lib/puppet/pops/evaluator/compare_operator.rb b/lib/puppet/pops/evaluator/compare_operator.rb index e930d46e5..19315b2cf 100644 --- a/lib/puppet/pops/evaluator/compare_operator.rb +++ b/lib/puppet/pops/evaluator/compare_operator.rb @@ -121,7 +121,7 @@ class Puppet::Pops::Evaluator::CompareOperator when Regexp # match (convert to boolean) !!(a =~ b) - when Number + when Numeric # convert string to number, true if == equals(a, b) when Puppet::Pops::Types::PStringType diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index 949e50f27..10da82454 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -31,6 +31,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator def initialize @@eval_visitor ||= Puppet::Pops::Visitor.new(self, "eval", 1, 1) + @@lvalue_visitor ||= Puppet::Pops::Visitor.new(self, "lvalue", 1, 1) @@assign_visitor ||= Puppet::Pops::Visitor.new(self, "assign", 3, 3) @@call_visitor ||= Puppet::Pops::Visitor.new(self, "call", 3, 3) @@ -89,6 +90,10 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator @@assign_visitor.visit_this(self, target, value, o, scope) end + def lvalue(o, scope) + @@lvalue_visitor.visit_this(self, o, scope) + end + # Call a closure - Can only be called with a closure (for now), may be refactored later # to also handle other types of calls (function calls are also handled by CallNamedFunction and CallMethod, they # could create similar objects to Closure, wait until other types of defines are instantiated - they may behave @@ -159,6 +164,21 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator protected + def lvalue_VariableExpression(o, scope) + # evaluate the name + evaluate(o.expr, scope) + end + + def lvalue_LiteralList(o, scope) + evaluate(o.expr, scope) + end + + # Catches all illegal lvalues + # + def lvalue_Object(name, value, o, scope) + fail("An object of type #{o.class} can not be on the left side of an assignment", o, scope) + end + # Assign value to named variable. # The '$' sign is never part of the name. # @example In Puppet DSL @@ -287,7 +307,9 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # @todo support for -= ('without' to remove from array) concrete syntax not yet implemented # def eval_AssignmentExpression(o, scope) - name, value = eval_BinaryExpression(o, scope) + + name = lvalue(o.left_expr, scope) + value = evaluate(o.right_expr, scope) case o.operator when :'=' # regular assignment @@ -347,7 +369,6 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # Handles binary expression where lhs and rhs are array/hash or numeric and operator is +, - , *, % / << >> # def calculate(left, right, operator, left_o, right_o, scope) - require 'debugger'; debugger unless ARITHMETIC_OPERATORS.include?(operator) raise ArgumentError, "Unknown arithmetic operator #{o.operator}" end @@ -590,6 +611,10 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # TODO end + def eval_ParenthesizedExpression(o, scope) + evaluate(o.expr, scope) + end + # TODO: # Definition < Expression (abstract) # NamedDefinition < Definition (abstract) @@ -632,10 +657,14 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # Evaluation of CallMethodExpression handles a NamedAccessExpression functor (receiver.function_name) # def eval_CallMethodExpression(o, scope) - fail("Unacceptable expression for name of function", o.functor_expr, scope) unless o.functor_expr.is_a? Puppet::Pops::Model::NamedAccessExpression + unless o.functor_expr.is_a? Puppet::Pops::Model::NamedAccessExpression + fail("Unacceptable expression for name of function", o.functor_expr, scope) + end receiver = evaluate(o.functor_expr.left_expr, scope) - name = o.right_expr - fail("Unacceptable expression for name of function/method", name, scope) unless name.is_a? Puppet::Pops::Model::QualifiedName + name = o.functor_expr.right_expr + unless name.is_a? Puppet::Pops::Model::QualifiedName + fail("Unacceptable expression for name of function/method", name, scope) + end name = name.value # the string function name assert_function_available(name, o, scope) evaluated_arguments = [receiver] + (o.arguments || []).collect {|arg| evaluate(arg, scope) } diff --git a/lib/puppet/pops/parser/egrammar.ra b/lib/puppet/pops/parser/egrammar.ra index 6465d8a44..81de5791c 100644 --- a/lib/puppet/pops/parser/egrammar.ra +++ b/lib/puppet/pops/parser/egrammar.ra @@ -93,8 +93,8 @@ expression : higher_precedence | expression LBRACK expressions RBRACK =LBRACK { result = val[0][*val[2]] ; loc result, val[0], val[3] } | expression IN expression { result = val[0].in val[2] ; loc result, val[1] } - | expression MATCH match_rvalue { result = val[0] =~ val[2] ; loc result, val[1] } - | expression NOMATCH match_rvalue { result = val[0].mne val[2] ; loc result, val[1] } + | expression MATCH expression { result = val[0] =~ val[2] ; loc result, val[1] } + | expression NOMATCH expression { result = val[0].mne val[2] ; loc result, val[1] } | expression PLUS expression { result = val[0] + val[2] ; loc result, val[1] } | expression MINUS expression { result = val[0] - val[2] ; loc result, val[1] } | expression DIV expression { result = val[0] / val[2] ; loc result, val[1] } @@ -600,10 +600,10 @@ parameter #--RESTRICTED EXPRESSIONS # i.e. where one could have expected an expression, but the set is limited -# What is allowed RHS of match operators (see expression) -match_rvalue - : regex - | text_or_name +## What is allowed RHS of match operators (see expression) +#match_rvalue +# : regex +# | text_or_name #--VARIABLE # diff --git a/lib/puppet/pops/parser/eparser.rb b/lib/puppet/pops/parser/eparser.rb index 7e6144f5b..093b16d88 100644 --- a/lib/puppet/pops/parser/eparser.rb +++ b/lib/puppet/pops/parser/eparser.rb @@ -30,68 +30,78 @@ module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 705) ##### State transition tables begin ### clist = [ -'71,247,209,222,53,55,316,113,315,90,91,87,82,94,242,98,284,93,-208,-199', -'83,85,84,86,-129,209,-127,224,237,53,55,117,232,231,117,116,321,206', -'116,97,237,57,248,89,88,53,55,75,76,78,77,80,81,270,73,74,53,55,117', -'-208,-199,72,116,71,62,-129,126,-127,238,124,79,92,90,91,87,82,94,117', -'98,344,93,116,57,83,85,84,86,283,117,62,53,55,116,126,227,330,124,117', -'236,226,299,116,97,237,244,62,89,88,301,205,75,76,78,77,80,81,62,73', -'74,53,55,316,229,315,72,230,71,126,303,290,124,219,66,79,92,90,91,87', -'82,94,246,98,269,93,308,71,83,85,84,86,62,71,67,69,68,70,126,309,94', -'124,98,310,93,209,94,97,98,196,93,89,88,313,286,75,76,78,77,80,81,62', -'73,74,317,319,97,268,172,72,244,71,97,246,326,327,106,268,79,92,90,91', -'87,82,94,71,98,262,93,261,66,83,85,84,86,137,260,337,94,246,98,246,93', -'118,244,340,106,107,225,106,97,219,319,346,89,88,347,348,75,76,78,77', -'80,81,97,73,74,349,103,351,352,353,72,287,71,66,63,360,361,362,363,79', -'92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,', -',75,76,78,77,80,81,,73,74,,,,,,72,,71,,95,,,,,79,92,90,91,87,82,94,', -'98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81', -',73,74,,,,,,72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93,,,83,85,84,86', -',,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,', -',,,,79,92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,', -'89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,,,,228,,79,92,90,91,87', -'82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77', -'80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93,,,83,85', -'84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72', -',71,,215,,,,,79,92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,', -',,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90', -'91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76', -'78,77,80,81,,73,74,,,,,,72,,71,,214,,,,,79,92,90,91,87,82,94,,98,,93', +'71,220,207,235,117,53,55,113,116,90,91,87,82,94,240,98,268,93,71,-129', +'83,85,84,86,235,222,106,53,55,53,55,94,319,98,117,93,-206,204,116,97', +'217,-197,126,89,88,124,297,75,76,78,77,80,81,282,73,74,-127,97,245,266', +'-129,72,207,71,126,62,126,124,236,124,79,92,90,91,87,82,94,-206,98,72', +'93,71,-197,83,85,84,86,62,117,62,53,55,116,299,94,328,98,-127,93,246', +'225,227,97,234,228,224,89,88,235,217,75,76,78,77,80,81,301,73,74,117', +'97,117,106,116,72,116,71,126,117,314,124,313,116,79,92,90,91,87,82,94', +'71,98,72,93,260,71,83,85,84,86,62,71,314,94,313,98,203,93,94,244,98', +'259,93,306,94,97,98,307,93,89,88,230,229,75,76,78,77,80,81,97,73,74', +'308,207,97,223,311,72,66,71,97,67,69,68,70,315,79,92,90,91,87,82,94', +'317,98,258,93,284,242,83,85,84,86,244,324,325,288,266,194,170,66,137', +'244,335,242,244,118,242,97,338,106,107,89,88,281,267,75,76,78,77,80', +'81,342,73,74,317,344,345,346,347,72,103,71,349,350,351,285,66,63,79', +'92,90,91,87,82,94,358,98,359,93,360,361,83,85,84,86,,,,,,,,,,,,,,,,97', +',,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,95,,,,,79,92,90,91', +'87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78', +'77,80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93,,,83', +'85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,', +'72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,', +',,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,,,,226,,79,92', +'90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75', +'76,78,77,80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93', ',,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74', ',,,,,72,,71,,213,,,,,79,92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,', -',,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,212,', -',,,79,92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89', -'88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90,91,87,82,94', -',98,,93,,201,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80', -'81,,73,74,,,,,,72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93,,,83,85,84', +',,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,,,,,', +'79,92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88', +',,75,76,78,77,80,81,,73,74,,,,,,72,,71,,212,,,,,79,92,90,91,87,82,94', +',98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81', +',73,74,,,,,,72,,71,,211,,,,,79,92,90,91,87,82,94,,98,,93,,,83,85,84', '86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71', -',,,,,,79,92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,', -',,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90,91,87', -'82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77', -'80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93,,,83,85', -'84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72', -',71,,,,,,,79,92,90,91,87,82,94,71,98,,93,,71,83,85,84,86,,,,94,,98,', -'93,94,,98,,93,,,97,,,,89,88,,,75,76,78,77,80,81,97,73,74,53,55,97,,49', -'72,50,,,,,,73,74,79,92,,73,74,72,,,13,,72,,,,174,191,185,192,48,186', -'194,187,183,181,,176,189,,,,,58,12,195,190,188,53,55,11,,49,,50,,,,62', -',,,71,193,175,,,,56,,13,,,,,94,39,98,46,93,48,100,,47,61,57,,41,60,', -',,,58,12,,,59,53,55,11,97,49,129,50,,,,62,,,78,77,,40,,73,74,56,,13', -',,72,,,39,,46,,48,100,79,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50', -',,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,', -'58,12,53,55,59,71,49,11,50,,,,,,,62,,,94,,98,40,93,,13,56,,,,,39,,46', -',48,100,,47,61,57,,41,60,,97,,,58,12,53,55,59,,49,11,50,,,,73,74,,62', -',,,72,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53', -'55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57', -',41,60,44,45,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,', -',39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,,,', -',,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58', -'12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47', -'61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13', -'56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50', -',,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58', -'12,53,55,59,,49,11,50,336,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43', +',210,,,,,79,92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97', +',,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90,91,87', +'82,94,,98,,93,,199,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78', +'77,80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93,,,83', +'85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,', +'72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,', +',,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90', +'91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76', +'78,77,80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93,,', +'83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,', +',,,72,,71,,,,,,,79,92,90,91,87,82,94,71,98,,93,,,83,85,84,86,,,,94,', +'98,,93,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,97,73,74,,,53,55,,72,49', +'290,50,,,,73,74,79,92,,,,72,,,,,13,,,,,,39,,46,,48,100,,47,61,57,,41', +'60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,71,,,,,40,,,13,56,,,,94,39', +'98,46,93,48,100,,47,61,57,,41,60,,,,,58,12,,,59,53,55,11,97,49,129,50', +',,,62,,,78,77,,40,,73,74,56,,13,,,72,,,39,,46,,48,100,79,47,61,57,,41', +'60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46', +',48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,71,49,11,50,,,,,,,62', +',,94,,98,40,93,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,97,,,58,12', +'53,55,59,,49,11,50,,,,73,74,,62,,,,72,,40,,,13,56,,,,,39,,46,,48,43', ',47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,', ',13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49', +'11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44', +'45,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48', +'43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40', +',,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,71', +'49,11,50,,,,,,,62,,,94,,98,40,93,,13,56,,,,,39,,46,,48,100,,47,61,57', +',41,60,,97,,,58,12,53,55,59,,49,11,50,,,,73,74,,62,,,,72,,40,,,13,56', +',,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,', +',,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12', +'53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61', +'57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,', +'39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62', +',,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55', +'59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,', +'41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,', +'46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,', +',,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59', +',49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41', +'60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46', +',48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,', +'40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49', '11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,', ',,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48', '100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,', @@ -106,87 +116,80 @@ clist = [ ',,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,', ',,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53', '55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57', +',41,60,,,,,58,12,,,59,53,55,11,,49,,50,334,,,62,,,,,,40,,,169,56,,13', +',,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50', +',,,,,,62,,,,,,40,,,13,56,,,,,172,189,183,190,48,184,192,185,181,179', +',174,187,,,,,58,12,193,188,186,53,55,11,,49,,50,,,,62,,,,,191,173,,', +',56,,13,,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,', +'49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60', +',,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48', +'100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,320,,,,,,62,,,,,,40', +',,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11', +'50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,', +'58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100', +',47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,71,,,,,40,,,13', +'56,,,,94,39,98,46,93,48,100,,47,61,57,,41,60,,,,,58,12,,,59,53,55,11', +'97,49,,50,,,,62,,,78,77,,40,,73,74,56,,13,201,,72,,,39,,46,,48,100,79', +'47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56', +',,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,', +',,,62,71,,,,,40,,,13,56,,,,94,39,98,46,93,48,100,,47,61,57,,41,60,,', +',,58,12,,,59,53,55,11,97,49,,50,,,,62,75,76,78,77,,40,,73,74,56,,13', +'209,,72,,,39,,46,,48,100,79,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11', +'50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,', +'58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100', +',47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13', +'56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50', +',,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58', +'12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47', +'61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,', +',,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,', +',,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53', +'55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57', +',41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39', +',46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,', +',,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55', +'59,,49,11,50,292,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57', ',41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39', ',46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,', ',,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59', ',49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41', -'60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46', -',48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,', -'40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49', +'60,,,,,58,12,53,55,59,,49,11,50,132,,,,,,62,,,,,,40,,,13,56,,,,,39,', +'46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,', +',,,40,,,13,56,,,,,172,189,183,190,48,184,192,185,181,179,,174,187,,', +',,58,12,193,188,186,53,55,11,,49,,50,,,,62,,,,,191,173,,,,56,,13,,,', +',,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,272,', +',,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58', +'12,53,55,59,,49,11,50,270,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43', +',47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,264,,,,,,62,,,,,,40', +',,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49', '11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,', ',,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48', -'100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,', -',13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,,,59,53,55,11,', -'49,,50,322,,,62,,,,,,40,,,171,56,,13,,,,,,39,,46,,48,100,,47,61,57,', -'41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,174', -'191,185,192,48,186,194,187,183,181,,176,189,,,,,58,12,195,190,188,53', -'55,11,,49,,50,,,,62,,,,,193,175,,,,56,,13,,,,,,39,,46,,48,100,,47,61', -'57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,', -'39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62', -',,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55', -'59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,', -'41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,', -'46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,', -',,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59', -',49,11,50,,,,,,,62,71,,,,,40,,,13,56,,,,94,39,98,46,93,48,100,,47,61', -'57,,41,60,,,,,58,12,,,59,53,55,11,97,49,,50,,,,62,,,78,77,,40,,73,74', -'56,,13,203,,72,,,39,,46,,48,100,79,47,61,57,,41,60,,,,,58,12,53,55,59', -',49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41', -'60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,71,,,,,40,,,13,56,,,,94,39', -'98,46,93,48,100,,47,61,57,,41,60,,,,,58,12,,,59,53,55,11,97,49,,50,', -',,62,75,76,78,77,,40,,73,74,56,,13,211,,72,,,39,,46,,48,100,79,47,61', -'57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,', -'39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62', -',,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55', -'59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,', -'41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,', -'46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,', -',,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59', +'100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,71,,,,,40', +',,13,56,,,,94,39,98,46,93,48,100,,47,61,57,,41,60,71,,,,58,12,,,59,', +',11,97,94,,98,,93,,62,75,76,78,77,,40,,73,74,56,,,,,72,,,,,97,,,,79', +',71,,75,76,78,77,80,81,,73,74,,,94,,98,72,93,,,,,,,,79,,,,,,,,,,,,,', +'97,,,,,,71,,75,76,78,77,80,81,,73,74,,82,94,,98,72,93,,,83,,,,,79,,', +',,,,,,,,,,,97,,,,,,71,,75,76,78,77,80,81,,73,74,,82,94,,98,72,93,,,83', +',,,,79,,,,,,,,,,,,,,97,,,,,,71,,75,76,78,77,80,81,,73,74,,82,94,,98', +'72,93,,,83,,,,,79,,,,,,,,,,,,,,97,,,,,,71,,75,76,78,77,80,81,,73,74', +',82,94,,98,72,93,,,83,,,,,79,,,,,,,,,,,,,,97,,,,,,71,,75,76,78,77,80', +'81,,73,74,87,82,94,,98,72,93,,,83,85,84,86,,79,,,,,,,,,,,,,,97,,,,,', +'71,,75,76,78,77,80,81,,73,74,87,82,94,,98,72,93,,,83,85,84,86,,79,,', +',,,,,,,,53,55,,97,49,,50,353,88,,,75,76,78,77,80,81,,73,74,,,13,,,72', +',,39,,46,,48,43,79,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50', +'355,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45', +',,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43', +',47,61,57,64,41,60,44,45,,,58,12,53,55,59,,49,11,50,357,,,,,,62,,,,', +',40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59', ',49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41', '60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46', -',48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,294,,,,,,62,,', -',,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59', -',49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41', -'60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,71,,,,,40,,,13,56,,,,94,39', -'98,46,93,48,43,,47,61,57,,41,60,44,45,,,58,12,,,59,53,55,11,97,49,292', -'50,,,,62,75,76,78,77,,40,,73,74,56,,13,,,72,,,39,,46,,48,100,79,47,61', -'57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,', -'39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,132,,,', -',,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53', -'55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57', -',41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39', -',46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,274,,,,,,62', -',,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53', -'55,59,,49,11,50,272,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61', -'57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,266,,,,,,62,,,,,,40,,,13', -'56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11', -'50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,', -'58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100', -',47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13', -'56,,,,,39,,46,,48,100,,47,61,57,71,41,60,,,,,58,12,,,59,,94,11,98,,93', -',,,,62,,,71,,,40,,,,56,,,,,,94,97,98,,93,,,,,75,76,78,77,80,81,,73,74', -',,,,,72,,,97,,,,,,79,,75,76,78,77,80,81,,73,74,71,,,,,72,,,,,,,82,94', -'79,98,,93,,,83,,,,,71,,,,,,,,,,,,82,94,97,98,,93,,,83,,75,76,78,77,80', -'81,,73,74,,,,,,72,,,97,,,,,,79,,75,76,78,77,80,81,,73,74,71,,,,,72,', -',,,,,82,94,79,98,,93,,,83,,,,,71,,,,,,,,,,,,82,94,97,98,,93,,,83,,75', -'76,78,77,80,81,,73,74,,,,,,72,,,97,,,,,,79,,75,76,78,77,80,81,,73,74', -'71,,,,,72,,,,,,87,82,94,79,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,', -',,,71,,75,76,78,77,80,81,,73,74,87,82,94,,98,72,93,,,83,85,84,86,,79', -',,,,,,,,,,53,55,,97,49,,50,355,88,,,75,76,78,77,80,81,,73,74,,,13,,', -'72,,,39,,46,,48,43,79,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11', -'50,357,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44', -'45,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48', -'43,,47,61,57,64,41,60,44,45,,,58,12,53,55,59,,49,11,50,359,,,,,,62,', -',,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55', -'59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,', -'41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,', -'46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,,,,,,,62', -',,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,,,59,', -',11,,,,256,191,255,192,62,253,194,257,251,250,40,252,254,,56,,,,,195', -'190,258,256,191,255,192,,253,194,257,251,250,,252,254,,,193,259,,,195', -'190,258,256,191,255,192,,253,194,257,251,250,,252,254,,,193,259,,,195', -'190,258,,,,,,,,,,,,,,,,193,259' ] - racc_action_table = arr = ::Array.new(5222, nil) +',48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,,,,,,,62,,', +',,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,,,59,,,11', +',,,254,189,253,190,62,251,192,255,249,248,40,250,252,,56,,,,,193,188', +'256,254,189,253,190,,251,192,255,249,248,,250,252,,,191,257,,,193,188', +'256,254,189,253,190,,251,192,255,249,248,,250,252,,,191,257,,,193,188', +'256,,,,,,,,,,,,,,,,191,257' ] + racc_action_table = arr = ::Array.new(5345, nil) idx = 0 clist.each do |str| str.split(',', -1).each do |i| @@ -196,180 +199,184 @@ clist = [ end clist = [ -'173,184,106,120,74,74,272,43,272,173,173,173,173,173,173,173,219,173', -'188,189,173,173,173,173,183,219,181,120,275,222,222,186,136,136,43,186', -'275,106,43,173,210,74,184,173,173,73,73,173,173,173,173,173,173,210', -'173,173,187,187,185,188,189,173,185,168,74,183,222,181,168,222,173,173', -'168,168,168,168,168,100,168,316,168,100,73,168,168,168,168,217,283,222', -'47,47,283,187,128,283,187,46,144,128,239,46,168,144,216,73,168,168,243', -'105,168,168,168,168,168,168,187,168,168,224,224,313,131,313,168,131', -'167,47,245,225,47,119,138,168,168,167,167,167,167,167,249,167,209,167', -'263,145,167,167,167,167,47,101,7,7,7,7,224,265,145,224,145,267,145,268', -'101,167,101,96,101,167,167,271,220,167,167,167,167,167,167,224,167,167', -'273,274,145,207,94,167,278,166,101,279,280,281,203,285,167,167,166,166', -'166,166,166,99,166,202,166,200,65,166,166,166,166,63,198,298,99,178', -'99,300,99,44,177,307,308,38,122,37,166,114,317,319,166,166,320,324,166', -'166,166,166,166,166,99,166,166,325,36,331,332,335,166,221,165,5,1,350', -'354,356,358,166,166,165,165,165,165,165,,165,,165,,,165,165,165,165', -',,,,,,,,,,,,,,,165,,,,165,165,,,165,165,165,165,165,165,,165,165,,,', -',,165,,10,,10,,,,,165,165,10,10,10,10,10,,10,,10,,,10,10,10,10,,,,,', -',,,,,,,,,,10,,,,10,10,,,10,10,10,10,10,10,,10,10,,,,,,10,,197,,,,,,', -'10,10,197,197,197,197,197,,197,,197,,,197,197,197,197,,,,,,,,,,,,,,', -',197,,,,197,197,,,197,197,197,197,197,197,,197,197,,,,,,197,,134,,,', -',,,197,197,134,134,134,134,134,,134,,134,,,134,134,134,134,,,,,,,,,', -',,,,,,134,,,,134,134,,,134,134,134,134,134,134,,134,134,,,,,,134,,130', -',,,,130,,134,134,130,130,130,130,130,,130,,130,,,130,130,130,130,,,', -',,,,,,,,,,,,130,,,,130,130,,,130,130,130,130,130,130,,130,130,,,,,,130', -',127,,,,,,,130,130,127,127,127,127,127,,127,,127,,,127,127,127,127,', -',,,,,,,,,,,,,,127,,,,127,127,,,127,127,127,127,127,127,,127,127,,,,', -',127,,112,,112,,,,,127,127,112,112,112,112,112,,112,,112,,,112,112,112', -'112,,,,,,,,,,,,,,,,112,,,,112,112,,,112,112,112,112,112,112,,112,112', -',,,,,112,,312,,,,,,,112,112,312,312,312,312,312,,312,,312,,,312,312', -'312,312,,,,,,,,,,,,,,,,312,,,,312,312,,,312,312,312,312,312,312,,312', -'312,,,,,,312,,111,,111,,,,,312,312,111,111,111,111,111,,111,,111,,,111', -'111,111,111,,,,,,,,,,,,,,,,111,,,,111,111,,,111,111,111,111,111,111', -',111,111,,,,,,111,,110,,110,,,,,111,111,110,110,110,110,110,,110,,110', -',,110,110,110,110,,,,,,,,,,,,,,,,110,,,,110,110,,,110,110,110,110,110', -'110,,110,110,,,,,,110,,108,,108,,,,,110,110,108,108,108,108,108,,108', -',108,,,108,108,108,108,,,,,,,,,,,,,,,,108,,,,108,108,,,108,108,108,108', -'108,108,,108,108,,,,,,108,,102,,,,,,,108,108,102,102,102,102,102,,102', -',102,,102,102,102,102,102,,,,,,,,,,,,,,,,102,,,,102,102,,,102,102,102', -'102,102,102,,102,102,,,,,,102,,306,,,,,,,102,102,306,306,306,306,306', -',306,,306,,,306,306,306,306,,,,,,,,,,,,,,,,306,,,,306,306,,,306,306', -'306,306,306,306,,306,306,,,,,,306,,305,,,,,,,306,306,305,305,305,305', -'305,,305,,305,,,305,305,305,305,,,,,,,,,,,,,,,,305,,,,305,305,,,305', -'305,305,305,305,305,,305,305,,,,,,305,,291,,,,,,,305,305,291,291,291', -'291,291,,291,,291,,,291,291,291,291,,,,,,,,,,,,,,,,291,,,,291,291,,', -'291,291,291,291,291,291,,291,291,,,,,,291,,297,,,,,,,291,291,297,297', -'297,297,297,,297,,297,,,297,297,297,297,,,,,,,,,,,,,,,,297,,,,297,297', -',,297,297,297,297,297,297,,297,297,,,,,,297,,293,,,,,,,297,297,293,293', -'293,293,293,154,293,,293,,153,293,293,293,293,,,,154,,154,,154,153,', -'153,,153,,,293,,,,293,293,,,293,293,293,293,293,293,154,293,293,215', -'215,153,,215,293,215,,,,,,154,154,293,293,,153,153,154,,,215,,153,,', -',215,215,215,215,215,215,215,215,215,215,,215,215,,,,,215,215,215,215', -'215,54,54,215,,54,,54,,,,215,,,,150,215,215,,,,215,,54,,,,,150,54,150', -'54,150,54,54,,54,54,54,,54,54,,,,,54,54,,,54,49,49,54,150,49,49,49,', -',,54,,,150,150,,54,,150,150,54,,49,,,150,,,49,,49,,49,49,150,49,49,49', -',49,49,,,,,49,49,64,64,49,,64,49,64,,,,,,,49,,,,,,49,,,64,49,,,,,64', -',64,,64,64,,64,64,64,,64,64,64,64,,,64,64,299,299,64,152,299,64,299', -',,,,,,64,,,152,,152,64,152,,299,64,,,,,299,,299,,299,299,,299,299,299', -',299,299,,152,,,299,299,66,66,299,,66,299,66,,,,152,152,,299,,,,152', -',299,,,66,299,,,,,66,,66,,66,66,,66,66,66,,66,66,66,66,,,66,66,67,67', -'66,,67,66,67,,,,,,,66,,,,,,66,,,67,66,,,,,67,,67,,67,67,,67,67,67,,67', -'67,67,67,,,67,67,68,68,67,,68,67,68,,,,,,,67,,,,,,67,,,68,67,,,,,68', -',68,,68,68,,68,68,68,,68,68,68,68,,,68,68,69,69,68,,69,68,69,,,,,,,68', -',,,,,68,,,69,68,,,,,69,,69,,69,69,,69,69,69,,69,69,69,69,,,69,69,70', -'70,69,,70,69,70,,,,,,,69,,,,,,69,,,70,69,,,,,70,,70,,70,70,,70,70,70', -',70,70,70,70,,,70,70,71,71,70,,71,70,71,,,,,,,70,,,,,,70,,,71,70,,,', -',71,,71,,71,71,,71,71,71,,71,71,,,,,71,71,72,72,71,,72,71,72,,,,,,,71', -',,,,,71,,,72,71,,,,,72,,72,,72,72,,72,72,72,,72,72,,,,,72,72,287,287', -'72,,287,72,287,287,,,,,,72,,,,,,72,,,287,72,,,,,287,,287,,287,287,,287', -'287,287,,287,287,287,287,,,287,287,286,286,287,,286,287,286,,,,,,,287', -',,,,,287,,,286,287,,,,,286,,286,,286,286,,286,286,286,,286,286,286,286', -',,286,286,75,75,286,,75,286,75,,,,,,,286,,,,,,286,,,75,286,,,,,75,,75', -',75,75,,75,75,75,,75,75,,,,,75,75,76,76,75,,76,75,76,,,,,,,75,,,,,,75', -',,76,75,,,,,76,,76,,76,76,,76,76,76,,76,76,,,,,76,76,77,77,76,,77,76', -'77,,,,,,,76,,,,,,76,,,77,76,,,,,77,,77,,77,77,,77,77,77,,77,77,,,,,77', -'77,78,78,77,,78,77,78,,,,,,,77,,,,,,77,,,78,77,,,,,78,,78,,78,78,,78', -'78,78,,78,78,,,,,78,78,79,79,78,,79,78,79,,,,,,,78,,,,,,78,,,79,78,', -',,,79,,79,,79,79,,79,79,79,,79,79,,,,,79,79,80,80,79,,80,79,80,,,,,', -',79,,,,,,79,,,80,79,,,,,80,,80,,80,80,,80,80,80,,80,80,,,,,80,80,81', -'81,80,,81,80,81,,,,,,,80,,,,,,80,,,81,80,,,,,81,,81,,81,81,,81,81,81', -',81,81,,,,,81,81,82,82,81,,82,81,82,,,,,,,81,,,,,,81,,,82,81,,,,,82', -',82,,82,82,,82,82,82,,82,82,,,,,82,82,83,83,82,,83,82,83,,,,,,,82,,', -',,,82,,,83,82,,,,,83,,83,,83,83,,83,83,83,,83,83,,,,,83,83,84,84,83', -',84,83,84,,,,,,,83,,,,,,83,,,84,83,,,,,84,,84,,84,84,,84,84,84,,84,84', -',,,,84,84,85,85,84,,85,84,85,,,,,,,84,,,,,,84,,,85,84,,,,,85,,85,,85', -'85,,85,85,85,,85,85,,,,,85,85,86,86,85,,86,85,86,,,,,,,85,,,,,,85,,', -'86,85,,,,,86,,86,,86,86,,86,86,86,,86,86,,,,,86,86,87,87,86,,87,86,87', -',,,,,,86,,,,,,86,,,87,86,,,,,87,,87,,87,87,,87,87,87,,87,87,,,,,87,87', -'88,88,87,,88,87,88,,,,,,,87,,,,,,87,,,88,87,,,,,88,,88,,88,88,,88,88', -'88,,88,88,,,,,88,88,89,89,88,,89,88,89,,,,,,,88,,,,,,88,,,89,88,,,,', -'89,,89,,89,89,,89,89,89,,89,89,,,,,89,89,90,90,89,,90,89,90,,,,,,,89', -',,,,,89,,,90,89,,,,,90,,90,,90,90,,90,90,90,,90,90,,,,,90,90,91,91,90', -',91,90,91,,,,,,,90,,,,,,90,,,91,90,,,,,91,,91,,91,91,,91,91,91,,91,91', -',,,,91,91,92,92,91,,92,91,92,,,,,,,91,,,,,,91,,,92,91,,,,,92,,92,,92', -'92,,92,92,92,,92,92,,,,,92,92,93,93,92,,93,92,93,,,,,,,92,,,,,,92,,', -'93,92,,,,,93,,93,,93,93,,93,93,93,,93,93,,,,,93,93,,,93,276,276,93,', -'276,,276,276,,,93,,,,,,93,,,93,93,,276,,,,,,276,,276,,276,276,,276,276', -'276,,276,276,,,,,276,276,95,95,276,,95,276,95,,,,,,,276,,,,,,276,,,95', -'276,,,,,95,95,95,95,95,95,95,95,95,95,,95,95,,,,,95,95,95,95,95,269', -'269,95,,269,,269,,,,95,,,,,95,95,,,,95,,269,,,,,,269,,269,,269,269,', -'269,269,269,,269,269,,,,,269,269,97,97,269,,97,269,97,,,,,,,269,,,,', -',269,,,97,269,,,,,97,,97,,97,97,,97,97,97,,97,97,,,,,97,97,98,98,97', -',98,97,98,,,,,,,97,,,,,,97,,,98,97,,,,,98,,98,,98,98,,98,98,98,,98,98', -',,,,98,98,262,262,98,,262,98,262,,,,,,,98,,,,,,98,,,262,98,,,,,262,', -'262,,262,262,,262,262,262,,262,262,,,,,262,262,248,248,262,,248,262', -'248,,,,,,,262,,,,,,262,,,248,262,,,,,248,,248,,248,248,,248,248,248', -',248,248,,,,,248,248,174,174,248,,174,248,174,,,,,,,248,,,,,,248,,,174', -'248,,,,,174,,174,,174,174,,174,174,174,,174,174,,,,,174,174,42,42,174', -',42,174,42,,,,,,,174,151,,,,,174,,,42,174,,,,151,42,151,42,151,42,42', -',42,42,42,,42,42,,,,,42,42,,,42,103,103,42,151,103,,103,,,,42,,,151', -'151,,42,,151,151,42,,103,103,,151,,,103,,103,,103,103,151,103,103,103', -',103,103,,,,,103,103,244,244,103,,244,103,244,,,,,,,103,,,,,,103,,,244', -'103,,,,,244,,244,,244,244,,244,244,244,,244,244,,,,,244,244,238,238', -'244,,238,244,238,,,,,,,244,155,,,,,244,,,238,244,,,,155,238,155,238', -'155,238,238,,238,238,238,,238,238,,,,,238,238,,,238,107,107,238,155', -'107,,107,,,,238,155,155,155,155,,238,,155,155,238,,107,107,,155,,,107', -',107,,107,107,155,107,107,107,,107,107,,,,,107,107,41,41,107,,41,107', -'41,,,,,,,107,,,,,,107,,,41,107,,,,,41,,41,,41,41,,41,41,41,,41,41,,', -',,41,41,40,40,41,,40,41,40,,,,,,,41,,,,,,41,,,40,41,,,,,40,,40,,40,40', -',40,40,40,,40,40,,,,,40,40,39,39,40,,39,40,39,,,,,,,40,,,,,,40,,,39', -'40,,,,,39,,39,,39,39,,39,39,39,,39,39,,,,,39,39,315,315,39,,315,39,315', -',,,,,,39,,,,,,39,,,315,39,,,,,315,,315,,315,315,,315,315,315,,315,315', -',,,,315,315,113,113,315,,113,315,113,,,,,,,315,,,,,,315,,,113,315,,', -',,113,,113,,113,113,,113,113,113,,113,113,,,,,113,113,237,237,113,,237', -'113,237,,,,,,,113,,,,,,113,,,237,113,,,,,237,,237,,237,237,,237,237', -'237,,237,237,,,,,237,237,235,235,237,,235,237,235,,,,,,,237,,,,,,237', -',,235,237,,,,,235,,235,,235,235,,235,235,235,,235,235,,,,,235,235,230', -'230,235,,230,235,230,230,,,,,,235,,,,,,235,,,230,235,,,,,230,,230,,230', -'230,,230,230,230,,230,230,,,,,230,230,228,228,230,,228,230,228,,,,,', -',230,,,,,,230,,,228,230,,,,,228,,228,,228,228,,228,228,228,,228,228', -',,,,228,228,327,327,228,,327,228,327,,,,,,,228,156,,,,,228,,,327,228', -',,,156,327,156,327,156,327,327,,327,327,327,,327,327,327,327,,,327,327', -',,327,226,226,327,156,226,226,226,,,,327,156,156,156,156,,327,,156,156', -'327,,226,,,156,,,226,,226,,226,226,156,226,226,226,,226,226,,,,,226', -'226,13,13,226,,13,226,13,,,,,,,226,,,,,,226,,,13,226,,,,,13,,13,,13', -'13,,13,13,13,,13,13,,,,,13,13,50,50,13,,50,13,50,50,,,,,,13,,,,,,13', -',,50,13,,,,,50,,50,,50,50,,50,50,50,,50,50,,,,,50,50,12,12,50,,12,50', -'12,,,,,,,50,,,,,,50,,,12,50,,,,,12,,12,,12,12,,12,12,12,,12,12,,,,,12', -'12,214,214,12,,214,12,214,,,,,,,12,,,,,,12,,,214,12,,,,,214,,214,,214', -'214,,214,214,214,,214,214,,,,,214,214,213,213,214,,213,214,213,213,', -',,,,214,,,,,,214,,,213,214,,,,,213,,213,,213,213,,213,213,213,,213,213', -'213,213,,,213,213,212,212,213,,212,213,212,212,,,,,,213,,,,,,213,,,212', -'213,,,,,212,,212,,212,212,,212,212,212,,212,212,212,212,,,212,212,205', -'205,212,,205,212,205,205,,,,,,212,,,,,,212,,,205,212,,,,,205,,205,,205', -'205,,205,205,205,,205,205,205,205,,,205,205,11,11,205,,11,205,11,,,', -',,,205,,,,,,205,,,11,205,,,,,11,,11,,11,11,,11,11,11,,11,11,,,,,11,11', -'176,176,11,,176,11,176,,,,,,,11,,,,,,11,,,176,11,,,,,176,,176,,176,176', -',176,176,176,,176,176,,,,,176,176,175,175,176,,175,176,175,,,,,,,176', -',,,,,176,,,175,176,,,,,175,,175,,175,175,,175,175,175,157,175,175,,', -',,175,175,,,175,,157,175,157,,157,,,,,175,,,158,,,175,,,,175,,,,,,158', -'157,158,,158,,,,,157,157,157,157,157,157,,157,157,,,,,,157,,,158,,,', -',,157,,158,158,158,158,158,158,,158,158,159,,,,,158,,,,,,,159,159,158', -'159,,159,,,159,,,,,160,,,,,,,,,,,,160,160,159,160,,160,,,160,,159,159', -'159,159,159,159,,159,159,,,,,,159,,,160,,,,,,159,,160,160,160,160,160', -'160,,160,160,161,,,,,160,,,,,,,161,161,160,161,,161,,,161,,,,,162,,', -',,,,,,,,,162,162,161,162,,162,,,162,,161,161,161,161,161,161,,161,161', -',,,,,161,,,162,,,,,,161,,162,162,162,162,162,162,,162,162,163,,,,,162', -',,,,,163,163,163,162,163,,163,,,163,163,163,163,,,,,,,,,,,,,,,,163,', -',,,,164,,163,163,163,163,163,163,,163,163,164,164,164,,164,163,164,', -',164,164,164,164,,163,,,,,,,,,,,344,344,,164,344,,344,344,164,,,164', -'164,164,164,164,164,,164,164,,,344,,,164,,,344,,344,,344,344,164,344', -'344,344,,344,344,344,344,,,344,344,346,346,344,,346,344,346,346,,,,', -',344,,,,,,344,,,346,344,,,,,346,,346,,346,346,,346,346,346,,346,346', -'346,346,,,346,346,4,4,346,,4,346,4,,,,,,,346,,,,,,346,,,4,346,,,,,4', -',4,,4,4,,4,4,4,4,4,4,4,4,,,4,4,347,347,4,,347,4,347,347,,,,,,4,,,,,', -'4,,,347,4,,,,,347,,347,,347,347,,347,347,347,,347,347,347,347,,,347', -'347,171,171,347,,171,347,171,,,,,,,347,,,,,,347,,,171,347,,,,,171,,171', -',171,171,,171,171,171,,171,171,,,,,171,171,0,0,171,,0,171,0,,,,,,,171', -',,,,,171,,,0,171,,,,,0,,0,,0,0,,0,0,0,,0,0,0,0,,,0,0,247,247,0,,247', -'0,247,,,,,,,0,,,,,,0,,,247,0,,,,,247,,247,,247,247,,247,247,247,,247', -'247,,,,,247,247,,,247,,,247,,,,241,241,241,241,247,241,241,241,241,241', -'247,241,241,,247,,,,,241,241,241,246,246,246,246,,246,246,246,246,246', -',246,246,,,241,241,,,246,246,246,196,196,196,196,,196,196,196,196,196', -',196,196,,,246,246,,,196,196,196,,,,,,,,,,,,,,,,196,196' ] - racc_action_check = arr = ::Array.new(5222, nil) +'171,120,106,208,100,47,47,43,100,171,171,171,171,171,171,171,208,171', +'147,181,171,171,171,171,273,120,37,185,185,222,222,147,273,147,43,147', +'186,106,43,171,119,187,47,171,171,47,237,171,171,171,171,171,171,217', +'171,171,179,147,182,205,181,171,217,166,185,47,222,185,166,222,171,171', +'166,166,166,166,166,186,166,147,166,146,187,166,166,166,166,185,281', +'222,220,220,281,241,146,281,146,179,146,182,128,131,166,144,131,128', +'166,166,144,114,166,166,166,166,166,166,243,166,166,184,146,46,201,184', +'166,46,165,220,183,311,220,311,183,166,166,165,165,165,165,165,99,165', +'146,165,200,101,165,165,165,165,220,145,270,99,270,99,105,99,101,247', +'101,198,101,261,145,165,145,263,145,165,165,136,136,165,165,165,165', +'165,165,99,165,165,265,266,101,122,269,165,138,164,145,7,7,7,7,271,165', +'165,164,164,164,164,164,272,164,196,164,218,276,164,164,164,164,277', +'278,279,223,283,96,94,65,63,176,296,214,298,44,175,164,305,306,38,164', +'164,215,207,164,164,164,164,164,164,314,164,164,315,317,318,322,323', +'164,36,163,329,330,333,219,5,1,164,164,163,163,163,163,163,348,163,352', +'163,354,356,163,163,163,163,,,,,,,,,,,,,,,,163,,,,163,163,,,163,163', +'163,163,163,163,,163,163,,,,,,163,,10,,10,,,,,163,163,10,10,10,10,10', +',10,,10,,,10,10,10,10,,,,,,,,,,,,,,,,10,,,,10,10,,,10,10,10,10,10,10', +',10,10,,,,,,10,,195,,,,,,,10,10,195,195,195,195,195,,195,,195,,,195', +'195,195,195,,,,,,,,,,,,,,,,195,,,,195,195,,,195,195,195,195,195,195', +',195,195,,,,,,195,,134,,,,,,,195,195,134,134,134,134,134,,134,,134,', +',134,134,134,134,,,,,,,,,,,,,,,,134,,,,134,134,,,134,134,134,134,134', +'134,,134,134,,,,,,134,,130,,,,,130,,134,134,130,130,130,130,130,,130', +',130,,,130,130,130,130,,,,,,,,,,,,,,,,130,,,,130,130,,,130,130,130,130', +'130,130,,130,130,,,,,,130,,127,,,,,,,130,130,127,127,127,127,127,,127', +',127,,,127,127,127,127,,,,,,,,,,,,,,,,127,,,,127,127,,,127,127,127,127', +'127,127,,127,127,,,,,,127,,112,,112,,,,,127,127,112,112,112,112,112', +',112,,112,,,112,112,112,112,,,,,,,,,,,,,,,,112,,,,112,112,,,112,112', +'112,112,112,112,,112,112,,,,,,112,,310,,,,,,,112,112,310,310,310,310', +'310,,310,,310,,,310,310,310,310,,,,,,,,,,,,,,,,310,,,,310,310,,,310', +'310,310,310,310,310,,310,310,,,,,,310,,111,,111,,,,,310,310,111,111', +'111,111,111,,111,,111,,,111,111,111,111,,,,,,,,,,,,,,,,111,,,,111,111', +',,111,111,111,111,111,111,,111,111,,,,,,111,,110,,110,,,,,111,111,110', +'110,110,110,110,,110,,110,,,110,110,110,110,,,,,,,,,,,,,,,,110,,,,110', +'110,,,110,110,110,110,110,110,,110,110,,,,,,110,,108,,108,,,,,110,110', +'108,108,108,108,108,,108,,108,,,108,108,108,108,,,,,,,,,,,,,,,,108,', +',,108,108,,,108,108,108,108,108,108,,108,108,,,,,,108,,102,,,,,,,108', +'108,102,102,102,102,102,,102,,102,,102,102,102,102,102,,,,,,,,,,,,,', +',,102,,,,102,102,,,102,102,102,102,102,102,,102,102,,,,,,102,,304,,', +',,,,102,102,304,304,304,304,304,,304,,304,,,304,304,304,304,,,,,,,,', +',,,,,,,304,,,,304,304,,,304,304,304,304,304,304,,304,304,,,,,,304,,303', +',,,,,,304,304,303,303,303,303,303,,303,,303,,,303,303,303,303,,,,,,', +',,,,,,,,,303,,,,303,303,,,303,303,303,303,303,303,,303,303,,,,,,303', +',289,,,,,,,303,303,289,289,289,289,289,,289,,289,,,289,289,289,289,', +',,,,,,,,,,,,,,289,,,,289,289,,,289,289,289,289,289,289,,289,289,,,,', +',289,,295,,,,,,,289,289,295,295,295,295,295,,295,,295,,,295,295,295', +'295,,,,,,,,,,,,,,,,295,,,,295,295,,,295,295,295,295,295,295,,295,295', +',,,,,295,,291,,,,,,,295,295,291,291,291,291,291,152,291,,291,,,291,291', +'291,291,,,,152,,152,,152,,,,,,,,291,,,,291,291,,,291,291,291,291,291', +'291,152,291,291,,,224,224,,291,224,224,224,,,,152,152,291,291,,,,152', +',,,,224,,,,,,224,,224,,224,224,,224,224,224,,224,224,,,,,224,224,54', +'54,224,,54,224,54,,,,,,,224,148,,,,,224,,,54,224,,,,148,54,148,54,148', +'54,54,,54,54,54,,54,54,,,,,54,54,,,54,49,49,54,148,49,49,49,,,,54,,', +'148,148,,54,,148,148,54,,49,,,148,,,49,,49,,49,49,148,49,49,49,,49,49', +',,,,49,49,64,64,49,,64,49,64,,,,,,,49,,,,,,49,,,64,49,,,,,64,,64,,64', +'64,,64,64,64,,64,64,64,64,,,64,64,172,172,64,151,172,64,172,,,,,,,64', +',,151,,151,64,151,,172,64,,,,,172,,172,,172,172,,172,172,172,,172,172', +',151,,,172,172,66,66,172,,66,172,66,,,,151,151,,172,,,,151,,172,,,66', +'172,,,,,66,,66,,66,66,,66,66,66,,66,66,66,66,,,66,66,67,67,66,,67,66', +'67,,,,,,,66,,,,,,66,,,67,66,,,,,67,,67,,67,67,,67,67,67,,67,67,67,67', +',,67,67,68,68,67,,68,67,68,,,,,,,67,,,,,,67,,,68,67,,,,,68,,68,,68,68', +',68,68,68,,68,68,68,68,,,68,68,69,69,68,,69,68,69,,,,,,,68,,,,,,68,', +',69,68,,,,,69,,69,,69,69,,69,69,69,,69,69,69,69,,,69,69,70,70,69,,70', +'69,70,,,,,,,69,,,,,,69,,,70,69,,,,,70,,70,,70,70,,70,70,70,,70,70,70', +'70,,,70,70,71,71,70,150,71,70,71,,,,,,,70,,,150,,150,70,150,,71,70,', +',,,71,,71,,71,71,,71,71,71,,71,71,,150,,,71,71,72,72,71,,72,71,72,,', +',150,150,,71,,,,150,,71,,,72,71,,,,,72,,72,,72,72,,72,72,72,,72,72,', +',,,72,72,73,73,72,,73,72,73,,,,,,,72,,,,,,72,,,73,72,,,,,73,,73,,73', +'73,,73,73,73,,73,73,,,,,73,73,74,74,73,,74,73,74,,,,,,,73,,,,,,73,,', +'74,73,,,,,74,,74,,74,74,,74,74,74,,74,74,,,,,74,74,75,75,74,,75,74,75', +',,,,,,74,,,,,,74,,,75,74,,,,,75,,75,,75,75,,75,75,75,,75,75,,,,,75,75', +'76,76,75,,76,75,76,,,,,,,75,,,,,,75,,,76,75,,,,,76,,76,,76,76,,76,76', +'76,,76,76,,,,,76,76,77,77,76,,77,76,77,,,,,,,76,,,,,,76,,,77,76,,,,', +'77,,77,,77,77,,77,77,77,,77,77,,,,,77,77,78,78,77,,78,77,78,,,,,,,77', +',,,,,77,,,78,77,,,,,78,,78,,78,78,,78,78,78,,78,78,,,,,78,78,79,79,78', +',79,78,79,,,,,,,78,,,,,,78,,,79,78,,,,,79,,79,,79,79,,79,79,79,,79,79', +',,,,79,79,80,80,79,,80,79,80,,,,,,,79,,,,,,79,,,80,79,,,,,80,,80,,80', +'80,,80,80,80,,80,80,,,,,80,80,81,81,80,,81,80,81,,,,,,,80,,,,,,80,,', +'81,80,,,,,81,,81,,81,81,,81,81,81,,81,81,,,,,81,81,82,82,81,,82,81,82', +',,,,,,81,,,,,,81,,,82,81,,,,,82,,82,,82,82,,82,82,82,,82,82,,,,,82,82', +'83,83,82,,83,82,83,,,,,,,82,,,,,,82,,,83,82,,,,,83,,83,,83,83,,83,83', +'83,,83,83,,,,,83,83,84,84,83,,84,83,84,,,,,,,83,,,,,,83,,,84,83,,,,', +'84,,84,,84,84,,84,84,84,,84,84,,,,,84,84,85,85,84,,85,84,85,,,,,,,84', +',,,,,84,,,85,84,,,,,85,,85,,85,85,,85,85,85,,85,85,,,,,85,85,86,86,85', +',86,85,86,,,,,,,85,,,,,,85,,,86,85,,,,,86,,86,,86,86,,86,86,86,,86,86', +',,,,86,86,87,87,86,,87,86,87,,,,,,,86,,,,,,86,,,87,86,,,,,87,,87,,87', +'87,,87,87,87,,87,87,,,,,87,87,88,88,87,,88,87,88,,,,,,,87,,,,,,87,,', +'88,87,,,,,88,,88,,88,88,,88,88,88,,88,88,,,,,88,88,89,89,88,,89,88,89', +',,,,,,88,,,,,,88,,,89,88,,,,,89,,89,,89,89,,89,89,89,,89,89,,,,,89,89', +'90,90,89,,90,89,90,,,,,,,89,,,,,,89,,,90,89,,,,,90,,90,,90,90,,90,90', +'90,,90,90,,,,,90,90,91,91,90,,91,90,91,,,,,,,90,,,,,,90,,,91,90,,,,', +'91,,91,,91,91,,91,91,91,,91,91,,,,,91,91,92,92,91,,92,91,92,,,,,,,91', +',,,,,91,,,92,91,,,,,92,,92,,92,92,,92,92,92,,92,92,,,,,92,92,93,93,92', +',93,92,93,,,,,,,92,,,,,,92,,,93,92,,,,,93,,93,,93,93,,93,93,93,,93,93', +',,,,93,93,,,93,285,285,93,,285,,285,285,,,93,,,,,,93,,,93,93,,285,,', +',,,285,,285,,285,285,,285,285,285,,285,285,285,285,,,285,285,95,95,285', +',95,285,95,,,,,,,285,,,,,,285,,,95,285,,,,,95,95,95,95,95,95,95,95,95', +'95,,95,95,,,,,95,95,95,95,95,284,284,95,,284,,284,,,,95,,,,,95,95,,', +',95,,284,,,,,,284,,284,,284,284,,284,284,284,,284,284,284,284,,,284', +'284,97,97,284,,97,284,97,,,,,,,284,,,,,,284,,,97,284,,,,,97,,97,,97', +'97,,97,97,97,,97,97,,,,,97,97,98,98,97,,98,97,98,,,,,,,97,,,,,,97,,', +'98,97,,,,,98,,98,,98,98,,98,98,98,,98,98,,,,,98,98,274,274,98,,274,98', +'274,274,,,,,,98,,,,,,98,,,274,98,,,,,274,,274,,274,274,,274,274,274', +',274,274,,,,,274,274,267,267,274,,267,274,267,,,,,,,274,,,,,,274,,,267', +'274,,,,,267,,267,,267,267,,267,267,267,,267,267,,,,,267,267,260,260', +'267,,260,267,260,,,,,,,267,,,,,,267,,,260,267,,,,,260,,260,,260,260', +',260,260,260,,260,260,,,,,260,260,42,42,260,,42,260,42,,,,,,,260,149', +',,,,260,,,42,260,,,,149,42,149,42,149,42,42,,42,42,42,,42,42,,,,,42', +'42,,,42,103,103,42,149,103,,103,,,,42,,,149,149,,42,,149,149,42,,103', +'103,,149,,,103,,103,,103,103,149,103,103,103,,103,103,,,,,103,103,246', +'246,103,,246,103,246,,,,,,,103,,,,,,103,,,246,103,,,,,246,,246,,246', +'246,,246,246,246,,246,246,,,,,246,246,245,245,246,,245,246,245,,,,,', +',246,153,,,,,246,,,245,246,,,,153,245,153,245,153,245,245,,245,245,245', +',245,245,,,,,245,245,,,245,107,107,245,153,107,,107,,,,245,153,153,153', +'153,,245,,153,153,245,,107,107,,153,,,107,,107,,107,107,153,107,107', +'107,,107,107,,,,,107,107,41,41,107,,41,107,41,,,,,,,107,,,,,,107,,,41', +'107,,,,,41,,41,,41,41,,41,41,41,,41,41,,,,,41,41,40,40,41,,40,41,40', +',,,,,,41,,,,,,41,,,40,41,,,,,40,,40,,40,40,,40,40,40,,40,40,,,,,40,40', +'39,39,40,,39,40,39,,,,,,,40,,,,,,40,,,39,40,,,,,39,,39,,39,39,,39,39', +'39,,39,39,,,,,39,39,313,313,39,,313,39,313,,,,,,,39,,,,,,39,,,313,39', +',,,,313,,313,,313,313,,313,313,313,,313,313,,,,,313,313,113,113,313', +',113,313,113,,,,,,,313,,,,,,313,,,113,313,,,,,113,,113,,113,113,,113', +'113,113,,113,113,,,,,113,113,242,242,113,,242,113,242,,,,,,,113,,,,', +',113,,,242,113,,,,,242,,242,,242,242,,242,242,242,,242,242,,,,,242,242', +'236,236,242,,236,242,236,,,,,,,242,,,,,,242,,,236,242,,,,,236,,236,', +'236,236,,236,236,236,,236,236,,,,,236,236,235,235,236,,235,236,235,', +',,,,,236,,,,,,236,,,235,236,,,,,235,,235,,235,235,,235,235,235,,235', +'235,,,,,235,235,233,233,235,,233,235,233,,,,,,,235,,,,,,235,,,233,235', +',,,,233,,233,,233,233,,233,233,233,,233,233,,,,,233,233,325,325,233', +',325,233,325,,,,,,,233,,,,,,233,,,325,233,,,,,325,,325,,325,325,,325', +'325,325,,325,325,325,325,,,325,325,228,228,325,,228,325,228,228,,,,', +',325,,,,,,325,,,228,325,,,,,228,,228,,228,228,,228,228,228,,228,228', +',,,,228,228,13,13,228,,13,228,13,,,,,,,228,,,,,,228,,,13,228,,,,,13', +',13,,13,13,,13,13,13,,13,13,,,,,13,13,226,226,13,,226,13,226,,,,,,,13', +',,,,,13,,,226,13,,,,,226,,226,,226,226,,226,226,226,,226,226,,,,,226', +'226,12,12,226,,12,226,12,,,,,,,226,,,,,,226,,,12,226,,,,,12,,12,,12', +'12,,12,12,12,,12,12,,,,,12,12,50,50,12,,50,12,50,50,,,,,,12,,,,,,12', +',,50,12,,,,,50,,50,,50,50,,50,50,50,,50,50,,,,,50,50,213,213,50,,213', +'50,213,,,,,,,50,,,,,,50,,,213,50,,,,,213,213,213,213,213,213,213,213', +'213,213,,213,213,,,,,213,213,213,213,213,212,212,213,,212,,212,,,,213', +',,,,213,213,,,,213,,212,,,,,,212,,212,,212,212,,212,212,212,,212,212', +',,,,212,212,211,211,212,,211,212,211,211,,,,,,212,,,,,,212,,,211,212', +',,,,211,,211,,211,211,,211,211,211,,211,211,211,211,,,211,211,210,210', +'211,,210,211,210,210,,,,,,211,,,,,,211,,,210,211,,,,,210,,210,,210,210', +',210,210,210,,210,210,210,210,,,210,210,203,203,210,,203,210,203,203', +',,,,,210,,,,,,210,,,203,210,,,,,203,,203,,203,203,,203,203,203,,203', +'203,203,203,,,203,203,11,11,203,,11,203,11,,,,,,,203,,,,,,203,,,11,203', +',,,,11,,11,,11,11,,11,11,11,,11,11,,,,,11,11,174,174,11,,174,11,174', +',,,,,,11,,,,,,11,,,174,11,,,,,174,,174,,174,174,,174,174,174,,174,174', +',,,,174,174,173,173,174,,173,174,173,,,,,,,174,154,,,,,174,,,173,174', +',,,154,173,154,173,154,173,173,,173,173,173,,173,173,155,,,,173,173', +',,173,,,173,154,155,,155,,155,,173,154,154,154,154,,173,,154,154,173', +',,,,154,,,,,155,,,,154,,156,,155,155,155,155,155,155,,155,155,,,156', +',156,155,156,,,,,,,,155,,,,,,,,,,,,,,156,,,,,,157,,156,156,156,156,156', +'156,,156,156,,157,157,,157,156,157,,,157,,,,,156,,,,,,,,,,,,,,157,,', +',,,158,,157,157,157,157,157,157,,157,157,,158,158,,158,157,158,,,158', +',,,,157,,,,,,,,,,,,,,158,,,,,,159,,158,158,158,158,158,158,,158,158', +',159,159,,159,158,159,,,159,,,,,158,,,,,,,,,,,,,,159,,,,,,160,,159,159', +'159,159,159,159,,159,159,,160,160,,160,159,160,,,160,,,,,159,,,,,,,', +',,,,,,160,,,,,,161,,160,160,160,160,160,160,,160,160,161,161,161,,161', +'160,161,,,161,161,161,161,,160,,,,,,,,,,,,,,161,,,,,,162,,161,161,161', +'161,161,161,,161,161,162,162,162,,162,161,162,,,162,162,162,162,,161', +',,,,,,,,,,342,342,,162,342,,342,342,162,,,162,162,162,162,162,162,,162', +'162,,,342,,,162,,,342,,342,,342,342,162,342,342,342,,342,342,342,342', +',,342,342,344,344,342,,344,342,344,344,,,,,,342,,,,,,342,,,344,342,', +',,,344,,344,,344,344,,344,344,344,,344,344,344,344,,,344,344,4,4,344', +',4,344,4,,,,,,,344,,,,,,344,,,4,344,,,,,4,,4,,4,4,,4,4,4,4,4,4,4,4,', +',4,4,345,345,4,,345,4,345,345,,,,,,4,,,,,,4,,,345,4,,,,,345,,345,,345', +'345,,345,345,345,,345,345,345,345,,,345,345,169,169,345,,169,345,169', +',,,,,,345,,,,,,345,,,169,345,,,,,169,,169,,169,169,,169,169,169,,169', +'169,,,,,169,169,0,0,169,,0,169,0,,,,,,,169,,,,,,169,,,0,169,,,,,0,,0', +',0,0,,0,0,0,,0,0,0,0,,,0,0,297,297,0,,297,0,297,,,,,,,0,,,,,,0,,,297', +'0,,,,,297,,297,,297,297,,297,297,297,,297,297,,,,,297,297,,,297,,,297', +',,,239,239,239,239,297,239,239,239,239,239,297,239,239,,297,,,,,239', +'239,239,194,194,194,194,,194,194,194,194,194,,194,194,,,239,239,,,194', +'194,194,244,244,244,244,,244,244,244,244,244,,244,244,,,194,194,,,244', +'244,244,,,,,,,,,,,,,,,,244,244' ] + racc_action_check = arr = ::Array.new(5345, nil) idx = 0 clist.each do |str| str.split(',', -1).each do |i| @@ -379,178 +386,178 @@ clist = [ end racc_action_pointer = [ - 5034, 254, nil, nil, 4893, 241, nil, 89, nil, nil, - 309, 4343, 4108, 4014, nil, nil, nil, nil, nil, nil, + 5157, 258, nil, nil, 5016, 245, nil, 128, nil, nil, + 309, 4436, 4104, 4010, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, 222, 158, 201, 3588, - 3541, 3494, 3253, -1, 178, nil, 62, 88, nil, 1414, - 4061, nil, nil, nil, 1364, nil, nil, nil, nil, nil, - nil, nil, nil, 213, 1461, 196, 1555, 1602, 1649, 1696, - 1743, 1790, 1837, 43, 2, 1978, 2025, 2072, 2119, 2166, - 2213, 2260, 2307, 2354, 2401, 2448, 2495, 2542, 2589, 2636, - 2683, 2730, 2777, 2824, 147, 2921, 159, 3018, 3065, 197, - 42, 145, 939, 3303, nil, 101, -32, 3447, 876, nil, - 813, 750, 624, 3682, 205, nil, nil, nil, nil, 107, - -9, nil, 207, nil, nil, nil, nil, 561, 87, nil, - 498, 113, nil, nil, 435, nil, 28, nil, 120, nil, - nil, nil, nil, nil, 91, 139, nil, nil, nil, nil, - 1374, 3263, 1507, 1273, 1268, 3407, 3927, 4471, 4496, 4552, - 4577, 4633, 4658, 4714, 4759, 246, 183, 120, 57, nil, - nil, 4987, nil, -6, 3206, 4437, 4390, 182, 205, nil, - nil, 15, nil, 13, -10, 23, -4, 54, 7, 8, - nil, nil, nil, nil, nil, nil, 5153, 372, 168, nil, - 185, nil, 193, 125, nil, 4296, nil, 173, nil, 127, - 28, nil, 4249, 4202, 4155, 1314, 64, 51, nil, -9, - 164, 243, 27, nil, 117, 90, 3967, nil, 3870, nil, - 3823, nil, nil, nil, nil, 3776, nil, 3729, 3397, 88, - nil, 5109, nil, 99, 3350, 119, 5131, 5081, 3159, 128, + nil, nil, nil, nil, nil, nil, 227, -43, 207, 3587, + 3540, 3493, 3252, -1, 183, nil, 86, 3, nil, 1413, + 4151, nil, nil, nil, 1363, nil, nil, nil, nil, nil, + nil, nil, nil, 221, 1460, 208, 1554, 1601, 1648, 1695, + 1742, 1789, 1836, 1883, 1930, 1977, 2024, 2071, 2118, 2165, + 2212, 2259, 2306, 2353, 2400, 2447, 2494, 2541, 2588, 2635, + 2682, 2729, 2776, 2823, 180, 2920, 210, 3017, 3064, 134, + -31, 139, 939, 3302, nil, 148, -32, 3446, 876, nil, + 813, 750, 624, 3681, 85, nil, nil, nil, nil, 16, + -11, nil, 166, nil, nil, nil, nil, 561, 93, nil, + 498, 92, nil, nil, 435, nil, 167, nil, 176, nil, + nil, nil, nil, nil, 96, 145, 75, 12, 1373, 3262, + 1788, 1506, 1268, 3406, 4540, 4567, 4612, 4657, 4702, 4747, + 4792, 4837, 4882, 246, 183, 120, 57, nil, nil, 5110, + nil, -6, 1507, 4530, 4483, 187, 210, nil, nil, 45, + nil, 8, 47, 93, 84, 25, 25, 30, nil, nil, + nil, nil, nil, nil, 5254, 372, 159, nil, 139, nil, + 132, 53, nil, 4389, nil, 47, nil, 220, -9, nil, + 4342, 4295, 4248, 4198, 184, 198, nil, 28, 199, 248, + 88, nil, 27, 177, 1316, nil, 4057, nil, 3963, nil, + nil, nil, nil, 3869, nil, 3822, 3775, 34, nil, 5232, + nil, 84, 3728, 107, 5276, 3396, 3349, 147, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, 3112, 119, nil, 148, nil, 92, 129, 2971, - nil, 162, -25, 173, 152, 16, 2874, nil, 148, 179, - 183, 185, nil, 53, nil, 183, 1931, 1884, nil, nil, - nil, 1128, nil, 1254, nil, nil, nil, 1191, 206, 1508, - 207, nil, nil, nil, nil, 1065, 1002, 214, 155, nil, - nil, nil, 687, 90, nil, 3635, 71, 199, nil, 223, - 226, nil, nil, nil, 226, 236, nil, 3917, nil, nil, - nil, 222, 239, nil, nil, 240, nil, nil, nil, nil, - nil, nil, nil, nil, 4799, nil, 4846, 4940, nil, nil, - 246, nil, nil, nil, 247, nil, 248, nil, 249, nil, - nil, nil, nil, nil ] + 3205, 138, nil, 158, nil, 113, 149, 3158, nil, 177, + 121, 186, 172, 12, 3111, nil, 168, 201, 205, 207, + nil, 53, nil, 205, 2970, 2873, nil, nil, nil, 1128, + nil, 1254, nil, nil, nil, 1191, 214, 5204, 213, nil, + nil, nil, nil, 1065, 1002, 220, 161, nil, nil, nil, + 687, 98, nil, 3634, 234, 214, nil, 238, 239, nil, + nil, nil, 239, 240, nil, 3916, nil, nil, nil, 228, + 245, nil, nil, 246, nil, nil, nil, nil, nil, nil, + nil, nil, 4922, nil, 4969, 5063, nil, nil, 257, nil, + nil, nil, 259, nil, 261, nil, 262, nil, nil, nil, + nil, nil ] racc_action_default = [ - -210, -211, -1, -2, -3, -4, -7, -9, -10, -15, - -105, -211, -211, -211, -44, -45, -46, -47, -48, -49, + -208, -209, -1, -2, -3, -4, -7, -9, -10, -15, + -105, -209, -209, -209, -44, -45, -46, -47, -48, -49, -50, -51, -52, -53, -54, -55, -56, -57, -58, -59, - -60, -61, -62, -63, -64, -65, -70, -71, -75, -211, - -211, -211, -211, -211, -115, -117, -211, -211, -164, -211, - -211, -174, -175, -176, -211, -178, -185, -186, -187, -188, - -189, -190, -191, -211, -211, -6, -211, -211, -211, -211, - -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, - -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, - -211, -211, -211, -211, -211, -124, -119, -210, -210, -27, - -211, -34, -211, -211, -72, -211, -211, -211, -211, -82, - -211, -211, -211, -211, -210, -134, -153, -154, -116, -210, - -210, -143, -145, -146, -147, -148, -149, -42, -211, -167, - -211, -211, -170, -171, -182, -177, -211, 364, -5, -8, - -11, -12, -13, -14, -211, -17, -18, -162, -163, -19, - -20, -21, -22, -23, -24, -25, -26, -28, -29, -30, - -31, -32, -33, -35, -36, -37, -38, -39, -211, -40, - -100, -211, -76, -211, -203, -209, -197, -194, -192, -113, - -125, -186, -128, -190, -211, -200, -198, -206, -188, -189, - -196, -201, -202, -204, -205, -207, -124, -123, -211, -122, - -211, -41, -192, -67, -77, -211, -80, -192, -158, -161, - -211, -74, -211, -211, -211, -124, -194, -210, -155, -211, - -211, -211, -211, -151, -211, -211, -211, -165, -211, -168, - -211, -179, -180, -181, -183, -211, -16, -211, -211, -192, - -102, -124, -112, -211, -195, -211, -193, -211, -211, -192, - -127, -129, -197, -198, -199, -200, -203, -206, -208, -209, - -120, -121, -193, -211, -69, -211, -79, -211, -193, -211, - -73, -211, -85, -211, -91, -211, -211, -95, -194, -192, - -211, -211, -137, -211, -156, -192, -210, -211, -144, -152, - -150, -43, -166, -173, -169, -172, -184, -104, -211, -193, - -192, -108, -114, -109, -126, -130, -131, -211, -66, -78, - -81, -159, -160, -85, -84, -211, -211, -91, -90, -211, - -211, -99, -94, -96, -211, -211, -110, -210, -138, -139, - -140, -211, -211, -135, -136, -211, -142, -101, -103, -111, - -118, -68, -83, -86, -211, -89, -211, -211, -106, -107, - -211, -157, -132, -141, -211, -88, -211, -93, -211, -98, - -133, -87, -92, -97 ] + -60, -61, -62, -63, -64, -65, -70, -71, -75, -209, + -209, -209, -209, -209, -115, -117, -209, -209, -162, -209, + -209, -172, -173, -174, -209, -176, -183, -184, -185, -186, + -187, -188, -189, -209, -209, -6, -209, -209, -209, -209, + -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, + -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, + -209, -209, -209, -209, -209, -124, -119, -208, -208, -27, + -209, -34, -209, -209, -72, -209, -209, -209, -209, -82, + -209, -209, -209, -209, -208, -134, -153, -154, -116, -208, + -208, -143, -145, -146, -147, -148, -149, -42, -209, -165, + -209, -209, -168, -169, -180, -175, -209, 362, -5, -8, + -11, -12, -13, -14, -209, -17, -18, -19, -20, -21, + -22, -23, -24, -25, -26, -28, -29, -30, -31, -32, + -33, -35, -36, -37, -38, -39, -209, -40, -100, -209, + -76, -209, -201, -207, -195, -192, -190, -113, -125, -184, + -128, -188, -209, -198, -196, -204, -186, -187, -194, -199, + -200, -202, -203, -205, -124, -123, -209, -122, -209, -41, + -190, -67, -77, -209, -80, -190, -158, -161, -209, -74, + -209, -209, -209, -124, -192, -208, -155, -209, -209, -209, + -209, -151, -209, -209, -209, -163, -209, -166, -209, -177, + -178, -179, -181, -209, -16, -209, -209, -190, -102, -124, + -112, -209, -193, -209, -191, -209, -209, -190, -127, -129, + -195, -196, -197, -198, -201, -204, -206, -207, -120, -121, + -191, -209, -69, -209, -79, -209, -191, -209, -73, -209, + -85, -209, -91, -209, -209, -95, -192, -190, -209, -209, + -137, -209, -156, -190, -208, -209, -144, -152, -150, -43, + -164, -171, -167, -170, -182, -104, -209, -191, -190, -108, + -114, -109, -126, -130, -131, -209, -66, -78, -81, -159, + -160, -85, -84, -209, -209, -91, -90, -209, -209, -99, + -94, -96, -209, -209, -110, -208, -138, -139, -140, -209, + -209, -135, -136, -209, -142, -101, -103, -111, -118, -68, + -83, -86, -209, -89, -209, -209, -106, -107, -209, -157, + -132, -141, -209, -88, -209, -93, -209, -98, -133, -87, + -92, -97 ] racc_goto_table = [ - 2, 119, 3, 99, 101, 102, 104, 135, 170, 133, - 123, 177, 178, 207, 243, 318, 314, 125, 332, 217, - 140, 141, 142, 143, 220, 1, 288, 245, 289, 216, - 277, 108, 110, 111, 112, 302, 65, 198, 200, 128, - 241, 127, 130, 147, 147, 239, 134, 146, 149, 148, - 148, 263, 304, 280, 320, 276, 267, 342, 345, 350, - 343, 144, 281, 127, 145, 328, 221, 150, 151, 152, - 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, - 163, 164, 165, 166, 167, 168, 240, 173, 298, 197, - 197, 204, 323, 202, 311, 127, 138, 210, 307, 127, - 131, 169, 139, 234, 235, 173, 233, nil, nil, nil, - nil, nil, nil, 249, nil, 324, 218, nil, nil, nil, - nil, 218, 223, nil, nil, nil, 285, nil, 325, nil, - nil, 278, 279, nil, 331, nil, nil, nil, nil, nil, - 119, nil, nil, nil, nil, nil, nil, nil, nil, 339, - 123, nil, nil, nil, nil, nil, nil, 125, 300, nil, - nil, nil, nil, 168, nil, nil, 108, 110, 111, nil, - nil, nil, 264, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, 123, nil, 123, 296, 295, - nil, nil, 125, nil, 125, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, 265, 127, 173, nil, nil, - nil, nil, 271, 273, 338, nil, nil, nil, 291, 282, - 293, nil, 130, nil, nil, nil, nil, 134, nil, 291, - 297, nil, nil, nil, nil, nil, 173, nil, 329, 305, - 306, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, 291, nil, nil, nil, nil, nil, - nil, 312, nil, nil, nil, nil, nil, nil, 127, nil, - nil, nil, nil, nil, nil, nil, nil, 341, nil, nil, - nil, nil, nil, nil, nil, nil, nil, 335, 334, nil, - nil, 168, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, 108, nil, nil, + 2, 119, 3, 99, 101, 102, 104, 135, 125, 133, + 168, 176, 175, 205, 241, 123, 316, 312, 215, 330, + 286, 237, 287, 218, 196, 198, 300, 243, 239, 275, + 214, 108, 110, 111, 112, 318, 65, 302, 274, 128, + 341, 127, 130, 202, 279, 326, 134, 140, 141, 142, + 143, 261, 219, 278, 167, 309, 265, 131, 340, 343, + 348, 144, 139, 127, 145, 146, 147, 148, 149, 150, + 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, + 161, 162, 163, 164, 165, 166, 238, 171, 296, 195, + 195, 321, 1, 200, 232, 127, 138, 208, 305, 127, + 233, 231, nil, nil, nil, 171, nil, nil, nil, nil, + 247, nil, nil, nil, nil, 322, 216, nil, nil, nil, + nil, 216, 221, nil, 283, nil, nil, nil, 323, 277, + 276, nil, nil, nil, 329, nil, nil, nil, 119, nil, + nil, nil, nil, nil, nil, nil, 125, nil, nil, 337, + nil, nil, nil, 123, nil, 298, nil, nil, nil, nil, + nil, 166, nil, nil, 108, 110, 111, nil, nil, nil, + 262, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, 125, nil, 125, nil, nil, 294, 293, 123, nil, + 123, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, 263, 127, 171, nil, nil, nil, nil, + 269, 271, nil, nil, 336, nil, 289, 280, 291, nil, + 130, nil, nil, nil, nil, 134, nil, 289, 295, nil, + nil, nil, nil, nil, 171, nil, 327, 303, 304, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, 334, + nil, nil, 289, nil, nil, nil, nil, nil, nil, 310, + nil, nil, nil, nil, nil, nil, 127, nil, nil, nil, + nil, nil, nil, nil, nil, 339, nil, nil, nil, nil, + nil, nil, nil, nil, nil, 333, 332, nil, nil, 166, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, 354, nil, 356, 358 ] + nil, nil, nil, nil, nil, 108, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, 332, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, 352, nil, 354, 356 ] racc_goto_check = [ - 2, 63, 3, 9, 9, 9, 38, 79, 50, 75, - 36, 53, 55, 43, 54, 46, 45, 30, 65, 64, - 7, 7, 7, 7, 64, 1, 71, 37, 71, 53, - 48, 9, 9, 9, 9, 56, 5, 59, 59, 11, - 57, 9, 9, 30, 30, 51, 9, 12, 12, 31, - 31, 37, 60, 54, 49, 47, 37, 45, 46, 65, - 44, 11, 67, 9, 9, 68, 70, 9, 9, 9, + 2, 62, 3, 9, 9, 9, 37, 78, 29, 74, + 49, 54, 52, 42, 53, 35, 45, 44, 63, 64, + 70, 50, 70, 63, 58, 58, 55, 36, 56, 47, + 52, 9, 9, 9, 9, 48, 5, 59, 46, 11, + 43, 9, 9, 41, 66, 67, 9, 7, 7, 7, + 7, 36, 69, 53, 12, 72, 36, 73, 44, 45, + 64, 11, 6, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 50, 9, 37, 9, - 9, 42, 48, 11, 73, 9, 5, 11, 37, 9, - 74, 13, 6, 80, 81, 9, 83, nil, nil, nil, - nil, nil, nil, 55, nil, 54, 3, nil, nil, nil, - nil, 3, 3, nil, nil, nil, 43, nil, 37, nil, - nil, 53, 55, nil, 37, nil, nil, nil, nil, nil, - 63, nil, nil, nil, nil, nil, nil, nil, nil, 37, - 36, nil, nil, nil, nil, nil, nil, 30, 55, nil, - nil, nil, nil, 9, nil, nil, 9, 9, 9, nil, - nil, nil, 38, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, 36, nil, 36, 79, 75, - nil, nil, 30, nil, 30, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, 2, 9, 9, nil, nil, - nil, nil, 2, 2, 50, nil, nil, nil, 9, 3, - 9, nil, 9, nil, nil, nil, nil, 9, nil, 9, - 9, nil, nil, nil, nil, nil, 9, nil, 63, 9, - 9, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, 9, nil, nil, nil, nil, nil, - nil, 9, nil, nil, nil, nil, nil, nil, 9, nil, - nil, nil, nil, nil, nil, nil, nil, 38, nil, nil, - nil, nil, nil, nil, nil, nil, nil, 2, 3, nil, - nil, 9, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, 9, nil, nil, + 9, 9, 9, 9, 9, 9, 49, 9, 36, 9, + 9, 47, 1, 11, 79, 9, 5, 11, 36, 9, + 80, 82, nil, nil, nil, 9, nil, nil, nil, nil, + 54, nil, nil, nil, nil, 53, 3, nil, nil, nil, + nil, 3, 3, nil, 42, nil, nil, nil, 36, 54, + 52, nil, nil, nil, 36, nil, nil, nil, 62, nil, + nil, nil, nil, nil, nil, nil, 29, nil, nil, 36, + nil, nil, nil, 35, nil, 54, nil, nil, nil, nil, + nil, 9, nil, nil, 9, 9, 9, nil, nil, nil, + 37, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, 29, nil, 29, nil, nil, 78, 74, 35, nil, + 35, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, 2, 9, 9, nil, nil, nil, nil, + 2, 2, nil, nil, 49, nil, 9, 3, 9, nil, + 9, nil, nil, nil, nil, 9, nil, 9, 9, nil, + nil, nil, nil, nil, 9, nil, 62, 9, 9, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, 3, + nil, nil, 9, nil, nil, nil, nil, nil, nil, 9, + nil, nil, nil, nil, nil, nil, 9, nil, nil, nil, + nil, nil, nil, nil, nil, 37, nil, nil, nil, nil, + nil, nil, nil, nil, nil, 2, 3, nil, nil, 9, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, 2, nil, 2, 2 ] + nil, nil, nil, nil, nil, 9, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, 3, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, 2, nil, 2, 2 ] racc_goto_pointer = [ - nil, 25, 0, 2, nil, 32, 36, -47, nil, -8, - nil, -10, -26, 8, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - -30, -24, nil, nil, nil, nil, -37, -151, -31, nil, - nil, nil, -14, -93, -255, -256, -259, -159, -184, -221, - -85, -126, nil, -84, -163, -83, -209, -133, nil, -60, - -194, nil, nil, -45, -95, -268, nil, -155, -218, nil, - -54, -196, nil, -174, 50, -41, nil, nil, nil, -47, - -33, -32, nil, -30 ] + nil, 92, 0, 2, nil, 32, -4, -20, nil, -8, + nil, -10, -39, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, -39, + nil, nil, nil, nil, nil, -32, -149, -31, nil, nil, + nil, -62, -93, -273, -253, -256, -174, -183, -238, -83, + -148, nil, -83, -161, -84, -216, -143, nil, -73, -207, + nil, nil, -45, -96, -265, nil, -171, -236, nil, -68, + -200, nil, -211, 7, -41, nil, nil, nil, -47, -42, + -36, nil, -35 ] racc_goto_default = [ - nil, nil, 333, 199, 4, 5, 6, 7, 8, 10, - 9, 275, nil, nil, 14, 36, 15, 16, 17, 18, - 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, - 29, 30, 31, 32, 33, 34, 35, nil, nil, 37, - 38, 105, nil, nil, 109, nil, nil, nil, nil, nil, - nil, nil, 42, nil, nil, nil, 179, nil, 96, nil, - 180, 184, 182, 115, nil, nil, 114, nil, nil, 120, - nil, 121, 122, 208, nil, nil, 51, 52, 54, nil, - nil, nil, 136, nil ] + nil, nil, 331, 197, 4, 5, 6, 7, 8, 10, + 9, 273, nil, 14, 36, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, nil, nil, 37, 38, + 105, nil, nil, 109, nil, nil, nil, nil, nil, nil, + nil, 42, nil, nil, nil, 177, nil, 96, nil, 178, + 182, 180, 115, nil, nil, 114, nil, nil, 120, nil, + 121, 122, 206, nil, nil, 51, 52, 54, nil, nil, + nil, 136, nil ] racc_reduce_table = [ 0, 0, :racc_error, @@ -598,6 +605,16 @@ racc_reduce_table = [ 1, 91, :_reduce_42, 3, 91, :_reduce_43, 1, 90, :_reduce_none, + 1, 94, :_reduce_none, + 1, 94, :_reduce_none, + 1, 94, :_reduce_none, + 1, 94, :_reduce_none, + 1, 94, :_reduce_none, + 1, 94, :_reduce_none, + 1, 94, :_reduce_none, + 1, 94, :_reduce_none, + 1, 94, :_reduce_none, + 1, 94, :_reduce_none, 1, 95, :_reduce_none, 1, 95, :_reduce_none, 1, 95, :_reduce_none, @@ -607,167 +624,155 @@ racc_reduce_table = [ 1, 95, :_reduce_none, 1, 95, :_reduce_none, 1, 95, :_reduce_none, - 1, 95, :_reduce_none, - 1, 96, :_reduce_none, - 1, 96, :_reduce_none, - 1, 96, :_reduce_none, - 1, 96, :_reduce_none, - 1, 96, :_reduce_none, - 1, 96, :_reduce_none, - 1, 96, :_reduce_none, - 1, 96, :_reduce_none, - 1, 96, :_reduce_none, - 1, 111, :_reduce_64, - 1, 111, :_reduce_65, - 5, 94, :_reduce_66, - 3, 94, :_reduce_67, - 6, 94, :_reduce_68, - 4, 94, :_reduce_69, - 1, 94, :_reduce_70, - 1, 98, :_reduce_71, - 2, 98, :_reduce_72, - 4, 119, :_reduce_73, - 3, 119, :_reduce_74, - 1, 119, :_reduce_75, - 3, 120, :_reduce_76, - 2, 118, :_reduce_77, - 3, 122, :_reduce_78, - 2, 122, :_reduce_79, - 2, 121, :_reduce_80, - 4, 121, :_reduce_81, - 2, 101, :_reduce_82, - 5, 124, :_reduce_83, - 4, 124, :_reduce_84, + 1, 110, :_reduce_64, + 1, 110, :_reduce_65, + 5, 93, :_reduce_66, + 3, 93, :_reduce_67, + 6, 93, :_reduce_68, + 4, 93, :_reduce_69, + 1, 93, :_reduce_70, + 1, 97, :_reduce_71, + 2, 97, :_reduce_72, + 4, 118, :_reduce_73, + 3, 118, :_reduce_74, + 1, 118, :_reduce_75, + 3, 119, :_reduce_76, + 2, 117, :_reduce_77, + 3, 121, :_reduce_78, + 2, 121, :_reduce_79, + 2, 120, :_reduce_80, + 4, 120, :_reduce_81, + 2, 100, :_reduce_82, + 5, 123, :_reduce_83, + 4, 123, :_reduce_84, + 0, 124, :_reduce_none, + 2, 124, :_reduce_86, + 4, 124, :_reduce_87, + 3, 124, :_reduce_88, + 6, 101, :_reduce_89, + 5, 101, :_reduce_90, 0, 125, :_reduce_none, - 2, 125, :_reduce_86, - 4, 125, :_reduce_87, - 3, 125, :_reduce_88, - 6, 102, :_reduce_89, - 5, 102, :_reduce_90, - 0, 126, :_reduce_none, - 4, 126, :_reduce_92, - 3, 126, :_reduce_93, - 5, 100, :_reduce_94, - 1, 127, :_reduce_95, - 2, 127, :_reduce_96, - 5, 128, :_reduce_97, - 4, 128, :_reduce_98, - 1, 129, :_reduce_99, - 1, 93, :_reduce_none, - 4, 93, :_reduce_101, - 1, 131, :_reduce_102, - 3, 131, :_reduce_103, - 3, 130, :_reduce_104, + 4, 125, :_reduce_92, + 3, 125, :_reduce_93, + 5, 99, :_reduce_94, + 1, 126, :_reduce_95, + 2, 126, :_reduce_96, + 5, 127, :_reduce_97, + 4, 127, :_reduce_98, + 1, 128, :_reduce_99, + 1, 92, :_reduce_none, + 4, 92, :_reduce_101, + 1, 130, :_reduce_102, + 3, 130, :_reduce_103, + 3, 129, :_reduce_104, 1, 88, :_reduce_105, 6, 88, :_reduce_106, 6, 88, :_reduce_107, 5, 88, :_reduce_108, 5, 88, :_reduce_109, 5, 88, :_reduce_110, - 4, 136, :_reduce_111, - 1, 137, :_reduce_112, - 1, 133, :_reduce_113, - 3, 133, :_reduce_114, - 1, 132, :_reduce_115, - 2, 132, :_reduce_116, - 1, 132, :_reduce_117, - 6, 99, :_reduce_118, - 2, 99, :_reduce_119, - 3, 138, :_reduce_120, - 3, 138, :_reduce_121, - 1, 139, :_reduce_none, - 1, 139, :_reduce_none, - 0, 135, :_reduce_124, - 1, 135, :_reduce_125, - 3, 135, :_reduce_126, - 1, 141, :_reduce_none, - 1, 141, :_reduce_none, - 1, 141, :_reduce_none, - 3, 140, :_reduce_130, - 3, 140, :_reduce_131, - 6, 103, :_reduce_132, - 7, 104, :_reduce_133, - 1, 146, :_reduce_134, - 1, 145, :_reduce_none, - 1, 145, :_reduce_none, + 4, 135, :_reduce_111, + 1, 136, :_reduce_112, + 1, 132, :_reduce_113, + 3, 132, :_reduce_114, + 1, 131, :_reduce_115, + 2, 131, :_reduce_116, + 1, 131, :_reduce_117, + 6, 98, :_reduce_118, + 2, 98, :_reduce_119, + 3, 137, :_reduce_120, + 3, 137, :_reduce_121, + 1, 138, :_reduce_none, + 1, 138, :_reduce_none, + 0, 134, :_reduce_124, + 1, 134, :_reduce_125, + 3, 134, :_reduce_126, + 1, 140, :_reduce_none, + 1, 140, :_reduce_none, + 1, 140, :_reduce_none, + 3, 139, :_reduce_130, + 3, 139, :_reduce_131, + 6, 102, :_reduce_132, + 7, 103, :_reduce_133, + 1, 145, :_reduce_134, + 1, 144, :_reduce_none, + 1, 144, :_reduce_none, + 1, 146, :_reduce_none, + 2, 146, :_reduce_138, 1, 147, :_reduce_none, - 2, 147, :_reduce_138, - 1, 148, :_reduce_none, - 1, 148, :_reduce_none, - 6, 105, :_reduce_141, - 5, 105, :_reduce_142, - 1, 149, :_reduce_143, - 3, 149, :_reduce_144, - 1, 151, :_reduce_145, - 1, 151, :_reduce_146, - 1, 151, :_reduce_147, - 1, 151, :_reduce_none, - 1, 152, :_reduce_149, - 3, 152, :_reduce_150, + 1, 147, :_reduce_none, + 6, 104, :_reduce_141, + 5, 104, :_reduce_142, + 1, 148, :_reduce_143, + 3, 148, :_reduce_144, + 1, 150, :_reduce_145, + 1, 150, :_reduce_146, + 1, 150, :_reduce_147, 1, 150, :_reduce_none, - 2, 150, :_reduce_152, - 1, 143, :_reduce_153, - 1, 143, :_reduce_154, - 1, 144, :_reduce_155, - 2, 144, :_reduce_156, - 4, 144, :_reduce_157, - 1, 123, :_reduce_158, - 3, 123, :_reduce_159, - 3, 153, :_reduce_160, - 1, 153, :_reduce_161, - 1, 92, :_reduce_none, - 1, 92, :_reduce_none, - 1, 97, :_reduce_164, - 3, 106, :_reduce_165, - 4, 106, :_reduce_166, - 2, 106, :_reduce_167, - 3, 109, :_reduce_168, - 4, 109, :_reduce_169, - 2, 109, :_reduce_170, - 1, 154, :_reduce_171, - 3, 154, :_reduce_172, - 3, 155, :_reduce_173, - 1, 116, :_reduce_none, - 1, 116, :_reduce_none, - 1, 156, :_reduce_176, - 2, 157, :_reduce_177, - 1, 158, :_reduce_178, - 1, 160, :_reduce_179, + 1, 151, :_reduce_149, + 3, 151, :_reduce_150, + 1, 149, :_reduce_none, + 2, 149, :_reduce_152, + 1, 142, :_reduce_153, + 1, 142, :_reduce_154, + 1, 143, :_reduce_155, + 2, 143, :_reduce_156, + 4, 143, :_reduce_157, + 1, 122, :_reduce_158, + 3, 122, :_reduce_159, + 3, 152, :_reduce_160, + 1, 152, :_reduce_161, + 1, 96, :_reduce_162, + 3, 105, :_reduce_163, + 4, 105, :_reduce_164, + 2, 105, :_reduce_165, + 3, 108, :_reduce_166, + 4, 108, :_reduce_167, + 2, 108, :_reduce_168, + 1, 153, :_reduce_169, + 3, 153, :_reduce_170, + 3, 154, :_reduce_171, + 1, 115, :_reduce_none, + 1, 115, :_reduce_none, + 1, 155, :_reduce_174, + 2, 156, :_reduce_175, + 1, 157, :_reduce_176, + 1, 159, :_reduce_177, + 1, 160, :_reduce_178, + 2, 158, :_reduce_179, 1, 161, :_reduce_180, - 2, 159, :_reduce_181, - 1, 162, :_reduce_182, - 1, 163, :_reduce_183, - 2, 163, :_reduce_184, + 1, 162, :_reduce_181, + 2, 162, :_reduce_182, + 1, 111, :_reduce_183, + 1, 114, :_reduce_184, 1, 112, :_reduce_185, - 1, 115, :_reduce_186, - 1, 113, :_reduce_187, - 1, 114, :_reduce_188, - 1, 108, :_reduce_189, - 1, 107, :_reduce_190, - 1, 110, :_reduce_191, - 0, 117, :_reduce_none, - 1, 117, :_reduce_193, - 0, 134, :_reduce_none, - 1, 134, :_reduce_none, - 1, 142, :_reduce_none, - 1, 142, :_reduce_none, - 1, 142, :_reduce_none, - 1, 142, :_reduce_none, - 1, 142, :_reduce_none, - 1, 142, :_reduce_none, - 1, 142, :_reduce_none, - 1, 142, :_reduce_none, - 1, 142, :_reduce_none, - 1, 142, :_reduce_none, - 1, 142, :_reduce_none, - 1, 142, :_reduce_none, - 1, 142, :_reduce_none, - 1, 142, :_reduce_none, - 0, 83, :_reduce_210 ] + 1, 113, :_reduce_186, + 1, 107, :_reduce_187, + 1, 106, :_reduce_188, + 1, 109, :_reduce_189, + 0, 116, :_reduce_none, + 1, 116, :_reduce_191, + 0, 133, :_reduce_none, + 1, 133, :_reduce_none, + 1, 141, :_reduce_none, + 1, 141, :_reduce_none, + 1, 141, :_reduce_none, + 1, 141, :_reduce_none, + 1, 141, :_reduce_none, + 1, 141, :_reduce_none, + 1, 141, :_reduce_none, + 1, 141, :_reduce_none, + 1, 141, :_reduce_none, + 1, 141, :_reduce_none, + 1, 141, :_reduce_none, + 1, 141, :_reduce_none, + 1, 141, :_reduce_none, + 1, 141, :_reduce_none, + 0, 83, :_reduce_208 ] -racc_reduce_n = 211 +racc_reduce_n = 209 -racc_shift_n = 364 +racc_shift_n = 362 racc_token_table = { false => 0, @@ -964,7 +969,6 @@ Racc_token_to_s_table = [ "expression", "higher_precedence", "expressions", - "match_rvalue", "selector_entries", "call_function_expression", "primary_expression", @@ -2087,204 +2091,204 @@ module_eval(<<'.,.,', 'egrammar.ra', 597) end .,., -# reduce 162 omitted - -# reduce 163 omitted - module_eval(<<'.,.,', 'egrammar.ra', 610) - def _reduce_164(val, _values, result) + def _reduce_162(val, _values, result) result = Factory.fqn(val[0][:value]).var ; loc result, val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 616) - def _reduce_165(val, _values, result) + def _reduce_163(val, _values, result) result = Factory.LIST(val[1]); loc result, val[0], val[2] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 617) - def _reduce_166(val, _values, result) + def _reduce_164(val, _values, result) result = Factory.LIST(val[1]); loc result, val[0], val[3] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 618) - def _reduce_167(val, _values, result) + def _reduce_165(val, _values, result) result = Factory.literal([]) ; loc result, val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 621) - def _reduce_168(val, _values, result) + def _reduce_166(val, _values, result) result = Factory.HASH(val[1]); loc result, val[0], val[2] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 622) - def _reduce_169(val, _values, result) + def _reduce_167(val, _values, result) result = Factory.HASH(val[1]); loc result, val[0], val[3] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 623) - def _reduce_170(val, _values, result) + def _reduce_168(val, _values, result) result = Factory.literal({}) ; loc result, val[0], val[3] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 626) - def _reduce_171(val, _values, result) + def _reduce_169(val, _values, result) result = [val[0]] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 627) - def _reduce_172(val, _values, result) + def _reduce_170(val, _values, result) result = val[0].push val[2] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 630) - def _reduce_173(val, _values, result) + def _reduce_171(val, _values, result) result = Factory.KEY_ENTRY(val[0], val[2]); loc result, val[1] result end .,., -# reduce 174 omitted +# reduce 172 omitted -# reduce 175 omitted +# reduce 173 omitted module_eval(<<'.,.,', 'egrammar.ra', 636) - def _reduce_176(val, _values, result) + def _reduce_174(val, _values, result) result = Factory.literal(val[0][:value]) ; loc result, val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 637) - def _reduce_177(val, _values, result) + def _reduce_175(val, _values, result) result = Factory.string(val[0], *val[1]) ; loc result, val[0], val[1][-1] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 638) - def _reduce_178(val, _values, result) + def _reduce_176(val, _values, result) result = Factory.literal(val[0][:value]); loc result, val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 639) - def _reduce_179(val, _values, result) + def _reduce_177(val, _values, result) result = Factory.literal(val[0][:value]); loc result, val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 640) - def _reduce_180(val, _values, result) + def _reduce_178(val, _values, result) result = Factory.literal(val[0][:value]); loc result, val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 641) - def _reduce_181(val, _values, result) + def _reduce_179(val, _values, result) result = [val[0]] + val[1] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 642) - def _reduce_182(val, _values, result) + def _reduce_180(val, _values, result) result = Factory.TEXT(val[0]) result end .,., module_eval(<<'.,.,', 'egrammar.ra', 645) - def _reduce_183(val, _values, result) + def _reduce_181(val, _values, result) result = [val[0]] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 646) - def _reduce_184(val, _values, result) + def _reduce_182(val, _values, result) result = [val[0]] + val[1] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 648) - def _reduce_185(val, _values, result) + def _reduce_183(val, _values, result) result = Factory.NUMBER(val[0][:value]) ; loc result, val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 649) - def _reduce_186(val, _values, result) + def _reduce_184(val, _values, result) result = Factory.QNAME_OR_NUMBER(val[0][:value]) ; loc result, val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 650) - def _reduce_187(val, _values, result) + def _reduce_185(val, _values, result) result = Factory.QREF(val[0][:value]) ; loc result, val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 651) - def _reduce_188(val, _values, result) + def _reduce_186(val, _values, result) result = Factory.literal(:undef); loc result, val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 652) - def _reduce_189(val, _values, result) + def _reduce_187(val, _values, result) result = Factory.literal(:default); loc result, val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 657) - def _reduce_190(val, _values, result) + def _reduce_188(val, _values, result) result = Factory.literal(val[0][:value]) ; loc result, val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 660) - def _reduce_191(val, _values, result) + def _reduce_189(val, _values, result) result = Factory.literal(val[0][:value]); loc result, val[0] result end .,., +# reduce 190 omitted + +module_eval(<<'.,.,', 'egrammar.ra', 666) + def _reduce_191(val, _values, result) + result = nil + result + end +.,., + # reduce 192 omitted -module_eval(<<'.,.,', 'egrammar.ra', 666) - def _reduce_193(val, _values, result) - result = nil - result - end -.,., +# reduce 193 omitted # reduce 194 omitted @@ -2314,12 +2318,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 666) # reduce 207 omitted -# reduce 208 omitted - -# reduce 209 omitted - module_eval(<<'.,.,', 'egrammar.ra', 689) - def _reduce_210(val, _values, result) + def _reduce_208(val, _values, result) result = nil result end diff --git a/lib/puppet/pops/parser/evaluating_parser.rb b/lib/puppet/pops/parser/evaluating_parser.rb index 5d3aba875..7379565c8 100644 --- a/lib/puppet/pops/parser/evaluating_parser.rb +++ b/lib/puppet/pops/parser/evaluating_parser.rb @@ -31,7 +31,6 @@ class Puppet::Pops::Parser::EvaluatingParser end def evaluate_string(scope, s, file_source='unknown') - require 'debugger'; debugger evaluate(scope, parse_string(s, file_source)) end @@ -99,7 +98,6 @@ class Puppet::Pops::Parser::EvaluatingParser formatter = Puppet::Pops::Validation::DiagnosticFormatterPuppetStyle.new if errors.size == 1 || max_errors <= 1 # raise immediately - require 'debugger'; debugger raise Puppet::ParseError.new(formatter.format(errors[0])) end emitted = 0 diff --git a/lib/puppet/pops/parser/lexer2.rb b/lib/puppet/pops/parser/lexer2.rb index 04de1e485..910a4ed1c 100644 --- a/lib/puppet/pops/parser/lexer2.rb +++ b/lib/puppet/pops/parser/lexer2.rb @@ -643,10 +643,15 @@ class Puppet::Pops::Parser::Lexer2 case @lexing_context[:after] # Ends of (potential) R-value generating expressions - when :RPAREN, :RBRACK, :RBRACE, :RRCOLLECT, :RCOLLECT + when :RPAREN, :RBRACK, :RRCOLLECT, :RCOLLECT false - # Operands (that can be followed by DIV (even if illegal in grammar) + # End of (potential) R-value - but must be allowed because of case expressions + # Called out here to not be mistaken for a bug. + when :RBRACE + true + + # Operands (that can be followed by DIV (even if illegal in grammar) when :NAME, :CLASSREF, :NUMBER, :STRING, :BOOLEAN, :DQPRE, :DQMID, :DQPOST, :HEREDOC, :REGEX false diff --git a/spec/unit/pops/evaluator/arithmetic_ops_spec.rb b/spec/unit/pops/evaluator/arithmetic_ops_spec.rb index d1567f77b..a8b98d8ee 100644 --- a/spec/unit/pops/evaluator/arithmetic_ops_spec.rb +++ b/spec/unit/pops/evaluator/arithmetic_ops_spec.rb @@ -67,8 +67,8 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do expect { evaluate(literal('0x12.3') + literal('010'))}.to raise_error(Puppet::ParseError) end - it "'012.3' + '0.3' == 12.6 (not error, floats can start with 0)" do - evaluate(literal('012.3') + literal('010')) == 12.6 + it "'012.3' + '010' == 20.3 (not error, floats can start with 0)" do + evaluate(literal('012.3') + literal('010')).should == 20.3 end end end diff --git a/spec/unit/pops/evaluator/conditionals_spec.rb b/spec/unit/pops/evaluator/conditionals_spec.rb index 21257d429..020734a5d 100644 --- a/spec/unit/pops/evaluator/conditionals_spec.rb +++ b/spec/unit/pops/evaluator/conditionals_spec.rb @@ -66,10 +66,12 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do end it 'unless true {2} elsif true {5} == 5' do + # not supported by concrete syntax evaluate(UNLESS(literal(true), literal(2), IF(literal(true), literal(5)))).should == 5 end it 'unless true {2} elsif false {5} == nil' do + # not supported by concrete syntax evaluate(UNLESS(literal(true), literal(2), IF(literal(false), literal(5)))).should == nil end end diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index 0e3c51aec..cde266a33 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -18,87 +18,446 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do let(:node) { 'node.example.com' } let(:scope) { s = create_test_scope_for_node(node); s } + context "When evaluator evaluates literals" do + { + "1" => 1, + "010" => 8, + "0x10" => 16, + "3.14" => 3.14, + "0.314e1" => 3.14, + "31.4e-1" => 3.14, + "'1'" => '1', + "'banana'" => 'banana', + '"banana"' => 'banana', + "banana" => 'banana', + "banana::split" => 'banana::split', + "false" => false, + "true" => true, + "Array" => Puppet::Pops::Types::TypeFactory.array_of_data(), + "/.*/" => /.*/ + }.each do |source, result| + it "should parse and evaluate the expression '#{source}' to #{result}" do + parser.evaluate_string(scope, source, __FILE__).should == result + end + end + end + + context "When the evaluator evaluates Lists and Hashes" do + { + "[]" => [], + "[1,2,3]" => [1,2,3], + "[1,[2.0, 2.1, [2.2]],[3.0, 3.1]]" => [1,[2.0, 2.1, [2.2]],[3.0, 3.1]], + "[2 + 2]" => [4], + "[1,2,3] == [1,2,3]" => true, + "[1,2,3] != [2,3,4]" => true, + "[1,2,3] == [2,2,3]" => false, + "[1,2,3] != [1,2,3]" => false, + "[1,2,3][2]" => 3, + "[1,2,3] + [4,5]" => [1,2,3,4,5], + "[1,2,3] + [[4,5]]" => [1,2,3,[4,5]], + "[1,2,3] + {'a' => 1, 'b'=>2}" => [1,2,3,['a',1],['b',2]], + "[1,2,3] + 4" => [1,2,3,4], + "[1,2,3] << [4,5]" => [1,2,3,[4,5]], + "[1,2,3] << {'a' => 1, 'b'=>2}" => [1,2,3,{'a' => 1, 'b'=>2}], + "[1,2,3] << 4" => [1,2,3,4], + "[1,2,3,4] - [2,3]" => [1,4], + "[1,2,3,4] - [2,5]" => [1,3,4], + "[1,2,3,4] - 2" => [1,3,4], + "[1,2,3,[2],4] - 2" => [1,3,[2],4], + "[1,2,3,[2,3],4] - [[2,3]]" => [1,2,3,4], + "[1,2,3,3,2,4,2,3] - [2,3]" => [1,4], + "[1,2,3,['a',1],['b',2]] - {'a' => 1, 'b'=>2}" => [1,2,3], + "[1,2,3,{'a'=>1,'b'=>2}] - [{'a' => 1, 'b'=>2}]" => [1,2,3], + }.each do |source, result| + it "should parse and evaluate the expression '#{source}' to #{result}" do + parser.evaluate_string(scope, source, __FILE__).should == result + end + end + + { + "[1,2,3][a]" => :error, + }.each do |source, result| + it "should parse and raise error for '#{source}'" do + expect { parser.evaluate_string(scope, source, __FILE__) }.to raise_error(Puppet::ParseError) + end + end + + { + "{}" => {}, + "{'a'=>1,'b'=>2}" => {'a'=>1,'b'=>2}, + "{'a'=>1,'b'=>{'x'=>2.1,'y'=>2.2}}" => {'a'=>1,'b'=>{'x'=>2.1,'y'=>2.2}}, + "{'a'=> 2 + 2}" => {'a'=> 4}, + "{'a'=> 1, 'b'=>2} == {'a'=> 1, 'b'=>2}" => true, + "{'a'=> 1, 'b'=>2} != {'x'=> 1, 'b'=>2}" => true, + "{'a'=> 1, 'b'=>2} == {'a'=> 2, 'b'=>3}" => false, + "{'a'=> 1, 'b'=>2} != {'a'=> 1, 'b'=>2}" => false, + "{a => 1, b => 2}[b]" => 2, + "{2+2 => sum, b => 2}[4]" => 'sum', + "{'a'=>1, 'b'=>2} + {'c'=>3}" => {'a'=>1,'b'=>2,'c'=>3}, + "{'a'=>1, 'b'=>2} + {'b'=>3}" => {'a'=>1,'b'=>3}, + "{'a'=>1, 'b'=>2} + ['c', 3, 'b', 3]" => {'a'=>1,'b'=>3, 'c'=>3}, + "{'a'=>1, 'b'=>2} + [['c', 3], ['b', 3]]" => {'a'=>1,'b'=>3, 'c'=>3}, + "{'a'=>1, 'b'=>2} - {'b' => 3}" => {'a'=>1}, + "{'a'=>1, 'b'=>2, 'c'=>3} - ['b', 'c']" => {'a'=>1}, + "{'a'=>1, 'b'=>2, 'c'=>3} - 'c'" => {'a'=>1, 'b'=>2}, + }.each do |source, result| + it "should parse and evaluate the expression '#{source}' to #{result}" do + parser.evaluate_string(scope, source, __FILE__).should == result + end + end + + { + "{'a' => 1, 'b'=>2} << 1" => :error, + }.each do |source, result| + it "should parse and raise error for '#{source}'" do + expect { parser.evaluate_string(scope, source, __FILE__) }.to raise_error(Puppet::ParseError) + end + end + end + + context "When the evaluator perform comparisons" do + { + "'a' == 'a'" => true, + "'a' == 'b'" => false, + "'a' != 'a'" => false, + "'a' != 'b'" => true, + "'a' < 'b' " => true, + "'a' < 'a' " => false, + "'b' < 'a' " => false, + "'a' <= 'b'" => true, + "'a' <= 'a'" => true, + "'b' <= 'a'" => false, + "'a' > 'b' " => false, + "'a' > 'a' " => false, + "'b' > 'a' " => true, + "'a' >= 'b'" => false, + "'a' >= 'a'" => true, + "'b' >= 'a'" => true, + "'a' == 'A'" => true, + "'a' != 'A'" => false, + "'a' > 'A'" => false, + "'a' >= 'A'" => true, + "'A' < 'a'" => false, + "'A' <= 'a'" => true, + "1 == 1" => true, + "1 == 2" => false, + "1 != 1" => false, + "1 != 2" => true, + "1 < 2 " => true, + "1 < 1 " => false, + "2 < 1 " => false, + "1 <= 2" => true, + "1 <= 1" => true, + "2 <= 1" => false, + "1 > 2 " => false, + "1 > 1 " => false, + "2 > 1 " => true, + "1 >= 2" => false, + "1 >= 1" => true, + "2 >= 1" => true, + "1 == 1.0 " => true, + "1 < 1.1 " => true, + "'1' < 1.1" => true, + "1.0 == 1 " => true, + "1.0 < 2 " => true, + "'1.0' < 1.1" => true, + "'1.0' < 'a'" => true, + "'1.0' < '' " => true, + "'1.0' < ' '" => true, + "'a' > '1.0'" => true, + "/.*/ == /.*/ " => true, + "/.*/ != /a.*/" => true, + "true == true " => true, + "false == false" => true, + "true == false" => false, + }.each do |source, result| + it "should parse and evaluate the expression '#{source}' to #{result}" do + parser.evaluate_string(scope, source, __FILE__).should == result + end + end + + { + "'a' =~ /.*/" => true, + "'a' =~ '.*'" => true, + "/.*/ != /a.*/" => true, + "'a' !~ /b.*/" => true, + "'a' !~ 'b.*'" => true, + '$x = a; a =~ "$x.*"' => true, + "a =~ Pattern['a.*']" => true, + "$x = /a.*/ a =~ $x" => true, + "$x = Pattern['a.*'] a =~ $x" => true, + }.each do |source, result| + it "should parse and evaluate the expression '#{source}' to #{result}" do + parser.evaluate_string(scope, source, __FILE__).should == result + end + end + + { + "666 =~ /6/" => :error, + "[a] =~ /a/" => :error, + "{a=>1} =~ /a/" => :error, + "/a/ =~ /a/" => :error, + "Array =~ /A/" => :error, + }.each do |source, result| + it "should parse and raise error for '#{source}'" do + expect { parser.evaluate_string(scope, source, __FILE__) }.to raise_error(Puppet::ParseError) + end + end + + { + "1 in [1,2,3]" => true, + "4 in [1,2,3]" => false, + "a in {x=>1, a=>2}" => true, + "z in {x=>1, a=>2}" => false, + "ana in bananas" => true, + "xxx in bananas" => false, + "/ana/ in bananas" => true, + "/xxx/ in bananas" => false, + "ANA in bananas" => false, # ANA is a type, not a String + "'ANA' in bananas" => true, + "ana in 'BANANAS'" => true, + "/ana/ in 'BANANAS'" => false, + "/ANA/ in 'BANANAS'" => true, + "xxx in 'BANANAS'" => false, + "[2,3] in [1,[2,3],4]" => true, + "[2,4] in [1,[2,3],4]" => false, + "[a,b] in ['A',['A','B'],'C']" => true, + "[x,y] in ['A',['A','B'],'C']" => false, + "a in {a=>1}" => true, + "x in {a=>1}" => false, + "'A' in {a=>1}" => true, + "'X' in {a=>1}" => false, + "a in {'A'=>1}" => true, + "x in {'A'=>1}" => false, + "/xxx/ in {'aaaxxxbbb'=>1}" => true, + "/yyy/ in {'aaaxxxbbb'=>1}" => false, + "15 in [1, 0xf]" => true, + "15 in [1, '0xf']" => true, + "'15' in [1, 0xf]" => true, + "15 in [1, 115]" => false, + "1 in [11, '111']" => false, + "'1' in [11, '111']" => false, + "Array[Integer] in [2, 3]" => false, + "Array[Integer] in [2, [3, 4]]" => true, + "Array[Integer] in [2, [a, 4]]" => false, + "Integer in { 2 =>'a'}" => true, + "Integer in {'a'=>'a'}" => false, + "Integer in {'a'=>1}" => false, + }.each do |source, result| + it "should parse and evaluate the expression '#{source}' to #{result}" do + parser.evaluate_string(scope, source, __FILE__).should == result + end + end + + end + context "When the evaluator performs arithmetic" do context "on Integers" do - { "2+2" => 4, - "2 + 2" => 4, - "7 - 3" => 4, - "6 * 3" => 18, - "6 / 3" => 2, - "6 % 3" => 0, + { "2+2" => 4, + "2 + 2" => 4, + "7 - 3" => 4, + "6 * 3" => 18, + "6 / 3" => 2, + "6 % 3" => 0, "10 % 3" => 1, "-(6/3)" => -2, "-6/3 " => -2, "8 >> 1" => 4, "8 << 1" => 16, }.each do |source, result| - it "should parse and evaluate the expression '#{source}' to #{result}" do - parser.evaluate_string(scope, source, __FILE__).should == result + it "should parse and evaluate the expression '#{source}' to #{result}" do + parser.evaluate_string(scope, source, __FILE__).should == result + end end - end context "on Floats" do { - "2.2 + 2.2" => 4.4, - "7.7 - 3.3" => 4.4, - "6.1 * 3.1" => 18.91, - "6.6 / 3.3" => 2.0, - "6.6 % 3.3" => 0.0, + "2.2 + 2.2" => 4.4, + "7.7 - 3.3" => 4.4, + "6.1 * 3.1" => 18.91, + "6.6 / 3.3" => 2.0, + "6.6 % 3.3" => 0.0, "10.0 % 3.0" => 1.0, "-(6.0/3.0)" => -2.0, - "-6.0/3.0 " => -2.0, + "-6.0/3.0 " => -2.0, }.each do |source, result| - it "should parse and evaluate the expression '#{source}' to #{result}" do - parser.evaluate_string(scope, source, __FILE__).should == result + it "should parse and evaluate the expression '#{source}' to #{result}" do + parser.evaluate_string(scope, source, __FILE__).should == result + end end - end { "3.14 << 2" => :error, "3.14 >> 2" => :error, }.each do |source, result| + it "should parse and raise error for '#{source}'" do + expect { parser.evaluate_string(scope, source, __FILE__) }.to raise_error(Puppet::ParseError) + end + end + end + + context "on strings requiring boxing to Numeric" do + { + "'2' + '2'" => 4, + "'2.2' + '2.2'" => 4.4, + "'0xF7' + '010'" => 0xFF, + "'0xF7' + '0x8'" => 0xFF, + "'0367' + '010'" => 0xFF, + "'012.3' + '010'" => 20.3, + }.each do |source, result| + it "should parse and evaluate the expression '#{source}' to #{result}" do + parser.evaluate_string(scope, source, __FILE__).should == result + end + end + + { + "'0888' + '010'" => :error, + "'0xWTF' + '010'" => :error, + "'0x12.3' + '010'" => :error, + "'0x12.3' + '010'" => :error, + }.each do |source, result| + it "should parse and raise error for '#{source}'" do + expect { parser.evaluate_string(scope, source, __FILE__) }.to raise_error(Puppet::ParseError) + end + end + end + end + end # arithmetic + + context "When the evaluator evaluates conditionals" do + { + "if true {5}" => 5, + "if false {5}" => nil, + "if false {2} else {5}" => 5, + "if false {2} elsif true {5}" => 5, + "if false {2} elsif false {5}" => nil, + "unless false {5}" => 5, + "unless true {5}" => nil, + "unless true {2} else {5}" => 5, + }.each do |source, result| + it "should parse and evaluate the expression '#{source}' to #{result}" do + parser.evaluate_string(scope, source, __FILE__).should == result + end + end + + { + "case 1 { 1 : { yes } }" => 'yes', + "case 2 { 1,2,3 : { yes} }" => 'yes', + "case 2 { 1,3 : { no } 2: { yes} }" => 'yes', + "case 2 { 1,3 : { no } 5: { no } default: { yes }}" => 'yes', + "case 2 { 1,3 : { no } 5: { no } }" => nil, + "case 'banana' { 1,3 : { no } /.*ana.*/: { yes } }" => 'yes', + "case 'banana' { /.*(ana).*/: { $1 } }" => 'ana', + }.each do |source, result| + it "should parse and evaluate the expression '#{source}' to #{result}" do + parser.evaluate_string(scope, source, __FILE__).should == result + end + end + + { + "2 ? { 1 => no, 2 => yes}" => 'yes', + "3 ? { 1 => no, 2 => no}" => nil, + "3 ? { 1 => no, 2 => no, default => yes }" => 'yes', + "3 ? { 1 => no, default => yes, 3 => no }" => 'yes', + "'banana' ? { /.*(ana).*/ => $1 }" => 'ana', + }.each do |source, result| + it "should parse and evaluate the expression '#{source}' to #{result}" do + parser.evaluate_string(scope, source, __FILE__).should == result + end + end + end + + context "When the evaluator performs boolean operations" do + { + "true and true" => true, + "false and true" => false, + "true and false" => false, + "false and false" => false, + "true or true" => true, + "false or true" => true, + "true or false" => true, + "false or false" => false, + "! true" => false, + "!! true" => true, + "!! false" => false, + "! 'x'" => false, + "! ''" => true, + "! undef" => true, + "! [a]" => false, + "! []" => false, + "! {a=>1}" => false, + "! {}" => false, + "true and false and '0xwtf' + 1" => false, + "false or true or '0xwtf' + 1" => true, + }.each do |source, result| + it "should parse and evaluate the expression '#{source}' to #{result}" do + parser.evaluate_string(scope, source, __FILE__).should == result + end + end + + { + "false || false || '0xwtf' + 1" => :error, + }.each do |source, result| it "should parse and raise error for '#{source}'" do expect { parser.evaluate_string(scope, source, __FILE__) }.to raise_error(Puppet::ParseError) end end - - end -# it "3.14 << 2 == error" do; expect { evaluate(literal(3.14) << literal(2))}.to raise_error(Puppet::ParseError); end -# it "3.14 >> 2 == error" do; expect { evaluate(literal(3.14) >> literal(2))}.to raise_error(Puppet::ParseError); end - end end + + context "When evaluator performs calls" do + let(:populate) do + parser.evaluate_string(scope, "$a = 10 $b = [1,2,3]") + end + + { + 'sprintf( "x%iy", $a )' => "x10y", + '"x%iy".sprintf( $a )' => "x10y", + '$b.reduce |$memo,$x| { $memo + $x }' => 6, + 'reduce($b) |$memo,$x| { $memo + $x }' => 6, + }.each do |source, result| + it "should parse and evaluate the expression '#{source}' to #{result}" do + populate + parser.evaluate_string(scope, source, __FILE__).should == result + end + end + + { + '"value is ${a*2} yo"' => :error, + }.each do |source, result| + it "should parse and raise error for '#{source}'" do + expect { parser.evaluate_string(scope, source, __FILE__) }.to raise_error(Puppet::ParseError) + end + end + end + + context "When evaluator performs string interpolation" do + let(:populate) do + parser.evaluate_string(scope, "$a = 10 $b = [1,2,3]") + end + + { + '"value is $a yo"' => "value is 10 yo", + '"value is \$a yo"' => "value is $a yo", + '"value is ${a} yo"' => "value is 10 yo", + '"value is \${a} yo"' => "value is ${a} yo", + '"value is ${$a} yo"' => "value is 10 yo", + '"value is ${$a*2} yo"' => "value is 20 yo", + '"value is ${sprintf("x%iy",$a)} yo"' => "value is x10y yo", + '"value is ${"x%iy".sprintf($a)} yo"' => "value is x10y yo", + }.each do |source, result| + it "should parse and evaluate the expression '#{source}' to #{result}" do + populate + parser.evaluate_string(scope, source, __FILE__).should == result + end + end + + { + '"value is ${a*2} yo"' => :error, + }.each do |source, result| + it "should parse and raise error for '#{source}'" do + expect { parser.evaluate_string(scope, source, __FILE__) }.to raise_error(Puppet::ParseError) + end + end + end + end -# context "on strings requiring boxing to Numeric" do -# it "'2' + '2' == 4" do -# evaluate(literal('2') + literal('2')).should == 4 -# end -# -# it "'2.2' + '2.2' == 4.4" do -# evaluate(literal('2.2') + literal('2.2')).should == 4.4 -# end -# -# it "'0xF7' + '0x8' == 0xFF" do -# evaluate(literal('0xF7') + literal('0x8')).should == 0xFF -# end -# -# it "'0367' + '010' == 0xFF" do -# evaluate(literal('0367') + literal('010')).should == 0xFF -# end -# -# it "'0888' + '010' == error" do -# expect { evaluate(literal('0888') + literal('010'))}.to raise_error(Puppet::ParseError) -# end -# -# it "'0xWTF' + '010' == error" do -# expect { evaluate(literal('0xWTF') + literal('010'))}.to raise_error(Puppet::ParseError) -# end -# -# it "'0x12.3' + '010' == error" do -# expect { evaluate(literal('0x12.3') + literal('010'))}.to raise_error(Puppet::ParseError) -# end -# -# it "'012.3' + '0.3' == 12.6 (not error, floats can start with 0)" do -# evaluate(literal('012.3') + literal('010')) == 12.6 -# end -# end -# end diff --git a/spec/unit/pops/parser/lexer2_spec.rb b/spec/unit/pops/parser/lexer2_spec.rb index 1c5970a2f..51f78de30 100644 --- a/spec/unit/pops/parser/lexer2_spec.rb +++ b/spec/unit/pops/parser/lexer2_spec.rb @@ -219,7 +219,6 @@ describe 'Lexer2' do "a" => ["a /./", [:NAME, :DIV, :DOT, :DIV]], "A" => ["A /./", [:CLASSREF, :DIV, :DOT, :DIV]], ")" => [") /./", [:RPAREN, :DIV, :DOT, :DIV]], - "}" => ["} /./", [:RBRACE, :DIV, :DOT, :DIV]], "]" => ["] /./", [:RBRACK, :DIV, :DOT, :DIV]], "|>" => ["|> /./", [:RCOLLECT, :DIV, :DOT, :DIV]], "|>>" => ["|>> /./", [:RRCOLLECT, :DIV, :DOT, :DIV]], From 08595191695f4ead8e2a268ead7bdf162498e4d9 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 26 Oct 2013 04:51:44 +0200 Subject: [PATCH 050/800] (maint) Fix indentation --- spec/unit/pops/evaluator/evaluating_parser_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index cde266a33..600291c60 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -193,11 +193,11 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do end { - "666 =~ /6/" => :error, - "[a] =~ /a/" => :error, + "666 =~ /6/" => :error, + "[a] =~ /a/" => :error, "{a=>1} =~ /a/" => :error, - "/a/ =~ /a/" => :error, - "Array =~ /A/" => :error, + "/a/ =~ /a/" => :error, + "Array =~ /A/" => :error, }.each do |source, result| it "should parse and raise error for '#{source}'" do expect { parser.evaluate_string(scope, source, __FILE__) }.to raise_error(Puppet::ParseError) From d9be82dcce3504fbc922e28c5b5c0bd375512bd1 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 27 Oct 2013 16:22:33 +0100 Subject: [PATCH 051/800] (maint) Add missing DELETES token in grammar All tokens should be listed in the beginning of the grammar. The DELETES (-=) token was missing. (Seems to work anyway because it is given a precedence). --- lib/puppet/pops/parser/egrammar.ra | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/pops/parser/egrammar.ra b/lib/puppet/pops/parser/egrammar.ra index 81de5791c..5bda3cf17 100644 --- a/lib/puppet/pops/parser/egrammar.ra +++ b/lib/puppet/pops/parser/egrammar.ra @@ -6,7 +6,7 @@ class Puppet::Pops::Parser::Parser token STRING DQPRE DQMID DQPOST token LBRACK RBRACK LBRACE RBRACE SYMBOL FARROW COMMA TRUE -token FALSE EQUALS APPENDS LESSEQUAL NOTEQUAL DOT COLON LLCOLLECT RRCOLLECT +token FALSE EQUALS APPENDS DELETES LESSEQUAL NOTEQUAL DOT COLON LLCOLLECT RRCOLLECT token QMARK LPAREN RPAREN ISEQUAL GREATEREQUAL GREATERTHAN LESSTHAN token IF ELSE token DEFINE ELSIF VARIABLE CLASS INHERITS NODE BOOLEAN From 01b5296503762843bdf64b71d44b7a3b85683f55 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 27 Oct 2013 16:23:00 +0100 Subject: [PATCH 052/800] (maint) Cleanup comments --- lib/puppet/pops/evaluator/evaluator_impl.rb | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index 10da82454..207f736c7 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -299,12 +299,12 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator [ evaluate(o.left_expr, scope), evaluate(o.right_expr, scope) ] end - # Evaluates assignment with operators =, +=, -= and []= + # Evaluates assignment with operators =, +=, -= and # # @example Puppet DSL # $a = 1 # $a += 1 - # @todo support for -= ('without' to remove from array) concrete syntax not yet implemented + # $a -= 1 # def eval_AssignmentExpression(o, scope) @@ -645,7 +645,6 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator name = o.functor_expr.value assert_function_available(name, o, scope) evaluated_arguments = o.arguments.collect {|arg| evaluate(arg, scope) } - # rval_required = o.rval_required # TODO: is this really needed - it can just return nil for a function that is not rval # wrap lambda in a callable block if it is present evaluated_arguments << Puppet::Evaluator::Lambda.new(self, o.lambda) if o.lambda call_function(name, evaluated_arguments, o, scope) do |result| @@ -735,9 +734,9 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator when String when Numeric else - fail "Internal error: a variable name should result in a String when evaluated. Got expression of #{o.expr.class} type.", o, scope + fail "Internal error: a variable name should result in a String or Number when evaluated. Got expression of #{o.expr.class} type.", o, scope end - # TODO: Check for valid variable name + # TODO: Check for valid variable name (Task for validator) # TODO: semantics of undefined variable in scope, this just returns what scope does == value or nil get_variable_value(name, o, scope) end From 68f72726dabadf1287714388237740dcc6283c3a Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 28 Oct 2013 01:46:03 +0100 Subject: [PATCH 053/800] (maint) Remove unused file CallOperator --- lib/puppet/pops/evaluator/call_operator.rb | 28 ---------------------- 1 file changed, 28 deletions(-) delete mode 100644 lib/puppet/pops/evaluator/call_operator.rb diff --git a/lib/puppet/pops/evaluator/call_operator.rb b/lib/puppet/pops/evaluator/call_operator.rb deleted file mode 100644 index 50f12fba4..000000000 --- a/lib/puppet/pops/evaluator/call_operator.rb +++ /dev/null @@ -1,28 +0,0 @@ -# CallOperator -# Call operator is part of evaluation -# -class Puppet::Pops::Evaluator::CallOperator - # Provides access to the Puppet 3.x runtime (scope, etc.) - # This separation has been made to make it easier to later migrate the evaluator to an improved runtime. - # - include Puppet::Pops::Evaluator::Runtime3Support - - attr_reader :eval_visitor - def initialize - @call_visitor = Puppet::Pops::Visitor.new(self, "call", 2, 2) - end - - def call (o, scope, *params, &block) - x = @call_visitor.visit(o, scope, params) - if block_given? - block.call(x) - else - x - end - end - - protected - - def call_FunctionExpression o, scope, params - end -end From ada7756f4b30e2904505e1316593c9ae8ac3e31e Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 28 Oct 2013 02:02:38 +0100 Subject: [PATCH 054/800] (maint) Protect against endless recursion if user calls with wrong type When user of the function find_adapter called this method with an array, the eContainer method returned the container of each member of the array until it got an emtpy array. At that point recursion became endless. The function is not designed to be called with an array of objects, but better to produce a nil value than causing an endless recursion for which no stack trace can be obtained. --- lib/puppet/pops/utils.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/pops/utils.rb b/lib/puppet/pops/utils.rb index 01a540432..1fbb2c8b1 100644 --- a/lib/puppet/pops/utils.rb +++ b/lib/puppet/pops/utils.rb @@ -96,7 +96,7 @@ module Puppet::Pops::Utils # with true, and Adaptable#adapters. # def self.find_adapter(o, adapter) - return nil unless o + return nil if o.nil? || (o.is_a?(Array) && o.empty?) a = adapter.get(o) return a if a return find_adapter(o.eContainer, adapter) From f61cdde810eccd3a08a8206b7c6650713a9df5f0 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 28 Oct 2013 02:04:27 +0100 Subject: [PATCH 055/800] (#22363) Change evaluator error reporting to use Pops::Issues The evaluator made call to a fail function that just printed the message without any details. This changes the behavior to use the Pops::Issues system. Many issues have been added. Some error messages were changed. --- lib/puppet/pops/evaluator/access_operator.rb | 55 +++--- lib/puppet/pops/evaluator/closure.rb | 2 +- lib/puppet/pops/evaluator/evaluator_impl.rb | 164 +++++------------- .../pops/evaluator/relationship_operator.rb | 73 +++++--- lib/puppet/pops/evaluator/runtime3_support.rb | 39 +++-- lib/puppet/pops/issue_reporter.rb | 2 +- lib/puppet/pops/issues.rb | 105 ++++++++++- lib/puppet/pops/model/model_label_provider.rb | 7 +- lib/puppet/pops/validation.rb | 3 + spec/unit/pops/evaluator/access_ops_spec.rb | 8 +- .../pops/evaluator/collections_ops_spec.rb | 2 +- .../pops/evaluator/comparison_ops_spec.rb | 2 +- .../evaluator/string_interpolation_spec.rb | 10 +- spec/unit/pops/evaluator/variables_spec.rb | 70 ++++---- spec/unit/pops/issues_spec.rb | 2 +- 15 files changed, 302 insertions(+), 242 deletions(-) diff --git a/lib/puppet/pops/evaluator/access_operator.rb b/lib/puppet/pops/evaluator/access_operator.rb index 229ad23af..a2a9c2f0c 100644 --- a/lib/puppet/pops/evaluator/access_operator.rb +++ b/lib/puppet/pops/evaluator/access_operator.rb @@ -7,6 +7,8 @@ class Puppet::Pops::Evaluator::AccessOperator # include Puppet::Pops::Evaluator::Runtime3Support + Issues = Puppet::Pops::Issues + attr_reader :semantic # Initialize with AccessExpression to enable reporting issues @@ -25,20 +27,19 @@ class Puppet::Pops::Evaluator::AccessOperator protected def access_Object(o, scope, keys) - # TODO: Use Issues for better handling fail("The [] operator is not applicable to the result of the LHS expression: #{o.class}", semantic.left_expr, scope) end def access_String(o, scope, keys) case keys.size when 0 - fail("String supports [] with one or two arguments. Got #{keys.size}", @semantic.left_expr, scope) + fail(Puppet::Pops::Issues::BAD_STRING_SLICE_ARITY, @semantic.left_expr, {:actual => keys.size}) when 1 o[box_numeric(keys[0], @semantic.keys, scope)] when 2 o[box_numeric(keys[0], @semantic.keys, scope), box_numeric(keys[1], @semantic.keys, scope)] else - fail("String supports [] with one or two arguments. Got #{keys.size}", @semantic.left_expr, scope) + fail(Puppet::Pops::Issues::BAD_STRING_SLICE_ARITY, @semantic.left_expr, {:actual => keys.size}) end end @@ -59,19 +60,17 @@ class Puppet::Pops::Evaluator::AccessOperator case keys.size when 0 # What does this mean: [] ? Is it error, unit, empty array - fail("Array supports [] with one or two arguments. Got #{keys.size}", @semantic.left_expr, scope) + fail(Puppet::Pops::Issues::BAD_ARRAY_SLICE_ARITY, @semantic.left_expr, {:actual => keys.size}) when 1 - # TODO: Objects passed in case of failure are not ideal, it gives all the keys in the model, not the - # failing key (needs to pass index as well) - o[box_numeric(keys[0], @semantic.keys, scope)] + k = box_numeric(keys[0], @semantic.keys[0], scope) + o[k] when 2 # A slice [from, to] with support for -1 to mean start, or end respectively. - # TODO: Objects passed in case of failure are not ideal, it gives all the keys in the model, not the - # failing key (needs to pass index as well) - o[box_numeric(keys[0], @semantic.keys, scope), box_numeric(keys[1], @semantic.keys, scope)] + k1 = box_numeric(keys[0], @semantic.keys[0], scope) + k2 = box_numeric(keys[1], @semantic.keys[1], scope) + o[k1, k2] else - # TODO: Use Issues for better handling - fail("Array supports [] with one or two arguments. Got #{keys.size}", semantic.left_expr, scope) + fail(Puppet::Pops::Issues::BAD_ARRAY_SLICE_ARITY, @semantic.left_expr, {:actual => keys.size}) end end @@ -83,8 +82,7 @@ class Puppet::Pops::Evaluator::AccessOperator result = keys.collect {|k| o[k] } case result.size when 0 - # TODO: Use Issues for better handling - fail("Hash supports [] with one or more arguments. Got #{keys.size}", semantic.left_expr, scope) + fail(Puppet::Pops::Issues::BAD_HASH_SLICE_ARITY, @semantic.left_expr, {:actual => keys.size}) when 1 result.pop else @@ -102,11 +100,14 @@ class Puppet::Pops::Evaluator::AccessOperator end unless keys.size.between?(2, 3) - fail("Integer[] only accepts two or three parameters (from, to, step). Got #{keys.size}", @semantic.keys, scope) + fail(Puppet::Pops::Issues::BAD_INTEGER_SLICE_ARITY, @semantic, {:actual => keys.size}) + end + keys.each_with_index do |x, index| + fail(Puppet::Pops::Issues::BAD_INTEGER_SLICE_TYPE, @semantic.keys[index], + {:actual => x.class}) unless x.is_a?(Numeric) end - keys.each {|x| fail("Integer[] requires all keys to be integers", @semantic.keys, scope) unless x.is_a?(Numeric) } from, to, step = keys - fail("Integer[] cannot step with increment of 0", @semantic.keys, scope) if step == 0 + fail(Puppet::Pops::Issues::INTEGER_STEP_0, @semantic.keys[2]) if step == 0 step ||= 1 # Ok, so this is quite bad for very large arrays... @@ -117,9 +118,9 @@ class Puppet::Pops::Evaluator::AccessOperator # It is not possible to create a collection of Hash types. # def access_PHashType(o, scope, keys) - keys.each do |k| + keys.each_with_index do |k, index| unless k.is_a?(Puppet::Pops::Types::PAbstractType) - fail("Arguments to Hash[] must be types. Got #{k.class}", @semantic.keys, scope) + fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[index], {:base_type => 'Hash', :actual => k.class}) end end case keys.size @@ -136,8 +137,7 @@ class Puppet::Pops::Evaluator::AccessOperator result.element_type = keys[1] result else - # TODO: Use Issues for better handling - fail("Hash type only accepts one or two type parameters (key, and value type). Got #{keys.size}", @semantic.keys, scope) + fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_ARITY, @semantic, {:base_type => 'Hash', :min => 1, :max => 2, :actual => keys.size}) end end @@ -149,14 +149,13 @@ class Puppet::Pops::Evaluator::AccessOperator Marshal.load(Marshal.dump(o)) # Deep copy when 1 unless keys[0].is_a?(Puppet::Pops::Types::PAbstractType) - fail("Argument to Array[] must be a type. Got #{keys[0].class}", @semantic.keys, scope) + fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[0], {:base_type => 'Array', :actual => keys[0].class}) end result = Puppet::Pops::Types::PArrayType.new() result.element_type = keys[0] result else - # TODO: Use Issues for better handling - fail("Array type only accepts one type parameter (element type). Got #{keys.size}", semantic.right_expr, scope) + fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_ARITY, @semantic, {:base_type => 'Array', :min => 1, :actual => keys.size}) end end @@ -177,8 +176,7 @@ class Puppet::Pops::Evaluator::AccessOperator return Marshal.load(Marshal.dump(o)) # Deep copy end unless o.title.nil? - # TODO: Use Issues for better handling - fail("Cannot specialize an already specialized resource type", semantic.left_expr, scope) + fail(Puppet::Pops::Issues::ILLEGAL_TYPE_SPECIALIZATION, semantic.left_expr, {:kind => 'Resource'}) end # type_name is LHS type_name if set, else the first given arg @@ -189,7 +187,7 @@ class Puppet::Pops::Evaluator::AccessOperator when String type_name.downcase else - fail("First argument to Resource[] must be a resource type or a string. Got #[type_name.class}") + fail(Puppet::Pops::Issues::ILLEGAL_RESOURCE_SPECIALIZATION, @semantic.keys, {:actual => type_name.class}) end keys = [nil] if keys.size < 1 # if there was only a type_name and it was consumed result = keys.collect do |t| @@ -207,8 +205,7 @@ class Puppet::Pops::Evaluator::AccessOperator return Marshal.load(Marshal.dump(o)) # Deep copy end unless o.class_name.nil? - # TODO: Use Issues for better handling - fail("Cannot specialize an already specialized Class type", semantic.left_expr, scope) + fail(Puppet::Pops::Issues::ILLEGAL_TYPE_SPECIALIZATION, semantic.left_expr, {:kind => 'Class'}) end result = keys.collect do |c| ctype = Puppet::Pops::Types::PHostClassType.new() diff --git a/lib/puppet/pops/evaluator/closure.rb b/lib/puppet/pops/evaluator/closure.rb index 0e7232654..58d9addd1 100644 --- a/lib/puppet/pops/evaluator/closure.rb +++ b/lib/puppet/pops/evaluator/closure.rb @@ -1,6 +1,6 @@ -class Puppet::Evaluator::Lambda +class Puppet::Pops::Evaluator::Lambda attr_reader :evaluator attr_reader :model attr_reader :enclosing_scope diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index 207f736c7..f673ef41c 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -1,8 +1,8 @@ require 'rgen/ecore/ecore' require 'puppet/pops/evaluator/compare_operator' -require 'puppet/pops/evaluator/call_operator' require 'puppet/pops/evaluator/relationship_operator' require 'puppet/pops/evaluator/access_operator' +require 'puppet/pops/evaluator/closure' # This implementation of {Puppet::Pops::Evaluator} performs evaluation using the puppet 3.x runtime system # in a manner largely compatible with Puppet 3.x, but adds new features and introduces constraints. @@ -29,6 +29,11 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # include Puppet::Pops::Evaluator::Runtime3Support + # Reference to Issues name space makes it easier to refer to issues + # (Issues are shared with the validator). + # + Issues = Puppet::Pops::Issues + def initialize @@eval_visitor ||= Puppet::Pops::Visitor.new(self, "eval", 1, 1) @@lvalue_visitor ||= Puppet::Pops::Visitor.new(self, "lvalue", 1, 1) @@ -94,7 +99,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator @@lvalue_visitor.visit_this(self, o, scope) end - # Call a closure - Can only be called with a closure (for now), may be refactored later + # Call a closure - Can only be called with a Closure (for now), may be refactored later # to also handle other types of calls (function calls are also handled by CallNamedFunction and CallMethod, they # could create similar objects to Closure, wait until other types of defines are instantiated - they may behave # as special cases of calls - i.e. 'new') @@ -103,7 +108,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # @raise ArgumentError, if given closure is not a Puppet::Pops::Evaluator::Closure # def call(closure, args, scope, &block) - raise ArgumentError, "Can only call a Lambda" unless closure.is_a?(Puppet::Pops::Evaluator::Closure) + raise ArgumentError, "Can only call a Lambda" unless closure.is_a?(Puppet::Pops::Evaluator::Lambda) pblock = closure.model parameters = pblock.parameters || [] @@ -155,7 +160,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator ensure set_scope_nesting_level(scope, scope_memo) end - if block_given + if block_given? block.call(result) else result @@ -175,8 +180,8 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # Catches all illegal lvalues # - def lvalue_Object(name, value, o, scope) - fail("An object of type #{o.class} can not be on the left side of an assignment", o, scope) + def lvalue_Object(o, scope) + fail(Issues::ILLEGAL_ASSIGNMENT, o) end # Assign value to named variable. @@ -191,15 +196,14 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # def assign_String(name, value, o, scope) if name =~ /::/ - # Issues::CROSS_SCOPE_ASSIGNMENT - fail("Cross-namespace assignment is not allowed, cannot assign to $#{name}", o.left_expr, scope) + fail(Issues::CROSS_SCOPE_ASSIGNMENT, o.left_expr, {:name => name}) end set_variable(name, value, o, scope) value end - def assign_Number(n, value, o, scope) - fail("Cannot assign to the numeric (match result) variable: $#{name}", o.left_expr, scope) + def assign_Numeric(n, value, o, scope) + fail(Issues::ILLEGAL_NUMERIC_ASSIGNMENT, o.left_expr, {:varname => n.to_s}) end # Assign values to named variables in an array. @@ -235,7 +239,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # Catches all illegal assignment (e.g. 1 = 2, {'a'=>1} = 2, etc) # def assign_Object(name, value, o, scope) - fail("An object of type #{o.class} is not assignable", o, scope) + fail(Issues::ILLEGAL_ASSIGNMENT, o) end def eval_Factory(o, scope) @@ -325,7 +329,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # same way as ArithmeticExpression performs `+`. assign(name, calculate(get_variable_value(name, o, scope), value, :'+', o.left_expr, o.right_expr, scope), o, scope) rescue ArgumentError => e - fail("Append assignment += failed with error: #{e.message}", o, scope) + fail(Issues::APPEND_FAILED, o, {:message => e.message}) end when :'-=' @@ -339,10 +343,10 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # Delegate to delete function to deal with check of LHS, and perform deletion assign(name, delete(get_variable_value(name, o, scope), value), o, scope) rescue ArgumentError => e - fail("'Without' assignment -= failed with error: #{e.message}", o, scope) + fail(Issues::APPEND_FAILED, o, {:message => e.message}) end else - fail("Unknown assignment operator: '#{o.operator}'.", o, scope) + fail(Issues::UNSUPPORTED_OPERATOR, o, {:operator => o.operator}) end value end @@ -354,13 +358,13 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # def eval_ArithmeticExpression(o, scope) unless ARITHMETIC_OPERATORS.include?(o.operator) - fail("Unknown arithmetic operator #{o.operator}", o, scope) + fail(Issues::UNSUPPORTED_OPERATOR, o, {:operator => o.operator}) end left, right = eval_BinaryExpression(o, scope) begin result = calculate(left, right, o.operator, o.left_expr, o.right_expr, scope) rescue ArgumentError => e - fail(e.message, o, scope) + fail(Issues::RUNTIME_ERROR, o, {:detail => e.message}, e) end result end @@ -370,7 +374,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # def calculate(left, right, operator, left_o, right_o, scope) unless ARITHMETIC_OPERATORS.include?(operator) - raise ArgumentError, "Unknown arithmetic operator #{o.operator}" + fail(Issues::UNSUPPORTED_OPERATOR, left_o.eContainer, {:operator => o.operator}) end if (left.is_a?(Array) || left.is_a?(Hash)) && COLLECTION_OPERATORS.include?(operator) @@ -382,8 +386,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator delete(left, right) when :'<<' unless left.is_a?(Array) - # TODO: when improving fail, pass o.left_expr - raise ArgumentError, "Left operand in '<<' expression is not an Array" + fail(Issues::OPERATOR_NOT_APPLICABLE, left_o, {:operator => operator, :left_value => left}) end left + [right] end @@ -394,7 +397,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator begin result = left.send(operator, right) rescue NoMethodError => e - raise ArgumentError, "Operator #{operator} not applicable to a value of type #{left.class}" + fail(Issues::OPERATOR_NOT_APPLICABLE, left_o, {:operator => operator, :left_value => left}) end result end @@ -444,7 +447,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # left can be assigned to right @@type_calculator.assignable?(right, left) else - fail("Internal Error: unhandled comparison operator '#{o.operator}'.", o, scope) + fail(Issues::UNSUPPORTED_OPERATOR, o, {:operator => o.operator}) end else case o.operator @@ -461,11 +464,15 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator when :'>=' @@compare_operator.compare(left,right) >= 0 else - fail("Internal Error: unhandled comparison operator '#{o.operator}'.", o, scope) + fail(Issues::UNSUPPORTED_OPERATOR, o, {:operator => o.operator}) end end rescue ArgumentError => e - fail("Comparison of #{left.class} #{o.operator} #{right.class} is not possible. Caused by #{e.message}.", o, scope) + fail(Issues::COMPARISON_NOT_POSSIBLE, o, { + :operator => o.operator, + :left_value => left, + :right_value => right, + :detail => e.message}) end end @@ -485,10 +492,10 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator begin pattern = Regexp.new(pattern) unless pattern.is_a?(Regexp) rescue StandardError => e - fail "Can not convert right expression to a regular expression. Caused by: '#{e.message}'", o, scope + fail(Issues::MATCH_NOT_REGEXP, o.right_expr, {:detail => e.message}) end unless left.is_a?(String) - fail("Match expression requires a String as left operand", o.left_expr, scope) + fail(Issues::MATCH_NOT_STRING, o.left_expr, {:left_value => left}) end matched = pattern.match(left) # nil, or MatchData @@ -641,12 +648,14 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator def eval_CallNamedFunctionExpression(o, scope) # The functor expression is not evaluated, it is not possible to select the function to call # via an expression like $a() - fail("Unacceptable expression for name of function", o, scope) unless o.functor_expr.is_a? Puppet::Pops::Model::QualifiedName + unless o.functor_expr.is_a? Puppet::Pops::Model::QualifiedName + fail(Issues::ILLEGAL_EXPRESSION, o.functor_expr, {:feature=>'function name', :container => o}) + end name = o.functor_expr.value assert_function_available(name, o, scope) evaluated_arguments = o.arguments.collect {|arg| evaluate(arg, scope) } # wrap lambda in a callable block if it is present - evaluated_arguments << Puppet::Evaluator::Lambda.new(self, o.lambda) if o.lambda + evaluated_arguments << Puppet::Pops::Evaluator::Lambda.new(self, o.lambda, scope) if o.lambda call_function(name, evaluated_arguments, o, scope) do |result| # prevent functions that are not r-value from leaking its return value rvalue_function?(name, o, scope) ? result : nil @@ -657,17 +666,17 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # def eval_CallMethodExpression(o, scope) unless o.functor_expr.is_a? Puppet::Pops::Model::NamedAccessExpression - fail("Unacceptable expression for name of function", o.functor_expr, scope) + fail(Issues::ILLEGAL_EXPRESSION, o.functor_expr, {:feature=>'function accessor', :container => o}) end receiver = evaluate(o.functor_expr.left_expr, scope) name = o.functor_expr.right_expr unless name.is_a? Puppet::Pops::Model::QualifiedName - fail("Unacceptable expression for name of function/method", name, scope) + fail(Issues::ILLEGAL_EXPRESSION, o.functor_expr, {:feature=>'function name', :container => o}) end name = name.value # the string function name assert_function_available(name, o, scope) evaluated_arguments = [receiver] + (o.arguments || []).collect {|arg| evaluate(arg, scope) } - evaluated_arguments << Puppet::Evaluator::Lambda.new(self, o.lambda) if o.lambda + evaluated_arguments << Puppet::Pops::Evaluator::Lambda.new(self, o.lambda, scope) if o.lambda call_function(name, evaluated_arguments, o, scope) do |result| # prevent functions that are not r-value from leaking its return value rvalue_function?(name, o, scope) ? result : nil @@ -734,7 +743,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator when String when Numeric else - fail "Internal error: a variable name should result in a String or Number when evaluated. Got expression of #{o.expr.class} type.", o, scope + fail(Issues::ILLEGAL_VARIABLE_EXPRESSION, o.expr) end # TODO: Check for valid variable name (Task for validator) # TODO: semantics of undefined variable in scope, this just returns what scope does == value or nil @@ -747,99 +756,6 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator o.segments.collect {|expr| evaluate(expr, scope).to_s}.join end - # Create a metadata object that describes an attribute (an ECore EAttribute). - # Only free-standing metadata is created to the actual attribute in a class (that happens later) - # - # Part of type creation. - # - # @todo possibly support: - # changeable: false (i.e. constants) - # volatile: non having storage allocated (default for derived), if true = some kind of cache - # transient: not serialized - # unsettable: how the value can be reset (to default, or unset state) - # - def eval_CreateAttributeExpression o, scope - # Note: Only some of the validation required takes place here - # There are additional nonsensical invariants; like derived attributes with default values - the_attr = RGen::ECore::EAttribute.new - - evaluator.fail("An attribute name must be a String", o, scope) unless o.name.is_a? String - the_attr.name = o.name - - datatype = datatype_reference(evaluate(o.type, scope), o.name, scope) - evaluator.fail("Invalid data-type expression.", o.type, scope) unless datatype - the_attr.eType = datatype - - min = evaluate(o.min_expr, scope) - max = evaluate(o.max_expr, scope) - min = 0 if !min || min < 0 - max = (min == 0 ? 1 : min) unless max - max = -1 if max == 'unlimited' || max == 'many' || max == '*' || max == 'M' - max = -1 if max < -1 - if max >= 0 && min > max - fail("The max occurrence of an attribute must be bigger than the min occurrence (or be unlimited).", o.max_expr, scope) - end - if(min == 0 && max == 0) - fail("The min and max occurrences of an attribute can not both be 0.", o.max_expr, scope) - end - the_attr.lowerBound = min - the_attr.upperBound = max - - # derived? - the_attr.derived = true if o.derived_expr - - # TODO: possibly support: - # changeable: false (i.e. constants) - # volatile: non having storage allocated (default for derived), if true = cache - # transient: not serialized - # unsettable: how the value can be reset (to default, or unset state) - # - the_attr - end - - # Creates a metadata object describing an Enumerator (An Ecore EEnum) - # This only creates the free standing metadata. It is later used when creating a type. - # - def eval_CreateEnumExpression o, scope - e_enum = RGen::ECore::EEnum.new - e_enum.name = o.name - values = evaluate(o.values, scope) - case values - when Array - # Convert entries, the numerical representation is based on order - values.each_index do |i| - e_literal = RGen::ECore::EEnumLiteral.new - e_literal.literal = values.slice(i).to_s - e_literal.value = i - e_literal.eEnum = e_enum - end - when Hash - begin - # Convert entries, the numerical representation is based on mapping name to value - values.each do |k,v| - e_literal = RGen::ECore::EEnumLiteral.new - e_literal.literal = k.to_s - e_literal.value = v.to_i - e_literal.eEnum = e_enum - end - rescue StandardError => e - fail("The given hash could not be converted to Enum entries. Error: "+e.message, o, scope) - end - else - fail("An enumerator accepts an Array of String values, or a Hash of String to Integer mappings. Got instance of #{values.class}.", o, scope) - end - end - - # Creates a type, and returns a Class implementing this type - # @todo this implementation uses scope to create the type; should use the type creator associated - # with the logic that wants to create a type. - # - def eval_CreateTypeExpression(o, scope) - # The actual type creator is kept in the top scope (it keeps all created types) - # The type_creator calls back to this evaluator to evaluate attributes and enums - # it then creates both the model and a class implementation. - scope.top_scope.type_creator.create_type(o, scope, self) - end # If the wrapped expression is a QualifiedName, it is taken as the name of a variable in scope. # Note that this is different from the 3.x implementation, where an initial qualified name diff --git a/lib/puppet/pops/evaluator/relationship_operator.rb b/lib/puppet/pops/evaluator/relationship_operator.rb index 7f2fdfb8d..d65d82fcc 100644 --- a/lib/puppet/pops/evaluator/relationship_operator.rb +++ b/lib/puppet/pops/evaluator/relationship_operator.rb @@ -13,6 +13,22 @@ class Puppet::Pops::Evaluator::RelationshipOperator # include Puppet::Pops::Evaluator::Runtime3Support + Issues = Puppet::Pops::Issues + + class IllegalRelationshipOperandError < RuntimeError + attr_reader :operand + def initialize operand + @operand = operand + end + end + + class NotCatalogTypeError < RuntimeError + attr_reader :type + def initialize type + @type = type + end + end + def initialize @type_transformer_visitor = Puppet::Pops::Visitor.new(self, "transform", 1, 1) @type_calculator = Puppet::Pops::Types::TypeCalculator.new() @@ -28,7 +44,7 @@ class Puppet::Pops::Evaluator::RelationshipOperator # Catch all non transformable objects # @api private def transform_Object(o, scope) - fail("Not a valid reference in a relationship", o, scope) + raise IllegalRelationshipOperandError.new(o) end # A string must be a type reference in string format @@ -54,9 +70,11 @@ class Puppet::Pops::Evaluator::RelationshipOperator # def assert_catalog_type(o, scope) unless @type_calculator.assignable?(@catalog_type, o) - fail("The reference is not a catalog type", o, scope) + raise NotCatalogTypeError.new(o) end - # TODO must check if this is an abstract PResourceType (i.e. without a type_name) - which should fail + # TODO must check if this is an abstract PResourceType (i.e. without a type_name) - which should fail ? + # e.g. File -> File (and other similar constructs) - maybe the catalog protects against this since references + # may be to future objects... o end @@ -69,33 +87,46 @@ class Puppet::Pops::Evaluator::RelationshipOperator :'<~' => :subscription } + # Evaluate a relationship. + # TODO: The error reporting is not fine grained since evaluation has already taken place + # There is no references to the original source expressions at this point, only the overall + # relationship expression. (e.g.. the expression may be ['string', func_call(), etc.] -> func_call()) + # To implement this, the general evaluator needs to be able to track each evaluation result and associate + # it with a corresponding expression. This structure should then be passed to the relationship operator. + # def evaluate (left_right_evaluated, relationship_expression, scope) # assert operator (should have been validated, but this logic makes assumptions which would # screw things up royally). Better safe than sorry. unless RELATIONSHIP_OPERATORS.include?(relationship_expression.operator) - fail("Unknown relationship operator #{relationship_expression.operator}.", relationship_expression, scope) + fail(Issues::UNSUPPORTED_OPERATOR, relationship_expression, {:operator => relationship_expression.operator}) end - # Turn each side into an array of types (this also asserts their type) - # (note wrap in array first if value is not already an array) - # - # TODO: Later when objects are Puppet Runtime Objects and know their type, it will be more efficient to check/infer - # the type first since a chained operation then does not have to visit each element again. This is not meaningful now - # since inference needs to visit each object each time, and this is what the transformation does anyway). - # - # real is [left, right], and both the left and right may be a single value or an array. In each case all content - # should be flattened, and then transformed to a type. - # - real = real.collect {|x| [x].flatten.collect {|x| transform(x, scope) }} + begin + # Turn each side into an array of types (this also asserts their type) + # (note wrap in array first if value is not already an array) + # + # TODO: Later when objects are Puppet Runtime Objects and know their type, it will be more efficient to check/infer + # the type first since a chained operation then does not have to visit each element again. This is not meaningful now + # since inference needs to visit each object each time, and this is what the transformation does anyway). + # + # real is [left, right], and both the left and right may be a single value or an array. In each case all content + # should be flattened, and then transformed to a type. + # + real = real.collect {|x| [x].flatten.collect {|x| transform(x, scope) }} - # reverse order if operator is Right to Left - source, target = reverse_operator?(relationship_expression) ? real.reverse : real + # reverse order if operator is Right to Left + source, target = reverse_operator?(relationship_expression) ? real.reverse : real - # Add the relationships to the catalog - source.each {|s| target.each {|t| add_relationship(s, t, RELATION_TYPE[relationship_expression.operator]) }} + # Add the relationships to the catalog + source.each {|s| target.each {|t| add_relationship(s, t, RELATION_TYPE[relationship_expression.operator]) }} - # Produce the transformed source RHS (if this is a chain, this does not need to be done again) - real.slice(1) + # Produce the transformed source RHS (if this is a chain, this does not need to be done again) + real.slice(1) + rescue NotCatalogTypeError => e + fail(Issues::ILLEGAL_RELATIONSHIP_OPERAND_TYPE, relationship_expression, {:type => @type_calculator.string(e.type)}) + rescue IllegalRelationshipOperandError => e + fail(Issues::ILLEGAL_RELATIONSHIP_OPERAND_TYPE, relationship_expression, {:operand => e.operand}) + end end def reverse_operator?(o) diff --git a/lib/puppet/pops/evaluator/runtime3_support.rb b/lib/puppet/pops/evaluator/runtime3_support.rb index d8318d434..0be315bd6 100644 --- a/lib/puppet/pops/evaluator/runtime3_support.rb +++ b/lib/puppet/pops/evaluator/runtime3_support.rb @@ -1,19 +1,14 @@ module Puppet::Pops::Evaluator::Runtime3Support - # Fails the evaluation of _o_ in the given scope with the given message + # Fails the evaluation of _semantic_ with a given issue. # - # @status may need an extra parameter for error code - # @param message [String] the error message - # @param o [Object] the object for which evaluation failed in some way. Used to determine origin. - # @param scope [Puppet::Parser::Scope] the runtime specific scope in which evaluation failed + # @param issue [Puppet::Pops::Issue] the issue to report + # @param semantic [Puppet::Pops::ModelPopsObject] the object for which evaluation failed in some way. Used to determine origin. + # @param options [Hash] hash of optional named data elements for the given issue # @return [!] this method does not return # @raise [Puppet::ParseError] an evaluation error initialized from the arguments (TODO: Change to EvaluationError) - # @todo fail evaluation with message, failure evaluating o, in scope - # @todo This just fails with the message, should include a label for the expression - # and any origin set in an adapter for o. Scope could be passed for debugging - # purposes / stackdump # - def fail(message, o, scope) - raise Puppet::ParseError.new(message) + def fail(issue, semantic, options={}, except=nil) + diagnostic_producer.accept(issue, semantic, options, except) end # Binds the given variable name to the given value in the given scope. @@ -48,7 +43,7 @@ module Puppet::Pops::Evaluator::Runtime3Support def set_match_data(match_data, o, scope) # TODO: Get file, line from semantic o and pass as options to scope since it tracks where these values - # came from. + # came from. (No it does not! It simply uses them to report errors). # NOTE: The 3x scope adds one ephemeral(match) to its internal stack per match that succeeds ! It never # clears anything. Thus a context that performs many matches will get very deep (there simply is no way to # clear the match variables without rolling back the ephemeral stack.) @@ -158,7 +153,7 @@ module Puppet::Pops::Evaluator::Runtime3Support # def box_numeric(v, o, scope) unless n = Puppet::Pops::Utils.to_n(v) - fail("Value '#{v}' can not be converted to Numeric.", o, scope) + fail(Puppet::Pops::Issues::NOT_NUMERIC, o, {:value => v}) end n end @@ -167,7 +162,7 @@ module Puppet::Pops::Evaluator::Runtime3Support # as a side effect. Fails if the function does not exist. # def assert_function_available(name, o, scope) - fail("Unknown function #{name}", o, scope) unless Puppet::Parser::Functions.function(name) + fail(Puppet::Pops::Issues::UNKNOWN_FUNCTION, o, {:name => name}) unless Puppet::Parser::Functions.function(name) end def call_function(name, args, o, scope) @@ -218,4 +213,20 @@ module Puppet::Pops::Evaluator::Runtime3Support end end + # Creates a diagnostic producer + def diagnostic_producer + Puppet::Pops::Validation::DiagnosticProducer.new( + ExceptionRaisingAcceptor.new(), # Raises exception on all issues + Puppet::Pops::Validation::SeverityProducer.new(), # All issues are errors + Puppet::Pops::Model::ModelLabelProvider.new()) + end + + # An acceptor of diagnostics that immediately raises an exception. + class ExceptionRaisingAcceptor < Puppet::Pops::Validation::Acceptor + def accept(diagnostic) + super + Puppet::Pops::IssueReporter.assert_and_report(self, {:message => "Evaluation Error:" }) + raise ArgumentError, "Internal Error: Configuration of runtime error handling wrong: should have raised exception" + end + end end \ No newline at end of file diff --git a/lib/puppet/pops/issue_reporter.rb b/lib/puppet/pops/issue_reporter.rb index 675b03992..bc9511cbe 100644 --- a/lib/puppet/pops/issue_reporter.rb +++ b/lib/puppet/pops/issue_reporter.rb @@ -13,7 +13,7 @@ class Puppet::Pops::IssueReporter max_warnings = Puppet[:max_warnings] + 1 max_deprecations = Puppet[:max_deprecations] + 1 emit_warnings = options[:emit_warnings] || false - emit_errors = options[:emit_errors] || true + emit_errors = options[:emit_errors].nil? ? true : !!options[:emit_errors] emit_message = options[:message] emit_exception = options[:exception_class] || Puppet::ParseError diff --git a/lib/puppet/pops/issues.rb b/lib/puppet/pops/issues.rb index f0d737b62..83a418bb3 100644 --- a/lib/puppet/pops/issues.rb +++ b/lib/puppet/pops/issues.rb @@ -73,10 +73,12 @@ module Puppet::Pops::Issues end # Returns the label provider given as a key in the hash passed to #format. + # If given an argument, calls #label on the label provider (caller would otherwise have to + # call label.label(it) # - def label + def label(it = nil) raise "Label provider key :label must be set to produce the text of the message!" unless @data[:label] - @data[:label] + it.nil? ? @data[:label] : @data[:label].label(it) end # Returns the label provider given as a key in the hash passed to #format. @@ -162,6 +164,14 @@ module Puppet::Pops::Issues "Illegal attempt to assign to the numeric match result variable '$#{varname}'. Numeric variables are not assignable" end + APPEND_FAILED = issue :APPEND_FAILED, :message do + "Append assignment += failed with error: #{message}" + end + + DELETE_FAILED = issue :DELETE_FAILED, :message do + "'Delete' assignment -= failed with error: #{message}" + end + # parameters cannot have numeric names, clashes with match result variables ILLEGAL_NUMERIC_PARAMETER = issue :ILLEGAL_NUMERIC_PARAMETER, :name do "The numeric parameter name '$#{varname}' cannot be used (clashes with numeric match result variables)" @@ -185,7 +195,25 @@ module Puppet::Pops::Issues # For unsupported operators (e.g. -= in puppet 3). # UNSUPPORTED_OPERATOR = hard_issue :UNSUPPORTED_OPERATOR, :operator do - "The operator #{operator} in #{label.a_an(semantic)} is not supported in this version of the puppet language." + "The operator '#{operator}' in #{label.a_an(semantic)} is not supported." + end + + # For non applicable operators (e.g. << on Hash). + # + OPERATOR_NOT_APPLICABLE = hard_issue :OPERATOR_NOT_APPLICABLE, :operator, :left_value do + "Operator '#{operator}' is not applicable to #{label.a_an(left_value)}." + end + + COMPARISON_NOT_POSSIBLE = hard_issue :COMPARISON_NOT_POSSIBLE, :operator, :left_value, :right_value, :detail do + "Comparison of: #{label(left_value)} #{operator} #{label(right_value)}, is not possible. Caused by '#{detail}'." + end + + MATCH_NOT_REGEXP = hard_issue :MATCH_NOT_REGEXP, :detail do + "Can not convert right match operand to a regular expression. Caused by '#{detail}'." + end + + MATCH_NOT_STRING = hard_issue :MATCH_NOT_STRING, :left_value do + "Left match operand must result in a String value. Got #{label.a_an(left_value)}." end # Some expressions/statements may not produce a value (known as right-value, or rvalue). @@ -246,6 +274,13 @@ module Puppet::Pops::Issues "Illegal expression. #{label.a_an_uc(semantic)} is unacceptable as #{feature} in #{label.a_an(container)}" end + # Issues when an expression is used where it is not legal. + # E.g. an arithmetic expression where a hostname is expected. + # + ILLEGAL_VARIABLE_EXPRESSION = hard_issue :ILLEGAL_VARIABLE_EXPRESSION do + "Illegal variable expression. #{label.a_an_uc(semantic)} did not produce a variable name (String or Numeric)." + end + # Issues when an expression is used illegaly in a query. # query only supports == and !=, and not <, > etc. # @@ -269,4 +304,68 @@ module Puppet::Pops::Issues DEPRECATED_NAME_AS_TYPE = issue :DEPRECATED_NAME_AS_TYPE, :name do "Resource references should now be capitalized. The given '#{name}' does not have the correct form" end + + ILLEGAL_RELATIONSHIP_OPERAND_TYPE = issue :ILLEGAL_RELATIONSHIP_OPERAND_TYPE, :operand do + "Illegal relationship operand, can not form a relationship with #{label.a_an(operand)}. A Catalog type is required." + end + + NOT_CATALOG_TYPE = issue :NOT_CATALOG_TYPE, :type do + "Illegal relationship operand, can not form a relationship with something of type #{type}. A Catalog type is required." + end + + BAD_STRING_SLICE_ARITY = issue :BAD_STRING_SLICE_ARITY, :actual do + "String supports [] with one or two arguments. Got #{actual}" + end + + BAD_ARRAY_SLICE_ARITY = issue :BAD_ARRAY_SLICE_ARITY, :actual do + "Array supports [] with one or two arguments. Got #{actual}" + end + + BAD_HASH_SLICE_ARITY = issue :BAD_HASH_SLICE_ARITY, :actual do + "Hash supports [] with one or more arguments. Got #{actual}" + end + + BAD_INTEGER_SLICE_ARITY = issue :BAD_INTEGER_SLICE_ARITY, :actual do + "Integer supports [] with two or three arguments (from, to, step). Got #{actual}" + end + + BAD_INTEGER_SLICE_TYPE = issue :BAD_INTEGER_SLICE_TYPE, :actual do + "Integer [] requires all arguments to be integers. Got #{actual}" + end + + INTEGER_STEP_0 = issue :INTEGER_STEP_0 do + "Integer [] can not step with a '0' increment." + end + + BAD_TYPE_SLICE_TYPE = issue :BAD_TYPE_SLICE_TYPE, :base_type, :actual do + "#{base_type}[] arguments must be types. Got #{actual}" + end + + BAD_TYPE_SLICE_ARITY = issue :BAD_TYPE_SLICE_ARITY, :base_type, :min, :max, :actual do + if max + "#{base_type}[] requires #{min} to #{max} arguments. Got #{actual}" + else + "#{base_type}[] requires #{min} arguments. Got #{actual}" + end + end + + ILLEGAL_TYPE_SPECIALIZATION = issue :ILLEGAL_TYPE_SPECIALIZATION, :kind do + "Cannot specialize an already specialized #{kind} type" + end + + ILLEGAL_RESOURCE_SPECIALIZATION = issue :ILLEGAL_RESOURCE_SPECIALIZATION, :actual do + "First argument to Resource[] must be a resource type or a string. Got #{actual}." + end + + NOT_NUMERIC = issue :NOT_NUMERIC, :value do + "The value '#{value}' cannot be converted to Numeric." + end + + UNKNOWN_FUNCTION = issue :UNKNOWN_FUNCTION, :name do + "Unknown function: '#{name}'." + end + + RUNTIME_ERROR = issue :RUNTIME_ERROR, :detail do + "Error while evaluating #{label.a_an(semantic)}, #{detail}" + end end diff --git a/lib/puppet/pops/model/model_label_provider.rb b/lib/puppet/pops/model/model_label_provider.rb index 469130de4..2a91fae4e 100644 --- a/lib/puppet/pops/model/model_label_provider.rb +++ b/lib/puppet/pops/model/model_label_provider.rb @@ -67,9 +67,12 @@ class Puppet::Pops::Model::ModelLabelProvider < Puppet::Pops::LabelProvider def label_ResourceExpression o ; "Resource Statement" end def label_SelectorExpression o ; "Selector Expression" end def label_SelectorEntry o ; "Selector Option" end - def label_String o ; "Ruby String" end - def label_Object o ; "Ruby Object" end + def label_String o ; "String" end + def label_Object o ; "Object" end + def label_Hash o ; "Hash" end def label_QualifiedName o ; "Name" end def label_QualifiedReference o ; "Type Name" end + # TODO: Could use the TypeFactory to infer and output more detailed type information instead of + # just printing Object, Hash, Array, etc. end diff --git a/lib/puppet/pops/validation.rb b/lib/puppet/pops/validation.rb index f003c1d42..ff9b3e666 100644 --- a/lib/puppet/pops/validation.rb +++ b/lib/puppet/pops/validation.rb @@ -194,6 +194,7 @@ module Puppet::Pops::Validation arguments[:semantic] ||= semantic # A detail message is always provided, but is blank by default. + # TODO: this support is questionable, it requires knowledge that :detail is special arguments[:detail] ||= '' origin_adapter = Puppet::Pops::Utils.find_adapter(semantic, Puppet::Pops::Adapters::OriginAdapter) @@ -221,6 +222,8 @@ module Puppet::Pops::Validation @file = file @source_pos = source_pos @arguments = arguments + # TODO: Currently unused, the intention is to provide more information (stack backtrace, etc.) when + # debugging or similar - this to catch internal problems reported as higher level issues. @exception = exception end end diff --git a/spec/unit/pops/evaluator/access_ops_spec.rb b/spec/unit/pops/evaluator/access_ops_spec.rb index 1f0af97bd..71c57ce1a 100644 --- a/spec/unit/pops/evaluator/access_ops_spec.rb +++ b/spec/unit/pops/evaluator/access_ops_spec.rb @@ -120,7 +120,7 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do it 'cannot step Integer sequence with a step of 0' do expr = fqr('Integer')[6, 1, 0] - expect { evaluate(expr)}.to raise_error(/Integer\[\] cannot step with increment of 0/) + expect { evaluate(expr)}.to raise_error(/Integer \[\] can not step with a '0' increment/) end # Hash @@ -142,7 +142,7 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do it "gives an error if parameter is not a type" do expr = fqr('Hash')['String'] - expect { evaluate(expr)}.to raise_error(/Arguments to Hash\[\] must be types/) + expect { evaluate(expr)}.to raise_error(/Hash\[\] arguments must be types/) end # Array @@ -159,7 +159,7 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do it "gives an error if parameter is not a type" do expr = fqr('Array')['String'] - expect { evaluate(expr)}.to raise_error(/Argument to Array\[\] must be a type/) + expect { evaluate(expr)}.to raise_error(/Array\[\] arguments must be types/) end it 'creates a Regexp instance when applied to a Pattern' do @@ -222,7 +222,7 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do it 'gives an error if resource is already specialized' do expr = fqr('File')[fqn('x')][fqn('y')] - expect {evaluate(expr)}.to raise_error(/Cannot specialize an already specialized resource type/) + expect {evaluate(expr)}.to raise_error(/Cannot specialize an already specialized Resource type/) end end diff --git a/spec/unit/pops/evaluator/collections_ops_spec.rb b/spec/unit/pops/evaluator/collections_ops_spec.rb index da317c790..f8d6bfa2b 100644 --- a/spec/unit/pops/evaluator/collections_ops_spec.rb +++ b/spec/unit/pops/evaluator/collections_ops_spec.rb @@ -72,7 +72,7 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/Concat/Delete' do it 'raises and error when LHS of << is a hash' do expect { evaluate(literal({'a' => 1, 'b'=>2}) << literal(1)) - }.to raise_error(/Left operand in '<<' expression is not an Array/) + }.to raise_error(/Operator '<<' is not applicable to a Hash/) end end diff --git a/spec/unit/pops/evaluator/comparison_ops_spec.rb b/spec/unit/pops/evaluator/comparison_ops_spec.rb index ce6d79efd..4a757ecb5 100644 --- a/spec/unit/pops/evaluator/comparison_ops_spec.rb +++ b/spec/unit/pops/evaluator/comparison_ops_spec.rb @@ -166,7 +166,7 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do end it "$a = Pattern['.*']; 'a' =~ $a == true" do - expr = block(fqn('a').set(fqr('Pattern')['foo']), literal('foo') =~ var('a')) + expr = block(var('a').set(fqr('Pattern')['foo']), literal('foo') =~ var('a')) evaluate(expr).should == true end diff --git a/spec/unit/pops/evaluator/string_interpolation_spec.rb b/spec/unit/pops/evaluator/string_interpolation_spec.rb index 35d0e529e..49f674588 100644 --- a/spec/unit/pops/evaluator/string_interpolation_spec.rb +++ b/spec/unit/pops/evaluator/string_interpolation_spec.rb @@ -13,29 +13,29 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do context "When evaluator performs string interpolation" do it "should interpolate a bare word as a variable name, \"${var}\"" do - a_block = block(fqn('a').set(10), string('value is ', text(fqn('a')), ' yo')) + a_block = block(var('a').set(10), string('value is ', text(fqn('a')), ' yo')) evaluate(a_block).should == "value is 10 yo" end it "should interpolate a variable in a text expression, \"${$var}\"" do - a_block = block(fqn('a').set(10), string('value is ', text(var(fqn('a'))), ' yo')) + a_block = block(var('a').set(10), string('value is ', text(var(fqn('a'))), ' yo')) evaluate(a_block).should == "value is 10 yo" end it "should interpolate a variable, \"$var\"" do - a_block = block(fqn('a').set(10), string('value is ', var(fqn('a')), ' yo')) + a_block = block(var('a').set(10), string('value is ', var(fqn('a')), ' yo')) evaluate(a_block).should == "value is 10 yo" end it "should interpolate any expression in a text expression, \"${$var*2}\"" do - a_block = block(fqn('a').set(5), string('value is ', text(var(fqn('a')) * 2) , ' yo')) + a_block = block(var('a').set(5), string('value is ', text(var(fqn('a')) * 2) , ' yo')) evaluate(a_block).should == "value is 10 yo" end it "should interpolate any expression without a text expression, \"${$var*2}\"" do # there is no concrete syntax for this, but the parser can generate this simpler # equivalent form where the expression is not wrapped in a TextExpression - a_block = block(fqn('a').set(5), string('value is ', var(fqn('a')) * 2 , ' yo')) + a_block = block(var('a').set(5), string('value is ', var(fqn('a')) * 2 , ' yo')) evaluate(a_block).should == "value is 10 yo" end diff --git a/spec/unit/pops/evaluator/variables_spec.rb b/spec/unit/pops/evaluator/variables_spec.rb index 0896d7c72..b5833cb44 100644 --- a/spec/unit/pops/evaluator/variables_spec.rb +++ b/spec/unit/pops/evaluator/variables_spec.rb @@ -18,35 +18,35 @@ describe 'Puppet::Pops::Impl::EvaluatorImpl' do context "When the evaluator deals with variables" do context "it should handle" do it "simple assignment and dereference" do - evaluate_l(block( fqn('a').set(literal(2)+literal(2)), var('a'))).should == 4 + evaluate_l(block( var('a').set(literal(2)+literal(2)), var('a'))).should == 4 end it "local scope shadows top scope" do - top_scope_block = block( fqn('a').set(literal(2)+literal(2)), var('a')) - local_scope_block = block( fqn('a').set(var('a') + literal(2)), var('a')) + top_scope_block = block( var('a').set(literal(2)+literal(2)), var('a')) + local_scope_block = block( var('a').set(var('a') + literal(2)), var('a')) evaluate_l(top_scope_block, local_scope_block).should == 6 end it "shadowed in local does not affect parent scope" do - top_scope_block = block( fqn('a').set(literal(2)+literal(2)), var('a')) - local_scope_block = block( fqn('a').set(var('a') + literal(2)), var('a')) + top_scope_block = block( var('a').set(literal(2)+literal(2)), var('a')) + local_scope_block = block( var('a').set(var('a') + literal(2)), var('a')) top_scope_again = var('a') evaluate_l(top_scope_block, local_scope_block, top_scope_again).should == 4 end it "access to global names works in top scope" do - top_scope_block = block( fqn('a').set(literal(2)+literal(2)), var('::a')) + top_scope_block = block( var('a').set(literal(2)+literal(2)), var('::a')) evaluate_l(top_scope_block).should == 4 end it "access to global names works in local scope" do - top_scope_block = block( fqn('a').set(literal(2)+literal(2))) - local_scope_block = block( fqn('a').set(var('::a')+literal(2)), var('::a')) + top_scope_block = block( var('a').set(literal(2)+literal(2))) + local_scope_block = block( var('a').set(var('::a')+literal(2)), var('::a')) evaluate_l(top_scope_block, local_scope_block).should == 6 end it "can not change a variable value in same scope" do - expect { evaluate_l(block(fqn('a').set(10), fqn('a').set(20))) }.to raise_error(/Cannot reassign variable a/) + expect { evaluate_l(block(var('a').set(10), var('a').set(20))) }.to raise_error(/Cannot reassign variable a/) end context "-= operations" do @@ -54,20 +54,20 @@ describe 'Puppet::Pops::Impl::EvaluatorImpl' do # the -= operation itself is tested (there are many combinations) # it 'deleting from non existing value produces nil, nil -= ?' do - top_scope_block = fqn('b').set([1,2,3]) - local_scope_block = fqn('a').minus_set([4]) + top_scope_block = var('b').set([1,2,3]) + local_scope_block = var('a').minus_set([4]) evaluate_l(top_scope_block, local_scope_block).should == nil end it 'deletes from a list' do - top_scope_block = fqn('a').set([1,2,3]) - local_scope_block = block(fqn('a').minus_set([2]), fqn('a').var()) + top_scope_block = var('a').set([1,2,3]) + local_scope_block = block(var('a').minus_set([2]), fqn('a').var()) evaluate_l(top_scope_block, local_scope_block).should == [1,3] end it 'deletes from a hash' do - top_scope_block = fqn('a').set({'a'=>1,'b'=>2,'c'=>3}) - local_scope_block = block(fqn('a').minus_set('b'), fqn('a').var()) + top_scope_block = var('a').set({'a'=>1,'b'=>2,'c'=>3}) + local_scope_block = block(var('a').minus_set('b'), fqn('a').var()) evaluate_l(top_scope_block, local_scope_block).should == {'a'=>1,'c'=>3} end end @@ -75,21 +75,21 @@ describe 'Puppet::Pops::Impl::EvaluatorImpl' do context "+= operations" do # Also see collections_ops_spec.rb where concatenation via + is fully tested it "appending to non existing value, nil += []" do - top_scope_block = fqn('b').set([1,2,3]) - local_scope_block = fqn('a').plus_set([4]) + top_scope_block = var('b').set([1,2,3]) + local_scope_block = var('a').plus_set([4]) evaluate_l(top_scope_block, local_scope_block).should == [4] end context "appending to list" do it "from list, [] += []" do - top_scope_block = fqn('a').set([1,2,3]) - local_scope_block = block(fqn('a').plus_set([4]), fqn('a').var()) + top_scope_block = var('a').set([1,2,3]) + local_scope_block = block(var('a').plus_set([4]), fqn('a').var()) evaluate_l(top_scope_block, local_scope_block).should == [1,2,3,4] end it "from hash, [] += {a=>b}" do - top_scope_block = fqn('a').set([1,2,3]) - local_scope_block = block(fqn('a').plus_set({'a' => 1, 'b'=>2}), fqn('a').var()) + top_scope_block = var('a').set([1,2,3]) + local_scope_block = block(var('a').plus_set({'a' => 1, 'b'=>2}), fqn('a').var()) evaluate_l(top_scope_block, local_scope_block).should satisfy {|result| # hash in 1.8.7 is not insertion order preserving, hence this hoop result == [1,2,3,['a',1],['b',2]] || result == [1,2,3,['b',2],['a',1]] @@ -97,22 +97,22 @@ describe 'Puppet::Pops::Impl::EvaluatorImpl' do end it "from single value, [] += x" do - top_scope_block = fqn('a').set([1,2,3]) - local_scope_block = block(fqn('a').plus_set(4), fqn('a').var()) + top_scope_block = var('a').set([1,2,3]) + local_scope_block = block(var('a').plus_set(4), fqn('a').var()) evaluate_l(top_scope_block, local_scope_block).should == [1,2,3,4] end it "from embedded list, [] += [[x]]" do - top_scope_block = fqn('a').set([1,2,3]) - local_scope_block = block(fqn('a').plus_set([[4,5]]), fqn('a').var()) + top_scope_block = var('a').set([1,2,3]) + local_scope_block = block(var('a').plus_set([[4,5]]), fqn('a').var()) evaluate_l(top_scope_block, local_scope_block).should == [1,2,3,[4,5]] end end context "appending to hash" do it "from hash, {a=>b} += {x=>y}" do - top_scope_block = fqn('a').set({'a' => 1, 'b' => 2}) - local_scope_block = block(fqn('a').plus_set({'c' => 3}), fqn('a').var()) + top_scope_block = var('a').set({'a' => 1, 'b' => 2}) + local_scope_block = block(var('a').plus_set({'c' => 3}), fqn('a').var()) evaluate_l(top_scope_block, local_scope_block) do |scope| # Assert no change to top scope hash scope['a'].should == {'a' =>1, 'b'=> 2} @@ -120,8 +120,8 @@ describe 'Puppet::Pops::Impl::EvaluatorImpl' do end it "from list, {a=>b} += ['x', y]" do - top_scope_block = fqn('a').set({'a' => 1, 'b' => 2}) - local_scope_block = block(fqn('a').plus_set(['c', 3]), fqn('a').var()) + top_scope_block = var('a').set({'a' => 1, 'b' => 2}) + local_scope_block = block(var('a').plus_set(['c', 3]), fqn('a').var()) evaluate_l(top_scope_block, local_scope_block) do |scope| # Assert no change to top scope hash scope['a'].should == {'a' =>1, 'b'=> 2} @@ -129,8 +129,8 @@ describe 'Puppet::Pops::Impl::EvaluatorImpl' do end it "with overwrite from hash, {a=>b} += {a=>c}" do - top_scope_block = fqn('a').set({'a' => 1, 'b' => 2}) - local_scope_block = block(fqn('a').plus_set({'b' => 4, 'c' => 3}),fqn('a').var()) + top_scope_block = var('a').set({'a' => 1, 'b' => 2}) + local_scope_block = block(var('a').plus_set({'b' => 4, 'c' => 3}),fqn('a').var()) evaluate_l(top_scope_block, local_scope_block) do |scope| # Assert no change to top scope hash scope['a'].should == {'a' =>1, 'b'=> 2} @@ -138,8 +138,8 @@ describe 'Puppet::Pops::Impl::EvaluatorImpl' do end it "with overwrite from list, {a=>b} += ['a', c]" do - top_scope_block = fqn('a').set({'a' => 1, 'b' => 2}) - local_scope_block = block(fqn('a').plus_set(['b', 4, 'c', 3]), fqn('a').var()) + top_scope_block = var('a').set({'a' => 1, 'b' => 2}) + local_scope_block = block(var('a').plus_set(['b', 4, 'c', 3]), fqn('a').var()) evaluate_l(top_scope_block, local_scope_block) do |scope| # Assert no change to topscope hash scope['a'].should == {'a' =>1, 'b'=> 2} @@ -147,8 +147,8 @@ describe 'Puppet::Pops::Impl::EvaluatorImpl' do end it "from odd length array - error" do - top_scope_block = fqn('a').set({'a' => 1, 'b' => 2}) - local_scope_block = fqn('a').plus_set(['b', 4, 'c']) + top_scope_block = var('a').set({'a' => 1, 'b' => 2}) + local_scope_block = var('a').plus_set(['b', 4, 'c']) expect { evaluate_l(top_scope_block, local_scope_block) }.to raise_error(/Append assignment \+= failed with error: odd number of arguments for Hash/) end end diff --git a/spec/unit/pops/issues_spec.rb b/spec/unit/pops/issues_spec.rb index d8650b956..93f093d61 100644 --- a/spec/unit/pops/issues_spec.rb +++ b/spec/unit/pops/issues_spec.rb @@ -16,7 +16,7 @@ describe "Puppet::Pops::Issues" do x.format(:name => 'Boo-Hoo', :label => Puppet::Pops::Model::ModelLabelProvider.new, :semantic => "dummy" - ).should == "A Ruby String may not have a name containing a hyphen. The name 'Boo-Hoo' is not legal" + ).should == "A String may not have a name containing a hyphen. The name 'Boo-Hoo' is not legal" end it "should should format a message that does not require an argument" do From 6f45d1c2823159cf26d5e4902517ed756affe541 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 28 Oct 2013 03:57:37 +0100 Subject: [PATCH 056/800] (perf) Move all behcmarks to one unit test and exclude it with filter All parser benchmarks are now located in one spec unit test. Thy are automatically excluded by a filter that can be turned off by setting the ENV variable BENCHMARK to true when running the tests. --- spec/spec_helper.rb | 4 +- spec/unit/pops/benchmark_spec.rb | 62 +++++++++++++++++++ .../pops/parser/evaluating_parser_spec.rb | 19 ------ spec/unit/pops/parser/lexer2_spec.rb | 25 -------- spec/unit/pops/parser/lexer_spec.rb | 9 --- spec/unit/pops/parser/parser_spec.rb | 17 ----- 6 files changed, 65 insertions(+), 71 deletions(-) create mode 100644 spec/unit/pops/benchmark_spec.rb diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index e72fecee4..188ec9037 100755 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -67,7 +67,9 @@ RSpec.configure do |config| # IPAddr.new("::2:3:4:5:6:7:8") # end # end - config.filter_run_excluding :broken => true + exclude_filters = {:broken => true} + exclude_filters[:benchmark] = true unless ENV['BENCHMARK'] + config.filter_run_excluding exclude_filters config.mock_with :mocha diff --git a/spec/unit/pops/benchmark_spec.rb b/spec/unit/pops/benchmark_spec.rb new file mode 100644 index 000000000..7758395b6 --- /dev/null +++ b/spec/unit/pops/benchmark_spec.rb @@ -0,0 +1,62 @@ +#! /usr/bin/env ruby +require 'spec_helper' +require 'puppet/pops' + +describe "Benchmark", :benchmark => true do + + def code + 'if true +{ +$a = 10 + 10 +} +else +{ +$a = "interpolate ${foo} and stuff" +} +' end + + it "transformer", :profile => true do + parser = Puppet::Pops::Parser::Parser.new() + model = parser.parse_string(code).current + transformer = Puppet::Pops::Model::AstTransformer.new() + m = Benchmark.measure { 10000.times { transformer.transform(model) }} + puts "Transformer: #{m}" + end + + it "parse transform", :profile => true do + parser = Puppet::Pops::Parser::Parser.new() + transformer = Puppet::Pops::Model::AstTransformer.new() + m = Benchmark.measure { 10000.times { transformer.transform(parser.parse_string(code).current) }} + puts "Parse and transform: #{m}" + end + + it "parser0", :profile => true do + parser = Puppet::Parser::Parser.new('test') + m = Benchmark.measure { 10000.times { parser.parse(code) }} + puts "Parser 0: #{m}" + end + + it "parser1", :profile => true do + parser = Puppet::Pops::Parser::EvaluatingParser.new() + m = Benchmark.measure { 10000.times { parser.parse_string(code) }} + puts "Parser1: #{m}" + end + + it "lexer2", :profile => true do + lexer = Puppet::Pops::Parser::Lexer2.new + m = Benchmark.measure {10000.times {lexer.string = code; lexer.fullscan }} + puts "Lexer2: #{m}" + end + + it "lexer1", :profile => true do + lexer = Puppet::Pops::Parser::Lexer.new + m = Benchmark.measure {10000.times {lexer.string = code; lexer.fullscan }} + puts "Pops Lexer: #{m}" + end + + it "lexer0", :profile => true do + lexer = Puppet::Parser::Lexer.new + m = Benchmark.measure {10000.times {lexer.string = code; lexer.fullscan }} + puts "Original Lexer: #{m}" + end +end diff --git a/spec/unit/pops/parser/evaluating_parser_spec.rb b/spec/unit/pops/parser/evaluating_parser_spec.rb index b2db895ad..8448a5af3 100644 --- a/spec/unit/pops/parser/evaluating_parser_spec.rb +++ b/spec/unit/pops/parser/evaluating_parser_spec.rb @@ -87,23 +87,4 @@ describe 'The Evaluating Parser' do test_interpolate("\\s", ' ') end end - - describe "when benchmarked" do - - it "Pops Parser", :profile => true do - code = 'if true -{ -$a = 10 + 10 -} -else -{ -$a = "interpolate ${foo} and stuff" -} -' - parser = Puppet::Pops::Parser::EvaluatingParser.new() - m = Benchmark.measure { 10000.times { parser.parse_string(code) }} - puts "Parser: #{m}" - end - end - end diff --git a/spec/unit/pops/parser/lexer2_spec.rb b/spec/unit/pops/parser/lexer2_spec.rb index 51f78de30..fdaf5e66e 100644 --- a/spec/unit/pops/parser/lexer2_spec.rb +++ b/spec/unit/pops/parser/lexer2_spec.rb @@ -362,28 +362,3 @@ describe 'Lexer2' do end end -describe "when benchmarked" do - -# it "Lexer 2", :profile => true do -# lexer = Lexer2.new -# code = 'if true \n{\n 10 + 10\n }\n else\n {\n "interpolate ${foo} and stuff"\n }\n' -# m = Benchmark.measure {10000.times {lexer.string = code; lexer.fullscan }} -# puts "Lexer2: #{m}" -# end - -# it "Pops Optimized", :profile => true do -# lexer = Puppet::Pops::Parser::Lexer.new -# # code = 'if true { 10 + 10 } else { "interpolate ${foo} andn stuff" }' -# code = 'if true \n{\n 10 + 10\n }\n else\n {\n "interpolate ${foo} and stuff"\n }\n' -# m = Benchmark.measure {10000.times {lexer.string = code; lexer.fullscan }} -# puts "Pops O: #{m}" -# end -# -# it "Original", :profile => true do -# lexer = Puppet::Parser::Lexer.new -# # code = 'if true { 10 + 10 } else { "interpolate ${foo} andn stuff" }' -# code = 'if true \n{\n 10 + 10\n }\n else\n {\n "interpolate ${foo} and stuff"\n }\n' -# m = Benchmark.measure {10000.times {lexer.string = code; lexer.fullscan }} -# puts "Original: #{m}" -# end -end \ No newline at end of file diff --git a/spec/unit/pops/parser/lexer_spec.rb b/spec/unit/pops/parser/lexer_spec.rb index fc616fdc6..19f52ce8c 100755 --- a/spec/unit/pops/parser/lexer_spec.rb +++ b/spec/unit/pops/parser/lexer_spec.rb @@ -838,12 +838,3 @@ describe "when lexing interpolation detailed positioning should be correct" do ) end end - -describe "when benchmarked" do -# it "should not take longer than x sec", :profile => true do -# lexer = Puppet::Pops::Parser::Lexer.new -## code = 'if true { 10 + 10 } else { "interpolate ${foo} andn stuff" }' -# code = 'if true \n{\n 10 + 10\n }\n else\n {\n "interpolate ${foo} andn stuff"\n }\n' -# puts Benchmark.measure {10000.times {lexer.string = code; lexer.fullscan }} -# end -end diff --git a/spec/unit/pops/parser/parser_spec.rb b/spec/unit/pops/parser/parser_spec.rb index 98e39e0b7..e7a9c7cbe 100644 --- a/spec/unit/pops/parser/parser_spec.rb +++ b/spec/unit/pops/parser/parser_spec.rb @@ -13,21 +13,4 @@ describe Puppet::Pops::Parser::Parser do model.class.should == Puppet::Pops::Model::AssignmentExpression end -# describe "when benchmarked" do -# -# it "Pops Parser", :profile => true do -# code = 'if true -#{ -#10 + 10 -#} -#else -#{ -#"interpolate ${foo} and stuff" -#} -#' -# parser = Puppet::Pops::Parser::Parser.new() -# m = Benchmark.measure { 10000.times { parser.parse_string(code) }} -# puts "Parser: #{m}" -# end -# end end From f61569a9c60162acc240105a529df1cf0cba344b Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 28 Oct 2013 04:57:19 +0100 Subject: [PATCH 057/800] (perf) Make small performance improvement to validation This adds a benchmark for the validator and makes a small improvement on its performance --- lib/puppet/pops/validation.rb | 9 +++++---- spec/unit/pops/benchmark_spec.rb | 7 +++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/puppet/pops/validation.rb b/lib/puppet/pops/validation.rb index ff9b3e666..1fce3a228 100644 --- a/lib/puppet/pops/validation.rb +++ b/lib/puppet/pops/validation.rb @@ -90,6 +90,7 @@ module Puppet::Pops::Validation # @api public # class SeverityProducer + @@severity_hash = {:ignore => true, :warning => true, :error => true, :deprecation => true } # Creates a new instance where all issues are diagnosed as :error unless overridden. # @api public @@ -122,8 +123,8 @@ module Puppet::Pops::Validation # @api public # def []=(issue, level) - assert_issue(issue) - assert_severity(level) + raise Puppet::DevError.new("Attempt to set validation severity for something that is not an Issue. (Got #{issue.class})") unless issue.is_a? Puppet::Pops::Issues::Issue + raise Puppet::DevError.new("Illegal severity level: #{option}") unless @@severity_hash[level] raise Puppet::DevError.new("Attempt to demote the hard issue '#{issue.issue_code}' to #{level}") unless issue.demotable? || level == :error @severities[issue] = level end @@ -134,7 +135,7 @@ module Puppet::Pops::Validation # @api public # def should_report? issue - diagnose = self[issue] + diagnose = @severities[issue] diagnose == :error || diagnose == :warning || diagnose == :deprecation end @@ -149,7 +150,7 @@ module Puppet::Pops::Validation # @api private # def assert_severity level - raise Puppet::DevError.new("Illegal severity level: #{option}") unless [:ignore, :warning, :error, :deprecation].include? level + raise Puppet::DevError.new("Illegal severity level: #{option}") unless @@severity_hash[level] end end diff --git a/spec/unit/pops/benchmark_spec.rb b/spec/unit/pops/benchmark_spec.rb index 7758395b6..c73b481c1 100644 --- a/spec/unit/pops/benchmark_spec.rb +++ b/spec/unit/pops/benchmark_spec.rb @@ -23,6 +23,13 @@ $a = "interpolate ${foo} and stuff" puts "Transformer: #{m}" end + it "validator", :profile => true do + parser = Puppet::Pops::Parser::EvaluatingParser.new() + model = parser.parse_string(code) + m = Benchmark.measure { 100000.times { parser.assert_and_report(model) }} + puts "Validator: #{m}" + end + it "parse transform", :profile => true do parser = Puppet::Pops::Parser::Parser.new() transformer = Puppet::Pops::Model::AstTransformer.new() From 40ded61bed62a55a0bde2c0e509751a10a8c82c0 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 28 Oct 2013 05:15:42 +0100 Subject: [PATCH 058/800] (perf) Make small performance improvement to assignment validation It is faster to pass an explicit number of arguments --- lib/puppet/pops/validation/checker3_1.rb | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/puppet/pops/validation/checker3_1.rb b/lib/puppet/pops/validation/checker3_1.rb index be277aba6..725a9aeae 100644 --- a/lib/puppet/pops/validation/checker3_1.rb +++ b/lib/puppet/pops/validation/checker3_1.rb @@ -41,45 +41,45 @@ class Puppet::Pops::Validation::Checker3_1 # Performs regular validity check def check(o) - @@check_visitor.visit_this(self, o) + @@check_visitor.visit_this_0(self, o) end # Performs check if this is a vaid hostname expression # @param single_feature_name [String, nil] the name of a single valued hostname feature of the value's container. e.g. 'parent' def hostname(o, semantic, single_feature_name = nil) - @@hostname_visitor.visit_this(self, o, semantic, single_feature_name) + @@hostname_visitor.visit_this_2(self, o, semantic, single_feature_name) end # Performs check if this is valid as a query def query(o) - @@query_visitor.visit_this(self, o) + @@query_visitor.visit_this_0(self, o) end # Performs check if this is valid as a relationship side def relation(o, container) - @@relation_visitor.visit_this(self, o, container) + @@relation_visitor.visit_this_1(self, o, container) end # Performs check if this is valid as a rvalue def rvalue(o) - @@rvalue_visitor.visit_this(self, o) + @@rvalue_visitor.visit_this_0(self, o) end # Performs check if this is valid as a container of a definition (class, define, node) def top(o, definition) - @@top_visitor.visit_this(self, o, definition) + @@top_visitor.visit_this_1(self, o, definition) end # Checks the LHS of an assignment (is it assignable?). # If args[0] is true, assignment via index is checked. # - def assign(o, *args) - @@assignment_visitor.visit_this(self, o, *args) + def assign(o, via_index = false) + @@assignment_visitor.visit_this_1(self, o, via_index) end #---ASSIGNMENT CHECKS - def assign_VariableExpression(o, *args) + def assign_VariableExpression(o, via_index) varname_string = varname_to_s(o.expr) if varname_string =~ /^[0-9]+$/ acceptor.accept(Issues::ILLEGAL_NUMERIC_ASSIGNMENT, o, :varname => varname_string) @@ -95,7 +95,7 @@ class Puppet::Pops::Validation::Checker3_1 # TODO: Investigate if there are invalid cases for += assignment end - def assign_AccessExpression(o, *args) + def assign_AccessExpression(o, via_index) # Are indexed assignments allowed at all ? $x[x] = '...' if acceptor.will_accept? Issues::ILLEGAL_INDEXED_ASSIGNMENT acceptor.accept(Issues::ILLEGAL_INDEXED_ASSIGNMENT, o) @@ -105,7 +105,7 @@ class Puppet::Pops::Validation::Checker3_1 end end - def assign_Object(o, *args) + def assign_Object(o, via_index) # Can not assign to anything else (differentiate if this is via index or not) # i.e. 10 = 'hello' vs. 10['x'] = 'hello' (the root is reported as being in error in both cases) # From 2dba6bfea1052bac94568bed08c16ecbe9f3529f Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 29 Oct 2013 04:49:05 +0100 Subject: [PATCH 059/800] (maint) Fix issue after optimizing validation calls; renamed var Instead of passing *args for a single argumet it was made explicit. When refactoring, the rename was missed in one place. This corrects the mistake. --- lib/puppet/pops/validation/checker3_1.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/pops/validation/checker3_1.rb b/lib/puppet/pops/validation/checker3_1.rb index 725a9aeae..da71c47ba 100644 --- a/lib/puppet/pops/validation/checker3_1.rb +++ b/lib/puppet/pops/validation/checker3_1.rb @@ -109,7 +109,7 @@ class Puppet::Pops::Validation::Checker3_1 # Can not assign to anything else (differentiate if this is via index or not) # i.e. 10 = 'hello' vs. 10['x'] = 'hello' (the root is reported as being in error in both cases) # - acceptor.accept(args[0] ? Issues::ILLEGAL_ASSIGNMENT_VIA_INDEX : Issues::ILLEGAL_ASSIGNMENT, o) + acceptor.accept(via_index ? Issues::ILLEGAL_ASSIGNMENT_VIA_INDEX : Issues::ILLEGAL_ASSIGNMENT, o) end #---CHECKS From 563554a32b4ec60003a96bcc42d448c3d2a4acde Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 29 Oct 2013 04:51:23 +0100 Subject: [PATCH 060/800] (maint) Remove unused model class LiteralText This class was never used since LiteralString works just fine eveywhere. The distinction that LiteralText used different escapes is a moot point, as after parsing has taken place it does not really matter. --- lib/puppet/pops/model/ast_transformer.rb | 5 ----- lib/puppet/pops/model/model.rb | 7 ++----- lib/puppet/pops/model/model_label_provider.rb | 1 - lib/puppet/pops/model/model_tree_dumper.rb | 4 ---- 4 files changed, 2 insertions(+), 15 deletions(-) diff --git a/lib/puppet/pops/model/ast_transformer.rb b/lib/puppet/pops/model/ast_transformer.rb index 1a0a14ca3..c7320a736 100644 --- a/lib/puppet/pops/model/ast_transformer.rb +++ b/lib/puppet/pops/model/ast_transformer.rb @@ -358,11 +358,6 @@ class Puppet::Pops::Model::AstTransformer ast o, AST::String, :value => o.value end - # Literal text in a concatenated string - def transform_LiteralText(o) - ast o, AST::String, :value => o.value - end - def transform_LambdaExpression(o) astargs = { :parameters => o.parameters.collect {|p| transform(p) } } astargs.merge!({ :children => transform(o.body) }) if o.body # do not want children if it is nil/nop diff --git a/lib/puppet/pops/model/model.rb b/lib/puppet/pops/model/model.rb index 3f4d9f98a..0d66afdd8 100644 --- a/lib/puppet/pops/model/model.rb +++ b/lib/puppet/pops/model/model.rb @@ -23,6 +23,8 @@ require 'rgen/metamodel_builder' module Puppet::Pops::Model + extend RGen::MetamodelBuilder::ModuleExtension + # A base class for modeled objects that makes them Visitable, and Adaptable. # @todo currently includes Containment which will not be needed when the corresponding methods # are added to RGen (in some version after 0.6.2). @@ -340,11 +342,6 @@ module Puppet::Pops::Model # class LiteralString < LiteralValue; end - # A literal text is like a literal string, but has other rules for escaped characters. It - # is used as part of a ConcatenatedString - # - class LiteralText < LiteralValue; end - # A literal number has a radix of decimal (10), octal (8), or hex (16) to enable string conversion with the input radix. # By default, a radix of 10 is used. # diff --git a/lib/puppet/pops/model/model_label_provider.rb b/lib/puppet/pops/model/model_label_provider.rb index 2a91fae4e..2fb732dc8 100644 --- a/lib/puppet/pops/model/model_label_provider.rb +++ b/lib/puppet/pops/model/model_label_provider.rb @@ -35,7 +35,6 @@ class Puppet::Pops::Model::ModelLabelProvider < Puppet::Pops::LabelProvider def label_KeyedEntry o ; "Hash Entry" end def label_LiteralBoolean o ; "Boolean" end def label_LiteralString o ; "String" end - def label_LiteralText o ; "Text in Interpolated String" end def label_LambdaExpression o ; "Lambda" end def label_LiteralDefault o ; "'default' expression" end def label_LiteralUndef o ; "'undef' expression" end diff --git a/lib/puppet/pops/model/model_tree_dumper.rb b/lib/puppet/pops/model/model_tree_dumper.rb index 24d341cda..3933b8b29 100644 --- a/lib/puppet/pops/model/model_tree_dumper.rb +++ b/lib/puppet/pops/model/model_tree_dumper.rb @@ -123,10 +123,6 @@ class Puppet::Pops::Model::ModelTreeDumper < Puppet::Pops::Model::TreeDumper "'#{o.value}'" end - def dump_LiteralText o - o.value - end - def dump_LambdaExpression o result = ["lambda"] result << ["parameters"] + o.parameters.collect {|p| do_dump(p) } if o.parameters.size() > 0 From aebf6b2ecbc18cc25a413e3fd57c2d11c35c7219 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 29 Oct 2013 16:05:57 +0100 Subject: [PATCH 061/800] (#22363) Refactor model of literals There were several problems with the model of literals: * LiteralNumber had radix which is not applicable for floats * All LiteralValue used an inherited Object attribute which makes it difficult/impossible to correctly serialize literal values. The refactoring moves the value attribute to each subclass, adds LiteralInteger (with radix), and LiteralFloat as subclasses of LiteralNumber. LiteralRegexp required special handling since a regular expression must be serialized as a String. Now LiteralRegularExpression has two attributes 'pattern' (string), and 'value' (regexp). Setting either changes the other. When serialized, only the pattern is serialized (and the regexp value is reconstructed on de-serialization). --- .../pops/binder/hiera2/bindings_provider.rb | 8 ++- lib/puppet/pops/model/ast_transformer.rb | 7 ++- lib/puppet/pops/model/ast_tree_dumper.rb | 6 ++- lib/puppet/pops/model/factory.rb | 45 +++++++++++----- lib/puppet/pops/model/model.rb | 54 ++++++++++++++++--- lib/puppet/pops/model/model_label_provider.rb | 3 +- lib/puppet/pops/model/model_tree_dumper.rb | 6 ++- lib/puppet/pops/utils.rb | 4 +- spec/unit/pops/benchmark_spec.rb | 47 ++++++++++++++++ spec/unit/pops/factory_spec.rb | 12 ++--- spec/unit/pops/model/ast_transformer_spec.rb | 2 +- spec/unit/pops/model/model_spec.rb | 4 +- 12 files changed, 160 insertions(+), 38 deletions(-) diff --git a/lib/puppet/pops/binder/hiera2/bindings_provider.rb b/lib/puppet/pops/binder/hiera2/bindings_provider.rb index d0e5d9a5e..06285bb1c 100644 --- a/lib/puppet/pops/binder/hiera2/bindings_provider.rb +++ b/lib/puppet/pops/binder/hiera2/bindings_provider.rb @@ -108,8 +108,12 @@ module Puppet::Pops::Binder::Hiera2 end when Enumerable value.inject(Model::LiteralList.new) {|a,v| a.addValues(build_expr(v, hiera_data_file_path)); a } - when Numeric - expr = Model::LiteralNumber.new + when Integer + expr = Model::LiteralInteger.new + expr.value = value; + expr + when Float + expr = Model::LiteralFloat.new expr.value = value; expr when TrueClass, FalseClass diff --git a/lib/puppet/pops/model/ast_transformer.rb b/lib/puppet/pops/model/ast_transformer.rb index c7320a736..dee4f95bd 100644 --- a/lib/puppet/pops/model/ast_transformer.rb +++ b/lib/puppet/pops/model/ast_transformer.rb @@ -68,7 +68,12 @@ class Puppet::Pops::Model::AstTransformer @@hostname_transform_visitor.visit_this(self, o) end - def transform_LiteralNumber(o) + def transform_LiteralFloat(o) + # Numbers are Names in the AST !! (Name a.k.a BareWord) + ast o, AST::Name, :value => o.value.to_s + end + + def transform_LiteralInteger(o) s = case o.radix when 10 o.value.to_s diff --git a/lib/puppet/pops/model/ast_tree_dumper.rb b/lib/puppet/pops/model/ast_tree_dumper.rb index 624b12267..b5c7ce692 100644 --- a/lib/puppet/pops/model/ast_tree_dumper.rb +++ b/lib/puppet/pops/model/ast_tree_dumper.rb @@ -8,7 +8,11 @@ class Puppet::Pops::Model::AstTreeDumper < Puppet::Pops::Model::TreeDumper AST = Puppet::Parser::AST Model = Puppet::Pops::Model - def dump_LiteralNumber o + def dump_LiteralFloat o + o.value.to_s + end + + def dump_LiteralInteger o case o.radix when 10 o.value.to_s diff --git a/lib/puppet/pops/model/factory.rb b/lib/puppet/pops/model/factory.rb index 86d02ad08..5ff92184d 100644 --- a/lib/puppet/pops/model/factory.rb +++ b/lib/puppet/pops/model/factory.rb @@ -169,7 +169,12 @@ class Puppet::Pops::Model::Factory o end - def build_LiteralNumber(o, val, radix) + def build_LiteralFloat(o, val) + o.value = val + o + end + + def build_LiteralInteger(o, val, radix) o.value = val o.radix = radix o @@ -570,20 +575,30 @@ class Puppet::Pops::Model::Factory new(Model::QualifiedName, name) end - def self.NUMBER(name) - if n_radix = Puppet::Pops::Utils.to_n_with_radix(name) - new(Model::LiteralNumber, *n_radix) + def self.NUMBER(name_or_numeric) + if n_radix = Puppet::Pops::Utils.to_n_with_radix(name_or_numeric) + val, radix = n_radix + if val.is_a?(Float) + new(Model::LiteralFloat, val) + else + new(Model::LiteralInteger, val, radix) + end else # Bad number should already have been caught by lexer - this should never happen - raise ArgumentError, "Internal Error, NUMBER token does not contain a valid number, #{name}" + raise ArgumentError, "Internal Error, NUMBER token does not contain a valid number, #{name_or_numeric}" end end - # Convert input string to either a qualified name, or a LiteralNumber with radix + # Convert input string to either a qualified name, a LiteralInteger with radix, or a LiteralFloat # def self.QNAME_OR_NUMBER(name) if n_radix = Puppet::Pops::Utils.to_n_with_radix(name) - new(Model::LiteralNumber, *n_radix) + val, radix = n_radix + if val.is_a?(Float) + new(Model::LiteralFloat, val) + else + new(Model::LiteralInteger, val, radix) + end else new(Model::QualifiedName, name) end @@ -736,13 +751,13 @@ class Puppet::Pops::Model::Factory end def build_Fixnum(o) - x = Model::LiteralNumber.new + x = Model::LiteralInteger.new x.value = o; x end def build_Float(o) - x = Model::LiteralNumber.new + x = Model::LiteralFloat.new x.value = o; x end @@ -834,10 +849,9 @@ class Puppet::Pops::Model::Factory interpolate(o.current) end - def interpolate_LiteralNumber(o) - # TODO - # convert number to name using correct radix - # convert name to variable + def interpolate_LiteralInteger(o) + # convert number to a variable + self.class.new(o).var end def interpolate_Object(o) @@ -876,9 +890,12 @@ class Puppet::Pops::Model::Factory def is_interop_rewriteable?(o) case o - when Model::LiteralNumber, Model::AccessExpression, Model::QualifiedName, + when Model::AccessExpression, Model::QualifiedName, Model::NamedAccessExpression, Model::CallMethodExpression true + when Model::LiteralInteger + # Only decimal integers can represent variables, else it is a number + o.radix == 10 else false end diff --git a/lib/puppet/pops/model/model.rb b/lib/puppet/pops/model/model.rb index 0d66afdd8..bcc743e2e 100644 --- a/lib/puppet/pops/model/model.rb +++ b/lib/puppet/pops/model/model.rb @@ -331,22 +331,56 @@ module Puppet::Pops::Model # class LiteralValue < Literal abstract - has_attr 'value', Object, :lowerBound => 1 end # A Regular Expression Literal. # - class LiteralRegularExpression < LiteralValue; end + class LiteralRegularExpression < LiteralValue + has_attr 'value', Object, :lowerBound => 1, :transient => true + has_attr 'pattern', String, :lowerBound => 1 + + module ClassModule + # Go through the gymnastics of making either value or pattern settable + # with syncronization to the other form. A derived value cannot be serialized + # and we want to serialize the pattern. When recreating the object we need to + # recreate it from the pattern string. + # The below sets both values if one is changed. + # +# alias _value= value= +# alias _pattern= pattern= + def value= regexp + setValue regexp + setPattern regexp.to_s + end + + def pattern= regexp_string + setPattern regexp_string + setValue Regexp.new(regexp_string) + end + end + + end # A Literal String # - class LiteralString < LiteralValue; end + class LiteralString < LiteralValue + has_attr 'value', String, :lowerBound => 1 + end + + class LiteralNumber < LiteralValue + abstract + end # A literal number has a radix of decimal (10), octal (8), or hex (16) to enable string conversion with the input radix. # By default, a radix of 10 is used. # - class LiteralNumber < LiteralValue + class LiteralInteger < LiteralNumber has_attr 'radix', Integer, :lowerBound => 1, :defaultValueLiteral => "10" + has_attr 'value', Integer, :lowerBound => 1 + end + + class LiteralFloat < LiteralNumber + has_attr 'value', Float, :lowerBound => 1 end # The DSL `undef`. @@ -357,7 +391,9 @@ module Puppet::Pops::Model class LiteralDefault < Literal; end # DSL `true` or `false` - class LiteralBoolean < LiteralValue; end + class LiteralBoolean < LiteralValue + has_attr 'value', Boolean, :lowerBound => 1 + end # A text expression is an interpolation of an expression. If the embedded expression is # a QualifiedName, it it taken as a variable name and resolved. All other expressions are evaluated. @@ -376,11 +412,15 @@ module Puppet::Pops::Model # A DSL NAME (one or multiple parts separated by '::'). # - class QualifiedName < LiteralValue; end + class QualifiedName < LiteralValue + has_attr 'value', String, :lowerBound => 1 + end # A DSL CLASSREF (one or multiple parts separated by '::' where (at least) the first part starts with an upper case letter). # - class QualifiedReference < LiteralValue; end + class QualifiedReference < LiteralValue + has_attr 'value', String, :lowerBound => 1 + end # A Variable expression looks up value of expr (some kind of name) in scope. # The expression is typically a QualifiedName, or QualifiedReference. diff --git a/lib/puppet/pops/model/model_label_provider.rb b/lib/puppet/pops/model/model_label_provider.rb index 2fb732dc8..a55e69aaa 100644 --- a/lib/puppet/pops/model/model_label_provider.rb +++ b/lib/puppet/pops/model/model_label_provider.rb @@ -14,7 +14,8 @@ class Puppet::Pops::Model::ModelLabelProvider < Puppet::Pops::LabelProvider def label_Factory o ; label(o.current) end def label_Array o ; "Array Object" end - def label_LiteralNumber o ; "Literal Number" end + def label_LiteralInteger o ; "Literal Integer" end + def label_LiteralFloat o ; "Literal Float" end def label_ArithmeticExpression o ; "'#{o.operator}' expression" end def label_AccessExpression o ; "'[]' expression" end def label_MatchExpression o ; "'#{o.operator}' expression" end diff --git a/lib/puppet/pops/model/model_tree_dumper.rb b/lib/puppet/pops/model/model_tree_dumper.rb index 3933b8b29..b9d923401 100644 --- a/lib/puppet/pops/model/model_tree_dumper.rb +++ b/lib/puppet/pops/model/model_tree_dumper.rb @@ -7,7 +7,11 @@ class Puppet::Pops::Model::ModelTreeDumper < Puppet::Pops::Model::TreeDumper o.collect {|e| do_dump(e) } end - def dump_LiteralNumber o + def dump_LiteralFloat o + o.value.to_s + end + + def dump_LiteralInteger o case o.radix when 10 o.value.to_s diff --git a/lib/puppet/pops/utils.rb b/lib/puppet/pops/utils.rb index 1fbb2c8b1..b14b5554f 100644 --- a/lib/puppet/pops/utils.rb +++ b/lib/puppet/pops/utils.rb @@ -14,8 +14,8 @@ module Puppet::Pops::Utils end end - # To LiteralNumber with radix, or nil if not a number. - # If the value is already a number it is returned verbatim with a radix of 10. + # To Numeric with radix, or nil if not a number. + # If the value is already Numeric it is returned verbatim with a radix of 10. # @param o [String, Number] a string containing a number in octal, hex, integer (decimal) or floating point form # @return [Array, nil] array with converted number and radix, or nil if not possible to convert # @api public diff --git a/spec/unit/pops/benchmark_spec.rb b/spec/unit/pops/benchmark_spec.rb index c73b481c1..96a21484f 100644 --- a/spec/unit/pops/benchmark_spec.rb +++ b/spec/unit/pops/benchmark_spec.rb @@ -1,6 +1,10 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/pops' +require 'rgen/environment' +require 'rgen/metamodel_builder' +require 'rgen/serializer/json_serializer' +require 'rgen/instantiator/json_instantiator' describe "Benchmark", :benchmark => true do @@ -15,6 +19,32 @@ $a = "interpolate ${foo} and stuff" } ' end + class StringWriter < String + alias write concat + end + + class MyJSonSerializer < RGen::Serializer::JsonSerializer + def attributeValue(value, a) + x = super + require 'debugger'; debugger + puts "#{a.eType} value: <<#{value}>> serialize: <<#{x}>>" + x + end + end + + def json_dump(model) + output = StringWriter.new + ser = MyJSonSerializer.new(output) + ser.serialize(model) + output + end + + def json_load(string) + env = RGen::Environment.new + inst = RGen::Instantiator::JsonInstantiator.new(env, Puppet::Pops::Model) + inst.instantiate(string) + end + it "transformer", :profile => true do parser = Puppet::Pops::Parser::Parser.new() model = parser.parse_string(code).current @@ -49,6 +79,23 @@ $a = "interpolate ${foo} and stuff" puts "Parser1: #{m}" end + it "marshal1", :profile => true do + parser = Puppet::Pops::Parser::EvaluatingParser.new() + model = parser.parse_string(code).current + dumped = Marshal.dump(model) + m = Benchmark.measure { 10000.times { Marshal.load(dumped) }} + puts "Marshal1: #{m}" + end + + it "rgenjson", :profile => true do + require 'debugger'; debugger + parser = Puppet::Pops::Parser::EvaluatingParser.new() + model = parser.parse_string(code).current + dumped = json_dump(model) + m = Benchmark.measure { 10000.times { json_load(dumped) }} + puts "RGen Json: #{m}" + end + it "lexer2", :profile => true do lexer = Puppet::Pops::Parser::Lexer2.new m = Benchmark.measure {10000.times {lexer.string = code; lexer.fullscan }} diff --git a/spec/unit/pops/factory_spec.rb b/spec/unit/pops/factory_spec.rb index 3cefbc78f..beb0c18f3 100644 --- a/spec/unit/pops/factory_spec.rb +++ b/spec/unit/pops/factory_spec.rb @@ -267,7 +267,7 @@ describe Puppet::Pops::Model::Factory do built = UNLESS(true, literal(1), nil).current built.class.should == Puppet::Pops::Model::UnlessExpression built.test.class.should == Puppet::Pops::Model::LiteralBoolean - built.then_expr.class.should == Puppet::Pops::Model::LiteralNumber + built.then_expr.class.should == Puppet::Pops::Model::LiteralInteger built.else_expr.class.should == Puppet::Pops::Model::Nop end @@ -275,8 +275,8 @@ describe Puppet::Pops::Model::Factory do built = UNLESS(true, literal(1), literal(2)).current built.class.should == Puppet::Pops::Model::UnlessExpression built.test.class.should == Puppet::Pops::Model::LiteralBoolean - built.then_expr.class.should == Puppet::Pops::Model::LiteralNumber - built.else_expr.class.should == Puppet::Pops::Model::LiteralNumber + built.then_expr.class.should == Puppet::Pops::Model::LiteralInteger + built.else_expr.class.should == Puppet::Pops::Model::LiteralInteger end end @@ -285,7 +285,7 @@ describe Puppet::Pops::Model::Factory do built = IF(true, literal(1), nil).current built.class.should == Puppet::Pops::Model::IfExpression built.test.class.should == Puppet::Pops::Model::LiteralBoolean - built.then_expr.class.should == Puppet::Pops::Model::LiteralNumber + built.then_expr.class.should == Puppet::Pops::Model::LiteralInteger built.else_expr.class.should == Puppet::Pops::Model::Nop end @@ -293,8 +293,8 @@ describe Puppet::Pops::Model::Factory do built = IF(true, literal(1), literal(2)).current built.class.should == Puppet::Pops::Model::IfExpression built.test.class.should == Puppet::Pops::Model::LiteralBoolean - built.then_expr.class.should == Puppet::Pops::Model::LiteralNumber - built.else_expr.class.should == Puppet::Pops::Model::LiteralNumber + built.then_expr.class.should == Puppet::Pops::Model::LiteralInteger + built.else_expr.class.should == Puppet::Pops::Model::LiteralInteger end end diff --git a/spec/unit/pops/model/ast_transformer_spec.rb b/spec/unit/pops/model/ast_transformer_spec.rb index bf38463bc..beec804e4 100644 --- a/spec/unit/pops/model/ast_transformer_spec.rb +++ b/spec/unit/pops/model/ast_transformer_spec.rb @@ -46,7 +46,7 @@ describe Puppet::Pops::Model::AstTransformer do end it "converts an unknown radix to an error string" do - ast = transform(Puppet::Pops::Model::Factory.new(Puppet::Pops::Model::LiteralNumber, 3, 2)) + ast = transform(Puppet::Pops::Model::Factory.new(Puppet::Pops::Model::LiteralInteger, 3, 2)) ast.should be_kind_of(Puppet::Parser::AST::Name) ast.value.should == "bad radix:3" diff --git a/spec/unit/pops/model/model_spec.rb b/spec/unit/pops/model/model_spec.rb index 5014a64fa..3c1b22e3e 100644 --- a/spec/unit/pops/model/model_spec.rb +++ b/spec/unit/pops/model/model_spec.rb @@ -19,8 +19,8 @@ describe Puppet::Pops::Model::Factory do current = x.current current.is_a?(Model::ArithmeticExpression).should == true current.operator.should == :'+' - current.left_expr.class.should == Model::LiteralNumber - current.right_expr.class.should == Model::LiteralNumber + current.left_expr.class.should == Model::LiteralInteger + current.right_expr.class.should == Model::LiteralInteger current.left_expr.value.should == 10 current.right_expr.value.should == 20 end From 6fbc1efe5dcd091ce77de53cc49e54aa48c29e47 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 1 Nov 2013 15:54:58 +0100 Subject: [PATCH 062/800] (#21874) Apply making name[x] and name [x] different. This also includes a cleanup of LAMBDA (unused/dead logic in grammar) --- lib/puppet/pops/parser/egrammar.ra | 8 +- lib/puppet/pops/parser/eparser.rb | 2002 +++++++++++++------------- lib/puppet/pops/parser/lexer2.rb | 8 +- spec/unit/pops/parser/lexer2_spec.rb | 10 +- 4 files changed, 1044 insertions(+), 984 deletions(-) diff --git a/lib/puppet/pops/parser/egrammar.ra b/lib/puppet/pops/parser/egrammar.ra index 5bda3cf17..4895ea2ef 100644 --- a/lib/puppet/pops/parser/egrammar.ra +++ b/lib/puppet/pops/parser/egrammar.ra @@ -27,7 +27,8 @@ prechigh left AT ATAT left DOT left CALL - left LBRACK + left LBRACK LISTSTART + left RBRACK left QMARK left LCOLLECT LLCOLLECT right NOT @@ -150,7 +151,7 @@ literal_expression | default | hash | regex - | text_or_name =LOW # resolves hash key ambiguity (racc W U require this?) + | text_or_name | number | type | undef @@ -617,6 +618,9 @@ array : LBRACK expressions RBRACK { result = Factory.LIST(val[1]); loc result, val[0], val[2] } | LBRACK expressions COMMA RBRACK { result = Factory.LIST(val[1]); loc result, val[0], val[3] } | LBRACK RBRACK { result = Factory.literal([]) ; loc result, val[0] } + | LISTSTART expressions RBRACK { result = Factory.LIST(val[1]); loc result, val[0], val[2] } + | LISTSTART expressions COMMA RBRACK { result = Factory.LIST(val[1]); loc result, val[0], val[3] } + | LISTSTART RBRACK { result = Factory.literal([]) ; loc result, val[0] } hash : LBRACE hashpairs RBRACE { result = Factory.HASH(val[1]); loc result, val[0], val[2] } diff --git a/lib/puppet/pops/parser/eparser.rb b/lib/puppet/pops/parser/eparser.rb index 093b16d88..19b742b48 100644 --- a/lib/puppet/pops/parser/eparser.rb +++ b/lib/puppet/pops/parser/eparser.rb @@ -20,7 +20,7 @@ module Puppet module Parser class Parser < Racc::Parser -module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 705) +module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 709) # Make emacs happy # Local Variables: @@ -30,166 +30,174 @@ module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 705) ##### State transition tables begin ### clist = [ -'71,220,207,235,117,53,55,113,116,90,91,87,82,94,240,98,268,93,71,-129', -'83,85,84,86,235,222,106,53,55,53,55,94,319,98,117,93,-206,204,116,97', -'217,-197,126,89,88,124,297,75,76,78,77,80,81,282,73,74,-127,97,245,266', -'-129,72,207,71,126,62,126,124,236,124,79,92,90,91,87,82,94,-206,98,72', -'93,71,-197,83,85,84,86,62,117,62,53,55,116,299,94,328,98,-127,93,246', -'225,227,97,234,228,224,89,88,235,217,75,76,78,77,80,81,301,73,74,117', -'97,117,106,116,72,116,71,126,117,314,124,313,116,79,92,90,91,87,82,94', -'71,98,72,93,260,71,83,85,84,86,62,71,314,94,313,98,203,93,94,244,98', -'259,93,306,94,97,98,307,93,89,88,230,229,75,76,78,77,80,81,97,73,74', -'308,207,97,223,311,72,66,71,97,67,69,68,70,315,79,92,90,91,87,82,94', -'317,98,258,93,284,242,83,85,84,86,244,324,325,288,266,194,170,66,137', -'244,335,242,244,118,242,97,338,106,107,89,88,281,267,75,76,78,77,80', -'81,342,73,74,317,344,345,346,347,72,103,71,349,350,351,285,66,63,79', -'92,90,91,87,82,94,358,98,359,93,360,361,83,85,84,86,,,,,,,,,,,,,,,,97', -',,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,95,,,,,79,92,90,91', -'87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78', -'77,80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93,,,83', -'85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,', -'72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,', -',,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,,,,226,,79,92', -'90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75', -'76,78,77,80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93', -',,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74', -',,,,,72,,71,,213,,,,,79,92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,', -',,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,,,,,', -'79,92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88', -',,75,76,78,77,80,81,,73,74,,,,,,72,,71,,212,,,,,79,92,90,91,87,82,94', -',98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81', -',73,74,,,,,,72,,71,,211,,,,,79,92,90,91,87,82,94,,98,,93,,,83,85,84', -'86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71', -',210,,,,,79,92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97', -',,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90,91,87', -'82,94,,98,,93,,199,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78', -'77,80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93,,,83', -'85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,', -'72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,', -',,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90', -'91,87,82,94,,98,,93,,,83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76', -'78,77,80,81,,73,74,,,,,,72,,71,,,,,,,79,92,90,91,87,82,94,,98,,93,,', -'83,85,84,86,,,,,,,,,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,,73,74,,', -',,,72,,71,,,,,,,79,92,90,91,87,82,94,71,98,,93,,,83,85,84,86,,,,94,', -'98,,93,,,,,,,,97,,,,89,88,,,75,76,78,77,80,81,97,73,74,,,53,55,,72,49', -'290,50,,,,73,74,79,92,,,,72,,,,,13,,,,,,39,,46,,48,100,,47,61,57,,41', -'60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,71,,,,,40,,,13,56,,,,94,39', -'98,46,93,48,100,,47,61,57,,41,60,,,,,58,12,,,59,53,55,11,97,49,129,50', -',,,62,,,78,77,,40,,73,74,56,,13,,,72,,,39,,46,,48,100,79,47,61,57,,41', -'60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46', -',48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,71,49,11,50,,,,,,,62', -',,94,,98,40,93,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,97,,,58,12', -'53,55,59,,49,11,50,,,,73,74,,62,,,,72,,40,,,13,56,,,,,39,,46,,48,43', -',47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,', -',13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49', -'11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44', -'45,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48', -'43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40', -',,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,71', -'49,11,50,,,,,,,62,,,94,,98,40,93,,13,56,,,,,39,,46,,48,100,,47,61,57', -',41,60,,97,,,58,12,53,55,59,,49,11,50,,,,73,74,,62,,,,72,,40,,,13,56', -',,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,', -',,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12', -'53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61', -'57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,', -'39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62', -',,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55', -'59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,', -'41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,', -'46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,', -',,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59', -',49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41', -'60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46', -',48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,', -'40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49', -'11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,', -',,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48', -'100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,', -',13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11', -'50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,', -'58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100', -',47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13', -'56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50', -',,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58', -'12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47', -'61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,', -',,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,', -',,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53', -'55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57', -',41,60,,,,,58,12,,,59,53,55,11,,49,,50,334,,,62,,,,,,40,,,169,56,,13', -',,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50', -',,,,,,62,,,,,,40,,,13,56,,,,,172,189,183,190,48,184,192,185,181,179', -',174,187,,,,,58,12,193,188,186,53,55,11,,49,,50,,,,62,,,,,191,173,,', -',56,,13,,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,', -'49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60', -',,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48', -'100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,320,,,,,,62,,,,,,40', -',,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11', -'50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,', -'58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100', -',47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,71,,,,,40,,,13', -'56,,,,94,39,98,46,93,48,100,,47,61,57,,41,60,,,,,58,12,,,59,53,55,11', -'97,49,,50,,,,62,,,78,77,,40,,73,74,56,,13,201,,72,,,39,,46,,48,100,79', -'47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56', -',,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,', -',,,62,71,,,,,40,,,13,56,,,,94,39,98,46,93,48,100,,47,61,57,,41,60,,', -',,58,12,,,59,53,55,11,97,49,,50,,,,62,75,76,78,77,,40,,73,74,56,,13', -'209,,72,,,39,,46,,48,100,79,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11', -'50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,', -'58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100', -',47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13', -'56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50', -',,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58', -'12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47', -'61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,', -',,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,', -',,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53', -'55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57', -',41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39', -',46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,', -',,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55', -'59,,49,11,50,292,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57', -',41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39', -',46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,', -',,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59', -',49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41', -'60,,,,,58,12,53,55,59,,49,11,50,132,,,,,,62,,,,,,40,,,13,56,,,,,39,', -'46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,', -',,,40,,,13,56,,,,,172,189,183,190,48,184,192,185,181,179,,174,187,,', -',,58,12,193,188,186,53,55,11,,49,,50,,,,62,,,,,191,173,,,,56,,13,,,', -',,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,272,', -',,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58', -'12,53,55,59,,49,11,50,270,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43', -',47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,264,,,,,,62,,,,,,40', -',,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49', -'11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,', -',,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48', -'100,,47,61,57,,41,60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,71,,,,,40', -',,13,56,,,,94,39,98,46,93,48,100,,47,61,57,,41,60,71,,,,58,12,,,59,', -',11,97,94,,98,,93,,62,75,76,78,77,,40,,73,74,56,,,,,72,,,,,97,,,,79', -',71,,75,76,78,77,80,81,,73,74,,,94,,98,72,93,,,,,,,,79,,,,,,,,,,,,,', -'97,,,,,,71,,75,76,78,77,80,81,,73,74,,82,94,,98,72,93,,,83,,,,,79,,', -',,,,,,,,,,,97,,,,,,71,,75,76,78,77,80,81,,73,74,,82,94,,98,72,93,,,83', -',,,,79,,,,,,,,,,,,,,97,,,,,,71,,75,76,78,77,80,81,,73,74,,82,94,,98', -'72,93,,,83,,,,,79,,,,,,,,,,,,,,97,,,,,,71,,75,76,78,77,80,81,,73,74', -',82,94,,98,72,93,,,83,,,,,79,,,,,,,,,,,,,,97,,,,,,71,,75,76,78,77,80', -'81,,73,74,87,82,94,,98,72,93,,,83,85,84,86,,79,,,,,,,,,,,,,,97,,,,,', -'71,,75,76,78,77,80,81,,73,74,87,82,94,,98,72,93,,,83,85,84,86,,79,,', -',,,,,,,,53,55,,97,49,,50,353,88,,,75,76,78,77,80,81,,73,74,,,13,,,72', -',,39,,46,,48,43,79,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50', -'355,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45', -',,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,43', -',47,61,57,64,41,60,44,45,,,58,12,53,55,59,,49,11,50,357,,,,,,62,,,,', -',40,,,13,56,,,,,39,,46,,48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59', -',49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41', -'60,,,,,58,12,53,55,59,,49,11,50,,,,,,,62,,,,,,40,,,13,56,,,,,39,,46', -',48,43,,47,61,57,,41,60,44,45,,,58,12,53,55,59,,49,11,50,,,,,,,62,,', -',,,40,,,13,56,,,,,39,,46,,48,100,,47,61,57,,41,60,,,,,58,12,,,59,,,11', -',,,254,189,253,190,62,251,192,255,249,248,40,250,252,,56,,,,,193,188', -'256,254,189,253,190,,251,192,255,249,248,,250,252,,,191,257,,,193,188', -'256,254,189,253,190,,251,192,255,249,248,,250,252,,,191,257,,,193,188', -'256,,,,,,,,,,,,,,,,191,257' ] - racc_action_table = arr = ::Array.new(5345, nil) +'54,56,-209,240,49,118,51,54,56,117,230,239,334,287,72,229,240,273,54', +'56,-127,-200,210,13,-129,210,235,234,95,39,99,46,94,48,43,250,47,62', +'58,272,41,61,44,45,-209,127,59,12,125,67,60,54,56,11,98,49,127,51,363', +'125,207,63,-127,-200,79,78,-129,40,63,74,75,57,240,228,13,50,73,251', +'227,63,39,325,46,114,48,43,80,47,62,58,303,41,61,44,45,247,118,59,12', +'223,117,60,54,56,11,320,49,319,51,54,56,118,63,118,305,117,72,117,40', +'118,54,56,57,117,225,13,50,320,249,319,95,39,99,46,94,48,43,307,47,62', +'58,65,41,61,44,45,226,127,59,12,125,293,60,54,56,11,98,49,127,51,361', +'125,232,63,118,233,79,78,117,40,63,74,75,57,220,249,13,50,73,220,312', +'63,39,313,46,314,48,43,80,47,62,58,210,41,61,44,45,263,317,59,12,290', +'321,60,54,56,11,323,49,289,51,359,206,247,63,249,330,331,72,286,40,271', +'247,197,57,173,67,13,50,140,341,264,95,39,99,46,94,48,43,249,47,62,58', +'119,41,61,44,45,265,344,59,12,107,108,60,54,56,11,98,49,107,51,269,107', +'348,63,76,77,79,78,323,40,350,74,75,57,351,352,13,50,73,353,104,355', +'39,356,46,357,48,43,80,47,62,58,271,41,61,44,45,67,64,59,12,364,365', +'60,54,56,11,72,49,366,51,367,,,63,,,,72,,40,95,,99,57,94,,13,50,,,,95', +'39,99,46,94,48,101,,47,62,58,,41,61,,98,,,59,12,,,60,54,56,11,98,49', +',51,,,,63,76,77,79,78,,40,,74,75,57,,,13,50,73,,,,39,,46,,48,101,80', +'47,62,58,,41,61,68,70,69,71,59,12,,,60,54,56,11,72,49,,51,,,,63,,,,72', +',40,95,,99,57,94,,13,50,,,,95,39,99,46,94,48,101,,47,62,58,,41,61,,98', +',,59,12,,,60,54,56,11,98,49,,51,74,75,,63,,,,73,,40,,,,57,,,13,50,73', +',,,39,,46,,48,43,,47,62,58,,41,61,44,45,,,59,12,,,60,54,56,11,72,49', +',51,,,,63,,,,72,,40,95,,99,57,94,,13,50,,,,95,39,99,46,94,48,101,,47', +'62,58,,41,61,,98,,,59,12,,,60,54,56,11,98,49,,51,277,,,63,,,,72,,40', +',74,75,57,,,13,50,73,,,95,39,99,46,94,48,43,,47,62,58,,41,61,44,45,', +',59,12,,,60,54,56,11,98,49,,51,,,,63,,,,72,,40,,74,75,57,,,13,50,73', +',,95,39,99,46,94,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,98', +'49,,51,,,,63,,,,72,,40,,,,57,,,13,50,73,,,95,39,99,46,94,48,101,,47', +'62,58,,41,61,,,,,59,12,,,60,54,56,11,98,49,,51,,,,63,,,,,,40,,,,57,', +',13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,', +'49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41', +'61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39', +',46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,', +',,,,40,,,,57,,,13,50,,,,,175,192,186,193,48,187,195,188,184,182,,177', +'190,,,,,59,12,196,191,189,54,56,11,,49,,51,,,,63,,,,,194,176,,,,57,', +',13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,', +'49,295,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,', +'41,61,,,,,59,12,,,60,54,56,11,,49,130,51,,,,63,,,,,,40,,,,57,,,13,50', +',,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,132', +'51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,', +',,,59,12,,,60,54,56,11,,49,,51,135,,,63,,,,,,40,,,,57,,,13,50,,,,,39', +',46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,', +',,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,', +',60,54,56,11,,49,296,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101', +',47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57', +',,13,50,,,,,39,,46,,48,43,,47,62,58,,41,61,44,45,,,59,12,,,60,54,56', +'11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58', +',41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,', +',,,39,,46,,48,43,,47,62,58,,41,61,44,45,,,59,12,,,60,54,56,11,,49,,51', +',,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,43,,47,62,58,,41,61,44,45', +',,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46', +',48,43,,47,62,58,,41,61,44,45,,,59,12,,,60,54,56,11,,49,,51,,,,63,,', +',,,40,,,,57,,,13,50,,,,,39,,46,,48,43,,47,62,58,,41,61,44,45,,,59,12', +',,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,43', +',47,62,58,,41,61,44,45,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,', +',,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56', +'11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58', +',41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,', +',,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,', +',,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59', +'12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48', +'101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,', +',,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56', +'11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58', +',41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,', +',,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,', +',,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59', +'12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48', +'101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,', +',,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56', +'11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58', +',41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,', +',,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,', +',,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59', +'12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48', +'101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,', +',,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56', +'11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58', +',41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,', +',,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,', +',,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59', +'12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48', +'101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,', +',,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56', +'11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58', +',41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,', +',,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,', +',,63,,,,,,40,,,172,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,', +',,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,175,192', +'186,193,48,187,195,188,184,182,,177,190,,,,,59,12,196,191,189,54,56', +'11,,49,,51,340,,,63,,,,,194,176,,,,57,,,13,50,,,,,39,,46,,48,43,,47', +'62,58,,41,61,44,45,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57', +',,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11', +',49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41', +'61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39', +',46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,', +',,,,40,,,,57,,,13,50,,,,,39,,46,,48,43,,47,62,58,,41,61,44,45,,,59,12', +',,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101', +',47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57', +',,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,,,11,54,56', +',,49,,51,63,,,,,,40,,,,57,,,,50,,13,204,,,,,39,,46,,48,101,,47,62,58', +',41,61,,,,,59,12,,,60,54,56,11,,49,,51,326,,,63,,,,,,40,,,,57,,,13,50', +',,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51', +',,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,', +'59,12,,,60,,,11,54,56,,,49,,51,63,,,,,,40,,,,57,,,,50,,13,212,,,,,39', +',46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,298,,,63', +',,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12', +',,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101', +',47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57', +',,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11', +',49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41', +'61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39', +',46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,', +',,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,', +',60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101', +',47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57', +',,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11', +',49,,51,275,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,43,,47,62,58,', +'41,61,44,45,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50', +',,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,,,11,,,,,,,,63,,', +',,,40,72,,,57,,,,50,,91,92,93,88,83,95,,99,,94,,,84,86,85,87,,,,,,,', +',,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,72,,96,,,73,,,,91,92', +'93,88,83,95,80,99,,94,,,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76', +'77,79,78,81,82,,74,75,,,,,,73,,72,,,,,231,,,80,91,92,93,88,83,95,,99', +',94,,,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74', +'75,72,,216,,,73,,,,91,92,93,88,83,95,80,99,,94,,,84,86,85,87,,,,,,,', +',,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,72,,,,,73,,,,91,92,93', +'88,83,95,80,99,,94,,,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77', +'79,78,81,82,,74,75,72,,215,,,73,,,,91,92,93,88,83,95,80,99,,94,,,84', +'86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,72,,214', +',,73,,,,91,92,93,88,83,95,80,99,,94,,,84,86,85,87,,,,,,,,,,,,,,,,98', +',,,90,89,,,76,77,79,78,81,82,,74,75,72,,213,,,73,,,,91,92,93,88,83,95', +'80,99,,94,,,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81', +'82,,74,75,72,,,,,73,,,,91,92,93,88,83,95,80,99,,94,,,84,86,85,87,,,', +',,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,72,,,,,73,,,,91,92', +'93,88,83,95,80,99,,94,,,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76', +'77,79,78,81,82,,74,75,72,,,,,73,,,,91,92,93,88,83,95,80,99,,94,,,84', +'86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,72,,', +',,73,,,,91,92,93,88,83,95,80,99,,94,,,84,86,85,87,,,,,,,,,,,,,,,,98', +',,,90,89,,,76,77,79,78,81,82,,74,75,72,,,,,73,,,,91,92,93,88,83,95,80', +'99,,94,,,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82', +',74,75,72,,,,,73,,,,91,92,93,88,83,95,80,99,,94,,,84,86,85,87,72,,,', +',,,,,,,,,,95,98,99,,94,90,89,,,76,77,79,78,81,82,,74,75,,,,,,73,,,98', +',,72,,,,80,76,77,79,78,81,82,,74,75,95,,99,,94,73,,,,,,72,,,,80,,,,', +',,,,83,95,98,99,,94,,,84,,76,77,79,78,81,82,,74,75,,,,,,73,,,98,,,,', +'72,,80,76,77,79,78,81,82,,74,75,,83,95,,99,73,94,,,84,,,,72,,80,,,,', +',,,,,,83,95,98,99,,94,,,84,,76,77,79,78,81,82,,74,75,,,,,,73,,,98,,', +',,72,,80,76,77,79,78,81,82,,74,75,,83,95,,99,73,94,,,84,,,,,,80,,,,', +',,,,,,,,98,,,,,72,,,76,77,79,78,81,82,,74,75,88,83,95,,99,73,94,,,84', +'86,85,87,,,80,,,,,,,,,,,,,98,,,,,72,,,76,77,79,78,81,82,,74,75,88,83', +'95,,99,73,94,,,84,86,85,87,,,80,,,,,,,,,,,,,98,,,,,89,,,76,77,79,78', +'81,82,,74,75,72,,,,,73,,,,91,92,93,88,83,95,80,99,,94,,,84,86,85,87', +',,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,72,,,,,73,,,,91', +'92,93,88,83,95,80,99,,94,,,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,', +'76,77,79,78,81,82,,74,75,72,,,,,73,,,,91,92,93,88,83,95,80,99,,94,,', +'84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,,', +',,,73,,72,,,,,241,,,80,91,92,93,88,83,95,,99,,94,,,84,86,85,87,,,,,', +',,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,72,,,,,73,,,,91,92', +'93,88,83,95,80,99,,94,,202,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,', +'76,77,79,78,81,82,,74,75,,,,,,73,,72,,,,,,,,80,91,92,93,88,83,95,245', +'99,,94,,,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82', +',74,75,72,,,,,73,,,,91,92,93,88,83,95,80,99,,94,,,84,86,85,87,,,,,,', +',,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,,259,192,258,193,73', +'256,195,260,254,253,,255,257,,80,,,,,196,191,261,259,192,258,193,,256', +'195,260,254,253,,255,257,,,194,262,,,196,191,261,259,192,258,193,,256', +'195,260,254,253,,255,257,,,194,262,,,196,191,261,,,,,,,,,,,,,,,,194', +'262' ] + racc_action_table = arr = ::Array.new(5601, nil) idx = 0 clist.each do |str| str.split(',', -1).each do |i| @@ -199,184 +207,192 @@ clist = [ end clist = [ -'171,120,106,208,100,47,47,43,100,171,171,171,171,171,171,171,208,171', -'147,181,171,171,171,171,273,120,37,185,185,222,222,147,273,147,43,147', -'186,106,43,171,119,187,47,171,171,47,237,171,171,171,171,171,171,217', -'171,171,179,147,182,205,181,171,217,166,185,47,222,185,166,222,171,171', -'166,166,166,166,166,186,166,147,166,146,187,166,166,166,166,185,281', -'222,220,220,281,241,146,281,146,179,146,182,128,131,166,144,131,128', -'166,166,144,114,166,166,166,166,166,166,243,166,166,184,146,46,201,184', -'166,46,165,220,183,311,220,311,183,166,166,165,165,165,165,165,99,165', -'146,165,200,101,165,165,165,165,220,145,270,99,270,99,105,99,101,247', -'101,198,101,261,145,165,145,263,145,165,165,136,136,165,165,165,165', -'165,165,99,165,165,265,266,101,122,269,165,138,164,145,7,7,7,7,271,165', -'165,164,164,164,164,164,272,164,196,164,218,276,164,164,164,164,277', -'278,279,223,283,96,94,65,63,176,296,214,298,44,175,164,305,306,38,164', -'164,215,207,164,164,164,164,164,164,314,164,164,315,317,318,322,323', -'164,36,163,329,330,333,219,5,1,164,164,163,163,163,163,163,348,163,352', -'163,354,356,163,163,163,163,,,,,,,,,,,,,,,,163,,,,163,163,,,163,163', -'163,163,163,163,,163,163,,,,,,163,,10,,10,,,,,163,163,10,10,10,10,10', -',10,,10,,,10,10,10,10,,,,,,,,,,,,,,,,10,,,,10,10,,,10,10,10,10,10,10', -',10,10,,,,,,10,,195,,,,,,,10,10,195,195,195,195,195,,195,,195,,,195', -'195,195,195,,,,,,,,,,,,,,,,195,,,,195,195,,,195,195,195,195,195,195', -',195,195,,,,,,195,,134,,,,,,,195,195,134,134,134,134,134,,134,,134,', -',134,134,134,134,,,,,,,,,,,,,,,,134,,,,134,134,,,134,134,134,134,134', -'134,,134,134,,,,,,134,,130,,,,,130,,134,134,130,130,130,130,130,,130', -',130,,,130,130,130,130,,,,,,,,,,,,,,,,130,,,,130,130,,,130,130,130,130', -'130,130,,130,130,,,,,,130,,127,,,,,,,130,130,127,127,127,127,127,,127', -',127,,,127,127,127,127,,,,,,,,,,,,,,,,127,,,,127,127,,,127,127,127,127', -'127,127,,127,127,,,,,,127,,112,,112,,,,,127,127,112,112,112,112,112', -',112,,112,,,112,112,112,112,,,,,,,,,,,,,,,,112,,,,112,112,,,112,112', -'112,112,112,112,,112,112,,,,,,112,,310,,,,,,,112,112,310,310,310,310', -'310,,310,,310,,,310,310,310,310,,,,,,,,,,,,,,,,310,,,,310,310,,,310', -'310,310,310,310,310,,310,310,,,,,,310,,111,,111,,,,,310,310,111,111', -'111,111,111,,111,,111,,,111,111,111,111,,,,,,,,,,,,,,,,111,,,,111,111', -',,111,111,111,111,111,111,,111,111,,,,,,111,,110,,110,,,,,111,111,110', -'110,110,110,110,,110,,110,,,110,110,110,110,,,,,,,,,,,,,,,,110,,,,110', -'110,,,110,110,110,110,110,110,,110,110,,,,,,110,,108,,108,,,,,110,110', -'108,108,108,108,108,,108,,108,,,108,108,108,108,,,,,,,,,,,,,,,,108,', -',,108,108,,,108,108,108,108,108,108,,108,108,,,,,,108,,102,,,,,,,108', -'108,102,102,102,102,102,,102,,102,,102,102,102,102,102,,,,,,,,,,,,,', -',,102,,,,102,102,,,102,102,102,102,102,102,,102,102,,,,,,102,,304,,', -',,,,102,102,304,304,304,304,304,,304,,304,,,304,304,304,304,,,,,,,,', -',,,,,,,304,,,,304,304,,,304,304,304,304,304,304,,304,304,,,,,,304,,303', -',,,,,,304,304,303,303,303,303,303,,303,,303,,,303,303,303,303,,,,,,', -',,,,,,,,,303,,,,303,303,,,303,303,303,303,303,303,,303,303,,,,,,303', -',289,,,,,,,303,303,289,289,289,289,289,,289,,289,,,289,289,289,289,', -',,,,,,,,,,,,,,289,,,,289,289,,,289,289,289,289,289,289,,289,289,,,,', -',289,,295,,,,,,,289,289,295,295,295,295,295,,295,,295,,,295,295,295', -'295,,,,,,,,,,,,,,,,295,,,,295,295,,,295,295,295,295,295,295,,295,295', -',,,,,295,,291,,,,,,,295,295,291,291,291,291,291,152,291,,291,,,291,291', -'291,291,,,,152,,152,,152,,,,,,,,291,,,,291,291,,,291,291,291,291,291', -'291,152,291,291,,,224,224,,291,224,224,224,,,,152,152,291,291,,,,152', -',,,,224,,,,,,224,,224,,224,224,,224,224,224,,224,224,,,,,224,224,54', -'54,224,,54,224,54,,,,,,,224,148,,,,,224,,,54,224,,,,148,54,148,54,148', -'54,54,,54,54,54,,54,54,,,,,54,54,,,54,49,49,54,148,49,49,49,,,,54,,', -'148,148,,54,,148,148,54,,49,,,148,,,49,,49,,49,49,148,49,49,49,,49,49', -',,,,49,49,64,64,49,,64,49,64,,,,,,,49,,,,,,49,,,64,49,,,,,64,,64,,64', -'64,,64,64,64,,64,64,64,64,,,64,64,172,172,64,151,172,64,172,,,,,,,64', -',,151,,151,64,151,,172,64,,,,,172,,172,,172,172,,172,172,172,,172,172', -',151,,,172,172,66,66,172,,66,172,66,,,,151,151,,172,,,,151,,172,,,66', -'172,,,,,66,,66,,66,66,,66,66,66,,66,66,66,66,,,66,66,67,67,66,,67,66', -'67,,,,,,,66,,,,,,66,,,67,66,,,,,67,,67,,67,67,,67,67,67,,67,67,67,67', -',,67,67,68,68,67,,68,67,68,,,,,,,67,,,,,,67,,,68,67,,,,,68,,68,,68,68', -',68,68,68,,68,68,68,68,,,68,68,69,69,68,,69,68,69,,,,,,,68,,,,,,68,', -',69,68,,,,,69,,69,,69,69,,69,69,69,,69,69,69,69,,,69,69,70,70,69,,70', -'69,70,,,,,,,69,,,,,,69,,,70,69,,,,,70,,70,,70,70,,70,70,70,,70,70,70', -'70,,,70,70,71,71,70,150,71,70,71,,,,,,,70,,,150,,150,70,150,,71,70,', -',,,71,,71,,71,71,,71,71,71,,71,71,,150,,,71,71,72,72,71,,72,71,72,,', -',150,150,,71,,,,150,,71,,,72,71,,,,,72,,72,,72,72,,72,72,72,,72,72,', -',,,72,72,73,73,72,,73,72,73,,,,,,,72,,,,,,72,,,73,72,,,,,73,,73,,73', -'73,,73,73,73,,73,73,,,,,73,73,74,74,73,,74,73,74,,,,,,,73,,,,,,73,,', -'74,73,,,,,74,,74,,74,74,,74,74,74,,74,74,,,,,74,74,75,75,74,,75,74,75', -',,,,,,74,,,,,,74,,,75,74,,,,,75,,75,,75,75,,75,75,75,,75,75,,,,,75,75', -'76,76,75,,76,75,76,,,,,,,75,,,,,,75,,,76,75,,,,,76,,76,,76,76,,76,76', -'76,,76,76,,,,,76,76,77,77,76,,77,76,77,,,,,,,76,,,,,,76,,,77,76,,,,', -'77,,77,,77,77,,77,77,77,,77,77,,,,,77,77,78,78,77,,78,77,78,,,,,,,77', -',,,,,77,,,78,77,,,,,78,,78,,78,78,,78,78,78,,78,78,,,,,78,78,79,79,78', -',79,78,79,,,,,,,78,,,,,,78,,,79,78,,,,,79,,79,,79,79,,79,79,79,,79,79', -',,,,79,79,80,80,79,,80,79,80,,,,,,,79,,,,,,79,,,80,79,,,,,80,,80,,80', -'80,,80,80,80,,80,80,,,,,80,80,81,81,80,,81,80,81,,,,,,,80,,,,,,80,,', -'81,80,,,,,81,,81,,81,81,,81,81,81,,81,81,,,,,81,81,82,82,81,,82,81,82', -',,,,,,81,,,,,,81,,,82,81,,,,,82,,82,,82,82,,82,82,82,,82,82,,,,,82,82', -'83,83,82,,83,82,83,,,,,,,82,,,,,,82,,,83,82,,,,,83,,83,,83,83,,83,83', -'83,,83,83,,,,,83,83,84,84,83,,84,83,84,,,,,,,83,,,,,,83,,,84,83,,,,', -'84,,84,,84,84,,84,84,84,,84,84,,,,,84,84,85,85,84,,85,84,85,,,,,,,84', -',,,,,84,,,85,84,,,,,85,,85,,85,85,,85,85,85,,85,85,,,,,85,85,86,86,85', -',86,85,86,,,,,,,85,,,,,,85,,,86,85,,,,,86,,86,,86,86,,86,86,86,,86,86', -',,,,86,86,87,87,86,,87,86,87,,,,,,,86,,,,,,86,,,87,86,,,,,87,,87,,87', -'87,,87,87,87,,87,87,,,,,87,87,88,88,87,,88,87,88,,,,,,,87,,,,,,87,,', -'88,87,,,,,88,,88,,88,88,,88,88,88,,88,88,,,,,88,88,89,89,88,,89,88,89', -',,,,,,88,,,,,,88,,,89,88,,,,,89,,89,,89,89,,89,89,89,,89,89,,,,,89,89', -'90,90,89,,90,89,90,,,,,,,89,,,,,,89,,,90,89,,,,,90,,90,,90,90,,90,90', -'90,,90,90,,,,,90,90,91,91,90,,91,90,91,,,,,,,90,,,,,,90,,,91,90,,,,', -'91,,91,,91,91,,91,91,91,,91,91,,,,,91,91,92,92,91,,92,91,92,,,,,,,91', -',,,,,91,,,92,91,,,,,92,,92,,92,92,,92,92,92,,92,92,,,,,92,92,93,93,92', -',93,92,93,,,,,,,92,,,,,,92,,,93,92,,,,,93,,93,,93,93,,93,93,93,,93,93', -',,,,93,93,,,93,285,285,93,,285,,285,285,,,93,,,,,,93,,,93,93,,285,,', -',,,285,,285,,285,285,,285,285,285,,285,285,285,285,,,285,285,95,95,285', -',95,285,95,,,,,,,285,,,,,,285,,,95,285,,,,,95,95,95,95,95,95,95,95,95', -'95,,95,95,,,,,95,95,95,95,95,284,284,95,,284,,284,,,,95,,,,,95,95,,', -',95,,284,,,,,,284,,284,,284,284,,284,284,284,,284,284,284,284,,,284', -'284,97,97,284,,97,284,97,,,,,,,284,,,,,,284,,,97,284,,,,,97,,97,,97', -'97,,97,97,97,,97,97,,,,,97,97,98,98,97,,98,97,98,,,,,,,97,,,,,,97,,', -'98,97,,,,,98,,98,,98,98,,98,98,98,,98,98,,,,,98,98,274,274,98,,274,98', -'274,274,,,,,,98,,,,,,98,,,274,98,,,,,274,,274,,274,274,,274,274,274', -',274,274,,,,,274,274,267,267,274,,267,274,267,,,,,,,274,,,,,,274,,,267', -'274,,,,,267,,267,,267,267,,267,267,267,,267,267,,,,,267,267,260,260', -'267,,260,267,260,,,,,,,267,,,,,,267,,,260,267,,,,,260,,260,,260,260', -',260,260,260,,260,260,,,,,260,260,42,42,260,,42,260,42,,,,,,,260,149', -',,,,260,,,42,260,,,,149,42,149,42,149,42,42,,42,42,42,,42,42,,,,,42', -'42,,,42,103,103,42,149,103,,103,,,,42,,,149,149,,42,,149,149,42,,103', -'103,,149,,,103,,103,,103,103,149,103,103,103,,103,103,,,,,103,103,246', -'246,103,,246,103,246,,,,,,,103,,,,,,103,,,246,103,,,,,246,,246,,246', -'246,,246,246,246,,246,246,,,,,246,246,245,245,246,,245,246,245,,,,,', -',246,153,,,,,246,,,245,246,,,,153,245,153,245,153,245,245,,245,245,245', -',245,245,,,,,245,245,,,245,107,107,245,153,107,,107,,,,245,153,153,153', -'153,,245,,153,153,245,,107,107,,153,,,107,,107,,107,107,153,107,107', -'107,,107,107,,,,,107,107,41,41,107,,41,107,41,,,,,,,107,,,,,,107,,,41', -'107,,,,,41,,41,,41,41,,41,41,41,,41,41,,,,,41,41,40,40,41,,40,41,40', -',,,,,,41,,,,,,41,,,40,41,,,,,40,,40,,40,40,,40,40,40,,40,40,,,,,40,40', -'39,39,40,,39,40,39,,,,,,,40,,,,,,40,,,39,40,,,,,39,,39,,39,39,,39,39', -'39,,39,39,,,,,39,39,313,313,39,,313,39,313,,,,,,,39,,,,,,39,,,313,39', -',,,,313,,313,,313,313,,313,313,313,,313,313,,,,,313,313,113,113,313', -',113,313,113,,,,,,,313,,,,,,313,,,113,313,,,,,113,,113,,113,113,,113', -'113,113,,113,113,,,,,113,113,242,242,113,,242,113,242,,,,,,,113,,,,', -',113,,,242,113,,,,,242,,242,,242,242,,242,242,242,,242,242,,,,,242,242', -'236,236,242,,236,242,236,,,,,,,242,,,,,,242,,,236,242,,,,,236,,236,', -'236,236,,236,236,236,,236,236,,,,,236,236,235,235,236,,235,236,235,', -',,,,,236,,,,,,236,,,235,236,,,,,235,,235,,235,235,,235,235,235,,235', -'235,,,,,235,235,233,233,235,,233,235,233,,,,,,,235,,,,,,235,,,233,235', -',,,,233,,233,,233,233,,233,233,233,,233,233,,,,,233,233,325,325,233', -',325,233,325,,,,,,,233,,,,,,233,,,325,233,,,,,325,,325,,325,325,,325', -'325,325,,325,325,325,325,,,325,325,228,228,325,,228,325,228,228,,,,', -',325,,,,,,325,,,228,325,,,,,228,,228,,228,228,,228,228,228,,228,228', -',,,,228,228,13,13,228,,13,228,13,,,,,,,228,,,,,,228,,,13,228,,,,,13', -',13,,13,13,,13,13,13,,13,13,,,,,13,13,226,226,13,,226,13,226,,,,,,,13', -',,,,,13,,,226,13,,,,,226,,226,,226,226,,226,226,226,,226,226,,,,,226', -'226,12,12,226,,12,226,12,,,,,,,226,,,,,,226,,,12,226,,,,,12,,12,,12', -'12,,12,12,12,,12,12,,,,,12,12,50,50,12,,50,12,50,50,,,,,,12,,,,,,12', -',,50,12,,,,,50,,50,,50,50,,50,50,50,,50,50,,,,,50,50,213,213,50,,213', -'50,213,,,,,,,50,,,,,,50,,,213,50,,,,,213,213,213,213,213,213,213,213', -'213,213,,213,213,,,,,213,213,213,213,213,212,212,213,,212,,212,,,,213', -',,,,213,213,,,,213,,212,,,,,,212,,212,,212,212,,212,212,212,,212,212', -',,,,212,212,211,211,212,,211,212,211,211,,,,,,212,,,,,,212,,,211,212', -',,,,211,,211,,211,211,,211,211,211,,211,211,211,211,,,211,211,210,210', -'211,,210,211,210,210,,,,,,211,,,,,,211,,,210,211,,,,,210,,210,,210,210', -',210,210,210,,210,210,210,210,,,210,210,203,203,210,,203,210,203,203', -',,,,,210,,,,,,210,,,203,210,,,,,203,,203,,203,203,,203,203,203,,203', -'203,203,203,,,203,203,11,11,203,,11,203,11,,,,,,,203,,,,,,203,,,11,203', -',,,,11,,11,,11,11,,11,11,11,,11,11,,,,,11,11,174,174,11,,174,11,174', -',,,,,,11,,,,,,11,,,174,11,,,,,174,,174,,174,174,,174,174,174,,174,174', -',,,,174,174,173,173,174,,173,174,173,,,,,,,174,154,,,,,174,,,173,174', -',,,154,173,154,173,154,173,173,,173,173,173,,173,173,155,,,,173,173', -',,173,,,173,154,155,,155,,155,,173,154,154,154,154,,173,,154,154,173', -',,,,154,,,,,155,,,,154,,156,,155,155,155,155,155,155,,155,155,,,156', -',156,155,156,,,,,,,,155,,,,,,,,,,,,,,156,,,,,,157,,156,156,156,156,156', -'156,,156,156,,157,157,,157,156,157,,,157,,,,,156,,,,,,,,,,,,,,157,,', -',,,158,,157,157,157,157,157,157,,157,157,,158,158,,158,157,158,,,158', -',,,,157,,,,,,,,,,,,,,158,,,,,,159,,158,158,158,158,158,158,,158,158', -',159,159,,159,158,159,,,159,,,,,158,,,,,,,,,,,,,,159,,,,,,160,,159,159', -'159,159,159,159,,159,159,,160,160,,160,159,160,,,160,,,,,159,,,,,,,', -',,,,,,160,,,,,,161,,160,160,160,160,160,160,,160,160,161,161,161,,161', -'160,161,,,161,161,161,161,,160,,,,,,,,,,,,,,161,,,,,,162,,161,161,161', -'161,161,161,,161,161,162,162,162,,162,161,162,,,162,162,162,162,,161', -',,,,,,,,,,342,342,,162,342,,342,342,162,,,162,162,162,162,162,162,,162', -'162,,,342,,,162,,,342,,342,,342,342,162,342,342,342,,342,342,342,342', -',,342,342,344,344,342,,344,342,344,344,,,,,,342,,,,,,342,,,344,342,', -',,,344,,344,,344,344,,344,344,344,,344,344,344,344,,,344,344,4,4,344', -',4,344,4,,,,,,,344,,,,,,344,,,4,344,,,,,4,,4,,4,4,,4,4,4,4,4,4,4,4,', -',4,4,345,345,4,,345,4,345,345,,,,,,4,,,,,,4,,,345,4,,,,,345,,345,,345', -'345,,345,345,345,,345,345,345,345,,,345,345,169,169,345,,169,345,169', -',,,,,,345,,,,,,345,,,169,345,,,,,169,,169,,169,169,,169,169,169,,169', -'169,,,,,169,169,0,0,169,,0,169,0,,,,,,,169,,,,,,169,,,0,169,,,,,0,,0', -',0,0,,0,0,0,,0,0,0,0,,,0,0,297,297,0,,297,0,297,,,,,,,0,,,,,,0,,,297', -'0,,,,,297,,297,,297,297,,297,297,297,,297,297,,,,,297,297,,,297,,,297', -',,,239,239,239,239,297,239,239,239,239,239,297,239,239,,297,,,,,239', -'239,239,194,194,194,194,,194,194,194,194,194,,194,194,,,239,239,,,194', -'194,194,244,244,244,244,,244,244,244,244,244,,244,244,,,194,194,,,244', -'244,244,,,,,,,,,,,,,,,,244,244' ] - racc_action_check = arr = ::Array.new(5345, nil) +'0,0,189,211,0,286,0,47,47,286,131,147,286,220,151,131,147,211,188,188', +'182,190,220,0,184,107,139,139,151,0,151,0,151,0,0,185,0,0,0,210,0,0', +'0,0,189,47,0,0,47,141,0,351,351,0,151,351,188,351,351,188,107,0,182', +'190,151,151,184,0,47,151,151,0,278,129,351,0,151,185,129,188,351,278', +'351,43,351,351,151,351,351,351,242,351,351,351,351,178,186,351,351,121', +'186,351,4,4,351,275,4,275,4,223,223,43,351,46,246,43,152,46,351,187', +'225,225,351,187,121,4,351,317,179,317,152,4,152,4,152,4,4,248,4,4,4', +'4,4,4,4,4,123,223,4,4,223,226,4,350,350,4,152,350,225,350,350,225,134', +'4,101,134,152,152,101,4,223,152,152,4,120,252,350,4,152,115,266,225', +'350,268,350,270,350,350,152,350,350,350,271,350,350,350,350,199,274', +'350,350,222,276,350,348,348,350,277,348,221,348,348,106,281,350,282', +'283,284,156,218,350,288,217,97,350,95,66,348,350,64,302,201,156,348', +'156,348,156,348,348,304,348,348,348,44,348,348,348,348,203,311,348,348', +'312,38,348,206,206,348,156,206,204,206,206,37,320,348,156,156,156,156', +'321,348,323,156,156,348,324,328,206,348,156,329,36,335,206,336,206,339', +'206,206,156,206,206,206,208,206,206,206,206,5,1,206,206,354,358,206', +'11,11,206,100,11,360,11,362,,,206,,,,157,,206,100,,100,206,100,,11,206', +',,,157,11,157,11,157,11,11,,11,11,11,,11,11,,100,,,11,11,,,11,12,12', +'11,157,12,,12,,,,11,157,157,157,157,,11,,157,157,11,,,12,11,157,,,,12', +',12,,12,12,157,12,12,12,,12,12,7,7,7,7,12,12,,,12,13,13,12,153,13,,13', +',,,12,,,,149,,12,153,,153,12,153,,13,12,,,,149,13,149,13,149,13,13,', +'13,13,13,,13,13,,153,,,13,13,,,13,331,331,13,149,331,,331,153,153,,13', +',,,153,,13,,,,13,,,331,13,149,,,,331,,331,,331,331,,331,331,331,,331', +'331,331,331,,,331,331,,,331,319,319,331,148,319,,319,,,,331,,,,155,', +'331,148,,148,331,148,,319,331,,,,155,319,155,319,155,319,319,,319,319', +'319,,319,319,,148,,,319,319,,,319,214,214,319,155,214,,214,214,,,319', +',,,154,,319,,155,155,319,,,214,319,155,,,154,214,154,214,154,214,214', +',214,214,214,,214,214,214,214,,,214,214,,,214,39,39,214,154,39,,39,', +',,214,,,,150,,214,,154,154,214,,,39,214,154,,,150,39,150,39,150,39,39', +',39,39,39,,39,39,,,,,39,39,,,39,40,40,39,150,40,,40,,,,39,,,,102,,39', +',,,39,,,40,39,150,,,102,40,102,40,102,40,40,,40,40,40,,40,40,,,,,40', +'40,,,40,41,41,40,102,41,,41,,,,40,,,,,,40,,,,40,,,41,40,,,,,41,,41,', +'41,41,,41,41,41,,41,41,,,,,41,41,,,41,42,42,41,,42,,42,,,,41,,,,,,41', +',,,41,,,42,41,,,,,42,,42,,42,42,,42,42,42,,42,42,,,,,42,42,,,42,215', +'215,42,,215,,215,,,,42,,,,,,42,,,,42,,,215,42,,,,,215,,215,,215,215', +',215,215,215,,215,215,,,,,215,215,,,215,216,216,215,,216,,216,,,,215', +',,,,,215,,,,215,,,216,215,,,,,216,216,216,216,216,216,216,216,216,216', +',216,216,,,,,216,216,216,216,216,303,303,216,,303,,303,,,,216,,,,,216', +'216,,,,216,,,303,216,,,,,303,,303,,303,303,,303,303,303,,303,303,,,', +',303,303,,,303,227,227,303,,227,227,227,,,,303,,,,,,303,,,,303,,,227', +'303,,,,,227,,227,,227,227,,227,227,227,,227,227,,,,,227,227,,,227,49', +'49,227,,49,49,49,,,,227,,,,,,227,,,,227,,,49,227,,,,,49,,49,,49,49,', +'49,49,49,,49,49,,,,,49,49,,,49,50,50,49,,50,50,50,,,,49,,,,,,49,,,,49', +',,50,49,,,,,50,,50,,50,50,,50,50,50,,50,50,,,,,50,50,,,50,51,51,50,', +'51,,51,51,,,50,,,,,,50,,,,50,,,51,50,,,,,51,,51,,51,51,,51,51,51,,51', +'51,,,,,51,51,,,51,55,55,51,,55,,55,,,,51,,,,,,51,,,,51,,,55,51,,,,,55', +',55,,55,55,,55,55,55,,55,55,,,,,55,55,,,55,229,229,55,,229,229,229,', +',,55,,,,,,55,,,,55,,,229,55,,,,,229,,229,,229,229,,229,229,229,,229', +'229,,,,,229,229,,,229,65,65,229,,65,,65,,,,229,,,,,,229,,,,229,,,65', +'229,,,,,65,,65,,65,65,,65,65,65,,65,65,65,65,,,65,65,,,65,231,231,65', +',231,,231,,,,65,,,,,,65,,,,65,,,231,65,,,,,231,,231,,231,231,,231,231', +'231,,231,231,,,,,231,231,,,231,67,67,231,,67,,67,,,,231,,,,,,231,,,', +'231,,,67,231,,,,,67,,67,,67,67,,67,67,67,,67,67,67,67,,,67,67,,,67,68', +'68,67,,68,,68,,,,67,,,,,,67,,,,67,,,68,67,,,,,68,,68,,68,68,,68,68,68', +',68,68,68,68,,,68,68,,,68,69,69,68,,69,,69,,,,68,,,,,,68,,,,68,,,69', +'68,,,,,69,,69,,69,69,,69,69,69,,69,69,69,69,,,69,69,,,69,70,70,69,,70', +',70,,,,69,,,,,,69,,,,69,,,70,69,,,,,70,,70,,70,70,,70,70,70,,70,70,70', +'70,,,70,70,,,70,71,71,70,,71,,71,,,,70,,,,,,70,,,,70,,,71,70,,,,,71', +',71,,71,71,,71,71,71,,71,71,71,71,,,71,71,,,71,247,247,71,,247,,247', +',,,71,,,,,,71,,,,71,,,247,71,,,,,247,,247,,247,247,,247,247,247,,247', +'247,,,,,247,247,,,247,73,73,247,,73,,73,,,,247,,,,,,247,,,,247,,,73', +'247,,,,,73,,73,,73,73,,73,73,73,,73,73,,,,,73,73,,,73,74,74,73,,74,', +'74,,,,73,,,,,,73,,,,73,,,74,73,,,,,74,,74,,74,74,,74,74,74,,74,74,,', +',,74,74,,,74,75,75,74,,75,,75,,,,74,,,,,,74,,,,74,,,75,74,,,,,75,,75', +',75,75,,75,75,75,,75,75,,,,,75,75,,,75,76,76,75,,76,,76,,,,75,,,,,,75', +',,,75,,,76,75,,,,,76,,76,,76,76,,76,76,76,,76,76,,,,,76,76,,,76,77,77', +'76,,77,,77,,,,76,,,,,,76,,,,76,,,77,76,,,,,77,,77,,77,77,,77,77,77,', +'77,77,,,,,77,77,,,77,78,78,77,,78,,78,,,,77,,,,,,77,,,,77,,,78,77,,', +',,78,,78,,78,78,,78,78,78,,78,78,,,,,78,78,,,78,79,79,78,,79,,79,,,', +'78,,,,,,78,,,,78,,,79,78,,,,,79,,79,,79,79,,79,79,79,,79,79,,,,,79,79', +',,79,80,80,79,,80,,80,,,,79,,,,,,79,,,,79,,,80,79,,,,,80,,80,,80,80', +',80,80,80,,80,80,,,,,80,80,,,80,81,81,80,,81,,81,,,,80,,,,,,80,,,,80', +',,81,80,,,,,81,,81,,81,81,,81,81,81,,81,81,,,,,81,81,,,81,82,82,81,', +'82,,82,,,,81,,,,,,81,,,,81,,,82,81,,,,,82,,82,,82,82,,82,82,82,,82,82', +',,,,82,82,,,82,83,83,82,,83,,83,,,,82,,,,,,82,,,,82,,,83,82,,,,,83,', +'83,,83,83,,83,83,83,,83,83,,,,,83,83,,,83,84,84,83,,84,,84,,,,83,,,', +',,83,,,,83,,,84,83,,,,,84,,84,,84,84,,84,84,84,,84,84,,,,,84,84,,,84', +'85,85,84,,85,,85,,,,84,,,,,,84,,,,84,,,85,84,,,,,85,,85,,85,85,,85,85', +'85,,85,85,,,,,85,85,,,85,86,86,85,,86,,86,,,,85,,,,,,85,,,,85,,,86,85', +',,,,86,,86,,86,86,,86,86,86,,86,86,,,,,86,86,,,86,87,87,86,,87,,87,', +',,86,,,,,,86,,,,86,,,87,86,,,,,87,,87,,87,87,,87,87,87,,87,87,,,,,87', +'87,,,87,88,88,87,,88,,88,,,,87,,,,,,87,,,,87,,,88,87,,,,,88,,88,,88', +'88,,88,88,88,,88,88,,,,,88,88,,,88,89,89,88,,89,,89,,,,88,,,,,,88,,', +',88,,,89,88,,,,,89,,89,,89,89,,89,89,89,,89,89,,,,,89,89,,,89,90,90', +'89,,90,,90,,,,89,,,,,,89,,,,89,,,90,89,,,,,90,,90,,90,90,,90,90,90,', +'90,90,,,,,90,90,,,90,91,91,90,,91,,91,,,,90,,,,,,90,,,,90,,,91,90,,', +',,91,,91,,91,91,,91,91,91,,91,91,,,,,91,91,,,91,92,92,91,,92,,92,,,', +'91,,,,,,91,,,,91,,,92,91,,,,,92,,92,,92,92,,92,92,92,,92,92,,,,,92,92', +',,92,93,93,92,,93,,93,,,,92,,,,,,92,,,,92,,,93,92,,,,,93,,93,,93,93', +',93,93,93,,93,93,,,,,93,93,,,93,94,94,93,,94,,94,,,,93,,,,,,93,,,,93', +',,94,93,,,,,94,,94,,94,94,,94,94,94,,94,94,,,,,94,94,,,94,177,177,94', +',177,,177,,,,94,,,,,,94,,,94,94,,,177,94,,,,,177,,177,,177,177,,177', +'177,177,,177,177,,,,,177,177,,,177,96,96,177,,96,,96,,,,177,,,,,,177', +',,,177,,,96,177,,,,,96,96,96,96,96,96,96,96,96,96,,96,96,,,,,96,96,96', +'96,96,290,290,96,,290,,290,290,,,96,,,,,96,96,,,,96,,,290,96,,,,,290', +',290,,290,290,,290,290,290,,290,290,290,290,,,290,290,,,290,98,98,290', +',98,,98,,,,290,,,,,,290,,,,290,,,98,290,,,,,98,,98,,98,98,,98,98,98', +',98,98,,,,,98,98,,,98,99,99,98,,99,,99,,,,98,,,,,,98,,,,98,,,99,98,', +',,,99,,99,,99,99,,99,99,99,,99,99,,,,,99,99,,,99,176,176,99,,176,,176', +',,,99,,,,,,99,,,,99,,,176,99,,,,,176,,176,,176,176,,176,176,176,,176', +'176,,,,,176,176,,,176,289,289,176,,289,,289,,,,176,,,,,,176,,,,176,', +',289,176,,,,,289,,289,,289,289,,289,289,289,,289,289,289,289,,,289,289', +',,289,175,175,289,,175,,175,,,,289,,,,,,289,,,,289,,,175,289,,,,,175', +',175,,175,175,,175,175,175,,175,175,,,,,175,175,,,175,172,172,175,,172', +',172,,,,175,,,,,,175,,,,175,,,172,175,,,,,172,,172,,172,172,,172,172', +'172,,172,172,,,,,172,172,,,172,,,172,104,104,,,104,,104,172,,,,,,172', +',,,172,,,,172,,104,104,,,,,104,,104,,104,104,,104,104,104,,104,104,', +',,,104,104,,,104,279,279,104,,279,,279,279,,,104,,,,,,104,,,,104,,,279', +'104,,,,,279,,279,,279,279,,279,279,279,,279,279,,,,,279,279,,,279,272', +'272,279,,272,,272,,,,279,,,,,,279,,,,279,,,272,279,,,,,272,,272,,272', +'272,,272,272,272,,272,272,,,,,272,272,,,272,,,272,108,108,,,108,,108', +'272,,,,,,272,,,,272,,,,272,,108,108,,,,,108,,108,,108,108,,108,108,108', +',108,108,,,,,108,108,,,108,233,233,108,,233,,233,233,,,108,,,,,,108', +',,,108,,,233,108,,,,,233,,233,,233,233,,233,233,233,,233,233,,,,,233', +'233,,,233,238,238,233,,238,,238,,,,233,,,,,,233,,,,233,,,238,233,,,', +',238,,238,,238,238,,238,238,238,,238,238,,,,,238,238,,,238,240,240,238', +',240,,240,,,,238,,,,,,238,,,,238,,,240,238,,,,,240,,240,,240,240,,240', +'240,240,,240,240,,,,,240,240,,,240,241,241,240,,241,,241,,,,240,,,,', +',240,,,,240,,,241,240,,,,,241,,241,,241,241,,241,241,241,,241,241,,', +',,241,241,,,241,114,114,241,,114,,114,,,,241,,,,,,241,,,,241,,,114,241', +',,,,114,,114,,114,114,,114,114,114,,114,114,,,,,114,114,,,114,265,265', +'114,,265,,265,,,,114,,,,,,114,,,,114,,,265,114,,,,,265,,265,,265,265', +',265,265,265,,265,265,,,,,265,265,,,265,251,251,265,,251,,251,,,,265', +',,,,,265,,,,265,,,251,265,,,,,251,,251,,251,251,,251,251,251,,251,251', +',,,,251,251,,,251,250,250,251,,250,,250,,,,251,,,,,,251,,,,251,,,250', +'251,,,,,250,,250,,250,250,,250,250,250,,250,250,,,,,250,250,,,250,213', +'213,250,,213,,213,213,,,250,,,,,,250,,,,250,,,213,250,,,,,213,,213,', +'213,213,,213,213,213,,213,213,213,213,,,213,213,,,213,72,72,213,,72', +',72,,,,213,,,,,,213,,,,213,,,72,213,,,,,72,,72,,72,72,,72,72,72,,72', +'72,,,,,72,72,,,72,,,72,,,,,,,,72,,,,,,72,128,,,72,,,,72,,128,128,128', +'128,128,128,,128,,128,,,128,128,128,128,,,,,,,,,,,,,,,,128,,,,128,128', +',,128,128,128,128,128,128,,128,128,10,,10,,,128,,,,10,10,10,10,10,10', +'128,10,,10,,,10,10,10,10,,,,,,,,,,,,,,,,10,,,,10,10,,,10,10,10,10,10', +'10,,10,10,,,,,,10,,133,,,,,133,,,10,133,133,133,133,133,133,,133,,133', +',,133,133,133,133,,,,,,,,,,,,,,,,133,,,,133,133,,,133,133,133,133,133', +'133,,133,133,113,,113,,,133,,,,113,113,113,113,113,113,133,113,,113', +',,113,113,113,113,,,,,,,,,,,,,,,,113,,,,113,113,,,113,113,113,113,113', +'113,,113,113,137,,,,,113,,,,137,137,137,137,137,137,113,137,,137,,,137', +'137,137,137,,,,,,,,,,,,,,,,137,,,,137,137,,,137,137,137,137,137,137', +',137,137,112,,112,,,137,,,,112,112,112,112,112,112,137,112,,112,,,112', +'112,112,112,,,,,,,,,,,,,,,,112,,,,112,112,,,112,112,112,112,112,112', +',112,112,111,,111,,,112,,,,111,111,111,111,111,111,112,111,,111,,,111', +'111,111,111,,,,,,,,,,,,,,,,111,,,,111,111,,,111,111,111,111,111,111', +',111,111,109,,109,,,111,,,,109,109,109,109,109,109,111,109,,109,,,109', +'109,109,109,,,,,,,,,,,,,,,,109,,,,109,109,,,109,109,109,109,109,109', +',109,109,316,,,,,109,,,,316,316,316,316,316,316,109,316,,316,,,316,316', +'316,316,,,,,,,,,,,,,,,,316,,,,316,316,,,316,316,316,316,316,316,,316', +'316,310,,,,,316,,,,310,310,310,310,310,310,316,310,,310,,,310,310,310', +'310,,,,,,,,,,,,,,,,310,,,,310,310,,,310,310,310,310,310,310,,310,310', +'309,,,,,310,,,,309,309,309,309,309,309,310,309,,309,,,309,309,309,309', +',,,,,,,,,,,,,,,309,,,,309,309,,,309,309,309,309,309,309,,309,309,198', +',,,,309,,,,198,198,198,198,198,198,309,198,,198,,,198,198,198,198,,', +',,,,,,,,,,,,,198,,,,198,198,,,198,198,198,198,198,198,,198,198,301,', +',,,198,,,,301,301,301,301,301,301,198,301,,301,,,301,301,301,301,,,', +',,,,,,,,,,,,301,,,,301,301,,,301,301,301,301,301,301,,301,301,297,,', +',,301,,,,297,297,297,297,297,297,301,297,,297,,,297,297,297,297,158', +',,,,,,,,,,,,,158,297,158,,158,297,297,,,297,297,297,297,297,297,,297', +'297,,,,,,297,,,158,,,159,,,,297,158,158,158,158,158,158,,158,158,159', +',159,,159,158,,,,,,160,,,,158,,,,,,,,,160,160,159,160,,160,,,160,,159', +'159,159,159,159,159,,159,159,,,,,,159,,,160,,,,,161,,159,160,160,160', +'160,160,160,,160,160,,161,161,,161,160,161,,,161,,,,162,,160,,,,,,,', +',,,162,162,161,162,,162,,,162,,161,161,161,161,161,161,,161,161,,,,', +',161,,,162,,,,,163,,161,162,162,162,162,162,162,,162,162,,163,163,,163', +'162,163,,,163,,,,,,162,,,,,,,,,,,,,163,,,,,164,,,163,163,163,163,163', +'163,,163,163,164,164,164,,164,163,164,,,164,164,164,164,,,163,,,,,,', +',,,,,,164,,,,,165,,,164,164,164,164,164,164,,164,164,165,165,165,,165', +'164,165,,,165,165,165,165,,,164,,,,,,,,,,,,,165,,,,,165,,,165,165,165', +'165,165,165,,165,165,294,,,,,165,,,,294,294,294,294,294,294,165,294', +',294,,,294,294,294,294,,,,,,,,,,,,,,,,294,,,,294,294,,,294,294,294,294', +'294,294,,294,294,167,,,,,294,,,,167,167,167,167,167,167,294,167,,167', +',,167,167,167,167,,,,,,,,,,,,,,,,167,,,,167,167,,,167,167,167,167,167', +'167,,167,167,168,,,,,167,,,,168,168,168,168,168,168,167,168,,168,,,168', +'168,168,168,,,,,,,,,,,,,,,,168,,,,168,168,,,168,168,168,168,168,168', +',168,168,,,,,,168,,169,,,,,169,,,168,169,169,169,169,169,169,,169,,169', +',,169,169,169,169,,,,,,,,,,,,,,,,169,,,,169,169,,,169,169,169,169,169', +'169,,169,169,103,,,,,169,,,,103,103,103,103,103,103,169,103,,103,,103', +'103,103,103,103,,,,,,,,,,,,,,,,103,,,,103,103,,,103,103,103,103,103', +'103,,103,103,,,,,,103,,174,,,,,,,,103,174,174,174,174,174,174,174,174', +',174,,,174,174,174,174,,,,,,,,,,,,,,,,174,,,,174,174,,,174,174,174,174', +'174,174,,174,174,166,,,,,174,,,,166,166,166,166,166,166,174,166,,166', +',,166,166,166,166,,,,,,,,,,,,,,,,166,,,,166,166,,,166,166,166,166,166', +'166,,166,166,,197,197,197,197,166,197,197,197,197,197,,197,197,,166', +',,,,197,197,197,244,244,244,244,,244,244,244,244,244,,244,244,,,197', +'197,,,244,244,244,249,249,249,249,,249,249,249,249,249,,249,249,,,244', +'244,,,249,249,249,,,,,,,,,,,,,,,,249,249' ] + racc_action_check = arr = ::Array.new(5601, nil) idx = 0 clist.each do |str| str.split(',', -1).each do |i| @@ -386,235 +402,227 @@ clist = [ end racc_action_pointer = [ - 5157, 258, nil, nil, 5016, 245, nil, 128, nil, nil, - 309, 4436, 4104, 4010, nil, nil, nil, nil, nil, nil, + -2, 300, nil, nil, 100, 287, nil, 335, nil, nil, + 4052, 304, 355, 406, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, 227, -43, 207, 3587, - 3540, 3493, 3252, -1, 183, nil, 86, 3, nil, 1413, - 4151, nil, nil, nil, 1363, nil, nil, nil, nil, nil, - nil, nil, nil, 221, 1460, 208, 1554, 1601, 1648, 1695, - 1742, 1789, 1836, 1883, 1930, 1977, 2024, 2071, 2118, 2165, - 2212, 2259, 2306, 2353, 2400, 2447, 2494, 2541, 2588, 2635, - 2682, 2729, 2776, 2823, 180, 2920, 210, 3017, 3064, 134, - -31, 139, 939, 3302, nil, 148, -32, 3446, 876, nil, - 813, 750, 624, 3681, 85, nil, nil, nil, nil, 16, - -11, nil, 166, nil, nil, nil, nil, 561, 93, nil, - 498, 92, nil, nil, 435, nil, 167, nil, 176, nil, - nil, nil, nil, nil, 96, 145, 75, 12, 1373, 3262, - 1788, 1506, 1268, 3406, 4540, 4567, 4612, 4657, 4702, 4747, - 4792, 4837, 4882, 246, 183, 120, 57, nil, nil, 5110, - nil, -6, 1507, 4530, 4483, 187, 210, nil, nil, 45, - nil, 8, 47, 93, 84, 25, 25, 30, nil, nil, - nil, nil, nil, nil, 5254, 372, 159, nil, 139, nil, - 132, 53, nil, 4389, nil, 47, nil, 220, -9, nil, - 4342, 4295, 4248, 4198, 184, 198, nil, 28, 199, 248, - 88, nil, 27, 177, 1316, nil, 4057, nil, 3963, nil, - nil, nil, nil, 3869, nil, 3822, 3775, 34, nil, 5232, - nil, 84, 3728, 107, 5276, 3396, 3349, 147, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - 3205, 138, nil, 158, nil, 113, 149, 3158, nil, 177, - 121, 186, 172, 12, 3111, nil, 168, 201, 205, 207, - nil, 53, nil, 205, 2970, 2873, nil, nil, nil, 1128, - nil, 1254, nil, nil, nil, 1191, 214, 5204, 213, nil, - nil, nil, nil, 1065, 1002, 220, 161, nil, nil, nil, - 687, 98, nil, 3634, 234, 214, nil, 238, 239, nil, - nil, nil, 239, 240, nil, 3916, nil, nil, nil, 228, - 245, nil, nil, 246, nil, nil, nil, nil, nil, nil, - nil, nil, 4922, nil, 4969, 5063, nil, nil, 257, nil, - nil, nil, 259, nil, 261, nil, 262, nil, nil, nil, - nil, nil ] + nil, nil, nil, nil, nil, nil, 257, 193, 228, 610, + 661, 712, 763, 75, 199, nil, 77, 5, nil, 1018, + 1069, 1120, nil, nil, nil, 1171, nil, nil, nil, nil, + nil, nil, nil, nil, 229, 1273, 214, 1375, 1426, 1477, + 1528, 1579, 3931, 1681, 1732, 1783, 1834, 1885, 1936, 1987, + 2038, 2089, 2140, 2191, 2242, 2293, 2344, 2395, 2446, 2497, + 2548, 2599, 2650, 2701, 2752, 185, 2854, 215, 2956, 3007, + 303, 128, 671, 5333, 3265, nil, 204, -10, 3421, 4401, + nil, 4344, 4287, 4173, 3676, 154, nil, nil, nil, nil, + 149, 87, nil, 126, nil, nil, nil, nil, 3995, 66, + nil, 3, nil, 4116, 153, nil, nil, 4230, nil, 22, + nil, 37, nil, nil, nil, nil, nil, 4, 507, 416, + 620, 8, 110, 405, 569, 518, 212, 314, 4768, 4811, + 4836, 4881, 4906, 4951, 4996, 5041, 5454, 5155, 5212, 5276, + nil, nil, 3211, nil, 5397, 3160, 3058, 2803, 54, 116, + nil, nil, 9, nil, 13, 24, 60, 83, 16, -9, + 10, nil, nil, nil, nil, nil, nil, 5487, 4629, 150, + nil, 208, nil, 236, 190, nil, 253, nil, 282, nil, + 24, -9, nil, 3880, 559, 814, 865, 181, 182, nil, + -13, 201, 193, 107, nil, 118, 111, 967, nil, 1222, + nil, 1324, nil, 3472, nil, nil, nil, nil, 3523, nil, + 3574, 3625, 78, nil, 5509, nil, 105, 1630, 128, 5531, + 3829, 3778, 163, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, 3727, 154, nil, 174, nil, + 115, 157, 3367, nil, 189, 73, 193, 175, 60, 3316, + nil, 172, 203, 207, 209, nil, -31, nil, 209, 3109, + 2905, nil, nil, nil, 5098, nil, nil, 4743, nil, nil, + nil, 4686, 221, 916, 227, nil, nil, nil, nil, 4572, + 4515, 240, 182, nil, nil, nil, 4458, 95, nil, 508, + 256, 238, nil, 264, 268, nil, nil, nil, 268, 272, + nil, 457, nil, nil, nil, 257, 276, nil, nil, 278, + nil, nil, nil, nil, nil, nil, nil, nil, 202, nil, + 151, 49, nil, nil, 294, nil, nil, nil, 295, nil, + 302, nil, 304, nil, nil, nil, nil, nil ] racc_action_default = [ - -208, -209, -1, -2, -3, -4, -7, -9, -10, -15, - -105, -209, -209, -209, -44, -45, -46, -47, -48, -49, + -211, -212, -1, -2, -3, -4, -7, -9, -10, -15, + -105, -212, -212, -212, -44, -45, -46, -47, -48, -49, -50, -51, -52, -53, -54, -55, -56, -57, -58, -59, - -60, -61, -62, -63, -64, -65, -70, -71, -75, -209, - -209, -209, -209, -209, -115, -117, -209, -209, -162, -209, - -209, -172, -173, -174, -209, -176, -183, -184, -185, -186, - -187, -188, -189, -209, -209, -6, -209, -209, -209, -209, - -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, - -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, - -209, -209, -209, -209, -209, -124, -119, -208, -208, -27, - -209, -34, -209, -209, -72, -209, -209, -209, -209, -82, - -209, -209, -209, -209, -208, -134, -153, -154, -116, -208, - -208, -143, -145, -146, -147, -148, -149, -42, -209, -165, - -209, -209, -168, -169, -180, -175, -209, 362, -5, -8, - -11, -12, -13, -14, -209, -17, -18, -19, -20, -21, - -22, -23, -24, -25, -26, -28, -29, -30, -31, -32, - -33, -35, -36, -37, -38, -39, -209, -40, -100, -209, - -76, -209, -201, -207, -195, -192, -190, -113, -125, -184, - -128, -188, -209, -198, -196, -204, -186, -187, -194, -199, - -200, -202, -203, -205, -124, -123, -209, -122, -209, -41, - -190, -67, -77, -209, -80, -190, -158, -161, -209, -74, - -209, -209, -209, -124, -192, -208, -155, -209, -209, -209, - -209, -151, -209, -209, -209, -163, -209, -166, -209, -177, - -178, -179, -181, -209, -16, -209, -209, -190, -102, -124, - -112, -209, -193, -209, -191, -209, -209, -190, -127, -129, - -195, -196, -197, -198, -201, -204, -206, -207, -120, -121, - -191, -209, -69, -209, -79, -209, -191, -209, -73, -209, - -85, -209, -91, -209, -209, -95, -192, -190, -209, -209, - -137, -209, -156, -190, -208, -209, -144, -152, -150, -43, - -164, -171, -167, -170, -182, -104, -209, -191, -190, -108, - -114, -109, -126, -130, -131, -209, -66, -78, -81, -159, - -160, -85, -84, -209, -209, -91, -90, -209, -209, -99, - -94, -96, -209, -209, -110, -208, -138, -139, -140, -209, - -209, -135, -136, -209, -142, -101, -103, -111, -118, -68, - -83, -86, -209, -89, -209, -209, -106, -107, -209, -157, - -132, -141, -209, -88, -209, -93, -209, -98, -133, -87, - -92, -97 ] + -60, -61, -62, -63, -64, -65, -70, -71, -75, -212, + -212, -212, -212, -212, -115, -117, -212, -212, -162, -212, + -212, -212, -175, -176, -177, -212, -179, -186, -187, -188, + -189, -190, -191, -192, -212, -212, -6, -212, -212, -212, + -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, + -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, + -212, -212, -212, -212, -212, -212, -124, -119, -211, -211, + -27, -212, -34, -212, -212, -72, -212, -212, -212, -212, + -82, -212, -212, -212, -212, -211, -134, -153, -154, -116, + -211, -211, -143, -145, -146, -147, -148, -149, -42, -212, + -165, -212, -168, -212, -212, -171, -172, -183, -178, -212, + 368, -5, -8, -11, -12, -13, -14, -212, -17, -18, + -19, -20, -21, -22, -23, -24, -25, -26, -28, -29, + -30, -31, -32, -33, -35, -36, -37, -38, -39, -212, + -40, -100, -212, -76, -212, -204, -210, -198, -195, -193, + -113, -125, -187, -128, -191, -212, -201, -199, -207, -189, + -190, -197, -202, -203, -205, -206, -208, -124, -123, -212, + -122, -212, -41, -193, -67, -77, -212, -80, -193, -158, + -161, -212, -74, -212, -212, -212, -124, -195, -211, -155, + -212, -212, -212, -212, -151, -212, -212, -212, -163, -212, + -166, -212, -169, -212, -180, -181, -182, -184, -212, -16, + -212, -212, -193, -102, -124, -112, -212, -196, -212, -194, + -212, -212, -193, -127, -129, -198, -199, -200, -201, -204, + -207, -209, -210, -120, -121, -194, -212, -69, -212, -79, + -212, -194, -212, -73, -212, -85, -212, -91, -212, -212, + -95, -195, -193, -212, -212, -137, -212, -156, -193, -211, + -212, -144, -152, -150, -43, -164, -167, -174, -170, -173, + -185, -104, -212, -194, -193, -108, -114, -109, -126, -130, + -131, -212, -66, -78, -81, -159, -160, -85, -84, -212, + -212, -91, -90, -212, -212, -99, -94, -96, -212, -212, + -110, -211, -138, -139, -140, -212, -212, -135, -136, -212, + -142, -101, -103, -111, -118, -68, -83, -86, -212, -89, + -212, -212, -106, -107, -212, -157, -132, -141, -212, -88, + -212, -93, -212, -98, -133, -87, -92, -97 ] racc_goto_table = [ - 2, 119, 3, 99, 101, 102, 104, 135, 125, 133, - 168, 176, 175, 205, 241, 123, 316, 312, 215, 330, - 286, 237, 287, 218, 196, 198, 300, 243, 239, 275, - 214, 108, 110, 111, 112, 318, 65, 302, 274, 128, - 341, 127, 130, 202, 279, 326, 134, 140, 141, 142, - 143, 261, 219, 278, 167, 309, 265, 131, 340, 343, - 348, 144, 139, 127, 145, 146, 147, 148, 149, 150, - 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, - 161, 162, 163, 164, 165, 166, 238, 171, 296, 195, - 195, 321, 1, 200, 232, 127, 138, 208, 305, 127, - 233, 231, nil, nil, nil, 171, nil, nil, nil, nil, - 247, nil, nil, nil, nil, 322, 216, nil, nil, nil, - nil, 216, 221, nil, 283, nil, nil, nil, 323, 277, - 276, nil, nil, nil, 329, nil, nil, nil, 119, nil, - nil, nil, nil, nil, nil, nil, 125, nil, nil, 337, - nil, nil, nil, 123, nil, 298, nil, nil, nil, nil, - nil, 166, nil, nil, 108, 110, 111, nil, nil, nil, - 262, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, 125, nil, 125, nil, nil, 294, 293, 123, nil, - 123, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, 263, 127, 171, nil, nil, nil, nil, - 269, 271, nil, nil, 336, nil, 289, 280, 291, nil, - 130, nil, nil, nil, nil, 134, nil, 289, 295, nil, - nil, nil, nil, nil, 171, nil, 327, 303, 304, nil, + 2, 120, 3, 100, 102, 103, 105, 138, 126, 171, + 136, 124, 179, 208, 322, 246, 218, 178, 318, 336, + 242, 221, 143, 144, 145, 146, 248, 291, 280, 292, + 306, 109, 111, 112, 113, 217, 66, 199, 201, 129, + 131, 128, 128, 133, 244, 324, 308, 137, 279, 347, + 266, 205, 284, 332, 283, 270, 222, 170, 349, 315, + 346, 354, 147, 134, 128, 148, 149, 150, 151, 152, + 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, + 163, 164, 165, 166, 167, 168, 169, 243, 174, 302, + 198, 198, 327, 142, 203, 1, 128, 141, 211, 311, + 128, 237, 238, 236, nil, nil, 174, nil, nil, nil, + nil, nil, nil, 252, nil, nil, nil, 219, 328, nil, + nil, nil, 219, 224, nil, nil, 288, nil, nil, 329, + nil, nil, 282, nil, nil, 335, nil, 281, nil, nil, + nil, 120, nil, nil, nil, nil, nil, nil, nil, 126, + nil, 343, 124, nil, nil, nil, nil, nil, nil, nil, + 304, nil, nil, nil, 169, nil, nil, 109, 111, 112, + nil, nil, nil, 267, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, 126, nil, 126, 124, nil, 124, + 300, nil, 299, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, 268, 128, 174, nil, + nil, nil, nil, 274, 276, nil, nil, nil, 342, 294, + 285, 294, nil, 297, nil, 133, nil, nil, nil, nil, + 137, nil, 294, 301, nil, nil, nil, nil, nil, 174, + nil, 333, 309, 310, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, 294, nil, nil, + nil, nil, nil, nil, 316, nil, nil, nil, nil, nil, + nil, 128, nil, nil, nil, nil, nil, nil, nil, nil, + nil, 345, nil, nil, nil, nil, nil, nil, nil, nil, + 339, 338, nil, nil, nil, 169, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, 289, nil, nil, nil, nil, nil, nil, 310, - nil, nil, nil, nil, nil, nil, 127, nil, nil, nil, - nil, nil, nil, nil, nil, 339, nil, nil, nil, nil, - nil, nil, nil, nil, nil, 333, 332, nil, nil, 166, + nil, 109, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, 108, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, 332, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, 352, nil, 354, 356 ] + nil, nil, nil, 338, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, 358, nil, + 360, 362 ] racc_goto_check = [ - 2, 62, 3, 9, 9, 9, 37, 78, 29, 74, - 49, 54, 52, 42, 53, 35, 45, 44, 63, 64, - 70, 50, 70, 63, 58, 58, 55, 36, 56, 47, - 52, 9, 9, 9, 9, 48, 5, 59, 46, 11, - 43, 9, 9, 41, 66, 67, 9, 7, 7, 7, - 7, 36, 69, 53, 12, 72, 36, 73, 44, 45, - 64, 11, 6, 9, 9, 9, 9, 9, 9, 9, + 2, 62, 3, 9, 9, 9, 37, 78, 29, 49, + 74, 35, 54, 42, 45, 53, 63, 52, 44, 64, + 50, 63, 7, 7, 7, 7, 36, 70, 47, 70, + 55, 9, 9, 9, 9, 52, 5, 58, 58, 11, + 11, 9, 9, 9, 56, 48, 59, 9, 46, 43, + 36, 41, 66, 67, 53, 36, 69, 12, 45, 72, + 44, 64, 11, 73, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 49, 9, 36, 9, - 9, 47, 1, 11, 79, 9, 5, 11, 36, 9, - 80, 82, nil, nil, nil, 9, nil, nil, nil, nil, - 54, nil, nil, nil, nil, 53, 3, nil, nil, nil, - nil, 3, 3, nil, 42, nil, nil, nil, 36, 54, - 52, nil, nil, nil, 36, nil, nil, nil, 62, nil, - nil, nil, nil, nil, nil, nil, 29, nil, nil, 36, - nil, nil, nil, 35, nil, 54, nil, nil, nil, nil, - nil, 9, nil, nil, 9, 9, 9, nil, nil, nil, - 37, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, 29, nil, 29, nil, nil, 78, 74, 35, nil, - 35, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, 2, 9, 9, nil, nil, nil, nil, - 2, 2, nil, nil, 49, nil, 9, 3, 9, nil, - 9, nil, nil, nil, nil, 9, nil, 9, 9, nil, - nil, nil, nil, nil, 9, nil, 62, 9, 9, nil, + 9, 9, 9, 9, 9, 9, 9, 49, 9, 36, + 9, 9, 47, 6, 11, 1, 9, 5, 11, 36, + 9, 79, 80, 82, nil, nil, 9, nil, nil, nil, + nil, nil, nil, 54, nil, nil, nil, 3, 53, nil, + nil, nil, 3, 3, nil, nil, 42, nil, nil, 36, + nil, nil, 54, nil, nil, 36, nil, 52, nil, nil, + nil, 62, nil, nil, nil, nil, nil, nil, nil, 29, + nil, 36, 35, nil, nil, nil, nil, nil, nil, nil, + 54, nil, nil, nil, 9, nil, nil, 9, 9, 9, + nil, nil, nil, 37, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, 29, nil, 29, 35, nil, 35, + 78, nil, 74, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, 2, 9, 9, nil, + nil, nil, nil, 2, 2, nil, nil, nil, 49, 9, + 3, 9, nil, 9, nil, 9, nil, nil, nil, nil, + 9, nil, 9, 9, nil, nil, nil, nil, nil, 9, + nil, 62, 9, 9, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, 9, nil, nil, + nil, nil, nil, nil, 9, nil, nil, nil, nil, nil, + nil, 9, nil, nil, nil, nil, nil, nil, nil, nil, + nil, 37, nil, nil, nil, nil, nil, nil, nil, nil, + 2, 3, nil, nil, nil, 9, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, 9, nil, nil, nil, nil, nil, nil, 9, - nil, nil, nil, nil, nil, nil, 9, nil, nil, nil, - nil, nil, nil, nil, nil, 37, nil, nil, nil, nil, - nil, nil, nil, nil, nil, 2, 3, nil, nil, 9, + nil, 9, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, 9, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, 3, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, 2, nil, 2, 2 ] + nil, nil, nil, 3, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, 2, nil, + 2, 2 ] racc_goto_pointer = [ - nil, 92, 0, 2, nil, 32, -4, -20, nil, -8, - nil, -10, -39, nil, nil, nil, nil, nil, nil, nil, + nil, 95, 0, 2, nil, 32, 26, -46, nil, -8, + nil, -10, -37, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, -39, - nil, nil, nil, nil, nil, -32, -149, -31, nil, nil, - nil, -62, -93, -273, -253, -256, -174, -183, -238, -83, - -148, nil, -83, -161, -84, -216, -143, nil, -73, -207, - nil, nil, -45, -96, -265, nil, -171, -236, nil, -68, - -200, nil, -211, 7, -41, nil, nil, nil, -47, -42, - -36, nil, -35 ] + nil, nil, nil, nil, nil, -36, -153, -31, nil, nil, + nil, -55, -94, -270, -257, -263, -167, -187, -233, -85, + -152, nil, -79, -163, -84, -217, -130, nil, -61, -203, + nil, nil, -45, -99, -270, nil, -166, -233, nil, -65, + -196, nil, -212, 12, -41, nil, nil, nil, -48, -38, + -37, nil, -36 ] racc_goto_default = [ - nil, nil, 331, 197, 4, 5, 6, 7, 8, 10, - 9, 273, nil, 14, 36, 15, 16, 17, 18, 19, + nil, nil, 337, 200, 4, 5, 6, 7, 8, 10, + 9, 278, nil, 14, 36, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, nil, nil, 37, 38, - 105, nil, nil, 109, nil, nil, nil, nil, nil, nil, - nil, 42, nil, nil, nil, 177, nil, 96, nil, 178, - 182, 180, 115, nil, nil, 114, nil, nil, 120, nil, - 121, 122, 206, nil, nil, 51, 52, 54, nil, nil, - nil, 136, nil ] + 106, nil, nil, 110, nil, nil, nil, nil, nil, nil, + nil, 42, nil, nil, nil, 180, nil, 97, nil, 181, + 185, 183, 116, nil, nil, 115, nil, nil, 121, nil, + 122, 123, 209, nil, nil, 52, 53, 55, nil, nil, + nil, 139, nil ] racc_reduce_table = [ 0, 0, :racc_error, - 1, 81, :_reduce_1, - 1, 81, :_reduce_none, - 1, 82, :_reduce_3, - 1, 84, :_reduce_4, - 3, 84, :_reduce_5, - 2, 84, :_reduce_6, - 1, 85, :_reduce_7, - 3, 85, :_reduce_8, - 1, 86, :_reduce_none, - 1, 87, :_reduce_10, - 3, 87, :_reduce_11, - 3, 87, :_reduce_12, - 3, 87, :_reduce_13, - 3, 87, :_reduce_14, - 1, 89, :_reduce_none, - 4, 89, :_reduce_16, - 3, 89, :_reduce_17, - 3, 89, :_reduce_18, - 3, 89, :_reduce_19, - 3, 89, :_reduce_20, - 3, 89, :_reduce_21, - 3, 89, :_reduce_22, - 3, 89, :_reduce_23, - 3, 89, :_reduce_24, - 3, 89, :_reduce_25, - 3, 89, :_reduce_26, - 2, 89, :_reduce_27, - 3, 89, :_reduce_28, - 3, 89, :_reduce_29, - 3, 89, :_reduce_30, - 3, 89, :_reduce_31, - 3, 89, :_reduce_32, - 3, 89, :_reduce_33, - 2, 89, :_reduce_34, - 3, 89, :_reduce_35, - 3, 89, :_reduce_36, - 3, 89, :_reduce_37, - 3, 89, :_reduce_38, - 3, 89, :_reduce_39, - 3, 89, :_reduce_40, - 3, 89, :_reduce_41, - 1, 91, :_reduce_42, - 3, 91, :_reduce_43, + 1, 82, :_reduce_1, + 1, 82, :_reduce_none, + 1, 83, :_reduce_3, + 1, 85, :_reduce_4, + 3, 85, :_reduce_5, + 2, 85, :_reduce_6, + 1, 86, :_reduce_7, + 3, 86, :_reduce_8, + 1, 87, :_reduce_none, + 1, 88, :_reduce_10, + 3, 88, :_reduce_11, + 3, 88, :_reduce_12, + 3, 88, :_reduce_13, + 3, 88, :_reduce_14, 1, 90, :_reduce_none, - 1, 94, :_reduce_none, - 1, 94, :_reduce_none, - 1, 94, :_reduce_none, - 1, 94, :_reduce_none, - 1, 94, :_reduce_none, - 1, 94, :_reduce_none, - 1, 94, :_reduce_none, - 1, 94, :_reduce_none, - 1, 94, :_reduce_none, - 1, 94, :_reduce_none, + 4, 90, :_reduce_16, + 3, 90, :_reduce_17, + 3, 90, :_reduce_18, + 3, 90, :_reduce_19, + 3, 90, :_reduce_20, + 3, 90, :_reduce_21, + 3, 90, :_reduce_22, + 3, 90, :_reduce_23, + 3, 90, :_reduce_24, + 3, 90, :_reduce_25, + 3, 90, :_reduce_26, + 2, 90, :_reduce_27, + 3, 90, :_reduce_28, + 3, 90, :_reduce_29, + 3, 90, :_reduce_30, + 3, 90, :_reduce_31, + 3, 90, :_reduce_32, + 3, 90, :_reduce_33, + 2, 90, :_reduce_34, + 3, 90, :_reduce_35, + 3, 90, :_reduce_36, + 3, 90, :_reduce_37, + 3, 90, :_reduce_38, + 3, 90, :_reduce_39, + 3, 90, :_reduce_40, + 3, 90, :_reduce_41, + 1, 92, :_reduce_42, + 3, 92, :_reduce_43, + 1, 91, :_reduce_none, 1, 95, :_reduce_none, 1, 95, :_reduce_none, 1, 95, :_reduce_none, @@ -624,155 +632,168 @@ racc_reduce_table = [ 1, 95, :_reduce_none, 1, 95, :_reduce_none, 1, 95, :_reduce_none, - 1, 110, :_reduce_64, - 1, 110, :_reduce_65, - 5, 93, :_reduce_66, - 3, 93, :_reduce_67, - 6, 93, :_reduce_68, - 4, 93, :_reduce_69, - 1, 93, :_reduce_70, - 1, 97, :_reduce_71, - 2, 97, :_reduce_72, - 4, 118, :_reduce_73, - 3, 118, :_reduce_74, - 1, 118, :_reduce_75, - 3, 119, :_reduce_76, - 2, 117, :_reduce_77, - 3, 121, :_reduce_78, - 2, 121, :_reduce_79, - 2, 120, :_reduce_80, - 4, 120, :_reduce_81, - 2, 100, :_reduce_82, - 5, 123, :_reduce_83, - 4, 123, :_reduce_84, - 0, 124, :_reduce_none, - 2, 124, :_reduce_86, - 4, 124, :_reduce_87, - 3, 124, :_reduce_88, - 6, 101, :_reduce_89, - 5, 101, :_reduce_90, + 1, 95, :_reduce_none, + 1, 96, :_reduce_none, + 1, 96, :_reduce_none, + 1, 96, :_reduce_none, + 1, 96, :_reduce_none, + 1, 96, :_reduce_none, + 1, 96, :_reduce_none, + 1, 96, :_reduce_none, + 1, 96, :_reduce_none, + 1, 96, :_reduce_none, + 1, 111, :_reduce_64, + 1, 111, :_reduce_65, + 5, 94, :_reduce_66, + 3, 94, :_reduce_67, + 6, 94, :_reduce_68, + 4, 94, :_reduce_69, + 1, 94, :_reduce_70, + 1, 98, :_reduce_71, + 2, 98, :_reduce_72, + 4, 119, :_reduce_73, + 3, 119, :_reduce_74, + 1, 119, :_reduce_75, + 3, 120, :_reduce_76, + 2, 118, :_reduce_77, + 3, 122, :_reduce_78, + 2, 122, :_reduce_79, + 2, 121, :_reduce_80, + 4, 121, :_reduce_81, + 2, 101, :_reduce_82, + 5, 124, :_reduce_83, + 4, 124, :_reduce_84, 0, 125, :_reduce_none, - 4, 125, :_reduce_92, - 3, 125, :_reduce_93, - 5, 99, :_reduce_94, - 1, 126, :_reduce_95, - 2, 126, :_reduce_96, - 5, 127, :_reduce_97, - 4, 127, :_reduce_98, - 1, 128, :_reduce_99, - 1, 92, :_reduce_none, - 4, 92, :_reduce_101, - 1, 130, :_reduce_102, - 3, 130, :_reduce_103, - 3, 129, :_reduce_104, - 1, 88, :_reduce_105, - 6, 88, :_reduce_106, - 6, 88, :_reduce_107, - 5, 88, :_reduce_108, - 5, 88, :_reduce_109, - 5, 88, :_reduce_110, - 4, 135, :_reduce_111, - 1, 136, :_reduce_112, - 1, 132, :_reduce_113, - 3, 132, :_reduce_114, - 1, 131, :_reduce_115, - 2, 131, :_reduce_116, - 1, 131, :_reduce_117, - 6, 98, :_reduce_118, - 2, 98, :_reduce_119, - 3, 137, :_reduce_120, - 3, 137, :_reduce_121, - 1, 138, :_reduce_none, - 1, 138, :_reduce_none, - 0, 134, :_reduce_124, - 1, 134, :_reduce_125, - 3, 134, :_reduce_126, - 1, 140, :_reduce_none, - 1, 140, :_reduce_none, - 1, 140, :_reduce_none, - 3, 139, :_reduce_130, - 3, 139, :_reduce_131, - 6, 102, :_reduce_132, - 7, 103, :_reduce_133, - 1, 145, :_reduce_134, - 1, 144, :_reduce_none, - 1, 144, :_reduce_none, - 1, 146, :_reduce_none, - 2, 146, :_reduce_138, + 2, 125, :_reduce_86, + 4, 125, :_reduce_87, + 3, 125, :_reduce_88, + 6, 102, :_reduce_89, + 5, 102, :_reduce_90, + 0, 126, :_reduce_none, + 4, 126, :_reduce_92, + 3, 126, :_reduce_93, + 5, 100, :_reduce_94, + 1, 127, :_reduce_95, + 2, 127, :_reduce_96, + 5, 128, :_reduce_97, + 4, 128, :_reduce_98, + 1, 129, :_reduce_99, + 1, 93, :_reduce_none, + 4, 93, :_reduce_101, + 1, 131, :_reduce_102, + 3, 131, :_reduce_103, + 3, 130, :_reduce_104, + 1, 89, :_reduce_105, + 6, 89, :_reduce_106, + 6, 89, :_reduce_107, + 5, 89, :_reduce_108, + 5, 89, :_reduce_109, + 5, 89, :_reduce_110, + 4, 136, :_reduce_111, + 1, 137, :_reduce_112, + 1, 133, :_reduce_113, + 3, 133, :_reduce_114, + 1, 132, :_reduce_115, + 2, 132, :_reduce_116, + 1, 132, :_reduce_117, + 6, 99, :_reduce_118, + 2, 99, :_reduce_119, + 3, 138, :_reduce_120, + 3, 138, :_reduce_121, + 1, 139, :_reduce_none, + 1, 139, :_reduce_none, + 0, 135, :_reduce_124, + 1, 135, :_reduce_125, + 3, 135, :_reduce_126, + 1, 141, :_reduce_none, + 1, 141, :_reduce_none, + 1, 141, :_reduce_none, + 3, 140, :_reduce_130, + 3, 140, :_reduce_131, + 6, 103, :_reduce_132, + 7, 104, :_reduce_133, + 1, 146, :_reduce_134, + 1, 145, :_reduce_none, + 1, 145, :_reduce_none, 1, 147, :_reduce_none, - 1, 147, :_reduce_none, - 6, 104, :_reduce_141, - 5, 104, :_reduce_142, - 1, 148, :_reduce_143, - 3, 148, :_reduce_144, - 1, 150, :_reduce_145, - 1, 150, :_reduce_146, - 1, 150, :_reduce_147, + 2, 147, :_reduce_138, + 1, 148, :_reduce_none, + 1, 148, :_reduce_none, + 6, 105, :_reduce_141, + 5, 105, :_reduce_142, + 1, 149, :_reduce_143, + 3, 149, :_reduce_144, + 1, 151, :_reduce_145, + 1, 151, :_reduce_146, + 1, 151, :_reduce_147, + 1, 151, :_reduce_none, + 1, 152, :_reduce_149, + 3, 152, :_reduce_150, 1, 150, :_reduce_none, - 1, 151, :_reduce_149, - 3, 151, :_reduce_150, - 1, 149, :_reduce_none, - 2, 149, :_reduce_152, - 1, 142, :_reduce_153, - 1, 142, :_reduce_154, - 1, 143, :_reduce_155, - 2, 143, :_reduce_156, - 4, 143, :_reduce_157, - 1, 122, :_reduce_158, - 3, 122, :_reduce_159, - 3, 152, :_reduce_160, - 1, 152, :_reduce_161, - 1, 96, :_reduce_162, - 3, 105, :_reduce_163, - 4, 105, :_reduce_164, - 2, 105, :_reduce_165, - 3, 108, :_reduce_166, - 4, 108, :_reduce_167, - 2, 108, :_reduce_168, - 1, 153, :_reduce_169, - 3, 153, :_reduce_170, - 3, 154, :_reduce_171, - 1, 115, :_reduce_none, - 1, 115, :_reduce_none, - 1, 155, :_reduce_174, - 2, 156, :_reduce_175, - 1, 157, :_reduce_176, - 1, 159, :_reduce_177, - 1, 160, :_reduce_178, - 2, 158, :_reduce_179, - 1, 161, :_reduce_180, - 1, 162, :_reduce_181, - 2, 162, :_reduce_182, - 1, 111, :_reduce_183, - 1, 114, :_reduce_184, - 1, 112, :_reduce_185, - 1, 113, :_reduce_186, - 1, 107, :_reduce_187, - 1, 106, :_reduce_188, - 1, 109, :_reduce_189, - 0, 116, :_reduce_none, - 1, 116, :_reduce_191, - 0, 133, :_reduce_none, - 1, 133, :_reduce_none, - 1, 141, :_reduce_none, - 1, 141, :_reduce_none, - 1, 141, :_reduce_none, - 1, 141, :_reduce_none, - 1, 141, :_reduce_none, - 1, 141, :_reduce_none, - 1, 141, :_reduce_none, - 1, 141, :_reduce_none, - 1, 141, :_reduce_none, - 1, 141, :_reduce_none, - 1, 141, :_reduce_none, - 1, 141, :_reduce_none, - 1, 141, :_reduce_none, - 1, 141, :_reduce_none, - 0, 83, :_reduce_208 ] + 2, 150, :_reduce_152, + 1, 143, :_reduce_153, + 1, 143, :_reduce_154, + 1, 144, :_reduce_155, + 2, 144, :_reduce_156, + 4, 144, :_reduce_157, + 1, 123, :_reduce_158, + 3, 123, :_reduce_159, + 3, 153, :_reduce_160, + 1, 153, :_reduce_161, + 1, 97, :_reduce_162, + 3, 106, :_reduce_163, + 4, 106, :_reduce_164, + 2, 106, :_reduce_165, + 3, 106, :_reduce_166, + 4, 106, :_reduce_167, + 2, 106, :_reduce_168, + 3, 109, :_reduce_169, + 4, 109, :_reduce_170, + 2, 109, :_reduce_171, + 1, 154, :_reduce_172, + 3, 154, :_reduce_173, + 3, 155, :_reduce_174, + 1, 116, :_reduce_none, + 1, 116, :_reduce_none, + 1, 156, :_reduce_177, + 2, 157, :_reduce_178, + 1, 158, :_reduce_179, + 1, 160, :_reduce_180, + 1, 161, :_reduce_181, + 2, 159, :_reduce_182, + 1, 162, :_reduce_183, + 1, 163, :_reduce_184, + 2, 163, :_reduce_185, + 1, 112, :_reduce_186, + 1, 115, :_reduce_187, + 1, 113, :_reduce_188, + 1, 114, :_reduce_189, + 1, 108, :_reduce_190, + 1, 107, :_reduce_191, + 1, 110, :_reduce_192, + 0, 117, :_reduce_none, + 1, 117, :_reduce_194, + 0, 134, :_reduce_none, + 1, 134, :_reduce_none, + 1, 142, :_reduce_none, + 1, 142, :_reduce_none, + 1, 142, :_reduce_none, + 1, 142, :_reduce_none, + 1, 142, :_reduce_none, + 1, 142, :_reduce_none, + 1, 142, :_reduce_none, + 1, 142, :_reduce_none, + 1, 142, :_reduce_none, + 1, 142, :_reduce_none, + 1, 142, :_reduce_none, + 1, 142, :_reduce_none, + 1, 142, :_reduce_none, + 1, 142, :_reduce_none, + 0, 84, :_reduce_211 ] -racc_reduce_n = 209 +racc_reduce_n = 212 -racc_shift_n = 362 +racc_shift_n = 368 racc_token_table = { false => 0, @@ -792,71 +813,72 @@ racc_token_table = { :FALSE => 14, :EQUALS => 15, :APPENDS => 16, - :LESSEQUAL => 17, - :NOTEQUAL => 18, - :DOT => 19, - :COLON => 20, - :LLCOLLECT => 21, - :RRCOLLECT => 22, - :QMARK => 23, - :LPAREN => 24, - :RPAREN => 25, - :ISEQUAL => 26, - :GREATEREQUAL => 27, - :GREATERTHAN => 28, - :LESSTHAN => 29, - :IF => 30, - :ELSE => 31, - :DEFINE => 32, - :ELSIF => 33, - :VARIABLE => 34, - :CLASS => 35, - :INHERITS => 36, - :NODE => 37, - :BOOLEAN => 38, - :NAME => 39, - :SEMIC => 40, - :CASE => 41, - :DEFAULT => 42, - :AT => 43, - :ATAT => 44, - :LCOLLECT => 45, - :RCOLLECT => 46, - :CLASSREF => 47, - :NOT => 48, - :OR => 49, - :AND => 50, - :UNDEF => 51, - :PARROW => 52, - :PLUS => 53, - :MINUS => 54, - :TIMES => 55, - :DIV => 56, - :LSHIFT => 57, - :RSHIFT => 58, - :UMINUS => 59, - :MATCH => 60, - :NOMATCH => 61, - :REGEX => 62, - :IN_EDGE => 63, - :OUT_EDGE => 64, - :IN_EDGE_SUB => 65, - :OUT_EDGE_SUB => 66, - :IN => 67, - :UNLESS => 68, - :PIPE => 69, - :LAMBDA => 70, - :SELBRACE => 71, - :NUMBER => 72, - :LOW => 73, - :HIGH => 74, - :CALL => 75, - :MODULO => 76, - :DELETES => 77, - :TITLE_COLON => 78, - :CASE_COLON => 79 } + :DELETES => 17, + :LESSEQUAL => 18, + :NOTEQUAL => 19, + :DOT => 20, + :COLON => 21, + :LLCOLLECT => 22, + :RRCOLLECT => 23, + :QMARK => 24, + :LPAREN => 25, + :RPAREN => 26, + :ISEQUAL => 27, + :GREATEREQUAL => 28, + :GREATERTHAN => 29, + :LESSTHAN => 30, + :IF => 31, + :ELSE => 32, + :DEFINE => 33, + :ELSIF => 34, + :VARIABLE => 35, + :CLASS => 36, + :INHERITS => 37, + :NODE => 38, + :BOOLEAN => 39, + :NAME => 40, + :SEMIC => 41, + :CASE => 42, + :DEFAULT => 43, + :AT => 44, + :ATAT => 45, + :LCOLLECT => 46, + :RCOLLECT => 47, + :CLASSREF => 48, + :NOT => 49, + :OR => 50, + :AND => 51, + :UNDEF => 52, + :PARROW => 53, + :PLUS => 54, + :MINUS => 55, + :TIMES => 56, + :DIV => 57, + :LSHIFT => 58, + :RSHIFT => 59, + :UMINUS => 60, + :MATCH => 61, + :NOMATCH => 62, + :REGEX => 63, + :IN_EDGE => 64, + :OUT_EDGE => 65, + :IN_EDGE_SUB => 66, + :OUT_EDGE_SUB => 67, + :IN => 68, + :UNLESS => 69, + :PIPE => 70, + :LAMBDA => 71, + :SELBRACE => 72, + :NUMBER => 73, + :LOW => 74, + :HIGH => 75, + :CALL => 76, + :LISTSTART => 77, + :MODULO => 78, + :TITLE_COLON => 79, + :CASE_COLON => 80 } -racc_nt_base = 80 +racc_nt_base = 81 racc_use_result_var = true @@ -894,6 +916,7 @@ Racc_token_to_s_table = [ "FALSE", "EQUALS", "APPENDS", + "DELETES", "LESSEQUAL", "NOTEQUAL", "DOT", @@ -953,8 +976,8 @@ Racc_token_to_s_table = [ "LOW", "HIGH", "CALL", + "LISTSTART", "MODULO", - "DELETES", "TITLE_COLON", "CASE_COLON", "$start", @@ -1047,7 +1070,7 @@ Racc_debug_parser = false # reduce 0 omitted -module_eval(<<'.,.,', 'egrammar.ra', 58) +module_eval(<<'.,.,', 'egrammar.ra', 59) def _reduce_1(val, _values, result) result = Factory.block_or_expression(*val[0]) result @@ -1056,42 +1079,42 @@ module_eval(<<'.,.,', 'egrammar.ra', 58) # reduce 2 omitted -module_eval(<<'.,.,', 'egrammar.ra', 63) +module_eval(<<'.,.,', 'egrammar.ra', 64) def _reduce_3(val, _values, result) result = transform_calls(val[0]) result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 69) +module_eval(<<'.,.,', 'egrammar.ra', 70) def _reduce_4(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 70) +module_eval(<<'.,.,', 'egrammar.ra', 71) def _reduce_5(val, _values, result) result = val[0].push val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 71) +module_eval(<<'.,.,', 'egrammar.ra', 72) def _reduce_6(val, _values, result) result = val[0].push val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 75) +module_eval(<<'.,.,', 'egrammar.ra', 76) def _reduce_7(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 76) +module_eval(<<'.,.,', 'egrammar.ra', 77) def _reduce_8(val, _values, result) result = aryfy(val[0]).push val[2] result @@ -1100,35 +1123,35 @@ module_eval(<<'.,.,', 'egrammar.ra', 76) # reduce 9 omitted -module_eval(<<'.,.,', 'egrammar.ra', 82) +module_eval(<<'.,.,', 'egrammar.ra', 83) def _reduce_10(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 83) +module_eval(<<'.,.,', 'egrammar.ra', 84) def _reduce_11(val, _values, result) result = val[0].relop(val[1][:value], val[2]); loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 84) +module_eval(<<'.,.,', 'egrammar.ra', 85) def _reduce_12(val, _values, result) result = val[0].relop(val[1][:value], val[2]); loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 85) +module_eval(<<'.,.,', 'egrammar.ra', 86) def _reduce_13(val, _values, result) result = val[0].relop(val[1][:value], val[2]); loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 86) +module_eval(<<'.,.,', 'egrammar.ra', 87) def _reduce_14(val, _values, result) result = val[0].relop(val[1][:value], val[2]); loc result, val[1] result @@ -1137,196 +1160,196 @@ module_eval(<<'.,.,', 'egrammar.ra', 86) # reduce 15 omitted -module_eval(<<'.,.,', 'egrammar.ra', 93) +module_eval(<<'.,.,', 'egrammar.ra', 94) def _reduce_16(val, _values, result) result = val[0][*val[2]] ; loc result, val[0], val[3] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 94) +module_eval(<<'.,.,', 'egrammar.ra', 95) def _reduce_17(val, _values, result) result = val[0].in val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 95) +module_eval(<<'.,.,', 'egrammar.ra', 96) def _reduce_18(val, _values, result) result = val[0] =~ val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 96) +module_eval(<<'.,.,', 'egrammar.ra', 97) def _reduce_19(val, _values, result) result = val[0].mne val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 97) +module_eval(<<'.,.,', 'egrammar.ra', 98) def _reduce_20(val, _values, result) result = val[0] + val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 98) +module_eval(<<'.,.,', 'egrammar.ra', 99) def _reduce_21(val, _values, result) result = val[0] - val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 99) +module_eval(<<'.,.,', 'egrammar.ra', 100) def _reduce_22(val, _values, result) result = val[0] / val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 100) +module_eval(<<'.,.,', 'egrammar.ra', 101) def _reduce_23(val, _values, result) result = val[0] * val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 101) +module_eval(<<'.,.,', 'egrammar.ra', 102) def _reduce_24(val, _values, result) result = val[0] % val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 102) +module_eval(<<'.,.,', 'egrammar.ra', 103) def _reduce_25(val, _values, result) result = val[0] << val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 103) +module_eval(<<'.,.,', 'egrammar.ra', 104) def _reduce_26(val, _values, result) result = val[0] >> val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 104) +module_eval(<<'.,.,', 'egrammar.ra', 105) def _reduce_27(val, _values, result) result = val[1].minus() ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 105) +module_eval(<<'.,.,', 'egrammar.ra', 106) def _reduce_28(val, _values, result) result = val[0].ne val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 106) +module_eval(<<'.,.,', 'egrammar.ra', 107) def _reduce_29(val, _values, result) result = val[0] == val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 107) +module_eval(<<'.,.,', 'egrammar.ra', 108) def _reduce_30(val, _values, result) result = val[0] > val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 108) +module_eval(<<'.,.,', 'egrammar.ra', 109) def _reduce_31(val, _values, result) result = val[0] >= val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 109) +module_eval(<<'.,.,', 'egrammar.ra', 110) def _reduce_32(val, _values, result) result = val[0] < val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 110) +module_eval(<<'.,.,', 'egrammar.ra', 111) def _reduce_33(val, _values, result) result = val[0] <= val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 111) +module_eval(<<'.,.,', 'egrammar.ra', 112) def _reduce_34(val, _values, result) result = val[1].not ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 112) +module_eval(<<'.,.,', 'egrammar.ra', 113) def _reduce_35(val, _values, result) result = val[0].and val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 113) +module_eval(<<'.,.,', 'egrammar.ra', 114) def _reduce_36(val, _values, result) result = val[0].or val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 114) +module_eval(<<'.,.,', 'egrammar.ra', 115) def _reduce_37(val, _values, result) result = val[0].set(val[2]) ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 115) +module_eval(<<'.,.,', 'egrammar.ra', 116) def _reduce_38(val, _values, result) result = val[0].plus_set(val[2]) ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 116) +module_eval(<<'.,.,', 'egrammar.ra', 117) def _reduce_39(val, _values, result) result = val[0].minus_set(val[2]); loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 117) +module_eval(<<'.,.,', 'egrammar.ra', 118) def _reduce_40(val, _values, result) result = val[0].select(*val[2]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 118) +module_eval(<<'.,.,', 'egrammar.ra', 119) def _reduce_41(val, _values, result) result = val[1].paren() ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 126) +module_eval(<<'.,.,', 'egrammar.ra', 127) def _reduce_42(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 127) +module_eval(<<'.,.,', 'egrammar.ra', 128) def _reduce_43(val, _values, result) result = val[0].push(val[2]) result @@ -1373,21 +1396,21 @@ module_eval(<<'.,.,', 'egrammar.ra', 127) # reduce 63 omitted -module_eval(<<'.,.,', 'egrammar.ra', 158) +module_eval(<<'.,.,', 'egrammar.ra', 159) def _reduce_64(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 159) +module_eval(<<'.,.,', 'egrammar.ra', 160) def _reduce_65(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 167) +module_eval(<<'.,.,', 'egrammar.ra', 168) def _reduce_66(val, _values, result) result = Factory.CALL_NAMED(val[0], true, val[2]) loc result, val[0], val[4] @@ -1396,7 +1419,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 167) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 171) +module_eval(<<'.,.,', 'egrammar.ra', 172) def _reduce_67(val, _values, result) result = Factory.CALL_NAMED(val[0], true, []) loc result, val[0], val[2] @@ -1405,7 +1428,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 171) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 175) +module_eval(<<'.,.,', 'egrammar.ra', 176) def _reduce_68(val, _values, result) result = Factory.CALL_NAMED(val[0], true, val[2]) loc result, val[0], val[4] @@ -1415,7 +1438,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 175) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 180) +module_eval(<<'.,.,', 'egrammar.ra', 181) def _reduce_69(val, _values, result) result = Factory.CALL_NAMED(val[0], true, []) loc result, val[0], val[2] @@ -1425,49 +1448,49 @@ module_eval(<<'.,.,', 'egrammar.ra', 180) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 184) +module_eval(<<'.,.,', 'egrammar.ra', 185) def _reduce_70(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 189) +module_eval(<<'.,.,', 'egrammar.ra', 190) def _reduce_71(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 190) +module_eval(<<'.,.,', 'egrammar.ra', 191) def _reduce_72(val, _values, result) result = val[0]; val[0].lambda = val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 193) +module_eval(<<'.,.,', 'egrammar.ra', 194) def _reduce_73(val, _values, result) result = Factory.CALL_METHOD(val[0], val[2]); loc result, val[1], val[3] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 194) +module_eval(<<'.,.,', 'egrammar.ra', 195) def _reduce_74(val, _values, result) result = Factory.CALL_METHOD(val[0], []); loc result, val[1], val[3] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 195) +module_eval(<<'.,.,', 'egrammar.ra', 196) def _reduce_75(val, _values, result) result = Factory.CALL_METHOD(val[0], []); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 200) +module_eval(<<'.,.,', 'egrammar.ra', 201) def _reduce_76(val, _values, result) result = val[0].dot(Factory.fqn(val[2][:value])) loc result, val[1], val[2] @@ -1476,7 +1499,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 200) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 212) +module_eval(<<'.,.,', 'egrammar.ra', 213) def _reduce_77(val, _values, result) result = Factory.LAMBDA(val[0], val[1]) # loc result, val[1] # TODO @@ -1485,35 +1508,35 @@ module_eval(<<'.,.,', 'egrammar.ra', 212) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 217) +module_eval(<<'.,.,', 'egrammar.ra', 218) def _reduce_78(val, _values, result) result = val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 218) +module_eval(<<'.,.,', 'egrammar.ra', 219) def _reduce_79(val, _values, result) result = nil result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 222) +module_eval(<<'.,.,', 'egrammar.ra', 223) def _reduce_80(val, _values, result) result = [] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 223) +module_eval(<<'.,.,', 'egrammar.ra', 224) def _reduce_81(val, _values, result) result = val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 233) +module_eval(<<'.,.,', 'egrammar.ra', 234) def _reduce_82(val, _values, result) result = val[1] loc(result, val[0], val[1]) @@ -1522,7 +1545,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 233) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 240) +module_eval(<<'.,.,', 'egrammar.ra', 241) def _reduce_83(val, _values, result) result = Factory.IF(val[0], Factory.block_or_expression(*val[2]), val[4]) loc(result, val[0], (val[4] ? val[4] : val[3])) @@ -1531,7 +1554,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 240) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 244) +module_eval(<<'.,.,', 'egrammar.ra', 245) def _reduce_84(val, _values, result) result = Factory.IF(val[0], nil, val[3]) loc(result, val[0], (val[3] ? val[3] : val[2])) @@ -1542,7 +1565,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 244) # reduce 85 omitted -module_eval(<<'.,.,', 'egrammar.ra', 252) +module_eval(<<'.,.,', 'egrammar.ra', 253) def _reduce_86(val, _values, result) result = val[1] loc(result, val[0], val[1]) @@ -1551,7 +1574,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 252) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 256) +module_eval(<<'.,.,', 'egrammar.ra', 257) def _reduce_87(val, _values, result) result = Factory.block_or_expression(*val[2]) loc result, val[0], val[3] @@ -1560,7 +1583,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 256) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 260) +module_eval(<<'.,.,', 'egrammar.ra', 261) def _reduce_88(val, _values, result) result = nil # don't think a nop is needed here either @@ -1568,7 +1591,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 260) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 269) +module_eval(<<'.,.,', 'egrammar.ra', 270) def _reduce_89(val, _values, result) result = Factory.UNLESS(val[1], Factory.block_or_expression(*val[3]), val[5]) loc result, val[0], val[4] @@ -1577,7 +1600,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 269) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 273) +module_eval(<<'.,.,', 'egrammar.ra', 274) def _reduce_90(val, _values, result) result = Factory.UNLESS(val[1], nil, nil) loc result, val[0], val[4] @@ -1588,7 +1611,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 273) # reduce 91 omitted -module_eval(<<'.,.,', 'egrammar.ra', 283) +module_eval(<<'.,.,', 'egrammar.ra', 284) def _reduce_92(val, _values, result) result = Factory.block_or_expression(*val[2]) loc result, val[0], val[3] @@ -1597,7 +1620,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 283) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 287) +module_eval(<<'.,.,', 'egrammar.ra', 288) def _reduce_93(val, _values, result) result = nil # don't think a nop is needed here either @@ -1605,7 +1628,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 287) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 295) +module_eval(<<'.,.,', 'egrammar.ra', 296) def _reduce_94(val, _values, result) result = Factory.CASE(val[1], *val[3]) loc result, val[0], val[4] @@ -1614,21 +1637,21 @@ module_eval(<<'.,.,', 'egrammar.ra', 295) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 301) +module_eval(<<'.,.,', 'egrammar.ra', 302) def _reduce_95(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 302) +module_eval(<<'.,.,', 'egrammar.ra', 303) def _reduce_96(val, _values, result) result = val[0].push val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 307) +module_eval(<<'.,.,', 'egrammar.ra', 308) def _reduce_97(val, _values, result) result = Factory.WHEN(val[0], val[3]) loc result, val[1], val[4] @@ -1637,7 +1660,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 307) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 311) +module_eval(<<'.,.,', 'egrammar.ra', 312) def _reduce_98(val, _values, result) result = Factory.WHEN(val[0], nil) loc result, val[1], val[3] @@ -1646,7 +1669,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 311) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 315) +module_eval(<<'.,.,', 'egrammar.ra', 316) def _reduce_99(val, _values, result) result = val[0] result @@ -1655,7 +1678,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 315) # reduce 100 omitted -module_eval(<<'.,.,', 'egrammar.ra', 326) +module_eval(<<'.,.,', 'egrammar.ra', 327) def _reduce_101(val, _values, result) result = val[1] @@ -1663,28 +1686,28 @@ module_eval(<<'.,.,', 'egrammar.ra', 326) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 331) +module_eval(<<'.,.,', 'egrammar.ra', 332) def _reduce_102(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 332) +module_eval(<<'.,.,', 'egrammar.ra', 333) def _reduce_103(val, _values, result) result = val[0].push val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 337) +module_eval(<<'.,.,', 'egrammar.ra', 338) def _reduce_104(val, _values, result) result = Factory.MAP(val[0], val[2]) ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 353) +module_eval(<<'.,.,', 'egrammar.ra', 354) def _reduce_105(val, _values, result) result = val[0] @@ -1692,7 +1715,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 353) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 356) +module_eval(<<'.,.,', 'egrammar.ra', 357) def _reduce_106(val, _values, result) result = case Factory.resource_shape(val[1]) when :resource, :class @@ -1712,7 +1735,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 356) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 371) +module_eval(<<'.,.,', 'egrammar.ra', 372) def _reduce_107(val, _values, result) result = case Factory.resource_shape(val[1]) when :resource, :class, :defaults, :override @@ -1725,7 +1748,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 371) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 379) +module_eval(<<'.,.,', 'egrammar.ra', 380) def _reduce_108(val, _values, result) result = case Factory.resource_shape(val[0]) when :resource, :class @@ -1743,7 +1766,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 379) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 392) +module_eval(<<'.,.,', 'egrammar.ra', 393) def _reduce_109(val, _values, result) result = case Factory.resource_shape(val[0]) when :resource, :class @@ -1763,7 +1786,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 392) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 407) +module_eval(<<'.,.,', 'egrammar.ra', 408) def _reduce_110(val, _values, result) result = Factory.RESOURCE(Factory.fqn(token_text(val[0])), val[2]) loc result, val[0], val[4] @@ -1772,56 +1795,56 @@ module_eval(<<'.,.,', 'egrammar.ra', 407) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 412) +module_eval(<<'.,.,', 'egrammar.ra', 413) def _reduce_111(val, _values, result) result = Factory.RESOURCE_BODY(val[0], val[2]) result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 414) +module_eval(<<'.,.,', 'egrammar.ra', 415) def _reduce_112(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 417) +module_eval(<<'.,.,', 'egrammar.ra', 418) def _reduce_113(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 418) +module_eval(<<'.,.,', 'egrammar.ra', 419) def _reduce_114(val, _values, result) result = val[0].push val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 423) +module_eval(<<'.,.,', 'egrammar.ra', 424) def _reduce_115(val, _values, result) result = :virtual result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 424) +module_eval(<<'.,.,', 'egrammar.ra', 425) def _reduce_116(val, _values, result) result = :exported result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 425) +module_eval(<<'.,.,', 'egrammar.ra', 426) def _reduce_117(val, _values, result) result = :exported result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 437) +module_eval(<<'.,.,', 'egrammar.ra', 438) def _reduce_118(val, _values, result) result = Factory.COLLECT(val[0], val[1], val[3]) loc result, val[0], val[5] @@ -1830,7 +1853,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 437) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 441) +module_eval(<<'.,.,', 'egrammar.ra', 442) def _reduce_119(val, _values, result) result = Factory.COLLECT(val[0], val[1], []) loc result, val[0], val[1] @@ -1839,14 +1862,14 @@ module_eval(<<'.,.,', 'egrammar.ra', 441) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 446) +module_eval(<<'.,.,', 'egrammar.ra', 447) def _reduce_120(val, _values, result) result = Factory.VIRTUAL_QUERY(val[1]) ; loc result, val[0], val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 447) +module_eval(<<'.,.,', 'egrammar.ra', 448) def _reduce_121(val, _values, result) result = Factory.EXPORTED_QUERY(val[1]) ; loc result, val[0], val[2] result @@ -1857,21 +1880,21 @@ module_eval(<<'.,.,', 'egrammar.ra', 447) # reduce 123 omitted -module_eval(<<'.,.,', 'egrammar.ra', 460) +module_eval(<<'.,.,', 'egrammar.ra', 461) def _reduce_124(val, _values, result) result = [] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 461) +module_eval(<<'.,.,', 'egrammar.ra', 462) def _reduce_125(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 462) +module_eval(<<'.,.,', 'egrammar.ra', 463) def _reduce_126(val, _values, result) result = val[0].push(val[2]) result @@ -1884,7 +1907,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 462) # reduce 129 omitted -module_eval(<<'.,.,', 'egrammar.ra', 478) +module_eval(<<'.,.,', 'egrammar.ra', 479) def _reduce_130(val, _values, result) result = Factory.ATTRIBUTE_OP(val[0][:value], :'=>', val[2]) loc result, val[0], val[2] @@ -1893,7 +1916,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 478) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 482) +module_eval(<<'.,.,', 'egrammar.ra', 483) def _reduce_131(val, _values, result) result = Factory.ATTRIBUTE_OP(val[0][:value], :'+>', val[2]) loc result, val[0], val[2] @@ -1902,7 +1925,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 482) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 492) +module_eval(<<'.,.,', 'egrammar.ra', 493) def _reduce_132(val, _values, result) result = Factory.DEFINITION(classname(val[1][:value]), val[2], val[4]) loc result, val[0], val[5] @@ -1915,7 +1938,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 492) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 506) +module_eval(<<'.,.,', 'egrammar.ra', 507) def _reduce_133(val, _values, result) # Remove this class' name from the namestack as all nested classes have been parsed namepop @@ -1926,7 +1949,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 506) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 516) +module_eval(<<'.,.,', 'egrammar.ra', 517) def _reduce_134(val, _values, result) namestack(val[0][:value]) ; result = val[0] result @@ -1939,7 +1962,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 516) # reduce 137 omitted -module_eval(<<'.,.,', 'egrammar.ra', 525) +module_eval(<<'.,.,', 'egrammar.ra', 526) def _reduce_138(val, _values, result) result = val[1] result @@ -1950,7 +1973,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 525) # reduce 140 omitted -module_eval(<<'.,.,', 'egrammar.ra', 542) +module_eval(<<'.,.,', 'egrammar.ra', 543) def _reduce_141(val, _values, result) result = Factory.NODE(val[1], val[2], val[4]) loc result, val[0], val[5] @@ -1959,7 +1982,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 542) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 546) +module_eval(<<'.,.,', 'egrammar.ra', 547) def _reduce_142(val, _values, result) result = Factory.NODE(val[1], val[2], nil) loc result, val[0], val[4] @@ -1968,35 +1991,35 @@ module_eval(<<'.,.,', 'egrammar.ra', 546) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 556) +module_eval(<<'.,.,', 'egrammar.ra', 557) def _reduce_143(val, _values, result) result = [result] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 557) +module_eval(<<'.,.,', 'egrammar.ra', 558) def _reduce_144(val, _values, result) result = val[0].push(val[2]) result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 562) +module_eval(<<'.,.,', 'egrammar.ra', 563) def _reduce_145(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 563) +module_eval(<<'.,.,', 'egrammar.ra', 564) def _reduce_146(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 564) +module_eval(<<'.,.,', 'egrammar.ra', 565) def _reduce_147(val, _values, result) result = Factory.literal(:default); loc result, val[0] result @@ -2005,14 +2028,14 @@ module_eval(<<'.,.,', 'egrammar.ra', 564) # reduce 148 omitted -module_eval(<<'.,.,', 'egrammar.ra', 568) +module_eval(<<'.,.,', 'egrammar.ra', 569) def _reduce_149(val, _values, result) result = Factory.literal(val[0][:value]); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 569) +module_eval(<<'.,.,', 'egrammar.ra', 570) def _reduce_150(val, _values, result) result = Factory.concat(val[0], '.', val[2][:value]); loc result, val[0], val[2] result @@ -2021,277 +2044,292 @@ module_eval(<<'.,.,', 'egrammar.ra', 569) # reduce 151 omitted -module_eval(<<'.,.,', 'egrammar.ra', 574) +module_eval(<<'.,.,', 'egrammar.ra', 575) def _reduce_152(val, _values, result) result = val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 580) +module_eval(<<'.,.,', 'egrammar.ra', 581) def _reduce_153(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 581) +module_eval(<<'.,.,', 'egrammar.ra', 582) def _reduce_154(val, _values, result) error val[0], "'class' is not a valid classname" result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 585) +module_eval(<<'.,.,', 'egrammar.ra', 586) def _reduce_155(val, _values, result) result = [] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 586) +module_eval(<<'.,.,', 'egrammar.ra', 587) def _reduce_156(val, _values, result) result = [] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 587) +module_eval(<<'.,.,', 'egrammar.ra', 588) def _reduce_157(val, _values, result) result = val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 591) +module_eval(<<'.,.,', 'egrammar.ra', 592) def _reduce_158(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 592) +module_eval(<<'.,.,', 'egrammar.ra', 593) def _reduce_159(val, _values, result) result = val[0].push(val[2]) result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 596) +module_eval(<<'.,.,', 'egrammar.ra', 597) def _reduce_160(val, _values, result) result = Factory.PARAM(val[0][:value], val[2]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 597) +module_eval(<<'.,.,', 'egrammar.ra', 598) def _reduce_161(val, _values, result) result = Factory.PARAM(val[0][:value]); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 610) +module_eval(<<'.,.,', 'egrammar.ra', 611) def _reduce_162(val, _values, result) result = Factory.fqn(val[0][:value]).var ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 616) +module_eval(<<'.,.,', 'egrammar.ra', 617) def _reduce_163(val, _values, result) result = Factory.LIST(val[1]); loc result, val[0], val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 617) +module_eval(<<'.,.,', 'egrammar.ra', 618) def _reduce_164(val, _values, result) result = Factory.LIST(val[1]); loc result, val[0], val[3] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 618) +module_eval(<<'.,.,', 'egrammar.ra', 619) def _reduce_165(val, _values, result) result = Factory.literal([]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 621) +module_eval(<<'.,.,', 'egrammar.ra', 620) def _reduce_166(val, _values, result) - result = Factory.HASH(val[1]); loc result, val[0], val[2] + result = Factory.LIST(val[1]); loc result, val[0], val[2] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 621) + def _reduce_167(val, _values, result) + result = Factory.LIST(val[1]); loc result, val[0], val[3] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 622) - def _reduce_167(val, _values, result) - result = Factory.HASH(val[1]); loc result, val[0], val[3] + def _reduce_168(val, _values, result) + result = Factory.literal([]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 623) - def _reduce_168(val, _values, result) - result = Factory.literal({}) ; loc result, val[0], val[3] +module_eval(<<'.,.,', 'egrammar.ra', 625) + def _reduce_169(val, _values, result) + result = Factory.HASH(val[1]); loc result, val[0], val[2] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 626) - def _reduce_169(val, _values, result) - result = [val[0]] + def _reduce_170(val, _values, result) + result = Factory.HASH(val[1]); loc result, val[0], val[3] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 627) - def _reduce_170(val, _values, result) - result = val[0].push val[2] + def _reduce_171(val, _values, result) + result = Factory.literal({}) ; loc result, val[0], val[3] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 630) - def _reduce_171(val, _values, result) - result = Factory.KEY_ENTRY(val[0], val[2]); loc result, val[1] - result - end -.,., - -# reduce 172 omitted - -# reduce 173 omitted - -module_eval(<<'.,.,', 'egrammar.ra', 636) - def _reduce_174(val, _values, result) - result = Factory.literal(val[0][:value]) ; loc result, val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 637) - def _reduce_175(val, _values, result) - result = Factory.string(val[0], *val[1]) ; loc result, val[0], val[1][-1] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 638) - def _reduce_176(val, _values, result) - result = Factory.literal(val[0][:value]); loc result, val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 639) - def _reduce_177(val, _values, result) - result = Factory.literal(val[0][:value]); loc result, val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 640) - def _reduce_178(val, _values, result) - result = Factory.literal(val[0][:value]); loc result, val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 641) - def _reduce_179(val, _values, result) - result = [val[0]] + val[1] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 642) - def _reduce_180(val, _values, result) - result = Factory.TEXT(val[0]) - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 645) - def _reduce_181(val, _values, result) + def _reduce_172(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 646) +module_eval(<<'.,.,', 'egrammar.ra', 631) + def _reduce_173(val, _values, result) + result = val[0].push val[2] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 634) + def _reduce_174(val, _values, result) + result = Factory.KEY_ENTRY(val[0], val[2]); loc result, val[1] + result + end +.,., + +# reduce 175 omitted + +# reduce 176 omitted + +module_eval(<<'.,.,', 'egrammar.ra', 640) + def _reduce_177(val, _values, result) + result = Factory.literal(val[0][:value]) ; loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 641) + def _reduce_178(val, _values, result) + result = Factory.string(val[0], *val[1]) ; loc result, val[0], val[1][-1] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 642) + def _reduce_179(val, _values, result) + result = Factory.literal(val[0][:value]); loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 643) + def _reduce_180(val, _values, result) + result = Factory.literal(val[0][:value]); loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 644) + def _reduce_181(val, _values, result) + result = Factory.literal(val[0][:value]); loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 645) def _reduce_182(val, _values, result) result = [val[0]] + val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 648) +module_eval(<<'.,.,', 'egrammar.ra', 646) def _reduce_183(val, _values, result) - result = Factory.NUMBER(val[0][:value]) ; loc result, val[0] + result = Factory.TEXT(val[0]) result end .,., module_eval(<<'.,.,', 'egrammar.ra', 649) def _reduce_184(val, _values, result) - result = Factory.QNAME_OR_NUMBER(val[0][:value]) ; loc result, val[0] + result = [val[0]] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 650) def _reduce_185(val, _values, result) - result = Factory.QREF(val[0][:value]) ; loc result, val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 651) - def _reduce_186(val, _values, result) - result = Factory.literal(:undef); loc result, val[0] + result = [val[0]] + val[1] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 652) + def _reduce_186(val, _values, result) + result = Factory.NUMBER(val[0][:value]) ; loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 653) def _reduce_187(val, _values, result) + result = Factory.QNAME_OR_NUMBER(val[0][:value]) ; loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 654) + def _reduce_188(val, _values, result) + result = Factory.QREF(val[0][:value]) ; loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 655) + def _reduce_189(val, _values, result) + result = Factory.literal(:undef); loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 656) + def _reduce_190(val, _values, result) result = Factory.literal(:default); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 657) - def _reduce_188(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 661) + def _reduce_191(val, _values, result) result = Factory.literal(val[0][:value]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 660) - def _reduce_189(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 664) + def _reduce_192(val, _values, result) result = Factory.literal(val[0][:value]); loc result, val[0] result end .,., -# reduce 190 omitted +# reduce 193 omitted -module_eval(<<'.,.,', 'egrammar.ra', 666) - def _reduce_191(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 670) + def _reduce_194(val, _values, result) result = nil result end .,., -# reduce 192 omitted - -# reduce 193 omitted - -# reduce 194 omitted - # reduce 195 omitted # reduce 196 omitted @@ -2318,8 +2356,14 @@ module_eval(<<'.,.,', 'egrammar.ra', 666) # reduce 207 omitted -module_eval(<<'.,.,', 'egrammar.ra', 689) - def _reduce_208(val, _values, result) +# reduce 208 omitted + +# reduce 209 omitted + +# reduce 210 omitted + +module_eval(<<'.,.,', 'egrammar.ra', 693) + def _reduce_211(val, _values, result) result = nil result end diff --git a/lib/puppet/pops/parser/lexer2.rb b/lib/puppet/pops/parser/lexer2.rb index 910a4ed1c..0ee47ede7 100644 --- a/lib/puppet/pops/parser/lexer2.rb +++ b/lib/puppet/pops/parser/lexer2.rb @@ -29,11 +29,11 @@ class Puppet::Pops::Parser::Lexer2 # the token, and to advance the scanner position (without having to advance it with a scan(regexp)). # TOKEN_LBRACK = [:LBRACK, '['.freeze, 1].freeze + TOKEN_LISTSTART = [:LISTSTART, '['.freeze, 1].freeze TOKEN_RBRACK = [:RBRACK, ']'.freeze, 1].freeze TOKEN_LBRACE = [:LBRACE, '{'.freeze, 1].freeze TOKEN_RBRACE = [:RBRACE, '}'.freeze, 1].freeze TOKEN_SELBRACE = [:SELBRACE, '{'.freeze, 1].freeze - TOKEN_LAMBDA = [:LAMBDA, '{'.freeze, 1].freeze # should be removed TOKEN_LPAREN = [:LPAREN, '('.freeze, 1].freeze TOKEN_RPAREN = [:RPAREN, ')'.freeze, 1].freeze @@ -319,7 +319,11 @@ class Puppet::Pops::Parser::Lexer2 emit(TOKEN_COMMA, before) when '[' - emit(TOKEN_LBRACK, before) + if ctx[:after] == :NAME && (before == 0 || scn.string[before-1,1] =~ /[[:blank:]\r\n]+/) + emit(TOKEN_LISTSTART, before) + else + emit(TOKEN_LBRACK, before) + end when ']' emit(TOKEN_RBRACK, before) diff --git a/spec/unit/pops/parser/lexer2_spec.rb b/spec/unit/pops/parser/lexer2_spec.rb index fdaf5e66e..2fb94826a 100644 --- a/spec/unit/pops/parser/lexer2_spec.rb +++ b/spec/unit/pops/parser/lexer2_spec.rb @@ -177,6 +177,14 @@ describe 'Lexer2' do end end + it "differentiates between foo[x] and foo [x] (whitespace)" do + tokens_scanned_from("$a[1]").should match_tokens2(:VARIABLE, :LBRACK, :NUMBER, :RBRACK) + tokens_scanned_from("$a [1]").should match_tokens2(:VARIABLE, :LBRACK, :NUMBER, :RBRACK) + tokens_scanned_from("a[1]").should match_tokens2(:NAME, :LBRACK, :NUMBER, :RBRACK) + tokens_scanned_from("a [1]").should match_tokens2(:NAME, :LISTSTART, :NUMBER, :RBRACK) + tokens_scanned_from(" if \n\r\t\nif if ").should match_tokens2(:IF, :IF, :IF) + end + it "skips whitepsace" do tokens_scanned_from(" if if if ").should match_tokens2(:IF, :IF, :IF) tokens_scanned_from(" if \n\r\t\nif if ").should match_tokens2(:IF, :IF, :IF) @@ -232,7 +240,7 @@ describe 'Lexer2' do it 'should lex assignment' do tokens_scanned_from("$a = 10").should match_tokens2([:VARIABLE, "a"], :EQUALS, [:NUMBER, '10']) end - + # TODO: Tricky, and heredoc not supported yet # it "should not lex regexp after heredoc" do # tokens_scanned_from("1 / /./").should match_tokens2(:NUMBER, :DIV, :REGEX) From 04fe41e1d9729bf957a5a351c5a57f333c9310e4 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 1 Nov 2013 16:29:07 +0100 Subject: [PATCH 063/800] (#22363) Add Puppet 4x validator & tests Adds a puppet 4x validator (not derived from the 3x validator due to performance concerns). The 4x validator relaxes rules for operator [] to support ranges and hash multi lookup. Tests added for these cases. --- lib/puppet/pops.rb | 2 + lib/puppet/pops/parser/evaluating_parser.rb | 4 ++ .../pops/evaluator/evaluating_parser_spec.rb | 58 +++++++++++++++++++ 3 files changed, 64 insertions(+) diff --git a/lib/puppet/pops.rb b/lib/puppet/pops.rb index ffc4ccff6..62e2cd138 100644 --- a/lib/puppet/pops.rb +++ b/lib/puppet/pops.rb @@ -81,6 +81,8 @@ module Puppet module Validation require 'puppet/pops/validation/checker3_1' require 'puppet/pops/validation/validator_factory_3_1' + require 'puppet/pops/validation/checker4_0' + require 'puppet/pops/validation/validator_factory_4_0' end module Evaluator diff --git a/lib/puppet/pops/parser/evaluating_parser.rb b/lib/puppet/pops/parser/evaluating_parser.rb index 7379565c8..5ba286625 100644 --- a/lib/puppet/pops/parser/evaluating_parser.rb +++ b/lib/puppet/pops/parser/evaluating_parser.rb @@ -168,5 +168,9 @@ class Puppet::Pops::Parser::EvaluatingParser Puppet::Pops::Evaluator::EvaluatorImpl.new().evaluate(model, scope) end + def validator() + @validator ||= Puppet::Pops::Validation::ValidatorFactory_4_0.new().validator(acceptor) + end + end end diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index 600291c60..92a5141f8 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -367,6 +367,64 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do end end + context "When evaluator performs [] operations" do + { + "[1,2,3][0]" => 1, + "[1,2,3][2]" => 3, + "[1,2,3][3]" => nil, + "[1,2,3][-1]" => 3, + "[1,2,3][-2]" => 2, + "[1,2,3][-4]" => nil, + "[1,2,3,4][0,2]" => [1,2], + "[1,2,3,4][1,3]" => [2,3,4], + "[1,2,3,4][-2,2]" => [3,4], + }.each do |source, result| + it "should parse and evaluate the expression '#{source}' to #{result}" do + parser.evaluate_string(scope, source, __FILE__).should == result + end + end + + { + "{a=>1, b=>2, c=>3}[a]" => 1, + "{a=>1, b=>2, c=>3}[c]" => 3, + "{a=>1, b=>2, c=>3}[x]" => nil, + "{a=>1, b=>2, c=>3}[c,b]" => [3,2], + "{a=>1, b=>2, c=>3}[a,b,c]" => [1,2,3], + }.each do |source, result| + it "should parse and evaluate the expression '#{source}' to #{result}" do + parser.evaluate_string(scope, source, __FILE__).should == result + end + end + + { + "'abc'[0]" => 'a', + "'abc'[2]" => 'c', + "'abc'[-1]" => 'c', + "'abc'[-2]" => 'b', + "'abc'[-3]" => 'a', + "'abc'[-4]" => nil, + "'abc'[3]" => nil, + "abc[0]" => 'a', + "abc[2]" => 'c', + "abc[-1]" => 'c', + "abc[-2]" => 'b', + "abc[-3]" => 'a', + "abc[-4]" => nil, + "abc[3]" => nil, + "'abcd'[0,2]" => 'ab', + "'abcd'[1,3]" => 'bcd', + "'abcd'[-2,2]" => 'cd', + "'abcd'[-3,2]" => 'bc', + "'abcd'[3,5]" => 'd', + "'abcd'[5,2]" => nil, + "'abcd'[-5,2]" => nil, + }.each do |source, result| + it "should parse and evaluate the expression '#{source}' to #{result}" do + parser.evaluate_string(scope, source, __FILE__).should == result + end + end + end + context "When the evaluator performs boolean operations" do { "true and true" => true, From 52ca43bf275f15587ff3f9a9884637b34195481d Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 1 Nov 2013 16:47:34 +0100 Subject: [PATCH 064/800] (#22363) Add tests for evaluation of types --- .../pops/evaluator/evaluating_parser_spec.rb | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index 92a5141f8..61a23c519 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -17,6 +17,7 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do let(:parser) { Puppet::Pops::Parser::EvaluatingParser::Transitional.new } let(:node) { 'node.example.com' } let(:scope) { s = create_test_scope_for_node(node); s } + types = Puppet::Pops::Types::TypeFactory context "When evaluator evaluates literals" do { @@ -33,7 +34,7 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do "banana::split" => 'banana::split', "false" => false, "true" => true, - "Array" => Puppet::Pops::Types::TypeFactory.array_of_data(), + "Array" => types.array_of_data(), "/.*/" => /.*/ }.each do |source, result| it "should parse and evaluate the expression '#{source}' to #{result}" do @@ -423,6 +424,28 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do parser.evaluate_string(scope, source, __FILE__).should == result end end + + # Type operations (full set tested by tests covering type calculator) + { + "Array[Integer]" => types.array_of(types.integer), + "Hash[Integer,Integer]" => types.hash_of(types.integer, types.integer), + "Resource[File]" => types.resource('File'), + "Resource['File']" => types.resource(types.resource('File')), + }.each do |source, result| + it "should parse and evaluate the expression '#{source}' to #{result}" do + parser.evaluate_string(scope, source, __FILE__).should == result + end + end + + { + "file {foo: } File[foo]" => :TODO, + "file {[foo, bar]: } File[foo, bar]" => [:TODO], + }.each do |source, result| + it "should parse and evaluate the expression '#{source}' to #{result}" do + pending "Not yet implemented Resource Instance resolution" + parser.evaluate_string(scope, source, __FILE__).should == result + end + end end context "When the evaluator performs boolean operations" do From eafaec241a9e6f7b75d63a5d1d807d7477a65a02 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 1 Nov 2013 16:57:36 +0100 Subject: [PATCH 065/800] (#22363) Make if, unless, case be rvalue producers The evaluator already treated these statements as rvalue producers. The change is a relaxation in the validator. This also adds tests. Also correct mistake of not checking in the validator 4. --- lib/puppet/pops/validation/checker4_0.rb | 562 ++++++++++++++++++ .../pops/validation/validator_factory_4_0.rb | 31 + .../pops/evaluator/evaluating_parser_spec.rb | 8 + 3 files changed, 601 insertions(+) create mode 100644 lib/puppet/pops/validation/checker4_0.rb create mode 100644 lib/puppet/pops/validation/validator_factory_4_0.rb diff --git a/lib/puppet/pops/validation/checker4_0.rb b/lib/puppet/pops/validation/checker4_0.rb new file mode 100644 index 000000000..8eea8eacd --- /dev/null +++ b/lib/puppet/pops/validation/checker4_0.rb @@ -0,0 +1,562 @@ +# A Validator validates a model. +# +# Validation is performed on each model element in isolation. Each method should validate the model element's state +# but not validate its referenced/contained elements except to check their validity in their respective role. +# The intent is to drive the validation with a tree iterator that visits all elements in a model. +# +# +# TODO: Add validation of multiplicities - this is a general validation that can be checked for all +# Model objects via their metamodel. (I.e an extra call to multiplicity check in polymorph check). +# This is however mostly valuable when validating model to model transformations, and is therefore T.B.D +# +class Puppet::Pops::Validation::Checker4_0 + Issues = Puppet::Pops::Issues + Model = Puppet::Pops::Model + + attr_reader :acceptor + # Initializes the validator with a diagnostics producer. This object must respond to + # `:will_accept?` and `:accept`. + # + def initialize(diagnostics_producer) + @@check_visitor ||= Puppet::Pops::Visitor.new(nil, "check", 0, 0) + @@rvalue_visitor ||= Puppet::Pops::Visitor.new(nil, "rvalue", 0, 0) + @@hostname_visitor ||= Puppet::Pops::Visitor.new(nil, "hostname", 1, 2) + @@assignment_visitor ||= Puppet::Pops::Visitor.new(nil, "assign", 0, 1) + @@query_visitor ||= Puppet::Pops::Visitor.new(nil, "query", 0, 0) + @@top_visitor ||= Puppet::Pops::Visitor.new(nil, "top", 1, 1) + @@relation_visitor ||= Puppet::Pops::Visitor.new(nil, "relation", 1, 1) + + @acceptor = diagnostics_producer + end + + # Validates the entire model by visiting each model element and calling `check`. + # The result is collected (or acted on immediately) by the configured diagnostic provider/acceptor + # given when creating this Checker. + # + def validate(model) + # tree iterate the model, and call check for each element + check(model) + model.eAllContents.each {|m| check(m) } + end + + # Performs regular validity check + def check(o) + @@check_visitor.visit_this_0(self, o) + end + + # Performs check if this is a vaid hostname expression + # @param single_feature_name [String, nil] the name of a single valued hostname feature of the value's container. e.g. 'parent' + def hostname(o, semantic, single_feature_name = nil) + @@hostname_visitor.visit_this_2(self, o, semantic, single_feature_name) + end + + # Performs check if this is valid as a query + def query(o) + @@query_visitor.visit_this_0(self, o) + end + + # Performs check if this is valid as a relationship side + def relation(o, container) + @@relation_visitor.visit_this_1(self, o, container) + end + + # Performs check if this is valid as a rvalue + def rvalue(o) + @@rvalue_visitor.visit_this_0(self, o) + end + + # Performs check if this is valid as a container of a definition (class, define, node) + def top(o, definition) + @@top_visitor.visit_this_1(self, o, definition) + end + + # Checks the LHS of an assignment (is it assignable?). + # If args[0] is true, assignment via index is checked. + # + def assign(o, via_index = false) + @@assignment_visitor.visit_this_1(self, o, via_index) + end + + #---ASSIGNMENT CHECKS + + def assign_VariableExpression(o, via_index) + varname_string = varname_to_s(o.expr) + if varname_string =~ /^[0-9]+$/ + acceptor.accept(Issues::ILLEGAL_NUMERIC_ASSIGNMENT, o, :varname => varname_string) + end + # Can not assign to something in another namespace (i.e. a '::' in the name is not legal) + if acceptor.will_accept? Issues::CROSS_SCOPE_ASSIGNMENT + if varname_string =~ /::/ + acceptor.accept(Issues::CROSS_SCOPE_ASSIGNMENT, o, :name => varname_string) + end + end + # TODO: Could scan for reassignment of the same variable if done earlier in the same container + # Or if assigning to a parameter (more work). + # TODO: Investigate if there are invalid cases for += assignment + end + + def assign_AccessExpression(o, via_index) + # Are indexed assignments allowed at all ? $x[x] = '...' + if acceptor.will_accept? Issues::ILLEGAL_INDEXED_ASSIGNMENT + acceptor.accept(Issues::ILLEGAL_INDEXED_ASSIGNMENT, o) + else + # Then the left expression must be assignable-via-index + assign(o.left_expr, true) + end + end + + def assign_Object(o, via_index) + # Can not assign to anything else (differentiate if this is via index or not) + # i.e. 10 = 'hello' vs. 10['x'] = 'hello' (the root is reported as being in error in both cases) + # + acceptor.accept(via_index ? Issues::ILLEGAL_ASSIGNMENT_VIA_INDEX : Issues::ILLEGAL_ASSIGNMENT, o) + end + + #---CHECKS + + def check_Object(o) + end + + def check_Factory(o) + check(o.current) + end + + def check_AccessExpression(o) + # Check multiplicity of keys + case o.left_expr + when Model::QualifiedName + # allows many keys, but the name should really be a QualifiedReference + # acceptor.accept(Issues::DEPRECATED_NAME_AS_TYPE, o, :name => o.value) + # OK in 4.x since a name evaluates to a String, and String[] is supported + # Also allows many + when Model::QualifiedReference + # ok, allows many - this is a type / resource reference + + else + # i.e. for any other expression that may produce an array or hash +# if o.keys.size > 1 +# acceptor.accept(Issues::UNSUPPORTED_RANGE, o, :count => o.keys.size) +# end + # Range is ok, multiple keys supported by many LHS types (checked at runtime) + # TODO: can statically check key size for some types Name & String 1-2, Array 1,2, Hash 1-many, etc + # (but is captured as RT error) + if o.keys.size < 1 + acceptor.accept(Issues::MISSING_INDEX, o) + end + end + end + + def check_AssignmentExpression(o) + acceptor.accept(Issues::UNSUPPORTED_OPERATOR, o, {:operator => o.operator}) unless [:'=', :'+='].include? o.operator + assign(o.left_expr) + rvalue(o.right_expr) + end + + # Checks that operation with :+> is contained in a ResourceOverride or Collector. + # + # Parent of an AttributeOperation can be one of: + # * CollectExpression + # * ResourceOverride + # * ResourceBody (ILLEGAL this is a regular resource expression) + # * ResourceDefaults (ILLEGAL) + # + def check_AttributeOperation(o) + if o.operator == :'+>' + # Append operator use is constrained + parent = o.eContainer + unless parent.is_a?(Model::CollectExpression) || parent.is_a?(Model::ResourceOverrideExpression) + acceptor.accept(Issues::ILLEGAL_ATTRIBUTE_APPEND, o, {:name=>o.attribute_name, :parent=>parent}) + end + end + rvalue(o.value_expr) + end + + def check_BinaryExpression(o) + rvalue(o.left_expr) + rvalue(o.right_expr) + end + + def check_CallNamedFunctionExpression(o) + unless o.functor_expr.is_a? Model::QualifiedName + acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.functor_expr, :feature => 'function name', :container => o) + end + end + + def check_MethodCallExpression(o) + unless o.functor_expr.is_a? Model::QualifiedName + acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.functor_expr, :feature => 'function name', :container => o) + end + end + + def check_CaseExpression(o) + # There should only be one LiteralDefault case option value + # TODO: Implement this check + end + + def check_CollectExpression(o) + unless o.type_expr.is_a? Model::QualifiedReference + acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.type_expr, :feature=> 'type name', :container => o) + end + + # If a collect expression tries to collect exported resources and storeconfigs is not on + # then it will not work... This was checked in the parser previously. This is a runtime checking + # thing as opposed to a language thing. + if acceptor.will_accept?(Issues::RT_NO_STORECONFIGS) && o.query.is_a?(Model::ExportedQuery) + acceptor.accept(Issues::RT_NO_STORECONFIGS, o) + end + end + + # Only used for function names, grammar should not be able to produce something faulty, but + # check anyway if model is created programatically (it will fail in transformation to AST for sure). + def check_NamedAccessExpression(o) + name = o.right_expr + unless name.is_a? Model::QualifiedName + acceptor.accept(Issues::ILLEGAL_EXPRESSION, name, :feature=> 'function name', :container => o.eContainer) + end + end + + # for 'class' and 'define' + def check_NamedDefinition(o) + top(o.eContainer, o) + if (acceptor.will_accept? Issues::NAME_WITH_HYPHEN) && o.name.include?('-') + acceptor.accept(Issues::NAME_WITH_HYPHEN, o, {:name => o.name}) + end + end + + def check_ImportExpression(o) + o.files.each do |f| + unless f.is_a? Model::LiteralString + acceptor.accept(Issues::ILLEGAL_EXPRESSION, f, :feature => 'file name', :container => o) + end + end + end + + def check_InstanceReference(o) + # TODO: Original warning is : + # Puppet.warning addcontext("Deprecation notice: Resource references should now be capitalized") + # This model element is not used in the egrammar. + # Either implement checks or deprecate the use of InstanceReference (the same is acheived by + # transformation of AccessExpression when used where an Instance/Resource reference is allowed. + # + end + + # Restrictions on hash key are because of the strange key comparisons/and merge rules in the AST evaluation + # (Even the allowed ones are handled in a strange way). + # + def transform_KeyedEntry(o) + case o.key + when Model::QualifiedName + when Model::LiteralString + when Model::LiteralNumber + when Model::ConcatenatedString + else + acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.key, :feature => 'hash key', :container => o.eContainer) + end + end + + # A Lambda is a Definition, but it may appear in other scopes that top scope (Which check_Definition asserts). + # + def check_LambdaExpression(o) + end + + def check_NodeDefinition(o) + # Check that hostnames are valid hostnames (or regular expressons) + hostname(o.host_matches, o) + hostname(o.parent, o, 'parent') unless o.parent.nil? + top(o.eContainer, o) + end + + # Asserts that value is a valid QualifiedName. No additional checking is made, objects that use + # a QualifiedName as a name should check the validity - this since a QualifiedName is used as a BARE WORD + # and then additional chars may be valid (like a hyphen). + # + def check_QualifiedName(o) + # Is this a valid qualified name? + if o.value !~ Puppet::Pops::Patterns::NAME + acceptor.accept(Issues::ILLEGAL_NAME, o, {:name=>o.value}) + end + end + + # Checks that the value is a valid UpperCaseWord (a CLASSREF), and optionally if it contains a hypen. + # DOH: QualifiedReferences are created with LOWER CASE NAMES at parse time + def check_QualifiedReference(o) + # Is this a valid qualified name? + if o.value !~ Puppet::Pops::Patterns::CLASSREF + acceptor.accept(Issues::ILLEGAL_CLASSREF, o, {:name=>o.value}) + elsif (acceptor.will_accept? Issues::NAME_WITH_HYPHEN) && o.value.include?('-') + acceptor.accept(Issues::NAME_WITH_HYPHEN, o, {:name => o.value}) + end + end + + def check_QueryExpression(o) + query(o.expr) if o.expr # is optional + end + + def relation_Object(o, rel_expr) + acceptor.accept(Issues::ILLEGAL_EXPRESSION, o, {:feature => o.eContainingFeature, :container => rel_expr}) + end + + def relation_AccessExpression(o, rel_expr); end + + def relation_CollectExpression(o, rel_expr); end + + def relation_VariableExpression(o, rel_expr); end + + def relation_LiteralString(o, rel_expr); end + + def relation_ConcatenatedStringExpression(o, rel_expr); end + + def relation_SelectorExpression(o, rel_expr); end + + def relation_CaseExpression(o, rel_expr); end + + def relation_ResourceExpression(o, rel_expr); end + + def relation_RelationshipExpression(o, rel_expr); end + + def check_Parameter(o) + if o.name =~ /^[0-9]+$/ + acceptor.accept(Issues::ILLEGAL_NUMERIC_PARAMETER, o, :name => o.name) + end + end + + #relationship_side: resource + # | resourceref + # | collection + # | variable + # | quotedtext + # | selector + # | casestatement + # | hasharrayaccesses + + def check_RelationshipExpression(o) + relation(o.left_expr, o) + relation(o.right_expr, o) + end + + def check_ResourceExpression(o) + # A resource expression must have a lower case NAME as its type e.g. 'file { ... }' + unless o.type_name.is_a? Model::QualifiedName + acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.type_name, :feature => 'resource type', :container => o) + end + + # This is a runtime check - the model is valid, but will have runtime issues when evaluated + # and storeconfigs is not set. + if acceptor.will_accept?(Issues::RT_NO_STORECONFIGS) && o.exported + acceptor.accept(Issues::RT_NO_STORECONFIGS_EXPORT, o) + end + end + + def check_ResourceDefaultsExpression(o) + if o.form && o.form != :regular + acceptor.accept(Issues::NOT_VIRTUALIZEABLE, o) + end + end + + # Transformation of SelectorExpression is limited to certain types of expressions. + # This is probably due to constraints in the old grammar rather than any real concerns. + def select_SelectorExpression(o) + case o.left_expr + when Model::CallNamedFunctionExpression + when Model::AccessExpression + when Model::VariableExpression + when Model::ConcatenatedString + else + acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.left_expr, :feature => 'left operand', :container => o) + end + end + + def check_UnaryExpression(o) + rvalue(o.expr) + end + + def check_UnlessExpression(o) + # TODO: Unless may not have an elsif + # TODO: 3.x unless may not have an else + end + + def check_VariableExpression(o) + # The expression must be a qualified name + if !o.expr.is_a? Model::QualifiedName + acceptor.accept(Issues::ILLEGAL_EXPRESSION, o, :feature => 'name', :container => o) + else + # Note, that if it later becomes illegal with hyphen in any name, this special check + # can be skipped in favor of the check in QualifiedName, which is now not done if contained in + # a VariableExpression + name = o.expr.value + if (acceptor.will_accept? Issues::VAR_WITH_HYPHEN) && name.include?('-') + acceptor.accept(Issues::VAR_WITH_HYPHEN, o, {:name => name}) + end + end + end + + #--- HOSTNAME CHECKS + + # Transforms Array of host matching expressions into a (Ruby) array of AST::HostName + def hostname_Array(o, semantic, single_feature_name) + if single_feature_name + acceptor.accept(Issues::ILLEGAL_EXPRESSION, o, {:feature=>single_feature_name, :container=>semantic}) + end + o.each {|x| hostname(x, semantic, false) } + end + + def hostname_String(o, semantic, single_feature_name) + # The 3.x checker only checks for illegal characters - if matching /[^-\w.]/ the name is invalid, + # but this allows pathological names like "a..b......c", "----" + # TODO: Investigate if more illegal hostnames should be flagged. + # + if o =~ Puppet::Pops::Patterns::ILLEGAL_HOSTNAME_CHARS + acceptor.accept(Issues::ILLEGAL_HOSTNAME_CHARS, semantic, :hostname => o) + end + end + + def hostname_LiteralValue(o, semantic, single_feature_name) + hostname_String(o.value.to_s, o, single_feature_name) + end + + def hostname_ConcatenatedString(o, semantic, single_feature_name) + # Puppet 3.1. only accepts a concatenated string without interpolated expressions + if the_expr = o.segments.index {|s| s.is_a?(Model::TextExpression) } + acceptor.accept(Issues::ILLEGAL_HOSTNAME_INTERPOLATION, o.segments[the_expr].expr) + elsif o.segments.size() != 1 + # corner case, bad model, concatenation of several plain strings + acceptor.accept(Issues::ILLEGAL_HOSTNAME_INTERPOLATION, o) + else + # corner case, may be ok, but lexer may have replaced with plain string, this is + # here if it does not + hostname_String(o.segments[0], o.segments[0], false) + end + end + + def hostname_QualifiedName(o, semantic, single_feature_name) + hostname_String(o.value.to_s, o, single_feature_name) + end + + def hostname_QualifiedReference(o, semantic, single_feature_name) + hostname_String(o.value.to_s, o, single_feature_name) + end + + def hostname_LiteralNumber(o, semantic, single_feature_name) + # always ok + end + + def hostname_LiteralDefault(o, semantic, single_feature_name) + # always ok + end + + def hostname_LiteralRegularExpression(o, semantic, single_feature_name) + # always ok + end + + def hostname_Object(o, semantic, single_feature_name) + acceptor.accept(Issues::ILLEGAL_EXPRESSION, o, {:feature=> single_feature_name || 'hostname', :container=>semantic}) + end + + #---QUERY CHECKS + + # Anything not explicitly allowed is flagged as error. + def query_Object(o) + acceptor.accept(Issues::ILLEGAL_QUERY_EXPRESSION, o) + end + + # Puppet AST only allows == and != + # + def query_ComparisonExpression(o) + acceptor.accept(Issues::ILLEGAL_QUERY_EXPRESSION, o) unless [:'==', :'!='].include? o.operator + end + + # Allows AND, OR, and checks if left/right are allowed in query. + def query_BooleanExpression(o) + query o.left_expr + query o.right_expr + end + + def query_ParenthesizedExpression(o) + query(o.expr) + end + + def query_VariableExpression(o); end + + def query_QualifiedName(o); end + + def query_LiteralNumber(o); end + + def query_LiteralString(o); end + + def query_LiteralBoolean(o); end + + #---RVALUE CHECKS + + # By default, all expressions are reported as being rvalues + # Implement specific rvalue checks for those that are not. + # + def rvalue_Expression(o); end + + def rvalue_ImportExpression(o) ; acceptor.accept(Issues::NOT_RVALUE, o) ; end + + def rvalue_BlockExpression(o) ; acceptor.accept(Issues::NOT_RVALUE, o) ; end + +# def rvalue_CaseExpression(o) ; acceptor.accept(Issues::NOT_RVALUE, o) ; end + +# def rvalue_IfExpression(o) ; acceptor.accept(Issues::NOT_RVALUE, o) ; end + +# def rvalue_UnlessExpression(o) ; acceptor.accept(Issues::NOT_RVALUE, o) ; end + + def rvalue_ResourceExpression(o) ; acceptor.accept(Issues::NOT_RVALUE, o) ; end + + def rvalue_ResourceDefaultsExpression(o); acceptor.accept(Issues::NOT_RVALUE, o) ; end + + def rvalue_ResourceOverrideExpression(o); acceptor.accept(Issues::NOT_RVALUE, o) ; end + + def rvalue_CollectExpression(o) ; acceptor.accept(Issues::NOT_RVALUE, o) ; end + + def rvalue_Definition(o) ; acceptor.accept(Issues::NOT_RVALUE, o) ; end + + def rvalue_NodeDefinition(o) ; acceptor.accept(Issues::NOT_RVALUE, o) ; end + + def rvalue_UnaryExpression(o) ; rvalue o.expr ; end + + #---TOP CHECK + + def top_NilClass(o, definition) + # ok, reached the top, no more parents + end + + def top_Object(o, definition) + # fail, reached a container that is not top level + acceptor.accept(Issues::NOT_TOP_LEVEL, definition) + end + + def top_BlockExpression(o, definition) + # ok, if this is a block representing the body of a class, or is top level + top o.eContainer, definition + end + + def top_HostClassDefinition(o, definition) + # ok, stop scanning parents + end + + # A LambdaExpression is a BlockExpression, and this method is needed to prevent the polymorph method for BlockExpression + # to accept a lambda. + # A lambda can not iteratively create classes, nodes or defines as the lambda does not have a closure. + # + def top_LambdaExpression(o, definition) + # fail, stop scanning parents + acceptor.accept(Issues::NOT_TOP_LEVEL, definition) + end + + #--- NON POLYMORPH, NON CHECKING CODE + + # Produces string part of something named, or nil if not a QualifiedName or QualifiedReference + # + def varname_to_s(o) + case o + when Model::QualifiedName + o.value + when Model::QualifiedReference + o.value + else + nil + end + end +end diff --git a/lib/puppet/pops/validation/validator_factory_4_0.rb b/lib/puppet/pops/validation/validator_factory_4_0.rb new file mode 100644 index 000000000..95ff0d687 --- /dev/null +++ b/lib/puppet/pops/validation/validator_factory_4_0.rb @@ -0,0 +1,31 @@ +# Configures validation suitable for 4.0 +# +class Puppet::Pops::Validation::ValidatorFactory_4_0 < Puppet::Pops::Validation::Factory + Issues = Puppet::Pops::Issues + + # Produces the checker to use + def checker diagnostic_producer + Puppet::Pops::Validation::Checker4_0.new(diagnostic_producer) + end + + # Produces the label provider to use + def label_provider + Puppet::Pops::Model::ModelLabelProvider.new() + end + + # Produces the severity producer to use + def severity_producer + p = super + + # Configure each issue that should **not** be an error + # + # Validate as per the current runtime configuration + p[Issues::RT_NO_STORECONFIGS_EXPORT] = Puppet[:storeconfigs] ? :ignore : :warning + p[Issues::RT_NO_STORECONFIGS] = Puppet[:storeconfigs] ? :ignore : :warning + + p[Issues::NAME_WITH_HYPHEN] = :error + p[Issues::DEPRECATED_NAME_AS_TYPE] = :error + + p + end +end diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index 61a23c519..0d40a34ce 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -335,6 +335,14 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do "unless false {5}" => 5, "unless true {5}" => nil, "unless true {2} else {5}" => 5, + "$a = if true {5} $a" => 5, + "$a = if false {5} $a" => nil, + "$a = if false {2} else {5} $a" => 5, + "$a = if false {2} elsif true {5} $a" => 5, + "$a = if false {2} elsif false {5} $a" => nil, + "$a = unless false {5} $a" => 5, + "$a = unless true {5} $a" => nil, + "$a = unless true {2} else {5} $a" => 5, }.each do |source, result| it "should parse and evaluate the expression '#{source}' to #{result}" do parser.evaluate_string(scope, source, __FILE__).should == result From bb39e91bda2005b925d34d179396051ae2ddf6b7 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 2 Nov 2013 02:00:28 +0100 Subject: [PATCH 066/800] (maint) Remove unused cruft from pops model This removes unused code from the pops model and related factory methods. Also removes some stale comments and commented out code. --- lib/puppet/pops/evaluator/evaluator_impl.rb | 6 +- lib/puppet/pops/model/factory.rb | 7 -- lib/puppet/pops/model/model.rb | 111 +------------------- lib/puppet/pops/parser/parser_support.rb | 21 +--- spec/unit/pops/factory_spec.rb | 23 ---- 5 files changed, 8 insertions(+), 160 deletions(-) diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index f673ef41c..e36c90bd0 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -629,8 +629,6 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # NodeDefinition < Expression # HostClassDefinition < NamedDefinition - # TypeReference < Expression - # InstanceReferences < TypeReference # ResourceExpression < Expression # class ResourceBody < ASTObject # ResourceDefaultsExpression < Expression @@ -772,6 +770,10 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # TODO: formalize, when scope returns nil, vs error get_variable_value(o.expr.value, o, scope).to_s else + # TODO: This is not very good as it forces the to_s to apply to all kind of results + # It should call a polymorph method to allow to_s to be applied in general, and possible some other + # to string formatter for other values (like PType Objects). + # evaluate(o.expr, scope).to_s end end diff --git a/lib/puppet/pops/model/factory.rb b/lib/puppet/pops/model/factory.rb index 5ff92184d..4f4cf8bd0 100644 --- a/lib/puppet/pops/model/factory.rb +++ b/lib/puppet/pops/model/factory.rb @@ -616,13 +616,6 @@ class Puppet::Pops::Model::Factory new(Model::ExportedQuery, query_expr) end - # Used by regular grammar, egrammar creates an AccessExpression instead, and evaluation determines - # if access is to instances or something else. - # - def self.INSTANCE(type_name, name_expressions) - new(Model::InstanceReferences, type_name, name_expressions) - end - def self.ATTRIBUTE_OP(name, op, expr) new(Model::AttributeOperation, name, op, expr) end diff --git a/lib/puppet/pops/model/model.rb b/lib/puppet/pops/model/model.rb index bcc743e2e..77cc1bc9f 100644 --- a/lib/puppet/pops/model/model.rb +++ b/lib/puppet/pops/model/model.rb @@ -254,8 +254,6 @@ module Puppet::Pops::Model # A resource type definition (a 'define' in the DSL). # class ResourceTypeDefinition < NamedDefinition - # FUTURE - # contains_one_uni 'producer', Producer end # A node definition matches hosts using Strings, or Regular expressions. It may inherit from @@ -341,13 +339,11 @@ module Puppet::Pops::Model module ClassModule # Go through the gymnastics of making either value or pattern settable - # with syncronization to the other form. A derived value cannot be serialized + # with synchronization to the other form. A derived value cannot be serialized # and we want to serialize the pattern. When recreating the object we need to # recreate it from the pattern string. # The below sets both values if one is changed. # -# alias _value= value= -# alias _pattern= pattern= def value= regexp setValue regexp setPattern regexp.to_s @@ -427,18 +423,6 @@ module Puppet::Pops::Model # class VariableExpression < UnaryExpression; end - # A type reference is a reference to a type. - # - class TypeReference < Expression - contains_one_uni 'type_name', QualifiedReference, :lowerBound => 1 - end - - # An instance reference is a reference to one or many named instances of a particular type - # - class InstanceReferences < TypeReference - contains_many_uni 'names', Expression, :lowerBound => 1 - end - # A resource body describes one resource instance # class ResourceBody < PopsObject @@ -451,6 +435,7 @@ module Puppet::Pops::Model # All derived classes may not support all forms, and these needs to be validated # class AbstractResource < Expression + abstract has_attr 'form', RGen::MetamodelBuilder::DataTypes::Enum.new([:regular, :virtual, :exported ]), :lowerBound => 1, :defaultValueLiteral => "regular" has_attr 'virtual', Boolean, :derived => true has_attr 'exported', Boolean, :derived => true @@ -505,100 +490,8 @@ module Puppet::Pops::Model contains_many_uni 'selectors', SelectorEntry end - # Create Invariant. Future suggested enhancement Puppet Types. - # - class CreateInvariantExpression < Expression - has_attr 'name', String - contains_one_uni 'message_expr', Expression, :lowerBound => 1 - contains_one_uni 'constraint_expr', Expression, :lowerBound => 1 - end - - # Create Attribute. Future suggested enhancement Puppet Types. - # - class CreateAttributeExpression < Expression - has_attr 'name', String, :lowerBound => 1 - - # Should evaluate to name of datatype (String, Integer, Float, Boolean) or an EEnum metadata - # (created by CreateEnumExpression). If omitted, the type is a String. - # - contains_one_uni 'type', Expression - contains_one_uni 'min_expr', Expression - contains_one_uni 'max_expr', Expression - contains_one_uni 'default_value', Expression - contains_one_uni 'input_transformer', Expression - contains_one_uni 'derived_expr', Expression - end - - # Create Attribute. Future suggested enhancement Puppet Types. - # - class CreateEnumExpression < Expression - has_attr 'name', String - contains_one_uni 'values', Expression - end - - # Create Type. Future suggested enhancement Puppet Types. - # - class CreateTypeExpression < Expression - has_attr 'name', String, :lowerBound => 1 - has_attr 'super_name', String - contains_many_uni 'attributes', CreateAttributeExpression - contains_many_uni 'invariants', CreateInvariantExpression - end - - # Create ResourceType. Future suggested enhancement Puppet Types. - # @todo UNFINISHED - # - class CreateResourceType < CreateTypeExpression - # TODO CreateResourceType - # - has features required by the provider - provider invariant? - # - super type must be a ResourceType - end - # A named access expression looks up a named part. (e.g. $a.b) # class NamedAccessExpression < BinaryExpression; end - # A named function definition declares and defines a new function - # Future enhancement. - # - class NamedFunctionDefinition < NamedDefinition; end - - # Future enhancements - Injection - Unfinished - # - module Injection - # A producer expression produces an instance of a type. The instance is initialized - # from an expression (or from the current scope if this expression is missing). - #-- - # new. to handle production of injections - # - class Producer < Expression - contains_one_uni 'type_name', TypeReference, :lowerBound => 1 - contains_one_uni 'instantiation_expr', Expression - end - - # A binding entry binds one capability generically or named, specifies default bindings or - # composition of other bindings. - # - class BindingEntry < PopsObject - contains_one_uni 'key', Expression - contains_one_uni 'value', Expression - end - - # Defines an optionally named binding. - # - class Binding < Expression - contains_one_uni 'title_expr', Expression - contains_many_uni 'bindings', BindingEntry - end - - # An injection provides a value bound in the effective binding scope. The injection - # is based on a type (a capability) and an optional list of instance names (i.e. an InstanceReference). - # Invariants: optional and instantiation are mutually exclusive - # - class InjectExpression < Expression - has_attr 'optional', Boolean - contains_one_uni 'binding', Expression, :lowerBound => 1 - contains_one_uni 'instantiation', Expression - end - end end diff --git a/lib/puppet/pops/parser/parser_support.rb b/lib/puppet/pops/parser/parser_support.rb index dfff7b15c..8c8e874ee 100644 --- a/lib/puppet/pops/parser/parser_support.rb +++ b/lib/puppet/pops/parser/parser_support.rb @@ -101,12 +101,12 @@ class Puppet::Pops::Parser::Parser error = "Syntax error at #{value_at}" # The 'expected' is only of value at end of input, otherwise any parse error involving a - # start of a pair will be reported as expecting the close of the pair - e.g. "$x.each |$x {", would + # start of a pair will be reported as expecting the close of the pair - e.g. "$x.each |$x {|", would # report that "seeing the '{', the '}' is expected. That would be wrong. # Real "expected" tokens are very difficult to compute (would require parsing of racc output data). Output of the stack # could help, but can require extensive backtracking and produce many options. # - # The lexer should handle the "expected instead of end of file for strings, and interpolation", other exåectancies + # The lexer should handle the "expected instead of end of file for strings, and interpolation", other expectancies # must be handled by the grammar. The lexer may have enqueued tokens far ahead - the lexer's opinion about this # is not trustworthy. # @@ -152,23 +152,6 @@ class Puppet::Pops::Parser::Parser factory.doc = doc_string end -# def sourcepos(o) -# if !o -# Puppet::Pops::Adapters::SourcePosAdapter.new -# elsif o.is_a? Puppet::Pops::Model::Factory -# # It is a built model element with loc set returns start at pos 0 -# o.loc -# else -# loc = Puppet::Pops::Adapters::SourcePosAdapter.new -# # It must be a token -# loc.line = o[:line] -# loc.pos = o[:pos] -# loc.offset = o[:offset] -# loc.length = o[:length] -# loc -# end -# end - def aryfy(o) o = [o] unless o.is_a?(Array) o diff --git a/spec/unit/pops/factory_spec.rb b/spec/unit/pops/factory_spec.rb index beb0c18f3..779a0630d 100644 --- a/spec/unit/pops/factory_spec.rb +++ b/spec/unit/pops/factory_spec.rb @@ -239,29 +239,6 @@ describe Puppet::Pops::Model::Factory do end end - context "When processing instance / resource references" do - it "should produce an InstanceReference without a reference" do - built = INSTANCE(QREF('a'), []).current - built.class.should == Puppet::Pops::Model::InstanceReferences - built.names.size.should == 0 - end - - it "should produce an InstanceReference with one reference" do - built = INSTANCE(QREF('a'), [QNAME('b')]).current - built.class.should == Puppet::Pops::Model::InstanceReferences - built.names.size.should == 1 - built.names[0].value.should == 'b' - end - - it "should produce an InstanceReference with two references" do - built = INSTANCE(QREF('a'), [QNAME('b'), QNAME('c')]).current - built.class.should == Puppet::Pops::Model::InstanceReferences - built.names.size.should == 2 - built.names[0].value.should == 'b' - built.names[1].value.should == 'c' - end - end - context "When processing UNLESS" do it "should create an UNLESS expression with then part" do built = UNLESS(true, literal(1), nil).current From 8a4de7a443f474b2a0af0f6c14e1f7485b3d9bdc Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 4 Nov 2013 02:29:50 +0100 Subject: [PATCH 067/800] (#22363) Add top level Program model, and add pops AST bridge This adds classes Puppet::Parser::AST::PopsBridge that bridges between a top level Puppet::Pops::Model::Program and the 3x API as well as a Puppet::Pops::Model::Expression. These classes makes it possible to wire the model into the 3x compiler, Resource and Type objects. The AST equivalent objects for host class, node and definition are bypassed and these are instead handled by the bridge. The Puppet::Pops::Model::Program object is populated by the e_parser to avoid having to scan for the definitions (these are now found as references in the top level Program model that is returned by the parser if the "program" contains any definitions, else just the body. The Program class is transparent in all other respects (it does not have a dump footprint, and when evaluated it just evauates its body. The bridge classes are yet untested, but the regular tests should pass as the new evaluator has not yet replaced the 3x evaluation. --- lib/puppet/parser/ast/pops_bridge.rb | 127 ++++++++++++++++++++ lib/puppet/pops/evaluator/evaluator_impl.rb | 22 +++- lib/puppet/pops/model/ast_transformer.rb | 4 + lib/puppet/pops/model/factory.rb | 17 ++- lib/puppet/pops/model/model.rb | 17 ++- lib/puppet/pops/model/model_tree_dumper.rb | 6 + lib/puppet/pops/parser/egrammar.ra | 10 +- lib/puppet/pops/parser/eparser.rb | 10 +- lib/puppet/pops/parser/parser_support.rb | 12 ++ 9 files changed, 202 insertions(+), 23 deletions(-) create mode 100644 lib/puppet/parser/ast/pops_bridge.rb diff --git a/lib/puppet/parser/ast/pops_bridge.rb b/lib/puppet/parser/ast/pops_bridge.rb new file mode 100644 index 000000000..8115b3d49 --- /dev/null +++ b/lib/puppet/parser/ast/pops_bridge.rb @@ -0,0 +1,127 @@ +require 'puppet/parser/ast/top_level_construct' +require 'pops' + +# The AST::Bridge contains classes that bridges between the new Pops based model +# and the 3.x AST. This is required to be able to reuse the Puppet::Resource::Type which is +# fundamental for the rest of the logic. +# +class Puppet::Parser::AST::PopsBridge + + # Bridges to one Pops Model Expression + # The @value is the expression + # This is used to represent the body of a class, definition, or node, and for each parameter's defauöt value + # expression. + # + class Expression < Puppet::Parser::ASTLeaf + + def to_s + Puppet::Pops::ModelTreeDumper.new(dump(@value)) + end + + def evaluate(scope) + # TODO: This is wasteful, a new evaluator created for each expression + evaluator = Puppet::Pops::Evaluator::EvaluatingParser::Transitional.new() + evaluator.evaluate(scope, @value) + end + end + + # Bridges the top level "Program" produced by the pops parser. + # Its main purpose is to give one point where all definitions are instantiated (actually defined since the + # Puppet 3x terminology is somewhat misleading - the definitions are instantiated, but instances of the created types + # are not created, that happens when classes are included / required, nodes are matched and when resources are instantiated + # by a resource expression (which is also used to instantiate a host class). + # + class Program < Puppet::Parser::AST::TopLevelConstruct + attr_reader :program_model, :context + + def initialize(program_model, context = {}) + @program_model = model + @context = context + @ast_transformer ||= Puppet::Pops::Model::AstTransformer.new(@context[:file]) + end + + # This is the 3x API, the 3x AST searches through all code to find the instantiatable instructions. + # This pops model based instantiation relies on the parser to build this list while parsing (which is more + # efficient as it avoids one full scan of all logic via recursive enumeration/yield) + # + def instantiate(modname) + decorate_program + @program_model.definitions.collect do |d| + case d + when Puppet::Pops::Model::HostClassDefinition + instantiate_HostClassDefinition(d, modname) + when Puppet::Pops::Model::ResourceTypeDefinition + instantiate_ResourceTypeDefinition(d, modname) + when Puppet::Pops::Model::NodeDefinition + instantiate_NodeDefinition(d, modname) + else + raise Puppet::ParseError("Internal Error: Unknown type of definition - got '#{d.class}'") + end + end.flatten() # flatten since node definition may have returned an array + end + + private + + def instantiate_Parameter(o) + # 3x needs parameters as an array of `[name]` or `[name, value_expr]` + # One problem is that the parameter evaluation takes place in the wrong context in 3x (the caller's and + # can thus reference all sorts of information. Here the value expression is wrapped in an AST Bridge to a Pops + # expression since the Pops side can not control the evaluation + if o.value + [ o.name, Puppet::AST::PopsBridge::Expression.new(:value => o.value) ] + else + [ o.name ] + end + end + + # Produces a hash with data for Definition and HostClass + def args_from_definition(o, modname) + args = { + :arguments => o.parameters.collect {|p| instantiate_Parameter(o) }, + :module_name => modname + } + unless is_nop?(o.body) + args[:code] = Puppet::AST::Bridge::PopsBridge::Expression.new(:value => o.body) + end + @ast_transformer.merge_location(args, o) + end + + def instantiate_HostClassDefinition(o, modname) + args = args_from_definition(o, modname) + args[:parent] = o.parent_class + Puppet::Resource::Type.new(:hostclass, o.name, @context.merge(args)) + end + + def instantiate_ResourceTypeDefinition(o, modname) + Puppet::Resource::Type.new(:definition, o.name, @context.merge(args_from_definition(o, modname))) + end + + def instantiate_NodeDefinition(o, modname) + args = { :module_name => modname } + + unless is_nop?(o.body) + args[:code] = Puppet::AST::Bridge::PopsBridge::Expression.new(:value => o.body) + end + + unless is_nop?(o.parent) + args[:parent] = @ast_transformer.hostname(o.parent) + end + + host_matches = @ast_transformer.hostname(o.host_matches) + @ast_transformer.merge_location(args, o) + host_matches.collect do |name| + Puppet::Resource::Type.new(:node, name, @context.merge(args)) + end + end + + def code() + Puppet::AST::Bridge::PopsBridge::Expression.new(:value => @value) + end + + def is_nop?(o) + @ast_transformer.is_nop?(o) + end + + end + +end diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index e36c90bd0..81d1124fd 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -622,13 +622,23 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator evaluate(o.expr, scope) end - # TODO: - # Definition < Expression (abstract) - # NamedDefinition < Definition (abstract) - # ResourceTypeDefinition < NamedDefinition + # This evaluates classes, nodes and resource type definitions to nil, since 3x: + # instantiates them, and evaluates thwir parameters and body. This is acheived by + # providing bridge AST classes in Puppet::Parser::AST::PopsBridge that bridges a + # Pops Program and a Pops Expression. + # + # Since all Definitions are handled "out of band", they are treated as a no-op when + # evaluated. + # + def eval_Definition(o, scope) + nil + end - # NodeDefinition < Expression - # HostClassDefinition < NamedDefinition + def eval_Program(o, scope) + evaluate(o.body, scope) + end + + # TODO: # ResourceExpression < Expression # class ResourceBody < ASTObject # ResourceDefaultsExpression < Expression diff --git a/lib/puppet/pops/model/ast_transformer.rb b/lib/puppet/pops/model/ast_transformer.rb index dee4f95bd..acff56ff3 100644 --- a/lib/puppet/pops/model/ast_transformer.rb +++ b/lib/puppet/pops/model/ast_transformer.rb @@ -545,6 +545,10 @@ class Puppet::Pops::Model::AstTransformer transform(o.expr) end + def transform_Program(o) + transform(o.body) + end + def transform_IfExpression(o) args = { :test => transform(o.test), :statements => transform(o.then_expr) } args[:else] = transform(o.else_expr) # Tests say Nop should be there (unless is_nop? o.else_expr), probably not needed diff --git a/lib/puppet/pops/model/factory.rb b/lib/puppet/pops/model/factory.rb index 4f4cf8bd0..9319a7b45 100644 --- a/lib/puppet/pops/model/factory.rb +++ b/lib/puppet/pops/model/factory.rb @@ -224,7 +224,7 @@ class Puppet::Pops::Model::Factory end end - def build_Definition(o, parameters, body) + def build_LambdaExpression(o, parameters, body) parameters.each {|p| o.addParameters(build(p)) } b = f_build_body(body) o.body = b.current if b @@ -232,7 +232,9 @@ class Puppet::Pops::Model::Factory end def build_NamedDefinition(o, name, parameters, body) - build_Definition(o, parameters, body) + parameters.each {|p| o.addParameters(build(p)) } + b = f_build_body(body) + o.body = b.current if b o.name = name o end @@ -307,6 +309,13 @@ class Puppet::Pops::Model::Factory o end + def build_Program(o, body, definitions) + o.body = to_ops(body) + # non containment + definitions.each { |d| o.addDefinitions(d) } + o + end + def build_QualifiedName(o, name) o.value = name.to_s o @@ -659,6 +668,10 @@ class Puppet::Pops::Model::Factory new(Model::ResourceBody, resource_title, attribute_operations) end + def self.PROGRAM(body, definitions) + new(Model::Program, body, definitions) + end + # Builds a BlockExpression if args size > 1, else the single expression/value in args def self.block_or_expression(*args) if args.size > 1 diff --git a/lib/puppet/pops/model/model.rb b/lib/puppet/pops/model/model.rb index 77cc1bc9f..670b7f2ad 100644 --- a/lib/puppet/pops/model/model.rb +++ b/lib/puppet/pops/model/model.rb @@ -241,14 +241,14 @@ module Puppet::Pops::Model # class Definition < Expression abstract - contains_many_uni 'parameters', Parameter - contains_one_uni 'body', Expression end - # Abstract base class for named definitions. + # Abstract base class for named and parameterized definitions. class NamedDefinition < Definition abstract has_attr 'name', String, :lowerBound => 1 + contains_many_uni 'parameters', Parameter + contains_one_uni 'body', Expression end # A resource type definition (a 'define' in the DSL). @@ -259,7 +259,7 @@ module Puppet::Pops::Model # A node definition matches hosts using Strings, or Regular expressions. It may inherit from # a parent node (also using a String or Regular expression). # - class NodeDefinition < Expression + class NodeDefinition < Definition contains_one_uni 'parent', Expression contains_many_uni 'host_matches', Expression, :lowerBound => 1 contains_one_uni 'body', Expression @@ -272,7 +272,10 @@ module Puppet::Pops::Model end # i.e {|parameters| body } - class LambdaExpression < Definition; end + class LambdaExpression < Expression + contains_many_uni 'parameters', Parameter + contains_one_uni 'body', Expression + end # If expression. If test is true, the then_expr part should be evaluated, else the (optional) # else_expr. An 'elsif' is simply an else_expr = IfExpression, and 'else' is simply else == Block. @@ -494,4 +497,8 @@ module Puppet::Pops::Model # class NamedAccessExpression < BinaryExpression; end + class Program < PopsObject + contains_one_uni 'body', Expression + has_many 'definitions', Definition + end end diff --git a/lib/puppet/pops/model/model_tree_dumper.rb b/lib/puppet/pops/model/model_tree_dumper.rb index b9d923401..553021271 100644 --- a/lib/puppet/pops/model/model_tree_dumper.rb +++ b/lib/puppet/pops/model/model_tree_dumper.rb @@ -245,6 +245,12 @@ class Puppet::Pops::Model::ModelTreeDumper < Puppet::Pops::Model::TreeDumper do_dump(o.expr) end + # Hides that Program exists in the output (only its body is shown), the definitions are just + # references to contained classes, resource types, and nodes + def dump_Program(o) + dump(o.body) + end + def dump_IfExpression o result = ["if", do_dump(o.test), :indent, :break, ["then", :indent, do_dump(o.then_expr), :dedent]] diff --git a/lib/puppet/pops/parser/egrammar.ra b/lib/puppet/pops/parser/egrammar.ra index 4895ea2ef..c283302b5 100644 --- a/lib/puppet/pops/parser/egrammar.ra +++ b/lib/puppet/pops/parser/egrammar.ra @@ -57,7 +57,7 @@ preclow rule # Produces [Model::BlockExpression, Model::Expression, nil] depending on multiple statements, single statement or empty program - : statements { result = Factory.block_or_expression(*val[0]) } + : statements { result = create_program(Factory.block_or_expression(*val[0])) } | nil # Produces a semantic model (non validated, but semantically adjusted). @@ -491,7 +491,7 @@ attribute_operations # definition_expression : DEFINE classname parameter_list LBRACE opt_statements RBRACE { - result = Factory.DEFINITION(classname(val[1][:value]), val[2], val[4]) + result = add_definition(Factory.DEFINITION(classname(val[1][:value]), val[2], val[4])) loc result, val[0], val[5] # New lexer does not keep track of this, this is done in validation if @lexer.respond_to?(:'indefine=') @@ -507,7 +507,7 @@ hostclass_expression : CLASS stacked_classname parameter_list classparent LBRACE opt_statements RBRACE { # Remove this class' name from the namestack as all nested classes have been parsed namepop - result = Factory.HOSTCLASS(classname(val[1][:value]), val[2], token_text(val[3]), val[5]) + result = add_definition(Factory.HOSTCLASS(classname(val[1][:value]), val[2], token_text(val[3]), val[5])) loc result, val[0], val[6] } @@ -541,11 +541,11 @@ hostclass_expression # node_definition_expression : NODE hostnames nodeparent LBRACE statements RBRACE { - result = Factory.NODE(val[1], val[2], val[4]) + result = add_definition(Factory.NODE(val[1], val[2], val[4])) loc result, val[0], val[5] } | NODE hostnames nodeparent LBRACE RBRACE { - result = Factory.NODE(val[1], val[2], nil) + result = add_definition(Factory.NODE(val[1], val[2], nil)) loc result, val[0], val[4] } diff --git a/lib/puppet/pops/parser/eparser.rb b/lib/puppet/pops/parser/eparser.rb index 19b742b48..12f3f2c89 100644 --- a/lib/puppet/pops/parser/eparser.rb +++ b/lib/puppet/pops/parser/eparser.rb @@ -1072,7 +1072,7 @@ Racc_debug_parser = false module_eval(<<'.,.,', 'egrammar.ra', 59) def _reduce_1(val, _values, result) - result = Factory.block_or_expression(*val[0]) + result = create_program(Factory.block_or_expression(*val[0])) result end .,., @@ -1927,7 +1927,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 483) module_eval(<<'.,.,', 'egrammar.ra', 493) def _reduce_132(val, _values, result) - result = Factory.DEFINITION(classname(val[1][:value]), val[2], val[4]) + result = add_definition(Factory.DEFINITION(classname(val[1][:value]), val[2], val[4])) loc result, val[0], val[5] # New lexer does not keep track of this, this is done in validation if @lexer.respond_to?(:'indefine=') @@ -1942,7 +1942,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 507) def _reduce_133(val, _values, result) # Remove this class' name from the namestack as all nested classes have been parsed namepop - result = Factory.HOSTCLASS(classname(val[1][:value]), val[2], token_text(val[3]), val[5]) + result = add_definition(Factory.HOSTCLASS(classname(val[1][:value]), val[2], token_text(val[3]), val[5])) loc result, val[0], val[6] result @@ -1975,7 +1975,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 526) module_eval(<<'.,.,', 'egrammar.ra', 543) def _reduce_141(val, _values, result) - result = Factory.NODE(val[1], val[2], val[4]) + result = add_definition(Factory.NODE(val[1], val[2], val[4])) loc result, val[0], val[5] result @@ -1984,7 +1984,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 543) module_eval(<<'.,.,', 'egrammar.ra', 547) def _reduce_142(val, _values, result) - result = Factory.NODE(val[1], val[2], nil) + result = add_definition(Factory.NODE(val[1], val[2], nil)) loc result, val[0], val[4] result diff --git a/lib/puppet/pops/parser/parser_support.rb b/lib/puppet/pops/parser/parser_support.rb index 8c8e874ee..55bcd8652 100644 --- a/lib/puppet/pops/parser/parser_support.rb +++ b/lib/puppet/pops/parser/parser_support.rb @@ -24,6 +24,7 @@ class Puppet::Pops::Parser::Parser include Puppet::Resource::TypeCollectionHelper attr_accessor :lexer + attr_reader :definitions # Returns the token text of the given lexer token, or nil, if token is nil def token_text t @@ -86,6 +87,7 @@ class Puppet::Pops::Parser::Parser def initvars @lexer = Puppet::Pops::Parser::Lexer2.new @namestack = [] + @definitions = [] end # This is a callback from the generated grammar (when an error occurs while parsing) @@ -169,6 +171,11 @@ class Puppet::Pops::Parser::Parser @namestack.pop end + def add_definition(definition) + @definitions << definition.current + definition + end + # Transforms an array of expressions containing literal name expressions to calls if followed by an # expression, or expression list # @@ -176,6 +183,11 @@ class Puppet::Pops::Parser::Parser Factory.transform_calls(expressions) end + # If there are definitions that require initialization a Program is produced, else the body + def create_program(body) + definitions.empty? ? body : Factory.PROGRAM(body, definitions) + end + # Performs the parsing and returns the resulting model. # The lexer holds state, and this is setup with {#parse_string}, or {#parse_file}. # From f523ac8b643ce0ff6bad72498715e57fa0c91d9a Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Wed, 6 Nov 2013 17:46:57 +0100 Subject: [PATCH 068/800] (#22363) First wiring of new evaluator to parser factory This is the initial work of wiring the new evaluator into the regular puppet logic. This is achieved via a new E4ParserAdapter that is used together with the AST::PopsBridge classes. The ParserFactory is modified to instantiate the new E4ParserAdapter. Since not everything is finished, there are many tests failing, but at this point a command like puppet apply --parser future -e 'notice hi' is parsed and evaluated using the new evaluator. --- lib/puppet/parser/ast.rb | 1 + lib/puppet/parser/ast/pops_bridge.rb | 32 ++++++- lib/puppet/parser/e4_parser_adapter.rb | 126 +++++++++++++++++++++++++ lib/puppet/parser/parser_factory.rb | 13 ++- 4 files changed, 166 insertions(+), 6 deletions(-) create mode 100644 lib/puppet/parser/e4_parser_adapter.rb diff --git a/lib/puppet/parser/ast.rb b/lib/puppet/parser/ast.rb index 438cb49b4..4cb1f3705 100644 --- a/lib/puppet/parser/ast.rb +++ b/lib/puppet/parser/ast.rb @@ -128,3 +128,4 @@ require 'puppet/parser/ast/resourceparam' require 'puppet/parser/ast/selector' require 'puppet/parser/ast/tag' require 'puppet/parser/ast/vardef' +require 'puppet/parser/ast/pops_bridge' diff --git a/lib/puppet/parser/ast/pops_bridge.rb b/lib/puppet/parser/ast/pops_bridge.rb index 8115b3d49..753327246 100644 --- a/lib/puppet/parser/ast/pops_bridge.rb +++ b/lib/puppet/parser/ast/pops_bridge.rb @@ -1,5 +1,5 @@ require 'puppet/parser/ast/top_level_construct' -require 'pops' +require 'puppet/pops' # The AST::Bridge contains classes that bridges between the new Pops based model # and the 3.x AST. This is required to be able to reuse the Puppet::Resource::Type which is @@ -12,16 +12,26 @@ class Puppet::Parser::AST::PopsBridge # This is used to represent the body of a class, definition, or node, and for each parameter's defauöt value # expression. # - class Expression < Puppet::Parser::ASTLeaf + class Expression < Puppet::Parser::AST::Leaf + + def initialize args + super + @@evaluator ||= Puppet::Pops::Parser::EvaluatingParser::Transitional.new() + end def to_s Puppet::Pops::ModelTreeDumper.new(dump(@value)) end def evaluate(scope) - # TODO: This is wasteful, a new evaluator created for each expression - evaluator = Puppet::Pops::Evaluator::EvaluatingParser::Transitional.new() - evaluator.evaluate(scope, @value) + @@evaluator.evaluate(scope, @value) + end + + # Adapts to 3x where top level constructs needs to have each to iterate over children. Short circuit this + # by yielding self. By adding this there is no need to wrap a pops expression inside an AST::BlockExpression + # + def each + yield self end end @@ -38,6 +48,7 @@ class Puppet::Parser::AST::PopsBridge @program_model = model @context = context @ast_transformer ||= Puppet::Pops::Model::AstTransformer.new(@context[:file]) + @@evaluator ||= Puppet::Pops::Parser::EvaluatingParser::Transitional.new() end # This is the 3x API, the 3x AST searches through all code to find the instantiatable instructions. @@ -60,6 +71,17 @@ class Puppet::Parser::AST::PopsBridge end.flatten() # flatten since node definition may have returned an array end + def evaluate(scope) + @@evaluator.evaluate(scope, model) + end + + # Adapts to 3x where top level constructs needs to have each to iterate over children. Short circuit this + # by yielding self. This means that the HostClass container will call this bridge instance with `instantiate`. + # + def each + yield self + end + private def instantiate_Parameter(o) diff --git a/lib/puppet/parser/e4_parser_adapter.rb b/lib/puppet/parser/e4_parser_adapter.rb new file mode 100644 index 000000000..3a43488b4 --- /dev/null +++ b/lib/puppet/parser/e4_parser_adapter.rb @@ -0,0 +1,126 @@ +require 'puppet/pops' + +module Puppet; module Parser; end; end; +# Adapts an egrammar/eparser to respond to the public API of the classic parser +# and makes use of the new evaluator. +# +class Puppet::Parser::E4ParserAdapter + + def initialize() + @file = '' + @string = '' + @use = :undefined + @@evaluating_parser ||= Puppet::Pops::Parser::EvaluatingParser::Transitional.new() + end + + def file=(file) + @file = file + @use = :file + end + + def parse(string = nil) + if @file =~ /\.rb$/ + # Will throw an error + parse_ruby_file + end + self.string= string if string + + parse_result = + if @use == :string + # Parse with a source_file to set in created AST objects (it was either given, or it may be unknown + # if caller did not set a file and the present a string. + # + @@evaluating_parser.parse_string(@string, @file || "unknown-source-location") + else + @@evaluating_parser.parse_file(@file) + end + + # the parse_result may be + # * empty / nil (no input) + # * a Model::Program + # * a Model::Expression + # + model = parse_result.current + args = {} + Puppet::Pops::Model::AstTransformer.new(@file).merge_location(args, model) + + ast_code = + if model.is_a? Puppet::Pops::Model::Program + Puppet::Parser::AST::PopsBridge::Program.new(model, args) + else + args[:value] = model + Puppet::Parser::AST::PopsBridge::Expression.new(args) + end + # Create the "main" class for the content - this content will get merged with all other "main" content + Puppet::Parser::AST::Hostclass.new('', :code => ast_code) + + end + + def validate(parse_result) + # TODO: This is too many hoops to jump through... ugly API + # could reference a ValidatorFactory.validator_3_1(acceptor) instead. + # and let the factory abstract the rest. + # + return unless parse_result + + acceptor = Puppet::Pops::Validation::Acceptor.new + validator = Puppet::Pops::Validation::ValidatorFactory_3_1.new().validator(acceptor) + validator.validate(parse_result) + + max_errors = Puppet[:max_errors] + max_warnings = Puppet[:max_warnings] + 1 + max_deprecations = Puppet[:max_deprecations] + 1 + + # If there are warnings output them + warnings = acceptor.warnings + if warnings.size > 0 + formatter = Puppet::Pops::Validation::DiagnosticFormatterPuppetStyle.new + emitted_w = 0 + emitted_dw = 0 + acceptor.warnings.each {|w| + if w.severity == :deprecation + # Do *not* call Puppet.deprecation_warning it is for internal deprecation, not + # deprecation of constructs in manifests! (It is not designed for that purpose even if + # used throughout the code base). + # + Puppet.warning(formatter.format(w)) if emitted_dw < max_deprecations + emitted_dw += 1 + else + Puppet.warning(formatter.format(w)) if emitted_w < max_warnings + emitted_w += 1 + end + break if emitted_w > max_warnings && emitted_dw > max_deprecations # but only then + } + end + + # If there were errors, report the first found. Use a puppet style formatter. + errors = acceptor.errors + if errors.size > 0 + formatter = Puppet::Pops::Validation::DiagnosticFormatterPuppetStyle.new + if errors.size == 1 || max_errors <= 1 + # raise immediately + raise Puppet::ParseError.new(formatter.format(errors[0])) + end + emitted = 0 + errors.each do |e| + Puppet.err(formatter.format(e)) + emitted += 1 + break if emitted >= max_errors + end + warnings_message = warnings.size > 0 ? ", and #{warnings.size} warnings" : "" + giving_up_message = "Found #{errors.size} errors#{warnings_message}. Giving up" + exception = Puppet::ParseError.new(giving_up_message) + exception.file = errors[0].file + raise exception + end + end + + def string=(string) + @string = string + @use = :string + end + + def parse_ruby_file + raise Puppet::ParseError, "Ruby DSL is no longer supported. Attempt to parse #{@file}" + end +end diff --git a/lib/puppet/parser/parser_factory.rb b/lib/puppet/parser/parser_factory.rb index 06c612f39..7e76bf652 100644 --- a/lib/puppet/parser/parser_factory.rb +++ b/lib/puppet/parser/parser_factory.rb @@ -11,7 +11,8 @@ module Puppet::Parser def self.parser(environment) case Puppet[:parser] when 'future' - eparser(environment) + evaluating_parser() +# eparser(environment) else classic_parser(environment) end @@ -24,6 +25,16 @@ module Puppet::Parser Puppet::Parser::Parser.new(environment) end + # Returns an instance of an EvaluatingParser + def self.evaluating_parser + # Since RGen is optional, test that it is installed + @@asserted ||= false + assert_rgen_installed() unless @@asserted + @@asserted = true + require 'puppet/parser/e4_parser_adapter' + E4ParserAdapter.new() + end + # Creates an instance of the expression based parser 'eparser' # def self.eparser(environment) From acb7f73ea4418e196a6e76ca141c4ed92a7844ef Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 7 Nov 2013 00:38:25 +0100 Subject: [PATCH 069/800] (#22363) Fix typos, missing methods The new evaluator's lambda was missing parameter_count method. All validation of classes failed due to Program not recognized as top level. --- lib/puppet/parser/e4_parser_adapter.rb | 2 +- lib/puppet/pops/evaluator/closure.rb | 8 ++++++++ lib/puppet/pops/validation/checker3_1.rb | 6 ++++++ lib/puppet/pops/validation/checker4_0.rb | 5 +++++ 4 files changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/puppet/parser/e4_parser_adapter.rb b/lib/puppet/parser/e4_parser_adapter.rb index 3a43488b4..ecb02995b 100644 --- a/lib/puppet/parser/e4_parser_adapter.rb +++ b/lib/puppet/parser/e4_parser_adapter.rb @@ -40,7 +40,7 @@ class Puppet::Parser::E4ParserAdapter # * a Model::Program # * a Model::Expression # - model = parse_result.current + model = parse_result.nil? ? nil : parse_result.current args = {} Puppet::Pops::Model::AstTransformer.new(@file).merge_location(args, model) diff --git a/lib/puppet/pops/evaluator/closure.rb b/lib/puppet/pops/evaluator/closure.rb index 58d9addd1..ef5593cbc 100644 --- a/lib/puppet/pops/evaluator/closure.rb +++ b/lib/puppet/pops/evaluator/closure.rb @@ -25,4 +25,12 @@ class Puppet::Pops::Evaluator::Lambda def parameters() @model.parameters || [] end + + # Returns the number of parameters (required and optional) + # @return [Integer] the total number of accepted parameters + def parameter_count + # yes, this is duplication of code, but it saves a method call + (@model.parameters || []).size + end + end diff --git a/lib/puppet/pops/validation/checker3_1.rb b/lib/puppet/pops/validation/checker3_1.rb index da71c47ba..243c5f25d 100644 --- a/lib/puppet/pops/validation/checker3_1.rb +++ b/lib/puppet/pops/validation/checker3_1.rb @@ -518,6 +518,8 @@ class Puppet::Pops::Validation::Checker3_1 end def top_Object(o, definition) + require 'debugger'; debugger + # fail, reached a container that is not top level acceptor.accept(Issues::NOT_TOP_LEVEL, definition) end @@ -531,6 +533,10 @@ class Puppet::Pops::Validation::Checker3_1 # ok, stop scanning parents end + def top_Program(o, definition) + # ok + end + # A LambdaExpression is a BlockExpression, and this method is needed to prevent the polymorph method for BlockExpression # to accept a lambda. # A lambda can not iteratively create classes, nodes or defines as the lambda does not have a closure. diff --git a/lib/puppet/pops/validation/checker4_0.rb b/lib/puppet/pops/validation/checker4_0.rb index 8eea8eacd..26e7f292b 100644 --- a/lib/puppet/pops/validation/checker4_0.rb +++ b/lib/puppet/pops/validation/checker4_0.rb @@ -523,6 +523,7 @@ class Puppet::Pops::Validation::Checker4_0 end def top_Object(o, definition) + require 'debugger'; debugger # fail, reached a container that is not top level acceptor.accept(Issues::NOT_TOP_LEVEL, definition) end @@ -536,6 +537,10 @@ class Puppet::Pops::Validation::Checker4_0 # ok, stop scanning parents end + def top_Program(o, definition) + # ok + end + # A LambdaExpression is a BlockExpression, and this method is needed to prevent the polymorph method for BlockExpression # to accept a lambda. # A lambda can not iteratively create classes, nodes or defines as the lambda does not have a closure. From 86d6f53e0fadee3a106c69c35e34b0e85e514168 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 7 Nov 2013 23:23:32 +0100 Subject: [PATCH 070/800] (#22363) Add evaluation of Resource Expression This adds support for evaluation of resource expressions. The TypeCalculator can now infer a Resource type from a PuppetResource. The evaluator can create new resource instances. Most parser tests are now passing (but a rebase is required since grammar is at par with head, but not the tests). --- lib/puppet/pops/evaluator/evaluator_impl.rb | 35 ++++++++++--- lib/puppet/pops/evaluator/runtime3_support.rb | 49 +++++++++++++++++++ lib/puppet/pops/types/type_calculator.rb | 10 ++++ 3 files changed, 87 insertions(+), 7 deletions(-) diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index 81d1124fd..1ce918a8a 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -34,6 +34,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # Issues = Puppet::Pops::Issues + def initialize @@eval_visitor ||= Puppet::Pops::Visitor.new(self, "eval", 1, 1) @@lvalue_visitor ||= Puppet::Pops::Visitor.new(self, "lvalue", 1, 1) @@ -47,6 +48,10 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator @@relationship_operator ||= Puppet::Pops::Evaluator::RelationshipOperator.new() end + def type_calculator + @@type_calculator + end + # Polymorphic evaluate - calls eval_TYPE # # ## Polymorphic evaluate @@ -117,7 +122,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # associate values with parameters merged = parameters.zip(args) # calculate missing arguments - missing = parameters.slice(args.size, parameters.size - args.size).select {|p| e.value.nil? } + missing = parameters.slice(args.size, parameters.size - args.size).select {|p| p.value.nil? } unless missing.empty? optional = parameters.count { |p| !p.value.nil? } raise ArgumentError, "Too few arguments; #{args.size} for #{optional > 0 ? ' min ' : ''}#{parameters.size - optional}" @@ -598,11 +603,6 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # TODO end - # @todo not implemented - def eval_AttributeOperation o, scope - # TODO - end - # @todo not implemented def eval_OptionalAttributeOperation o, scope # TODO @@ -638,12 +638,33 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator evaluate(o.body, scope) end + def eval_ResourceExpression(o, scope) + exported = o.exported + virtual = o.virtual + type_name = evaluate(o.type_name, scope) + o.bodies.each do |body| + titles = [evaluate(body.title, scope)].flatten + evaluated_parameters = body.operations.map {|op| evaluate(op, scope) } + titles.each do |title| + create_resources(o, scope, virtual, exported, type_name, titles, evaluated_parameters) + end + end + end + + def eval_ResourceDefaultsExpression(o, scope) + end + + def eval_ResourceOverrideExpression(o, scope) + end + + def eval_AttributeOperation(o, scope) + create_resource_parameter(o, scope, o.attribute_name, evaluate(o.value_expr, scope), o.operator) + end # TODO: # ResourceExpression < Expression # class ResourceBody < ASTObject # ResourceDefaultsExpression < Expression # ResourceOverrideExpression < Expression - # NamedAccessExpression < Expression # Puppet 3.1 AST only supports calling a function by name (it is not possible to produce a function # that is then called). TODO- should puppet 4 accept this? It is very powerful in combiantion with diff --git a/lib/puppet/pops/evaluator/runtime3_support.rb b/lib/puppet/pops/evaluator/runtime3_support.rb index 0be315bd6..4dc72d9f6 100644 --- a/lib/puppet/pops/evaluator/runtime3_support.rb +++ b/lib/puppet/pops/evaluator/runtime3_support.rb @@ -176,6 +176,55 @@ module Puppet::Pops::Evaluator::Runtime3Support Puppet::Parser::Functions.rvalue?(name) end + # The o is used for source reference + # TODO: The line information is wrong - it is cheating atm. + def create_resource_parameter(o, scope, name, value, operator) + Puppet::Parser::Resource::Param.new( + :name => name, + :value => value, + :source => scope.source, :line => -1, :file => 'TODO:Get file', + :add => operator == :'+>' + ) + end + + def create_resources(o, scope, virtual, exported, type_name, resource_titles, evaluated_parameters) + + # resolve in scope. TODO: Investigate what happens here - opportunity to optimize? + fully_qualified_type, resource_titles = scope.resolve_type_and_titles(type_name, resource_titles) + + # Build a resource for each title (there may be only one) + resource_titles.map do |resource_title| + resource = Puppet::Parser::Resource.new( + fully_qualified_type, resource_title, + :parameters => evaluated_parameters, + # TODO: Location + :file => 'TODO: file location', + :line => -1, + :exported => exported, + :virtual => virtual, + # WTF is this? Which source is this? The file? The name of the context ? + :source => scope.source, + :scope => scope, + :strict => true + ) + + if resource.resource_type.is_a? Puppet::Resource::Type + resource.resource_type.instantiate_resource(scope, resource) + end + scope.compiler.add_resource(scope, resource) + scope.compiler.evaluate_classes([resource_title], scope, false, true) if fully_qualified_type == 'class' + # Turn the resource into a PType (a reference to a resource type) + # weed out nil's + resource_to_ptype(resource) + end.flatten.compact + + end + + def resource_to_ptype(resource) + nil if resource.nil? + type_calculator.infer(resource) + end + # This is the same type of "truth" as used in the current Puppet DSL. # def is_true? o diff --git a/lib/puppet/pops/types/type_calculator.rb b/lib/puppet/pops/types/type_calculator.rb index 922ec7ea4..c1c3d83ca 100644 --- a/lib/puppet/pops/types/type_calculator.rb +++ b/lib/puppet/pops/types/type_calculator.rb @@ -406,6 +406,16 @@ class Puppet::Pops::Types::TypeCalculator Types::PBooleanType.new() end + # @api private + # A Puppet::Parser::Resource, or Puppet::Resource + # + def infer_Resource(o) + t = Types::PResourceType.new() + t.type_name = o.type.to_s + t.title = o.title + t + end + # @api private def infer_Array(o) type = Types::PArrayType.new() From 0f3a988e7233b2597d86820e19a710b435299d29 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 8 Nov 2013 17:05:54 +0100 Subject: [PATCH 071/800] (#22363) Fix issues relating to handling of undef and "to_s" This adds the polymorph string_ visitor to the evluator for the purpose of producing the string when doing interpolation. Previously this was simply done by doing .to_s on the object which then produced the text 'under' for the symbol 'undef'. --- lib/puppet/pops/evaluator/evaluator_impl.rb | 24 ++++++++++++++++++--- lib/puppet/pops/validation/checker3_1.rb | 2 -- lib/puppet/pops/validation/checker4_0.rb | 1 - 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index 1ce918a8a..02e711319 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -40,6 +40,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator @@lvalue_visitor ||= Puppet::Pops::Visitor.new(self, "lvalue", 1, 1) @@assign_visitor ||= Puppet::Pops::Visitor.new(self, "assign", 3, 3) @@call_visitor ||= Puppet::Pops::Visitor.new(self, "call", 3, 3) + @@string_visitor ||= Puppet::Pops::Visitor.new(self, "string", 1, 1) @@type_calculator ||= Puppet::Pops::Types::TypeCalculator.new() @@type_parser ||= Puppet::Pops::Types::TypeParser.new() @@ -104,6 +105,10 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator @@lvalue_visitor.visit_this(self, o, scope) end + def string(o, scope) + @@string_visitor.visit_this(self, o, scope) + end + # Call a closure - Can only be called with a Closure (for now), may be refactored later # to also handle other types of calls (function calls are also handled by CallNamedFunction and CallMethod, they # could create similar objects to Closure, wait until other types of defines are instantiated - they may behave @@ -782,7 +787,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # Evaluates double quoted strings that may contain interpolation # def eval_ConcatenatedString o, scope - o.segments.collect {|expr| evaluate(expr, scope).to_s}.join + o.segments.collect {|expr| string(evaluate(expr, scope), scope)}.join end @@ -799,13 +804,26 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator def eval_TextExpression o, scope if o.expr.is_a?(Puppet::Pops::Model::QualifiedName) # TODO: formalize, when scope returns nil, vs error - get_variable_value(o.expr.value, o, scope).to_s + string(get_variable_value(o.expr.value, o, scope), scope) else # TODO: This is not very good as it forces the to_s to apply to all kind of results # It should call a polymorph method to allow to_s to be applied in general, and possible some other # to string formatter for other values (like PType Objects). # - evaluate(o.expr, scope).to_s + string(evaluate(o.expr, scope), scope) + end + end + + def string_Object(o, scope) + o.to_s + end + + def string_Symbol(o, scope) + case o + when :undef + '' + else + o.to_s end end diff --git a/lib/puppet/pops/validation/checker3_1.rb b/lib/puppet/pops/validation/checker3_1.rb index 243c5f25d..d752db8b7 100644 --- a/lib/puppet/pops/validation/checker3_1.rb +++ b/lib/puppet/pops/validation/checker3_1.rb @@ -518,8 +518,6 @@ class Puppet::Pops::Validation::Checker3_1 end def top_Object(o, definition) - require 'debugger'; debugger - # fail, reached a container that is not top level acceptor.accept(Issues::NOT_TOP_LEVEL, definition) end diff --git a/lib/puppet/pops/validation/checker4_0.rb b/lib/puppet/pops/validation/checker4_0.rb index 26e7f292b..6c4c06145 100644 --- a/lib/puppet/pops/validation/checker4_0.rb +++ b/lib/puppet/pops/validation/checker4_0.rb @@ -523,7 +523,6 @@ class Puppet::Pops::Validation::Checker4_0 end def top_Object(o, definition) - require 'debugger'; debugger # fail, reached a container that is not top level acceptor.accept(Issues::NOT_TOP_LEVEL, definition) end From ff5c140cb26ba8d2312993627ea571e5b6fb6761 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 8 Nov 2013 21:50:41 +0100 Subject: [PATCH 072/800] (#22363) Add impl of evaluation of resource defaults and overrides This adds implementation of resource defaults (i.e. Type {a => b}, and resource overrides (i.e. Type[x,y,z] { a => b }). Smoke tested via the command line and small manifests. --- lib/puppet/pops/evaluator/access_operator.rb | 1 + lib/puppet/pops/evaluator/evaluator_impl.rb | 49 ++++++++++++------- lib/puppet/pops/evaluator/runtime3_support.rb | 33 +++++++++++-- 3 files changed, 62 insertions(+), 21 deletions(-) diff --git a/lib/puppet/pops/evaluator/access_operator.rb b/lib/puppet/pops/evaluator/access_operator.rb index a2a9c2f0c..524a403b1 100644 --- a/lib/puppet/pops/evaluator/access_operator.rb +++ b/lib/puppet/pops/evaluator/access_operator.rb @@ -173,6 +173,7 @@ class Puppet::Pops::Evaluator::AccessOperator # def access_PResourceType(o, scope, keys) if keys.size == 0 + # TODO: Either the below or an error return Marshal.load(Marshal.dump(o)) # Deep copy end unless o.title.nil? diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index 02e711319..c4defc1e2 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -618,11 +618,13 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # TODO end - # @todo not implemented - def eval_Parameter o, scope - # TODO - end - +# NOT NEEDED IN THIS VERSION SINCE PARAMTERS ARE HANDLED IN POPS BRIDGE FOR DEFINITIONS, AND AT POINT OF +# CALL FOR LAMBDAS +# +# # @todo not implemented +# def eval_Parameter o, scope +# end +# def eval_ParenthesizedExpression(o, scope) evaluate(o.expr, scope) end @@ -643,33 +645,40 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator evaluate(o.body, scope) end + # Produces Array[PObjectType], an array of resource references + # def eval_ResourceExpression(o, scope) exported = o.exported virtual = o.virtual type_name = evaluate(o.type_name, scope) - o.bodies.each do |body| + o.bodies.map do |body| titles = [evaluate(body.title, scope)].flatten evaluated_parameters = body.operations.map {|op| evaluate(op, scope) } - titles.each do |title| - create_resources(o, scope, virtual, exported, type_name, titles, evaluated_parameters) - end - end - end - - def eval_ResourceDefaultsExpression(o, scope) + create_resources(o, scope, virtual, exported, type_name, titles, evaluated_parameters) + end.flatten.compact end def eval_ResourceOverrideExpression(o, scope) + evaluated_resources = evaluate(o.resources, scope) + evaluated_parameters = o.operations.map { |op| evaluate(op, scope) } + create_resource_overrides(o, scope, [evaluated_resources].flatten, evaluated_parameters) + evaluated_resources end + # Produces 3x array of parameters def eval_AttributeOperation(o, scope) create_resource_parameter(o, scope, o.attribute_name, evaluate(o.value_expr, scope), o.operator) end - # TODO: - # ResourceExpression < Expression - # class ResourceBody < ASTObject - # ResourceDefaultsExpression < Expression - # ResourceOverrideExpression < Expression + + # Sets default parameter values for a type, produces the type + # + def eval_ResourceDefaultsExpression(o, scope) + type_name = o.type_ref.value # a QualifiedName's string value + evaluated_parameters = o.operations.map {|op| evaluate(op, scope) } + create_resource_defaults(o, scope, type_name, evaluated_parameters) + # Produce the type + evaluate(o.type_ref, scope) + end # Puppet 3.1 AST only supports calling a function by name (it is not possible to produce a function # that is then called). TODO- should puppet 4 accept this? It is very powerful in combiantion with @@ -827,6 +836,10 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator end end + def string_PAbstractType(o, scope) + @@type_calculator.string(o) + end + # Produces concatenation / merge of x and y. # # When x is an Array, y of type produces: diff --git a/lib/puppet/pops/evaluator/runtime3_support.rb b/lib/puppet/pops/evaluator/runtime3_support.rb index 4dc72d9f6..c612325ac 100644 --- a/lib/puppet/pops/evaluator/runtime3_support.rb +++ b/lib/puppet/pops/evaluator/runtime3_support.rb @@ -192,7 +192,7 @@ module Puppet::Pops::Evaluator::Runtime3Support # resolve in scope. TODO: Investigate what happens here - opportunity to optimize? fully_qualified_type, resource_titles = scope.resolve_type_and_titles(type_name, resource_titles) - # Build a resource for each title (there may be only one) + # Build a resource for each title resource_titles.map do |resource_title| resource = Puppet::Parser::Resource.new( fully_qualified_type, resource_title, @@ -216,8 +216,35 @@ module Puppet::Pops::Evaluator::Runtime3Support # Turn the resource into a PType (a reference to a resource type) # weed out nil's resource_to_ptype(resource) - end.flatten.compact + end + end + # Defines default parameters for a type with the given name. + # + def create_resource_defaults(o, scope, type_name, evaluated_parameters) + # Note that name must be capitalized in this 3x call + # The 3x impl creates a Resource instance with a bogus title and then asks the created resource + # for the type of the name + scope.define_settings(type_name.capitalize, evaluated_parameters) + end + + # Creates resource overrides for all resource type objects in evaluated_resources. The same set of + # evaluated parameters are applied to all. + # + def create_resource_overrides(o, scope, evaluated_resources, evaluated_parameters) + evaluated_resources.each do |r| + resource = Puppet::Parser::Resource.new( + r.type_name, r.title, + :parameters => evaluated_parameters, + :file => 'TODO: file location', + :line => -1, + # WTF is this? Which source is this? The file? The name of the context ? + :source => scope.source, + :scope => scope + ) + + scope.compiler.add_override(resource) + end end def resource_to_ptype(resource) @@ -249,7 +276,7 @@ module Puppet::Pops::Evaluator::Runtime3Support private # Produces an array with [type, title] from a PCatalogEntryType - # Only used to produce the reference resource instances that are used to form a relationship. + # Used to produce reference resource instances (used when 3x is operating on a resource). # def catalog_type_to_split_type_title(catalog_type) case catalog_type From 775d2091343b3ef31cf86b34b1a534d9a37cc01e Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 10 Nov 2013 01:10:58 +0100 Subject: [PATCH 073/800] (#22363) Add lookup of resource parameter value This makes it possible to get the value of a resource's parameter (or its not yet set default or override value) by using syntax such as: Notify[id][param] --- lib/puppet/pops/evaluator/access_operator.rb | 16 +++++++++-- lib/puppet/pops/evaluator/runtime3_support.rb | 28 +++++++++++++++++++ lib/puppet/pops/issues.rb | 9 ++++++ spec/unit/pops/evaluator/access_ops_spec.rb | 4 +-- .../pops/evaluator/evaluating_parser_spec.rb | 18 ++++++++++-- 5 files changed, 68 insertions(+), 7 deletions(-) diff --git a/lib/puppet/pops/evaluator/access_operator.rb b/lib/puppet/pops/evaluator/access_operator.rb index 524a403b1..83a16bc58 100644 --- a/lib/puppet/pops/evaluator/access_operator.rb +++ b/lib/puppet/pops/evaluator/access_operator.rb @@ -176,8 +176,20 @@ class Puppet::Pops::Evaluator::AccessOperator # TODO: Either the below or an error return Marshal.load(Marshal.dump(o)) # Deep copy end - unless o.title.nil? - fail(Puppet::Pops::Issues::ILLEGAL_TYPE_SPECIALIZATION, semantic.left_expr, {:kind => 'Resource'}) + if !o.title.nil? + # lookup resource and return one or more parameter values + resource = find_resource(scope, o.type_name, o.title) + unless resource + fail(Puppet::Pops::Issues::UNKNOWN_RESOURCE, @semantic, {:type_name => o.type_name, :title => o.title}) + end + result = keys.map do |k| + unless is_parameter_of_resource?(scope, resource, k) + fail(Puppet::Pops::Issues::UNKNOWN_RESOURCE_PARAMETER, @semantic, + {:type_name => o.type_name, :title => o.title, :param_name=>k}) + end + get_resource_parameter_value(scope, resource, k) + end + return result.size <= 1 ? result.pop : result end # type_name is LHS type_name if set, else the first given arg diff --git a/lib/puppet/pops/evaluator/runtime3_support.rb b/lib/puppet/pops/evaluator/runtime3_support.rb index c612325ac..227c7efd2 100644 --- a/lib/puppet/pops/evaluator/runtime3_support.rb +++ b/lib/puppet/pops/evaluator/runtime3_support.rb @@ -247,6 +247,34 @@ module Puppet::Pops::Evaluator::Runtime3Support end end + # Finds a resource given a type and a title. + # + def find_resource(scope, type_name, title) + scope.compiler.findresource(type_name, title) + end + + # Returns the value of a resource's parameter by first looking up the parameter in the resource + # and then in the defaults for the resource. Since the resource exists (it must in order to look up its + # parameters, any overrides have already been applied). Defaults are not applied to a resource until it + # has been finished (which typically has not taked place when this is evaluated; hence the dual lookup). + # + def get_resource_parameter_value(scope, resource, parameter_name) + val = resource[parameter_name] + if val.nil? && defaults = scope.lookupdefaults(resource.type) + # NOTE: 3x resource keeps defaults as hash using symbol for name as key to Parameter which (again) holds + # name and value. + param = defaults[parameter_name.to_sym] + val = param.value + end + val + end + + # Returns true, if the given name is the name of a resource parameter. + # + def is_parameter_of_resource?(scope, resource, name) + resource.valid_parameter?(name) + end + def resource_to_ptype(resource) nil if resource.nil? type_calculator.infer(resource) diff --git a/lib/puppet/pops/issues.rb b/lib/puppet/pops/issues.rb index 83a418bb3..36f01710a 100644 --- a/lib/puppet/pops/issues.rb +++ b/lib/puppet/pops/issues.rb @@ -368,4 +368,13 @@ module Puppet::Pops::Issues RUNTIME_ERROR = issue :RUNTIME_ERROR, :detail do "Error while evaluating #{label.a_an(semantic)}, #{detail}" end + + UNKNOWN_RESOURCE = issue :UNKNOWN_RESOURCE, :type_name, :title do + "Resource not found: #{type_name.capitalize}['#{title}']" + end + + UNKNOWN_RESOURCE_PARAMETER = issue :UNKNOWN_RESOURCE_PARAMETER, :type_name, :title, :param_name do + "The resource #{type_name.capitalize}['#{title}'] does not have a parameter called '#{param_name}'" + end + end diff --git a/spec/unit/pops/evaluator/access_ops_spec.rb b/spec/unit/pops/evaluator/access_ops_spec.rb index 71c57ce1a..286043d23 100644 --- a/spec/unit/pops/evaluator/access_ops_spec.rb +++ b/spec/unit/pops/evaluator/access_ops_spec.rb @@ -220,9 +220,9 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do expect(result[1]).to be_the_type(types.resource('File', 'y')) end - it 'gives an error if resource is already specialized' do + it 'gives an error if resource is not found' do expr = fqr('File')[fqn('x')][fqn('y')] - expect {evaluate(expr)}.to raise_error(/Cannot specialize an already specialized Resource type/) + expect {evaluate(expr)}.to raise_error(/Resource not found: File\['x'\]/) end end diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index 0d40a34ce..d8c13f970 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -439,21 +439,33 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do "Hash[Integer,Integer]" => types.hash_of(types.integer, types.integer), "Resource[File]" => types.resource('File'), "Resource['File']" => types.resource(types.resource('File')), + "File[foo]" => types.resource('file', 'foo'), + "File[foo, bar]" => [types.resource('file', 'foo'), types.resource('file', 'bar')], }.each do |source, result| it "should parse and evaluate the expression '#{source}' to #{result}" do parser.evaluate_string(scope, source, __FILE__).should == result end end + # Resource default and override expressions and resource parameter access { - "file {foo: } File[foo]" => :TODO, - "file {[foo, bar]: } File[foo, bar]" => [:TODO], + "notify { id: message=>explicit} Notify[id][message]" => "explicit", + "Notify { message=>by_default} notify {foo:} Notify[foo][message]" => "by_default", + "notify {foo:} Notify[foo]{message =>by_override} Notify[foo][message]" => "by_override", }.each do |source, result| it "should parse and evaluate the expression '#{source}' to #{result}" do - pending "Not yet implemented Resource Instance resolution" parser.evaluate_string(scope, source, __FILE__).should == result end end + # Resource default and override expressions and resource parameter access + { + "notify { xid: message=>explicit} Notify[id][message]" => /Resource not found/, + "notify { id: message=>explicit} Notify[id][mustard]" => /does not have a parameter called 'mustard'/, + }.each do |source, result| + it "should parse '#{source}' and raise error matching #{result}" do + expect { parser.evaluate_string(scope, source, __FILE__)}.to raise_error(result) + end + end end context "When the evaluator performs boolean operations" do From 5b3fec70562bae887ad4d94460b350ddb37eab02 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 10 Nov 2013 01:40:41 +0100 Subject: [PATCH 074/800] (maint) Remove unused OptionalAttributeOperation The expression OptionalAttributeOperation was thought as an instruction that would treat an assignment of :undef as antimatter vs. setting an explicit nil. This is not possible when using the 3x Resource class, and no concrete syntax was introduced. This removes it from the model and the todo note to implement it in the evaluator. --- lib/puppet/pops/evaluator/evaluator_impl.rb | 5 ----- lib/puppet/pops/model/model.rb | 9 --------- 2 files changed, 14 deletions(-) diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index c4defc1e2..36e443e7d 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -608,11 +608,6 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # TODO end - # @todo not implemented - def eval_OptionalAttributeOperation o, scope - # TODO - end - # @todo not implemented def eval_CollectExpression o, scope # TODO diff --git a/lib/puppet/pops/model/model.rb b/lib/puppet/pops/model/model.rb index 670b7f2ad..f5f32580f 100644 --- a/lib/puppet/pops/model/model.rb +++ b/lib/puppet/pops/model/model.rb @@ -214,15 +214,6 @@ module Puppet::Pops::Model contains_one_uni 'value_expr', Expression, :lowerBound => 1 end - # An optional attribute operation sets or appends a value to a named attribute unless - # the value is undef/nil in which case the opereration is a Nop. - # - # This is a new feature proposed to solve the undef as antimatter problem - # @note Currently Unused - # - class OptionalAttributeOperation < AttributeOperation - end - # An object that collects stored objects from the central cache and returns # them to the current host. Operations may optionally be applied. # From 29ecb43d32fea1bd5dd02b92998419ea629f814d Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 10 Nov 2013 01:50:52 +0100 Subject: [PATCH 075/800] (maint) Clean up stale comments --- lib/puppet/pops/evaluator/evaluator_impl.rb | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index 36e443e7d..d66aaa147 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -613,13 +613,6 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # TODO end -# NOT NEEDED IN THIS VERSION SINCE PARAMTERS ARE HANDLED IN POPS BRIDGE FOR DEFINITIONS, AND AT POINT OF -# CALL FOR LAMBDAS -# -# # @todo not implemented -# def eval_Parameter o, scope -# end -# def eval_ParenthesizedExpression(o, scope) evaluate(o.expr, scope) end @@ -676,7 +669,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator end # Puppet 3.1 AST only supports calling a function by name (it is not possible to produce a function - # that is then called). TODO- should puppet 4 accept this? It is very powerful in combiantion with + # that is then called). TODO- should puppet 4 accept this? It is very powerful in combination with # custom functions in puppet language. # # rval_required (for an expression) @@ -810,10 +803,6 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # TODO: formalize, when scope returns nil, vs error string(get_variable_value(o.expr.value, o, scope), scope) else - # TODO: This is not very good as it forces the to_s to apply to all kind of results - # It should call a polymorph method to allow to_s to be applied in general, and possible some other - # to string formatter for other values (like PType Objects). - # string(evaluate(o.expr, scope), scope) end end From fb6a1ee776d232337e3b91c0b60d3e9450581762 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 10 Nov 2013 02:21:44 +0100 Subject: [PATCH 076/800] (maint) Remove unused constructs and use faster polymorph calls This removes the ability to pass a block to evaluate since this was only used in one specific case, and there are many calls to evaluate (removes one conditional in very hot execution path). Adds caching to polymorph tables in compare operator sub evaluator. --- lib/puppet/pops/evaluator/compare_operator.rb | 12 +++---- lib/puppet/pops/evaluator/evaluator_impl.rb | 36 ++++++------------- 2 files changed, 17 insertions(+), 31 deletions(-) diff --git a/lib/puppet/pops/evaluator/compare_operator.rb b/lib/puppet/pops/evaluator/compare_operator.rb index 19315b2cf..30406af97 100644 --- a/lib/puppet/pops/evaluator/compare_operator.rb +++ b/lib/puppet/pops/evaluator/compare_operator.rb @@ -10,25 +10,25 @@ class Puppet::Pops::Evaluator::CompareOperator include Puppet::Pops::Utils def initialize - @@equals_visitor = Puppet::Pops::Visitor.new(self, "equals", 1, 1) - @@compare_visitor = Puppet::Pops::Visitor.new(self, "cmp", 1, 1) - @@include_visitor = Puppet::Pops::Visitor.new(self, "include", 1, 1) + @@equals_visitor ||= Puppet::Pops::Visitor.new(self, "equals", 1, 1) + @@compare_visitor ||= Puppet::Pops::Visitor.new(self, "cmp", 1, 1) + @@include_visitor ||= Puppet::Pops::Visitor.new(self, "include", 1, 1) @type_calculator = Puppet::Pops::Types::TypeCalculator.new() end def equals (a, b) - @@equals_visitor.visit_this(self, a, b) + @@equals_visitor.visit_this_1(self, a, b) end # Performs a comparison of a and b, and return > 0 if a is bigger, 0 if equal, and < 0 if b is bigger. # Comparison of String vs. Numeric always compares using numeric. def compare(a, b) - @@compare_visitor.visit_this(self, a, b) + @@compare_visitor.visit_this_1(self, a, b) end # Answers is b included in a def include?(a, b) - @@include_visitor.visit_this(self, a, b) + @@include_visitor.visit_this_1(self, a, b) end protected diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index d66aaa147..b287d2c9d 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -34,12 +34,10 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # Issues = Puppet::Pops::Issues - def initialize @@eval_visitor ||= Puppet::Pops::Visitor.new(self, "eval", 1, 1) @@lvalue_visitor ||= Puppet::Pops::Visitor.new(self, "lvalue", 1, 1) @@assign_visitor ||= Puppet::Pops::Visitor.new(self, "assign", 3, 3) - @@call_visitor ||= Puppet::Pops::Visitor.new(self, "call", 3, 3) @@string_visitor ||= Puppet::Pops::Visitor.new(self, "string", 1, 1) @@type_calculator ||= Puppet::Pops::Types::TypeCalculator.new() @@ -68,18 +66,11 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # @param target [Object] evaluation target - see methods on the pattern assign_TYPE for actual supported types. # @param scope [Object] the runtime specific scope class where evaluation should take place # @return [Object] the result of the evaluation - # @yieldparam [Object] the result of the evaluation of target - # @yieldreturn [Object] the result of evaluating the optional block # # @api # - def evaluate(target, scope, &block) - x = @@eval_visitor.visit_this(self, target, scope) - if block_given? - block.call(x) - else - x - end + def evaluate(target, scope) + @@eval_visitor.visit_this_1(self, target, scope) end # Polymorphic assign - calls assign_TYPE. @@ -98,15 +89,15 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # @param scope [Object] the runtime specific scope where evaluation should take place def assign(target, value, o, scope) - @@assign_visitor.visit_this(self, target, value, o, scope) + @@assign_visitor.visit_this_3(self, target, value, o, scope) end def lvalue(o, scope) - @@lvalue_visitor.visit_this(self, o, scope) + @@lvalue_visitor.visit_this_1(self, o, scope) end def string(o, scope) - @@string_visitor.visit_this(self, o, scope) + @@string_visitor.visit_this_1(self, o, scope) end # Call a closure - Can only be called with a Closure (for now), may be refactored later @@ -117,7 +108,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # @raise ArgumentError, if there are to many or too few arguments # @raise ArgumentError, if given closure is not a Puppet::Pops::Evaluator::Closure # - def call(closure, args, scope, &block) + def call(closure, args, scope) raise ArgumentError, "Can only call a Lambda" unless closure.is_a?(Puppet::Pops::Evaluator::Lambda) pblock = closure.model parameters = pblock.parameters || [] @@ -170,11 +161,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator ensure set_scope_nesting_level(scope, scope_memo) end - if block_given? - block.call(result) - else - result - end + result end protected @@ -723,11 +710,10 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # with_guarded_scope(scope) do test = evaluate(o.left_expr, scope) - selected = o.selectors.find {|s| - evaluate(s.matching_expr, scope) {|candidate| - candidate == :default || is_match?(test, candidate, s.matching_expr, scope) - } - } + selected = o.selectors.find do |s| + candidate = evaluate(s.matching_expr, scope) + candidate == :default || is_match?(test, candidate, s.matching_expr, scope) + end if selected evaluate(selected.value_expr, scope) else From 1cdea27015056491de8d13b09a8aed8ec94c3bc7 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 10 Nov 2013 22:13:30 +0100 Subject: [PATCH 077/800] (maint) Remove support for hyphen in name in lexer2 --- lib/puppet/pops/parser/lexer2.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/pops/parser/lexer2.rb b/lib/puppet/pops/parser/lexer2.rb index 0ee47ede7..50e351abe 100644 --- a/lib/puppet/pops/parser/lexer2.rb +++ b/lib/puppet/pops/parser/lexer2.rb @@ -153,7 +153,7 @@ class Puppet::Pops::Parser::Lexer2 # a letter a-z and may not contain dashes (\w includes letters, digits and _). # PATTERN_CLASSREF = %r{((::){0,1}[A-Z][\w]*)+} - PATTERN_NAME = %r{((::)?[a-z][-\w]*)(::[a-z][\w]*)*} + PATTERN_NAME = %r{((::)?[a-z][\w]*)(::[a-z][\w]*)*} PATTERN_DOLLAR_VAR = %r{\$(::)?(\w+::)*\w+} PATTERN_NUMBER = %r{\b(?:0[xX][0-9A-Fa-f]+|0?\d+(?:\.\d+)?(?:[eE]-?\d+)?)\b} From ac6a8d2a1776916ef65521384f6d080629c070d9 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 11 Nov 2013 16:38:24 +0100 Subject: [PATCH 078/800] (#22363) Add support for evaluation of CollectExpression. This replaces the earlier attempt of improving the CollectExpression evaluation with an extended set of operations. The 3x API proved to be too complicated. Instead, the implementation now performs a transformation to the 3x way of handling Collection and evaluates the transformed logic. --- lib/puppet/pops/evaluator/evaluator_impl.rb | 46 ++++++++++++--------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index b287d2c9d..d7682b289 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -100,6 +100,14 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator @@string_visitor.visit_this_1(self, o, scope) end + # Polymorphic query + # Produces a Predicate Proc that can be called with a resource instance to determine of the resource + # is to be included in the result or not. + # + def query(o, scope) + @@query_visitor.visit_this_1(self, o, scope) + end + # Call a closure - Can only be called with a Closure (for now), may be refactored later # to also handle other types of calls (function calls are also handled by CallNamedFunction and CallMethod, they # could create similar objects to Closure, wait until other types of defines are instantiated - they may behave @@ -264,8 +272,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator end # Captures all LiteralValues not handled elsewhere. - #-- - # QualifiedName < LiteralValue end + # def eval_LiteralValue(o, scope) o.value end @@ -580,24 +587,25 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator end end - # @todo not implemented - maybe not needed; this is an abstract class - def eval_QueryExpression o, scope - # TODO: or remove - this is the abstract query - end - - # @todo not implemented - def eval_ExportedQuery o, scope - # TODO - end - - # @todo not implemented - def eval_VirtualQuery o, scope - # TODO - end - - # @todo not implemented + # Evaluates a CollectExpression by transforming it into a 3x AST::Collection and then evaluating that. + # This is done because of the complex API between compiler, indirector, backends, and difference between + # collecting virtual resources and exported resources. + # def eval_CollectExpression o, scope - # TODO + # The Collect Expression and its contained query expressions are implemented in such a way in + # 3x that it is almost impossible to do anything about them (the AST objects are lazily evaluated, + # and the built structure consists of both higher order functions and arrays with query expressions + # that are either used as a predicate filter, or given to an indirection terminus (such as the Puppet DB + # resource terminus). Unfortunately, the 3x implementation has many inconsistencies that the implementation + # below carries forward. + # + collect_3x = Puppet::Pops::Model::AstTransformer.new().transform(o) + collect_3x.evaluate(scope) + # the 3x returns an instance of Collector (but it is only registered with the compiler at this + # point and does not contain any valuable information (like the result, count of the result etc.) + # Ensure that this object does not leak to the Puppet Program being evaluated. + # + nil end def eval_ParenthesizedExpression(o, scope) From c3e089f2720712168c3249543078f58396dbecae Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Wed, 13 Nov 2013 00:34:12 +0100 Subject: [PATCH 079/800] (#22363) Make selector and case use same equality as for '==' operator The is_match? used by selector and case used Ruby == for all RHS matches except for regular expression. This is now changed to use the Puppet '==' operator. Also, in this commit is a change that checks that LHS result is a String when the RHS is a regular expression - an issue is raised otherwise (other than the earlier Ruby TypeError). Tests added. --- lib/puppet/pops/evaluator/evaluator_impl.rb | 23 +++++++++---------- .../pops/evaluator/evaluating_parser_spec.rb | 6 +++++ 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index d7682b289..cc9fe09db 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -663,13 +663,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator evaluate(o.type_ref, scope) end - # Puppet 3.1 AST only supports calling a function by name (it is not possible to produce a function - # that is then called). TODO- should puppet 4 accept this? It is very powerful in combination with - # custom functions in puppet language. - # - # rval_required (for an expression) - # functor_expr (lhs - the "name" expression) - # arguments - list of arguments + # Evaluates function call by name. # def eval_CallNamedFunctionExpression(o, scope) # The functor expression is not evaluated, it is not possible to select the function to call @@ -936,18 +930,23 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # # This is the type of matching performed in a case option, using == for every type # of value except regular expression where a match is performed. - # @todo there are implementation issues left to deal with (see source) # def is_match? left, right, o, scope - # TODO: deal with TypeError - # TODO: match when left is a Number, or something strange - # TODO: solution should be used in MatchExpression if right.is_a?(Regexp) + # TODO: Possibly use unique Issue key for this + fail(Issues::MATCH_NOT_STRING, o, {:left_value => left}) unless left.is_a? String matched = right.match(left) set_match_data(matched, o, scope) # creates or clears ephemeral !!matched # convert to boolean + elsif right.is_a?(Puppet::Pops::Types::PAbstractType) && !left.is_a?(Puppet::Pops::Types::PAbstractType) + # right is a type and left is not - check if left is an instance of the given type + # (The reverse is not terribly meaningful - computing which of the case options that first produces + # an instance of a given type). + # + @@type_calculator.instance?(right, left) else - left == right + # Handle equality the same way as the language '==' operator (case insensitive etc.) + @@compare_operator.equals(left,right) end end diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index d8c13f970..7c6d1c150 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -357,6 +357,11 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do "case 2 { 1,3 : { no } 5: { no } }" => nil, "case 'banana' { 1,3 : { no } /.*ana.*/: { yes } }" => 'yes', "case 'banana' { /.*(ana).*/: { $1 } }" => 'ana', + "case [1] { Array : { yes } }" => 'yes', + "case [1] { + Array[String] : { no } + Array[Integer]: { yes } + }" => 'yes', }.each do |source, result| it "should parse and evaluate the expression '#{source}' to #{result}" do parser.evaluate_string(scope, source, __FILE__).should == result @@ -369,6 +374,7 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do "3 ? { 1 => no, 2 => no, default => yes }" => 'yes', "3 ? { 1 => no, default => yes, 3 => no }" => 'yes', "'banana' ? { /.*(ana).*/ => $1 }" => 'ana', + "[2] ? { Array[String] => yes, Array => yes}" => 'yes', }.each do |source, result| it "should parse and evaluate the expression '#{source}' to #{result}" do parser.evaluate_string(scope, source, __FILE__).should == result From 8779a4038fc3997f940804ea1cb4feced3d89427 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Wed, 13 Nov 2013 00:39:20 +0100 Subject: [PATCH 080/800] (#22363) Remove unused datatype_reference method in evaluator. This method was unused - its intent was to create an RGen meta type given a name - creation of types is deferred to a future version and this method was just dead weight. --- lib/puppet/pops/evaluator/evaluator_impl.rb | 50 --------------------- 1 file changed, 50 deletions(-) diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index cc9fe09db..fa1be626a 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -959,54 +959,4 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator end end - # Translates data type name to instance of a data type. - # - # Maps an object to an instance of RGen::ECore::EDataType - i.e. returns - # an enum, `EString`, `EInt`, `EFloat`, or `EBoolean`. The default (if nil) is `EString`. - # If an instance of EEnum is passed, a new Enum datatype is created named after the - # attributes (e.g. fooEnumValues) unless the enum is already named. This named enum can not - # be accessed and reused, but it is of value when debugging that it has a name related to - # the attribute). - # - # @param o [String, RGen::Ecore::EDataType, nil] reference to, or real data type - # @param attribute_name [String] the name of the attribute having data type given by `o` - # @param scope [Object] the runtime specific scope where this instruction is evaluated - # @return [RGen::ECore::EDataType] a datatype for `o` with name `attribute_name`, being one of - # a named enum, `EString`, `EInt`, `EFloat`, or `EBoolean`. The default (if nil) is `EString`. - # - # @todo the scope should not be part of the signature; it is currently used to get to a type creator where - # an enum can be created. It should instead use an adapter to find the type creator associated - # with the actual object (i.e. create the datatype in the same package as it's container). - # This is not known by the scope. - # - def datatype_reference(o, attribute_name, scope) - case o - when RGen::ECore::EEnum - o.ePackage = package - # anonymous enums are named after the attribute - # This is slightly problematic as the names are stored as constants in the - # module, and may thus overwrite a constant (which does not really matter) since - # the constant gets erased anyway by the type creator - # after having been associated with the created object/class. - # - o.name = attribute_name + "EnumValues" unless o.name - scope.top_scope.type_creator.create_enum o - when RGen::ECore::EDataType - # Already resolved to a data type - o - when 'String' - RGen::ECore::EString - when 'Integer' - RGen::ECore::EInt - when 'Float' - RGen::ECore::EFloat - when 'Boolean' - RGen::ECore::EBoolean - when NilClass - # Default, if no expression given - RGen::ECore::EString - else - nil - end - end end From ea3d6acebd5b211501c574bee679a86ccd74e479 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Wed, 13 Nov 2013 00:43:12 +0100 Subject: [PATCH 081/800] (maint) Remove stale TODO comments --- lib/puppet/pops/model/model.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/puppet/pops/model/model.rb b/lib/puppet/pops/model/model.rb index f5f32580f..6f7d58f75 100644 --- a/lib/puppet/pops/model/model.rb +++ b/lib/puppet/pops/model/model.rb @@ -18,7 +18,6 @@ # # This metamodel is expressed using RGen. # -# TODO: Anonymous Enums - probably ok, but they can be named (don't know if that is meaningsful) require 'rgen/metamodel_builder' @@ -26,8 +25,6 @@ module Puppet::Pops::Model extend RGen::MetamodelBuilder::ModuleExtension # A base class for modeled objects that makes them Visitable, and Adaptable. - # @todo currently includes Containment which will not be needed when the corresponding methods - # are added to RGen (in some version after 0.6.2). # class PopsObject < RGen::MetamodelBuilder::MMBase include Puppet::Pops::Visitable From 60bf2afbaf3227c1bb46510c69e4c7b4cfbffc88 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Wed, 13 Nov 2013 01:14:56 +0100 Subject: [PATCH 082/800] (#22363) Remove the support for applying [] operation without arg(s). The intent was to use an empty [] as a "default", but this was a bad idea, it is just as easy (and clearer) to write just Array instead of Array[] to mean an unspecified Array. All variants of apply of [] to LHS now raise an error if arguments are empty. --- lib/puppet/pops/evaluator/access_operator.rb | 31 ++++++++------------ spec/unit/pops/evaluator/access_ops_spec.rb | 2 +- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/lib/puppet/pops/evaluator/access_operator.rb b/lib/puppet/pops/evaluator/access_operator.rb index 83a16bc58..9ea7be415 100644 --- a/lib/puppet/pops/evaluator/access_operator.rb +++ b/lib/puppet/pops/evaluator/access_operator.rb @@ -21,7 +21,7 @@ class Puppet::Pops::Evaluator::AccessOperator end def access (o, scope, *keys) - @@access_visitor.visit_this(self, o, scope, keys) + @@access_visitor.visit_this_2(self, o, scope, keys) end protected @@ -95,10 +95,6 @@ class Puppet::Pops::Evaluator::AccessOperator # An integer type provides a way to create an Array of integers from, to (inclusive) (must be given), and an # optional step at the 3d position which defaults to 1 def access_PIntegerType(o, scope, keys) - if keys.size == 0 - return o - end - unless keys.size.between?(2, 3) fail(Puppet::Pops::Issues::BAD_INTEGER_SLICE_ARITY, @semantic, {:actual => keys.size}) end @@ -124,18 +120,16 @@ class Puppet::Pops::Evaluator::AccessOperator end end case keys.size - when 0 - Marshal.load(Marshal.dump(o)) # Deep copy when 1 result = Puppet::Pops::Types::PHashType.new() result.key_type = Marshal.load(Marshal.dump(o.key_type)) result.element_type = keys[0] result when 2 - result = Puppet::Pops::Types::PHashType.new() - result.key_type = keys[0] - result.element_type = keys[1] - result + result = Puppet::Pops::Types::PHashType.new() + result.key_type = keys[0] + result.element_type = keys[1] + result else fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_ARITY, @semantic, {:base_type => 'Hash', :min => 1, :max => 2, :actual => keys.size}) end @@ -144,10 +138,7 @@ class Puppet::Pops::Evaluator::AccessOperator # An Array can create a new Array type. It is not possible to create a collection of Array types. # def access_PArrayType(o, scope, keys) - case keys.size - when 0 - Marshal.load(Marshal.dump(o)) # Deep copy - when 1 + if keys.size == 1 unless keys[0].is_a?(Puppet::Pops::Types::PAbstractType) fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[0], {:base_type => 'Array', :actual => keys[0].class}) end @@ -173,8 +164,8 @@ class Puppet::Pops::Evaluator::AccessOperator # def access_PResourceType(o, scope, keys) if keys.size == 0 - # TODO: Either the below or an error - return Marshal.load(Marshal.dump(o)) # Deep copy + fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_ARITY, o, + :base_type => Puppet::Pops::Types::TypeCalculator.new().string(o), :min => 1, :actual => 0) end if !o.title.nil? # lookup resource and return one or more parameter values @@ -215,9 +206,13 @@ class Puppet::Pops::Evaluator::AccessOperator def access_PHostClassType(o, scope, keys) if keys.size == 0 - return Marshal.load(Marshal.dump(o)) # Deep copy + fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_ARITY, o, + :base_type => Puppet::Pops::Types::TypeCalculator.new().string(o), :min => 1, :actual => 0) end unless o.class_name.nil? + # TODO: if [] is applied to specific class, it should be treated the same as getting + # a resource parameter. Now it fails the operation + # fail(Puppet::Pops::Issues::ILLEGAL_TYPE_SPECIALIZATION, semantic.left_expr, {:kind => 'Class'}) end result = keys.collect do |c| diff --git a/spec/unit/pops/evaluator/access_ops_spec.rb b/spec/unit/pops/evaluator/access_ops_spec.rb index 286043d23..b11488315 100644 --- a/spec/unit/pops/evaluator/access_ops_spec.rb +++ b/spec/unit/pops/evaluator/access_ops_spec.rb @@ -178,7 +178,7 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do it 'produces same class if no class name is given' do expr = fqr('Class')[fqn('apache')][] - expect(evaluate(expr)).to be_the_type(types.host_class('apache')) + expect { evaluate(expr) }.to raise_error(/Evaluation Error: Class\[apache\]\[\] requires 1 arguments\. Got 0/) end it 'produces a collection of classes when multiple class names are given' do From 78a72704eb9406e491215c7ce057af993d14a6c9 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Wed, 13 Nov 2013 03:16:28 +0100 Subject: [PATCH 083/800] (#22363) Refactor name of Puppet::Pops::Evaluator::Lambda to Closure The new implementation is a Closure in that it has a reference to the Evaluator and Scope where it was created. --- lib/puppet/pops/evaluator/closure.rb | 10 ++++++++-- lib/puppet/pops/evaluator/evaluator_impl.rb | 6 +++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/puppet/pops/evaluator/closure.rb b/lib/puppet/pops/evaluator/closure.rb index ef5593cbc..980f3d139 100644 --- a/lib/puppet/pops/evaluator/closure.rb +++ b/lib/puppet/pops/evaluator/closure.rb @@ -1,6 +1,12 @@ - -class Puppet::Pops::Evaluator::Lambda +# A Closure represents logic bound to a particular scope. +# As long as the runtime (basically the scope implementation) has the behaviour of Puppet 3x it is not +# safe to use this closure when the the scope given to it when initialized goes "out of scope". +# +# Note that the implementation is backwards compatible in that the call method accepts a scope, but this +# scope is not used. +# +class Puppet::Pops::Evaluator::Closure attr_reader :evaluator attr_reader :model attr_reader :enclosing_scope diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index fa1be626a..4f7c3f4ee 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -117,7 +117,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # @raise ArgumentError, if given closure is not a Puppet::Pops::Evaluator::Closure # def call(closure, args, scope) - raise ArgumentError, "Can only call a Lambda" unless closure.is_a?(Puppet::Pops::Evaluator::Lambda) + raise ArgumentError, "Can only call a Lambda" unless closure.is_a?(Puppet::Pops::Evaluator::Closure) pblock = closure.model parameters = pblock.parameters || [] @@ -675,7 +675,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator assert_function_available(name, o, scope) evaluated_arguments = o.arguments.collect {|arg| evaluate(arg, scope) } # wrap lambda in a callable block if it is present - evaluated_arguments << Puppet::Pops::Evaluator::Lambda.new(self, o.lambda, scope) if o.lambda + evaluated_arguments << Puppet::Pops::Evaluator::Closure.new(self, o.lambda, scope) if o.lambda call_function(name, evaluated_arguments, o, scope) do |result| # prevent functions that are not r-value from leaking its return value rvalue_function?(name, o, scope) ? result : nil @@ -696,7 +696,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator name = name.value # the string function name assert_function_available(name, o, scope) evaluated_arguments = [receiver] + (o.arguments || []).collect {|arg| evaluate(arg, scope) } - evaluated_arguments << Puppet::Pops::Evaluator::Lambda.new(self, o.lambda, scope) if o.lambda + evaluated_arguments << Puppet::Pops::Evaluator::Closure.new(self, o.lambda, scope) if o.lambda call_function(name, evaluated_arguments, o, scope) do |result| # prevent functions that are not r-value from leaking its return value rvalue_function?(name, o, scope) ? result : nil From 9f1a1ae8c2413dc619bac9369c8fe2175083ab41 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Wed, 13 Nov 2013 03:36:29 +0100 Subject: [PATCH 084/800] (maint) Cleanup unused code and fix comments There is no need to have an Evaluator class that describes the API of an Evaluator at this point (since it is not used it had drifted from the real implementation anyway). --- lib/puppet/pops/evaluator/evaluator.rb | 16 ---------------- lib/puppet/pops/evaluator/evaluator_impl.rb | 21 ++++++++------------- 2 files changed, 8 insertions(+), 29 deletions(-) delete mode 100644 lib/puppet/pops/evaluator/evaluator.rb diff --git a/lib/puppet/pops/evaluator/evaluator.rb b/lib/puppet/pops/evaluator/evaluator.rb deleted file mode 100644 index 4fe56cb10..000000000 --- a/lib/puppet/pops/evaluator/evaluator.rb +++ /dev/null @@ -1,16 +0,0 @@ -# An evaluator evaluates a given object in the given Puppet::Pops::API::Scope scope. -# @abstract -# -class Puppet::Pops::Evaluator - # Evaluates the given object o in the given scope, optionally passing a block which will be - # called with the result of the evaluation. - # @abstract - # @param o [Object] the object to evaluate - # @param scope [Object] the runtime specific scope to evaluate in - # @yieldparam r {Object] the result of the evaluation - # @return [Object] the result of the evaluation, or the result of evaluating the optional block - # - def evaluate(o, scope, &block) - raise NotImplementedError, "A concrete Evaluator implementation should have implemented this method." - end -end diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index 4f7c3f4ee..db12179d8 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -19,9 +19,8 @@ require 'puppet/pops/evaluator/closure' # # See {Puppet::Pops::Visitable} and {Puppet::Pops::Visitor} for more information about # polymorphic calling. -# @api private # -class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator +class Puppet::Pops::Evaluator::EvaluatorImpl include Puppet::Pops::Utils # Provides access to the Puppet 3.x runtime (scope, etc.) @@ -47,6 +46,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator @@relationship_operator ||= Puppet::Pops::Evaluator::RelationshipOperator.new() end + # @api private def type_calculator @@type_calculator end @@ -73,9 +73,9 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator @@eval_visitor.visit_this_1(self, target, scope) end - # Polymorphic assign - calls assign_TYPE. + # Polymorphic assign - calls assign_TYPE # - # # Polymorphic assign + # ## Polymorphic assign # Polymorphic assign calls a method on the format assign_TYPE where TYPE is the last # part of the class of the given _target_. A search is performed starting with the actual class, continuing # with each of the _target_ class's super classes until a matching method is found. @@ -87,7 +87,9 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator # @param value [Object] the value to assign to `target` # @param o [Puppet::Pops::Model::PopsObject] originating instruction # @param scope [Object] the runtime specific scope where evaluation should take place - + # + # @api + # def assign(target, value, o, scope) @@assign_visitor.visit_this_3(self, target, value, o, scope) end @@ -100,14 +102,6 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator @@string_visitor.visit_this_1(self, o, scope) end - # Polymorphic query - # Produces a Predicate Proc that can be called with a resource instance to determine of the resource - # is to be included in the result or not. - # - def query(o, scope) - @@query_visitor.visit_this_1(self, o, scope) - end - # Call a closure - Can only be called with a Closure (for now), may be refactored later # to also handle other types of calls (function calls are also handled by CallNamedFunction and CallMethod, they # could create similar objects to Closure, wait until other types of defines are instantiated - they may behave @@ -180,6 +174,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # < Puppet::Pops::Evaluator end def lvalue_LiteralList(o, scope) + # evaluate the list - see assign_Array for how this is used evaluate(o.expr, scope) end From f88ab81fca5f31befeb6834bce3c1458fb7e673c Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Wed, 13 Nov 2013 03:44:35 +0100 Subject: [PATCH 085/800] (maint) Delete reference grammar.ra The file pops/parser/grammar.ra was an intermediate step between the old and the new grammar implementation. It used the old grammar but created the new model. It can now be removed as the new grammar is functioning as expected. (The original grammar is still available as an up to date reference. The copy in pops/parser was not maintained). --- lib/puppet/pops/parser/grammar.ra | 746 ------------------------------ lib/puppet/pops/parser/makefile | 7 - 2 files changed, 753 deletions(-) delete mode 100644 lib/puppet/pops/parser/grammar.ra diff --git a/lib/puppet/pops/parser/grammar.ra b/lib/puppet/pops/parser/grammar.ra deleted file mode 100644 index 7352bdbfb..000000000 --- a/lib/puppet/pops/parser/grammar.ra +++ /dev/null @@ -1,746 +0,0 @@ -# vim: syntax=ruby - -# Parser using the Pops model -# This grammar is a half step between the current 3.1. grammar and egrammar. -# FIXME! Keep as reference until egrammar is proven to work. - -class Puppet::Pops::Impl::Parser::Parser - -token STRING DQPRE DQMID DQPOST -token LBRACK RBRACK LBRACE RBRACE SYMBOL FARROW COMMA TRUE -token FALSE EQUALS APPENDS LESSEQUAL NOTEQUAL DOT COLON LLCOLLECT RRCOLLECT -token QMARK LPAREN RPAREN ISEQUAL GREATEREQUAL GREATERTHAN LESSTHAN -token IF ELSE IMPORT DEFINE ELSIF VARIABLE CLASS INHERITS NODE BOOLEAN -token NAME SEMIC CASE DEFAULT AT LCOLLECT RCOLLECT CLASSREF -token NOT OR AND UNDEF PARROW PLUS MINUS TIMES DIV LSHIFT RSHIFT UMINUS -token MATCH NOMATCH REGEX IN_EDGE OUT_EDGE IN_EDGE_SUB OUT_EDGE_SUB -token IN UNLESS PIPE -token LAMBDA - -prechigh - left DOT -# left LBRACE -# left LCOLLECT LLCOLLECT - right NOT - nonassoc UMINUS - left IN MATCH NOMATCH - left TIMES DIV - left MINUS PLUS - left LSHIFT RSHIFT - left NOTEQUAL ISEQUAL - left GREATEREQUAL GREATERTHAN LESSTHAN LESSEQUAL - left AND - left OR -# left IN_EDGE OUT_EDGE IN_EDGE_SUB OUT_EDGE_SUB -preclow - -rule -# Produces [Model::BlockExpression, Model::Expression, nil] depending on multiple statements, single statement or empty -program - : statements { result = Factory.block_or_expression(*val[0]) } - | nil - -# Change may have issues with nil; i.e. program is a sequence of nils/nops -# Simplified from original which had validation for top level constructs - see statement rule -# Produces Array -statements - : statement { result = [val[0]]} - | statements statement { result = val[0].push val[1] } - -# Removed validation construct regarding "top level statements" as it did not seem to catch all problems -# and relied on a "top-level-ness" encoded in the abstract syntax tree objects -# -# The main list of valid statements -# Produces Model::Expression -# -statement - : resource - | virtual_resource - | collection - | assignment - | casestatement - | if_expression - | unless_expression - | import - | call_named_function - | definition - | hostclass - | nodedef - | resource_override - | append - | relationship - | call_method_with_lambda - -keyword - : AND - | CASE - | CLASS - | DEFAULT - | DEFINE - | ELSE - | ELSIF - | IF - | IN - | IMPORT - | INHERITS - | NODE - | OR - | UNDEF - | UNLESS - -# Produces Model::RelationshipExpression -relationship - : relationship_side edge relationship_side { result = val[0].relop(val[1][:value], val[2]); loc result, val[1] } - | relationship edge relationship_side { result = val[0].relop(val[1][:value], val[2]); loc result, val[1] } - -# Produces Model::Expression -relationship_side - : resource - | resourceref - | collection - | variable - | quotedtext - | selector - | casestatement - | hasharrayaccesses - -# Produces String -edge - : IN_EDGE - | OUT_EDGE - | IN_EDGE_SUB - | OUT_EDGE_SUB - -# Produces Model::CallNamedFunctionExpression -call_named_function - : NAME LPAREN expressions RPAREN { result = Factory.CALL_NAMED(val[0][:value], false, val[2]) ; loc result, val[0], val[3] } - | NAME LPAREN expressions COMMA RPAREN { result = Factory.CALL_NAMED(val[0][:value], false, val[2]) ; loc result, val[0], val[4] } - | NAME LPAREN RPAREN { result = Factory.CALL_NAMED(val[0][:value], false, []) ; loc result, val[0], val[2] } - | NAME func_call_args { result = Factory.CALL_NAMED(val[0][:value], false, val[1]) ; loc result, val[0] } - -call_method_with_lambda - : call_method { result = val[0] } - | call_method lambda { result = val[0]; val[0].lambda = val[1] } - -call_method - : named_access LPAREN expressions RPAREN { result = Factory.CALL_METHOD(val[0], val[2]); loc result, val[1], val[3] } - | named_access LPAREN RPAREN { result = Factory.CALL_METHOD(val[0], []); loc result, val[1], val[3] } - | named_access { result = Factory.CALL_METHOD(val[0], []); loc result, val[0] } - -named_access - : named_access_lval DOT NAME { - result = val[0].dot(Factory.fqn(val[2][:value])) - loc result, val[1], val[2] - } - -# Obviously not ideal, it is not possible to use literal array or hash as lhs -# These must be assigned to a variable - this is also an issue in other places -# -named_access_lval - : variable - | hasharrayaccesses - | selector - | quotedtext - | call_named_rval_function - -lambda - : LAMBDA lambda_parameter_list statements RBRACE { - result = Factory.LAMBDA(val[1], val[2]) - loc result, val[0], val[3] - } - | LAMBDA lambda_parameter_list RBRACE { - result = Factory.LAMBDA(val[1], nil) - loc result, val[0], val[2] - } -# Produces Array -lambda_parameter_list - : PIPE PIPE { result = [] } - | PIPE parameters endcomma PIPE { result = val[1] } - -# Produces Array -func_call_args - : rvalue { result = [val[0]] } - | func_call_args COMMA rvalue { result = val[0].push(val[2]) } - -# Produces Array -expressions - : expression { result = [val[0]] } - | expressions comma expression { result = val[0].push(val[2]) } - - -# Produces [Model::ResourceExpression, Model::ResourceDefaultsExpression] -resource - : classname LBRACE resourceinstances endsemi RBRACE { - result = Factory.RESOURCE(Factory.fqn(token_text(val[0])), val[2]) - loc result, val[0], val[4] - } - | classname LBRACE attribute_operations endcomma RBRACE { - # This is a deprecated syntax. - # It also fails hard - TODO: create model and validate this case - error "All resource specifications require names" - } - | type LBRACE attribute_operations endcomma RBRACE { - # a defaults setting for a type - result = Factory.RESOURCE_DEFAULTS(val[0], val[2]) - loc result, val[0], val[4] - } - -# Override a value set elsewhere in the configuration. -# Produces Model::ResourceOverrideExpression -resource_override - : resourceref LBRACE attribute_operations endcomma RBRACE { - @lexer.commentpop - result = Factory.RESOURCE_OVERRIDE(val[0], val[2]) - loc result, val[0], val[4] - } - -# Exported and virtual resources; these don't get sent to the client -# unless they get collected elsewhere in the db. -# The original had validation here; checking if storeconfigs is on; this is moved to a validation step -# Also, validation was performed if an attempt was made to virtualize or export a resource defaults -# this is also now deferred to validation -# Produces [Model::ResourceExpression, Model::ResourceDefaultsExpression] -virtual_resource - : at resource { - val[1].form = val[0] # :virtual, :exported, (or :regular) - result = val[1] - } - -# Produces Symbol corresponding to resource form -at - : AT { result = :virtual } - | AT AT { result = :exported } - -# A collection statement. Currently supports no arguments at all, but eventually -# will, I assume. -# -# Produces Model::CollectExpression -# -collection - : type collect_query LBRACE attribute_operations endcomma RBRACE { - @lexer.commentpop - result = Factory.COLLECT(val[0].value.downcase, val[1], val[3]) - loc result, val[0], val[5] - } - | type collect_query { - result = Factory.COLLECT(val[0].value.downcase, val[1], []) - loc result, val[0], val[1] - } - -collect_query - : LCOLLECT optional_query RCOLLECT { result = Factory.VIRTUAL_QUERY(val[1]) ; loc result, val[0], val[2] } - | LLCOLLECT optional_query RRCOLLECT { result = Factory.EXPORTED_QUERY(val[1]) ; loc result, val[0], val[2] } - -# ORIGINAL COMMENT: A mini-language for handling collection comparisons. This is organized -# to avoid the need for precedence indications. -# (New implementation is slightly different; and when finished, it may be possible to streamline the -# grammar - the difference is mostly in evaluation, not in grammar) -# -optional_query - : nil - | query - -# ORIGINAL: Had a weird list structure where AND and OR where at the same level, and hence, there was the -# need to keep track of where parenthesis were (to get order correct). -# -# This is now not needed as AND has higher precedence than OR, and parenthesis are low in precedence - -query - : predicate_lval ISEQUAL expression { result = (val[0] == val[2]) ; loc result, val[1] } - | predicate_lval NOTEQUAL expression { result = (val[0].ne(val[2])) ; loc result, val[1] } - | LPAREN query RPAREN { result = val[1] } - | query AND query { result = val[0].and(val[2]) ; loc result, val[1] } - | query OR query { result = val[0].or(val[2]) ; loc result, val[1] } - - -# Produces Model::VariableExpression, or Model::QualifiedName -predicate_lval - : variable - | name - -resourceinst - : resourcename COLON attribute_operations endcomma { result = Factory.RESOURCE_BODY(val[0], val[2]) } - -resourceinstances - : resourceinst { result = [val[0]] } - | resourceinstances SEMIC resourceinst { result = val[0].push val[2] } - - -resourcename - : quotedtext - | name - | type - | selector - | variable - | array - | hasharrayaccesses - -# Assignment, only assignment to variable is legal, but parser builds expression for [] = anyway to -# enable a better error message -assignment - : VARIABLE EQUALS expression { result = Factory.var(Factory.fqn(val[0][:value])).set(val[2]) ; loc result, val[1] } - | hasharrayaccess EQUALS expression { result val[0].set(val[2]); loc result, val[1] } - -append - : VARIABLE APPENDS expression { result = Factory.var(val[0][:value]).plus_set(val[1]) ; loc result, val[1] } - -# Produces Array -attribute_operations - : { result = [] } - | attribute_operation { result = [val[0]] } - | attribute_operations COMMA attribute_operation { result = val[0].push(val[2]) } - -# Produces String -attribute_name - : NAME - | keyword - | BOOLEAN - -# Several grammar issues here: the addparam did not allow keyword and booleans as names. -# In this version, the wrong combinations are validated instead of producing syntax errors -# (Can give nicer error message +> is not applicable to...) -# WAT - Boolean as attribute name? -# Produces Model::AttributeOperation -# -attribute_operation - : attribute_name FARROW expression { - result = Factory.ATTRIBUTE_OP(val[0][:value], :'=>', val[2]) - loc result, val[0], val[2] - } - | attribute_name PARROW expression { - result = Factory.ATTRIBUTE_OP(val[0][:value], :'+>', val[2]) - loc result, val[0], val[2] - } - -# Produces Model::CallNamedFunction -call_named_rval_function - : NAME LPAREN expressions RPAREN { result = Factory.CALL_NAMED(val[0][:value], true, val[2]) ; loc result, val[0], val[3] } - | NAME LPAREN RPAREN { result = Factory.CALL_NAMED(val[0][:value], true, []) ; loc result, val[0], val[2] } - -quotedtext - : STRING { result = Factory.literal(val[0][:value]) ; loc result, val[0] } - | dqpre dqrval { result = Factory.string(val[0], *val[1]) ; loc result, val[0], val[1][-1] } - -dqpre : DQPRE { result = Factory.literal(val[0][:value]); loc result, val[0] } -dqpost : DQPOST { result = Factory.literal(val[0][:value]); loc result, val[0] } -dqmid : DQMID { result = Factory.literal(val[0][:value]); loc result, val[0] } -text_expression : expression { result = Factory.TEXT(val[0]) } - -dqrval - : text_expression dqtail { result = [val[0]] + val[1] } - -dqtail - : dqpost { result = [val[0]] } - | dqmid dqrval { result = [val[0]] + val[1] } - - -# Reference to Resource (future also reference to other instances of other types than Resources). -# First form (lower case name) is deprecated (deprecation message handled in validation). Note that -# this requires use of token NAME since a rule call to name causes shift reduce conflict with -# a function call NAME NAME (calling function with NAME as argument e.g. foo bar). -# -# Produces InstanceReference -resourceref - : NAME LBRACK expressions RBRACK { - # Would want to use rule name here, but can't (need a NAME with higher precedence), so must - # create a QualifiedName instance here for NAME - result = Factory.INSTANCE(Factory.QNAME_OR_NUMBER(val[0][:value]), val[2]); - loc result, val[0], val[2][-1] - } - | type LBRACK expressions RBRACK { - result = Factory.INSTANCE(val[0], val[2]); - loc result, val[0], val[2][-1] - } - -# Changed from Puppet 3x where there is no else part on unless -# -unless_expression - : UNLESS expression LBRACE statements RBRACE unless_else { - @lexer.commentpop - result = Factory.UNLESS(val[1], Factory.block_or_expression(*val[3]), val[5]) - loc result, val[0], val[4] - } - | UNLESS expression LBRACE RBRACE unless_else { - @lexer.commentpop - result = Factory.UNLESS(val[1], nil, nil) - loc result, val[0], val[4] - } - -# Different from else part of if, since "elsif" is not supported, but else is -# -# Produces [Model::Expression, nil] - nil if there is no else or elsif part -unless_else - : # nothing - | ELSE LBRACE statements RBRACE { - @lexer.commentpop - result = Factory.block_or_expression(*val[2]) - loc result, val[0], val[3] - } - | ELSE LBRACE RBRACE { - @lexer.commentpop - result = nil # don't think a nop is needed here either - } - -# Produces Model::IfExpression -if_expression - : IF if_expression_part { - result = val[1] - } - -# Produces Model::IfExpression -if_expression_part - : expression LBRACE statements RBRACE else { - @lexer.commentpop - result = Factory.IF(val[0], Factory.block_or_expression(*val[2]), val[4]) - loc(result, val[0], (val[4] ? val[4] : val[3])) - } - | expression LBRACE RBRACE else { - result = Factory.IF(val[0], nil, val[3]) - loc(result, val[0], (val[3] ? val[3] : val[2])) - } - -# Produces [Model::Expression, nil] - nil if there is no else or elsif part -else - : # nothing - | ELSIF if_expression_part { result = val[1] } - | ELSE LBRACE statements RBRACE { - @lexer.commentpop - result = Factory.block_or_expression(*val[2]) - loc result, val[0], val[3] - } - | ELSE LBRACE RBRACE { - @lexer.commentpop - result = nil # don't think a nop is needed here either - } - -# Produces Model::Expression -expression - : rvalue - | hash - | expression IN expression { result = val[0].in val[2] ; loc result, val[1] } - | expression MATCH match_rvalue { result = val[0] =~ val[2] ; loc result, val[1] } - | expression NOMATCH match_rvalue { result = val[0].mne val[2] ; loc result, val[1] } - | expression PLUS expression { result = val[0] + val[2] ; loc result, val[1] } - | expression MINUS expression { result = val[0] - val[2] ; loc result, val[1] } - | expression DIV expression { result = val[0] / val[2] ; loc result, val[1] } - | expression TIMES expression { result = val[0] * val[2] ; loc result, val[1] } - | expression LSHIFT expression { result = val[0] << val[2] ; loc result, val[1] } - | expression RSHIFT expression { result = val[0] >> val[2] ; loc result, val[1] } - | MINUS expression =UMINUS { result = val[1].minus() ; loc result, val[0] } - | expression NOTEQUAL expression { result = val[0].ne val[2] ; loc result, val[1] } - | expression ISEQUAL expression { result = val[0] == val[2] ; loc result, val[1] } - | expression GREATERTHAN expression { result = val[0] > val[2] ; loc result, val[1] } - | expression GREATEREQUAL expression { result = val[0] >= val[2] ; loc result, val[1] } - | expression LESSTHAN expression { result = val[0] < val[2] ; loc result, val[1] } - | expression LESSEQUAL expression { result = val[0] <= val[2] ; loc result, val[1] } - | NOT expression { result = val[1].not ; loc result, val[0] } - | expression AND expression { result = val[0].and val[2] ; loc result, val[1] } - | expression OR expression { result = val[0].or val[2] ; loc result, val[1] } - | LPAREN expression RPAREN { result = val[1] ; } - | call_method_with_lambda - -match_rvalue - : regex - | quotedtext - -# Produces Model::CaseExpression -casestatement - : CASE expression LBRACE case_options RBRACE { - @lexer.commentpop - result = Factory.CASE(val[1], *val[3]) - loc result, val[0], val[4] - } - -# Produces Array -case_options - : case_option { result = [val[0]] } - | case_options case_option { result = val[0].push val[1] } - -# Produced Model::CaseOption (aka When) -case_option - : case_values COLON LBRACE statements RBRACE { - @lexer.commentpop - result = Factory.WHEN(val[0], val[3]) - loc result, val[1], val[4] - } - | case_values COLON LBRACE RBRACE { - @lexer.commentpop - result = Factory.WHEN(val[0], nil) - loc result, val[1], val[3] - } - -# Produces Array mostly literals -case_values - : selectable { result = [val[0]] } - | case_values COMMA selectable { result = val[0].push val[2] } - -# Produces Model::SelectorExpression -selector - : selectable QMARK selector_entries { result = val[0].select(*val[2]) ; loc result, val[1] } - -# Produces Array -selector_entries - : selector_entry { result = [val[0]] } - | LBRACE selector_entry_list endcomma RBRACE { - @lexer.commentpop - result = val[1] - } - -# Produces Array -selector_entry_list - : selector_entry { result = [val[0]] } - | selector_entry_list COMMA selector_entry { result = val[0].push val[2] } - -# Produces a Model::SelectorEntry -selector_entry - : selectable FARROW rvalue { result = Factory.MAP(val[0], val[2]) ; loc result, val[1] } - -# Produces Model::Expression (most of the literals) -selectable - : name - | type - | quotedtext - | variable - | call_named_rval_function - | boolean - | undef - | hasharrayaccess - | default - | regex - - - -# Produces nil (noop) -import - : IMPORT strings { - error "Import not supported in this version of the parser", \ - :line => stmt.context[:line], :file => stmt.context[:file] - result = nil - } - -# IMPORT (T.B DEPRECATED IN PUPPET WHEN IT HAS BEEN FIGURED OUT HOW TO SUPPORT -# THE THINGS IMPORTS ARE USED FOR. -# BOLDLY DECIDED TO SKIP THIS COMPLETELY IN THIS IMPLEMENTATION - will trigger an error -# -# These are only used for importing, no interpolation -string - : STRING { result = [val[0][:value]] } - -strings - : string - | strings COMMA string { result = val[0].push val[2] } - -# Produces Model::Definition -definition - : DEFINE classname parameter_list LBRACE statements RBRACE { - @lexer.commentpop - result = Factory.DEFINITION(classname(val[1][:value]), val[2], val[4]) - loc result, val[0], val[5] - @lexer.indefine = false - } - | DEFINE classname parameter_list LBRACE RBRACE { - @lexer.commentpop - result = Factory.DEFINITION(classname(val[1][:value]), val[2], nil) - loc result, val[0], val[4] - @lexer.indefine = false - } - -# ORIGINAL COMMENT: Our class gets defined in the parent namespace, not our own. -# WAT ??! This is way odd; should get its complete name, classnames do not nest -# Seems like the call to classname makes use of the name scope -# (This is uneccesary, since the parent name is known when evaluating) -# -# Produces Model::HostClassDefinition -# -hostclass - : CLASS classname parameter_list classparent LBRACE statements RBRACE { - @lexer.commentpop - @lexer.namepop - result = Factory.HOSTCLASS(classname(val[1][:value]), val[2], token_text(val[3]), val[5]) - loc result, val[0], val[6] - } - | CLASS classname parameter_list classparent LBRACE RBRACE { - @lexer.commentpop - @lexer.namepop - result = Factory.HOSTCLASS(classname(val[1][:value]), val[2], token_text(val[3]), nil) - loc result, val[0], val[5] - } - -# Produces Model::NodeDefinition -nodedef - : NODE hostnames nodeparent LBRACE statements RBRACE { - @lexer.commentpop - result = Factory.NODE(val[1], val[2], val[4]) - loc result, val[0], val[5] - } - | NODE hostnames nodeparent LBRACE RBRACE { - @lexer.commentpop - result = Factory.NODE(val[1], val[2], nil) - loc result, val[0], val[4] - } - -# String result -classname - : NAME { result = val[0] } - | CLASS { result = val[0] } - -# Hostnames is not a list of names, it is a list of name matchers (including a Regexp). -# (The old implementation had a special "Hostname" object with some minimal validation) -# -# Produces Array -# -hostnames - : nodename { result = [result] } - | hostnames COMMA nodename { result = val[0].push(val[2]) } - -# Produces Model::LiteralExpression -# -nodename - : hostname - -# Produces a LiteralExpression (string, :default, or regexp) -hostname - : NAME { result = Factory.fqn(val[0][:value]); loc result, val[0] } - | STRING { result = Factory.literal(val[0][:value]); loc result, val[0] } - | DEFAULT { result = Factory.literal(:default); loc result, val[0] } - | regex - - -# Produces Array -parameter_list - : nil { result = [] } - | LPAREN RPAREN { result = [] } - | LPAREN parameters endcomma RPAREN { result = val[1] } - -# Produces Array -parameters - : parameter { result = [val[0]] } - | parameters COMMA parameter { result = val[0].push(val[2]) } - -# Produces Model::Parameter -parameter - : VARIABLE EQUALS expression { result = Factory.PARAM(val[0][:value], val[2]) ; loc result, val[0] } - | VARIABLE { result = Factory.PARAM(val[0][:value]); loc result, val[0] } - -# Produces Expression, since hostname is an Expression -nodeparent - : nil - | INHERITS hostname { result = val[1] } - -# Produces String, name or nil result -classparent - : nil - | INHERITS classnameordefault { result = val[1] } - -# Produces String (this construct allows a class to be named "default" and to be referenced as -# the parent class. -# TODO: Investigate the validity -# Produces a String (classname), or a token (DEFAULT). -# -classnameordefault - : classname - | DEFAULT - -rvalue - : quotedtext - | name - | type - | boolean - | selector - | variable - | array - | hasharrayaccesses - | resourceref - | call_named_rval_function - | undef - -array - : LBRACK expressions RBRACK { result = Factory.LIST(val[1]); loc result, val[0], val[2] } - | LBRACK expressions COMMA RBRACK { result = Factory.LIST(val[1]); loc result, val[0], val[3] } - | LBRACK RBRACK { result = Factory.literal([]) ; loc result, val[0] } - - -hash - : LBRACE hashpairs RBRACE { result = Factory.HASH(val[1]); loc result, val[0], val[2] } - | LBRACE hashpairs COMMA RBRACE { result = Factory.HASH(val[1]); loc result, val[0], val[3] } - | LBRACE RBRACE { result = Factory.literal({}) ; loc result, val[0], val[3] } - -hashpairs - : hashpair { result = [val[0]] } - | hashpairs COMMA hashpair { result = val[0].push val[2] } - -hashpair - : key FARROW expression { result = Factory.KEY_ENTRY(val[0], val[2]); loc result, val[1] } - -key - : NAME { result = Factory.literal(val[0][:value]) ; loc result, val[0] } - | quotedtext { result = val[0] } - -# NOTE: Limitation that LHS is a variable, means that it is not possible to do foo(10)[2] without -# using an intermediate variable -# -hasharrayaccess - : variable LBRACK expression RBRACK { result = val[0][val[2]]; loc result, val[0], val[3] } - -hasharrayaccesses - : hasharrayaccess - | hasharrayaccesses LBRACK expression RBRACK { result = val[0][val[2]] ; loc result, val[1], val[3] } - -# Produces Model::VariableExpression -variable : VARIABLE { result = Factory.fqn(val[0][:value]).var ; loc result, val[0] } -undef : UNDEF { result = Factory.literal(:undef); loc result, val[0] } -name : NAME { result = Factory.QNAME_OR_NUMBER(val[0][:value]) ; loc result, val[0] } -type : CLASSREF { result = Factory.QREF(val[0][:value]) ; loc result, val[0] } - -default - : DEFAULT { result = Factory.literal(:default); loc result, val[0] } - -boolean - # Assumes lexer produces a Boolean value for booleans, or this will go wrong (e.g. produce. LiteralString) - : BOOLEAN { result = Factory.literal(val[0][:value]) ; loc result, val[0] } - -regex - : REGEX { result = Factory.literal(val[0][:value]); loc result, val[0] } - -# ---Special markers & syntactic sugar - -# WAT !!!! this means array can be [1=>2=>3], func (1=>2=>3), and other retarded constructs -# TODO: Remove the FARROW (investigate if there is any validity) -comma - : FARROW - | COMMA - -endcomma - : # - | COMMA { result = nil } - -endsemi - : # - | SEMIC - -nil - : { result = nil} - -## Empty list - not really needed? TODO: Check if this can be removed -#empty_list -# : { result = [] } - -end - ----- header ---- -require 'puppet' -require 'puppet/util/loadedfile' -require 'puppet/pops' - -module Puppet - class ParseError < Puppet::Error; end - class ImportError < Racc::ParseError; end - class AlreadyImportedError < ImportError; end -end - ----- inner ---- - -# Make emacs happy -# Local Variables: -# mode: ruby -# End: diff --git a/lib/puppet/pops/parser/makefile b/lib/puppet/pops/parser/makefile index f521747cb..0e59f55ba 100644 --- a/lib/puppet/pops/parser/makefile +++ b/lib/puppet/pops/parser/makefile @@ -1,11 +1,4 @@ -#parser.rb: grammar.ra -# racc -o$@ grammar.ra -# -#grammar.output: grammar.ra -# racc -v -o$@ grammar.ra -# - eparser.rb: egrammar.ra racc -o$@ egrammar.ra From aaf2d91f73cf54d343e942ee0c253cbdef97fe9c Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Wed, 13 Nov 2013 04:11:27 +0100 Subject: [PATCH 086/800] (maint) Add TODO comment about \u handling in string quoting --- lib/puppet/pops/parser/evaluating_parser.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/puppet/pops/parser/evaluating_parser.rb b/lib/puppet/pops/parser/evaluating_parser.rb index 5ba286625..dcaca1bee 100644 --- a/lib/puppet/pops/parser/evaluating_parser.rb +++ b/lib/puppet/pops/parser/evaluating_parser.rb @@ -127,6 +127,8 @@ class Puppet::Pops::Parser::EvaluatingParser # The method makes an exception for the two character sequences \$ and \s. They # will not be escaped since they have a special meaning in puppet syntax. # + # TODO: Handle \uXXXX characters ?? + # # @param x [String] The string to quote and "unparse" # @return [String] The quoted string # From 1ce685e6eba8721ddcf0ea9ec781f721d75d0ee2 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Wed, 13 Nov 2013 04:16:56 +0100 Subject: [PATCH 087/800] (#22363) Improve handling of PNilType Without this an inference of nil / undef results in an error. Now the String 'Undef' is used. Also, without this, a check for assignability to Nil would result in assignability to PObjectType (which is the widest type), and this would be wrong. (This is quite esoteric, but added for correctness). To prevent the type reference Undef to be interpreted as a Resource type, it has also been added to the type parser. And as a consequence also to the type factory. --- lib/puppet/pops/types/type_calculator.rb | 9 +++++++++ lib/puppet/pops/types/type_factory.rb | 6 ++++++ lib/puppet/pops/types/type_parser.rb | 3 +++ 3 files changed, 18 insertions(+) diff --git a/lib/puppet/pops/types/type_calculator.rb b/lib/puppet/pops/types/type_calculator.rb index c1c3d83ca..edbf2b252 100644 --- a/lib/puppet/pops/types/type_calculator.rb +++ b/lib/puppet/pops/types/type_calculator.rb @@ -453,6 +453,12 @@ class Puppet::Pops::Types::TypeCalculator t2.is_a?(Types::PObjectType) end + # @api private + def assignable_PNilType(t, t2) + # Only undef/nil is assignable to nil type + t2.is_a?(Types::PNilType) + end + # @api private def assignable_PLiteralType(t, t2) t2.is_a?(Types::PLiteralType) @@ -567,6 +573,9 @@ class Puppet::Pops::Types::TypeCalculator # @api private def string_PObjectType(t) ; "Object" ; end + # @api private + def string_PNilType(t) ; 'Undef' ; end + # @api private def string_PBooleanType(t) ; "Boolean" ; end diff --git a/lib/puppet/pops/types/type_factory.rb b/lib/puppet/pops/types/type_factory.rb index d5373bc5e..28432d26e 100644 --- a/lib/puppet/pops/types/type_factory.rb +++ b/lib/puppet/pops/types/type_factory.rb @@ -69,6 +69,12 @@ module Puppet::Pops::Types::TypeFactory Types::PDataType.new() end + # Creates an instance of the Undef type + # @api public + def self.undef() + Types::PNilType.new() + end + # Produces an instance of the abstract type PCatalogEntryType def self.catalog_entry() Types::PCatalogEntryType.new() diff --git a/lib/puppet/pops/types/type_parser.rb b/lib/puppet/pops/types/type_parser.rb index ce431f625..743912256 100644 --- a/lib/puppet/pops/types/type_parser.rb +++ b/lib/puppet/pops/types/type_parser.rb @@ -97,6 +97,9 @@ class Puppet::Pops::Types::TypeParser TYPES.collection() when "catalogentry" TYPES.catalog_entry() + when "undef" + # Should not be interpreted as Resource type + TYPES.undef() when "object", "ruby", "type" # should not be interpreted as Resource type # TODO: these should not be errors From 19d2c2b11cbc235f4beb023d05502d830772e9bd Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Wed, 13 Nov 2013 05:10:29 +0100 Subject: [PATCH 088/800] (#22363) Add missing support for Numeric type The TypeParser and TypeFactory did not have support for the abstract Numeric type. --- lib/puppet/pops/types/type_factory.rb | 7 +++++++ lib/puppet/pops/types/type_parser.rb | 2 ++ 2 files changed, 9 insertions(+) diff --git a/lib/puppet/pops/types/type_factory.rb b/lib/puppet/pops/types/type_factory.rb index 28432d26e..aeeab8cad 100644 --- a/lib/puppet/pops/types/type_factory.rb +++ b/lib/puppet/pops/types/type_factory.rb @@ -20,6 +20,13 @@ module Puppet::Pops::Types::TypeFactory Types::PFloatType.new() end + # Produces the Numeric type + # @api public + # + def self.numeric() + Types::PNumericType.new() + end + # Produces a string representation of the type # @api public # diff --git a/lib/puppet/pops/types/type_parser.rb b/lib/puppet/pops/types/type_parser.rb index 743912256..b65a4c387 100644 --- a/lib/puppet/pops/types/type_parser.rb +++ b/lib/puppet/pops/types/type_parser.rb @@ -77,6 +77,8 @@ class Puppet::Pops::Types::TypeParser TYPES.integer when "float" TYPES.float + when "numeric" + TYPES.numeric when "string" TYPES.string when "boolean" From f944fc27a4276077195d7f08396f133721c21d61 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 14 Nov 2013 05:05:08 +0100 Subject: [PATCH 089/800] (#22363) Correct failing test that relied on fixed bug for Type[Undef] When the missing string conversion of Undef was added, the faulty test triggered. It is now correct to check that the result is Type[Undef] instead of Type[Object]. --- spec/unit/pops/types/type_calculator_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/unit/pops/types/type_calculator_spec.rb b/spec/unit/pops/types/type_calculator_spec.rb index 27c7938b2..5ac3f81e1 100644 --- a/spec/unit/pops/types/type_calculator_spec.rb +++ b/spec/unit/pops/types/type_calculator_spec.rb @@ -607,7 +607,7 @@ describe 'The type calculator' do it 'should infer PType as the type of all other types' do ptype = Puppet::Pops::Types::PType - calculator.string(calculator.infer(Puppet::Pops::Types::PNilType.new() )).should == "Type[Object]" + calculator.string(calculator.infer(Puppet::Pops::Types::PNilType.new() )).should == "Type[Undef]" calculator.string(calculator.infer(Puppet::Pops::Types::PDataType.new() )).should == "Type[Data]" calculator.string(calculator.infer(Puppet::Pops::Types::PLiteralType.new() )).should == "Type[Literal]" calculator.string(calculator.infer(Puppet::Pops::Types::PStringType.new() )).should == "Type[String]" From 450dd9e62ab56e335e18264c39d6eeb18eba197e Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 14 Nov 2013 05:11:00 +0100 Subject: [PATCH 090/800] (#22363) Make interpolation use string_ for all runtime types This adds polymorph string_ methods for Array, Hash, and Regexp to ensure that interpolation result is defined (as opposed to just using to_s and then being surprised when regexps and types come out weird. Tests added. --- lib/puppet/pops/evaluator/evaluator_impl.rb | 12 ++++++++++++ spec/unit/pops/evaluator/evaluating_parser_spec.rb | 3 +++ 2 files changed, 15 insertions(+) diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index db12179d8..b5f32aec7 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -803,6 +803,18 @@ class Puppet::Pops::Evaluator::EvaluatorImpl end end + def string_Array(o, scope) + ['[', o.map {|e| string(e, scope)}.join(', '), ']'].join() + end + + def string_Hash(o, scope) + ['{', o.map {|k,v| string(k, scope) + " => " + string(v, scope)}.join(', '), '}'].join() + end + + def string_Regexp(o, scope) + ['/', o.source, '/'].join() + end + def string_PAbstractType(o, scope) @@type_calculator.string(o) end diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index 7c6d1c150..526949ce7 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -551,6 +551,9 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do '"value is ${$a*2} yo"' => "value is 20 yo", '"value is ${sprintf("x%iy",$a)} yo"' => "value is x10y yo", '"value is ${"x%iy".sprintf($a)} yo"' => "value is x10y yo", + '"value is ${[1,2,3]} yo"' => "value is [1, 2, 3] yo", + '"value is ${{a=>1,b=>2}} yo"' => "value is {a => 1, b => 2} yo", + '"value is ${/.*/} yo"' => "value is /.*/ yo", }.each do |source, result| it "should parse and evaluate the expression '#{source}' to #{result}" do populate From 09b7115e331888ea4c8a2c8d4ac520b2510f02b6 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 14 Nov 2013 17:53:42 +0100 Subject: [PATCH 091/800] (#22363) Add test for interpolation string conversion undef / default There was no test for evaluation result of undef or default being interpolated. --- spec/unit/pops/evaluator/evaluating_parser_spec.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index 526949ce7..3ffd28185 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -554,6 +554,8 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do '"value is ${[1,2,3]} yo"' => "value is [1, 2, 3] yo", '"value is ${{a=>1,b=>2}} yo"' => "value is {a => 1, b => 2} yo", '"value is ${/.*/} yo"' => "value is /.*/ yo", + '$x = undef "value is $x yo"' => "value is yo", + '$x = default "value is $x yo"' => "value is default yo", }.each do |source, result| it "should parse and evaluate the expression '#{source}' to #{result}" do populate From 1713df3be18773bf1030dda2e8f5f166f5df1360 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 14 Nov 2013 18:22:31 +0100 Subject: [PATCH 092/800] (#22363) Add more String interpolation tests --- spec/unit/pops/evaluator/evaluating_parser_spec.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index 3ffd28185..5aafcc60b 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -555,7 +555,8 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do '"value is ${{a=>1,b=>2}} yo"' => "value is {a => 1, b => 2} yo", '"value is ${/.*/} yo"' => "value is /.*/ yo", '$x = undef "value is $x yo"' => "value is yo", - '$x = default "value is $x yo"' => "value is default yo", + '$x = default "value is $x yo"' => "value is default yo", + '$x = Array[Integer] "value is $x yo"' => "value is Array[Integer] yo", }.each do |source, result| it "should parse and evaluate the expression '#{source}' to #{result}" do populate From 050314a9417c49c68b6487c4d3f0000301ac9849 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 15 Nov 2013 00:13:44 +0100 Subject: [PATCH 093/800] (#22363) Remove ablity to apply operator % to floats The result is almost always surprising when trying modulo operator on floats. It only works in a meaningful way if the floating point numbers are integral in the first place. Rounding errors makes results very confusing. An error is now issued instead that % is not applicable to floats. --- lib/puppet/pops/evaluator/evaluator_impl.rb | 4 ++++ spec/unit/pops/evaluator/arithmetic_ops_spec.rb | 4 ++-- spec/unit/pops/evaluator/evaluating_parser_spec.rb | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index b5f32aec7..8760a8b59 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -394,6 +394,10 @@ class Puppet::Pops::Evaluator::EvaluatorImpl left = box_numeric(left, left_o, scope) right = box_numeric(right, right_o, scope) begin + if operator == :'%' && (left.is_a?(Float) || right.is_a?(Float)) + # Deny users the fun of seeing severe rounding errors and confusing results + fail(Issues::OPERATOR_NOT_APPLICABLE, left_o, {:operator => operator, :left_value => left}) + end result = left.send(operator, right) rescue NoMethodError => e fail(Issues::OPERATOR_NOT_APPLICABLE, left_o, {:operator => operator, :left_value => left}) diff --git a/spec/unit/pops/evaluator/arithmetic_ops_spec.rb b/spec/unit/pops/evaluator/arithmetic_ops_spec.rb index a8b98d8ee..6946b0039 100644 --- a/spec/unit/pops/evaluator/arithmetic_ops_spec.rb +++ b/spec/unit/pops/evaluator/arithmetic_ops_spec.rb @@ -30,10 +30,10 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do it "7.7 - 3.3 == 4.4" do; evaluate(literal(7.7) - literal(3.3)).should == 4.4 ; end it "6.1 * 3.1 == 18.91" do; evaluate(literal(6.1) * literal(3.1)).should == 18.91; end it "6.6 / 3.3 == 2.0" do; evaluate(literal(6.6) / literal(3.3)).should == 2.0 ; end - it "6.6 % 3.3 == 0.0" do; evaluate(literal(6.6) % literal(3.3)).should == 0.0 ; end - it "10.0 % 3.0 == 1.0" do; evaluate(literal(10.0) % literal(3.0)).should == 1.0 ; end it "-(6.0/3.0) == -2.0" do; evaluate(minus(literal(6.0) / literal(3.0))).should == -2.0; end it "-6.0/3.0 == -2.0" do; evaluate(minus(literal(6.0)) / literal(3.0)).should == -2.0; end + it "6.6 % 3.3 == 0.0" do; expect { evaluate(literal(6.6) % literal(3.3))}.to raise_error(Puppet::ParseError); end + it "10.0 % 3.0 == 1.0" do; expect { evaluate(literal(10.0) % literal(3.0))}.to raise_error(Puppet::ParseError); end it "3.14 << 2 == error" do; expect { evaluate(literal(3.14) << literal(2))}.to raise_error(Puppet::ParseError); end it "3.14 >> 2 == error" do; expect { evaluate(literal(3.14) >> literal(2))}.to raise_error(Puppet::ParseError); end end diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index 5aafcc60b..8dfcf2c9e 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -277,8 +277,6 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do "7.7 - 3.3" => 4.4, "6.1 * 3.1" => 18.91, "6.6 / 3.3" => 2.0, - "6.6 % 3.3" => 0.0, - "10.0 % 3.0" => 1.0, "-(6.0/3.0)" => -2.0, "-6.0/3.0 " => -2.0, }.each do |source, result| @@ -290,6 +288,8 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do { "3.14 << 2" => :error, "3.14 >> 2" => :error, + "6.6 % 3.3" => 0.0, + "10.0 % 3.0" => 1.0, }.each do |source, result| it "should parse and raise error for '#{source}'" do expect { parser.evaluate_string(scope, source, __FILE__) }.to raise_error(Puppet::ParseError) From e37a98b4a4b0b3bfb1187e416fdb9d630c613ee0 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 15 Nov 2013 00:25:39 +0100 Subject: [PATCH 094/800] (#22363) Add test that shows negaice shift reverses direction When shifting a negative count the direction of the shift is changed. This adds tests that shows this. --- spec/unit/pops/evaluator/arithmetic_ops_spec.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/unit/pops/evaluator/arithmetic_ops_spec.rb b/spec/unit/pops/evaluator/arithmetic_ops_spec.rb index 6946b0039..40e5d4844 100644 --- a/spec/unit/pops/evaluator/arithmetic_ops_spec.rb +++ b/spec/unit/pops/evaluator/arithmetic_ops_spec.rb @@ -23,6 +23,8 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do it "-6/3 == -2" do; evaluate(minus(literal(6)) / literal(3)).should == -2 ; end it "8 >> 1 == 4" do; evaluate(literal(8) >> literal(1)).should == 4 ; end it "8 << 1 == 16" do; evaluate(literal(8) << literal(1)).should == 16; end + it "8 >> -1 == 16" do; evaluate(literal(8) >> literal(-1)).should == 16 ; end + it "8 << -1 == 4" do; evaluate(literal(8) << literal(-1)).should == 4; end end context "on Floats" do From edf5e8eb24ba214c668e3c54bf78ef065fd39865 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 15 Nov 2013 01:07:59 +0100 Subject: [PATCH 095/800] (#22363) Add test that false != '' A falsey value is not equal to false. --- spec/unit/pops/evaluator/comparison_ops_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/unit/pops/evaluator/comparison_ops_spec.rb b/spec/unit/pops/evaluator/comparison_ops_spec.rb index 4a757ecb5..29938542e 100644 --- a/spec/unit/pops/evaluator/comparison_ops_spec.rb +++ b/spec/unit/pops/evaluator/comparison_ops_spec.rb @@ -91,6 +91,7 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do it "true == true == true" do; evaluate(literal(true) == literal(true)).should == true ; end; it "false == false == true" do; evaluate(literal(false) == literal(false)).should == true ; end; it "true == false != true" do; evaluate(literal(true) == literal(false)).should == false ; end; + it "false == '' == false" do; evaluate(literal(false) == literal('')).should == false ; end; end context "of collections" do From 8f6f9fbedd465d398c07c5bafc057efde24db61b Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 15 Nov 2013 03:43:14 +0100 Subject: [PATCH 096/800] (#22363) Remove support for compound assignments Compund (deflating) assignments where not completely implemented and it turned out to be a bad idea (i.e. indirect variable creating where [a,b,c] = [1,2,3] produces a = 1, b = 2, c = 3, and [a,b,c] = { x=>10, a=>1, b=>2, c =>3} produces the same). Not much is saved in terms of typing while having poor readability. It also adds further things that can not be statically analyzed since variable names can be dynamically computed. This can be abused. The construct was removed instead of completed. This also adds assignment tests for =, += and -= --- lib/puppet/pops/evaluator/evaluator_impl.rb | 36 ------------------- lib/puppet/pops/validation/checker4_0.rb | 2 +- .../pops/evaluator/evaluating_parser_spec.rb | 26 ++++++++++++++ 3 files changed, 27 insertions(+), 37 deletions(-) diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index 8760a8b59..7f0ad74d5 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -173,11 +173,6 @@ class Puppet::Pops::Evaluator::EvaluatorImpl evaluate(o.expr, scope) end - def lvalue_LiteralList(o, scope) - # evaluate the list - see assign_Array for how this is used - evaluate(o.expr, scope) - end - # Catches all illegal lvalues # def lvalue_Object(o, scope) @@ -206,36 +201,6 @@ class Puppet::Pops::Evaluator::EvaluatorImpl fail(Issues::ILLEGAL_NUMERIC_ASSIGNMENT, o.left_expr, {:varname => n.to_s}) end - # Assign values to named variables in an array. - # The '$' sign is never part of the name. - # @example Puppet DSL - # # all set to 10 - # [a, b, c] = 10 - # - # # set from matching entry in hash - # [a, b, c] = {a => 10, b => 20, c => 30} - # - # # set in sequence from array - # [a, b, c] = [1,2,3] - # - # @param names [Array] array of variable names without $ - # @param value [Object] value to assign to each variable - # @param o [Puppet::Pops::Model::PopsObject] originating instruction - # @param scope [Object] the runtime specific scope where evaluation should take place - # @return [value] - # - def assign_Array(names, value, o, scope) - case value - when Array - names.zip(value).each {|x| set_variable(x, value, o, scope) } - when Hash - names.each {|x| set_variable(x, value[x], o, scope) } - else - names.each {|x| set_variable(x, value, o, scope) } - end - value - end - # Catches all illegal assignment (e.g. 1 = 2, {'a'=>1} = 2, etc) # def assign_Object(name, value, o, scope) @@ -310,7 +275,6 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # $a -= 1 # def eval_AssignmentExpression(o, scope) - name = lvalue(o.left_expr, scope) value = evaluate(o.right_expr, scope) diff --git a/lib/puppet/pops/validation/checker4_0.rb b/lib/puppet/pops/validation/checker4_0.rb index 6c4c06145..f4f7833e9 100644 --- a/lib/puppet/pops/validation/checker4_0.rb +++ b/lib/puppet/pops/validation/checker4_0.rb @@ -147,7 +147,7 @@ class Puppet::Pops::Validation::Checker4_0 end def check_AssignmentExpression(o) - acceptor.accept(Issues::UNSUPPORTED_OPERATOR, o, {:operator => o.operator}) unless [:'=', :'+='].include? o.operator + acceptor.accept(Issues::UNSUPPORTED_OPERATOR, o, {:operator => o.operator}) unless [:'=', :'+=', :'-='].include? o.operator assign(o.left_expr) rvalue(o.right_expr) end diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index 8dfcf2c9e..956b982c5 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -325,6 +325,32 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do end end # arithmetic + context "When the evaluator evaluates assignment" do + { + "$a = 5" => 5, + "$a = 5; $a" => 5, + "$a = 5; $b = 6; $a" => 5, + "$a = $b = 5; $a == $b" => true, + "$a = [1,2,3]; [x].collect |$x| { $a += x; $a }" => [[1,2,3,'x']], + "$a = [a,x,c]; [x].collect |$x| { $a -= x; $a }" => [['a','c']], + }.each do |source, result| + it "should parse and evaluate the expression '#{source}' to #{result}" do + parser.evaluate_string(scope, source, __FILE__).should == result + end + end + + { + "[a,b,c] = [1,2,3]; $a == 1 and $b == 2 and $c == 3" => :error, + "[a,b,c] = {b=>2,c=>3,a=>1}; $a == 1 and $b == 2 and $c == 3" => :error, + "$a = [1,2,3]; [x].collect |$x| { [a] += x; $a }" => :error, + "$a = [a,x,c]; [x].collect |$x| { [a] -= x; $a }" => :error, + }.each do |source, result| + it "should parse and evaluate the expression '#{source}' to #{result}" do + expect { parser.evaluate_string(scope, source, __FILE__)}.to raise_error(Puppet::ParseError) + end + end + end + context "When the evaluator evaluates conditionals" do { "if true {5}" => 5, From aa234c0a3e49f3ce36daab8bc62dcca07a718c03 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 17 Nov 2013 05:09:14 +0100 Subject: [PATCH 097/800] (#22363) Make Integer[from, to] work as a Range type The idea to let Integer[x,y] create an array of all values in the range was bad. It is far more valuable to use this to create a subtype that limits the range of Integers to those between from, to. The implementation of this allows the range to be specified in both descending and ascending order (it is the same range in both cases), but behaves differently when iterated over. This also addes iteration of a type (added in the each function). Thus enabling almost the same behavior as earlier e.g. Integer[1,5].each |$x| { notice $x }, but without the step. Each supports (as for Array) using either index, value, or just value in the lambda. Also in this commit is a default to_s for all types that calls the type calculator to get a string. This because to_s is used through out puppet (e.g. when doing notice). Open ranges are supported by using default, a default in the first position means -Infinity, and a default in the second position means +Infinity. It is not possible to iterate over a range with an open end. --- lib/puppet/parser/functions/each.rb | 39 ++++++++++- lib/puppet/pops/evaluator/access_operator.rb | 35 +++++++--- lib/puppet/pops/evaluator/compare_operator.rb | 10 +-- lib/puppet/pops/evaluator/evaluator_impl.rb | 3 +- lib/puppet/pops/issues.rb | 4 +- lib/puppet/pops/types/type_calculator.rb | 69 ++++++++++++++++++- lib/puppet/pops/types/type_factory.rb | 10 +++ lib/puppet/pops/types/types.rb | 36 ++++++++++ spec/unit/pops/evaluator/access_ops_spec.rb | 34 ++++----- 9 files changed, 198 insertions(+), 42 deletions(-) diff --git a/lib/puppet/parser/functions/each.rb b/lib/puppet/parser/functions/each.rb index 70d8cb2bc..d9014ca20 100644 --- a/lib/puppet/parser/functions/each.rb +++ b/lib/puppet/parser/functions/each.rb @@ -79,6 +79,39 @@ Puppet::Parser::Functions::newfunction( o end + def foreach_Type(o, scope, pblock) + return nil unless pblock + tc = Puppet::Pops::Types::TypeCalculator.new() + enumerable = tc.enumerable(o) + if enumerable.nil? + raise ArgumentError, ("each(): given type '#{tc.string(o)}' is not enumerable") + end + serving_size = pblock.parameter_count + if serving_size == 0 + raise ArgumentError, "Block must define at least one parameter; value." + end + if serving_size > 2 + raise ArgumentError, "Block must define at most two parameters; index, value" + end + enumerator = enumerable.each + index = 0 + if serving_size == 1 + begin + loop { pblock.call(scope, enumerator.next) } + rescue StopIteration + end + else + begin + loop do + pblock.call(scope, index, enumerator.next) + index = index +1 + end + rescue StopIteration + end + end + o + end + raise ArgumentError, ("each(): wrong number of arguments (#{args.length}; must be 2)") if args.length != 2 receiver = args[0] pblock = args[1] @@ -90,6 +123,10 @@ Puppet::Parser::Functions::newfunction( when Hash foreach_Hash(receiver, self, pblock) else - raise ArgumentError, ("each(): wrong argument type (#{args[0].class}; must be an Array or a Hash.") + if receiver.is_a?(Puppet::Pops::Types::PAbstractType) + foreach_Type(receiver, self, pblock) + else + raise ArgumentError, ("each(): wrong argument type (#{args[0].class}; must be an Array, Hash, or Enumerable Type.") + end end end diff --git a/lib/puppet/pops/evaluator/access_operator.rb b/lib/puppet/pops/evaluator/access_operator.rb index 9ea7be415..fcfd210ef 100644 --- a/lib/puppet/pops/evaluator/access_operator.rb +++ b/lib/puppet/pops/evaluator/access_operator.rb @@ -92,24 +92,39 @@ class Puppet::Pops::Evaluator::AccessOperator end end - # An integer type provides a way to create an Array of integers from, to (inclusive) (must be given), and an - # optional step at the 3d position which defaults to 1 def access_PIntegerType(o, scope, keys) - unless keys.size.between?(2, 3) + unless keys.size.between?(1, 2) fail(Puppet::Pops::Issues::BAD_INTEGER_SLICE_ARITY, @semantic, {:actual => keys.size}) end keys.each_with_index do |x, index| fail(Puppet::Pops::Issues::BAD_INTEGER_SLICE_TYPE, @semantic.keys[index], - {:actual => x.class}) unless x.is_a?(Numeric) + {:actual => x.class}) unless (x.is_a?(Numeric) || x == :default) end - from, to, step = keys - fail(Puppet::Pops::Issues::INTEGER_STEP_0, @semantic.keys[2]) if step == 0 - step ||= 1 - - # Ok, so this is quite bad for very large arrays... - from.step(to, step).collect {|x| x} + ranged_integer = Puppet::Pops::Types::PIntegerType.new() + from, to = keys + ranged_integer.from = from == :default ? nil : from + ranged_integer.to = to == :default ? nil : to + ranged_integer end + # An integer type provides a way to create an Array of integers from, to (inclusive) (must be given), and an + # optional step at the 3d position which defaults to 1 +# def access_PIntegerType(o, scope, keys) +# unless keys.size.between?(2, 3) +# fail(Puppet::Pops::Issues::BAD_INTEGER_SLICE_ARITY, @semantic, {:actual => keys.size}) +# end +# keys.each_with_index do |x, index| +# fail(Puppet::Pops::Issues::BAD_INTEGER_SLICE_TYPE, @semantic.keys[index], +# {:actual => x.class}) unless x.is_a?(Numeric) +# end +# from, to, step = keys +# fail(Puppet::Pops::Issues::INTEGER_STEP_0, @semantic.keys[2]) if step == 0 +# step ||= 1 +# +# # Ok, so this is quite bad for very large arrays... +# from.step(to, step).collect {|x| x} +# end + # A Hash can create a new Hash type, one arg sets value type, two args sets key and value type in new type # It is not possible to create a collection of Hash types. # diff --git a/lib/puppet/pops/evaluator/compare_operator.rb b/lib/puppet/pops/evaluator/compare_operator.rb index 30406af97..f9d0e4146 100644 --- a/lib/puppet/pops/evaluator/compare_operator.rb +++ b/lib/puppet/pops/evaluator/compare_operator.rb @@ -127,11 +127,13 @@ class Puppet::Pops::Evaluator::CompareOperator when Puppet::Pops::Types::PStringType # is there a string in a string? (yes, each char is a string, and an empty string contains an empty string) true - when Puppet::Pops::Types::PDataType - # is there data in a string?, (yes, a substring is data) - true else - false + if b == Puppet::Pops::Types::PDataType || b == Puppet::Pops::Types::PObjectType + # A String is Data and Object (but not of all subtypes of those types). + true + else + false + end end end diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index 7f0ad74d5..970d0f1cf 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -908,8 +908,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # def is_match? left, right, o, scope if right.is_a?(Regexp) - # TODO: Possibly use unique Issue key for this - fail(Issues::MATCH_NOT_STRING, o, {:left_value => left}) unless left.is_a? String + return false unless left.is_a? String matched = right.match(left) set_match_data(matched, o, scope) # creates or clears ephemeral !!matched # convert to boolean diff --git a/lib/puppet/pops/issues.rb b/lib/puppet/pops/issues.rb index 36f01710a..824cffbca 100644 --- a/lib/puppet/pops/issues.rb +++ b/lib/puppet/pops/issues.rb @@ -326,11 +326,11 @@ module Puppet::Pops::Issues end BAD_INTEGER_SLICE_ARITY = issue :BAD_INTEGER_SLICE_ARITY, :actual do - "Integer supports [] with two or three arguments (from, to, step). Got #{actual}" + "Integer supports [] with one or two arguments (from, to). Got #{actual}" end BAD_INTEGER_SLICE_TYPE = issue :BAD_INTEGER_SLICE_TYPE, :actual do - "Integer [] requires all arguments to be integers. Got #{actual}" + "Integer [] requires all arguments to be integers (or default). Got #{actual}" end INTEGER_STEP_0 = issue :INTEGER_STEP_0 do diff --git a/lib/puppet/pops/types/type_calculator.rb b/lib/puppet/pops/types/type_calculator.rb index edbf2b252..94b3656ae 100644 --- a/lib/puppet/pops/types/type_calculator.rb +++ b/lib/puppet/pops/types/type_calculator.rb @@ -69,6 +69,7 @@ class Puppet::Pops::Types::TypeCalculator Types = Puppet::Pops::Types + TheInfinity = 1.0 / 0.0 # because the Infinity symbol is not defined # @api public # @@ -76,6 +77,7 @@ class Puppet::Pops::Types::TypeCalculator @@assignable_visitor ||= Puppet::Pops::Visitor.new(nil,"assignable",1,1) @@infer_visitor ||= Puppet::Pops::Visitor.new(nil,"infer",0,0) @@string_visitor ||= Puppet::Pops::Visitor.new(nil,"string",0,0) + @@enumerable_visitor ||= Puppet::Pops::Visitor.new(nil,"enumerable",0,0) da = Types::PArrayType.new() da.element_type = Types::PDataType.new() @@ -142,6 +144,11 @@ class Puppet::Pops::Types::TypeCalculator @@assignable_visitor.visit_this_1(self, t, t2) end + # Returns an enumerable if the t represents something that can be iterated + def enumerable(t) + @@enumerable_visitor.visit_this_0(self, t) + end + # Answers 'what is the Puppet Type corresponding to the given Ruby class' # @param c [Class] the class for which a puppet type is wanted # @api public @@ -260,6 +267,18 @@ class Puppet::Pops::Types::TypeCalculator return result end + # Integers have range, narrow the range to the common range + if t1.is_a?(Types::PIntegerType) && t2.is_a?(Types::PIntegerType) + t1range = from_to_ordered(t1.from, t1.to) + t2range = from_to_ordered(t2.from, t2.to) + t = Types::PIntegerType.new() + from = [t1range[0], t2range[0]].min + to = [t1range[1], t2range[1]].max + t.from = from unless from == TheInfinity + t.to = to unless to == TheInfinity + return t + end + # Common abstract types, from most specific to most general if common_numeric?(t1, t2) return Types::PNumericType.new() @@ -383,7 +402,10 @@ class Puppet::Pops::Types::TypeCalculator # @api private def infer_Integer(o) - Types::PIntegerType.new() + t = Types::PIntegerType.new() + t.from = o + t.to = o + t end # @api private @@ -471,7 +493,22 @@ class Puppet::Pops::Types::TypeCalculator # @api private def assignable_PIntegerType(t, t2) - t2.is_a?(Types::PIntegerType) + return false unless t2.is_a?(Types::PIntegerType) + trange = from_to_ordered(t.from, t.to) + t2range = from_to_ordered(t2.from, t2.to) + # If t2 min and max are within the range of t + trange[0] <= t2range[0] && trange[1] >= t2range[1] + end + + # @api private + def from_to_ordered(from, to) + x = (from.nil? || from == :default) ? -TheInfinity : from + y = (to.nil? || to == :default) ? TheInfinity : to + if x < y + [x, y] + else + [y, x] + end end # @api private @@ -589,7 +626,20 @@ class Puppet::Pops::Types::TypeCalculator def string_PNumericType(t) ; "Numeric" ; end # @api private - def string_PIntegerType(t) ; "Integer" ; end + def string_PIntegerType(t) + result = ["Integer"] + unless t.from.nil? && t.to.nil? + from = t.from.nil? ? 'default' : t.from + to = t.to.nil? ? 'default' : t.to + if from == to + "Integer[#{from}]" + else + "Integer[#{from}, #{to}]" + end + else + "Integer" + end + end # @api private def string_PFloatType(t) ; "Float" ; end @@ -643,6 +693,19 @@ class Puppet::Pops::Types::TypeCalculator end end + # Catches all non enumerable types + # @api private + def enumerable_Object(o) + nil + end + + # @api private + def enumerable_PIntegerType(t) + # Not enumerable if representing an infinite range + return nil if t.size == TheInfinity + t + end + private def class_from_string(str) diff --git a/lib/puppet/pops/types/type_factory.rb b/lib/puppet/pops/types/type_factory.rb index aeeab8cad..1f931803c 100644 --- a/lib/puppet/pops/types/type_factory.rb +++ b/lib/puppet/pops/types/type_factory.rb @@ -13,6 +13,16 @@ module Puppet::Pops::Types::TypeFactory Types::PIntegerType.new() end + # Produces the Integer type + # @api public + # + def self.range(from, to) + t = Types::PIntegerType.new() + t.from = from unless from == :default + t.to = to unless to == :default + t + end + # Produces the Float type # @api public # diff --git a/lib/puppet/pops/types/types.rb b/lib/puppet/pops/types/types.rb index a6a455246..e3ba3971e 100644 --- a/lib/puppet/pops/types/types.rb +++ b/lib/puppet/pops/types/types.rb @@ -34,6 +34,9 @@ module Puppet::Pops::Types alias eql? == + def to_s + Puppet::Pops::Types::TypeCalculator.new.string(self) + end end end @@ -86,6 +89,39 @@ module Puppet::Pops::Types # @api public class PIntegerType < PNumericType + has_attr 'from', Integer, :lowerBound => 0 + has_attr 'to', Integer, :lowerBound => 0 + + module ClassModule + # The integer type is enumerable when it defines a range + include Enumerable + + # Returns Float.Infinity if one end of the range is unbound + def size + return 1.0 / 0.0 if from.nil? || to.nil? + (to-from).abs + end + + # Returns Enumerator if no block is given + # Returns self if size is infinity (does not yield) + def each + return self.to_enum unless block_given? + return nil if from.nil? || to.nil? + if to < from + from.downto(to) {|x| yield x } + else + from.upto(to) {|x| yield x } + end + end + + def hash + [self.class, from, to].hash + end + + def ==(o) + self.class == o.class && from == from && to == to + end + end end # @api public diff --git a/spec/unit/pops/evaluator/access_ops_spec.rb b/spec/unit/pops/evaluator/access_ops_spec.rb index b11488315..107c0cecd 100644 --- a/spec/unit/pops/evaluator/access_ops_spec.rb +++ b/spec/unit/pops/evaluator/access_ops_spec.rb @@ -12,6 +12,10 @@ require File.join(File.dirname(__FILE__), '/evaluator_rspec_helper') describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do include EvaluatorRspecHelper + def range(from, to) + Puppet::Pops::Types::TypeFactory.range(from, to) + end + context 'The evaluator when operating on a String' do it 'can get a single character using a single key index to []' do expect(evaluate(literal('abc')[1])).to eql('b') @@ -93,34 +97,24 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do # Integer # - it 'produces an Array of ascending values in range from Integer[from, to]' do + it 'produces an Integer[from, to]' do expr = fqr('Integer')[1, 3] - expect(evaluate(expr)).to eql([1,2,3]) + expect(evaluate(expr)).to eql(range(1,3)) end - it 'produces an Array of one value in range from Integer[from, from]' do - expr = fqr('Integer')[1,1] - expect(evaluate(expr)).to eql([1]) + it 'produces an Integer[1]' do + expr = fqr('Integer')[1] + expect(evaluate(expr)).to eql(range(1,1)) end - it 'produces an empty Array from Integer[from, Date: Sun, 17 Nov 2013 23:31:34 +0100 Subject: [PATCH 098/800] (#22363) Add test for Integer type to string Tests missing for Integer to string when there is a range that is not +-Infinity. --- spec/unit/pops/types/type_calculator_spec.rb | 21 ++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/spec/unit/pops/types/type_calculator_spec.rb b/spec/unit/pops/types/type_calculator_spec.rb index 5ac3f81e1..898285090 100644 --- a/spec/unit/pops/types/type_calculator_spec.rb +++ b/spec/unit/pops/types/type_calculator_spec.rb @@ -525,8 +525,25 @@ describe 'The type calculator' do calculator.string(Puppet::Pops::Types::PNumericType.new()).should == 'Numeric' end - it 'should yield \'Integer\' for PIntegerType' do - calculator.string(Puppet::Pops::Types::PIntegerType.new()).should == 'Integer' + it 'should yield \'Integer\' and from/to for PIntegerType' do + int_T = Puppet::Pops::Types::PIntegerType + calculator.string(int_T.new()).should == 'Integer' + int = int_T.new() + int.from = 1 + int.to = 1 + calculator.string(int).should == 'Integer[1]' + int = int_T.new() + int.from = 1 + int.to = 2 + calculator.string(int).should == 'Integer[1, 2]' + int = int_T.new() + int.from = nil + int.to = 2 + calculator.string(int).should == 'Integer[default, 2]' + int = int_T.new() + int.from = 2 + int.to = nil + calculator.string(int).should == 'Integer[2, default]' end it 'should yield \'Float\' for PFloatType' do From 57c869db287ce980d44436a17f656b90c48c4ca6 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 17 Nov 2013 23:37:53 +0100 Subject: [PATCH 099/800] (#22363) Add Integer range inference test --- spec/unit/pops/types/type_calculator_spec.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/spec/unit/pops/types/type_calculator_spec.rb b/spec/unit/pops/types/type_calculator_spec.rb index 898285090..f5a8a5d0d 100644 --- a/spec/unit/pops/types/type_calculator_spec.rb +++ b/spec/unit/pops/types/type_calculator_spec.rb @@ -60,6 +60,13 @@ describe 'The type calculator' do calculator.infer([1,2**33]).element_type.class.should == Puppet::Pops::Types::PIntegerType end + it 'Range of integer values are computed' do + t = calculator.infer([-3,0,42]).element_type + t.class.should == Puppet::Pops::Types::PIntegerType + t.from.should == -3 + t.to.should == 42 + end + it 'with fixnum and float values translates to PArrayType[PNumericType]' do calculator.infer([1,2.0]).element_type.class.should == Puppet::Pops::Types::PNumericType end From 60dc68ca82677bf0deef03d1769b986da0b3247e Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 17 Nov 2013 23:52:09 +0100 Subject: [PATCH 100/800] (#22363) Add test of instance? for integer range --- spec/unit/pops/types/type_calculator_spec.rb | 55 +++++++++++++++++++- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/spec/unit/pops/types/type_calculator_spec.rb b/spec/unit/pops/types/type_calculator_spec.rb index f5a8a5d0d..47ce5c1d4 100644 --- a/spec/unit/pops/types/type_calculator_spec.rb +++ b/spec/unit/pops/types/type_calculator_spec.rb @@ -4,6 +4,13 @@ require 'puppet/pops' describe 'The type calculator' do let(:calculator) { Puppet::Pops::Types::TypeCalculator.new() } + def int_range(from, to) + t = Puppet::Pops::Types::PIntegerType.new + t.from = from + t.to = to + t + end + context 'when inferring ruby' do it 'fixnum translates to PIntegerType' do @@ -384,6 +391,42 @@ describe 'The type calculator' do calculator.assignable?(Puppet::Pops::Types::TypeFactory.hash_of_data(), Hash).should == true end + context 'when dealing with integer ranges' do + it 'should accept an equal range' do + calculator.assignable?(int_range(2,5), int_range(2,5)).should == true + end + + it 'should accept an equal reverse range' do + calculator.assignable?(int_range(2,5), int_range(5,2)).should == true + end + + it 'should accept a narrower range' do + calculator.assignable?(int_range(2,10), int_range(3,5)).should == true + end + + it 'should accept a narrower reverse range' do + calculator.assignable?(int_range(2,10), int_range(5,3)).should == true + end + + it 'should reject a wider range' do + calculator.assignable?(int_range(3,5), int_range(2,10)).should == false + end + + it 'should reject a wider reverse range' do + calculator.assignable?(int_range(3,5), int_range(10,2)).should == false + end + + it 'should reject a partially overlapping range' do + calculator.assignable?(int_range(3,5), int_range(2,4)).should == false + calculator.assignable?(int_range(3,5), int_range(4,6)).should == false + end + + it 'should reject a partially overlapping reverse range' do + calculator.assignable?(int_range(3,5), int_range(4,2)).should == false + calculator.assignable?(int_range(3,5), int_range(6,4)).should == false + end + end + it 'should recognize ruby type inheritance' do class Foo end @@ -456,11 +499,19 @@ describe 'The type calculator' do context 'when testing if x is instance of type t' do it 'should consider fixnum instanceof PIntegerType' do - calculator.instance?(Puppet::Pops::Types::PIntegerType.new(), 1) + calculator.instance?(Puppet::Pops::Types::PIntegerType.new(), 1) == true end it 'should consider fixnum instanceof Fixnum' do - calculator.instance?(Fixnum, 1) + calculator.instance?(Fixnum, 1) == true + end + + it 'should consider integer in range' do + range = int_range(0,10) + calculator.instance?(range, 1) == true + calculator.instance?(range, 10) == true + calculator.instance?(range, -1) == false + calculator.instance?(range, 11) == false end end From 7b28eaa1ad54e0d4e75a4679ba9bd41fe036a8a8 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 18 Nov 2013 00:55:28 +0100 Subject: [PATCH 101/800] (maint) Remove dead code/comment --- lib/puppet/pops/evaluator/access_operator.rb | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/lib/puppet/pops/evaluator/access_operator.rb b/lib/puppet/pops/evaluator/access_operator.rb index fcfd210ef..2bd3cbf43 100644 --- a/lib/puppet/pops/evaluator/access_operator.rb +++ b/lib/puppet/pops/evaluator/access_operator.rb @@ -107,24 +107,6 @@ class Puppet::Pops::Evaluator::AccessOperator ranged_integer end - # An integer type provides a way to create an Array of integers from, to (inclusive) (must be given), and an - # optional step at the 3d position which defaults to 1 -# def access_PIntegerType(o, scope, keys) -# unless keys.size.between?(2, 3) -# fail(Puppet::Pops::Issues::BAD_INTEGER_SLICE_ARITY, @semantic, {:actual => keys.size}) -# end -# keys.each_with_index do |x, index| -# fail(Puppet::Pops::Issues::BAD_INTEGER_SLICE_TYPE, @semantic.keys[index], -# {:actual => x.class}) unless x.is_a?(Numeric) -# end -# from, to, step = keys -# fail(Puppet::Pops::Issues::INTEGER_STEP_0, @semantic.keys[2]) if step == 0 -# step ||= 1 -# -# # Ok, so this is quite bad for very large arrays... -# from.step(to, step).collect {|x| x} -# end - # A Hash can create a new Hash type, one arg sets value type, two args sets key and value type in new type # It is not possible to create a collection of Hash types. # From 39acf016c2b7d298ac56492390b3a0371d4cc56e Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 18 Nov 2013 01:10:08 +0100 Subject: [PATCH 102/800] (#22363) Fix problems in TypeParser The TypeParser misinterpreted several types as being ResourceType if an attempt was made to parameterize a non parameterized type. The type Object is now also supported. When the TypeParser was used to only interpret it failed when there was an error - this because the type parser was not used to parse the original string. All ast objects have a Locator when they come from the future parser and it holds the parsed string. Now, the string is obtained from the locator if not originally parsed via the type parser. (This should be cleaned up later). --- lib/puppet/pops/types/type_factory.rb | 7 +++ lib/puppet/pops/types/type_parser.rb | 61 +++++++++++++++++++----- spec/unit/pops/types/type_parser_spec.rb | 16 ++++++- 3 files changed, 70 insertions(+), 14 deletions(-) diff --git a/lib/puppet/pops/types/type_factory.rb b/lib/puppet/pops/types/type_factory.rb index 1f931803c..059617621 100644 --- a/lib/puppet/pops/types/type_factory.rb +++ b/lib/puppet/pops/types/type_factory.rb @@ -58,6 +58,13 @@ module Puppet::Pops::Types::TypeFactory Types::PBooleanType.new() end + # Produces the Object type + # @api public + # + def self.object() + Types::PObjectType.new() + end + # Produces the Pattern type # @api public # diff --git a/lib/puppet/pops/types/type_parser.rb b/lib/puppet/pops/types/type_parser.rb index b65a4c387..d315291ec 100644 --- a/lib/puppet/pops/types/type_parser.rb +++ b/lib/puppet/pops/types/type_parser.rb @@ -29,6 +29,11 @@ class Puppet::Pops::Types::TypeParser # @api public # def parse(string) + # TODO: This state (@string) can be removed since the parse result of newer future parser + # contains a Locator in its SourcePosAdapter and the Locator keeps the the string. + # This way, there is no difference between a parsed "string" and something that has been parsed + # earlier and fed to 'interpret' + # @string = string model = @parser.parse_string(@string) if model @@ -40,34 +45,43 @@ class Puppet::Pops::Types::TypeParser # @api private def interpret(ast) - result = @type_transformer.visit_this(self, ast) + result = @type_transformer.visit_this_0(self, ast) raise_invalid_type_specification_error unless result.is_a?(Puppet::Pops::Types::PAbstractType) result end # @api private def interpret_any(ast) - @type_transformer.visit_this(self, ast) + @type_transformer.visit_this_0(self, ast) end # @api private - def interpret_Object(anything) + def interpret_Object(o) raise_invalid_type_specification_error end # @api private - def interpret_QualifiedName(name_ast) - name_ast.value + def interpret_QualifiedName(o) + o.value end # @api private - def interpret_LiteralString(string_ast) - string_ast.value + def interpret_LiteralString(o) + o.value end # @api private - def interpret_String(string_object) - string_object + def interpret_String(o) + o + end + + # @api private + def interpret_LiteralDefault(o) + :default + end + + def interpret_LiteralInteger(o) + o.value end # @api private @@ -102,7 +116,9 @@ class Puppet::Pops::Types::TypeParser when "undef" # Should not be interpreted as Resource type TYPES.undef() - when "object", "ruby", "type" + when "object" + TYPES.object() + when "ruby", "type" # should not be interpreted as Resource type # TODO: these should not be errors raise_unknown_type_error(name_ast) @@ -154,7 +170,25 @@ class Puppet::Pops::Types::TypeParser TYPES.resource(parameters[0], parameters[1]) end - when "object", "collection", "ruby", "type" + when "integer" + if parameters.size == 1 + case parameters[0] + when Integer + TYPES.range(parameters[0], parameters[0]) + when :default + TYPES.integer # unbound + end + elsif parameters.size != 2 + raise_invalid_parameters_error("Integer", "1 or 2", parameters.size) + else + TYPES.range(parameters[0] == :default ? nil : parameters[0], parameters[1] == :default ? nil : parameters[1]) + end + + when "object", "collection", "data", "catalogentry", "boolean", "float", "literal", "undef", "numeric", "pattern" + raise_unparameterized_type_error(parameterized_ast.left_expr) + + when "ruby", "type" + # TODO: Add Stage, Node (they are not Resource Type) # should not be interpreted as Resource type raise_unknown_type_error(parameterized_ast.left_expr) @@ -183,6 +217,9 @@ class Puppet::Pops::Types::TypeParser raise Puppet::ParseError, "Invalid number of type parameters specified: #{type} requires #{required}, #{given} provided" end + def raise_unparameterized_type_error(ast) + raise Puppet::ParseError, "Not a parameterized type <#{original_text_of(ast)}>" + end def raise_unknown_type_error(ast) raise Puppet::ParseError, "Unknown type <#{original_text_of(ast)}>" @@ -190,6 +227,6 @@ class Puppet::Pops::Types::TypeParser def original_text_of(ast) position = Puppet::Pops::Adapters::SourcePosAdapter.adapt(ast) - position.extract_text_from_string(@string) + position.extract_text_from_string(@string || position.locator.string) end end diff --git a/spec/unit/pops/types/type_parser_spec.rb b/spec/unit/pops/types/type_parser_spec.rb index 49b154e67..9c6250ef7 100644 --- a/spec/unit/pops/types/type_parser_spec.rb +++ b/spec/unit/pops/types/type_parser_spec.rb @@ -30,14 +30,22 @@ describe Puppet::Pops::Types::TypeParser do end it "does not support types that do not make sense in the puppet language" do - expect { parser.parse("Object") }.to raise_type_error_for("Object") - expect { parser.parse("Collection[Integer]") }.to raise_type_error_for("Collection") # These will/may make sense later, but are not yet implemented and should not be interpreted as a PResourceType expect { parser.parse("Ruby") }.to raise_type_error_for("Ruby") expect { parser.parse("Type") }.to raise_type_error_for("Type") end + [ + 'Object', 'Float', 'Collection', 'Data', 'CatalogEntry', 'Boolean', 'Float', 'Literal', 'Undef', 'Numeric', + 'Pattern' + ].each do |name| + it "does not support parameterizing unparameterized type <#{name}" do + expect { parser.parse("#{name}[Integer]") }.to raise_unparameterized_error_for(name) + end + end + it "parses a simple, unparameterized type into the type object" do + expect(the_type_parsed_from(types.object)).to be_the_type(types.object) expect(the_type_parsed_from(types.integer)).to be_the_type(types.integer) expect(the_type_parsed_from(types.float)).to be_the_type(types.float) expect(the_type_parsed_from(types.string)).to be_the_type(types.string) @@ -117,6 +125,10 @@ describe Puppet::Pops::Types::TypeParser do raise_error(Puppet::ParseError, /Unknown type <#{type_name}>/) end + def raise_unparameterized_error_for(type_name) + raise_error(Puppet::ParseError, /Not a parameterized type <#{type_name}>/) + end + def the_type_parsed_from(type) parser.parse(the_type_spec_for(type)) end From 81e85da6d6976e0cb609b68bfff57eeeadad18c0 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 18 Nov 2013 01:14:40 +0100 Subject: [PATCH 103/800] (#22363) Fix problem with reversal of compare <, > of types The logic was reversed compared to Ruby. Object > Integer should be true with the rational that the set of all objects > the set of all integers. The evaluator impl had this wrong. --- lib/puppet/pops/evaluator/evaluator_impl.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index 970d0f1cf..b8ca3a682 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -402,17 +402,17 @@ class Puppet::Pops::Evaluator::EvaluatorImpl when :'!=' ! @@compare_operator.equals(left,right) when :'<' - # right can be assigned to left, but they are not equal - @@type_calculator.assignable?(left, right) && ! @@compare_operator.equals(left,right) - when :'<=' - # right can be assigned to left - @@type_calculator.assignable?(left, right) - when :'>' # left can be assigned to right, but they are not equal @@type_calculator.assignable?(right, left) && ! @@compare_operator.equals(left,right) - when :'>=' + when :'<=' # left can be assigned to right @@type_calculator.assignable?(right, left) + when :'>' + # right can be assigned to left, but they are not equal + @@type_calculator.assignable?(left,right) && ! @@compare_operator.equals(left,right) + when :'>=' + # right can be assigned to left + @@type_calculator.assignable?(left, right) else fail(Issues::UNSUPPORTED_OPERATOR, o, {:operator => o.operator}) end From ac7618d1d840b6da2216c085a841d75676105bbd Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 18 Nov 2013 02:01:51 +0100 Subject: [PATCH 104/800] (#22363) Fix issues with type comparisons Integer type equality was broken (always true). Literal type missing in type parser. Literal type not considered to be Data Adds test. --- lib/puppet/pops/types/type_calculator.rb | 2 +- lib/puppet/pops/types/type_parser.rb | 2 ++ lib/puppet/pops/types/types.rb | 2 +- .../pops/evaluator/evaluating_parser_spec.rb | 26 +++++++++++++++++++ 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/lib/puppet/pops/types/type_calculator.rb b/lib/puppet/pops/types/type_calculator.rb index 94b3656ae..3d60bd367 100644 --- a/lib/puppet/pops/types/type_calculator.rb +++ b/lib/puppet/pops/types/type_calculator.rb @@ -579,7 +579,7 @@ class Puppet::Pops::Types::TypeCalculator # Data is assignable by other Data and by Array[Data] and Hash[Literal, Data] # @api private def assignable_PDataType(t, t2) - t2.is_a?(Types::PDataType) || assignable?(@data_array, t2) || assignable?(@data_hash, t2) + t2.is_a?(Types::PDataType) || t2.is_a?(Types::PLiteralType) || assignable?(@data_array, t2) || assignable?(@data_hash, t2) end # Assignable if t2's ruby class is same or subclass of t1's ruby class diff --git a/lib/puppet/pops/types/type_parser.rb b/lib/puppet/pops/types/type_parser.rb index d315291ec..1280fc67e 100644 --- a/lib/puppet/pops/types/type_parser.rb +++ b/lib/puppet/pops/types/type_parser.rb @@ -111,6 +111,8 @@ class Puppet::Pops::Types::TypeParser TYPES.resource() when "collection" TYPES.collection() + when "literal" + TYPES.literal() when "catalogentry" TYPES.catalog_entry() when "undef" diff --git a/lib/puppet/pops/types/types.rb b/lib/puppet/pops/types/types.rb index e3ba3971e..b0cdcce7d 100644 --- a/lib/puppet/pops/types/types.rb +++ b/lib/puppet/pops/types/types.rb @@ -119,7 +119,7 @@ module Puppet::Pops::Types end def ==(o) - self.class == o.class && from == from && to == to + self.class == o.class && from == o.from && to == o.to end end end diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index 956b982c5..15362d104 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -242,6 +242,8 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do "Array[Integer] in [2, [3, 4]]" => true, "Array[Integer] in [2, [a, 4]]" => false, "Integer in { 2 =>'a'}" => true, + "Integer[5,10] in [1,5,3]" => true, + "Integer[5,10] in [1,2,3]" => false, "Integer in {'a'=>'a'}" => false, "Integer in {'a'=>1}" => false, }.each do |source, result| @@ -250,6 +252,30 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do end end + { + 'Object' => ['Data', 'Literal', 'Numeric', 'Integer', 'Float', 'Boolean', 'String', 'Pattern', 'Collection', + 'Array', 'Hash', 'CatalogEntry', 'Resource', 'Class', 'Undef', 'File', 'NotYetKnownResourceType'], + + # Note, Data > Collection is false (so not included) + 'Data' => ['Literal', 'Numeric', 'Integer', 'Float', 'Boolean', 'String', 'Pattern', 'Array', 'Hash',], + 'Literal' => ['Numeric', 'Integer', 'Float', 'Boolean', 'String', 'Pattern'], + 'Numeric' => ['Integer', 'Float'], + 'CatalogEntry' => ['Class', 'Resource', 'File', 'NotYetKnownResourceType'], + 'Integer[1,10]' => ['Integer[2,3]'], + }.each do |general, specials| + specials.each do |special | + it "should compute that #{general} > #{special}" do + parser.evaluate_string(scope, "#{general} > #{special}", __FILE__).should == true + end + it "should compute that #{special} < #{general}" do + parser.evaluate_string(scope, "#{special} < #{general}", __FILE__).should == true + end + it "should compute that #{general} != #{special}" do + parser.evaluate_string(scope, "#{special} != #{general}", __FILE__).should == true + end + end + end + end context "When the evaluator performs arithmetic" do From 9c5b4af0791ea87e0dd026e37dff81935bd7f352 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 18 Nov 2013 02:25:43 +0100 Subject: [PATCH 105/800] (#22363) Add additional Integer range tests --- spec/unit/pops/evaluator/evaluating_parser_spec.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index 15362d104..3707390f1 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -276,6 +276,19 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do end end + { + 'Integer[1,10] > Integer[2,3]' => true, + 'Integer[1,10] == Integer[2,3]' => false, + 'Integer[1,10] > Integer[0,5]' => false, + 'Integer[1,10] > Integer[1,10]' => false, + 'Integer[1,10] >= Integer[1,10]' => true, + 'Integer[1,10] == Integer[1,10]' => true, + }.each do |source, result| + it "should parse and evaluate the integer range comparison expression '#{source}' to #{result}" do + parser.evaluate_string(scope, source, __FILE__).should == result + end + end + end context "When the evaluator performs arithmetic" do From 2ddb879433199474ed5fb3e7b325715b462ccc81 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 18 Nov 2013 17:07:35 +0100 Subject: [PATCH 106/800] (#22363) Add capability to match against Type Using the match operator with a Type RHS performs a puppet instance? test --- lib/puppet/pops/evaluator/evaluator_impl.rb | 29 ++++++++++++------- .../pops/evaluator/evaluating_parser_spec.rb | 21 ++++++++------ 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index b8ca3a682..fa251eb65 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -443,7 +443,8 @@ class Puppet::Pops::Evaluator::EvaluatorImpl end end - # Evaluates matching expressions with string or regexp rhs expression. + # Evaluates matching expressions with type, string or regexp rhs expression. + # If RHS is a type, the =~ matches compatible (assignable?) type. # # @example # x =~ /abc.*/ @@ -452,21 +453,27 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # @example # y = "abc" # x =~ "${y}.*" + # @example + # [1,2,3] =~ Array[Integer[1,10]] # @return [Boolean] if a match was made or not. Also sets $0..$n to matchdata in current scope. # def eval_MatchExpression o, scope left, pattern = eval_BinaryExpression o, scope - begin - pattern = Regexp.new(pattern) unless pattern.is_a?(Regexp) - rescue StandardError => e - fail(Issues::MATCH_NOT_REGEXP, o.right_expr, {:detail => e.message}) - end - unless left.is_a?(String) - fail(Issues::MATCH_NOT_STRING, o.left_expr, {:left_value => left}) - end + if pattern.is_a?(Puppet::Pops::Types::PAbstractType) + matched = @@type_calculator.instance?(pattern, left) + else + begin + pattern = Regexp.new(pattern) unless pattern.is_a?(Regexp) + rescue StandardError => e + fail(Issues::MATCH_NOT_REGEXP, o.right_expr, {:detail => e.message}) + end + unless left.is_a?(String) + fail(Issues::MATCH_NOT_STRING, o.left_expr, {:left_value => left}) + end - matched = pattern.match(left) # nil, or MatchData - set_match_data(matched, o, scope) # creates ephemeral + matched = pattern.match(left) # nil, or MatchData + set_match_data(matched, o, scope) # creates ephemeral + end # convert match result to Boolean true, or false o.operator == :'=~' ? !!matched : !matched diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index 3707390f1..d647d1bd0 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -178,15 +178,18 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do end { - "'a' =~ /.*/" => true, - "'a' =~ '.*'" => true, - "/.*/ != /a.*/" => true, - "'a' !~ /b.*/" => true, - "'a' !~ 'b.*'" => true, - '$x = a; a =~ "$x.*"' => true, - "a =~ Pattern['a.*']" => true, - "$x = /a.*/ a =~ $x" => true, - "$x = Pattern['a.*'] a =~ $x" => true, + "'a' =~ /.*/" => true, + "'a' =~ '.*'" => true, + "/.*/ != /a.*/" => true, + "'a' !~ /b.*/" => true, + "'a' !~ 'b.*'" => true, + '$x = a; a =~ "$x.*"' => true, + "a =~ Pattern['a.*']" => true, + "$x = /a.*/ a =~ $x" => true, + "$x = Pattern['a.*'] a =~ $x" => true, + "1 =~ Integer" => true, + "1 !~ Integer" => false, + "[1,2,3] =~ Array[Integer[1,10]]" => true, }.each do |source, result| it "should parse and evaluate the expression '#{source}' to #{result}" do parser.evaluate_string(scope, source, __FILE__).should == result From 463ce093e053ddc02c1da74c44d3a9bdfe2f15d8 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 18 Nov 2013 20:15:29 +0100 Subject: [PATCH 107/800] (#22363) Make access with [] also work for class parameters --- lib/puppet/parser/ast/pops_bridge.rb | 13 ++++++------- lib/puppet/parser/e4_parser_adapter.rb | 2 ++ lib/puppet/pops/evaluator/access_operator.rb | 15 ++++++++++++++- lib/puppet/pops/evaluator/runtime3_support.rb | 3 +++ lib/puppet/pops/issues.rb | 4 ++++ .../pops/evaluator/evaluating_parser_spec.rb | 18 ++++++++++++++++++ 6 files changed, 47 insertions(+), 8 deletions(-) diff --git a/lib/puppet/parser/ast/pops_bridge.rb b/lib/puppet/parser/ast/pops_bridge.rb index 753327246..a965ec9ce 100644 --- a/lib/puppet/parser/ast/pops_bridge.rb +++ b/lib/puppet/parser/ast/pops_bridge.rb @@ -45,18 +45,17 @@ class Puppet::Parser::AST::PopsBridge attr_reader :program_model, :context def initialize(program_model, context = {}) - @program_model = model + @program_model = program_model @context = context @ast_transformer ||= Puppet::Pops::Model::AstTransformer.new(@context[:file]) @@evaluator ||= Puppet::Pops::Parser::EvaluatingParser::Transitional.new() end - # This is the 3x API, the 3x AST searches through all code to find the instantiatable instructions. - # This pops model based instantiation relies on the parser to build this list while parsing (which is more + # This is the 3x API, the 3x AST searches through all code to find the instructions that can be instantiated. + # This Pops-model based instantiation relies on the parser to build this list while parsing (which is more # efficient as it avoids one full scan of all logic via recursive enumeration/yield) # def instantiate(modname) - decorate_program @program_model.definitions.collect do |d| case d when Puppet::Pops::Model::HostClassDefinition @@ -72,7 +71,7 @@ class Puppet::Parser::AST::PopsBridge end def evaluate(scope) - @@evaluator.evaluate(scope, model) + @@evaluator.evaluate(scope, program_model) end # Adapts to 3x where top level constructs needs to have each to iterate over children. Short circuit this @@ -90,7 +89,7 @@ class Puppet::Parser::AST::PopsBridge # can thus reference all sorts of information. Here the value expression is wrapped in an AST Bridge to a Pops # expression since the Pops side can not control the evaluation if o.value - [ o.name, Puppet::AST::PopsBridge::Expression.new(:value => o.value) ] + [ o.name, Expression.new(:value => o.value) ] else [ o.name ] end @@ -99,7 +98,7 @@ class Puppet::Parser::AST::PopsBridge # Produces a hash with data for Definition and HostClass def args_from_definition(o, modname) args = { - :arguments => o.parameters.collect {|p| instantiate_Parameter(o) }, + :arguments => o.parameters.collect {|p| instantiate_Parameter(p) }, :module_name => modname } unless is_nop?(o.body) diff --git a/lib/puppet/parser/e4_parser_adapter.rb b/lib/puppet/parser/e4_parser_adapter.rb index ecb02995b..4b282544a 100644 --- a/lib/puppet/parser/e4_parser_adapter.rb +++ b/lib/puppet/parser/e4_parser_adapter.rb @@ -56,6 +56,8 @@ class Puppet::Parser::E4ParserAdapter end + # TODO: This is unsused as the validating parser is used instead + # Remove this method ? def validate(parse_result) # TODO: This is too many hoops to jump through... ugly API # could reference a ValidatorFactory.validator_3_1(acceptor) instead. diff --git a/lib/puppet/pops/evaluator/access_operator.rb b/lib/puppet/pops/evaluator/access_operator.rb index 2bd3cbf43..c511ef9e4 100644 --- a/lib/puppet/pops/evaluator/access_operator.rb +++ b/lib/puppet/pops/evaluator/access_operator.rb @@ -206,7 +206,20 @@ class Puppet::Pops::Evaluator::AccessOperator fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_ARITY, o, :base_type => Puppet::Pops::Types::TypeCalculator.new().string(o), :min => 1, :actual => 0) end - unless o.class_name.nil? + if ! o.class_name.nil? + # lookup class resource and return one or more parameter values + resource = find_resource(scope, 'class', o.class_name) + unless resource + fail(Puppet::Pops::Issues::UNKNOWN_RESOURCE, @semantic, {:type_name => 'Class', :title => o.class_name}) + end + result = keys.map do |k| + unless is_parameter_of_resource?(scope, resource, k) + fail(Puppet::Pops::Issues::UNKNOWN_RESOURCE_PARAMETER, @semantic, + {:type_name => 'Class', :title => o.class_name, :param_name=>k}) + end + get_resource_parameter_value(scope, resource, k) + end + return result.size <= 1 ? result.pop : result # TODO: if [] is applied to specific class, it should be treated the same as getting # a resource parameter. Now it fails the operation # diff --git a/lib/puppet/pops/evaluator/runtime3_support.rb b/lib/puppet/pops/evaluator/runtime3_support.rb index 227c7efd2..cfaed712b 100644 --- a/lib/puppet/pops/evaluator/runtime3_support.rb +++ b/lib/puppet/pops/evaluator/runtime3_support.rb @@ -189,6 +189,9 @@ module Puppet::Pops::Evaluator::Runtime3Support def create_resources(o, scope, virtual, exported, type_name, resource_titles, evaluated_parameters) + # TODO: Unknown resource causes creation of Resource to fail with ArgumentError, should give + # a proper Issue + # resolve in scope. TODO: Investigate what happens here - opportunity to optimize? fully_qualified_type, resource_titles = scope.resolve_type_and_titles(type_name, resource_titles) diff --git a/lib/puppet/pops/issues.rb b/lib/puppet/pops/issues.rb index 824cffbca..baaf99cdc 100644 --- a/lib/puppet/pops/issues.rb +++ b/lib/puppet/pops/issues.rb @@ -369,6 +369,10 @@ module Puppet::Pops::Issues "Error while evaluating #{label.a_an(semantic)}, #{detail}" end + UNKNOWN_RESOURCE_TYPE = issue :UNKNOWN_RESOURCE_TYPE, :type_name do + "Resource type not found: #{type_name.capitalize}" + end + UNKNOWN_RESOURCE = issue :UNKNOWN_RESOURCE, :type_name, :title do "Resource not found: #{type_name.capitalize}['#{title}']" end diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index d647d1bd0..d1ecfa575 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -641,4 +641,22 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do end end + context "When evaluator performs access operatios" do + context "on catalog types" do + it "[n] gets resource parameter [n]" do + source = "notify { 'hello': message=>'yo'} Notify[hello][message]" + parser.evaluate_string(scope, source, __FILE__).should == 'yo' + end + + it "[n] gets class parameter [n]" do + source = "class wonka($produces='chocolate'){ } + include wonka + Class[wonka][produces]" + adapted_parser = Puppet::Parser::E4ParserAdapter.new + adapted_parser.file(__FILE__) + adapted_parser.parse(source).safeevaluate(scope).should == 'chocolate' +# parser.evaluate_string(scope, source, __FILE__).should == 'chocolate' + end + end + end end From b610a28f4267429e95d071da8eedc4c9ee5e67e2 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 19 Nov 2013 00:39:57 +0100 Subject: [PATCH 108/800] (#22363) Make test for [] on Class to get parameter really work The test was botched, it needed to do an import_ast to get class initialized, and then evaluate its code, not the class itself. A test in access_ops no longer works (the semantics of [] changed) and access_ops is not instrumented to do full code import. (The test is now in evaluating_parser_spec instead). The e4_parser_adapter was to eager to report a .rb file as being not supported (it should have checked if it was in "string mode" before failing). PopsBridge had a typo. --- lib/puppet/parser/ast/pops_bridge.rb | 2 +- lib/puppet/parser/e4_parser_adapter.rb | 174 +++++++++--------- spec/unit/pops/evaluator/access_ops_spec.rb | 5 - .../pops/evaluator/evaluating_parser_spec.rb | 10 +- 4 files changed, 96 insertions(+), 95 deletions(-) diff --git a/lib/puppet/parser/ast/pops_bridge.rb b/lib/puppet/parser/ast/pops_bridge.rb index a965ec9ce..266c790e8 100644 --- a/lib/puppet/parser/ast/pops_bridge.rb +++ b/lib/puppet/parser/ast/pops_bridge.rb @@ -20,7 +20,7 @@ class Puppet::Parser::AST::PopsBridge end def to_s - Puppet::Pops::ModelTreeDumper.new(dump(@value)) + Puppet::Pops::Model::ModelTreeDumper.new(dump(@value)) end def evaluate(scope) diff --git a/lib/puppet/parser/e4_parser_adapter.rb b/lib/puppet/parser/e4_parser_adapter.rb index 4b282544a..376f8239a 100644 --- a/lib/puppet/parser/e4_parser_adapter.rb +++ b/lib/puppet/parser/e4_parser_adapter.rb @@ -19,103 +19,105 @@ class Puppet::Parser::E4ParserAdapter end def parse(string = nil) - if @file =~ /\.rb$/ + self.string= string if string + + if @file =~ /\.rb$/ && @use != :string # Will throw an error parse_ruby_file end - self.string= string if string - parse_result = - if @use == :string - # Parse with a source_file to set in created AST objects (it was either given, or it may be unknown - # if caller did not set a file and the present a string. - # - @@evaluating_parser.parse_string(@string, @file || "unknown-source-location") - else - @@evaluating_parser.parse_file(@file) - end - - # the parse_result may be - # * empty / nil (no input) - # * a Model::Program - # * a Model::Expression + parse_result = + if @use == :string + # Parse with a source_file to set in created AST objects (it was either given, or it may be unknown + # if caller did not set a file and the present a string. # - model = parse_result.nil? ? nil : parse_result.current - args = {} - Puppet::Pops::Model::AstTransformer.new(@file).merge_location(args, model) + @@evaluating_parser.parse_string(@string, @file || "unknown-source-location") + else + @@evaluating_parser.parse_file(@file) + end + + # the parse_result may be + # * empty / nil (no input) + # * a Model::Program + # * a Model::Expression + # + model = parse_result.nil? ? nil : parse_result.current + args = {} + Puppet::Pops::Model::AstTransformer.new(@file).merge_location(args, model) + + ast_code = + if model.is_a? Puppet::Pops::Model::Program + Puppet::Parser::AST::PopsBridge::Program.new(model, args) + else + args[:value] = model + Puppet::Parser::AST::PopsBridge::Expression.new(args) + end - ast_code = - if model.is_a? Puppet::Pops::Model::Program - Puppet::Parser::AST::PopsBridge::Program.new(model, args) - else - args[:value] = model - Puppet::Parser::AST::PopsBridge::Expression.new(args) - end # Create the "main" class for the content - this content will get merged with all other "main" content Puppet::Parser::AST::Hostclass.new('', :code => ast_code) end - # TODO: This is unsused as the validating parser is used instead - # Remove this method ? - def validate(parse_result) - # TODO: This is too many hoops to jump through... ugly API - # could reference a ValidatorFactory.validator_3_1(acceptor) instead. - # and let the factory abstract the rest. - # - return unless parse_result - - acceptor = Puppet::Pops::Validation::Acceptor.new - validator = Puppet::Pops::Validation::ValidatorFactory_3_1.new().validator(acceptor) - validator.validate(parse_result) - - max_errors = Puppet[:max_errors] - max_warnings = Puppet[:max_warnings] + 1 - max_deprecations = Puppet[:max_deprecations] + 1 - - # If there are warnings output them - warnings = acceptor.warnings - if warnings.size > 0 - formatter = Puppet::Pops::Validation::DiagnosticFormatterPuppetStyle.new - emitted_w = 0 - emitted_dw = 0 - acceptor.warnings.each {|w| - if w.severity == :deprecation - # Do *not* call Puppet.deprecation_warning it is for internal deprecation, not - # deprecation of constructs in manifests! (It is not designed for that purpose even if - # used throughout the code base). - # - Puppet.warning(formatter.format(w)) if emitted_dw < max_deprecations - emitted_dw += 1 - else - Puppet.warning(formatter.format(w)) if emitted_w < max_warnings - emitted_w += 1 - end - break if emitted_w > max_warnings && emitted_dw > max_deprecations # but only then - } - end - - # If there were errors, report the first found. Use a puppet style formatter. - errors = acceptor.errors - if errors.size > 0 - formatter = Puppet::Pops::Validation::DiagnosticFormatterPuppetStyle.new - if errors.size == 1 || max_errors <= 1 - # raise immediately - raise Puppet::ParseError.new(formatter.format(errors[0])) - end - emitted = 0 - errors.each do |e| - Puppet.err(formatter.format(e)) - emitted += 1 - break if emitted >= max_errors - end - warnings_message = warnings.size > 0 ? ", and #{warnings.size} warnings" : "" - giving_up_message = "Found #{errors.size} errors#{warnings_message}. Giving up" - exception = Puppet::ParseError.new(giving_up_message) - exception.file = errors[0].file - raise exception - end - end +# # TODO: This is unsused as the validating parser is used instead +# # Remove this method ? +# def validate(parse_result) +# # TODO: This is too many hoops to jump through... ugly API +# # could reference a ValidatorFactory.validator_3_1(acceptor) instead. +# # and let the factory abstract the rest. +# # +# return unless parse_result +# +# acceptor = Puppet::Pops::Validation::Acceptor.new +# validator = Puppet::Pops::Validation::ValidatorFactory_3_1.new().validator(acceptor) +# validator.validate(parse_result) +# +# max_errors = Puppet[:max_errors] +# max_warnings = Puppet[:max_warnings] + 1 +# max_deprecations = Puppet[:max_deprecations] + 1 +# +# # If there are warnings output them +# warnings = acceptor.warnings +# if warnings.size > 0 +# formatter = Puppet::Pops::Validation::DiagnosticFormatterPuppetStyle.new +# emitted_w = 0 +# emitted_dw = 0 +# acceptor.warnings.each {|w| +# if w.severity == :deprecation +# # Do *not* call Puppet.deprecation_warning it is for internal deprecation, not +# # deprecation of constructs in manifests! (It is not designed for that purpose even if +# # used throughout the code base). +# # +# Puppet.warning(formatter.format(w)) if emitted_dw < max_deprecations +# emitted_dw += 1 +# else +# Puppet.warning(formatter.format(w)) if emitted_w < max_warnings +# emitted_w += 1 +# end +# break if emitted_w > max_warnings && emitted_dw > max_deprecations # but only then +# } +# end +# +# # If there were errors, report the first found. Use a puppet style formatter. +# errors = acceptor.errors +# if errors.size > 0 +# formatter = Puppet::Pops::Validation::DiagnosticFormatterPuppetStyle.new +# if errors.size == 1 || max_errors <= 1 +# # raise immediately +# raise Puppet::ParseError.new(formatter.format(errors[0])) +# end +# emitted = 0 +# errors.each do |e| +# Puppet.err(formatter.format(e)) +# emitted += 1 +# break if emitted >= max_errors +# end +# warnings_message = warnings.size > 0 ? ", and #{warnings.size} warnings" : "" +# giving_up_message = "Found #{errors.size} errors#{warnings_message}. Giving up" +# exception = Puppet::ParseError.new(giving_up_message) +# exception.file = errors[0].file +# raise exception +# end +# end def string=(string) @string = string diff --git a/spec/unit/pops/evaluator/access_ops_spec.rb b/spec/unit/pops/evaluator/access_ops_spec.rb index 107c0cecd..59b5b4db4 100644 --- a/spec/unit/pops/evaluator/access_ops_spec.rb +++ b/spec/unit/pops/evaluator/access_ops_spec.rb @@ -182,11 +182,6 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do expect(result[1]).to be_the_type(types.host_class('nginx')) end - it 'gives an error if class is already specialized' do - expr = fqr('Class')[fqn('apache')][fqn('nginx')] - expect {evaluate(expr)}.to raise_error(/Cannot specialize an already specialized Class type/) - end - # Resource it 'produces a specific resource type from Resource[type]' do expr = fqr('Resource')[fqr('File')] diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index d1ecfa575..3b5f3c02b 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -5,6 +5,7 @@ require 'puppet/pops' require 'puppet/pops/evaluator/evaluator_impl' require 'puppet_spec/pops' require 'puppet_spec/scope' +require 'puppet/parser/e4_parser_adapter' # relative to this spec file (./) does not work as this file is loaded by rspec @@ -652,10 +653,13 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do source = "class wonka($produces='chocolate'){ } include wonka Class[wonka][produces]" + + # This is more complicated since it needs to run like 3.x and do an import_ast adapted_parser = Puppet::Parser::E4ParserAdapter.new - adapted_parser.file(__FILE__) - adapted_parser.parse(source).safeevaluate(scope).should == 'chocolate' -# parser.evaluate_string(scope, source, __FILE__).should == 'chocolate' + adapted_parser.file = __FILE__ + ast = adapted_parser.parse(source) + scope.known_resource_types.import_ast(ast, '') + ast.code.safeevaluate(scope).should == 'chocolate' end end end From 8298f4be77fe96ad5d5ca2b0bd2572a9dc4192f6 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 19 Nov 2013 01:48:39 +0100 Subject: [PATCH 109/800] (#22363) Handle failing tests due to renamed collect/map Collect/map was renamed, but there were tests that still used collect. One test was broken in map_spec.rb (due to fix of undef/nil problem that was missed in rename/merge/rebase). --- lib/puppet/parser/functions/map.rb | 2 +- spec/unit/parser/methods/map_spec.rb | 3 +-- spec/unit/pops/evaluator/evaluating_parser_spec.rb | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/puppet/parser/functions/map.rb b/lib/puppet/parser/functions/map.rb index 3c871bc85..94ef4a0b7 100644 --- a/lib/puppet/parser/functions/map.rb +++ b/lib/puppet/parser/functions/map.rb @@ -31,7 +31,7 @@ Puppet::Parser::Functions::newfunction( receiver = args[0] pblock = args[1] - raise ArgumentError, ("map(): wrong argument type (#{pblock.class}; must be a parameterized block.") unless pblock.is_a? Puppet::Parser::AST::Lambda + raise ArgumentError, ("map(): wrong argument type (#{pblock.class}; must be a parameterized block.") unless pblock.respond_to?(:puppet_lambda) case receiver when Array diff --git a/spec/unit/parser/methods/map_spec.rb b/spec/unit/parser/methods/map_spec.rb index 025501754..200037ba7 100644 --- a/spec/unit/parser/methods/map_spec.rb +++ b/spec/unit/parser/methods/map_spec.rb @@ -85,8 +85,7 @@ describe 'the map method' do file { "/file_$i.$v": ensure => present } } MANIFEST - - catalog.resource(:file, "/file_0.")['ensure'].should == 'present' + catalog.resource(:file, "/file_0.something")['ensure'].should == 'present' end end it_should_behave_like 'all iterative functions argument checks', 'map' diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index 3b5f3c02b..ecdd82951 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -374,8 +374,8 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do "$a = 5; $a" => 5, "$a = 5; $b = 6; $a" => 5, "$a = $b = 5; $a == $b" => true, - "$a = [1,2,3]; [x].collect |$x| { $a += x; $a }" => [[1,2,3,'x']], - "$a = [a,x,c]; [x].collect |$x| { $a -= x; $a }" => [['a','c']], + "$a = [1,2,3]; [x].map |$x| { $a += x; $a }" => [[1,2,3,'x']], + "$a = [a,x,c]; [x].map |$x| { $a -= x; $a }" => [['a','c']], }.each do |source, result| it "should parse and evaluate the expression '#{source}' to #{result}" do parser.evaluate_string(scope, source, __FILE__).should == result From 37a301172e2d4533b7ada4aeea11748ff1325b94 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 19 Nov 2013 01:59:09 +0100 Subject: [PATCH 110/800] (#22363) Rename box_numeric to coerce_numeric There is no "boxing" going on in Ruby (since there are no native data types). The term 'coerce' is much better for this. --- lib/puppet/pops/evaluator/access_operator.rb | 10 +++++----- lib/puppet/pops/evaluator/evaluator_impl.rb | 6 +++--- lib/puppet/pops/evaluator/runtime3_support.rb | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/puppet/pops/evaluator/access_operator.rb b/lib/puppet/pops/evaluator/access_operator.rb index c511ef9e4..b3a490163 100644 --- a/lib/puppet/pops/evaluator/access_operator.rb +++ b/lib/puppet/pops/evaluator/access_operator.rb @@ -35,9 +35,9 @@ class Puppet::Pops::Evaluator::AccessOperator when 0 fail(Puppet::Pops::Issues::BAD_STRING_SLICE_ARITY, @semantic.left_expr, {:actual => keys.size}) when 1 - o[box_numeric(keys[0], @semantic.keys, scope)] + o[coerce_numeric(keys[0], @semantic.keys, scope)] when 2 - o[box_numeric(keys[0], @semantic.keys, scope), box_numeric(keys[1], @semantic.keys, scope)] + o[coerce_numeric(keys[0], @semantic.keys, scope), coerce_numeric(keys[1], @semantic.keys, scope)] else fail(Puppet::Pops::Issues::BAD_STRING_SLICE_ARITY, @semantic.left_expr, {:actual => keys.size}) end @@ -62,12 +62,12 @@ class Puppet::Pops::Evaluator::AccessOperator # What does this mean: [] ? Is it error, unit, empty array fail(Puppet::Pops::Issues::BAD_ARRAY_SLICE_ARITY, @semantic.left_expr, {:actual => keys.size}) when 1 - k = box_numeric(keys[0], @semantic.keys[0], scope) + k = coerce_numeric(keys[0], @semantic.keys[0], scope) o[k] when 2 # A slice [from, to] with support for -1 to mean start, or end respectively. - k1 = box_numeric(keys[0], @semantic.keys[0], scope) - k2 = box_numeric(keys[1], @semantic.keys[1], scope) + k1 = coerce_numeric(keys[0], @semantic.keys[0], scope) + k2 = coerce_numeric(keys[1], @semantic.keys[1], scope) o[k1, k2] else fail(Puppet::Pops::Issues::BAD_ARRAY_SLICE_ARITY, @semantic.left_expr, {:actual => keys.size}) diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index fa251eb65..15760dae8 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -256,7 +256,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl end def eval_UnaryMinusExpression(o, scope) - - box_numeric(evaluate(o.expr, scope), o, scope) + - coerce_numeric(evaluate(o.expr, scope), o, scope) end # Abstract evaluation, returns array [left, right] with the evaluated result of left_expr and @@ -355,8 +355,8 @@ class Puppet::Pops::Evaluator::EvaluatorImpl end else # Handle operation on numeric - left = box_numeric(left, left_o, scope) - right = box_numeric(right, right_o, scope) + left = coerce_numeric(left, left_o, scope) + right = coerce_numeric(right, right_o, scope) begin if operator == :'%' && (left.is_a?(Float) || right.is_a?(Float)) # Deny users the fun of seeing severe rounding errors and confusing results diff --git a/lib/puppet/pops/evaluator/runtime3_support.rb b/lib/puppet/pops/evaluator/runtime3_support.rb index cfaed712b..8d5f78697 100644 --- a/lib/puppet/pops/evaluator/runtime3_support.rb +++ b/lib/puppet/pops/evaluator/runtime3_support.rb @@ -151,7 +151,7 @@ module Puppet::Pops::Evaluator::Runtime3Support # @param scope [Object] the (runtime specific) scope where evaluation of o takes place # @return [Numeric] value `v` converted to Numeric. # - def box_numeric(v, o, scope) + def coerce_numeric(v, o, scope) unless n = Puppet::Pops::Utils.to_n(v) fail(Puppet::Pops::Issues::NOT_NUMERIC, o, {:value => v}) end From 8319670debae8f8cc9c4000db99e3971dfde7a0a Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 19 Nov 2013 02:11:50 +0100 Subject: [PATCH 111/800] (#22363) Port use of FileSystem to Lexer2 The regular lexer was modified to use the FileSystem when checking for file existence. This is no ported to Lexer2 --- lib/puppet/pops/parser/lexer2.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/pops/parser/lexer2.rb b/lib/puppet/pops/parser/lexer2.rb index 50e351abe..9633c1c54 100644 --- a/lib/puppet/pops/parser/lexer2.rb +++ b/lib/puppet/pops/parser/lexer2.rb @@ -225,7 +225,7 @@ class Puppet::Pops::Parser::Lexer2 # def lex_file(file) initvars - contents = File.exists?(file) ? File.read(file) : "" + contents = Puppet::FileSystem::File.exist?(file) ? File.read(file) : "" @scanner = StringScanner.new(contents.freeze) @locator = Puppet::Pops::Parser::Locator.locator(contents, file) end From d057f61640be32dd82042f0b053642d5a41330e7 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 19 Nov 2013 04:32:01 +0100 Subject: [PATCH 112/800] (#22363) Add strict variable lookup mode This adds the setting :strict_variables. When used the scope will throw :unkown_variable (which if not handled, will raise an exception). The future evaluator can handle this, but none of the other parsers/evaluator options. Testing of the future evaluator turns on strict variables mode (and has tests that an error is raised). This commit also contains maintenance (removal of unused code), fixing of the pops bridge (special case when evaluating without a proper boot) --- lib/puppet/defaults.rb | 9 ++- lib/puppet/parser/ast/pops_bridge.rb | 10 +++ lib/puppet/parser/e4_parser_adapter.rb | 61 ------------------- lib/puppet/parser/scope.rb | 11 +++- lib/puppet/pops/evaluator/runtime3_support.rb | 7 ++- lib/puppet/pops/issues.rb | 4 ++ lib/puppet/pops/validation.rb | 1 + spec/integration/parser/parser_spec.rb | 2 +- .../pops/evaluator/evaluating_parser_spec.rb | 13 ++++ 9 files changed, 51 insertions(+), 67 deletions(-) diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index 9ad4b0afa..454e55349 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -1757,8 +1757,15 @@ EOT warnings in case multiple errors have been detected. A value of 0 is the same as value 1. The count is per manifest. EOT + }, + :strict_variables => { + :default => false, + :type => :boolean, + :desc => <<-'EOT' + Makes the parser raise errors when referencing unknown variables. (This does not affect + referencing variables that are explicitly set to undef). + EOT } - ) define_settings(:puppetdoc, :document_all => { diff --git a/lib/puppet/parser/ast/pops_bridge.rb b/lib/puppet/parser/ast/pops_bridge.rb index 266c790e8..021668489 100644 --- a/lib/puppet/parser/ast/pops_bridge.rb +++ b/lib/puppet/parser/ast/pops_bridge.rb @@ -33,6 +33,16 @@ class Puppet::Parser::AST::PopsBridge def each yield self end + def sequence_with(other) + if value.nil? + # This happens when testing and not having a complete setup + other + else + # When does this happen ? Ever ? + require 'debugger'; debugger + Puppet::Parser::AST::BlockExpression.new(:children => [self] + other.children) + end + end end # Bridges the top level "Program" produced by the pops parser. diff --git a/lib/puppet/parser/e4_parser_adapter.rb b/lib/puppet/parser/e4_parser_adapter.rb index 376f8239a..8afc9dc8a 100644 --- a/lib/puppet/parser/e4_parser_adapter.rb +++ b/lib/puppet/parser/e4_parser_adapter.rb @@ -58,67 +58,6 @@ class Puppet::Parser::E4ParserAdapter end -# # TODO: This is unsused as the validating parser is used instead -# # Remove this method ? -# def validate(parse_result) -# # TODO: This is too many hoops to jump through... ugly API -# # could reference a ValidatorFactory.validator_3_1(acceptor) instead. -# # and let the factory abstract the rest. -# # -# return unless parse_result -# -# acceptor = Puppet::Pops::Validation::Acceptor.new -# validator = Puppet::Pops::Validation::ValidatorFactory_3_1.new().validator(acceptor) -# validator.validate(parse_result) -# -# max_errors = Puppet[:max_errors] -# max_warnings = Puppet[:max_warnings] + 1 -# max_deprecations = Puppet[:max_deprecations] + 1 -# -# # If there are warnings output them -# warnings = acceptor.warnings -# if warnings.size > 0 -# formatter = Puppet::Pops::Validation::DiagnosticFormatterPuppetStyle.new -# emitted_w = 0 -# emitted_dw = 0 -# acceptor.warnings.each {|w| -# if w.severity == :deprecation -# # Do *not* call Puppet.deprecation_warning it is for internal deprecation, not -# # deprecation of constructs in manifests! (It is not designed for that purpose even if -# # used throughout the code base). -# # -# Puppet.warning(formatter.format(w)) if emitted_dw < max_deprecations -# emitted_dw += 1 -# else -# Puppet.warning(formatter.format(w)) if emitted_w < max_warnings -# emitted_w += 1 -# end -# break if emitted_w > max_warnings && emitted_dw > max_deprecations # but only then -# } -# end -# -# # If there were errors, report the first found. Use a puppet style formatter. -# errors = acceptor.errors -# if errors.size > 0 -# formatter = Puppet::Pops::Validation::DiagnosticFormatterPuppetStyle.new -# if errors.size == 1 || max_errors <= 1 -# # raise immediately -# raise Puppet::ParseError.new(formatter.format(errors[0])) -# end -# emitted = 0 -# errors.each do |e| -# Puppet.err(formatter.format(e)) -# emitted += 1 -# break if emitted >= max_errors -# end -# warnings_message = warnings.size > 0 ? ", and #{warnings.size} warnings" : "" -# giving_up_message = "Found #{errors.size} errors#{warnings_message}. Giving up" -# exception = Puppet::ParseError.new(giving_up_message) -# exception.file = errors[0].file -# raise exception -# end -# end - def string=(string) @string = string @use = :string diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb index b14abd411..3eb92e967 100644 --- a/lib/puppet/parser/scope.rb +++ b/lib/puppet/parser/scope.rb @@ -364,11 +364,18 @@ class Puppet::Parser::Scope if next_scope next_scope.lookupvar(name, options) else - nil + variable_not_found() end end end + def variable_not_found + if Puppet[:strict_variables] + throw :undefined_variable + else + nil + end + end # Retrieves the variable value assigned to the name given as an argument. The name must be a String, # and namespace can be qualified with '::'. The value is looked up in this scope, its parent scopes, # or in a specific visible named scope. @@ -442,7 +449,7 @@ class Puppet::Parser::Scope "" end warning "Could not look up qualified variable '#{class_name}::#{variable_name}'; #{e.message}#{location}" - nil + variable_not_found() end end diff --git a/lib/puppet/pops/evaluator/runtime3_support.rb b/lib/puppet/pops/evaluator/runtime3_support.rb index 8d5f78697..919edd293 100644 --- a/lib/puppet/pops/evaluator/runtime3_support.rb +++ b/lib/puppet/pops/evaluator/runtime3_support.rb @@ -25,7 +25,10 @@ module Puppet::Pops::Evaluator::Runtime3Support # Puppet 3x stores all variables as strings (then converts them back to numeric with a regexp... to see if it is a match variable) # Not ideal, scope should support numeric lookup directly instead. # TODO: consider fixing scope - scope.lookupvar(name.to_s) + catch(:undefined_variable) { + return scope.lookupvar(name.to_s) + } + fail(Puppet::Pops::Issues::UNKNOWN_VARIABLE, o, {:name => name}) end # Returns true if the variable of the given name is set in the given most nested scope. True is returned even if @@ -35,7 +38,7 @@ module Puppet::Pops::Evaluator::Runtime3Support scope.bound?(name.to_s) end - # Returns true of the variable is bound to a value or nil, in the scope or it's parent scopes. + # Returns true if the variable is bound to a value or nil, in the scope or it's parent scopes. # def variable_exists?(name, scope) scope.exist?(name.to_s) diff --git a/lib/puppet/pops/issues.rb b/lib/puppet/pops/issues.rb index baaf99cdc..bbb50cb14 100644 --- a/lib/puppet/pops/issues.rb +++ b/lib/puppet/pops/issues.rb @@ -365,6 +365,10 @@ module Puppet::Pops::Issues "Unknown function: '#{name}'." end + UNKNOWN_VARIABLE = issue :UNKNOWN_VARIABLE, :name do + "Unknown variable: '#{name}'." + end + RUNTIME_ERROR = issue :RUNTIME_ERROR, :detail do "Error while evaluating #{label.a_an(semantic)}, #{detail}" end diff --git a/lib/puppet/pops/validation.rb b/lib/puppet/pops/validation.rb index 1fce3a228..b58916c1c 100644 --- a/lib/puppet/pops/validation.rb +++ b/lib/puppet/pops/validation.rb @@ -287,6 +287,7 @@ module Puppet::Pops::Validation # have to be used here for backwards compatibility. def format_location diagnostic file = diagnostic.file + file = file.is_a?(String) && file.empty? ? nil : file line = pos = nil if diagnostic.source_pos line = diagnostic.source_pos.line diff --git a/spec/integration/parser/parser_spec.rb b/spec/integration/parser/parser_spec.rb index 280fd040f..b3920c9a4 100755 --- a/spec/integration/parser/parser_spec.rb +++ b/spec/integration/parser/parser_spec.rb @@ -261,7 +261,7 @@ describe "Puppet::Parser::Parser" do $name = 'fred' node "macbook-owned-by$name" { } SOURCE - expect { @parser.parse(source) }.to raise_error(/An interpolated expression is not allowed in a hostname of a node at line 2:24/) + expect { @parser.parse(source) }.to raise_error(/An interpolated expression is not allowed in a hostname of a node at line 2:23/) end end end diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index ecdd82951..59523928e 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -14,6 +14,9 @@ require 'puppet/parser/e4_parser_adapter' describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do include PuppetSpec::Pops include PuppetSpec::Scope + before(:each) do + Puppet[:strict_variables] = true + end let(:parser) { Puppet::Pops::Parser::EvaluatingParser::Transitional.new } let(:node) { 'node.example.com' } @@ -642,6 +645,16 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do end end + context "When evaluating non existing variables error should be raised for" do + it "unqualified variable" do + expect { parser.evaluate_string(scope, "$quantum_gravity", __FILE__) }.to raise_error(/Unknown variable/) + end + + it "qualified variable" do + expect { parser.evaluate_string(scope, "$quantum_gravity::graviton", __FILE__) }.to raise_error(/Unknown variable/) + end + end + context "When evaluator performs access operatios" do context "on catalog types" do it "[n] gets resource parameter [n]" do From ee46b7b3289d51fc29699ad8b09bd4beb9cfd6e4 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 19 Nov 2013 04:41:11 +0100 Subject: [PATCH 113/800] (#22363) Add test that literal type can be interpolated Test that "interpolate ${Array[Integer]}" works (results in the type in string form). --- spec/unit/pops/evaluator/evaluating_parser_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index 59523928e..b61abfb68 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -629,6 +629,7 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do '$x = undef "value is $x yo"' => "value is yo", '$x = default "value is $x yo"' => "value is default yo", '$x = Array[Integer] "value is $x yo"' => "value is Array[Integer] yo", + '"value is ${Array[Integer]} yo"' => "value is Array[Integer] yo", }.each do |source, result| it "should parse and evaluate the expression '#{source}' to #{result}" do populate From 7c7bc582958520d26055eca3d430f20ed77bc8b8 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 19 Nov 2013 16:55:41 +0100 Subject: [PATCH 114/800] (#22363) Add tests for [] without keys and unsupported LHS => error --- .../pops/evaluator/evaluating_parser_spec.rb | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index b61abfb68..04300af4d 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -525,6 +525,27 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do end end + # LHS where [] not supported, and missing key(s) + { + "Array[]" => :error, + "'abc'[]" => :error, + "Resource[]" => :error, + "File[]" => :error, + "String[]" => :error, + "String[0]" => :error, + "1[]" => :error, + "1[0]" => :error, + "3.14[]" => :error, + "3.14[0]" => :error, + "/.*/[]" => :error, + "/.*/[0]" => :error, + "$a=[1] $a[]" => :error, + }.each do |source, result| + it "should parse and evaluate the expression '#{source}' to #{result}" do + expect { parser.evaluate_string(scope, source, __FILE__)}.to raise_error(Puppet::ParseError) + end + end + # Resource default and override expressions and resource parameter access { "notify { id: message=>explicit} Notify[id][message]" => "explicit", From 0a4e52ed110140279639e5935b5ee88a02acdef5 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 19 Nov 2013 17:00:39 +0100 Subject: [PATCH 115/800] (maint) Refactor evaluating parser tests Duplicated context, tests outside of context moved. --- .../pops/evaluator/evaluating_parser_spec.rb | 76 +++++++++---------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index 04300af4d..6aa53f8b7 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -546,25 +546,46 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do end end - # Resource default and override expressions and resource parameter access - { - "notify { id: message=>explicit} Notify[id][message]" => "explicit", - "Notify { message=>by_default} notify {foo:} Notify[foo][message]" => "by_default", - "notify {foo:} Notify[foo]{message =>by_override} Notify[foo][message]" => "by_override", - }.each do |source, result| - it "should parse and evaluate the expression '#{source}' to #{result}" do - parser.evaluate_string(scope, source, __FILE__).should == result - end - end - # Resource default and override expressions and resource parameter access - { - "notify { xid: message=>explicit} Notify[id][message]" => /Resource not found/, - "notify { id: message=>explicit} Notify[id][mustard]" => /does not have a parameter called 'mustard'/, - }.each do |source, result| - it "should parse '#{source}' and raise error matching #{result}" do - expect { parser.evaluate_string(scope, source, __FILE__)}.to raise_error(result) + context "on catalog types" do + it "[n] gets resource parameter [n]" do + source = "notify { 'hello': message=>'yo'} Notify[hello][message]" + parser.evaluate_string(scope, source, __FILE__).should == 'yo' + end + + it "[n] gets class parameter [n]" do + source = "class wonka($produces='chocolate'){ } + include wonka + Class[wonka][produces]" + + # This is more complicated since it needs to run like 3.x and do an import_ast + adapted_parser = Puppet::Parser::E4ParserAdapter.new + adapted_parser.file = __FILE__ + ast = adapted_parser.parse(source) + scope.known_resource_types.import_ast(ast, '') + ast.code.safeevaluate(scope).should == 'chocolate' + end + # Resource default and override expressions and resource parameter access with [] + { + "notify { id: message=>explicit} Notify[id][message]" => "explicit", + "Notify { message=>by_default} notify {foo:} Notify[foo][message]" => "by_default", + "notify {foo:} Notify[foo]{message =>by_override} Notify[foo][message]" => "by_override", + }.each do |source, result| + it "should parse and evaluate the expression '#{source}' to #{result}" do + parser.evaluate_string(scope, source, __FILE__).should == result + end + end + + # Resource default and override expressions and resource parameter access error conditions + { + "notify { xid: message=>explicit} Notify[id][message]" => /Resource not found/, + "notify { id: message=>explicit} Notify[id][mustard]" => /does not have a parameter called 'mustard'/, + }.each do |source, result| + it "should parse '#{source}' and raise error matching #{result}" do + expect { parser.evaluate_string(scope, source, __FILE__)}.to raise_error(result) + end end end + # end [] operations end context "When the evaluator performs boolean operations" do @@ -677,25 +698,4 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do end end - context "When evaluator performs access operatios" do - context "on catalog types" do - it "[n] gets resource parameter [n]" do - source = "notify { 'hello': message=>'yo'} Notify[hello][message]" - parser.evaluate_string(scope, source, __FILE__).should == 'yo' - end - - it "[n] gets class parameter [n]" do - source = "class wonka($produces='chocolate'){ } - include wonka - Class[wonka][produces]" - - # This is more complicated since it needs to run like 3.x and do an import_ast - adapted_parser = Puppet::Parser::E4ParserAdapter.new - adapted_parser.file = __FILE__ - ast = adapted_parser.parse(source) - scope.known_resource_types.import_ast(ast, '') - ast.code.safeevaluate(scope).should == 'chocolate' - end - end - end end From 63ea50e2173a22863cd0334746834be8ee6d54c0 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 19 Nov 2013 23:29:29 +0100 Subject: [PATCH 116/800] (#22363) Add relationship test This also fixes problems with relationship evaluation. --- .../pops/evaluator/relationship_operator.rb | 6 +-- lib/puppet/pops/evaluator/runtime3_support.rb | 2 +- .../pops/evaluator/evaluating_parser_spec.rb | 51 +++++++++++++++++-- 3 files changed, 50 insertions(+), 9 deletions(-) diff --git a/lib/puppet/pops/evaluator/relationship_operator.rb b/lib/puppet/pops/evaluator/relationship_operator.rb index d65d82fcc..83a12a5ad 100644 --- a/lib/puppet/pops/evaluator/relationship_operator.rb +++ b/lib/puppet/pops/evaluator/relationship_operator.rb @@ -38,7 +38,7 @@ class Puppet::Pops::Evaluator::RelationshipOperator end def transform(o, scope) - @type_transformer_vistor.visit(o, scope) + @type_transformer_visitor.visit_this_1(self, o, scope) end # Catch all non transformable objects @@ -112,13 +112,13 @@ class Puppet::Pops::Evaluator::RelationshipOperator # real is [left, right], and both the left and right may be a single value or an array. In each case all content # should be flattened, and then transformed to a type. # - real = real.collect {|x| [x].flatten.collect {|x| transform(x, scope) }} + real = left_right_evaluated.collect {|x| [x].flatten.collect {|x| transform(x, scope) }} # reverse order if operator is Right to Left source, target = reverse_operator?(relationship_expression) ? real.reverse : real # Add the relationships to the catalog - source.each {|s| target.each {|t| add_relationship(s, t, RELATION_TYPE[relationship_expression.operator]) }} + source.each {|s| target.each {|t| add_relationship(s, t, RELATION_TYPE[relationship_expression.operator], scope) }} # Produce the transformed source RHS (if this is a chain, this does not need to be done again) real.slice(1) diff --git a/lib/puppet/pops/evaluator/runtime3_support.rb b/lib/puppet/pops/evaluator/runtime3_support.rb index 919edd293..3ca499ef2 100644 --- a/lib/puppet/pops/evaluator/runtime3_support.rb +++ b/lib/puppet/pops/evaluator/runtime3_support.rb @@ -143,7 +143,7 @@ module Puppet::Pops::Evaluator::Runtime3Support source_resource = Puppet::Resource.new(type, title) type, title = catalog_type_to_split_type_title(target) target_resource = Puppet::Resource.new(type, title) - scope.compiler.add_relationship(Puppet::Parser::Relationship.new(source_resource, target_resource, type)) + scope.compiler.add_relationship(Puppet::Parser::Relationship.new(source_resource, target_resource, relationship_type)) end # Box value `v` to numeric or fails. diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index 6aa53f8b7..8929002d9 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -688,13 +688,54 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do end end - context "When evaluating non existing variables error should be raised for" do - it "unqualified variable" do - expect { parser.evaluate_string(scope, "$quantum_gravity", __FILE__) }.to raise_error(/Unknown variable/) + context "When evaluating variables" do + context "that are non existing an error is raised for" do + it "unqualified variable" do + expect { parser.evaluate_string(scope, "$quantum_gravity", __FILE__) }.to raise_error(/Unknown variable/) + end + + it "qualified variable" do + expect { parser.evaluate_string(scope, "$quantum_gravity::graviton", __FILE__) }.to raise_error(/Unknown variable/) + end + end + end + + context "When evaluating relationships" do + it 'should form a relation with ->' do + source = "File[a] -> File[b]" + parser.evaluate_string(scope, source, __FILE__) + scope.compiler.should have_relationship(['File', 'a', '->', 'File', 'b']) end - it "qualified variable" do - expect { parser.evaluate_string(scope, "$quantum_gravity::graviton", __FILE__) }.to raise_error(/Unknown variable/) + it 'should form a relation with <-' do + source = "File[a] <- File[b]" + parser.evaluate_string(scope, source, __FILE__) + scope.compiler.should have_relationship(['File', 'b', '->', 'File', 'a']) + end + + it 'should form a relation with <-' do + source = "File[a] <~ File[b]" + parser.evaluate_string(scope, source, __FILE__) + scope.compiler.should have_relationship(['File', 'b', '~>', 'File', 'a']) + end + end + + matcher :have_relationship do |expected| + calc = Puppet::Pops::Types::TypeCalculator.new + + match do |compiler| + op_name = {'->' => :relationship, '~>' => :subscription} + compiler.relationships.any? do | relation | + relation.source.type == expected[0] && + relation.source.title == expected[1] && + relation.type == op_name[expected[2]] && + relation.target.type == expected[3] && + relation.target.title == expected[4] + end + end + + failure_message_for_should do |actual| + "Relationship #{expected[0]}[#{expected[1]}] #{expected[2]} #{expected[3]}[#{expected[4]}] but was unknown to compiler" end end From f99fc7d9af199fc9e58d63f2300de15a6ac418fb Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 21 Nov 2013 00:24:04 +0100 Subject: [PATCH 117/800] (#22363) Fix problem with integer string to Integer on Ruby 1.8.7 The problem was caused by the logic using the Kernel method Integer(string, radix) which was introduced in Ruby 1.9.2. We do not need to pass radix, since the string converted starts with 0, 0x, or 0X in all cases. --- lib/puppet/pops/utils.rb | 7 +++++-- spec/unit/pops/parser/lexer2_spec.rb | 10 ++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/puppet/pops/utils.rb b/lib/puppet/pops/utils.rb index b14b5554f..fec084d38 100644 --- a/lib/puppet/pops/utils.rb +++ b/lib/puppet/pops/utils.rb @@ -35,10 +35,13 @@ module Puppet::Pops::Utils radix = 10 if match[1].to_s.length > 0 radix = 16 - elsif match[2].to_s.length > 1 && match[2][0] == '0' + elsif match[2].to_s.length > 1 && match[2][0,1] == '0' radix = 8 end - [Integer(match[0], radix), radix] + # Ruby 1.8.7 does not have a second argument to Kernel method that creates an + # integer from a string, it relies on the prefix 0x, 0X, 0 (and unsupported in puppet binary 'b') + # We have the correct string here, match[0] is safe to parse without passing on radix + [Integer(match[0]), radix] end when Numeric, Fixnum, Integer, Float # Impossible to calculate radix, assume decimal diff --git a/spec/unit/pops/parser/lexer2_spec.rb b/spec/unit/pops/parser/lexer2_spec.rb index 2fb94826a..b9bdd6eb4 100644 --- a/spec/unit/pops/parser/lexer2_spec.rb +++ b/spec/unit/pops/parser/lexer2_spec.rb @@ -119,6 +119,12 @@ describe 'Lexer2' do end end + { ' 1' => '1', '1 ' => '1', ' 1 ' => '1'}.each do |string, value| + it "should lex a NUMBER with surrounding space '#{string}'" do + tokens_scanned_from(string).should match_tokens2([:NUMBER, value]) + end + end + [ '0.0', '0.1', '0.2982383139', '29823.235', '10e23', '10e-23', '1.234e23'].each do |string| it "should lex a decimal floating point NUMBER on the form '#{string}'" do tokens_scanned_from(string).should match_tokens2([:NUMBER, string]) @@ -219,6 +225,10 @@ describe 'Lexer2' do end end + it "should lex a simple expression" do + tokens_scanned_from('1 + 1').should match_tokens2([:NUMBER, '1'], :PLUS, [:NUMBER, '1']) + end + { "1" => ["1 /./", [:NUMBER, :DIV, :DOT, :DIV]], "'a'" => ["'a' /./", [:STRING, :DIV, :DOT, :DIV]], "true" => ["true /./", [:BOOLEAN, :DIV, :DOT, :DIV]], From 560411559390566658ceaaf0608db2b385d12858 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 21 Nov 2013 03:08:01 +0100 Subject: [PATCH 118/800] (#22363) Make string and array access operation work as specified. The implementation relied on Ruby to do the right thing for both String and Array slice operations. Naturally there were differences between Ruby 1.8.7 and Ruby 1.9.2. This lead to a revisit of the specification and a more solid implementation. For string, a string is always produced (never undef). For array, undef is produced if a single element is requested (without count) and the index is outside of range. For a subarray of any length, including 0, an empty array is always produced if the given range is outside of the array's range. For both string and array, a negative start index enumerates from the end, and a negative count computes the wanted range using first an end enumeration, and then a computed count (start_index to end_index), if this is an inverse range (extending to the "left", or if the range is outside, an empty array / empty string is produced. In both cases, if there is partial overlap to the left or the right, the overlapping range is produced. --- lib/puppet/pops/evaluator/access_operator.rb | 41 ++++++++++++++++--- spec/unit/pops/evaluator/access_ops_spec.rb | 4 +- .../pops/evaluator/evaluating_parser_spec.rb | 32 +++++++++++---- 3 files changed, 63 insertions(+), 14 deletions(-) diff --git a/lib/puppet/pops/evaluator/access_operator.rb b/lib/puppet/pops/evaluator/access_operator.rb index b3a490163..827a85155 100644 --- a/lib/puppet/pops/evaluator/access_operator.rb +++ b/lib/puppet/pops/evaluator/access_operator.rb @@ -31,16 +31,36 @@ class Puppet::Pops::Evaluator::AccessOperator end def access_String(o, scope, keys) - case keys.size + result = case keys.size when 0 fail(Puppet::Pops::Issues::BAD_STRING_SLICE_ARITY, @semantic.left_expr, {:actual => keys.size}) when 1 - o[coerce_numeric(keys[0], @semantic.keys, scope)] + # Note that Ruby 1.8.7 requires a length of 1 to produce a String + k1 = coerce_numeric(keys[0], @semantic.keys, scope) + k2 = 1 + k1 = k1 < 0 ? o.length + k1 : k1 # abs pos + # if k1 is outside, a length of 1 always produces an empty string + if k1 < 0 + '' + else + o[ k1, k2 ] + end when 2 - o[coerce_numeric(keys[0], @semantic.keys, scope), coerce_numeric(keys[1], @semantic.keys, scope)] + k1 = coerce_numeric(keys[0], @semantic.keys, scope) + k2 = coerce_numeric(keys[1], @semantic.keys, scope) + k1 = k1 < 0 ? o.length + k1 : k1 # abs pos (negative is count from end) + k2 = k2 < 0 ? o.length - k1 + k2 + 1 : k2 # abs length (negative k2 is length from pos to end count) + # if k1 is outside, adjust to first position, and adjust length + if k1 < 0 + k2 = k2 + k1 + k1 = 0 + end + o[ k1, k2 ] else fail(Puppet::Pops::Issues::BAD_STRING_SLICE_ARITY, @semantic.left_expr, {:actual => keys.size}) end + # Specified as: an index outside of range, or empty result == empty string + (result.nil? || result.empty?) ? '' : result end # Speciaizes the Pattern p into itself p[], one regexp instance p[], or array of regexp instances @@ -59,7 +79,6 @@ class Puppet::Pops::Evaluator::AccessOperator def access_Array(o, scope, keys) case keys.size when 0 - # What does this mean: [] ? Is it error, unit, empty array fail(Puppet::Pops::Issues::BAD_ARRAY_SLICE_ARITY, @semantic.left_expr, {:actual => keys.size}) when 1 k = coerce_numeric(keys[0], @semantic.keys[0], scope) @@ -68,7 +87,19 @@ class Puppet::Pops::Evaluator::AccessOperator # A slice [from, to] with support for -1 to mean start, or end respectively. k1 = coerce_numeric(keys[0], @semantic.keys[0], scope) k2 = coerce_numeric(keys[1], @semantic.keys[1], scope) - o[k1, k2] + + # Help confused Ruby do the right thing (it truncates to the right, but negative index + length can never overlap + # the available range. + k1 = k1 < 0 ? o.length + k1 : k1 # abs pos (negative is count from end) + k2 = k2 < 0 ? o.length - k1 + k2 + 1 : k2 # abs length (negative k2 is length from pos to end count) + # if k1 is outside, adjust to first position, and adjust length + if k1 < 0 + k2 = k2 + k1 + k1 = 0 + end + # Help ruby always return empty array when asking for a sub array + result = o[ k1, k2 ] + result.nil? ? [] : result else fail(Puppet::Pops::Issues::BAD_ARRAY_SLICE_ARITY, @semantic.left_expr, {:actual => keys.size}) end diff --git a/spec/unit/pops/evaluator/access_ops_spec.rb b/spec/unit/pops/evaluator/access_ops_spec.rb index 59b5b4db4..ab0db0655 100644 --- a/spec/unit/pops/evaluator/access_ops_spec.rb +++ b/spec/unit/pops/evaluator/access_ops_spec.rb @@ -29,8 +29,8 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do expect(evaluate(literal('abcd')[1,2])).to eql('bc') end - it 'produces nil for a missing entry' do - expect(evaluate(literal('abc')[100])).to eql(nil) + it 'produces empty string for a substring out of range' do + expect(evaluate(literal('abc')[100])).to eql('') end it 'raises an error if arity is wrong for []' do diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index 8929002d9..f65b189cd 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -465,6 +465,17 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do "[1,2,3,4][0,2]" => [1,2], "[1,2,3,4][1,3]" => [2,3,4], "[1,2,3,4][-2,2]" => [3,4], + "[1,2,3,4][-3,2]" => [2,3], + "[1,2,3,4][3,5]" => [4], + "[1,2,3,4][5,2]" => [], + "[1,2,3,4][0,-1]" => [1,2,3,4], + "[1,2,3,4][0,-2]" => [1,2,3], + "[1,2,3,4][0,-4]" => [1], + "[1,2,3,4][0,-5]" => [], + "[1,2,3,4][-5,2]" => [1], + "[1,2,3,4][-5,-3]" => [1,2], + "[1,2,3,4][-6,-3]" => [1,2], + "[1,2,3,4][2,-3]" => [], }.each do |source, result| it "should parse and evaluate the expression '#{source}' to #{result}" do parser.evaluate_string(scope, source, __FILE__).should == result @@ -489,23 +500,30 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do "'abc'[-1]" => 'c', "'abc'[-2]" => 'b', "'abc'[-3]" => 'a', - "'abc'[-4]" => nil, - "'abc'[3]" => nil, + "'abc'[-4]" => '', + "'abc'[3]" => '', "abc[0]" => 'a', "abc[2]" => 'c', "abc[-1]" => 'c', "abc[-2]" => 'b', "abc[-3]" => 'a', - "abc[-4]" => nil, - "abc[3]" => nil, + "abc[-4]" => '', + "abc[3]" => '', "'abcd'[0,2]" => 'ab', "'abcd'[1,3]" => 'bcd', "'abcd'[-2,2]" => 'cd', "'abcd'[-3,2]" => 'bc', "'abcd'[3,5]" => 'd', - "'abcd'[5,2]" => nil, - "'abcd'[-5,2]" => nil, - }.each do |source, result| + "'abcd'[5,2]" => '', + "'abcd'[0,-1]" => 'abcd', + "'abcd'[0,-2]" => 'abc', + "'abcd'[0,-4]" => 'a', + "'abcd'[0,-5]" => '', + "'abcd'[-5,2]" => 'a', + "'abcd'[-5,-3]" => 'ab', + "'abcd'[-6,-3]" => 'ab', + "'abcd'[2,-3]" => '', + }.each do |source, result| it "should parse and evaluate the expression '#{source}' to #{result}" do parser.evaluate_string(scope, source, __FILE__).should == result end From 54300d97a403e22c2984d729eb1de94ad1e0a11a Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 21 Nov 2013 03:50:50 +0100 Subject: [PATCH 119/800] (#22363) Fix failing tests on Ruby 1.8.7 Tests that depended on hash having a defined order failed on Ruby 1.8.7. This corrects the failing tests. --- .../pops/evaluator/collections_ops_spec.rb | 4 +++- .../pops/evaluator/evaluating_parser_spec.rb | 23 +++++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/spec/unit/pops/evaluator/collections_ops_spec.rb b/spec/unit/pops/evaluator/collections_ops_spec.rb index f8d6bfa2b..1d7d642cf 100644 --- a/spec/unit/pops/evaluator/collections_ops_spec.rb +++ b/spec/unit/pops/evaluator/collections_ops_spec.rb @@ -22,7 +22,9 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/Concat/Delete' do end it 'concatenates a hash by converting it to array' do - expect(evaluate(literal([1,2,3]) + literal({'a' => 1, 'b'=>2}))).to eql([1,2,3,['a',1],['b',2]]) + # use match_array here since a hash does not have specified order and this + # hash has different order on Ruby 1.8.7, and 1.9.3. + expect(evaluate(literal([1,2,3]) + literal({'a' => 1, 'b'=>2}))).to match_array([1,2,3,['a',1],['b',2]]) end it 'concatenates a non array value with +' do diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index f65b189cd..02a995125 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -60,7 +60,6 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do "[1,2,3][2]" => 3, "[1,2,3] + [4,5]" => [1,2,3,4,5], "[1,2,3] + [[4,5]]" => [1,2,3,[4,5]], - "[1,2,3] + {'a' => 1, 'b'=>2}" => [1,2,3,['a',1],['b',2]], "[1,2,3] + 4" => [1,2,3,4], "[1,2,3] << [4,5]" => [1,2,3,[4,5]], "[1,2,3] << {'a' => 1, 'b'=>2}" => [1,2,3,{'a' => 1, 'b'=>2}], @@ -79,6 +78,16 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do end end + { + "[1,2,3] + {'a' => 1, 'b'=>2}" => [1,2,3,['a',1],['b',2]], + }.each do |source, result| + it "should parse and evaluate the expression '#{source}' to #{result}" do + # This test must be done with match_array since the order of the hash + # is undefined and Ruby 1.8.7 and 1.9.3 produce different results. + expect(parser.evaluate_string(scope, source, __FILE__)).to match_array(result) + end + end + { "[1,2,3][a]" => :error, }.each do |source, result| @@ -684,7 +693,6 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do '"value is ${sprintf("x%iy",$a)} yo"' => "value is x10y yo", '"value is ${"x%iy".sprintf($a)} yo"' => "value is x10y yo", '"value is ${[1,2,3]} yo"' => "value is [1, 2, 3] yo", - '"value is ${{a=>1,b=>2}} yo"' => "value is {a => 1, b => 2} yo", '"value is ${/.*/} yo"' => "value is /.*/ yo", '$x = undef "value is $x yo"' => "value is yo", '$x = default "value is $x yo"' => "value is default yo", @@ -697,6 +705,17 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do end end + it "should parse and evaluate an interpolation of a hash" do + source = '"value is ${{a=>1,b=>2}} yo"' + # This test requires testing against two options because a hash to string + # produces a result that is unordered + hashstr = {'a' => 1, 'b' => 2}.to_s + alt_results = ["value is {a => 1, b => 2} yo", "value is {b => 2, a => 1} yo" ] + populate + parse_result = parser.evaluate_string(scope, source, __FILE__) + alt_results.include?(parse_result).should == true + end + { '"value is ${a*2} yo"' => :error, }.each do |source, result| From aef7e431f424d0a3cb83ebf4fcb1ba583cd5c1c2 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 24 Nov 2013 05:22:03 +0100 Subject: [PATCH 120/800] (#22363) Refactor Type system, add Enum, Regexp, and Variant This changes the existing Pattern class to Regexp, and introduces a new Pattern class to describe a subset of strings based on a regular expression. An Enum type descriebs the set of strings equal to one of its values. A Variant type is disjoint set of types that describes instance of one of its types. The type calculator tests have been refactored for better maintainability. The operation [] on a (old) Pattern type, now Regexp) was changed to not produce an instance of Regexp, instead Regexp[s] produces a narrow type that can be used instead of a regular expression in matches. --- lib/puppet/pops/binder/bindings_factory.rb | 2 +- lib/puppet/pops/evaluator/access_operator.rb | 36 +- lib/puppet/pops/evaluator/evaluator_impl.rb | 33 +- lib/puppet/pops/issues.rb | 4 + lib/puppet/pops/types/class_loader.rb | 2 +- lib/puppet/pops/types/type_calculator.rb | 147 ++++- lib/puppet/pops/types/type_factory.rb | 59 +- lib/puppet/pops/types/type_parser.rb | 47 +- lib/puppet/pops/types/types.rb | 81 +++ spec/unit/pops/evaluator/access_ops_spec.rb | 9 +- .../pops/evaluator/evaluating_parser_spec.rb | 1 + spec/unit/pops/types/type_calculator_spec.rb | 600 +++++++++++++----- spec/unit/pops/types/type_factory_spec.rb | 12 + spec/unit/pops/types/type_parser_spec.rb | 1 - 14 files changed, 821 insertions(+), 213 deletions(-) diff --git a/lib/puppet/pops/binder/bindings_factory.rb b/lib/puppet/pops/binder/bindings_factory.rb index a5d3ca46a..7c8f02ac7 100644 --- a/lib/puppet/pops/binder/bindings_factory.rb +++ b/lib/puppet/pops/binder/bindings_factory.rb @@ -283,7 +283,7 @@ module Puppet::Pops::Binder::BindingsFactory end # Sets the type of the binding to Pattern. - # @return [Puppet::Pops::Types::PPatternType] the type + # @return [Puppet::Pops::Types::PRegexpType] the type # @api public def pattern() type(T.pattern()) diff --git a/lib/puppet/pops/evaluator/access_operator.rb b/lib/puppet/pops/evaluator/access_operator.rb index 827a85155..3e0d3a86d 100644 --- a/lib/puppet/pops/evaluator/access_operator.rb +++ b/lib/puppet/pops/evaluator/access_operator.rb @@ -63,15 +63,11 @@ class Puppet::Pops::Evaluator::AccessOperator (result.nil? || result.empty?) ? '' : result end - # Speciaizes the Pattern p into itself p[], one regexp instance p[], or array of regexp instances - # p[, ]. + # Parameterizes a PRegexp Type with a pattern string or r ruby egexp # - def access_PPatternType(o, scope, keys) - if keys.size == 0 - return Marshal.load(Marshal.dump(o)) - end - result = keys.collect {|p| Regexp.new(keys[0]) } - result.size == 1 ? result.pop : result + def access_PRegexpType(o, scope, keys) + fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_ARITY, o, :min=>1, :actual => keys.size) unless keys.size == 1 + Puppet::Pops::Types::TypeFactory.regexp(*keys) end # Evaluates [] with 1 or 2 arguments. One argument is an index lookup, two arguments is a slice from/to. @@ -123,6 +119,30 @@ class Puppet::Pops::Evaluator::AccessOperator end end + def access_PEnumType(o, scope, keys) + # TODO: Nice error handling + Puppet::Pops::Types::TypeFactory.enum(*keys) + end + + def access_PVariantType(o, scope, keys) + # TODO: Nice error handling + Puppet::Pops::Types::TypeFactory.variant(*keys) + end + + def access_PStringType(o, scope, keys) + # TODO: Nice error handling + begin + Puppet::Pops::Types::TypeFactory.string(*keys) + rescue StandardError => e + fail(Puppet::Pops::Issues::BAD_TYPE_SPECIALIZATION, o, :message => e.message) + end + end + + def access_PPatternType(o, scope, keys) + # TODO: Nice error handling + Puppet::Pops::Types::TypeFactory.pattern(*keys) + end + def access_PIntegerType(o, scope, keys) unless keys.size.between?(1, 2) fail(Puppet::Pops::Issues::BAD_INTEGER_SLICE_ARITY, @semantic, {:actual => keys.size}) diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index 15760dae8..0174448c8 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -459,22 +459,31 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # def eval_MatchExpression o, scope left, pattern = eval_BinaryExpression o, scope + # matches RHS types as instance of for all types except a parameterized Regexp[R] if pattern.is_a?(Puppet::Pops::Types::PAbstractType) - matched = @@type_calculator.instance?(pattern, left) - else - begin - pattern = Regexp.new(pattern) unless pattern.is_a?(Regexp) - rescue StandardError => e - fail(Issues::MATCH_NOT_REGEXP, o.right_expr, {:detail => e.message}) + if pattern.is_a?(Puppet::Pops::Types::PRegexpType) && pattern.pattern + # A qualified PRegexpType, get its ruby regexp + pattern = pattern.regexp + else + # evaluate as instance? + matched = @@type_calculator.instance?(pattern, left) + # convert match result to Boolean true, or false + return o.operator == :'=~' ? !!matched : !matched end - unless left.is_a?(String) - fail(Issues::MATCH_NOT_STRING, o.left_expr, {:left_value => left}) - end - - matched = pattern.match(left) # nil, or MatchData - set_match_data(matched, o, scope) # creates ephemeral end + begin + pattern = Regexp.new(pattern) unless pattern.is_a?(Regexp) + rescue StandardError => e + fail(Issues::MATCH_NOT_REGEXP, o.right_expr, {:detail => e.message}) + end + unless left.is_a?(String) + fail(Issues::MATCH_NOT_STRING, o.left_expr, {:left_value => left}) + end + + matched = pattern.match(left) # nil, or MatchData + set_match_data(matched, o, scope) # creates ephemeral + # convert match result to Boolean true, or false o.operator == :'=~' ? !!matched : !matched end diff --git a/lib/puppet/pops/issues.rb b/lib/puppet/pops/issues.rb index bbb50cb14..4337708b7 100644 --- a/lib/puppet/pops/issues.rb +++ b/lib/puppet/pops/issues.rb @@ -349,6 +349,10 @@ module Puppet::Pops::Issues end end + BAD_TYPE_SPECIALIZATION = hard_issue :BAD_TYPE_SPECIALIZATION, :message do + "Error creating type specialization of #{label.a_an(semantic)}, #{message}" + end + ILLEGAL_TYPE_SPECIALIZATION = issue :ILLEGAL_TYPE_SPECIALIZATION, :kind do "Cannot specialize an already specialized #{kind} type" end diff --git a/lib/puppet/pops/types/class_loader.rb b/lib/puppet/pops/types/class_loader.rb index abacd704e..d2b3a327b 100644 --- a/lib/puppet/pops/types/class_loader.rb +++ b/lib/puppet/pops/types/class_loader.rb @@ -52,7 +52,7 @@ class Puppet::Pops::Types::ClassLoader # when Puppet::Pops::Types::PArrayType ; Array when Puppet::Pops::Types::PHashType ; Hash - when Puppet::Pops::Types::PPatternType ; Regexp + when Puppet::Pops::Types::PRegexpType ; Regexp when Puppet::Pops::Types::PIntegerType ; Integer when Puppet::Pops::Types::PStringType ; String when Puppet::Pops::Types::PFloatType ; Float diff --git a/lib/puppet/pops/types/type_calculator.rb b/lib/puppet/pops/types/type_calculator.rb index 3d60bd367..34ab49246 100644 --- a/lib/puppet/pops/types/type_calculator.rb +++ b/lib/puppet/pops/types/type_calculator.rb @@ -77,6 +77,7 @@ class Puppet::Pops::Types::TypeCalculator @@assignable_visitor ||= Puppet::Pops::Visitor.new(nil,"assignable",1,1) @@infer_visitor ||= Puppet::Pops::Visitor.new(nil,"infer",0,0) @@string_visitor ||= Puppet::Pops::Visitor.new(nil,"string",0,0) + @@inspect_visitor ||= Puppet::Pops::Visitor.new(nil,"debug_string",0,0) @@enumerable_visitor ||= Puppet::Pops::Visitor.new(nil,"enumerable",0,0) da = Types::PArrayType.new() @@ -167,7 +168,7 @@ class Puppet::Pops::Types::TypeCalculator when c == String type = Types::PStringType.new() when c == Regexp - type = Types::PPatternType.new() + type = Types::PRegexpType.new() when c == NilClass type = Types::PNilType.new() when c == FalseClass, c == TrueClass @@ -219,6 +220,9 @@ class Puppet::Pops::Types::TypeCalculator end # Answers, 'What is the common type of t1 and t2?' + # + # TODO: The current implementation should be optimized for performance + # # @api public # def common_type(t1, t2) @@ -253,7 +257,7 @@ class Puppet::Pops::Types::TypeCalculator return type end - # when both are hot-classes, reduce to PHostClass[] (since one was not assignable to the other) + # when both are host-classes, reduce to PHostClass[] (since one was not assignable to the other) if t1.is_a?(Types::PHostClassType) && t2.is_a?(Types::PHostClassType) return Types::PHostClassType.new() end @@ -279,6 +283,38 @@ class Puppet::Pops::Types::TypeCalculator return t end + if t1.is_a?(Types::PStringType) && t2.is_a?(Types::PStringType) + t = Types::PStringType.new() + t.values = t1.values | t2.values + return t + end + + if t1.is_a?(Types::PPatternType) && t2.is_a?(Types::PPatternType) + t = Types::PPatternType.new() + t.patterns = t1.patterns | t2.patterns + return t + end + + if t1.is_a?(Types::PEnumType) && t2.is_a?(Types::PEnumType) + # The common type is one that complies with either set + t = Types::PEnumType.new + t.values = t1.values | t2.values + return t + end + + if t1.is_a?(Types::PVariantType) && t2.is_a?(Types::PVariantType) + # The common type is one that complies with either set + t = Types::PVariantType.new + t.types = (t1.types | t2.types).map {|opt_t| opt_t.copy } + return t + end + + if t1.is_a?(Types::PRegexpType) && t2.is_a?(Types::PRegexpType) + # if they were identical, the general rule would return a parameterized regexp + # since they were not, the result is a generic regexp type + return Types::PPatternType.new() + end + # Common abstract types, from most specific to most general if common_numeric?(t1, t2) return Types::PNumericType.new() @@ -335,6 +371,7 @@ class Puppet::Pops::Types::TypeCalculator end result end + # Produces a string representing the type # @api public # @@ -342,6 +379,13 @@ class Puppet::Pops::Types::TypeCalculator @@string_visitor.visit_this_0(self, t) end + # Produces a debug string representing the type (possibly with more information that the regular string format) + # @api public + # + def debug_string(t) + @@inspect_visitor.visit_this_0(self, t) + end + # Reduces an enumerable of types to a single common type. # @api public @@ -392,7 +436,9 @@ class Puppet::Pops::Types::TypeCalculator # @api private def infer_String(o) - Types::PStringType.new() + t = Types::PStringType.new() + t.addValues(o) + t end # @api private @@ -410,7 +456,9 @@ class Puppet::Pops::Types::TypeCalculator # @api private def infer_Regexp(o) - Types::PPatternType.new() + t = Types::PRegexpType.new() + t.pattern = o.source + t end # @api private @@ -511,9 +559,51 @@ class Puppet::Pops::Types::TypeCalculator end end + # @api private + def assignable_PVariantType(t, t2) + # A variant is assignable if t2 is assignable to any of its types + t.types.any? { |option_t| assignable?(option_t, t2) } + end + + def assignable_PEnumType(t, t2) + return true if t == t2 || (t.values.empty? && (t2.is_a?(Types::PStringType) || t2.is_a?(Types::PEnumType))) + if t2.is_a?(Types::PStringType) + # if the set of strings are all found in the set of enums + t2.values.all? { |s| t.values.any? { |e| e == s }} + else + false + end + end + # @api private def assignable_PStringType(t, t2) - t2.is_a?(Types::PStringType) + if t.values.empty? + # A general string is assignable by any other string, or pattern restricted string + t2.is_a?(Types::PStringType) || t2.is_a?(Types::PPatternType) || t2.is_a?(Types::PEnumType) + elsif t2.is_a?(Types::PStringType) + # A specific string acts as a set of strings - must have exactly the same strings + Set.new(t.values) == Set.new(t2.values) + else + # All others are false, since no other type describes the same set of specific strings + false + end + end + + # @api private + def assignable_PPatternType(t, t2) + return true if t == t2 + return false unless t2.is_a? Types::PStringType + + if t2.values.empty? + # Strings (unknown which ones) cannot all match a pattern, but if there is no pattern it is ok + # (There should really always be a pattern, but better safe than sorry). + return t.patterns.empty? ? true : false + end + # all strings in String type must match all patterns in Pattern type + t.patterns.all? do |p| + re = p.regexp + t2.values.all? {|v| re.match(v) } + end end # @api private @@ -527,8 +617,8 @@ class Puppet::Pops::Types::TypeCalculator end # @api private - def assignable_PPatternType(t, t2) - t2.is_a?(Types::PPatternType) + def assignable_PRegexpType(t, t2) + t2.is_a?(Types::PRegexpType) && (t.pattern.nil? || t.pattern == t2.pattern) end # @api private @@ -579,7 +669,11 @@ class Puppet::Pops::Types::TypeCalculator # Data is assignable by other Data and by Array[Data] and Hash[Literal, Data] # @api private def assignable_PDataType(t, t2) - t2.is_a?(Types::PDataType) || t2.is_a?(Types::PLiteralType) || assignable?(@data_array, t2) || assignable?(@data_hash, t2) + t2.is_a?(Types::PDataType) || + t2.is_a?(Types::PLiteralType) || + assignable?(@data_array, t2) || + assignable?(@data_hash, t2) || + (t2.is_a?(Types::PVariantType) && !t2.types.empty? && t2.types.all? {|t| assignable?(data, t) } ) end # Assignable if t2's ruby class is same or subclass of t1's ruby class @@ -592,6 +686,10 @@ class Puppet::Pops::Types::TypeCalculator !!(c2 <= c1) end + def debug_string_Object(t) + string(t) + end + # @api private def string_PType(t) if t.type.nil? @@ -645,10 +743,39 @@ class Puppet::Pops::Types::TypeCalculator def string_PFloatType(t) ; "Float" ; end # @api private - def string_PPatternType(t) ; "Pattern" ; end + def string_PRegexpType(t) + t.pattern.nil? ? "Regexp" : "Regexp[#{t.regexp.inspect}]" + end # @api private - def string_PStringType(t) ; "String" ; end + def string_PStringType(t) + # skip values in regular output - see debug_string + return "String" + end + + # @api private + def debug_string_PStringType(t) + return "String" # if t.values.empty? + "String[" << (t.values.map {|s| "'#{s}'" }).join(', ') << ']' + end + + # @api private + def string_PEnumType(t) + return "Enum" if t.values.empty? + "Enum[" << t.values.map {|s| "'#{s}'" }.join(', ') << ']' + end + + # @api private + def string_PVariantType(t) + return "Variant" if t.types.empty? + "Variant[" << t.types.map {|t2| string(t2) }.join(', ') << ']' + end + + # @api private + def string_PPatternType(t) + return "Pattern" if t.patterns.empty? + "Pattern[" << t.patterns.map {|s| "#{s.regexp.inspect}" }.join(', ') << ']' + end # @api private def string_PCollectionType(t) ; "Collection" ; end diff --git a/lib/puppet/pops/types/type_factory.rb b/lib/puppet/pops/types/type_factory.rb index 059617621..88f88ecbd 100644 --- a/lib/puppet/pops/types/type_factory.rb +++ b/lib/puppet/pops/types/type_factory.rb @@ -44,11 +44,31 @@ module Puppet::Pops::Types::TypeFactory @type_calculator.string(t) end - # Produces the String type + # Produces the String type, optionally with specific string values # @api public # - def self.string() - Types::PStringType.new() + def self.string(*values) + t = Types::PStringType.new() + values.each {|v| t.addValues(v) } + t + end + + # Produces the Enum type, optionally with specific string values + # @api public + # + def self.enum(*values) + t = Types::PEnumType.new() + values.each {|v| t.addValues(v) } + t + end + + # Produces the Variant type, optionally with the "one of" types + # @api public + # + def self.variant(*types) + t = Types::PVariantType.new() + types.each {|v| t.addTypes(type_of(v)) } + t end # Produces the Boolean type @@ -65,11 +85,38 @@ module Puppet::Pops::Types::TypeFactory Types::PObjectType.new() end - # Produces the Pattern type + # Produces the Regexp type + # @param pattern [Regexp, String, nil] (nil) The regular expression object or a regexp source string, or nil for bare type # @api public # - def self.pattern() - Types::PPatternType.new() + def self.regexp(pattern = nil) + t = Types::PRegexpType.new() + if pattern + t.pattern = pattern.is_a?(Regexp) ? pattern.inspect[1..-1] : pattern + end + t + end + + def self.pattern(*regular_expressions) + t = Types::PPatternType.new() + regular_expressions.each do |re| + case re + when String + re_T = Types::PRegexpType.new() + re_T.pattern = re + t.addPatterns(re_T) + when Regexp + re_T = Type::PRegexpType.new() + # Regep.to_s includes options user did not enter and does not escape source + # to work either as a string or as a // regexp. The inspect method does a better + # job, but includes the // + re_T.pattern = re.inspect[1..-2] + t.addPatterns(re_T) + else + raise ArgumentError, "Only String and Regexp are allowed: got '#{re.class}" + end + end + t end # Produces the Literal type diff --git a/lib/puppet/pops/types/type_parser.rb b/lib/puppet/pops/types/type_parser.rb index 1280fc67e..bc4a8e2ca 100644 --- a/lib/puppet/pops/types/type_parser.rb +++ b/lib/puppet/pops/types/type_parser.rb @@ -89,37 +89,62 @@ class Puppet::Pops::Types::TypeParser case name_ast.value when "integer" TYPES.integer + when "float" TYPES.float + when "numeric" TYPES.numeric + when "string" TYPES.string + + when "enum" + TYPES.enum + when "boolean" TYPES.boolean + when "pattern" TYPES.pattern + + when "regexp" + TYPES.regexp + when "data" TYPES.data + when "array" TYPES.array_of_data + when "hash" TYPES.hash_of_data + when "class" TYPES.host_class() + when "resource" TYPES.resource() + when "collection" TYPES.collection() + when "literal" TYPES.literal() + when "catalogentry" TYPES.catalog_entry() + when "undef" # Should not be interpreted as Resource type TYPES.undef() + when "object" TYPES.object() + + when "variant" + TYPES.variant() + when "ruby", "type" # should not be interpreted as Resource type # TODO: these should not be errors @@ -172,6 +197,26 @@ class Puppet::Pops::Types::TypeParser TYPES.resource(parameters[0], parameters[1]) end + when "regexp" + # 1 parameter being a string, or regular expression + raise_invalid_parameters_error("Regexp", "1", parameters.size) unless parameters.size == 1 + TYPES.regexp(parameters[0]) + + when "enum" + # 1..m parameters being strings + raise_invalid_parameters_error("Enum", "1 or more", parameters.size) unless parameters.size > 1 + TYPES.enum(*parameters) + + when "pattern" + # 1..m parameters being strings or regular expressions + raise_invalid_parameters_error("Pattern", "1 or more", parameters.size) unless parameters.size > 1 + TYPES.pattern(*parameters) + + when "variant" + # 1..m parameters being strings or regular expressions + raise_invalid_parameters_error("Variant", "1 or more", parameters.size) unless parameters.size > 1 + TYPES.variant(*parameters) + when "integer" if parameters.size == 1 case parameters[0] @@ -186,7 +231,7 @@ class Puppet::Pops::Types::TypeParser TYPES.range(parameters[0] == :default ? nil : parameters[0], parameters[1] == :default ? nil : parameters[1]) end - when "object", "collection", "data", "catalogentry", "boolean", "float", "literal", "undef", "numeric", "pattern" + when "object", "collection", "data", "catalogentry", "boolean", "float", "literal", "undef", "numeric", "pattern", "string" raise_unparameterized_type_error(parameterized_ast.left_expr) when "ruby", "type" diff --git a/lib/puppet/pops/types/types.rb b/lib/puppet/pops/types/types.rb index b0cdcce7d..1dceb4515 100644 --- a/lib/puppet/pops/types/types.rb +++ b/lib/puppet/pops/types/types.rb @@ -74,13 +74,58 @@ module Puppet::Pops::Types class PDataType < PObjectType end + # A flexible type describing an any? of other types + # @api public + class PVariantType < PObjectType + contains_many_uni 'types', PAbstractType, :lowerBound => 1 + + module ClassModule + + def hash + [self.class, Set.new(self.types)].hash + end + + def ==(o) + self.class == o.class && Set.new(types) == Set.new(o.types) + end + end + end + # Type that is PDataType compatible, but is not a PCollectionType. # @api public class PLiteralType < PDataType end + # A string type describing the set of strings having one of the given values + # + class PEnumType < PLiteralType + has_many_attr 'values', String, :lowerBound => 1 + + module ClassModule + def hash + [self.class, Set.new(self.values)].hash + end + + def ==(o) + self.class == o.class && Set.new(values) == Set.new(o.values) + end + end + end + # @api public class PStringType < PLiteralType + has_many_attr 'values', String, :lowerBound => 0, :upperBound => -1, :unique => true + + module ClassModule + + def hash + [self.class, Set.new(self.values)].hash + end + + def ==(o) + self.class == o.class && Set.new(values) == Set.new(o.values) + end + end end # @api public @@ -128,8 +173,44 @@ module Puppet::Pops::Types class PFloatType < PNumericType end + # @api public + class PRegexpType < PLiteralType + has_attr 'pattern', String, :lowerBound => 1 + has_attr 'regexp', Object, :derived => true + + module ClassModule + def regexp_derived + @_regexp = Regexp.new(pattern) unless @_regexp && @_regexp.source == pattern + @_regexp + end + + def hash + [self.class, pattern].hash + end + + def ==(o) + self.class == o.class && pattern == o.pattern + end + end + end + + # Represents a subtype of String that narrows the string to those matching the patterns + # If specified without a pattern it is basically the same as the String type. + # # @api public class PPatternType < PLiteralType + contains_many_uni 'patterns', PRegexpType + + module ClassModule + + def hash + [self.class, Set.new(patterns)].hash + end + + def ==(o) + self.class == o.class && Set.new(patterns) == Set.new(o.patterns) + end + end end # @api public diff --git a/spec/unit/pops/evaluator/access_ops_spec.rb b/spec/unit/pops/evaluator/access_ops_spec.rb index ab0db0655..a70dc6390 100644 --- a/spec/unit/pops/evaluator/access_ops_spec.rb +++ b/spec/unit/pops/evaluator/access_ops_spec.rb @@ -156,9 +156,14 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do expect { evaluate(expr)}.to raise_error(/Array\[\] arguments must be types/) end - it 'creates a Regexp instance when applied to a Pattern' do + it 'creates a PPatternType instance when applied to a Pattern' do regexp_expr = fqr('Pattern')['foo'] - expect('foo' =~ evaluate(regexp_expr)).to eql(0) + expect(evaluate(regexp_expr)).to eql(Puppet::Pops::Types::TypeFactory.pattern('foo')) + end + + it 'creates a Regexp instance when applied to a Pattern' do + regexp_expr = fqr('Regexp')['foo'] + expect(evaluate(regexp_expr)).to eql(Puppet::Pops::Types::TypeFactory.regexp('foo')) end # Class diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index 02a995125..47b016863 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -198,6 +198,7 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do "'a' !~ 'b.*'" => true, '$x = a; a =~ "$x.*"' => true, "a =~ Pattern['a.*']" => true, + "a =~ Regexp['a.*']" => true, "$x = /a.*/ a =~ $x" => true, "$x = Pattern['a.*'] a =~ $x" => true, "1 =~ Integer" => true, diff --git a/spec/unit/pops/types/type_calculator_spec.rb b/spec/unit/pops/types/type_calculator_spec.rb index 47ce5c1d4..64a3b75e4 100644 --- a/spec/unit/pops/types/type_calculator_spec.rb +++ b/spec/unit/pops/types/type_calculator_spec.rb @@ -11,6 +11,106 @@ describe 'The type calculator' do t end + def pattern_t(*patterns) + Puppet::Pops::Types::TypeFactory.pattern(*patterns) + end + + def string_t(*strings) + Puppet::Pops::Types::TypeFactory.string(*strings) + end + + def enum_t(*strings) + Puppet::Pops::Types::TypeFactory.enum(*strings) + end + + def variant_t(*types) + Puppet::Pops::Types::TypeFactory.variant(*types) + end + + def integer_t() + Puppet::Pops::Types::TypeFactory.integer() + end + + def array_t(t) + Puppet::Pops::Types::TypeFactory.array_of(t) + end + + def types + Puppet::Pops::Types + end + + shared_context "types_setup" do + + def all_types + [ Puppet::Pops::Types::PObjectType, + Puppet::Pops::Types::PNilType, + Puppet::Pops::Types::PDataType, + Puppet::Pops::Types::PLiteralType, + Puppet::Pops::Types::PStringType, + Puppet::Pops::Types::PNumericType, + Puppet::Pops::Types::PIntegerType, + Puppet::Pops::Types::PFloatType, + Puppet::Pops::Types::PRegexpType, + Puppet::Pops::Types::PBooleanType, + Puppet::Pops::Types::PCollectionType, + Puppet::Pops::Types::PArrayType, + Puppet::Pops::Types::PHashType, + Puppet::Pops::Types::PRubyType, + Puppet::Pops::Types::PHostClassType, + Puppet::Pops::Types::PResourceType, + Puppet::Pops::Types::PPatternType, + Puppet::Pops::Types::PEnumType, + Puppet::Pops::Types::PVariantType, + ] + end + + def literal_types + # PVariantType is also literal, if its types are all Literal + [ + Puppet::Pops::Types::PLiteralType, + Puppet::Pops::Types::PStringType, + Puppet::Pops::Types::PNumericType, + Puppet::Pops::Types::PIntegerType, + Puppet::Pops::Types::PFloatType, + Puppet::Pops::Types::PRegexpType, + Puppet::Pops::Types::PBooleanType, + Puppet::Pops::Types::PPatternType, + Puppet::Pops::Types::PEnumType, + ] + end + + def numeric_types + # PVariantType is also numeric, if its types are all numeric + [ + Puppet::Pops::Types::PNumericType, + Puppet::Pops::Types::PIntegerType, + Puppet::Pops::Types::PFloatType, + ] + end + + def string_types + # PVariantType is also string type, if its types are all compatible + [ + Puppet::Pops::Types::PStringType, + Puppet::Pops::Types::PPatternType, + Puppet::Pops::Types::PEnumType, + ] + end + + def collection_types + # PVariantType is also string type, if its types are all compatible + [ + Puppet::Pops::Types::PCollectionType, + Puppet::Pops::Types::PHashType, + Puppet::Pops::Types::PArrayType, + ] + end + + def data_compatible_types + literal_types + [Puppet::Pops::Types::PHashType, Puppet::Pops::Types::PArrayType, Puppet::Pops::Types::PDataType] + end + end + context 'when inferring ruby' do it 'fixnum translates to PIntegerType' do @@ -29,6 +129,12 @@ describe 'The type calculator' do calculator.infer('foo').class.should == Puppet::Pops::Types::PStringType end + it 'inferred string type knows the string value' do + t = calculator.infer('foo') + t.class.should == Puppet::Pops::Types::PStringType + t.values.should == ['foo'] + end + it 'boolean true translates to PBooleanType' do calculator.infer(true).class.should == Puppet::Pops::Types::PBooleanType end @@ -37,8 +143,8 @@ describe 'The type calculator' do calculator.infer(false).class.should == Puppet::Pops::Types::PBooleanType end - it 'regexp translates to PPatternType' do - calculator.infer(/^a regular exception$/).class.should == Puppet::Pops::Types::PPatternType + it 'regexp translates to PRegexpType' do + calculator.infer(/^a regular expression$/).class.should == Puppet::Pops::Types::PRegexpType end it 'nil translates to PNilType' do @@ -74,6 +180,12 @@ describe 'The type calculator' do t.to.should == 42 end + it "Compound string values are computed" do + t = calculator.infer(['a','b', 'c']).element_type + t.class.should == Puppet::Pops::Types::PStringType + t.values.should == ['a', 'b', 'c'] + end + it 'with fixnum and float values translates to PArrayType[PNumericType]' do calculator.infer([1,2.0]).element_type.class.should == Puppet::Pops::Types::PNumericType end @@ -162,9 +274,26 @@ describe 'The type calculator' do calculator.infer({:first => 1, :second => 2}).element_type.class.should == Puppet::Pops::Types::PIntegerType end end + end - # Deal with cases not covered by infer computing common type + context 'patterns' do + it "constructs a PPatternType" do + t = pattern_t('a(b)c') + t.class.should == Puppet::Pops::Types::PPatternType + t.patterns.size.should == 1 + t.patterns[0].class.should == Puppet::Pops::Types::PRegexpType + t.patterns[0].pattern.should == 'a(b)c' + t.patterns[0].regexp.match('abc')[1].should == 'b' + end + + it "constructs a PStringType with multiple strings" do + t = string_t('a', 'b', 'c', 'abc') + t.values.should == ['a', 'b', 'c', 'abc'] + end + end + + # Deal with cases not covered by computing common type context 'when computing common type' do it 'computes given resource type commonality' do r1 = Puppet::Pops::Types::PResourceType.new() @@ -211,184 +340,198 @@ describe 'The type calculator' do r1 = Puppet::Pops::Types::PHostClassType.new() calculator.string(calculator.common_type(r1, r2)).should == "Class" end + + it 'computes pattern commonality' do + t1 = pattern_t('abc') + t2 = pattern_t('xyz') + common_t = calculator.common_type(t1,t2) + common_t.class.should == Puppet::Pops::Types::PPatternType + common_t.patterns.map { |pr| pr.pattern }.should == ['abc', 'xyz'] + calculator.string(common_t).should == "Pattern[/abc/, /xyz/]" + end + + it 'computes enum commonality to value set diff' do + t1 = enum_t('a', 'b', 'c') + t2 = enum_t('x', 'y', 'z') + common_t = calculator.common_type(t1, t2) + common_t.should == enum_t('a', 'b', 'c', 'x', 'y', 'z') + end + + it 'computed variant commonality to type union' do + a_t1 = integer_t() + a_t2 = string_t() + v_a = variant_t(a_t1, a_t2) + b_t1 = enum_t('a') + v_b = variant_t(b_t1) + common_t = calculator.common_type(v_a, v_b) + common_t.class.should == Puppet::Pops::Types::PVariantType + Set.new(common_t.types).should == Set.new([a_t1, a_t2, b_t1]) + end end - context 'when testing if x is assignable to y' do - it 'should allow all object types to PObjectType' do - t = Puppet::Pops::Types::PObjectType.new() - calculator.assignable?(t, t).should() == true - calculator.assignable?(t,Puppet::Pops::Types::PNilType.new()).should() == true - calculator.assignable?(t,Puppet::Pops::Types::PDataType.new()).should() == true - calculator.assignable?(t,Puppet::Pops::Types::PLiteralType.new()).should() == true - calculator.assignable?(t,Puppet::Pops::Types::PStringType.new()).should() == true - calculator.assignable?(t,Puppet::Pops::Types::PNumericType.new()).should() == true - calculator.assignable?(t,Puppet::Pops::Types::PIntegerType.new()).should() == true - calculator.assignable?(t,Puppet::Pops::Types::PFloatType.new()).should() == true - calculator.assignable?(t,Puppet::Pops::Types::PPatternType.new()).should() == true - calculator.assignable?(t,Puppet::Pops::Types::PBooleanType.new()).should() == true - calculator.assignable?(t,Puppet::Pops::Types::PCollectionType.new()).should() == true - calculator.assignable?(t,Puppet::Pops::Types::PArrayType.new()).should() == true - calculator.assignable?(t,Puppet::Pops::Types::PHashType.new()).should() == true - calculator.assignable?(t,Puppet::Pops::Types::PRubyType.new()).should() == true - calculator.assignable?(t,Puppet::Pops::Types::PHostClassType.new()).should() == true - calculator.assignable?(t,Puppet::Pops::Types::PResourceType.new()).should() == true + context 'computes assignability' do + include_context "types_setup" + + context "for Object, such that" do + it 'all types are assignable to Object' do + t = Puppet::Pops::Types::PObjectType.new() + all_types.each { |t2| t2.new.should be_assignable_to(t) } + end + + it 'Object is not assignable to anything but Object' do + tested_types = all_types() - [Puppet::Pops::Types::PObjectType] + t = Puppet::Pops::Types::PObjectType.new() + tested_types.each { |t2| t.should_not be_assignable_to(t2.new) } + end end - it 'should reject PObjectType to less generic types' do - t = Puppet::Pops::Types::PObjectType.new() - calculator.assignable?(Puppet::Pops::Types::PDataType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PLiteralType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PStringType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PNumericType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PIntegerType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PFloatType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PPatternType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PBooleanType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PCollectionType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PArrayType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PHashType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PRubyType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PHostClassType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PResourceType.new(), t).should() == false + context "for Data, such that" do + it 'all literals + array and hash are assignable to Data' do + t = Puppet::Pops::Types::PDataType.new() + data_compatible_types.each { |t2| t2.new.should be_assignable_to(t) } + end + + it 'a Variant of literal, hash, or array is assignable to Data' do + t = Puppet::Pops::Types::PDataType.new() + data_compatible_types.each { |t2| variant_t(t2.new).should be_assignable_to(t) } + end + + it 'Data is not assignable to any of its subtypes' do + t = Puppet::Pops::Types::PDataType.new() + types_to_test = data_compatible_types- [Puppet::Pops::Types::PDataType] + types_to_test.each {|t2| t.should_not be_assignable_to(t2.new) } + end + + it 'Data is not assignable to a Variant of Data subtype' do + t = Puppet::Pops::Types::PDataType.new() + types_to_test = data_compatible_types- [Puppet::Pops::Types::PDataType] + types_to_test.each { |t2| t.should_not be_assignable_to(variant_t(t2.new)) } + end + + it 'Data is not assignable to any disjunct type' do + tested_types = all_types - [Puppet::Pops::Types::PObjectType, Puppet::Pops::Types::PDataType] - literal_types + t = Puppet::Pops::Types::PDataType.new() + tested_types.each {|t2| t.should_not be_assignable_to(t2.new) } + end end - it 'should allow all data types, array, and hash to PDataType' do - t = Puppet::Pops::Types::PDataType.new() - calculator.assignable?(t, t).should() == true - calculator.assignable?(t,Puppet::Pops::Types::PLiteralType.new()).should() == true - calculator.assignable?(t,Puppet::Pops::Types::PStringType.new()).should() == true - calculator.assignable?(t,Puppet::Pops::Types::PNumericType.new()).should() == true - calculator.assignable?(t,Puppet::Pops::Types::PIntegerType.new()).should() == true - calculator.assignable?(t,Puppet::Pops::Types::PFloatType.new()).should() == true - calculator.assignable?(t,Puppet::Pops::Types::PPatternType.new()).should() == true - calculator.assignable?(t,Puppet::Pops::Types::PBooleanType.new()).should() == true - calculator.assignable?(t,Puppet::Pops::Types::PArrayType.new()).should() == true - calculator.assignable?(t,Puppet::Pops::Types::PHashType.new()).should() == true + context "for Literal, such that" do + it "all literals are assignable to Literal" do + t = Puppet::Pops::Types::PLiteralType.new() + literal_types.each {|t2| t2.new.should be_assignable_to(t) } + end + + it 'Literal is not assignable to any of its subtypes' do + t = Puppet::Pops::Types::PLiteralType.new() + types_to_test = literal_types - [Puppet::Pops::Types::PLiteralType] + types_to_test.each {|t2| t.should_not be_assignable_to(t2.new) } + end + + it 'Literal is not assignable to any disjunct type' do + tested_types = all_types - [Puppet::Pops::Types::PObjectType, Puppet::Pops::Types::PDataType] - literal_types + t = Puppet::Pops::Types::PLiteralType.new() + tested_types.each {|t2| t.should_not be_assignable_to(t2.new) } + end end - it 'should reject PDataType to less generic data types' do - t = Puppet::Pops::Types::PDataType.new() - calculator.assignable?(Puppet::Pops::Types::PLiteralType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PStringType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PNumericType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PIntegerType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PFloatType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PPatternType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PBooleanType.new(), t).should() == false + context "for Numeric, such that" do + it "all numerics are assignable to Numeric" do + t = Puppet::Pops::Types::PNumericType.new() + numeric_types.each {|t2| t2.new.should be_assignable_to(t) } + end + + it 'Numeric is not assignable to any of its subtypes' do + t = Puppet::Pops::Types::PNumericType.new() + types_to_test = numeric_types - [Puppet::Pops::Types::PNumericType] + types_to_test.each {|t2| t.should_not be_assignable_to(t2.new) } + end + + it 'Numeric is not assignable to any disjunct type' do + tested_types = all_types - [ + Puppet::Pops::Types::PObjectType, + Puppet::Pops::Types::PDataType, + Puppet::Pops::Types::PLiteralType, + ] - numeric_types + t = Puppet::Pops::Types::PNumericType.new() + tested_types.each {|t2| t.should_not be_assignable_to(t2.new) } + end end - it 'should reject PDataType to non data types' do - t = Puppet::Pops::Types::PDataType.new() - calculator.assignable?(Puppet::Pops::Types::PCollectionType.new(),t).should() == false - calculator.assignable?(Puppet::Pops::Types::PArrayType.new(),t).should() == false - calculator.assignable?(Puppet::Pops::Types::PHashType.new(),t).should() == false - calculator.assignable?(Puppet::Pops::Types::PRubyType.new(), t).should() == false + context "for Collection, such that" do + it "all collections are assignable to Collection" do + t = Puppet::Pops::Types::PCollectionType.new() + collection_types.each {|t2| t2.new.should be_assignable_to(t) } + end + + it 'Collection is not assignable to any of its subtypes' do + t = Puppet::Pops::Types::PCollectionType.new() + types_to_test = collection_types - [Puppet::Pops::Types::PCollectionType] + types_to_test.each {|t2| t.should_not be_assignable_to(t2.new) } + end + + it 'Collection is not assignable to any disjunct type' do + tested_types = all_types - [Puppet::Pops::Types::PObjectType] - collection_types + t = Puppet::Pops::Types::PCollectionType.new() + tested_types.each {|t2| t.should_not be_assignable_to(t2.new) } + end end - it 'should allow all literal types to PLiteralType' do - t = Puppet::Pops::Types::PLiteralType.new() - calculator.assignable?(t, t).should() == true - calculator.assignable?(t,Puppet::Pops::Types::PStringType.new()).should() == true - calculator.assignable?(t,Puppet::Pops::Types::PNumericType.new()).should() == true - calculator.assignable?(t,Puppet::Pops::Types::PIntegerType.new()).should() == true - calculator.assignable?(t,Puppet::Pops::Types::PFloatType.new()).should() == true - calculator.assignable?(t,Puppet::Pops::Types::PPatternType.new()).should() == true - calculator.assignable?(t,Puppet::Pops::Types::PBooleanType.new()).should() == true + context "for Array, such that" do + it "Array is not assignable to any other Collection type" do + t = Puppet::Pops::Types::PArrayType.new() + tested_types = collection_types - [ + Puppet::Pops::Types::PCollectionType, + Puppet::Pops::Types::PArrayType] + tested_types.each {|t2| t.should_not be_assignable_to(t2.new) } + end + + it 'Array is not assignable to any disjunct type' do + tested_types = all_types - [ + Puppet::Pops::Types::PObjectType, + Puppet::Pops::Types::PObjectType, + Puppet::Pops::Types::PDataType] - collection_types + t = Puppet::Pops::Types::PArrayType.new() + tested_types.each {|t2| t.should_not be_assignable_to(t2.new) } + end end - it 'should reject PLiteralType to less generic literal types' do - t = Puppet::Pops::Types::PLiteralType.new() - calculator.assignable?(Puppet::Pops::Types::PStringType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PNumericType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PIntegerType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PFloatType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PPatternType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PBooleanType.new(), t).should() == false + context "for Hash, such that" do + it "Hash is not assignable to any other Collection type" do + t = Puppet::Pops::Types::PHashType.new() + tested_types = collection_types - [ + Puppet::Pops::Types::PCollectionType, + Puppet::Pops::Types::PHashType] + tested_types.each {|t2| t.should_not be_assignable_to(t2.new) } + end + + it 'Hash is not assignable to any disjunct type' do + tested_types = all_types - [ + Puppet::Pops::Types::PObjectType, + Puppet::Pops::Types::PObjectType, + Puppet::Pops::Types::PDataType] - collection_types + t = Puppet::Pops::Types::PHashType.new() + tested_types.each {|t2| t.should_not be_assignable_to(t2.new) } + end end - it 'should reject PLiteralType to non literal types' do - t = Puppet::Pops::Types::PLiteralType.new() - calculator.assignable?(Puppet::Pops::Types::PCollectionType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PArrayType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PHashType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PRubyType.new(), t).should() == false - end - it 'should allow all numeric types to PNumericType' do - t = Puppet::Pops::Types::PNumericType.new() - calculator.assignable?(t, t).should() == true - calculator.assignable?(t, Puppet::Pops::Types::PIntegerType.new()).should() == true - calculator.assignable?(t, Puppet::Pops::Types::PFloatType.new()).should() == true - end - - it 'should reject PNumericType to less generic numeric types' do - t = Puppet::Pops::Types::PNumericType.new() - calculator.assignable?(Puppet::Pops::Types::PIntegerType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PFloatType.new(), t).should() == false - end - - it 'should reject PNumericType to non numeric types' do - t = Puppet::Pops::Types::PNumericType.new() - calculator.assignable?(Puppet::Pops::Types::PStringType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PPatternType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PBooleanType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PCollectionType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PArrayType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PHashType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PRubyType.new(), t).should() == false - end - - it 'should allow all collection types to PCollectionType' do - t = Puppet::Pops::Types::PCollectionType.new() - calculator.assignable?(t, t).should() == true - calculator.assignable?(t, Puppet::Pops::Types::PArrayType.new()).should() == true - calculator.assignable?(t, Puppet::Pops::Types::PHashType.new()).should() == true - end - - it 'should reject PCollectionType to less generic collection types' do - t = Puppet::Pops::Types::PCollectionType.new() - calculator.assignable?(Puppet::Pops::Types::PArrayType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PHashType.new(), t).should() == false - end - - it 'should reject PCollectionType to non collection types' do - t = Puppet::Pops::Types::PCollectionType.new() - calculator.assignable?(Puppet::Pops::Types::PDataType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PLiteralType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PStringType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PNumericType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PIntegerType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PFloatType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PPatternType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PBooleanType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PRubyType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PHostClassType.new(), t).should() == false - calculator.assignable?(Puppet::Pops::Types::PResourceType.new(), t).should() == false - end - - it 'should reject PArrayType to non array type collections' do - t = Puppet::Pops::Types::PArrayType.new() - calculator.assignable?(Puppet::Pops::Types::PHashType.new(), t).should() == false - end - - it 'should reject PHashType to non hash type collections' do - t = Puppet::Pops::Types::PHashType.new() - calculator.assignable?(Puppet::Pops::Types::PArrayType.new(), t).should() == false - end - - it 'should recognize mapped ruby types' do - calculator.assignable?(Puppet::Pops::Types::PIntegerType.new(), Integer).should == true - calculator.assignable?(Puppet::Pops::Types::PIntegerType.new(), Fixnum).should == true - calculator.assignable?(Puppet::Pops::Types::PIntegerType.new(), Bignum).should == true - calculator.assignable?(Puppet::Pops::Types::PFloatType.new(), Float).should == true - calculator.assignable?(Puppet::Pops::Types::PNumericType.new(), Numeric).should == true - calculator.assignable?(Puppet::Pops::Types::PNilType.new(), NilClass).should == true - calculator.assignable?(Puppet::Pops::Types::PBooleanType.new(), FalseClass).should == true - calculator.assignable?(Puppet::Pops::Types::PBooleanType.new(), TrueClass).should == true - calculator.assignable?(Puppet::Pops::Types::PStringType.new(), String).should == true - calculator.assignable?(Puppet::Pops::Types::PPatternType.new(), Regexp).should == true - calculator.assignable?(Puppet::Pops::Types::TypeFactory.array_of_data(), Array).should == true - calculator.assignable?(Puppet::Pops::Types::TypeFactory.hash_of_data(), Hash).should == true + it 'should recognize mapped ruby types' do + { Integer => Puppet::Pops::Types::PIntegerType.new, + Fixnum => Puppet::Pops::Types::PIntegerType.new, + Bignum => Puppet::Pops::Types::PIntegerType.new, + Float => Puppet::Pops::Types::PFloatType.new, + Numeric => Puppet::Pops::Types::PNumericType.new, + NilClass => Puppet::Pops::Types::PNilType.new, + TrueClass => Puppet::Pops::Types::PBooleanType.new, + FalseClass => Puppet::Pops::Types::PBooleanType.new, + String => Puppet::Pops::Types::PStringType.new, + Regexp => Puppet::Pops::Types::PRegexpType.new, + Regexp => Puppet::Pops::Types::PRegexpType.new, + Array => Puppet::Pops::Types::TypeFactory.array_of_data(), + Hash => Puppet::Pops::Types::TypeFactory.hash_of_data() + }.each do |ruby_type, puppet_type | + ruby_type.should be_assignable_to(puppet_type) + end end context 'when dealing with integer ranges' do @@ -427,6 +570,38 @@ describe 'The type calculator' do end end + context 'when dealing with patterns' do + it 'should accept a string matching a pattern' do + p_t = pattern_t('abc') + p_s = string_t('XabcY') + calculator.assignable?(p_t, p_s).should == true + end + + it 'should accept a string matching all patterns' do + p_t = pattern_t('abc', 'ab', 'c') + p_s = string_t('XabcY') + calculator.assignable?(p_t, p_s).should == true + end + + it 'should accept multiple strings if they all match all patterns' do + p_t = pattern_t('abc', 'ab', 'c') + p_s = string_t('XabcY', 'abcde') + calculator.assignable?(p_t, p_s).should == true + end + + it 'should reject a string not matching all patterns' do + p_t = pattern_t('abc', 'ab', 'c', 'q') + p_s = string_t('XqqqY') + calculator.assignable?(p_t, p_s).should == false + end + + it 'should reject multiple strings if not all match all patterns' do + p_t = pattern_t('abc', 'ab', 'c', 'q') + p_s = string_t('abc', 'XqqqY') + calculator.assignable?(p_t, p_s).should == false + end + end + it 'should recognize ruby type inheritance' do class Foo end @@ -513,6 +688,32 @@ describe 'The type calculator' do calculator.instance?(range, -1) == false calculator.instance?(range, 11) == false end + + it 'should consider string matching enum as instanceof' do + enum = enum_t('XS', 'S', 'M', 'L', 'XL', '0') + calculator.instance?(enum, 'XS') == true + calculator.instance?(enum, 'S') == true + calculator.instance?(enum, 'XXL') == false + calculator.instance?(enum, '') == false + calculator.instance?(enum, '0') == true + calculator.instance?(enum, 0) == false + end + + it 'should consider array[string] as instance of Array[Enum] when strings are instance of Enum' do + enum = enum_t('XS', 'S', 'M', 'L', 'XL', '0') + array = array_t(enum) + calculator.instance?(array, ['XS', 'S', 'XL']) == true + calculator.instance?(array, ['XS', 'S', 'XXL']) == false + end + + it 'should consider array[mixed] as instance of Variant[mixed] when mixed types are listed in Variant' do + enum = enum_t('XS', 'S', 'M', 'L', 'XL') + sizes = int_range(30, 50) + array = variant_t(enum, sizes) + calculator.instance?(array, ['XS', 'S', 30, 50]) == true + calculator.instance?(array, ['XS', 'S', 'XXL']) == false + calculator.instance?(array, ['XS', 'S', 29]) == false + end end context 'when converting a ruby class' do @@ -540,8 +741,8 @@ describe 'The type calculator' do calculator.type(String).class.should == Puppet::Pops::Types::PStringType end - it 'should yield \'PPatternType\' for Regexp' do - calculator.type(Regexp).class.should == Puppet::Pops::Types::PPatternType + it 'should yield \'PRegexpType\' for Regexp' do + calculator.type(Regexp).class.should == Puppet::Pops::Types::PRegexpType end it 'should yield \'PArrayType[PDataType]\' for Array' do @@ -608,14 +809,24 @@ describe 'The type calculator' do calculator.string(Puppet::Pops::Types::PFloatType.new()).should == 'Float' end - it 'should yield \'Pattern\' for PPatternType' do - calculator.string(Puppet::Pops::Types::PPatternType.new()).should == 'Pattern' + it 'should yield \'Regexp\' for PRegexpType' do + calculator.string(Puppet::Pops::Types::PRegexpType.new()).should == 'Regexp' + end + + it 'should yield \'Regexp[/pat/]\' for parameterized PRegexpType' do + t = Puppet::Pops::Types::PRegexpType.new() + t.pattern = ('a/b') + calculator.string(Puppet::Pops::Types::PRegexpType.new()).should == 'Regexp' end it 'should yield \'String\' for PStringType' do calculator.string(Puppet::Pops::Types::PStringType.new()).should == 'String' end + it 'should yield \'String\' for PStringType with multiple values' do + calculator.string(string_t('a', 'b', 'c')).should == 'String' + end + it 'should yield \'Array[Integer]\' for PArrayType[PIntegerType]' do t = Puppet::Pops::Types::PArrayType.new() t.element_type = Puppet::Pops::Types::PIntegerType.new() @@ -657,6 +868,31 @@ describe 'The type calculator' do t.title = '/tmp/foo' calculator.string(t).should == "File['/tmp/foo']" end + + it "should yield 'Enum[s,...]' for a PEnumType[s,...]" do + t = enum_t('a', 'b', 'c') + calculator.string(t).should == "Enum['a', 'b', 'c']" + end + + it "should yield 'Pattern[/pat/,...]' for a PPatternType['pat',...]" do + t = pattern_t('a') + t2 = pattern_t('a', 'b', 'c') + calculator.string(t).should == "Pattern[/a/]" + calculator.string(t2).should == "Pattern[/a/, /b/, /c/]" + end + + it "should escape special characters in the string for a PPatternType['pat',...]" do + t = pattern_t('a/b') + calculator.string(t).should == "Pattern[/a\\/b/]" + end + + it "should yield 'Variant[t1,t2,...]' for a PVariantType[t1, t2,...]" do + t1 = string_t() + t2 = integer_t() + t3 = pattern_t('a') + t = variant_t(t1, t2, t3) + calculator.string(t).should == "Variant[String, Integer, Pattern[/a/]]" + end end context 'when processing meta type' do @@ -669,7 +905,7 @@ describe 'The type calculator' do calculator.infer(Puppet::Pops::Types::PNumericType.new() ).is_a?(ptype).should() == true calculator.infer(Puppet::Pops::Types::PIntegerType.new() ).is_a?(ptype).should() == true calculator.infer(Puppet::Pops::Types::PFloatType.new() ).is_a?(ptype).should() == true - calculator.infer(Puppet::Pops::Types::PPatternType.new() ).is_a?(ptype).should() == true + calculator.infer(Puppet::Pops::Types::PRegexpType.new() ).is_a?(ptype).should() == true calculator.infer(Puppet::Pops::Types::PBooleanType.new() ).is_a?(ptype).should() == true calculator.infer(Puppet::Pops::Types::PCollectionType.new()).is_a?(ptype).should() == true calculator.infer(Puppet::Pops::Types::PArrayType.new() ).is_a?(ptype).should() == true @@ -677,7 +913,9 @@ describe 'The type calculator' do calculator.infer(Puppet::Pops::Types::PRubyType.new() ).is_a?(ptype).should() == true calculator.infer(Puppet::Pops::Types::PHostClassType.new() ).is_a?(ptype).should() == true calculator.infer(Puppet::Pops::Types::PResourceType.new() ).is_a?(ptype).should() == true - calculator.string(calculator.infer(Puppet::Pops::Types::PIntegerType.new())).should == "Type[Integer]" + calculator.infer(Puppet::Pops::Types::PEnumType.new() ).is_a?(ptype).should() == true + calculator.infer(Puppet::Pops::Types::PPatternType.new() ).is_a?(ptype).should() == true + calculator.infer(Puppet::Pops::Types::PVariantType.new() ).is_a?(ptype).should() == true end it 'should infer PType as the type of all other types' do @@ -689,7 +927,7 @@ describe 'The type calculator' do calculator.string(calculator.infer(Puppet::Pops::Types::PNumericType.new() )).should == "Type[Numeric]" calculator.string(calculator.infer(Puppet::Pops::Types::PIntegerType.new() )).should == "Type[Integer]" calculator.string(calculator.infer(Puppet::Pops::Types::PFloatType.new() )).should == "Type[Float]" - calculator.string(calculator.infer(Puppet::Pops::Types::PPatternType.new() )).should == "Type[Pattern]" + calculator.string(calculator.infer(Puppet::Pops::Types::PRegexpType.new() )).should == "Type[Regexp]" calculator.string(calculator.infer(Puppet::Pops::Types::PBooleanType.new() )).should == "Type[Boolean]" calculator.string(calculator.infer(Puppet::Pops::Types::PCollectionType.new())).should == "Type[Collection]" calculator.string(calculator.infer(Puppet::Pops::Types::PArrayType.new() )).should == "Type[Array[?]]" @@ -697,6 +935,9 @@ describe 'The type calculator' do calculator.string(calculator.infer(Puppet::Pops::Types::PRubyType.new() )).should == "Type[Ruby[?]]" calculator.string(calculator.infer(Puppet::Pops::Types::PHostClassType.new() )).should == "Type[Class]" calculator.string(calculator.infer(Puppet::Pops::Types::PResourceType.new() )).should == "Type[Resource]" + calculator.string(calculator.infer(Puppet::Pops::Types::PEnumType.new() )).should == "Type[Enum]" + calculator.string(calculator.infer(Puppet::Pops::Types::PVariantType.new() )).should == "Type[Variant]" + calculator.string(calculator.infer(Puppet::Pops::Types::PPatternType.new() )).should == "Type[Pattern]" end it "computes the common type of PType's type parameter" do @@ -718,4 +959,21 @@ describe 'The type calculator' do calculator.infer(Puppet::Pops::Types::PType.new()).is_a?(Puppet::Pops::Types::PType).should() == true end end + + matcher :be_assignable_to do |type| + calc = Puppet::Pops::Types::TypeCalculator.new + + match do |actual| + calc.assignable?(type, actual) + end + + failure_message_for_should do |actual| + "#{calc.string(actual)} should be assignable to #{calc.string(type)}" + end + + failure_message_for_should_not do |actual| + "#{calc.string(actual)} is assignable to #{calc.string(type)} when it should not" + end + end + end diff --git a/spec/unit/pops/types/type_factory_spec.rb b/spec/unit/pops/types/type_factory_spec.rb index e4524d23b..682a97f1b 100644 --- a/spec/unit/pops/types/type_factory_spec.rb +++ b/spec/unit/pops/types/type_factory_spec.rb @@ -23,6 +23,18 @@ describe 'The type factory' do Puppet::Pops::Types::TypeFactory.pattern().class().should == Puppet::Pops::Types::PPatternType end + it 'regexp() returns PRegexpType' do + Puppet::Pops::Types::TypeFactory.regexp().class().should == Puppet::Pops::Types::PRegexpType + end + + it 'enum() returns PEnumType' do + Puppet::Pops::Types::TypeFactory.enum().class().should == Puppet::Pops::Types::PEnumType + end + + it 'variant() returns PVariantType' do + Puppet::Pops::Types::TypeFactory.variant().class().should == Puppet::Pops::Types::PVariantType + end + it 'literal() returns PLiteralType' do Puppet::Pops::Types::TypeFactory.literal().class().should == Puppet::Pops::Types::PLiteralType end diff --git a/spec/unit/pops/types/type_parser_spec.rb b/spec/unit/pops/types/type_parser_spec.rb index 9c6250ef7..e1b516cee 100644 --- a/spec/unit/pops/types/type_parser_spec.rb +++ b/spec/unit/pops/types/type_parser_spec.rb @@ -37,7 +37,6 @@ describe Puppet::Pops::Types::TypeParser do [ 'Object', 'Float', 'Collection', 'Data', 'CatalogEntry', 'Boolean', 'Float', 'Literal', 'Undef', 'Numeric', - 'Pattern' ].each do |name| it "does not support parameterizing unparameterized type <#{name}" do expect { parser.parse("#{name}[Integer]") }.to raise_unparameterized_error_for(name) From 8d1d883f2c3a184b344551dda26be36bfc8de135 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 25 Nov 2013 00:40:13 +0100 Subject: [PATCH 121/800] (#22363) Add alternative Visitor impl that may prove faster This adds an alternative Visitor implementation that may prove to be faster in certain situations. The current benchmarks are however too simplistic to give a good indication. It seems to be on par with the previous implementation for simple cases. It is worth keeping this implementation for later use with larger benchmarks. --- lib/puppet/pops/evaluator/evaluator_impl.rb | 5 +- lib/puppet/pops/parser/evaluating_parser.rb | 3 +- lib/puppet/pops/visitor.rb | 103 ++++++++++++++++++++ spec/unit/pops/benchmark_spec.rb | 28 ++++++ 4 files changed, 134 insertions(+), 5 deletions(-) diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index 0174448c8..ea2b0b057 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -34,7 +34,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl Issues = Puppet::Pops::Issues def initialize - @@eval_visitor ||= Puppet::Pops::Visitor.new(self, "eval", 1, 1) + @@eval_visitor ||= Puppet::Pops::Visitor.new(self, "eval", 1, 1) @@lvalue_visitor ||= Puppet::Pops::Visitor.new(self, "lvalue", 1, 1) @@assign_visitor ||= Puppet::Pops::Visitor.new(self, "assign", 3, 3) @@string_visitor ||= Puppet::Pops::Visitor.new(self, "string", 1, 1) @@ -320,9 +320,6 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # Handles binary expression where lhs and rhs are array/hash or numeric and operator is +, - , *, % / << >> # def eval_ArithmeticExpression(o, scope) - unless ARITHMETIC_OPERATORS.include?(o.operator) - fail(Issues::UNSUPPORTED_OPERATOR, o, {:operator => o.operator}) - end left, right = eval_BinaryExpression(o, scope) begin result = calculate(left, right, o.operator, o.left_expr, o.right_expr, scope) diff --git a/lib/puppet/pops/parser/evaluating_parser.rb b/lib/puppet/pops/parser/evaluating_parser.rb index dcaca1bee..d63db0f89 100644 --- a/lib/puppet/pops/parser/evaluating_parser.rb +++ b/lib/puppet/pops/parser/evaluating_parser.rb @@ -167,7 +167,8 @@ class Puppet::Pops::Parser::EvaluatingParser def evaluate(scope, model) return nil unless model - Puppet::Pops::Evaluator::EvaluatorImpl.new().evaluate(model, scope) + @@evaluator ||= Puppet::Pops::Evaluator::EvaluatorImpl.new() + @@evaluator.evaluate(model, scope) end def validator() diff --git a/lib/puppet/pops/visitor.rb b/lib/puppet/pops/visitor.rb index baae887f7..c35fa0b93 100644 --- a/lib/puppet/pops/visitor.rb +++ b/lib/puppet/pops/visitor.rb @@ -86,4 +86,107 @@ class Puppet::Pops::Visitor visit_this(receiver, thing, arg1, arg2, arg3) end + # This is an alternative implementation that separates the finding of method names + # (Cached in the Visitor2 class), and bound methods (in an inner Delegator class) that + # are cached for this receiver instance. This is based on micro benchmarks measuring that a send is slower + # that directly calling a bound method. + # Larger benchmark however show that the overhead is fractional. Additional (larger) tests may + # show otherwise. + # To use this class instead of the regular Visitor. + # @@the_visitor_c = Visitor2.new(...) + # @@the_visitor = @@the_visitor_c.instance(self) + # then visit with one of the Delegator's visit methods. + # + # Performance Note: there are still issues with this implementation (although cleaner) since it requires + # holding on to the first instance in order to compute respond_do?. This is required if the class + # is using method_missing? which cannot be computed by introspection of the class (which would be + # ideal). Another approach is to pre-scan all the available methods starting with the pattern for + # the visitor, scan the class, and just check if the class has this method. (This will not work + # for dispatch to methods that requires method missing. (Maybe that does not matter) + # Further experiments could try looking up unbound methods via the class, cloning and binding them + # instead of again looking them up with #method(name) + # Also note that this implementation does not check min/max args on each call - there was not much gain + # from skipping this. It is safe to skip, but produces less friendly errors if there is an error in the + # implementation. + # + class Visitor2 + attr_reader :receiver, :message, :min_args, :max_args, :cache + + def initialize(receiver, message, min_args=0, max_args=nil) + raise ArgumentError.new("receiver can not be nil") if receiver.nil? + raise ArgumentError.new("min_args must be >= 0") if min_args < 0 + raise ArgumentError.new("max_args must be >= min_args or nil") if max_args && max_args < min_args + + @receiver = receiver + @message = message + @min_args = min_args + @max_args = max_args + @cache = Hash.new + end + + def instance(receiver) + # Create a visitable instance for the receiver + Delegator.new(receiver, self) + end + + # Produce the name of the method to use + # @return [Symbol, nil] the method name symbol, or nil if there is no method to call for thing + # + def method_name_for(thing) + if method_name = @cache[thing.class] + return method_name + else + thing.class.ancestors().each do |ancestor| + method_name = :"#{@message}_#{ancestor.name.split(/::/).last}" + next unless receiver.respond_to?(method_name, true) + @cache[thing.class] = method_name + return method_name + end + end + end + + class Delegator + attr_reader :receiver, :visitor, :cache + def initialize(receiver, visitor) + @receiver = receiver + @visitor = visitor + @cache = Hash.new + end + + # Visit + def visit(thing, *args) + if method = @cache[thing.class] + return method.call(thing, *args) + else + method_name = visitor.method_name_for(thing) + method = receiver.method(method_name) + unless method + raise "Visitor Error: the configured receiver (#{receiver.class}) can't handle instance of: #{thing.class}" + end + @cache[thing.class] = method + method.call(thing, *args) + end + end + + # Visit an explicit receiver with 0 args + # (This is ~30% faster than calling the general method) + # + def visit_0(thing) + (method = @cache[thing.class]) ? method.call(thing) : visit(thing) + end + + def visit_1(thing, arg) + (method = @cache[thing.class]) ? method.call(thing, arg) : visit(thing, arg) + end + + def visit_2(thing, arg1, arg2) + (method = @cache[thing.class]) ? method.call(thing, arg1, arg2) : visit(thing, arg1, arg2) + end + + def visit_3(thing, arg1, arg2, arg3) + (method = @cache[thing.class]) ? method.call(thing, arg1, arg2, arg3) : visit(thing, arg1, arg2, arg3) + end + + end + end end diff --git a/spec/unit/pops/benchmark_spec.rb b/spec/unit/pops/benchmark_spec.rb index 96a21484f..ec699a672 100644 --- a/spec/unit/pops/benchmark_spec.rb +++ b/spec/unit/pops/benchmark_spec.rb @@ -1,12 +1,17 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/pops' +require 'puppet_spec/pops' +require 'puppet_spec/scope' + require 'rgen/environment' require 'rgen/metamodel_builder' require 'rgen/serializer/json_serializer' require 'rgen/instantiator/json_instantiator' describe "Benchmark", :benchmark => true do + include PuppetSpec::Pops + include PuppetSpec::Scope def code 'if true @@ -113,4 +118,27 @@ $a = "interpolate ${foo} and stuff" m = Benchmark.measure {10000.times {lexer.string = code; lexer.fullscan }} puts "Original Lexer: #{m}" end + + context "Measure Evaluator" do + let(:parser) { Puppet::Pops::Parser::EvaluatingParser::Transitional.new } + let(:node) { 'node.example.com' } + let(:scope) { s = create_test_scope_for_node(node); s } + it "evaluator", :profile => true do + # Do the loop in puppet code since it otherwise drowns in setup + puppet_loop = + 'Integer[0, 1000].each |$i| { if true +{ +$a = 10 + 10 +} +else +{ +$a = "interpolate ${foo} and stuff" +}} +' + # parse once, only measure the evaluation + model = parser.parse_string(puppet_loop, __FILE__) + m = Benchmark.measure { parser.evaluate(create_test_scope_for_node(node), model) } + puts("Evaluator: #{m}") + end + end end From 647a76acb0c3cb608c13406ca633b5cacb2a27ea Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 25 Nov 2013 01:12:21 +0100 Subject: [PATCH 122/800] (#22363) Add validation of case and hash. There were validation missing for case and case options (rvalue checks) Checks were added for hash entries (they must be rvalues) There were code to transform and dump the removed InstanceReferences Pops model object. This commit removes that dead code. --- lib/puppet/pops/model/ast_transformer.rb | 4 --- lib/puppet/pops/model/factory.rb | 6 ---- lib/puppet/pops/model/model_label_provider.rb | 1 - lib/puppet/pops/model/model_tree_dumper.rb | 4 --- lib/puppet/pops/validation/checker4_0.rb | 31 ++++++------------- 5 files changed, 10 insertions(+), 36 deletions(-) diff --git a/lib/puppet/pops/model/ast_transformer.rb b/lib/puppet/pops/model/ast_transformer.rb index acff56ff3..c52c1ffa0 100644 --- a/lib/puppet/pops/model/ast_transformer.rb +++ b/lib/puppet/pops/model/ast_transformer.rb @@ -281,10 +281,6 @@ class Puppet::Pops::Model::AstTransformer ast o, AST::Nop, {} end - def transform_InstanceReferences(o) - ast o, AST::ResourceReference, :type => o.type_name.value, :title => transform(o.names) - end - # Assignment in AST 3.1 is to variable or hasharray accesses !!! See Bug #16116 def transform_AssignmentExpression(o) args = {:value => transform(o.right_expr) } diff --git a/lib/puppet/pops/model/factory.rb b/lib/puppet/pops/model/factory.rb index 9319a7b45..0b9b82d22 100644 --- a/lib/puppet/pops/model/factory.rb +++ b/lib/puppet/pops/model/factory.rb @@ -180,12 +180,6 @@ class Puppet::Pops::Model::Factory o end - def build_InstanceReferences(o, type_name, name_expressions) - o.type_name = build(type_name) - name_expressions.each {|n| o.addNames(build(n)) } - o - end - def build_ImportExpression(o, files) # The argument files has already been built files.each {|f| o.addFiles(to_ops(f)) } diff --git a/lib/puppet/pops/model/model_label_provider.rb b/lib/puppet/pops/model/model_label_provider.rb index a55e69aaa..863a1f977 100644 --- a/lib/puppet/pops/model/model_label_provider.rb +++ b/lib/puppet/pops/model/model_label_provider.rb @@ -28,7 +28,6 @@ class Puppet::Pops::Model::ModelLabelProvider < Puppet::Pops::LabelProvider def label_OrExpression o ; "'or' expression" end def label_InExpression o ; "'in' expression" end def label_ImportExpression o ; "'import' expression" end - def label_InstanceReferences o ; "Resource Reference" end def label_AssignmentExpression o ; "'#{o.operator}' expression" end def label_AttributeOperation o ; "'#{o.operator}' expression" end def label_LiteralList o ; "Array Expression" end diff --git a/lib/puppet/pops/model/model_tree_dumper.rb b/lib/puppet/pops/model/model_tree_dumper.rb index 553021271..14ad52caf 100644 --- a/lib/puppet/pops/model/model_tree_dumper.rb +++ b/lib/puppet/pops/model/model_tree_dumper.rb @@ -94,10 +94,6 @@ class Puppet::Pops::Model::ModelTreeDumper < Puppet::Pops::Model::TreeDumper ["import"] + o.files.collect {|f| do_dump(f) } end - def dump_InstanceReferences o - ["instances", do_dump(o.type_name)] + o.names.collect {|n| do_dump(n) } - end - def dump_AssignmentExpression o [o.operator.to_s, do_dump(o.left_expr), do_dump(o.right_expr)] end diff --git a/lib/puppet/pops/validation/checker4_0.rb b/lib/puppet/pops/validation/checker4_0.rb index f4f7833e9..8ea140f1f 100644 --- a/lib/puppet/pops/validation/checker4_0.rb +++ b/lib/puppet/pops/validation/checker4_0.rb @@ -189,10 +189,15 @@ class Puppet::Pops::Validation::Checker4_0 end def check_CaseExpression(o) + rvalue(o.test) # There should only be one LiteralDefault case option value # TODO: Implement this check end + def check_CaseOption(o) + o.values.each { |v| rvalue(v) } + end + def check_CollectExpression(o) unless o.type_expr.is_a? Model::QualifiedReference acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.type_expr, :feature=> 'type name', :container => o) @@ -231,27 +236,11 @@ class Puppet::Pops::Validation::Checker4_0 end end - def check_InstanceReference(o) - # TODO: Original warning is : - # Puppet.warning addcontext("Deprecation notice: Resource references should now be capitalized") - # This model element is not used in the egrammar. - # Either implement checks or deprecate the use of InstanceReference (the same is acheived by - # transformation of AccessExpression when used where an Instance/Resource reference is allowed. - # - end - - # Restrictions on hash key are because of the strange key comparisons/and merge rules in the AST evaluation - # (Even the allowed ones are handled in a strange way). - # - def transform_KeyedEntry(o) - case o.key - when Model::QualifiedName - when Model::LiteralString - when Model::LiteralNumber - when Model::ConcatenatedString - else - acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.key, :feature => 'hash key', :container => o.eContainer) - end + def check_KeyedEntry(o) + rvalue(o.key) + rvalue(o.value) + # In case there are additional things to forbid than non-rvalues + # acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.key, :feature => 'hash key', :container => o.eContainer) end # A Lambda is a Definition, but it may appear in other scopes that top scope (Which check_Definition asserts). From 1f31e7ef6426ca27f9f403c3d8f40f66b5f5b9f0 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 25 Nov 2013 01:32:13 +0100 Subject: [PATCH 123/800] (#22363) Add additional rvalue checks to if, unless, and literal list This also includes some cleanup and spelling corrections. --- lib/puppet/pops/validation/checker4_0.rb | 41 +++++++++--------------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/lib/puppet/pops/validation/checker4_0.rb b/lib/puppet/pops/validation/checker4_0.rb index 8ea140f1f..698b95645 100644 --- a/lib/puppet/pops/validation/checker4_0.rb +++ b/lib/puppet/pops/validation/checker4_0.rb @@ -122,27 +122,10 @@ class Puppet::Pops::Validation::Checker4_0 end def check_AccessExpression(o) - # Check multiplicity of keys - case o.left_expr - when Model::QualifiedName - # allows many keys, but the name should really be a QualifiedReference - # acceptor.accept(Issues::DEPRECATED_NAME_AS_TYPE, o, :name => o.value) - # OK in 4.x since a name evaluates to a String, and String[] is supported - # Also allows many - when Model::QualifiedReference - # ok, allows many - this is a type / resource reference - - else - # i.e. for any other expression that may produce an array or hash -# if o.keys.size > 1 -# acceptor.accept(Issues::UNSUPPORTED_RANGE, o, :count => o.keys.size) -# end - # Range is ok, multiple keys supported by many LHS types (checked at runtime) - # TODO: can statically check key size for some types Name & String 1-2, Array 1,2, Hash 1-many, etc - # (but is captured as RT error) - if o.keys.size < 1 - acceptor.accept(Issues::MISSING_INDEX, o) - end + # Only min range is checked, all other checks are RT checks as they depend on the resulting type + # of the LHS. + if o.keys.size < 1 + acceptor.accept(Issues::MISSING_INDEX, o) end end @@ -228,6 +211,10 @@ class Puppet::Pops::Validation::Checker4_0 end end + def check_IfExpression(o) + rvalue(o.test) + end + def check_ImportExpression(o) o.files.each do |f| unless f.is_a? Model::LiteralString @@ -243,13 +230,17 @@ class Puppet::Pops::Validation::Checker4_0 # acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.key, :feature => 'hash key', :container => o.eContainer) end - # A Lambda is a Definition, but it may appear in other scopes that top scope (Which check_Definition asserts). + # A Lambda is a Definition, but it may appear in other scopes than top scope (Which check_Definition asserts). # def check_LambdaExpression(o) end + def check_LiteralList(o) + o.values.each {|v| rvalue(v) } + end + def check_NodeDefinition(o) - # Check that hostnames are valid hostnames (or regular expressons) + # Check that hostnames are valid hostnames (or regular expressions) hostname(o.host_matches, o) hostname(o.parent, o, 'parent') unless o.parent.nil? top(o.eContainer, o) @@ -360,8 +351,8 @@ class Puppet::Pops::Validation::Checker4_0 end def check_UnlessExpression(o) - # TODO: Unless may not have an elsif - # TODO: 3.x unless may not have an else + rvalue(o.test) + # TODO: Unless may not have an else part that is an IfExpression (grammar denies this though) end def check_VariableExpression(o) From ba4ced2ed6de72c46303c00493b45ac255f8a0fe Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 25 Nov 2013 03:02:32 +0100 Subject: [PATCH 124/800] (#22366) Fix model building of CollectExpression There was a mistake in the COLLECT factory method. It assumed that if it did not get a QualifiedReference it should build one out of its argument turned into a string. This was wrong since it gets prebuilt model objects. The result was that the CollectExpression ended up with a classname on the form puppet::pops::model::literalinteger<54875847584> which is clearly wrong. As a result a confusing error message was printed that that node did not exists, or that the horribly wrong name was an invalid name. With this fix, the CollectExpression is built using the parsed expression, and since it is wrong, validation kicks in and provides output that is meaningful. --- lib/puppet/pops/model/factory.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/puppet/pops/model/factory.rb b/lib/puppet/pops/model/factory.rb index 0b9b82d22..c224a0ba0 100644 --- a/lib/puppet/pops/model/factory.rb +++ b/lib/puppet/pops/model/factory.rb @@ -635,7 +635,8 @@ class Puppet::Pops::Model::Factory end def self.COLLECT(type_expr, query_expr, attribute_operations) - new(Model::CollectExpression, Puppet::Pops::Model::Factory.fqr(type_expr), query_expr, attribute_operations) + new(Model::CollectExpression, type_expr, query_expr, attribute_operations) +# new(Model::CollectExpression, Puppet::Pops::Model::Factory.fqr(type_expr), query_expr, attribute_operations) end def self.IMPORT(files) From 1fa7110b2e80a7566ba75e766c5214e990cd9c4b Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 25 Nov 2013 03:26:11 +0100 Subject: [PATCH 125/800] (#22365) Add validation of select expression The rvalue checks for select expression and select options where missing (they were indeed faulty because of a method name typo). --- lib/puppet/pops/validation/checker4_0.rb | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/lib/puppet/pops/validation/checker4_0.rb b/lib/puppet/pops/validation/checker4_0.rb index 698b95645..f87f64e8f 100644 --- a/lib/puppet/pops/validation/checker4_0.rb +++ b/lib/puppet/pops/validation/checker4_0.rb @@ -333,17 +333,12 @@ class Puppet::Pops::Validation::Checker4_0 end end - # Transformation of SelectorExpression is limited to certain types of expressions. - # This is probably due to constraints in the old grammar rather than any real concerns. - def select_SelectorExpression(o) - case o.left_expr - when Model::CallNamedFunctionExpression - when Model::AccessExpression - when Model::VariableExpression - when Model::ConcatenatedString - else - acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.left_expr, :feature => 'left operand', :container => o) - end + def check_SelectorExpression(o) + rvalue(o.left_expr) + end + + def check_SelectorEntry(o) + rvalue(o.matching_expr) end def check_UnaryExpression(o) From f9343fded802b2edcd50ccb1699b5c0743ca86b0 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 25 Nov 2013 04:05:49 +0100 Subject: [PATCH 126/800] (#22363) Improve error messages for division by 0 and infinity If a div by zero was performed (integer operation) this resulted in a runtime error with a wrapped exception. Now this is handled with a proper error message. Ruby (in its wisdom) produces Infinity when floating point numbers over/underflow as they do when dividing by 0. This is now handled as an error with a proper error message. --- lib/puppet/pops/evaluator/evaluator_impl.rb | 10 ++++++++++ lib/puppet/pops/issues.rb | 7 +++++++ lib/puppet/pops/model/ast_transformer.rb | 7 +++++++ 3 files changed, 24 insertions(+) diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index ea2b0b057..b12fc283b 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -28,6 +28,11 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # include Puppet::Pops::Evaluator::Runtime3Support + # This constant is not defined as Float::INFINITY in Ruby 1.8.7 (but is available in later version + # Refactor when support is dropped for Ruby 1.8.7. + # + INFINITY = 1.0 / 0.0 + # Reference to Issues name space makes it easier to refer to issues # (Issues are shared with the validator). # @@ -362,6 +367,11 @@ class Puppet::Pops::Evaluator::EvaluatorImpl result = left.send(operator, right) rescue NoMethodError => e fail(Issues::OPERATOR_NOT_APPLICABLE, left_o, {:operator => operator, :left_value => left}) + rescue ZeroDivisionError => e + fail(Issues::DIV_BY_ZERO, right_o) + end + if result == INFINITY || result == -INFINITY + fail(Issues::RESULT_IS_INFINITY, left_o, {:operator => operator}) end result end diff --git a/lib/puppet/pops/issues.rb b/lib/puppet/pops/issues.rb index 4337708b7..3c65d495b 100644 --- a/lib/puppet/pops/issues.rb +++ b/lib/puppet/pops/issues.rb @@ -389,4 +389,11 @@ module Puppet::Pops::Issues "The resource #{type_name.capitalize}['#{title}'] does not have a parameter called '#{param_name}'" end + DIV_BY_ZERO = hard_issue :DIV_BY_ZERO do + "Division by 0" + end + + RESULT_IS_INFINITY = hard_issue :RESULT_IS_INFINITY, :operator do + "The result of the #{operator} expression is Infinity" + end end diff --git a/lib/puppet/pops/model/ast_transformer.rb b/lib/puppet/pops/model/ast_transformer.rb index c52c1ffa0..1a64b5195 100644 --- a/lib/puppet/pops/model/ast_transformer.rb +++ b/lib/puppet/pops/model/ast_transformer.rb @@ -55,7 +55,14 @@ class Puppet::Pops::Model::AstTransformer # Transforms pops expressions into AST 3.1 statements/expressions def transform(o) + begin @@transform_visitor.visit_this(self,o) + rescue StandardError => e + loc_data = {} + merge_location(loc_data, o) + raise Puppet::ParseError.new("Error while transforming to Puppet 3 AST: #{e.message}", + loc_data[:file], loc_data[:line], loc_data[:pos], e) + end end # Transforms pops expressions into AST 3.1 query expressions From 20ae071c331b73cf29f12278238929fc1ec823fa Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 25 Nov 2013 04:36:06 +0100 Subject: [PATCH 127/800] (#22363) Produce meaningful output for all runtime errors This adds a catch around the evaluate method to ensure that generic errors fail with meaningful information. --- lib/puppet/pops/evaluator/evaluator_impl.rb | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index b12fc283b..41e9621d7 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -75,7 +75,14 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # @api # def evaluate(target, scope) - @@eval_visitor.visit_this_1(self, target, scope) + begin + @@eval_visitor.visit_this_1(self, target, scope) + rescue StandardError => e + if e.is_a? Puppet::ParseError + raise e + end + fail(Issues::RUNTIME_ERROR, target, {:detail => e.message}, e) + end end # Polymorphic assign - calls assign_TYPE From c64c1c110894ec79d28c986317ccaa9b123c82a8 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 26 Nov 2013 01:33:46 +0100 Subject: [PATCH 128/800] (maint) Error with file set to "" should be taked as file is not known Now, if tests set "" as the filename, the formatting is off (The expected "at line", does not appear, instead it becomes ":l:p" where l is the linenumber and pos the position as if it was preceded by a file name). --- lib/puppet/error.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/puppet/error.rb b/lib/puppet/error.rb index 34da17219..c31c360a2 100644 --- a/lib/puppet/error.rb +++ b/lib/puppet/error.rb @@ -23,12 +23,13 @@ module Puppet pos = nil end super(message, original) - @file = file + @file = file unless (file.is_a?(String) && file.empty?) @line = line @pos = pos end def to_s msg = super + @file = nil if (@file.is_a?(String) && @file.empty?) if @file and @line and @pos "#{msg} at #{@file}:#{@line}:#{@pos}" elsif @file and @line From 6921bcb31a46fd908fe998b805aab7f878061734 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Wed, 27 Nov 2013 00:31:01 +0100 Subject: [PATCH 129/800] (#22363) Fix problems with undef equality & problems from added tests Tests from the parser integration tests were added to the unit tests for the new evaluator (they were not very good integration tests). The old test ran for both "current" and "future" and now only run for "current". The relevant tests were moved to the evaluator unit tests. This showed several problems in the PopsBridge (typos) for yet unexercised code paths. These are now fixed. As the integration/parser/compiler_spec tests were being investigated to run also for the new parser, problems were found with comparison against literal undef. Tests were added for this in the evaluator unit test and the problem corrected (undef compared as :undef == nil => false which is not the intention. Comparsions :undef == nil, and nil == :undef are now done correctly. Exceptions did not show up correctly when file is empty string. This also fixes the logic where it is checked if a file is known (nil and empty string are now considered to be "file unknown" instead of file "" known. --- lib/puppet/parser/ast/pops_bridge.rb | 2 +- lib/puppet/pops/evaluator/compare_operator.rb | 8 ++ lib/puppet/pops/parser/evaluating_parser.rb | 3 +- lib/puppet/pops/validation.rb | 3 +- spec/integration/parser/compiler_spec.rb | 14 +-- spec/integration/parser/parser_spec.rb | 103 ------------------ .../pops/evaluator/evaluating_parser_spec.rb | 83 +++++++++++++- 7 files changed, 92 insertions(+), 124 deletions(-) diff --git a/lib/puppet/parser/ast/pops_bridge.rb b/lib/puppet/parser/ast/pops_bridge.rb index 021668489..022b6bd82 100644 --- a/lib/puppet/parser/ast/pops_bridge.rb +++ b/lib/puppet/parser/ast/pops_bridge.rb @@ -112,7 +112,7 @@ class Puppet::Parser::AST::PopsBridge :module_name => modname } unless is_nop?(o.body) - args[:code] = Puppet::AST::Bridge::PopsBridge::Expression.new(:value => o.body) + args[:code] = Expression.new(:value => o.body) end @ast_transformer.merge_location(args, o) end diff --git a/lib/puppet/pops/evaluator/compare_operator.rb b/lib/puppet/pops/evaluator/compare_operator.rb index f9d0e4146..df5730d17 100644 --- a/lib/puppet/pops/evaluator/compare_operator.rb +++ b/lib/puppet/pops/evaluator/compare_operator.rb @@ -109,6 +109,14 @@ class Puppet::Pops::Evaluator::CompareOperator a == b end + def equals_NilClass(a, b) + b.nil? || b == :undef + end + + def equals_Symbol(a, b) + a == b || a == :undef && b.nil? + end + def include_Object(a, b) false end diff --git a/lib/puppet/pops/parser/evaluating_parser.rb b/lib/puppet/pops/parser/evaluating_parser.rb index d63db0f89..d8d69c67f 100644 --- a/lib/puppet/pops/parser/evaluating_parser.rb +++ b/lib/puppet/pops/parser/evaluating_parser.rb @@ -19,7 +19,8 @@ class Puppet::Pops::Parser::EvaluatingParser begin assert_and_report(@parser.parse_string(s)) rescue Puppet::ParseError => e - e.file = @file_source unless e.file + # TODO: This is not quite right, why does not the exception have the correct file? + e.file = @file_source unless e.file.is_a?(String) && !e.file.empty? raise e end end diff --git a/lib/puppet/pops/validation.rb b/lib/puppet/pops/validation.rb index b58916c1c..38976a9dd 100644 --- a/lib/puppet/pops/validation.rb +++ b/lib/puppet/pops/validation.rb @@ -254,6 +254,7 @@ module Puppet::Pops::Validation def format_location diagnostic file = diagnostic.file + file = (file.is_a?(String) && file.empty?) ? nil : file line = pos = nil if diagnostic.source_pos line = diagnostic.source_pos.line @@ -287,7 +288,7 @@ module Puppet::Pops::Validation # have to be used here for backwards compatibility. def format_location diagnostic file = diagnostic.file - file = file.is_a?(String) && file.empty? ? nil : file + file = (file.is_a?(String) && file.empty?) ? nil : file line = pos = nil if diagnostic.source_pos line = diagnostic.source_pos.line diff --git a/spec/integration/parser/compiler_spec.rb b/spec/integration/parser/compiler_spec.rb index eb87d03ed..9ebd2dda9 100755 --- a/spec/integration/parser/compiler_spec.rb +++ b/spec/integration/parser/compiler_spec.rb @@ -138,7 +138,7 @@ describe "Puppet::Parser::Compiler" do end ['class', 'define', 'node'].each do |thing| - it "should not allow #{thing} inside evaluated conditional constructs" do + it "should not allow '#{thing}' inside evaluated conditional constructs" do Puppet[:code] = <<-PP if true { #{thing} foo { @@ -409,16 +409,4 @@ describe "Puppet::Parser::Compiler" do it_behaves_like 'the compiler' do end end - - describe 'using future parser' do - # have absolutely no clue to why this is needed - if not required here (even if required by used classes) - # the tests will fail with error that rgen/ecore/ruby_to_ecore cannot be found... - # TODO: Solve this mystery ! - require 'rgen/metamodel_builder' - - before :each do - Puppet[:parser] = 'future' - end - it_behaves_like 'the compiler' - end end diff --git a/spec/integration/parser/parser_spec.rb b/spec/integration/parser/parser_spec.rb index b3920c9a4..09c90de8e 100755 --- a/spec/integration/parser/parser_spec.rb +++ b/spec/integration/parser/parser_spec.rb @@ -162,107 +162,4 @@ describe "Puppet::Parser::Parser" do it_behaves_like 'a puppet parser' end - describe 'using future parser' do - before :each do - Puppet[:parser] = 'future' - end - it_behaves_like 'a puppet parser' - - context 'more detailed errors should be generated' do - before :each do - Puppet[:parser] = 'future' - @resource_type_collection = Puppet::Resource::TypeCollection.new("env") - @parser = Puppet::Parser::ParserFactory.parser("development") - end - - it 'should flag illegal type references' do - source = <<-SOURCE.gsub(/^ {8}/,'') - 1+1 { "title": } - SOURCE - # This error message is currently produced by the parser, and is not as detailed as desired - # It references position 16 at the closing '}' - expect { @parser.parse(source) }.to raise_error(/Expression is not valid as a resource.*line 1:16/) - end - - it 'should flag illegal type references and get position correct' do - source = <<-SOURCE.gsub(/^ {8}/,'') - 1+1 { "title": - } - SOURCE - # This error message is currently produced by the parser, and is not as detailed as desired - # It references position 16 at the closing '}' - expect { @parser.parse(source) }.to raise_error(/Expression is not valid as a resource.*line 2:3/) - end - - it 'should flag illegal use of non r-value producing if' do - source = <<-SOURCE.gsub(/^ {8}/,'') - $a = if true { - false - } - SOURCE - expect { @parser.parse(source) }.to raise_error(/An 'if' statement does not produce a value at line 1:6/) - end - - it 'should flag illegal use of non r-value producing case' do - source = <<-SOURCE.gsub(/^ {8}/,'') - $a = case true { - false :{ } - } - SOURCE - expect { @parser.parse(source) }.to raise_error(/A 'case' statement does not produce a value at line 1:6/) - end - - it 'should flag illegal use of non r-value producing <| |>' do - expect { @parser.parse("$a = File <| |>") }.to raise_error(/A Virtual Query does not produce a value at line 1:6/) - end - - it 'should flag illegal use of non r-value producing <<| |>>' do - expect { @parser.parse("$a = File <<| |>>") }.to raise_error(/An Exported Query does not produce a value at line 1:6/) - end - - it 'should flag illegal use of non r-value producing define' do - Puppet.expects(:err).with("Invalid use of expression. A 'define' expression does not produce a value at line 1:6") - Puppet.expects(:err).with("Classes, definitions, and nodes may only appear at toplevel or inside other classes at line 1:6") - expect { @parser.parse("$a = define foo { }") }.to raise_error(/2 errors/) - end - - it 'should flag illegal use of non r-value producing class' do - Puppet.expects(:err).with("Invalid use of expression. A Host Class Definition does not produce a value at line 1:6") - Puppet.expects(:err).with("Classes, definitions, and nodes may only appear at toplevel or inside other classes at line 1:6") - expect { @parser.parse("$a = class foo { }") }.to raise_error(/2 errors/) - end - - it 'unclosed quote should be flagged for start position of string' do - source = <<-SOURCE.gsub(/^ {8}/,'') - $a = "xx - yyy - SOURCE - expect { @parser.parse(source) }.to raise_error(/Unclosed quote after '"' followed by 'xx\\nyy\.\.\.' at line 1:6/) - end - - it 'can produce multiple errors and raise a summary exception' do - source = <<-SOURCE.gsub(/^ {8}/,'') - $a = node x { } - SOURCE - Puppet.expects(:err).with("Invalid use of expression. A Node Definition does not produce a value at line 1:6") - Puppet.expects(:err).with("Classes, definitions, and nodes may only appear at toplevel or inside other classes at line 1:6") - expect { @parser.parse(source) }.to raise_error(/2 errors/) - end - - it 'can produce detailed error for a bad hostname' do - source = <<-SOURCE.gsub(/^ {8}/,'') - node 'macbook+owned+by+name' { } - SOURCE - expect { @parser.parse(source) }.to raise_error(/The hostname 'macbook\+owned\+by\+name' contains illegal characters.*at line 1:6/) - end - - it 'can produce detailed error for a hostname with interpolation' do - source = <<-SOURCE.gsub(/^ {8}/,'') - $name = 'fred' - node "macbook-owned-by$name" { } - SOURCE - expect { @parser.parse(source) }.to raise_error(/An interpolated expression is not allowed in a hostname of a node at line 2:23/) - end - end - end end diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index 47b016863..a4eb38637 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -493,11 +493,12 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do end { - "{a=>1, b=>2, c=>3}[a]" => 1, - "{a=>1, b=>2, c=>3}[c]" => 3, - "{a=>1, b=>2, c=>3}[x]" => nil, - "{a=>1, b=>2, c=>3}[c,b]" => [3,2], - "{a=>1, b=>2, c=>3}[a,b,c]" => [1,2,3], + "{a=>1, b=>2, c=>3}[a]" => 1, + "{a=>1, b=>2, c=>3}[c]" => 3, + "{a=>1, b=>2, c=>3}[x]" => nil, + "{a=>1, b=>2, c=>3}[c,b]" => [3,2], + "{a=>1, b=>2, c=>3}[a,b,c]" => [1,2,3], + "{a=>{b=>{c=>'it works'}}}[a][b][c]" => 'it works' }.each do |source, result| it "should parse and evaluate the expression '#{source}' to #{result}" do parser.evaluate_string(scope, source, __FILE__).should == result @@ -653,6 +654,13 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do end end + context "When evaluator performs operations on literal undef" do + it "computes non existing hash lookup as undef" do + parser.evaluate_string(scope, "{a => 1}[b] == undef", __FILE__).should == true + parser.evaluate_string(scope, "undef == {a => 1}[b]", __FILE__).should == true + end + end + context "When evaluator performs calls" do let(:populate) do parser.evaluate_string(scope, "$a = 10 $b = [1,2,3]") @@ -736,6 +744,10 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do expect { parser.evaluate_string(scope, "$quantum_gravity::graviton", __FILE__) }.to raise_error(/Unknown variable/) end end + + it "a lex error should be raised for '$foo::::bar'" do + expect { parser.evaluate_string(scope, "$foo::::bar") }.to raise_error(Puppet::LexError, /Illegal fully qualified name at line 1:7/) + end end context "When evaluating relationships" do @@ -758,6 +770,67 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do end end + context "Detailed Error messages are reported" do + it 'for illegal type references' do + source = '1+1 { "title": }' + # Error references position 5 at the opening '{' + # Set file to nil to make it easier to match with line number (no file name in output) + expect { parser.parse_string(source, nil) }.to raise_error(/Expression is not valid as a resource.*line 1:5/) + end + + it 'for non r-value producing <| |>' do + expect { parser.parse_string("$a = File <| |>", nil) }.to raise_error(/A Virtual Query does not produce a value at line 1:6/) + end + + it 'for non r-value producing <<| |>>' do + expect { parser.parse_string("$a = File <<| |>>", nil) }.to raise_error(/An Exported Query does not produce a value at line 1:6/) + end + + it 'for non r-value producing define' do + Puppet.expects(:err).with("Invalid use of expression. A 'define' expression does not produce a value at line 1:6") + Puppet.expects(:err).with("Classes, definitions, and nodes may only appear at toplevel or inside other classes at line 1:6") + expect { parser.parse_string("$a = define foo { }", nil) }.to raise_error(/2 errors/) + end + + it 'for non r-value producing class' do + Puppet.expects(:err).with("Invalid use of expression. A Host Class Definition does not produce a value at line 1:6") + Puppet.expects(:err).with("Classes, definitions, and nodes may only appear at toplevel or inside other classes at line 1:6") + expect { parser.parse_string("$a = class foo { }", nil) }.to raise_error(/2 errors/) + end + + it 'for unclosed quote with indication of start position of string' do + source = <<-SOURCE.gsub(/^ {6}/,'') + $a = "xx + yyy + SOURCE + # first char after opening " reported as being in error. + expect { parser.parse_string(source) }.to raise_error(/Unclosed quote after '"' followed by 'xx\\nyy\.\.\.' at line 1:7/) + end + + it 'for multiple errors with a summary exception' do + Puppet.expects(:err).with("Invalid use of expression. A Node Definition does not produce a value at line 1:6") + Puppet.expects(:err).with("Classes, definitions, and nodes may only appear at toplevel or inside other classes at line 1:6") + expect { parser.parse_string("$a = node x { }",nil) }.to raise_error(/2 errors/) + end + + it 'for a bad hostname' do + expect { + parser.parse_string("node 'macbook+owned+by+name' { }", nil) + }.to raise_error(/The hostname 'macbook\+owned\+by\+name' contains illegal characters.*at line 1:6/) + end + + it 'for a hostname with interpolation' do + source = <<-SOURCE.gsub(/^ {6}/,'') + $name = 'fred' + node "macbook-owned-by$name" { } + SOURCE + expect { + parser.parse_string(source, nil) + }.to raise_error(/An interpolated expression is not allowed in a hostname of a node at line 2:23/) + end + + end + matcher :have_relationship do |expected| calc = Puppet::Pops::Types::TypeCalculator.new From 6c4f052f396b2a105909b018f0a0ada75113b3ae Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 28 Nov 2013 19:40:10 +0100 Subject: [PATCH 130/800] (#22363) Make queries work with future evaluator There were several problems with the query (Collector) implementation. The CollExpr matching logic used Ruby == and Ruby include? instead of Puppet Operators. This meant that numeric values in string vs. numeric form were not comparable. This is now changed by using an alternative implementation of CollExpr evaluate when Puppet[:parser] == future. The relationship operator implementation did not support handling a Collector (the result of evaluating a CollectExpression). The 3x compiler is responsible for late evaluation of the Collector, and a relationship formed with a Collector was not implemented. Now the Collector is passed verbatim as operand in a Relationship, and passed on to the compiler. --- lib/puppet/parser/ast/collexpr.rb | 57 ++++++++++++++++++- lib/puppet/pops/evaluator/evaluator_impl.rb | 13 +++-- .../pops/evaluator/relationship_operator.rb | 17 +++++- lib/puppet/pops/evaluator/runtime3_support.rb | 24 ++++++-- spec/integration/parser/compiler_spec.rb | 1 + 5 files changed, 98 insertions(+), 14 deletions(-) diff --git a/lib/puppet/parser/ast/collexpr.rb b/lib/puppet/parser/ast/collexpr.rb index 6032094ab..5a80f9684 100644 --- a/lib/puppet/parser/ast/collexpr.rb +++ b/lib/puppet/parser/ast/collexpr.rb @@ -8,8 +8,16 @@ class Puppet::Parser::AST class CollExpr < AST::Branch attr_accessor :test1, :test2, :oper, :form, :type, :parens - # We return an object that does a late-binding evaluation. def evaluate(scope) + if Puppet[:parser] == 'future' + evaluate4x(scope) + else + evaluate3x(scope) + end + end + + # We return an object that does a late-binding evaluation. + def evaluate3x(scope) # Make sure our contained expressions have all the info they need. [@test1, @test2].each do |t| if t.is_a?(self.class) @@ -37,6 +45,7 @@ class CollExpr < AST::Branch if resource[match1].is_a?(Array) resource[match1].include?(match2) else + require 'debugger'; debugger resource[match1] == match2 end end @@ -48,9 +57,53 @@ class CollExpr < AST::Branch return match, code end + # Late binding evaluation of a collect expression (as done in 3x), but with proper Puppet Langauge + # semantics for equals and include + # + def evaluate4x(scope) + # Make sure our contained expressions have all the info they need. + [@test1, @test2].each do |t| + if t.is_a?(self.class) + t.form ||= self.form + t.type ||= self.type + end + end + + # The code is only used for virtual lookups + match1, code1 = @test1.safeevaluate scope + match2, code2 = @test2.safeevaluate scope + + # First build up the virtual code. + # If we're a conjunction operator, then we're calling code. I did + # some speed comparisons, and it's at least twice as fast doing these + # case statements as doing an eval here. + code = proc do |resource| + case @oper + when "and"; code1.call(resource) and code2.call(resource) + when "or"; code1.call(resource) or code2.call(resource) + when "==" + if match1 == "tag" + resource.tagged?(match2) + else + if resource[match1].is_a?(Array) + @@compare_operator.include?(resource[match1], match2) + else + @@compare_operator.equals(resource[match1], match2) + end + end + when "!="; ! @@compare_operator.equals(resource[match1], match2) + end + end + + match = [match1, @oper, match2] + return match, code + end + def initialize(hash = {}) super - + if Puppet[:parser] == "future" + @@compare_operator ||= Puppet::Pops::Evaluator::CompareOperator.new + end raise ArgumentError, "Invalid operator #{@oper}" unless %w{== != and or}.include?(@oper) end end diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index 41e9621d7..16e6555e0 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -593,12 +593,13 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # below carries forward. # collect_3x = Puppet::Pops::Model::AstTransformer.new().transform(o) - collect_3x.evaluate(scope) - # the 3x returns an instance of Collector (but it is only registered with the compiler at this - # point and does not contain any valuable information (like the result, count of the result etc.) - # Ensure that this object does not leak to the Puppet Program being evaluated. - # - nil + collected = collect_3x.evaluate(scope) + # the 3x returns an instance of Parser::Collector (but it is only registered with the compiler at this + # point and does not contain any valuable information (like the result) + # Dilemma: If this object is returned, it is a first class value in the Puppet Language and we + # need to be able to perform operations on it. We can forbid it from leaking by making CollectExpression + # a non R-value. This makes it possible for the evaluator logic to make use of the Collector. + collected end def eval_ParenthesizedExpression(o, scope) diff --git a/lib/puppet/pops/evaluator/relationship_operator.rb b/lib/puppet/pops/evaluator/relationship_operator.rb index 83a12a5ad..e81a22b23 100644 --- a/lib/puppet/pops/evaluator/relationship_operator.rb +++ b/lib/puppet/pops/evaluator/relationship_operator.rb @@ -50,7 +50,7 @@ class Puppet::Pops::Evaluator::RelationshipOperator # A string must be a type reference in string format # @api private def transform_String(o, scope) - assert_catalog_type(@type_parser.parse(o.value)) + assert_catalog_type(@type_parser.parse(o), scope) end # A qualified name is short hand for a class with this name @@ -65,6 +65,16 @@ class Puppet::Pops::Evaluator::RelationshipOperator assert_catalog_type(o, scope) end + # This transforms a 3x Collector (the result of evaluating a 3x AST::Collection). + # It is passed through verbatim since it is evaluated late by the compiler. At the point + # where the relationship is evaluated, it is simply recorded with the compiler for later evaluation. + # If one of the sides of the relationship is a Collector it is evaluated before the actual + # relationship is formed. (All of this happens at a later point in time. + # + def transform_Collector(o, scope) + o + end + # Asserts (and returns) the type if it is a PCatalogEntryType # (A PCatalogEntryType is the base class of PHostClassType, and PResourceType). # @@ -110,9 +120,12 @@ class Puppet::Pops::Evaluator::RelationshipOperator # since inference needs to visit each object each time, and this is what the transformation does anyway). # # real is [left, right], and both the left and right may be a single value or an array. In each case all content - # should be flattened, and then transformed to a type. + # should be flattened, and then transformed to a type. left or right may also be a value that is transformed + # into an array, and thus the resulting left and right must be flattened individually # real = left_right_evaluated.collect {|x| [x].flatten.collect {|x| transform(x, scope) }} + real[0].flatten! + real[1].flatten! # reverse order if operator is Right to Left source, target = reverse_operator?(relationship_expression) ? real.reverse : real diff --git a/lib/puppet/pops/evaluator/runtime3_support.rb b/lib/puppet/pops/evaluator/runtime3_support.rb index 3ca499ef2..35228977c 100644 --- a/lib/puppet/pops/evaluator/runtime3_support.rb +++ b/lib/puppet/pops/evaluator/runtime3_support.rb @@ -139,10 +139,26 @@ module Puppet::Pops::Evaluator::Runtime3Support # TODO: logic that creates a PCatalogEntryType should resolve it to ensure it is loaded (to the best of known_resource_types knowledge). # If this is not done, the order in which things are done may be different? OTOH, it probably works anyway :-) # - type, title = catalog_type_to_split_type_title(source) - source_resource = Puppet::Resource.new(type, title) - type, title = catalog_type_to_split_type_title(target) - target_resource = Puppet::Resource.new(type, title) + # And if that is not enough, a source/target may be a Collector (a baked query that will be evaluated by the + # compiler - it is simply passed through here for processing by the compiler at the right time). + # + if source.is_a?(Puppet::Parser::Collector) + # use verbatim - behavior defined by 3x + source_resource = source + else + # transform into the wonderful String representation in 3x + type, title = catalog_type_to_split_type_title(source) + source_resource = Puppet::Resource.new(type, title) + end + if target.is_a?(Puppet::Parser::Collector) + # use verbatim - behavior defined by 3x + target_resource = target + else + # transform into the wonderful String representation in 3x + type, title = catalog_type_to_split_type_title(target) + target_resource = Puppet::Resource.new(type, title) + end + # Add the relationship to the compiler for later evaluation. scope.compiler.add_relationship(Puppet::Parser::Relationship.new(source_resource, target_resource, relationship_type)) end diff --git a/spec/integration/parser/compiler_spec.rb b/spec/integration/parser/compiler_spec.rb index 9ebd2dda9..ccfc4546a 100755 --- a/spec/integration/parser/compiler_spec.rb +++ b/spec/integration/parser/compiler_spec.rb @@ -230,6 +230,7 @@ describe "Puppet::Parser::Compiler" do end it "should create relationships using collection expressions" do + require 'debugger'; debugger code << "File <| mode == 0644 |> -> File <| mode == 0755 |>" expected_relationships.concat [ From ab509d9fe3ecddf8b4bb139a82fe07cb131975a4 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 29 Nov 2013 04:04:04 +0100 Subject: [PATCH 131/800] (maint) Remove call to debugger --- lib/puppet/parser/ast/collexpr.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/puppet/parser/ast/collexpr.rb b/lib/puppet/parser/ast/collexpr.rb index 5a80f9684..9f266ed47 100644 --- a/lib/puppet/parser/ast/collexpr.rb +++ b/lib/puppet/parser/ast/collexpr.rb @@ -45,7 +45,6 @@ class CollExpr < AST::Branch if resource[match1].is_a?(Array) resource[match1].include?(match2) else - require 'debugger'; debugger resource[match1] == match2 end end From 826c1572a7b1ff6e617c6cd7a5320dab3c9f3ddc Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 29 Nov 2013 04:20:59 +0100 Subject: [PATCH 132/800] (maint) Remove debugger call --- spec/integration/parser/compiler_spec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/integration/parser/compiler_spec.rb b/spec/integration/parser/compiler_spec.rb index ccfc4546a..9ebd2dda9 100755 --- a/spec/integration/parser/compiler_spec.rb +++ b/spec/integration/parser/compiler_spec.rb @@ -230,7 +230,6 @@ describe "Puppet::Parser::Compiler" do end it "should create relationships using collection expressions" do - require 'debugger'; debugger code << "File <| mode == 0644 |> -> File <| mode == 0755 |>" expected_relationships.concat [ From 231ddaa0a5aea7a682366a751af7921c05565b4d Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 1 Dec 2013 02:22:11 +0100 Subject: [PATCH 133/800] (#22363) Make compilation work using new evaluator An extract_type_and_title method was added to the type calculator. It extract method extracts type/title from catalog types. This is used in the 3x interface as 3x cannot directly deal with types. Class methods for the API methods are also added to make the type calculator easier to use (no need to keep an instance around; although doing so is slightly faster). Tests used references like Class[Baz] where Baz is a PResourceType. This is now supported by the AccessOperator, but it is questionable if this should be supported. The runtime3 support now has an initializer that is called from the evaluator implementation. It creates a value converter that is needed to convert the runtime objects from 4x to 3x (types, and regular expressions) that are not supported as first class objects in the 3x runtime. The model label provider now also provides labels for the type model. There are additional validation / runtime issues. The validating parser was too fond of one particular acceptor and errors from previous runs were accumulated. The evaluating parser did not clear the definitions and name stack arrays which led to accumulation of classes, definitions and nodes in subsequent use of the same instance of evaluating parser. --- lib/puppet/parser/ast/pops_bridge.rb | 1 + lib/puppet/pops/evaluator/access_operator.rb | 10 ++++ lib/puppet/pops/evaluator/evaluator_impl.rb | 5 +- lib/puppet/pops/evaluator/runtime3_support.rb | 59 ++++++++++++++++++- lib/puppet/pops/issues.rb | 4 ++ lib/puppet/pops/model/model_label_provider.rb | 1 + lib/puppet/pops/parser/evaluating_parser.rb | 27 +++++---- lib/puppet/pops/parser/parser_support.rb | 25 ++++---- lib/puppet/pops/types/type_calculator.rb | 56 ++++++++++++++++-- .../pops/evaluator/evaluating_parser_spec.rb | 2 +- spec/unit/pops/types/type_calculator_spec.rb | 48 +++++++++++++++ 11 files changed, 207 insertions(+), 31 deletions(-) diff --git a/lib/puppet/parser/ast/pops_bridge.rb b/lib/puppet/parser/ast/pops_bridge.rb index 022b6bd82..452ebfe4f 100644 --- a/lib/puppet/parser/ast/pops_bridge.rb +++ b/lib/puppet/parser/ast/pops_bridge.rb @@ -33,6 +33,7 @@ class Puppet::Parser::AST::PopsBridge def each yield self end + def sequence_with(other) if value.nil? # This happens when testing and not having a complete setup diff --git a/lib/puppet/pops/evaluator/access_operator.rb b/lib/puppet/pops/evaluator/access_operator.rb index 3e0d3a86d..30ab881b8 100644 --- a/lib/puppet/pops/evaluator/access_operator.rb +++ b/lib/puppet/pops/evaluator/access_operator.rb @@ -276,8 +276,18 @@ class Puppet::Pops::Evaluator::AccessOperator # fail(Puppet::Pops::Issues::ILLEGAL_TYPE_SPECIALIZATION, semantic.left_expr, {:kind => 'Class'}) end + # The type argument may be a Resource Type - the Puppet Language allows a reference such as + # Class[Foo], and this is interpreted as Class[Resource[Foo]] - which is ok as long as the resource + # does not have a title. This should probably be deprecated. + # result = keys.collect do |c| ctype = Puppet::Pops::Types::PHostClassType.new() + if c.is_a?(Puppet::Pops::Types::PResourceType) && c.title.nil? + c = c.type_name + end + unless c.is_a?(String) + fail(Puppet::Pops::Issues::ILLEGAL_HOSTCLASS_NAME, @semantic.keys, {:name => c}) + end ctype.class_name = c ctype end diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index 16e6555e0..8219678f2 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -49,6 +49,9 @@ class Puppet::Pops::Evaluator::EvaluatorImpl @@compare_operator ||= Puppet::Pops::Evaluator::CompareOperator.new() @@relationship_operator ||= Puppet::Pops::Evaluator::RelationshipOperator.new() + + # Initialize the runtime module + Puppet::Pops::Evaluator::Runtime3Support.instance_method(:initialize).bind(self).call() end # @api private @@ -607,7 +610,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl end # This evaluates classes, nodes and resource type definitions to nil, since 3x: - # instantiates them, and evaluates thwir parameters and body. This is acheived by + # instantiates them, and evaluates their parameters and body. This is achieved by # providing bridge AST classes in Puppet::Parser::AST::PopsBridge that bridges a # Pops Program and a Pops Expression. # diff --git a/lib/puppet/pops/evaluator/runtime3_support.rb b/lib/puppet/pops/evaluator/runtime3_support.rb index 35228977c..2ac8c8bbf 100644 --- a/lib/puppet/pops/evaluator/runtime3_support.rb +++ b/lib/puppet/pops/evaluator/runtime3_support.rb @@ -1,3 +1,8 @@ +# A module with bindings between the new evaluator and the 3x runtime. +# The intention is to separate all calls into scope, compiler, resource, etc. in this module +# to make it easier to later refactor the evaluator for better implementations of the 3x classes. +# +# @api private module Puppet::Pops::Evaluator::Runtime3Support # Fails the evaluation of _semantic_ with a given issue. # @@ -200,7 +205,7 @@ module Puppet::Pops::Evaluator::Runtime3Support def create_resource_parameter(o, scope, name, value, operator) Puppet::Parser::Resource::Param.new( :name => name, - :value => value, + :value => convert(value, scope), # converted to 3x since 4x supports additional objects / types :source => scope.source, :line => -1, :file => 'TODO:Get file', :add => operator == :'+>' ) @@ -323,6 +328,58 @@ module Puppet::Pops::Evaluator::Runtime3Support x.is_a?(TrueClass) || x.is_a?(FalseClass) end + def initialize + @@convert_visitor ||= Puppet::Pops::Visitor.new(self, "convert", 1, 1) + end + + # Converts 4x supported values to 3x values. This is required because + # resources and other objects do not know about the new type system, and does not support + # regular expressions. Unfortunately this has to be done for array and hash as well. + # A complication is that catalog types needs to be resolved against the scope. + # + def convert(o, scope) + @@convert_visitor.visit_this_1(self, o, scope) + end + + def convert_Object(o, scope) + o + end + + def convert_Array(o, scope) + o.map {|x| convert(x, scope) } + end + + def convert_Hash(o, scope) + result = {} + o.each {|k,v| result[convert(k, scope)] = convert(v, scope) } + result + end + + def convert_Regexp(o, scope) + # Puppet 3x cannot handle parameter values that are reqular expressions. Turn into regexp string in + # source form + o.inspect + end + + def convert_PAbstractType(o, scope) + # Convert all other types to their string forms + o.to_s + end + + def convert_PResourceType(o,scope) + # Needs conversion by calling scope to resolve the name and possibly return a different name + # Resolution can only be called with an array, and returns an array. Here there is only one name + type, titles = scope.resolve_type_and_titles(o.type_name, [o.title]) + Puppet::Resource.new(type, titles[0]) + end + + def convert_PHostClassType(o, scope) + # Needs conversion by calling scope to resolve the name and possibly return a different name + # Resolution can only be called with an array, and returns an array. Here there is only one name + type, titles = scope.resolve_type_and_titles('class', [o.class_name]) + Puppet::Resource.new(type, titles[0]) + end + private # Produces an array with [type, title] from a PCatalogEntryType diff --git a/lib/puppet/pops/issues.rb b/lib/puppet/pops/issues.rb index 3c65d495b..2b5e676bd 100644 --- a/lib/puppet/pops/issues.rb +++ b/lib/puppet/pops/issues.rb @@ -361,6 +361,10 @@ module Puppet::Pops::Issues "First argument to Resource[] must be a resource type or a string. Got #{actual}." end + ILLEGAL_HOSTCLASS_NAME = hard_issue :ILLEGAL_HOSTCLASS_NAME, :name do + "Illegal Class name in class reference. #{label.a_an_uc(name)} cannot be used where a String is expected" + end + NOT_NUMERIC = issue :NOT_NUMERIC, :value do "The value '#{value}' cannot be converted to Numeric." end diff --git a/lib/puppet/pops/model/model_label_provider.rb b/lib/puppet/pops/model/model_label_provider.rb index 863a1f977..5708f37f9 100644 --- a/lib/puppet/pops/model/model_label_provider.rb +++ b/lib/puppet/pops/model/model_label_provider.rb @@ -71,6 +71,7 @@ class Puppet::Pops::Model::ModelLabelProvider < Puppet::Pops::LabelProvider def label_Hash o ; "Hash" end def label_QualifiedName o ; "Name" end def label_QualifiedReference o ; "Type Name" end + def label_PAbstractType o ; "#{Puppet::Pops::Types::TypeCalculator.string(o)} Type" end # TODO: Could use the TypeFactory to infer and output more detailed type information instead of # just printing Object, Hash, Array, etc. diff --git a/lib/puppet/pops/parser/evaluating_parser.rb b/lib/puppet/pops/parser/evaluating_parser.rb index d8d69c67f..fba96cbc9 100644 --- a/lib/puppet/pops/parser/evaluating_parser.rb +++ b/lib/puppet/pops/parser/evaluating_parser.rb @@ -50,13 +50,18 @@ class Puppet::Pops::Parser::EvaluatingParser ast.safeevaluate(scope) end - def acceptor() - @acceptor ||= Puppet::Pops::Validation::Acceptor.new - @acceptor + def validate(parse_result) + resulting_acceptor = acceptor() + validator(resulting_acceptor).validate(parse_result) + resulting_acceptor end - def validator() - @validator ||= Puppet::Pops::Validation::ValidatorFactory_3_1.new().validator(acceptor) + def acceptor() + Puppet::Pops::Validation::Acceptor.new + end + + def validator(acceptor) + Puppet::Pops::Validation::ValidatorFactory_3_1.new().validator(acceptor) end def assert_and_report(parse_result) @@ -65,19 +70,19 @@ class Puppet::Pops::Parser::EvaluatingParser unless Puppet::Pops::Adapters::OriginAdapter.get(parse_result.model) Puppet::Pops::Adapters::OriginAdapter.adapt(parse_result.model).origin = @file_source end - validator.validate(parse_result) + validation_result = validate(parse_result) max_errors = Puppet[:max_errors] max_warnings = Puppet[:max_warnings] + 1 max_deprecations = Puppet[:max_deprecations] + 1 # If there are warnings output them - warnings = acceptor.warnings + warnings = validation_result.warnings if warnings.size > 0 formatter = Puppet::Pops::Validation::DiagnosticFormatterPuppetStyle.new emitted_w = 0 emitted_dw = 0 - acceptor.warnings.each {|w| + validation_result.warnings.each {|w| if w.severity == :deprecation # Do *not* call Puppet.deprecation_warning it is for internal deprecation, not # deprecation of constructs in manifests! (It is not designed for that purpose even if @@ -94,7 +99,7 @@ class Puppet::Pops::Parser::EvaluatingParser end # If there were errors, report the first found. Use a puppet style formatter. - errors = acceptor.errors + errors = validation_result.errors if errors.size > 0 formatter = Puppet::Pops::Validation::DiagnosticFormatterPuppetStyle.new if errors.size == 1 || max_errors <= 1 @@ -172,8 +177,8 @@ class Puppet::Pops::Parser::EvaluatingParser @@evaluator.evaluate(model, scope) end - def validator() - @validator ||= Puppet::Pops::Validation::ValidatorFactory_4_0.new().validator(acceptor) + def validator(acceptor) + Puppet::Pops::Validation::ValidatorFactory_4_0.new().validator(acceptor) end end diff --git a/lib/puppet/pops/parser/parser_support.rb b/lib/puppet/pops/parser/parser_support.rb index 55bcd8652..ee57ba130 100644 --- a/lib/puppet/pops/parser/parser_support.rb +++ b/lib/puppet/pops/parser/parser_support.rb @@ -46,11 +46,11 @@ class Puppet::Pops::Parser::Parser [namespace, name].join("::").sub(/^::/, '') end - # Reinitializes variables (i.e. creates a new lexer instance - # - def clear - initvars - end +# # Reinitializes variables (i.e. creates a new lexer instance +# # +# def clear +# initvars +# end # Raises a Parse error. def error(value, message, options = {}) @@ -78,18 +78,17 @@ class Puppet::Pops::Parser::Parser # and there is no syntax that requires knowing if something referenced exists, it is safe # to assume that no environment is needed when parsing. (All that comes later). # - initvars - end - - # Initializes the parser support by creating a new instance of {Puppet::Pops::Parser::Lexer} - # @return [void] - # - def initvars @lexer = Puppet::Pops::Parser::Lexer2.new @namestack = [] @definitions = [] end +# # Initializes the parser support by creating a new instance of {Puppet::Pops::Parser::Lexer} +# # @return [void] +# # +# def initvars +# end + # This is a callback from the generated grammar (when an error occurs while parsing) # TODO Picks up origin information from the lexer, probably needs this from the caller instead # (for code strings, and when start line is not line 1 in a code string (or file), etc.) @@ -216,6 +215,8 @@ class Puppet::Pops::Parser::Parser return main ensure @lexer.clear + @namestack = [] + @definitions = [] end end diff --git a/lib/puppet/pops/types/type_calculator.rb b/lib/puppet/pops/types/type_calculator.rb index 34ab49246..d4165ec5d 100644 --- a/lib/puppet/pops/types/type_calculator.rb +++ b/lib/puppet/pops/types/type_calculator.rb @@ -71,6 +71,34 @@ class Puppet::Pops::Types::TypeCalculator Types = Puppet::Pops::Types TheInfinity = 1.0 / 0.0 # because the Infinity symbol is not defined + def self.assignable?(t1, t2) + instance.assignable?(t1,t2) + end + + def self.string(t) + instance.string(t) + end + + def self.infer(o) + instance.infer(o) + end + + def self.debug_string(t) + instance.debug_string(t) + end + + def self.enumerable(t) + instance.enumerable(t) + end + + def self.instance() + @tc_instance ||= new + end + + def self.extract_type_and_title(t) + instance.extract_type_and_title(t) + end + # @api public # def initialize @@ -79,6 +107,7 @@ class Puppet::Pops::Types::TypeCalculator @@string_visitor ||= Puppet::Pops::Visitor.new(nil,"string",0,0) @@inspect_visitor ||= Puppet::Pops::Visitor.new(nil,"debug_string",0,0) @@enumerable_visitor ||= Puppet::Pops::Visitor.new(nil,"enumerable",0,0) + @@extract_visitor ||= Puppet::Pops::Visitor.new(nil,"extract",0,0) da = Types::PArrayType.new() da.element_type = Types::PDataType.new() @@ -145,13 +174,17 @@ class Puppet::Pops::Types::TypeCalculator @@assignable_visitor.visit_this_1(self, t, t2) end - # Returns an enumerable if the t represents something that can be iterated - def enumerable(t) - @@enumerable_visitor.visit_this_0(self, t) - end + # Returns an enumerable if the t represents something that can be iterated + def enumerable(t) + @@enumerable_visitor.visit_this_0(self, t) + end + + def extract_type_and_title(t) + @@extract_visitor.visit_this_0(self, t) + end # Answers 'what is the Puppet Type corresponding to the given Ruby class' - # @param c [Class] the class for which a puppet type is wanted + # @param c [Class] the class for which a puppet type is wanted # @api public # def type(c) @@ -833,6 +866,19 @@ class Puppet::Pops::Types::TypeCalculator t end + # @api private + def extract_Object(o) + nil + end + + def extract_PResourceType(t) + [t.type_name, t.title] + end + + def extract_PHostClassType(t) + ['class', t.class_name] + end + private def class_from_string(str) diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index a4eb38637..7bd13fd17 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -214,7 +214,7 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do "666 =~ /6/" => :error, "[a] =~ /a/" => :error, "{a=>1} =~ /a/" => :error, - "/a/ =~ /a/" => :error, + "/a/ =~ /a/" => :error, "Array =~ /A/" => :error, }.each do |source, result| it "should parse and raise error for '#{source}'" do diff --git a/spec/unit/pops/types/type_calculator_spec.rb b/spec/unit/pops/types/type_calculator_spec.rb index 64a3b75e4..bd644e827 100644 --- a/spec/unit/pops/types/type_calculator_spec.rb +++ b/spec/unit/pops/types/type_calculator_spec.rb @@ -960,6 +960,54 @@ describe 'The type calculator' do end end + context "when asking for an enumerable " do + it "should produce an enumerable for an Integer range that is not infinite" do + t = Puppet::Pops::Types::PIntegerType.new() + t.from = 1 + t.to = 10 + calculator.enumerable(t).respond_to?(:each).should == true + end + + it "should not produce an enumerable for an Integer range that has an infinite side" do + t = Puppet::Pops::Types::PIntegerType.new() + t.from = nil + t.to = 10 + calculator.enumerable(t).should == nil + + t = Puppet::Pops::Types::PIntegerType.new() + t.from = 1 + t.to = nil + calculator.enumerable(t).should == nil + end + + it "all but Integer range are not enumerable" do + [Object, Numeric, Float, String, Regexp, Array, Hash].each do |t| + calculator.enumerable(calculator.type(t)).should == nil + end + end + end + + context "when supporting 3x it should be possible to extract type & title" do + it "from a resource" do + t = Puppet::Pops::Types::PResourceType.new() + t.type_name = 'File' + t.title = '/tmp/foo' + calculator.extract_type_and_title(t).should == ['File', '/tmp/foo'] + end + + it "from a class" do + t = Puppet::Pops::Types::PHostClassType.new() + t.class_name = 'peekaboo' + calculator.extract_type_and_title(t).should == ['class', 'peekaboo'] + end + + it "but not from anything else" do + [Object, Numeric, Integer, Fixnum, Bignum, Float, String, Regexp, Array, Hash].each do |t| + calculator.extract_type_and_title(calculator.type(t)).should == nil + end + end + end + matcher :be_assignable_to do |type| calc = Puppet::Pops::Types::TypeCalculator.new From c88d587fbfea81bc3ead4dc14414018b84fff47e Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 2 Dec 2013 00:05:07 +0100 Subject: [PATCH 134/800] (#22363) Make errors for illegal Class[x] references better. This adds additional checks for Class[x] operations and adds more tests. This also removes hyphen as a legal character in regexp patterns used for checking (the lexer already did not include the hyphen and they were flagged as errors. This change affects when strings are checked for compliance with the name rule. --- lib/puppet/pops/evaluator/access_operator.rb | 11 +++++++---- lib/puppet/pops/issues.rb | 2 +- lib/puppet/pops/patterns.rb | 4 ++-- lib/puppet/pops/validation/checker4_0.rb | 6 ++++-- spec/unit/pops/evaluator/access_ops_spec.rb | 5 +++++ .../pops/evaluator/evaluating_parser_spec.rb | 17 +++++++++++++---- 6 files changed, 32 insertions(+), 13 deletions(-) diff --git a/lib/puppet/pops/evaluator/access_operator.rb b/lib/puppet/pops/evaluator/access_operator.rb index 30ab881b8..8e49be8ea 100644 --- a/lib/puppet/pops/evaluator/access_operator.rb +++ b/lib/puppet/pops/evaluator/access_operator.rb @@ -280,13 +280,16 @@ class Puppet::Pops::Evaluator::AccessOperator # Class[Foo], and this is interpreted as Class[Resource[Foo]] - which is ok as long as the resource # does not have a title. This should probably be deprecated. # - result = keys.collect do |c| + result = keys.each_with_index.map do |c, i| ctype = Puppet::Pops::Types::PHostClassType.new() - if c.is_a?(Puppet::Pops::Types::PResourceType) && c.title.nil? - c = c.type_name + if c.is_a?(Puppet::Pops::Types::PResourceType) && !c.type_name.nil? && c.title.nil? + c = c.type_name.downcase end unless c.is_a?(String) - fail(Puppet::Pops::Issues::ILLEGAL_HOSTCLASS_NAME, @semantic.keys, {:name => c}) + fail(Puppet::Pops::Issues::ILLEGAL_HOSTCLASS_NAME, @semantic.keys[i], {:name => c}) + end + if c !~ Puppet::Pops::Patterns::NAME + fail(Issues::ILLEGAL_NAME, @semantic.keys[i], {:name=>c}) end ctype.class_name = c ctype diff --git a/lib/puppet/pops/issues.rb b/lib/puppet/pops/issues.rb index 2b5e676bd..60304f27a 100644 --- a/lib/puppet/pops/issues.rb +++ b/lib/puppet/pops/issues.rb @@ -230,7 +230,7 @@ module Puppet::Pops::Issues end ILLEGAL_NAME = hard_issue :ILLEGAL_NAME, :name do - "Illegal name. The given name #{name} does not conform to the naming rule \\A((::)?[a-z0-9]\w*)(::[a-z0-9]\w*)*\\z" + "Illegal name. The given name #{name} does not conform to the naming rule /^((::)?[a-z0-9]\w*)(::[a-z0-9]\w*)*$/" end # In case a model is constructed programmatically, it must create valid type references. diff --git a/lib/puppet/pops/patterns.rb b/lib/puppet/pops/patterns.rb index b1c76ad43..f28729e3b 100644 --- a/lib/puppet/pops/patterns.rb +++ b/lib/puppet/pops/patterns.rb @@ -24,12 +24,12 @@ module Puppet::Pops::Patterns # where each part must start with a capital letter A-Z. # This name includes hyphen, which may be illegal in some cases. # - CLASSREF_EXT = %r{\A((::){0,1}[A-Z][-\w]*)+\z} + CLASSREF_EXT = %r{\A((::){0,1}[A-Z][\w]*)+\z} # CLASSREF matches a class reference the way it is represented internally in the # model (i.e. in lower case). # This name includes hyphen, which may be illegal in some cases. # - CLASSREF = %r{\A((::){0,1}[a-z][-\w]*)+\z} + CLASSREF = %r{\A((::){0,1}[a-z][\w]*)+\z} end diff --git a/lib/puppet/pops/validation/checker4_0.rb b/lib/puppet/pops/validation/checker4_0.rb index f87f64e8f..3ca655c02 100644 --- a/lib/puppet/pops/validation/checker4_0.rb +++ b/lib/puppet/pops/validation/checker4_0.rb @@ -263,8 +263,10 @@ class Puppet::Pops::Validation::Checker4_0 # Is this a valid qualified name? if o.value !~ Puppet::Pops::Patterns::CLASSREF acceptor.accept(Issues::ILLEGAL_CLASSREF, o, {:name=>o.value}) - elsif (acceptor.will_accept? Issues::NAME_WITH_HYPHEN) && o.value.include?('-') - acceptor.accept(Issues::NAME_WITH_HYPHEN, o, {:name => o.value}) +# hyphens are not allowed in CLASSREF pattern - if that is changed and it should be flagged instead, change +# to include this logic: +# elsif (acceptor.will_accept? Issues::NAME_WITH_HYPHEN) && o.value.include?('-') +# acceptor.accept(Issues::NAME_WITH_HYPHEN, o, {:name => o.value}) end end diff --git a/spec/unit/pops/evaluator/access_ops_spec.rb b/spec/unit/pops/evaluator/access_ops_spec.rb index a70dc6390..63987d3e3 100644 --- a/spec/unit/pops/evaluator/access_ops_spec.rb +++ b/spec/unit/pops/evaluator/access_ops_spec.rb @@ -187,6 +187,11 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do expect(result[1]).to be_the_type(types.host_class('nginx')) end + it 'raises error if the name is not a valid name' do + expr = fqr('Class')['fail-whale'] + expect { evaluate(expr) }.to raise_error(/Illegal name/) + end + # Resource it 'produces a specific resource type from Resource[type]' do expr = fqr('Resource')[fqr('File')] diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index 7bd13fd17..44c2a1917 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -561,13 +561,13 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do "Resource[]" => :error, "File[]" => :error, "String[]" => :error, - "String[0]" => :error, + "String[0]" => :error, "1[]" => :error, - "1[0]" => :error, + "1[0]" => :error, "3.14[]" => :error, - "3.14[0]" => :error, + "3.14[0]" => :error, "/.*/[]" => :error, - "/.*/[0]" => :error, + "/.*/[0]" => :error, "$a=[1] $a[]" => :error, }.each do |source, result| it "should parse and evaluate the expression '#{source}' to #{result}" do @@ -593,6 +593,7 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do scope.known_resource_types.import_ast(ast, '') ast.code.safeevaluate(scope).should == 'chocolate' end + # Resource default and override expressions and resource parameter access with [] { "notify { id: message=>explicit} Notify[id][message]" => "explicit", @@ -613,6 +614,14 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do expect { parser.evaluate_string(scope, source, __FILE__)}.to raise_error(result) end end + context 'with errors' do + { "Class['fail-whale']" => /Illegal name/, + }.each do | source, error_pattern| + it "an error is flagged for '#{source}'" do + expect { parser.evaluate_string(scope, source, __FILE__)}.to raise_error(error_pattern) + end + end + end end # end [] operations end From 7a6a7d8d827d42e45679c2d084664adccd7ceb2c Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 2 Dec 2013 02:01:42 +0100 Subject: [PATCH 135/800] (#22363) Add compiler integration test for future evaluator (This was missing in an earlier commit) --- .../parser/future_compiler_spec.rb | 398 ++++++++++++++++++ 1 file changed, 398 insertions(+) create mode 100644 spec/integration/parser/future_compiler_spec.rb diff --git a/spec/integration/parser/future_compiler_spec.rb b/spec/integration/parser/future_compiler_spec.rb new file mode 100644 index 000000000..7022eece1 --- /dev/null +++ b/spec/integration/parser/future_compiler_spec.rb @@ -0,0 +1,398 @@ +#! /usr/bin/env ruby +require 'spec_helper' +require 'puppet/pops' +require 'puppet/parser/parser_factory' +require 'puppet_spec/compiler' +require 'puppet_spec/pops' +require 'puppet_spec/scope' +require 'rgen/metamodel_builder' + +# This is a copy of the compiler_spec, with hacks to try to make it run for the future +# evaluator. +# :-( +# The first failing test does not fail if executed alone. +# +describe "Puppet::Parser::Compiler" do + include PuppetSpec::Compiler + + before :each do + Puppet[:parser] = 'future' + + # This is in the original test - what is this for? Does not seem to make a differenceself.code + @scope_resource = stub 'scope_resource', :builtin? => true, :finish => nil, :ref => 'Class[main]' + @scope = stub 'scope', :resource => @scope_resource, :source => mock("source") + end + + + after do + Puppet.settings.clear + end + + # shared because tests are invoked both for classic and future parser + # + describe "the compiler when using future parser and evaluator" do + it "should be able to determine the configuration version from a local version control repository" do + pending("Bug #14071 about semantics of Puppet::Util::Execute on Windows", :if => Puppet.features.microsoft_windows?) do + # This should always work, because we should always be + # in the puppet repo when we run this. + version = %x{git rev-parse HEAD}.chomp + + Puppet.settings[:config_version] = 'git rev-parse HEAD' + + parser = Puppet::Parser::ParserFactory.parser "development" + compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("testnode")) + compiler.catalog.version.should == version + end + end + + it "should not create duplicate resources when a class is referenced both directly and indirectly by the node classifier (4792)" do + Puppet[:code] = <<-PP + class foo + { + notify { foo_notify: } + include bar + } + class bar + { + notify { bar_notify: } + } + PP + + node = Puppet::Node.new("testnodex") + node.classes = ['foo', 'bar'] + #node.stubs(:classes).returns(['foo', 'bar']) + catalog = Puppet::Parser::Compiler.compile(node) + node.classes = nil + catalog.resource("Notify[foo_notify]").should_not be_nil + catalog.resource("Notify[bar_notify]").should_not be_nil + end + + describe "when resolving class references" do + it "should favor local scope, even if there's an included class in topscope" do + Puppet[:code] = <<-PP + class experiment { + class baz { + } + notify {"x" : require => Class[Baz] } + } + class baz { + } + include baz + include experiment + include experiment::baz + PP + + catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) + + notify_resource = catalog.resource( "Notify[x]" ) + + notify_resource[:require].title.should == "Experiment::Baz" + end + + it "should favor local scope, even if there's an unincluded class in topscope" do + Puppet[:code] = <<-PP + class experiment { + class baz { + } + notify {"x" : require => Class[Baz] } + } + class baz { + } + include experiment + include experiment::baz + PP + + catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) + + notify_resource = catalog.resource( "Notify[x]" ) + + notify_resource[:require].title.should == "Experiment::Baz" + end + end + describe "(ticket #13349) when explicitly specifying top scope" do + ["class {'::bar::baz':}", "include ::bar::baz"].each do |include| + describe "with #{include}" do + it "should find the top level class" do + Puppet[:code] = <<-MANIFEST + class { 'foo::test': } + class foo::test { + #{include} + } + class bar::baz { + notify { 'good!': } + } + class foo::bar::baz { + notify { 'bad!': } + } + MANIFEST + + catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) + + catalog.resource("Class[Bar::Baz]").should_not be_nil + catalog.resource("Notify[good!]").should_not be_nil + catalog.resource("Class[Foo::Bar::Baz]").should be_nil + catalog.resource("Notify[bad!]").should be_nil + end + end + end + end + + it "should recompute the version after input files are re-parsed" do + Puppet[:code] = 'class foo { }' + Time.stubs(:now).returns(1) + node = Puppet::Node.new('mynode') + Puppet::Parser::Compiler.compile(node).version.should == 1 + Time.stubs(:now).returns(2) + Puppet::Parser::Compiler.compile(node).version.should == 1 # no change because files didn't change + Puppet::Resource::TypeCollection.any_instance.stubs(:stale?).returns(true).then.returns(false) # pretend change + Puppet::Parser::Compiler.compile(node).version.should == 2 + end + + ['define', 'class', 'node'].each do |thing| + it "'#{thing}' is not allowed inside evaluated conditional constructs" do + Puppet[:code] = <<-PP + if true { + #{thing} foo { + } + notify { decoy: } + } + PP + + begin + catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) + raise "compilation should have raised Puppet::Error" + rescue Puppet::Error => e + e.message.should =~ /Classes, definitions, and nodes may only appear at toplevel/ + end + end + end + + ['define', 'class', 'node'].each do |thing| + it "'#{thing}' is not allowed inside un-evaluated conditional constructs" do + Puppet[:code] = <<-PP + if false { + #{thing} foo { + } + notify { decoy: } + } + PP + + begin + catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) + raise "compilation should have raised Puppet::Error" + rescue Puppet::Error => e + e.message.should =~ /Classes, definitions, and nodes may only appear at toplevel/ + end + end + end + + describe "relationships can be formed" do + def extract_name(ref) + ref.sub(/File\[(\w+)\]/, '\1') + end + + let(:node) { Puppet::Node.new('mynode') } + let(:code) do + <<-MANIFEST + file { [a,b,c]: + mode => 0644, + } + file { [d,e]: + mode => 0755, + } + MANIFEST + end + let(:expected_relationships) { [] } + let(:expected_subscriptions) { [] } + + before :each do + Puppet[:parser] = 'future' + Puppet[:code] = code + end + + after :each do + catalog = Puppet::Parser::Compiler.compile(node) + + resources = catalog.resources.select { |res| res.type == 'File' } + + actual_relationships, actual_subscriptions = [:before, :notify].map do |relation| + resources.map do |res| + dependents = Array(res[relation]) + dependents.map { |ref| [res.title, extract_name(ref)] } + end.inject(&:concat) + end + + actual_relationships.should =~ expected_relationships + actual_subscriptions.should =~ expected_subscriptions + end + + it "of regular type" do + code << "File[a] -> File[b]" + + expected_relationships << ['a','b'] + end + + it "of subscription type" do + code << "File[a] ~> File[b]" + + expected_subscriptions << ['a', 'b'] + end + + it "between multiple resources expressed as resource with multiple titles" do + code << "File[a,b] -> File[c,d]" + + expected_relationships.concat [ + ['a', 'c'], + ['b', 'c'], + ['a', 'd'], + ['b', 'd'], + ] + end + + it "between collection expressions" do + code << "File <| mode == 0644 |> -> File <| mode == 0755 |>" + + expected_relationships.concat [ + ['a', 'd'], + ['b', 'd'], + ['c', 'd'], + ['a', 'e'], + ['b', 'e'], + ['c', 'e'], + ] + end + + it "between resources expressed as Strings" do + code << "'File[a]' -> 'File[b]'" + + expected_relationships << ['a', 'b'] + end + + it "between resources expressed as variables" do + code << <<-MANIFEST + $var = File[a] + $var -> File[b] + MANIFEST + + expected_relationships << ['a', 'b'] + end + + it "between resources expressed as case statements" do + code << <<-MANIFEST + $var = 10 + case $var { + 10: { + file { s1: } + } + 12: { + file { s2: } + } + } + -> + case $var + 2 { + 10: { + file { t1: } + } + 12: { + file { t2: } + } + } + MANIFEST + + expected_relationships << ['s1', 't2'] + end + + it "using deep access in array" do + code << <<-MANIFEST + $var = [ [ [ File[a], File[b] ] ] ] + $var[0][0][0] -> $var[0][0][1] + MANIFEST + + expected_relationships << ['a', 'b'] + end + + it "using deep access in hash" do + code << <<-MANIFEST + $var = {'foo' => {'bar' => {'source' => File[a], 'target' => File[b]}}} + $var[foo][bar][source] -> $var[foo][bar][target] + MANIFEST + + expected_relationships << ['a', 'b'] + end + + it "using resource declarations" do + code << "file { l: } -> file { r: }" + + expected_relationships << ['l', 'r'] + end + + it "between entries in a chain of relationships" do + code << "File[a] -> File[b] ~> File[c] <- File[d] <~ File[e]" + + expected_relationships << ['a', 'b'] << ['d', 'c'] + expected_subscriptions << ['b', 'c'] << ['e', 'd'] + end + end + + context 'when working with the trusted data hash' do + context 'and have opted in to hashed_node_data' do + before :each do + Puppet[:hashed_node_data] = true + end + + it 'should make $trusted available' do + node = Puppet::Node.new("testing") + node.trusted_data = { "data" => "value" } + + catalog = compile_to_catalog(<<-MANIFEST, node) + notify { 'test': message => $trusted[data] } + MANIFEST + + catalog.resource("Notify[test]")[:message].should == "value" + end + + it 'should not allow assignment to $trusted' do + node = Puppet::Node.new("testing") + node.trusted_data = { "data" => "value" } + + expect do + catalog = compile_to_catalog(<<-MANIFEST, node) + $trusted = 'changed' + notify { 'test': message => $trusted == 'changed' } + MANIFEST + catalog.resource("Notify[test]")[:message].should == true + end.to raise_error(Puppet::Error, /Attempt to assign to a reserved variable name: 'trusted'/) + end + end + + context 'and have not opted in to hashed_node_data' do + before :each do + Puppet[:hashed_node_data] = false + end + + it 'should not make $trusted available' do + node = Puppet::Node.new("testing") + node.trusted_data = { "data" => "value" } + + catalog = compile_to_catalog(<<-MANIFEST, node) + notify { 'test': message => ($trusted == undef) } + MANIFEST + + catalog.resource("Notify[test]")[:message].should == true + end + + it 'should allow assignment to $trusted' do + node = Puppet::Node.new("testing") + + catalog = compile_to_catalog(<<-MANIFEST, node) + $trusted = 'changed' + notify { 'test': message => $trusted == 'changed' } + MANIFEST + + catalog.resource("Notify[test]")[:message].should == true + end + end + end + end + +end From 7d1ed7b1cbd252b434c6c83e4770f4acea6f3cca Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 2 Dec 2013 02:03:14 +0100 Subject: [PATCH 136/800] (#22363) Improve validation of names and variables The validation of names and variables was too relaxed. The lexer had stricter rules in some cases and validation rules for numeric variables were too relaxed. Patterns used in validation are now stricter. --- lib/puppet/pops/evaluator/runtime3_support.rb | 5 +++ lib/puppet/pops/issues.rb | 17 +++++++++- lib/puppet/pops/model/model_label_provider.rb | 7 +++- lib/puppet/pops/patterns.rb | 12 +++++-- lib/puppet/pops/validation/checker4_0.rb | 34 ++++++++----------- .../pops/evaluator/evaluating_parser_spec.rb | 24 +++++++++++++ 6 files changed, 76 insertions(+), 23 deletions(-) diff --git a/lib/puppet/pops/evaluator/runtime3_support.rb b/lib/puppet/pops/evaluator/runtime3_support.rb index 2ac8c8bbf..289267322 100644 --- a/lib/puppet/pops/evaluator/runtime3_support.rb +++ b/lib/puppet/pops/evaluator/runtime3_support.rb @@ -33,7 +33,12 @@ module Puppet::Pops::Evaluator::Runtime3Support catch(:undefined_variable) { return scope.lookupvar(name.to_s) } + # It is always ok to reference numeric variables even if they are not assigned. They are always undef + # if not set by a match expression. + # + unless name =~ Puppet::Pops::Patterns::NUMERIC_VAR_NAME fail(Puppet::Pops::Issues::UNKNOWN_VARIABLE, o, {:name => name}) + end end # Returns true if the variable of the given name is set in the given most nested scope. True is returned even if diff --git a/lib/puppet/pops/issues.rb b/lib/puppet/pops/issues.rb index 60304f27a..862d27675 100644 --- a/lib/puppet/pops/issues.rb +++ b/lib/puppet/pops/issues.rb @@ -230,7 +230,15 @@ module Puppet::Pops::Issues end ILLEGAL_NAME = hard_issue :ILLEGAL_NAME, :name do - "Illegal name. The given name #{name} does not conform to the naming rule /^((::)?[a-z0-9]\w*)(::[a-z0-9]\w*)*$/" + "Illegal name. The given name #{name} does not conform to the naming rule /^((::)?[a-z]\w*)(::[a-z]\w*)*$/" + end + + ILLEGAL_VAR_NAME = hard_issue :ILLEGAL_VAR_NAME, :name do + "Illegal variable name, The given name '#{name}' does not conform to the naming rule /^(::)?(\w+::)*\w+$/" + end + + ILLEGAL_NUMERIC_VAR_NAME = hard_issue :ILLEGAL_NUMERIC_VAR_NAME, :name do + "Illegal numeric variable name, The given name '#{name}' must be a decimal value if it starts with a digit 0-9" end # In case a model is constructed programmatically, it must create valid type references. @@ -365,6 +373,13 @@ module Puppet::Pops::Issues "Illegal Class name in class reference. #{label.a_an_uc(name)} cannot be used where a String is expected" end + # Issues when an expression is used where it is not legal. + # E.g. an arithmetic expression where a hostname is expected. + # + ILLEGAL_DEFINITION_NAME = hard_issue :ILLEGAL_DEFINTION_NAME, :name do + "Unacceptable name. The name '#{name}' is unacceptable as the name of #{label.a_an(semantic)}" + end + NOT_NUMERIC = issue :NOT_NUMERIC, :value do "The value '#{value}' cannot be converted to Numeric." end diff --git a/lib/puppet/pops/model/model_label_provider.rb b/lib/puppet/pops/model/model_label_provider.rb index 5708f37f9..0062b1214 100644 --- a/lib/puppet/pops/model/model_label_provider.rb +++ b/lib/puppet/pops/model/model_label_provider.rb @@ -13,7 +13,7 @@ class Puppet::Pops::Model::ModelLabelProvider < Puppet::Pops::LabelProvider end def label_Factory o ; label(o.current) end - def label_Array o ; "Array Object" end + def label_Array o ; "Array" end def label_LiteralInteger o ; "Literal Integer" end def label_LiteralFloat o ; "Literal Float" end def label_ArithmeticExpression o ; "'#{o.operator}' expression" end @@ -66,7 +66,12 @@ class Puppet::Pops::Model::ModelLabelProvider < Puppet::Pops::LabelProvider def label_ResourceExpression o ; "Resource Statement" end def label_SelectorExpression o ; "Selector Expression" end def label_SelectorEntry o ; "Selector Option" end + def label_Integer o ; "Integer" end + def label_Fixnum o ; "Integer" end + def label_Bignum o ; "Integer" end + def label_Float o ; "Float" end def label_String o ; "String" end + def label_Regexp o ; "Regexp" end def label_Object o ; "Object" end def label_Hash o ; "Hash" end def label_QualifiedName o ; "Name" end diff --git a/lib/puppet/pops/patterns.rb b/lib/puppet/pops/patterns.rb index f28729e3b..fd8764fa0 100644 --- a/lib/puppet/pops/patterns.rb +++ b/lib/puppet/pops/patterns.rb @@ -17,8 +17,7 @@ module Puppet::Pops::Patterns ILLEGAL_HOSTNAME_CHARS = %r{[^-\w.]} # NAME matches a name the same way as the lexer. - # This name includes hyphen, which may be illegal in variables, and names in general. - NAME = %r{\A((::)?[a-z0-9]\w*)(::[a-z0-9]\w*)*\z} + NAME = %r{\A((::)?[a-z]\w*)(::[a-z]\w*)*\z} # CLASSREF_EXT matches a class reference the same way as the lexer - i.e. the external source form # where each part must start with a capital letter A-Z. @@ -32,4 +31,13 @@ module Puppet::Pops::Patterns # CLASSREF = %r{\A((::){0,1}[a-z][\w]*)+\z} + # DOLLAR_VAR matches a variable name including the initial $ character + DOLLAR_VAR = %r{\$(::)?(\w+::)*\w+} + + # VAR_NAME matches the name part of a variable (The $ character is not included) + VAR_NAME = %r{(::)?(\w+::)*\w+} + + # A Numeric var name must be the decimal number 0, or a decimal number not starting with 0 + NUMERIC_VAR_NAME = %r{\A(?:0|(?:[1-9][0-9]*))\z} + end diff --git a/lib/puppet/pops/validation/checker4_0.rb b/lib/puppet/pops/validation/checker4_0.rb index 3ca655c02..85bbb0016 100644 --- a/lib/puppet/pops/validation/checker4_0.rb +++ b/lib/puppet/pops/validation/checker4_0.rb @@ -81,7 +81,7 @@ class Puppet::Pops::Validation::Checker4_0 def assign_VariableExpression(o, via_index) varname_string = varname_to_s(o.expr) - if varname_string =~ /^[0-9]+$/ + if varname_string =~ Puppet::Pops::Patterns::NUMERIC_VAR_NAME acceptor.accept(Issues::ILLEGAL_NUMERIC_ASSIGNMENT, o, :varname => varname_string) end # Can not assign to something in another namespace (i.e. a '::' in the name is not legal) @@ -206,8 +206,8 @@ class Puppet::Pops::Validation::Checker4_0 # for 'class' and 'define' def check_NamedDefinition(o) top(o.eContainer, o) - if (acceptor.will_accept? Issues::NAME_WITH_HYPHEN) && o.name.include?('-') - acceptor.accept(Issues::NAME_WITH_HYPHEN, o, {:name => o.name}) + if o.name !~ Puppet::Pops::Patterns::CLASSREF + acceptor.accept(Issues::ILLEGAL_DEFINITION_NAME, o, {:name=>o.name}) end end @@ -246,15 +246,12 @@ class Puppet::Pops::Validation::Checker4_0 top(o.eContainer, o) end - # Asserts that value is a valid QualifiedName. No additional checking is made, objects that use - # a QualifiedName as a name should check the validity - this since a QualifiedName is used as a BARE WORD - # and then additional chars may be valid (like a hyphen). + # No checking takes place - all expressions using a QualifiedName need to check. This because the + # rules are slightly different depending on the container (A variable allows a numeric start, but not + # other names). This means that (if the lexer/parser so chooses) a QualifiedName + # can be anything when it represents a Bare Word and evaluates to a String. # def check_QualifiedName(o) - # Is this a valid qualified name? - if o.value !~ Puppet::Pops::Patterns::NAME - acceptor.accept(Issues::ILLEGAL_NAME, o, {:name=>o.value}) - end end # Checks that the value is a valid UpperCaseWord (a CLASSREF), and optionally if it contains a hypen. @@ -263,10 +260,6 @@ class Puppet::Pops::Validation::Checker4_0 # Is this a valid qualified name? if o.value !~ Puppet::Pops::Patterns::CLASSREF acceptor.accept(Issues::ILLEGAL_CLASSREF, o, {:name=>o.value}) -# hyphens are not allowed in CLASSREF pattern - if that is changed and it should be flagged instead, change -# to include this logic: -# elsif (acceptor.will_accept? Issues::NAME_WITH_HYPHEN) && o.value.include?('-') -# acceptor.accept(Issues::NAME_WITH_HYPHEN, o, {:name => o.value}) end end @@ -352,17 +345,20 @@ class Puppet::Pops::Validation::Checker4_0 # TODO: Unless may not have an else part that is an IfExpression (grammar denies this though) end + # Checks that variable is either strictly 0, or a non 0 starting decimal number, or a valid VAR_NAME def check_VariableExpression(o) # The expression must be a qualified name if !o.expr.is_a? Model::QualifiedName acceptor.accept(Issues::ILLEGAL_EXPRESSION, o, :feature => 'name', :container => o) else - # Note, that if it later becomes illegal with hyphen in any name, this special check - # can be skipped in favor of the check in QualifiedName, which is now not done if contained in - # a VariableExpression + # name must be either a decimal value, or a valid NAME name = o.expr.value - if (acceptor.will_accept? Issues::VAR_WITH_HYPHEN) && name.include?('-') - acceptor.accept(Issues::VAR_WITH_HYPHEN, o, {:name => name}) + if name[0,1] =~ /[0-9]/ + unless name =~ Puppet::Pops::Patterns::NUMERIC_VAR_NAME + acceptor.accept(Issues::ILLEGAL_NUMERIC_VAR_NAME, o, :name => name) + end + else + check(o.expr) end end end diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index 44c2a1917..ebe9f5e64 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -614,8 +614,14 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do expect { parser.evaluate_string(scope, source, __FILE__)}.to raise_error(result) end end + context 'with errors' do { "Class['fail-whale']" => /Illegal name/, + "Class[0]" => /An Integer cannot be used where a String is expected/, + "Class[/.*/]" => /A Regexp cannot be used where a String is expected/, + "Class[4.1415]" => /A Float cannot be used where a String is expected/, + "Class[[1,2,3]]" => /An Array cannot be used where a String is expected/, + "Class[Integer]" => /An Integer Type cannot be used where a String is expected/, }.each do | source, error_pattern| it "an error is flagged for '#{source}'" do expect { parser.evaluate_string(scope, source, __FILE__)}.to raise_error(error_pattern) @@ -757,6 +763,24 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do it "a lex error should be raised for '$foo::::bar'" do expect { parser.evaluate_string(scope, "$foo::::bar") }.to raise_error(Puppet::LexError, /Illegal fully qualified name at line 1:7/) end + + { '$a = $0' => nil, + '$a = $1' => nil, + }.each do |source, value| + it "it is ok to reference numeric unassigned variables '#{source}'" do + parser.evaluate_string(scope, source, __FILE__).should == value + end + end + + { '$00 = 0' => /must be a decimal value/, + '$0xf = 0' => /must be a decimal value/, + '$0777 = 0' => /must be a decimal value/, + '$123a = 0' => /must be a decimal value/, + }.each do |source, error_pattern| + it "should raise an error for '#{source}'" do + expect { parser.evaluate_string(scope, source, __FILE__) }.to raise_error(error_pattern) + end + end end context "When evaluating relationships" do From 3ec41e2061d6b86abe053cadcbba2efb7bf8c739 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 2 Dec 2013 02:09:56 +0100 Subject: [PATCH 137/800] (#22363) Improve error for Class[File['foo']] The label produced for File['foo'] said "File['foo'] Type", now it says "File['foo'] Resource Reference" (if it has a title) and "File Type" if it does not. --- lib/puppet/pops/model/model_label_provider.rb | 7 +++++++ spec/unit/pops/evaluator/evaluating_parser_spec.rb | 1 + 2 files changed, 8 insertions(+) diff --git a/lib/puppet/pops/model/model_label_provider.rb b/lib/puppet/pops/model/model_label_provider.rb index 0062b1214..2688e3330 100644 --- a/lib/puppet/pops/model/model_label_provider.rb +++ b/lib/puppet/pops/model/model_label_provider.rb @@ -78,6 +78,13 @@ class Puppet::Pops::Model::ModelLabelProvider < Puppet::Pops::LabelProvider def label_QualifiedReference o ; "Type Name" end def label_PAbstractType o ; "#{Puppet::Pops::Types::TypeCalculator.string(o)} Type" end + def label_PResourceType o + if o.title + "#{Puppet::Pops::Types::TypeCalculator.string(o)} Resource Reference" + else + "#{Puppet::Pops::Types::TypeCalculator.string(o)} Type" + end + end # TODO: Could use the TypeFactory to infer and output more detailed type information instead of # just printing Object, Hash, Array, etc. end diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index ebe9f5e64..f32fd61cb 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -622,6 +622,7 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do "Class[4.1415]" => /A Float cannot be used where a String is expected/, "Class[[1,2,3]]" => /An Array cannot be used where a String is expected/, "Class[Integer]" => /An Integer Type cannot be used where a String is expected/, + "Class[File['tmp']]" => /A File['tmp'] Resource Reference cannot be used where a String is expected/, }.each do | source, error_pattern| it "an error is flagged for '#{source}'" do expect { parser.evaluate_string(scope, source, __FILE__)}.to raise_error(error_pattern) From 7d3b22b2cd9063b34d5f5209b55dafdd75b038ab Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 2 Dec 2013 03:20:56 +0100 Subject: [PATCH 138/800] (#22363) Fix failing test (typo in regexp). --- spec/unit/pops/evaluator/evaluating_parser_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index f32fd61cb..4fa9b237b 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -622,7 +622,7 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do "Class[4.1415]" => /A Float cannot be used where a String is expected/, "Class[[1,2,3]]" => /An Array cannot be used where a String is expected/, "Class[Integer]" => /An Integer Type cannot be used where a String is expected/, - "Class[File['tmp']]" => /A File['tmp'] Resource Reference cannot be used where a String is expected/, + "Class[File['tmp']]" => /A File\['tmp'\] Resource Reference cannot be used where a String is expected/, }.each do | source, error_pattern| it "an error is flagged for '#{source}'" do expect { parser.evaluate_string(scope, source, __FILE__)}.to raise_error(error_pattern) From dca73e6cc666d5b46b15105c5c04229c4b153be6 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 2 Dec 2013 03:25:21 +0100 Subject: [PATCH 139/800] (#22363) Remove support for extract type and title from type calculator The support for 3x logic to convert catalog types to name and title array was intended for calls from within 3x logic. This was not a desired solution and the the support in the type calculator ended up being unused. This removes it. (The corresponding logic is instead found in the runtime3_support module). --- lib/puppet/pops/types/type_calculator.rb | 21 -------------------- spec/unit/pops/types/type_calculator_spec.rb | 21 -------------------- 2 files changed, 42 deletions(-) diff --git a/lib/puppet/pops/types/type_calculator.rb b/lib/puppet/pops/types/type_calculator.rb index d4165ec5d..0c23c7358 100644 --- a/lib/puppet/pops/types/type_calculator.rb +++ b/lib/puppet/pops/types/type_calculator.rb @@ -95,10 +95,6 @@ class Puppet::Pops::Types::TypeCalculator @tc_instance ||= new end - def self.extract_type_and_title(t) - instance.extract_type_and_title(t) - end - # @api public # def initialize @@ -179,10 +175,6 @@ class Puppet::Pops::Types::TypeCalculator @@enumerable_visitor.visit_this_0(self, t) end - def extract_type_and_title(t) - @@extract_visitor.visit_this_0(self, t) - end - # Answers 'what is the Puppet Type corresponding to the given Ruby class' # @param c [Class] the class for which a puppet type is wanted # @api public @@ -866,19 +858,6 @@ class Puppet::Pops::Types::TypeCalculator t end - # @api private - def extract_Object(o) - nil - end - - def extract_PResourceType(t) - [t.type_name, t.title] - end - - def extract_PHostClassType(t) - ['class', t.class_name] - end - private def class_from_string(str) diff --git a/spec/unit/pops/types/type_calculator_spec.rb b/spec/unit/pops/types/type_calculator_spec.rb index bd644e827..cf68b67ab 100644 --- a/spec/unit/pops/types/type_calculator_spec.rb +++ b/spec/unit/pops/types/type_calculator_spec.rb @@ -987,27 +987,6 @@ describe 'The type calculator' do end end - context "when supporting 3x it should be possible to extract type & title" do - it "from a resource" do - t = Puppet::Pops::Types::PResourceType.new() - t.type_name = 'File' - t.title = '/tmp/foo' - calculator.extract_type_and_title(t).should == ['File', '/tmp/foo'] - end - - it "from a class" do - t = Puppet::Pops::Types::PHostClassType.new() - t.class_name = 'peekaboo' - calculator.extract_type_and_title(t).should == ['class', 'peekaboo'] - end - - it "but not from anything else" do - [Object, Numeric, Integer, Fixnum, Bignum, Float, String, Regexp, Array, Hash].each do |t| - calculator.extract_type_and_title(calculator.type(t)).should == nil - end - end - end - matcher :be_assignable_to do |type| calc = Puppet::Pops::Types::TypeCalculator.new From dfedea7974f4562de8b7bb79de65fdb898e5ed66 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 2 Dec 2013 17:53:42 +0100 Subject: [PATCH 140/800] (#22363) Add file/line when creating resources and parameters The previous version of the logic cheated with "todo" and -1. Now the file and line is picked up from the reference expression. While not 100% accurate (it gives the resource expression as a whole, or the parameter expr as a whole as the location instead of the more detailed expression (one particular title etc). --- lib/puppet/pops/evaluator/runtime3_support.rb | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/lib/puppet/pops/evaluator/runtime3_support.rb b/lib/puppet/pops/evaluator/runtime3_support.rb index 289267322..9dcbba992 100644 --- a/lib/puppet/pops/evaluator/runtime3_support.rb +++ b/lib/puppet/pops/evaluator/runtime3_support.rb @@ -206,12 +206,12 @@ module Puppet::Pops::Evaluator::Runtime3Support end # The o is used for source reference - # TODO: The line information is wrong - it is cheating atm. def create_resource_parameter(o, scope, name, value, operator) + file, line = extract_file_line(o) Puppet::Parser::Resource::Param.new( :name => name, :value => convert(value, scope), # converted to 3x since 4x supports additional objects / types - :source => scope.source, :line => -1, :file => 'TODO:Get file', + :source => scope.source, :line => line, :file => file, :add => operator == :'+>' ) end @@ -224,14 +224,19 @@ module Puppet::Pops::Evaluator::Runtime3Support # resolve in scope. TODO: Investigate what happens here - opportunity to optimize? fully_qualified_type, resource_titles = scope.resolve_type_and_titles(type_name, resource_titles) + # Not 100% accurate as this is the resource expression location and each title is processed separately + # The titles are however the result of evaluation and they have no location at this point (an array + # of positions for the source expressions are required for this to work. + # TODO: Revisit and possible improve the accuracy. + # + file, line = extract_file_line(o) # Build a resource for each title resource_titles.map do |resource_title| resource = Puppet::Parser::Resource.new( fully_qualified_type, resource_title, :parameters => evaluated_parameters, - # TODO: Location - :file => 'TODO: file location', - :line => -1, + :file => file, + :line => line, :exported => exported, :virtual => virtual, # WTF is this? Which source is this? The file? The name of the context ? @@ -401,6 +406,12 @@ module Puppet::Pops::Evaluator::Runtime3Support end end + def extract_file_line(o) + source_pos = Puppet::Pops::Utils.find_adapter(o, Puppet::Pops::Adapters::SourcePosAdapter) + return [nil, -1] unless source_pos + [source_pos.locator.file, source_pos.line] + end + # Creates a diagnostic producer def diagnostic_producer Puppet::Pops::Validation::DiagnosticProducer.new( From d91ec644c9ea2bb6235533371d9d938e50c15a6a Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 2 Dec 2013 23:29:12 +0100 Subject: [PATCH 141/800] (#22363) Add file/line information to resource operations The resource related statments were using fake file and line infomation. This is now picked up from the source position / locator and passed on to 3x logic. --- lib/puppet/pops/evaluator/runtime3_support.rb | 42 +++++++++++++------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/lib/puppet/pops/evaluator/runtime3_support.rb b/lib/puppet/pops/evaluator/runtime3_support.rb index 9dcbba992..472827730 100644 --- a/lib/puppet/pops/evaluator/runtime3_support.rb +++ b/lib/puppet/pops/evaluator/runtime3_support.rb @@ -10,15 +10,20 @@ module Puppet::Pops::Evaluator::Runtime3Support # @param semantic [Puppet::Pops::ModelPopsObject] the object for which evaluation failed in some way. Used to determine origin. # @param options [Hash] hash of optional named data elements for the given issue # @return [!] this method does not return - # @raise [Puppet::ParseError] an evaluation error initialized from the arguments (TODO: Change to EvaluationError) + # @raise [Puppet::ParseError] an evaluation error initialized from the arguments (TODO: Change to EvaluationError?) # def fail(issue, semantic, options={}, except=nil) diagnostic_producer.accept(issue, semantic, options, except) end # Binds the given variable name to the given value in the given scope. - # The reference object `o` is used for origin information. - # @todo yardoc this, and pass on origin + # The reference object `o` is intended to be used for origin information - the 3x scope implementation + # only makes use of location when there is an error. This is now handled by other mechanisms; first a check + # is made if a variable exists and an error is raised if attempting to change an immutable value. Errors + # in name, numeric variable assignment etc. have also been validated prior to this call. In the event the + # scope.setvar still raises an error, the general exception handling for evaluation of the assignment + # expression knows about its location. Because of this, there is no need to extract the location for each + # setting (extraction is somewhat expensive since 3x requires line instead of offset). # def set_variable(name, value, o, scope) scope.setvar(name, value) @@ -55,8 +60,7 @@ module Puppet::Pops::Evaluator::Runtime3Support end def set_match_data(match_data, o, scope) - # TODO: Get file, line from semantic o and pass as options to scope since it tracks where these values - # came from. (No it does not! It simply uses them to report errors). + # See set_variable for rationale for not passing file and line to ephemeral_from. # NOTE: The 3x scope adds one ephemeral(match) to its internal stack per match that succeeds ! It never # clears anything. Thus a context that performs many matches will get very deep (there simply is no way to # clear the match variables without rolling back the ephemeral stack.) @@ -148,6 +152,7 @@ module Puppet::Pops::Evaluator::Runtime3Support # # TODO: logic that creates a PCatalogEntryType should resolve it to ensure it is loaded (to the best of known_resource_types knowledge). # If this is not done, the order in which things are done may be different? OTOH, it probably works anyway :-) + # TODO: Not sure if references needs to be resolved via the scope? # # And if that is not enough, a source/target may be a Collector (a baked query that will be evaluated by the # compiler - it is simply passed through here for processing by the compiler at the right time). @@ -172,8 +177,8 @@ module Puppet::Pops::Evaluator::Runtime3Support scope.compiler.add_relationship(Puppet::Parser::Relationship.new(source_resource, target_resource, relationship_type)) end - # Box value `v` to numeric or fails. - # The given value `v` is converted to Numeric, and if that fails the operation + # Coerce value `v` to numeric or fails. + # The given value `v` is coerced to Numeric, and if that fails the operation # calls {#fail}. # @param v [Object] the value to convert # @param o [Object] originating instruction @@ -219,17 +224,19 @@ module Puppet::Pops::Evaluator::Runtime3Support def create_resources(o, scope, virtual, exported, type_name, resource_titles, evaluated_parameters) # TODO: Unknown resource causes creation of Resource to fail with ArgumentError, should give - # a proper Issue + # a proper Issue. Now the result is "Error while evaluating a Resource Statement" with the message + # from the raised exception. (It may be good enough). - # resolve in scope. TODO: Investigate what happens here - opportunity to optimize? + # resolve in scope. fully_qualified_type, resource_titles = scope.resolve_type_and_titles(type_name, resource_titles) # Not 100% accurate as this is the resource expression location and each title is processed separately # The titles are however the result of evaluation and they have no location at this point (an array - # of positions for the source expressions are required for this to work. + # of positions for the source expressions are required for this to work). # TODO: Revisit and possible improve the accuracy. # file, line = extract_file_line(o) + # Build a resource for each title resource_titles.map do |resource_title| resource = Puppet::Parser::Resource.new( @@ -261,7 +268,9 @@ module Puppet::Pops::Evaluator::Runtime3Support def create_resource_defaults(o, scope, type_name, evaluated_parameters) # Note that name must be capitalized in this 3x call # The 3x impl creates a Resource instance with a bogus title and then asks the created resource - # for the type of the name + # for the type of the name. + # Note, locations are available per parameter. + # scope.define_settings(type_name.capitalize, evaluated_parameters) end @@ -269,12 +278,19 @@ module Puppet::Pops::Evaluator::Runtime3Support # evaluated parameters are applied to all. # def create_resource_overrides(o, scope, evaluated_resources, evaluated_parameters) + # Not 100% accurate as this is the resource expression location and each title is processed separately + # The titles are however the result of evaluation and they have no location at this point (an array + # of positions for the source expressions are required for this to work. + # TODO: Revisit and possible improve the accuracy. + # + file, line = extract_file_line(o) + evaluated_resources.each do |r| resource = Puppet::Parser::Resource.new( r.type_name, r.title, :parameters => evaluated_parameters, - :file => 'TODO: file location', - :line => -1, + :file => file, + :line => line, # WTF is this? Which source is this? The file? The name of the context ? :source => scope.source, :scope => scope From a93904b9e875b82237727cd0f962ae0555ed367d Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 2 Dec 2013 23:53:57 +0100 Subject: [PATCH 142/800] (#22363) Make hash lookup agnostic for nil/undef lookup A hash lookup now interprets nil and undef as the same value when doing a lookup. --- lib/puppet/pops/evaluator/access_operator.rb | 17 ++++++++++++++++- .../pops/evaluator/evaluating_parser_spec.rb | 15 +++++++++------ 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/lib/puppet/pops/evaluator/access_operator.rb b/lib/puppet/pops/evaluator/access_operator.rb index 8e49be8ea..2fbd0c353 100644 --- a/lib/puppet/pops/evaluator/access_operator.rb +++ b/lib/puppet/pops/evaluator/access_operator.rb @@ -106,7 +106,22 @@ class Puppet::Pops::Evaluator::AccessOperator # is an array with each lookup. # def access_Hash(o, scope, keys) - result = keys.collect {|k| o[k] } + # Look up key in hash, if key is nil or :undef, try alternate form before giving up. + # This makes :undef and nil "be the same key". (The laternative is to always only write one or the other + # in all hashes - that is much harder to guarantee since the Hash is a regular Ruby hash. + # + result = keys.collect do |k| + o.fetch(k) do |key| + case key + when nil + o[:undef] + when :undef + o[:nil] + else + nil + end + end + end case result.size when 0 fail(Puppet::Pops::Issues::BAD_HASH_SLICE_ARITY, @semantic.left_expr, {:actual => keys.size}) diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index 4fa9b237b..4da7a990e 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -493,12 +493,15 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do end { - "{a=>1, b=>2, c=>3}[a]" => 1, - "{a=>1, b=>2, c=>3}[c]" => 3, - "{a=>1, b=>2, c=>3}[x]" => nil, - "{a=>1, b=>2, c=>3}[c,b]" => [3,2], - "{a=>1, b=>2, c=>3}[a,b,c]" => [1,2,3], - "{a=>{b=>{c=>'it works'}}}[a][b][c]" => 'it works' + "{a=>1, b=>2, c=>3}[a]" => 1, + "{a=>1, b=>2, c=>3}[c]" => 3, + "{a=>1, b=>2, c=>3}[x]" => nil, + "{a=>1, b=>2, c=>3}[c,b]" => [3,2], + "{a=>1, b=>2, c=>3}[a,b,c]" => [1,2,3], + "{a=>{b=>{c=>'it works'}}}[a][b][c]" => 'it works', + "$a = {undef => 10} $a[free_lunch]" => nil, + "$a = {undef => 10} $a[undef]" => 10, + "$a = {undef => 10} $a[$a[free_lunch]]" => 10, }.each do |source, result| it "should parse and evaluate the expression '#{source}' to #{result}" do parser.evaluate_string(scope, source, __FILE__).should == result From 5ab367fcc78b683eaebcf3a42c1564093f0dd368 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 3 Dec 2013 01:43:16 +0100 Subject: [PATCH 143/800] (#22363) Add test to assert that hash lookup miss == undef --- spec/unit/pops/evaluator/evaluating_parser_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index 4da7a990e..f4d310628 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -502,6 +502,7 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do "$a = {undef => 10} $a[free_lunch]" => nil, "$a = {undef => 10} $a[undef]" => 10, "$a = {undef => 10} $a[$a[free_lunch]]" => 10, + "$a = {} $a[free_lunch] == undef" => true, }.each do |source, result| it "should parse and evaluate the expression '#{source}' to #{result}" do parser.evaluate_string(scope, source, __FILE__).should == result From 0bf0e629ab73f76bae20925e570eec4b2c686090 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 3 Dec 2013 04:18:02 +0100 Subject: [PATCH 144/800] (#22363) Add label.plural support to LabelProvider This makes it easier to add plural s in error messages. --- lib/puppet/pops/label_provider.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/puppet/pops/label_provider.rb b/lib/puppet/pops/label_provider.rb index e8a75a784..3581c6183 100644 --- a/lib/puppet/pops/label_provider.rb +++ b/lib/puppet/pops/label_provider.rb @@ -35,6 +35,11 @@ class Puppet::Pops::LabelProvider "The #{label(o)}" end + # Appends 's' to (optional) text if count != 1 else an empty string + def plural_s(count, text = '') + count == 1 ? text : "#{text}s" + end + private # Produces an *indefinite article* (a/an) for the given text ('a' if From 61732650be7bf1af9c64d27e5c6d61eca03669ba Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 3 Dec 2013 05:35:03 +0100 Subject: [PATCH 145/800] (#22363) Improve label provider There were labels missing for Booleans, and when given a class. Also changes labels of all types to nnn-Type as that read better in error messages; e.g. Integer-Type, String-Type. --- lib/puppet/pops/model/model_label_provider.rb | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/lib/puppet/pops/model/model_label_provider.rb b/lib/puppet/pops/model/model_label_provider.rb index 2688e3330..9744421e7 100644 --- a/lib/puppet/pops/model/model_label_provider.rb +++ b/lib/puppet/pops/model/model_label_provider.rb @@ -8,8 +8,14 @@ class Puppet::Pops::Model::ModelLabelProvider < Puppet::Pops::LabelProvider end # Produces a label for the given objects type/operator without article. + # If a Class is given, its name is used as label + # def label o - @@label_visitor.visit(o) + if o.is_a?(Class) + o.name + else + @@label_visitor.visit(o) + end end def label_Factory o ; label(o.current) end @@ -34,6 +40,8 @@ class Puppet::Pops::Model::ModelLabelProvider < Puppet::Pops::LabelProvider def label_LiteralHash o ; "Hash Expression" end def label_KeyedEntry o ; "Hash Entry" end def label_LiteralBoolean o ; "Boolean" end + def label_TrueClass o ; "Boolean" end + def label_FalseClass o ; "Boolean" end def label_LiteralString o ; "String" end def label_LambdaExpression o ; "Lambda" end def label_LiteralDefault o ; "'default' expression" end @@ -75,16 +83,14 @@ class Puppet::Pops::Model::ModelLabelProvider < Puppet::Pops::LabelProvider def label_Object o ; "Object" end def label_Hash o ; "Hash" end def label_QualifiedName o ; "Name" end - def label_QualifiedReference o ; "Type Name" end - def label_PAbstractType o ; "#{Puppet::Pops::Types::TypeCalculator.string(o)} Type" end + def label_QualifiedReference o ; "Type-Name" end + def label_PAbstractType o ; "#{Puppet::Pops::Types::TypeCalculator.string(o)}-Type" end def label_PResourceType o if o.title - "#{Puppet::Pops::Types::TypeCalculator.string(o)} Resource Reference" + "#{Puppet::Pops::Types::TypeCalculator.string(o)} Resource-Reference" else - "#{Puppet::Pops::Types::TypeCalculator.string(o)} Type" + "#{Puppet::Pops::Types::TypeCalculator.string(o)}-Type" end end - # TODO: Could use the TypeFactory to infer and output more detailed type information instead of - # just printing Object, Hash, Array, etc. end From c37da6075e3532537abcb881bd9a3d631a0ffdf9 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 3 Dec 2013 05:58:01 +0100 Subject: [PATCH 146/800] (#22363) Improve error messages from [] operator There were several cases where errors in arity and type incompatible in the given keys produced generic error messages, messages without location, and in a few cases with errors while formatting the error message. Test examples added. --- lib/puppet/pops/evaluator/access_operator.rb | 112 +++++++++++++++--- lib/puppet/pops/issues.rb | 30 +++-- lib/puppet/pops/types/type_factory.rb | 2 +- spec/unit/pops/evaluator/access_ops_spec.rb | 6 +- .../pops/evaluator/evaluating_parser_spec.rb | 43 +++++-- 5 files changed, 153 insertions(+), 40 deletions(-) diff --git a/lib/puppet/pops/evaluator/access_operator.rb b/lib/puppet/pops/evaluator/access_operator.rb index 2fbd0c353..0c8158750 100644 --- a/lib/puppet/pops/evaluator/access_operator.rb +++ b/lib/puppet/pops/evaluator/access_operator.rb @@ -27,7 +27,7 @@ class Puppet::Pops::Evaluator::AccessOperator protected def access_Object(o, scope, keys) - fail("The [] operator is not applicable to the result of the LHS expression: #{o.class}", semantic.left_expr, scope) + fail(Issues::OPERATOR_NOT_APPLICABLE, @semantic.left_expr, :operator=>'[]', :left_value => o) end def access_String(o, scope, keys) @@ -37,6 +37,7 @@ class Puppet::Pops::Evaluator::AccessOperator when 1 # Note that Ruby 1.8.7 requires a length of 1 to produce a String k1 = coerce_numeric(keys[0], @semantic.keys, scope) + bad_access_key_type(o, 0, k1, Integer) unless k1.is_a?(Integer) k2 = 1 k1 = k1 < 0 ? o.length + k1 : k1 # abs pos # if k1 is outside, a length of 1 always produces an empty string @@ -48,6 +49,8 @@ class Puppet::Pops::Evaluator::AccessOperator when 2 k1 = coerce_numeric(keys[0], @semantic.keys, scope) k2 = coerce_numeric(keys[1], @semantic.keys, scope) + [k1, k2].each_with_index { |k,i| bad_access_key_type(o, i, k, Integer) unless k.is_a?(Integer) } + k1 = k1 < 0 ? o.length + k1 : k1 # abs pos (negative is count from end) k2 = k2 < 0 ? o.length - k1 + k2 + 1 : k2 # abs length (negative k2 is length from pos to end count) # if k1 is outside, adjust to first position, and adjust length @@ -66,7 +69,11 @@ class Puppet::Pops::Evaluator::AccessOperator # Parameterizes a PRegexp Type with a pattern string or r ruby egexp # def access_PRegexpType(o, scope, keys) - fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_ARITY, o, :min=>1, :actual => keys.size) unless keys.size == 1 + unless keys.size == 1 + blamed = keys.size == 0 ? @semantic : @semantic.keys[2] + fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_ARITY, blamed, :base_type => o, :min=>1, :actual => keys.size) + end + assert_keys(keys, o, 1, 1, String, Regexp) Puppet::Pops::Types::TypeFactory.regexp(*keys) end @@ -78,12 +85,17 @@ class Puppet::Pops::Evaluator::AccessOperator fail(Puppet::Pops::Issues::BAD_ARRAY_SLICE_ARITY, @semantic.left_expr, {:actual => keys.size}) when 1 k = coerce_numeric(keys[0], @semantic.keys[0], scope) + unless k.is_a?(Integer) + bad_access_key_type(o, 0, k, Integer) + end o[k] when 2 # A slice [from, to] with support for -1 to mean start, or end respectively. k1 = coerce_numeric(keys[0], @semantic.keys[0], scope) k2 = coerce_numeric(keys[1], @semantic.keys[1], scope) + [k1, k2].each_with_index { |k,i| bad_access_key_type(o, i, k, Integer) unless k.is_a?(Integer) } + # Help confused Ruby do the right thing (it truncates to the right, but negative index + length can never overlap # the available range. k1 = k1 < 0 ? o.length + k1 : k1 # abs pos (negative is count from end) @@ -134,27 +146,72 @@ class Puppet::Pops::Evaluator::AccessOperator end end + # Ruby does not have an infinity constant. TODO: Consider having one constant in Puppet. Now it is in several places. + INFINITY = 1.0 / 0.0 + def access_PEnumType(o, scope, keys) - # TODO: Nice error handling + assert_keys(keys, o, 1, INFINITY, String) Puppet::Pops::Types::TypeFactory.enum(*keys) end def access_PVariantType(o, scope, keys) - # TODO: Nice error handling + assert_keys(keys, o, 1, INFINITY, Puppet::Pops::Types::PAbstractType) Puppet::Pops::Types::TypeFactory.variant(*keys) end def access_PStringType(o, scope, keys) - # TODO: Nice error handling - begin + assert_keys(keys, o, 1, INFINITY, String) Puppet::Pops::Types::TypeFactory.string(*keys) - rescue StandardError => e - fail(Puppet::Pops::Issues::BAD_TYPE_SPECIALIZATION, o, :message => e.message) + end + + # Asserts type of each key and calls fail with BAD_TYPE_SPECIFICATION + # @param keys [Array] the evaluated keys + # @param o [Object] evaluated LHS reported as :base_type + # @param min [Integer] the minimum number of keys (typically 1) + # @param max [Numeric] the maximum number of keys (use same as min, specific number, or INFINITY) + # @param allowed_classes [Class] a variable number of classes that each key must be an instance of (any) + # @api private + # + def assert_keys(keys, o, min, max, *allowed_classes) + size = keys.size + unless size.between?(min, max || INFINITY) + fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_ARITY, blamed, :base_type => o, :min=>1, :max => max, :actual => keys.size) + end + keys.each_with_index do |k, i| + unless allowed_classes.any? {|clazz| k.is_a?(clazz) } + bad_type_specialization_key_type(o, i, k, allowed_classes) + end end end + def bad_access_key_type(lhs, key_index, actual, *expected_classes) + fail(Puppet::Pops::Issues::BAD_SLICE_KEY_TYPE, @semantic.keys[key_index], { + :left_value => lhs, + :actual => bad_key_type_name(actual), + :expected_classes => expected_classes + }) + end + + def bad_key_type_name(actual) + case actual + when nil, :undef + 'Undef' + when :default + 'Default' + else + actual.class.name + end + end + + def bad_type_specialization_key_type(type, key_index, actual, *expected_classes) + fail(Puppet::Pops::Issues::BAD_TYPE_SPECIALIZATION, @semantic.keys[key_index], { + :type => type, + :message => "Cannot use #{bad_key_type_name(actual)} where #{expected_classes.join(' or ')} is expected" + }) + end + def access_PPatternType(o, scope, keys) - # TODO: Nice error handling + assert_keys(keys, o, 1, INFINITY, String, Regexp) Puppet::Pops::Types::TypeFactory.pattern(*keys) end @@ -164,7 +221,7 @@ class Puppet::Pops::Evaluator::AccessOperator end keys.each_with_index do |x, index| fail(Puppet::Pops::Issues::BAD_INTEGER_SLICE_TYPE, @semantic.keys[index], - {:actual => x.class}) unless (x.is_a?(Numeric) || x == :default) + {:actual => x.class}) unless (x.is_a?(Integer) || x == :default) end ranged_integer = Puppet::Pops::Types::PIntegerType.new() from, to = keys @@ -179,7 +236,7 @@ class Puppet::Pops::Evaluator::AccessOperator def access_PHashType(o, scope, keys) keys.each_with_index do |k, index| unless k.is_a?(Puppet::Pops::Types::PAbstractType) - fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[index], {:base_type => 'Hash', :actual => k.class}) + fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[index], {:base_type => 'Hash-Type', :actual => k.class}) end end case keys.size @@ -194,7 +251,7 @@ class Puppet::Pops::Evaluator::AccessOperator result.element_type = keys[1] result else - fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_ARITY, @semantic, {:base_type => 'Hash', :min => 1, :max => 2, :actual => keys.size}) + fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_ARITY, @semantic, {:base_type => 'Hash-Type', :min => 1, :max => 2, :actual => keys.size}) end end @@ -203,13 +260,13 @@ class Puppet::Pops::Evaluator::AccessOperator def access_PArrayType(o, scope, keys) if keys.size == 1 unless keys[0].is_a?(Puppet::Pops::Types::PAbstractType) - fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[0], {:base_type => 'Array', :actual => keys[0].class}) + fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[0], {:base_type => 'Array-Type', :actual => keys[0].class}) end result = Puppet::Pops::Types::PArrayType.new() result.element_type = keys[0] result else - fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_ARITY, @semantic, {:base_type => 'Array', :min => 1, :actual => keys.size}) + fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_ARITY, @semantic, {:base_type => 'Array-Type', :min => 1, :actual => keys.size}) end end @@ -247,6 +304,7 @@ class Puppet::Pops::Evaluator::AccessOperator end # type_name is LHS type_name if set, else the first given arg + keys_orig_size = keys.size type_name = o.type_name || keys.shift type_name = case type_name when Puppet::Pops::Types::PResourceType @@ -254,13 +312,31 @@ class Puppet::Pops::Evaluator::AccessOperator when String type_name.downcase else - fail(Puppet::Pops::Issues::ILLEGAL_RESOURCE_SPECIALIZATION, @semantic.keys, {:actual => type_name.class}) + blame = keys_orig_size != keys.size ? @semantic.keys[0] : @semantic.left_expr + fail(Puppet::Pops::Issues::ILLEGAL_RESOURCE_SPECIALIZATION, blame, {:actual => type_name.class}) end - keys = [nil] if keys.size < 1 # if there was only a type_name and it was consumed - result = keys.collect do |t| + + keys = [:no_title] if keys.size < 1 # if there was only a type_name and it was consumed + result = keys.each_with_index.map do |t, i| + unless t.is_a?(String) || t == :no_title + type_to_report = case t + when nil, :undef + 'Undef' + when :default + 'Default' + else + t.class.name + end + index = keys_orig_size != keys.size ? i+1 : i + fail(Puppet::Pops::Issues::BAD_TYPE_SPECIALIZATION, @semantic.keys[index], { + :type => o, + :message => "Cannot use #{type_to_report} where String is expected" + }) + end + rtype = Puppet::Pops::Types::PResourceType.new() rtype.type_name = type_name - rtype.title = t + rtype.title = (t == :no_title ? nil : t) rtype end # returns single type as type, else an array of types diff --git a/lib/puppet/pops/issues.rb b/lib/puppet/pops/issues.rb index 862d27675..8cf2abcd1 100644 --- a/lib/puppet/pops/issues.rb +++ b/lib/puppet/pops/issues.rb @@ -303,7 +303,7 @@ module Puppet::Pops::Issues end # When an attempt is made to use multiple keys (to produce a range in Ruby - e.g. $arr[2,-1]). - # This is currently not supported, but may be in future versions + # This is not supported in 3x, but it allowed in 4x. # UNSUPPORTED_RANGE = issue :UNSUPPORTED_RANGE, :count do "Attempt to use unsupported range in #{label.a_an(semantic)}, #{count} values given for max 1" @@ -334,15 +334,20 @@ module Puppet::Pops::Issues end BAD_INTEGER_SLICE_ARITY = issue :BAD_INTEGER_SLICE_ARITY, :actual do - "Integer supports [] with one or two arguments (from, to). Got #{actual}" + "Integer-Type supports [] with one or two arguments (from, to). Got #{actual}" end BAD_INTEGER_SLICE_TYPE = issue :BAD_INTEGER_SLICE_TYPE, :actual do - "Integer [] requires all arguments to be integers (or default). Got #{actual}" + "Integer-Type [] requires all arguments to be integers (or default). Got #{actual}" end - INTEGER_STEP_0 = issue :INTEGER_STEP_0 do - "Integer [] can not step with a '0' increment." + BAD_SLICE_KEY_TYPE = issue :BAD_SLICE_KEY_TYPE, :left_value, :expected_classes, :actual do + expected_text = if expected_classes.size > 1 + "one of #{expected_classes.join(', ')} are" + else + "#{expected_classes[0]} is" + end + "#{label.a_an_uc(left_value)}[] cannot use #{actual} where #{expected_text} expected" end BAD_TYPE_SLICE_TYPE = issue :BAD_TYPE_SLICE_TYPE, :base_type, :actual do @@ -350,15 +355,18 @@ module Puppet::Pops::Issues end BAD_TYPE_SLICE_ARITY = issue :BAD_TYPE_SLICE_ARITY, :base_type, :min, :max, :actual do - if max - "#{base_type}[] requires #{min} to #{max} arguments. Got #{actual}" + base_type_label = base_type.is_a?(String) ? base_type : label.a_an_uc(base_type) + if max == -1 || max == 1.0 / 0.0 # Infinity + "#{base_type_label}[] accepts #{min} or more arguments. Got #{actual}" + elsif max + "#{base_type_label}[] accepts #{min} to #{max} arguments. Got #{actual}" else - "#{base_type}[] requires #{min} arguments. Got #{actual}" + "#{base_type_label}[] accepts #{min} #{label.plural_s(min, 'argument')}. Got #{actual}" end end - BAD_TYPE_SPECIALIZATION = hard_issue :BAD_TYPE_SPECIALIZATION, :message do - "Error creating type specialization of #{label.a_an(semantic)}, #{message}" + BAD_TYPE_SPECIALIZATION = hard_issue :BAD_TYPE_SPECIALIZATION, :type, :message do + "Error creating type specialization of #{label.a_an(type)}, #{message}" end ILLEGAL_TYPE_SPECIALIZATION = issue :ILLEGAL_TYPE_SPECIALIZATION, :kind do @@ -366,7 +374,7 @@ module Puppet::Pops::Issues end ILLEGAL_RESOURCE_SPECIALIZATION = issue :ILLEGAL_RESOURCE_SPECIALIZATION, :actual do - "First argument to Resource[] must be a resource type or a string. Got #{actual}." + "First argument to Resource[] must be a resource type or a String. Got #{actual}." end ILLEGAL_HOSTCLASS_NAME = hard_issue :ILLEGAL_HOSTCLASS_NAME, :name do diff --git a/lib/puppet/pops/types/type_factory.rb b/lib/puppet/pops/types/type_factory.rb index 88f88ecbd..58491fb97 100644 --- a/lib/puppet/pops/types/type_factory.rb +++ b/lib/puppet/pops/types/type_factory.rb @@ -92,7 +92,7 @@ module Puppet::Pops::Types::TypeFactory def self.regexp(pattern = nil) t = Types::PRegexpType.new() if pattern - t.pattern = pattern.is_a?(Regexp) ? pattern.inspect[1..-1] : pattern + t.pattern = pattern.is_a?(Regexp) ? pattern.inspect[1..-2] : pattern end t end diff --git a/spec/unit/pops/evaluator/access_ops_spec.rb b/spec/unit/pops/evaluator/access_ops_spec.rb index 63987d3e3..ea44cf8f4 100644 --- a/spec/unit/pops/evaluator/access_ops_spec.rb +++ b/spec/unit/pops/evaluator/access_ops_spec.rb @@ -136,7 +136,7 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do it "gives an error if parameter is not a type" do expr = fqr('Hash')['String'] - expect { evaluate(expr)}.to raise_error(/Hash\[\] arguments must be types/) + expect { evaluate(expr)}.to raise_error(/Hash-Type\[\] arguments must be types/) end # Array @@ -153,7 +153,7 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do it "gives an error if parameter is not a type" do expr = fqr('Array')['String'] - expect { evaluate(expr)}.to raise_error(/Array\[\] arguments must be types/) + expect { evaluate(expr)}.to raise_error(/Array-Type\[\] arguments must be types/) end it 'creates a PPatternType instance when applied to a Pattern' do @@ -177,7 +177,7 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do it 'produces same class if no class name is given' do expr = fqr('Class')[fqn('apache')][] - expect { evaluate(expr) }.to raise_error(/Evaluation Error: Class\[apache\]\[\] requires 1 arguments\. Got 0/) + expect { evaluate(expr) }.to raise_error(/Evaluation Error: Class\[apache\]\[\] accepts 1 argument\. Got 0/) end it 'produces a collection of classes when multiple class names are given' do diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index f4d310628..d5df607b9 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -565,17 +565,46 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do "Resource[]" => :error, "File[]" => :error, "String[]" => :error, - "String[0]" => :error, "1[]" => :error, - "1[0]" => :error, "3.14[]" => :error, - "3.14[0]" => :error, "/.*/[]" => :error, - "/.*/[0]" => :error, "$a=[1] $a[]" => :error, }.each do |source, result| it "should parse and evaluate the expression '#{source}' to #{result}" do - expect { parser.evaluate_string(scope, source, __FILE__)}.to raise_error(Puppet::ParseError) + expect { parser.evaluate_string(scope, source, __FILE__)}.to raise_error(/Syntax error/) + end + end + + # Errors when wrong number/type of keys are used + { + "Array[0]" => 'Array-Type[] arguments must be types. Got Fixnum', + "Hash[0]" => 'Hash-Type[] arguments must be types. Got Fixnum', + "Hash[Integer, 0]" => 'Hash-Type[] arguments must be types. Got Fixnum', + "Array[Integer,String]" => 'Array-Type[] accepts 1 argument. Got 2', + "Hash[Integer,String, Hash]" => 'Hash-Type[] accepts 1 to 2 arguments. Got 3', + "'abc'[x]" => "The value 'x' cannot be converted to Numeric", + "'abc'[1.0]" => "A String[] cannot use Float where Integer is expected", + "'abc'[1,2,3]" => "String supports [] with one or two arguments. Got 3", + "Resource[0]" => 'First argument to Resource[] must be a resource type or a String. Got Fixnum', + "Resource[a, 0]" => 'Error creating type specialization of a Resource-Type, Cannot use Fixnum where String is expected', + "File[0]" => 'Error creating type specialization of a File-Type, Cannot use Fixnum where String is expected', + "String[0]" => 'Error creating type specialization of a String-Type, Cannot use Fixnum where String is expected', + "Pattern[0]" => 'Error creating type specialization of a Pattern-Type, Cannot use Fixnum where String or Regexp is expected', + "Regexp[0]" => 'Error creating type specialization of a Regexp-Type, Cannot use Fixnum where String or Regexp is expected', + "Regexp[a,b]" => 'A Regexp-Type[] accepts 1 argument. Got 2', + "true[0]" => "Operator '[]' is not applicable to a Boolean", + "1[0]" => "Operator '[]' is not applicable to an Integer", + "3.14[0]" => "Operator '[]' is not applicable to a Float", + "/.*/[0]" => "Operator '[]' is not applicable to a Regexp", + "[1][a]" => "The value 'a' cannot be converted to Numeric", + "[1][0.0]" => "An Array[] cannot use Float where Integer is expected", + "[1]['0.0']" => "An Array[] cannot use Float where Integer is expected", + "[1,2][1, 0.0]" => "An Array[] cannot use Float where Integer is expected", + "[1,2][1.0, -1]" => "An Array[] cannot use Float where Integer is expected", + "[1,2][1, -1.0]" => "An Array[] cannot use Float where Integer is expected", + }.each do |source, result| + it "should parse and evaluate the expression '#{source}' to #{result}" do + expect { parser.evaluate_string(scope, source, __FILE__)}.to raise_error(Regexp.new(Regexp.quote(result))) end end @@ -625,8 +654,8 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do "Class[/.*/]" => /A Regexp cannot be used where a String is expected/, "Class[4.1415]" => /A Float cannot be used where a String is expected/, "Class[[1,2,3]]" => /An Array cannot be used where a String is expected/, - "Class[Integer]" => /An Integer Type cannot be used where a String is expected/, - "Class[File['tmp']]" => /A File\['tmp'\] Resource Reference cannot be used where a String is expected/, + "Class[Integer]" => /An Integer-Type cannot be used where a String is expected/, + "Class[File['tmp']]" => /A File\['tmp'\] Resource-Reference cannot be used where a String is expected/, }.each do | source, error_pattern| it "an error is flagged for '#{source}'" do expect { parser.evaluate_string(scope, source, __FILE__)}.to raise_error(error_pattern) From 96862dfb6e07356b53439273bc103a4ad76aee10 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 3 Dec 2013 07:06:00 +0100 Subject: [PATCH 147/800] (#22363) Add support for Float range. A Float range behaves like an Integer range, but it does not support iteration. (It is valuable in checking type of inferred type). e.g. [1.0,4.2] =~ Array[Float[0, default]] # are floats and positive --- lib/puppet/pops/evaluator/access_operator.rb | 15 +++++++ lib/puppet/pops/issues.rb | 8 ++++ lib/puppet/pops/types/type_calculator.rb | 41 +++++++++++++++++--- lib/puppet/pops/types/type_factory.rb | 12 +++++- lib/puppet/pops/types/type_parser.rb | 20 +++++++++- lib/puppet/pops/types/types.rb | 12 ++++++ spec/unit/pops/evaluator/access_ops_spec.rb | 33 +++++++++++++++- spec/unit/pops/types/type_parser_spec.rb | 10 ++++- 8 files changed, 142 insertions(+), 9 deletions(-) diff --git a/lib/puppet/pops/evaluator/access_operator.rb b/lib/puppet/pops/evaluator/access_operator.rb index 0c8158750..aaba07d47 100644 --- a/lib/puppet/pops/evaluator/access_operator.rb +++ b/lib/puppet/pops/evaluator/access_operator.rb @@ -230,6 +230,21 @@ class Puppet::Pops::Evaluator::AccessOperator ranged_integer end + def access_PFloatType(o, scope, keys) + unless keys.size.between?(1, 2) + fail(Puppet::Pops::Issues::BAD_FLOAT_SLICE_ARITY, @semantic, {:actual => keys.size}) + end + keys.each_with_index do |x, index| + fail(Puppet::Pops::Issues::BAD_FLOAT_SLICE_TYPE, @semantic.keys[index], + {:actual => x.class}) unless (x.is_a?(Float) || x.is_a?(Integer) || x == :default) + end + ranged_float = Puppet::Pops::Types::PFloatType.new() + from, to = keys + ranged_float.from = from == :default || from.nil? ? nil : Float(from) + ranged_float.to = to == :default || to.nil? ? nil : Float(to) + ranged_float + end + # A Hash can create a new Hash type, one arg sets value type, two args sets key and value type in new type # It is not possible to create a collection of Hash types. # diff --git a/lib/puppet/pops/issues.rb b/lib/puppet/pops/issues.rb index 8cf2abcd1..f5b9017b2 100644 --- a/lib/puppet/pops/issues.rb +++ b/lib/puppet/pops/issues.rb @@ -341,6 +341,14 @@ module Puppet::Pops::Issues "Integer-Type [] requires all arguments to be integers (or default). Got #{actual}" end + BAD_FLOAT_SLICE_ARITY = issue :BAD_INTEGER_SLICE_ARITY, :actual do + "Float-Type supports [] with one or two arguments (from, to). Got #{actual}" + end + + BAD_FLOAT_SLICE_TYPE = issue :BAD_INTEGER_SLICE_TYPE, :actual do + "Float-Type [] requires all arguments to be floats, or integers (or default). Got #{actual}" + end + BAD_SLICE_KEY_TYPE = issue :BAD_SLICE_KEY_TYPE, :left_value, :expected_classes, :actual do expected_text = if expected_classes.size > 1 "one of #{expected_classes.join(', ')} are" diff --git a/lib/puppet/pops/types/type_calculator.rb b/lib/puppet/pops/types/type_calculator.rb index 0c23c7358..b7c714a7f 100644 --- a/lib/puppet/pops/types/type_calculator.rb +++ b/lib/puppet/pops/types/type_calculator.rb @@ -296,7 +296,7 @@ class Puppet::Pops::Types::TypeCalculator return result end - # Integers have range, narrow the range to the common range + # Integers have range, expand the range to the common range if t1.is_a?(Types::PIntegerType) && t2.is_a?(Types::PIntegerType) t1range = from_to_ordered(t1.from, t1.to) t2range = from_to_ordered(t2.from, t2.to) @@ -308,6 +308,18 @@ class Puppet::Pops::Types::TypeCalculator return t end + # Floats have range, expand the range to the common range + if t1.is_a?(Types::PFloatType) && t2.is_a?(Types::PFloatType) + t1range = from_to_ordered(t1.from, t1.to) + t2range = from_to_ordered(t2.from, t2.to) + t = Types::PFloatType.new() + from = [t1range[0], t2range[0]].min + to = [t1range[1], t2range[1]].max + t.from = from unless from == TheInfinity + t.to = to unless to == TheInfinity + return t + end + if t1.is_a?(Types::PStringType) && t2.is_a?(Types::PStringType) t = Types::PStringType.new() t.values = t1.values | t2.values @@ -468,7 +480,10 @@ class Puppet::Pops::Types::TypeCalculator # @api private def infer_Float(o) - Types::PFloatType.new() + t = Types::PFloatType.new() + t.from = o + t.to = o + t end # @api private @@ -633,7 +648,11 @@ class Puppet::Pops::Types::TypeCalculator # @api private def assignable_PFloatType(t, t2) - t2.is_a?(Types::PFloatType) + return false unless t2.is_a?(Types::PFloatType) + trange = from_to_ordered(t.from, t.to) + t2range = from_to_ordered(t2.from, t2.to) + # If t2 min and max are within the range of t + trange[0] <= t2range[0] && trange[1] >= t2range[1] end # @api private @@ -764,8 +783,20 @@ class Puppet::Pops::Types::TypeCalculator end end - # @api private - def string_PFloatType(t) ; "Float" ; end + def string_PFloatType(t) + result = ["Float"] + unless t.from.nil? && t.to.nil? + from = t.from.nil? ? 'default' : t.from + to = t.to.nil? ? 'default' : t.to + if from == to + "Float[#{from}]" + else + "Float[#{from}, #{to}]" + end + else + "Float" + end + end # @api private def string_PRegexpType(t) diff --git a/lib/puppet/pops/types/type_factory.rb b/lib/puppet/pops/types/type_factory.rb index 58491fb97..f45c7500f 100644 --- a/lib/puppet/pops/types/type_factory.rb +++ b/lib/puppet/pops/types/type_factory.rb @@ -13,7 +13,7 @@ module Puppet::Pops::Types::TypeFactory Types::PIntegerType.new() end - # Produces the Integer type + # Produces an Integer range type # @api public # def self.range(from, to) @@ -23,6 +23,16 @@ module Puppet::Pops::Types::TypeFactory t end + # Produces a Float range type + # @api public + # + def self.float_range(from, to) + t = Types::PFloatType.new() + t.from = Float(from) unless from == :default || from.nil? + t.to = Float(to) unless to == :default || to.nil? + t + end + # Produces the Float type # @api public # diff --git a/lib/puppet/pops/types/type_parser.rb b/lib/puppet/pops/types/type_parser.rb index bc4a8e2ca..33b7c1a06 100644 --- a/lib/puppet/pops/types/type_parser.rb +++ b/lib/puppet/pops/types/type_parser.rb @@ -84,6 +84,10 @@ class Puppet::Pops::Types::TypeParser o.value end + def interpret_LiteralFloat(o) + o.value + end + # @api private def interpret_QualifiedReference(name_ast) case name_ast.value @@ -231,7 +235,21 @@ class Puppet::Pops::Types::TypeParser TYPES.range(parameters[0] == :default ? nil : parameters[0], parameters[1] == :default ? nil : parameters[1]) end - when "object", "collection", "data", "catalogentry", "boolean", "float", "literal", "undef", "numeric", "pattern", "string" + when "float" + if parameters.size == 1 + case parameters[0] + when Integer, Float + TYPES.float_range(parameters[0], parameters[0]) + when :default + TYPES.float # unbound + end + elsif parameters.size != 2 + raise_invalid_parameters_error("Float", "1 or 2", parameters.size) + else + TYPES.float_range(parameters[0] == :default ? nil : parameters[0], parameters[1] == :default ? nil : parameters[1]) + end + + when "object", "collection", "data", "catalogentry", "boolean", "literal", "undef", "numeric", "pattern", "string" raise_unparameterized_type_error(parameterized_ast.left_expr) when "ruby", "type" diff --git a/lib/puppet/pops/types/types.rb b/lib/puppet/pops/types/types.rb index 1dceb4515..b12f44f2e 100644 --- a/lib/puppet/pops/types/types.rb +++ b/lib/puppet/pops/types/types.rb @@ -171,6 +171,18 @@ module Puppet::Pops::Types # @api public class PFloatType < PNumericType + has_attr 'from', Float, :lowerBound => 0 + has_attr 'to', Float, :lowerBound => 0 + + module ClassModule + def hash + [self.class, from, to].hash + end + + def ==(o) + self.class == o.class && from == o.from && to == o.to + end + end end # @api public diff --git a/spec/unit/pops/evaluator/access_ops_spec.rb b/spec/unit/pops/evaluator/access_ops_spec.rb index ea44cf8f4..100d195dc 100644 --- a/spec/unit/pops/evaluator/access_ops_spec.rb +++ b/spec/unit/pops/evaluator/access_ops_spec.rb @@ -16,6 +16,10 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do Puppet::Pops::Types::TypeFactory.range(from, to) end + def float_range(from, to) + Puppet::Pops::Types::TypeFactory.float_range(from, to) + end + context 'The evaluator when operating on a String' do it 'can get a single character using a single key index to []' do expect(evaluate(literal('abc')[1])).to eql('b') @@ -112,11 +116,38 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do expect(evaluate(expr)).to eql(range(1,0)) end - it 'produces an error if there are more than 2 keys' do + it 'produces an error for Integer[] if there are more than 2 keys' do expr = fqr('Integer')[1,2,3] expect { evaluate(expr)}.to raise_error(/with one or two arguments/) end + # Float + # + it 'produces a Float[from, to]' do + expr = fqr('Float')[1, 3] + expect(evaluate(expr)).to eql(float_range(1.0,3.0)) + end + + it 'produces a Float[1.0]' do + expr = fqr('Float')[1.0] + expect(evaluate(expr)).to eql(float_range(1.0,1.0)) + end + + it 'produces a Float[1]' do + expr = fqr('Float')[1] + expect(evaluate(expr)).to eql(float_range(1.0,1.0)) + end + + it 'produces a Float[from, Date: Thu, 5 Dec 2013 01:28:09 +0100 Subject: [PATCH 148/800] (maint) Fix failing tests after rebase to latest master 4 tests were failing because Puppet[:hashed_node_data] was renamed to Puppet[:trusted_node_data]. This commit changes the failing tests to use the renamed setting. --- spec/integration/parser/future_compiler_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/integration/parser/future_compiler_spec.rb b/spec/integration/parser/future_compiler_spec.rb index 7022eece1..28c5c1b1f 100644 --- a/spec/integration/parser/future_compiler_spec.rb +++ b/spec/integration/parser/future_compiler_spec.rb @@ -337,7 +337,7 @@ describe "Puppet::Parser::Compiler" do context 'when working with the trusted data hash' do context 'and have opted in to hashed_node_data' do before :each do - Puppet[:hashed_node_data] = true + Puppet[:trusted_node_data] = true end it 'should make $trusted available' do @@ -367,7 +367,7 @@ describe "Puppet::Parser::Compiler" do context 'and have not opted in to hashed_node_data' do before :each do - Puppet[:hashed_node_data] = false + Puppet[:trusted_node_data] = false end it 'should not make $trusted available' do From 82300dcdb0a3ebdc07cb4ee490e353150171d732 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 5 Dec 2013 01:55:34 +0100 Subject: [PATCH 149/800] (maint) Raise exception when reaching corner case of code merge There is one case that occured once during testing where the runtime tried to merge code in a PopsBridge::Expression. This was probably due to faulty test setup. If this happens again an exception is now raised. (There was a call to 'debugger' there). --- lib/puppet/parser/ast/pops_bridge.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/parser/ast/pops_bridge.rb b/lib/puppet/parser/ast/pops_bridge.rb index 452ebfe4f..1f79965ab 100644 --- a/lib/puppet/parser/ast/pops_bridge.rb +++ b/lib/puppet/parser/ast/pops_bridge.rb @@ -40,7 +40,7 @@ class Puppet::Parser::AST::PopsBridge other else # When does this happen ? Ever ? - require 'debugger'; debugger + raise "sequence_with called on Puppet::Parser::AST::PopsBridge::Expression - please report use case" Puppet::Parser::AST::BlockExpression.new(:children => [self] + other.children) end end From fa18883227cf43050fa31ef8bf0fbf67e2282913 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 5 Dec 2013 01:56:50 +0100 Subject: [PATCH 150/800] (maint) Remove calls to debugger in performance / serialization tests --- spec/unit/pops/benchmark_spec.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/unit/pops/benchmark_spec.rb b/spec/unit/pops/benchmark_spec.rb index ec699a672..8d4bc0820 100644 --- a/spec/unit/pops/benchmark_spec.rb +++ b/spec/unit/pops/benchmark_spec.rb @@ -31,7 +31,6 @@ $a = "interpolate ${foo} and stuff" class MyJSonSerializer < RGen::Serializer::JsonSerializer def attributeValue(value, a) x = super - require 'debugger'; debugger puts "#{a.eType} value: <<#{value}>> serialize: <<#{x}>>" x end @@ -93,7 +92,6 @@ $a = "interpolate ${foo} and stuff" end it "rgenjson", :profile => true do - require 'debugger'; debugger parser = Puppet::Pops::Parser::EvaluatingParser.new() model = parser.parse_string(code).current dumped = json_dump(model) From 7e830259b31cb31d4efd40918d7f5d5b2eb73f08 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 5 Dec 2013 02:14:45 +0100 Subject: [PATCH 151/800] (maint) Fix indentation in benchmark_spec.rb --- spec/unit/pops/benchmark_spec.rb | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/spec/unit/pops/benchmark_spec.rb b/spec/unit/pops/benchmark_spec.rb index 8d4bc0820..03c2e743d 100644 --- a/spec/unit/pops/benchmark_spec.rb +++ b/spec/unit/pops/benchmark_spec.rb @@ -37,17 +37,17 @@ $a = "interpolate ${foo} and stuff" end def json_dump(model) - output = StringWriter.new - ser = MyJSonSerializer.new(output) - ser.serialize(model) - output - end + output = StringWriter.new + ser = MyJSonSerializer.new(output) + ser.serialize(model) + output + end - def json_load(string) - env = RGen::Environment.new - inst = RGen::Instantiator::JsonInstantiator.new(env, Puppet::Pops::Model) - inst.instantiate(string) - end + def json_load(string) + env = RGen::Environment.new + inst = RGen::Instantiator::JsonInstantiator.new(env, Puppet::Pops::Model) + inst.instantiate(string) + end it "transformer", :profile => true do parser = Puppet::Pops::Parser::Parser.new() @@ -122,9 +122,9 @@ $a = "interpolate ${foo} and stuff" let(:node) { 'node.example.com' } let(:scope) { s = create_test_scope_for_node(node); s } it "evaluator", :profile => true do - # Do the loop in puppet code since it otherwise drowns in setup - puppet_loop = - 'Integer[0, 1000].each |$i| { if true + # Do the loop in puppet code since it otherwise drowns in setup + puppet_loop = + 'Integer[0, 1000].each |$i| { if true { $a = 10 + 10 } From b2e845f192273b5203006b036a9d12ca4b4ba7d9 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 5 Dec 2013 02:36:56 +0100 Subject: [PATCH 152/800] (maint) Fix typo of o-diaresis char in comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fat fingered comment with spurios ö (o-diaresis) character. --- lib/puppet/parser/ast/pops_bridge.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/parser/ast/pops_bridge.rb b/lib/puppet/parser/ast/pops_bridge.rb index 1f79965ab..601b1bafa 100644 --- a/lib/puppet/parser/ast/pops_bridge.rb +++ b/lib/puppet/parser/ast/pops_bridge.rb @@ -9,7 +9,7 @@ class Puppet::Parser::AST::PopsBridge # Bridges to one Pops Model Expression # The @value is the expression - # This is used to represent the body of a class, definition, or node, and for each parameter's defauöt value + # This is used to represent the body of a class, definition, or node, and for each parameter's default value # expression. # class Expression < Puppet::Parser::AST::Leaf From fe9112ddfde679ecf4136842cfac8e1776658b02 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 5 Dec 2013 02:38:56 +0100 Subject: [PATCH 153/800] (maint) Fix indentation of attr_reader --- lib/puppet/parser/ast/pops_bridge.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/parser/ast/pops_bridge.rb b/lib/puppet/parser/ast/pops_bridge.rb index 601b1bafa..5e9dc647d 100644 --- a/lib/puppet/parser/ast/pops_bridge.rb +++ b/lib/puppet/parser/ast/pops_bridge.rb @@ -53,7 +53,7 @@ class Puppet::Parser::AST::PopsBridge # by a resource expression (which is also used to instantiate a host class). # class Program < Puppet::Parser::AST::TopLevelConstruct - attr_reader :program_model, :context + attr_reader :program_model, :context def initialize(program_model, context = {}) @program_model = program_model From 5d70a824c237056097d58f2230bac0d0c84b6d7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Pinson?= Date: Thu, 5 Dec 2013 10:44:24 +0100 Subject: [PATCH 154/800] Fix small typo --- lib/puppet/util/command_line.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/util/command_line.rb b/lib/puppet/util/command_line.rb index a334ef8db..5713cc7df 100644 --- a/lib/puppet/util/command_line.rb +++ b/lib/puppet/util/command_line.rb @@ -82,7 +82,7 @@ module Puppet # # @return [void] def execute - Puppet::Util.exit_on_fail("intialize global default settings") do + Puppet::Util.exit_on_fail("initialize global default settings") do Puppet.initialize_settings(args) end From 5f2281bb86d82a55c3dc079cc8d490c792d5a02f Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 5 Dec 2013 19:47:48 +0100 Subject: [PATCH 155/800] (#22363) Add description of iterating an enumerable type with each. --- lib/puppet/parser/functions/each.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/puppet/parser/functions/each.rb b/lib/puppet/parser/functions/each.rb index d9014ca20..569a6c416 100644 --- a/lib/puppet/parser/functions/each.rb +++ b/lib/puppet/parser/functions/each.rb @@ -25,6 +25,14 @@ Puppet::Parser::Functions::newfunction( $a.each {|$entry| ..."key ${$entry[0]}, value ${$entry[1]}" } $a.each {|$key, $value| ..."key ${key}, value ${value}" } + When the first argument is a Type and the type is Enumerable, the parameterized block should define one + or two block parameters. + For each application of the block, the next element from the type enumerator is selected, and it is passed to + the block if the block has one parameter. If the block has two parameters, the first is the elements + index, and the second the value. The index starts from 0. + + Integer[ 10, 20 ].each {|$index, $value| ... } + - Since 3.2 - requires `parser = future`. ENDHEREDOC From 66fce665a9ec2e5f2d998dbf9a30d2978c1a6aee Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 5 Dec 2013 19:54:00 +0100 Subject: [PATCH 156/800] (maint) Comment out unreachable code in pops bridge --- lib/puppet/parser/ast/pops_bridge.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/puppet/parser/ast/pops_bridge.rb b/lib/puppet/parser/ast/pops_bridge.rb index 5e9dc647d..5bf94e3a7 100644 --- a/lib/puppet/parser/ast/pops_bridge.rb +++ b/lib/puppet/parser/ast/pops_bridge.rb @@ -41,7 +41,8 @@ class Puppet::Parser::AST::PopsBridge else # When does this happen ? Ever ? raise "sequence_with called on Puppet::Parser::AST::PopsBridge::Expression - please report use case" - Puppet::Parser::AST::BlockExpression.new(:children => [self] + other.children) + # What should be done if the above happens (We don't want this to happen). + # Puppet::Parser::AST::BlockExpression.new(:children => [self] + other.children) end end end From facef51e65a8390c480ad84a0feb7e76402494d7 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 5 Dec 2013 20:15:12 +0100 Subject: [PATCH 157/800] (yardoc) Fix typo in source pos adapter yardoc. --- lib/puppet/pops/adapters.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/pops/adapters.rb b/lib/puppet/pops/adapters.rb index 111fb9971..ac84e4e92 100644 --- a/lib/puppet/pops/adapters.rb +++ b/lib/puppet/pops/adapters.rb @@ -22,7 +22,7 @@ module Puppet::Pops::Adapters attr_accessor :origin end - # A SourcePosAdapter describes hold a reference to something *locateable* (a position in source text). + # A SourcePosAdapter holds a reference to something *locateable* (a position in source text). # This is represented by an instance of Puppet::Pops::Parser::Locateable (it has an offset, a length, and # a Puppet::Pops::Parser::Locator) that are used together to provide derived information (line, and position # on line). From 4406e450d75447d23a376a6849e61e0f4f9f54d5 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 5 Dec 2013 20:43:26 +0100 Subject: [PATCH 158/800] (yardoc) Fix stale yardoc for Resource [] operator The documentation had typos and was wrong since support was added for getting the property of a fully qualified resource reference. I.e. File['foo']['bar'] gets the 'bar' property of the referenced file resource. Also had stale documentation stating that [] keys produced a deep copy. That is wrong - it produces a syntax error, and if done from code, it fails with an arity error. --- lib/puppet/pops/evaluator/access_operator.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/puppet/pops/evaluator/access_operator.rb b/lib/puppet/pops/evaluator/access_operator.rb index aaba07d47..aedb122c5 100644 --- a/lib/puppet/pops/evaluator/access_operator.rb +++ b/lib/puppet/pops/evaluator/access_operator.rb @@ -290,12 +290,12 @@ class Puppet::Pops::Evaluator::AccessOperator # @example # Resource[File] # => File # Resource[File, 'foo'] # => File[foo] - # Resource[File. 'foo', 'bar] # => [File[foo], File[bar]] + # Resource[File. 'foo', 'bar'] # => [File[foo], File[bar]] # File['foo', 'bar'] # => [File[foo], File[bar]] - # File['foo']['bar'] # => ERROR + # File['foo']['bar'] # => Value of the 'bar' parameter in the File['foo'] resource # Resource[File]['foo', 'bar'] # => [File[Foo], File[bar]] # Resource[File, 'foo', 'bar'] # => [File[foo], File[bar]] - # Resource[???][] # => deep copy of the type + # Resource[File, 'foo']['bar'] # => Value of the 'bar' parameter in the File['foo'] resource # def access_PResourceType(o, scope, keys) if keys.size == 0 From 157ac49fa262f551496f8ca38b0084179c42cff7 Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Thu, 5 Dec 2013 11:48:29 -0800 Subject: [PATCH 159/800] (maint) Fix spelling in a comment. --- lib/puppet/graph/relationship_graph.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/puppet/graph/relationship_graph.rb b/lib/puppet/graph/relationship_graph.rb index 54a38ad54..3c04faa12 100644 --- a/lib/puppet/graph/relationship_graph.rb +++ b/lib/puppet/graph/relationship_graph.rb @@ -180,22 +180,22 @@ class Puppet::Graph::RelationshipGraph < Puppet::Graph::SimpleGraph end # Impose our container information on another graph by using it - # to replace any container vertices X with a pair of verticies - # { admissible_X and completed_X } such that that + # to replace any container vertices X with a pair of vertices + # { admissible_X and completed_X } such that # # 0) completed_X depends on admissible_X # 1) contents of X each depend on admissible_X # 2) completed_X depends on each on the contents of X - # 3) everything which depended on X depens on completed_X + # 3) everything which depended on X depends on completed_X # 4) admissible_X depends on everything X depended on # 5) the containers and their edges must be removed # # Note that this requires attention to the possible case of containers # which contain or depend on other containers, but has the advantage # that the number of new edges created scales linearly with the number - # of contained verticies regardless of how containers are related; + # of contained vertices regardless of how containers are related; # alternatives such as replacing container-edges with content-edges - # scale as the product of the number of external dependences, which is + # scale as the product of the number of external dependencies, which is # to say geometrically in the case of nested / chained containers. # Default_label = { :callback => :refresh, :event => :ALL_EVENTS } @@ -207,8 +207,8 @@ class Puppet::Graph::RelationshipGraph < Puppet::Graph::SimpleGraph # # These two hashes comprise the aforementioned attention to the possible # case of containers that contain / depend on other containers; they map - # containers to their sentinels but pass other verticies through. Thus we - # can "do the right thing" for references to other verticies that may or + # containers to their sentinels but pass other vertices through. Thus we + # can "do the right thing" for references to other vertices that may or # may not be containers. # admissible = Hash.new { |h,k| k } From 16c909769807f545e395e55c7c3879334052b1b8 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 6 Dec 2013 01:55:30 +0100 Subject: [PATCH 160/800] (#22363) Remove validation of relationship expressions Relationship expressions had validation that their LHS and RHS where of certain types. This was moot since almost all expressions are capable of producing a value that it is meaningful to use in a relationship expression. The restrictions were artificial due to problems in the 3x grammar. This also adds a test for relation between two resources. More work is required to complete the testing for relationships and resource expression R-values. --- lib/puppet/pops/validation/checker4_0.rb | 42 ++----------------- .../pops/evaluator/evaluating_parser_spec.rb | 8 +++- 2 files changed, 10 insertions(+), 40 deletions(-) diff --git a/lib/puppet/pops/validation/checker4_0.rb b/lib/puppet/pops/validation/checker4_0.rb index 85bbb0016..a03e4b0f1 100644 --- a/lib/puppet/pops/validation/checker4_0.rb +++ b/lib/puppet/pops/validation/checker4_0.rb @@ -24,7 +24,6 @@ class Puppet::Pops::Validation::Checker4_0 @@assignment_visitor ||= Puppet::Pops::Visitor.new(nil, "assign", 0, 1) @@query_visitor ||= Puppet::Pops::Visitor.new(nil, "query", 0, 0) @@top_visitor ||= Puppet::Pops::Visitor.new(nil, "top", 1, 1) - @@relation_visitor ||= Puppet::Pops::Visitor.new(nil, "relation", 1, 1) @acceptor = diagnostics_producer end @@ -55,11 +54,6 @@ class Puppet::Pops::Validation::Checker4_0 @@query_visitor.visit_this_0(self, o) end - # Performs check if this is valid as a relationship side - def relation(o, container) - @@relation_visitor.visit_this_1(self, o, container) - end - # Performs check if this is valid as a rvalue def rvalue(o) @@rvalue_visitor.visit_this_0(self, o) @@ -267,27 +261,6 @@ class Puppet::Pops::Validation::Checker4_0 query(o.expr) if o.expr # is optional end - def relation_Object(o, rel_expr) - acceptor.accept(Issues::ILLEGAL_EXPRESSION, o, {:feature => o.eContainingFeature, :container => rel_expr}) - end - - def relation_AccessExpression(o, rel_expr); end - - def relation_CollectExpression(o, rel_expr); end - - def relation_VariableExpression(o, rel_expr); end - - def relation_LiteralString(o, rel_expr); end - - def relation_ConcatenatedStringExpression(o, rel_expr); end - - def relation_SelectorExpression(o, rel_expr); end - - def relation_CaseExpression(o, rel_expr); end - - def relation_ResourceExpression(o, rel_expr); end - - def relation_RelationshipExpression(o, rel_expr); end def check_Parameter(o) if o.name =~ /^[0-9]+$/ @@ -295,18 +268,9 @@ class Puppet::Pops::Validation::Checker4_0 end end - #relationship_side: resource - # | resourceref - # | collection - # | variable - # | quotedtext - # | selector - # | casestatement - # | hasharrayaccesses - def check_RelationshipExpression(o) - relation(o.left_expr, o) - relation(o.right_expr, o) + rvalue(o.left_expr) + rvalue(o.right_expr) end def check_ResourceExpression(o) @@ -475,7 +439,7 @@ class Puppet::Pops::Validation::Checker4_0 # def rvalue_UnlessExpression(o) ; acceptor.accept(Issues::NOT_RVALUE, o) ; end - def rvalue_ResourceExpression(o) ; acceptor.accept(Issues::NOT_RVALUE, o) ; end +# def rvalue_ResourceExpression(o) ; acceptor.accept(Issues::NOT_RVALUE, o) ; end def rvalue_ResourceDefaultsExpression(o); acceptor.accept(Issues::NOT_RVALUE, o) ; end diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index d5df607b9..6fc772367 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -818,12 +818,18 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do end context "When evaluating relationships" do - it 'should form a relation with ->' do + it 'should form a relation with File[a] -> File[b]' do source = "File[a] -> File[b]" parser.evaluate_string(scope, source, __FILE__) scope.compiler.should have_relationship(['File', 'a', '->', 'File', 'b']) end + it 'should form a relation with resource -> resource' do + source = "notify{a:} -> notify{b:}" + parser.evaluate_string(scope, source, __FILE__) + scope.compiler.should have_relationship(['Notify', 'a', '->', 'Notify', 'b']) + end + it 'should form a relation with <-' do source = "File[a] <- File[b]" parser.evaluate_string(scope, source, __FILE__) From 1d121c30ed717a14e7978cccf92a4bbcac4dcf2a Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 6 Dec 2013 01:59:17 +0100 Subject: [PATCH 161/800] (maint) Fix stale comments and remove commented out code. --- spec/integration/parser/future_compiler_spec.rb | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/spec/integration/parser/future_compiler_spec.rb b/spec/integration/parser/future_compiler_spec.rb index 28c5c1b1f..a84d411bd 100644 --- a/spec/integration/parser/future_compiler_spec.rb +++ b/spec/integration/parser/future_compiler_spec.rb @@ -7,10 +7,7 @@ require 'puppet_spec/pops' require 'puppet_spec/scope' require 'rgen/metamodel_builder' -# This is a copy of the compiler_spec, with hacks to try to make it run for the future -# evaluator. -# :-( -# The first failing test does not fail if executed alone. +# Test compilation using the future evaluator # describe "Puppet::Parser::Compiler" do include PuppetSpec::Compiler @@ -18,7 +15,7 @@ describe "Puppet::Parser::Compiler" do before :each do Puppet[:parser] = 'future' - # This is in the original test - what is this for? Does not seem to make a differenceself.code + # This is in the original test - what is this for? Does not seem to make a differencese at all @scope_resource = stub 'scope_resource', :builtin? => true, :finish => nil, :ref => 'Class[main]' @scope = stub 'scope', :resource => @scope_resource, :source => mock("source") end @@ -28,8 +25,6 @@ describe "Puppet::Parser::Compiler" do Puppet.settings.clear end - # shared because tests are invoked both for classic and future parser - # describe "the compiler when using future parser and evaluator" do it "should be able to determine the configuration version from a local version control repository" do pending("Bug #14071 about semantics of Puppet::Util::Execute on Windows", :if => Puppet.features.microsoft_windows?) do @@ -60,7 +55,6 @@ describe "Puppet::Parser::Compiler" do node = Puppet::Node.new("testnodex") node.classes = ['foo', 'bar'] - #node.stubs(:classes).returns(['foo', 'bar']) catalog = Puppet::Parser::Compiler.compile(node) node.classes = nil catalog.resource("Notify[foo_notify]").should_not be_nil From 718ebc45ba9bfa6a82ba732a1ddc5a8f0436736b Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 6 Dec 2013 02:54:24 +0100 Subject: [PATCH 162/800] (#22363) Fix problems from changed validation of relationships. The vaidation was changed from a specific relationship validation to using just R-Value check. This was a mistake since a CollectExpression is a valid operand in a relationship even if not producing an R-Value. Old version resurrected and streamlined to do either R-Value check, or check that operand is a Collector. --- lib/puppet/pops/validation/checker4_0.rb | 34 +++++++++++++------ .../parser/future_compiler_spec.rb | 2 +- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/lib/puppet/pops/validation/checker4_0.rb b/lib/puppet/pops/validation/checker4_0.rb index a03e4b0f1..705989331 100644 --- a/lib/puppet/pops/validation/checker4_0.rb +++ b/lib/puppet/pops/validation/checker4_0.rb @@ -24,6 +24,7 @@ class Puppet::Pops::Validation::Checker4_0 @@assignment_visitor ||= Puppet::Pops::Visitor.new(nil, "assign", 0, 1) @@query_visitor ||= Puppet::Pops::Visitor.new(nil, "query", 0, 0) @@top_visitor ||= Puppet::Pops::Visitor.new(nil, "top", 1, 1) + @@relation_visitor ||= Puppet::Pops::Visitor.new(nil, "relation", 0, 0) @acceptor = diagnostics_producer end @@ -54,6 +55,11 @@ class Puppet::Pops::Validation::Checker4_0 @@query_visitor.visit_this_0(self, o) end + # Performs check if this is valid as a relationship side + def relation(o) + @@relation_visitor.visit_this_0(self, o) + end + # Performs check if this is valid as a rvalue def rvalue(o) @@rvalue_visitor.visit_this_0(self, o) @@ -261,6 +267,13 @@ class Puppet::Pops::Validation::Checker4_0 query(o.expr) if o.expr # is optional end + def relation_Object(o) + rvalue(o) + end + + def relation_CollectExpression(o); end + + def relation_RelationshipExpression(o); end def check_Parameter(o) if o.name =~ /^[0-9]+$/ @@ -268,9 +281,18 @@ class Puppet::Pops::Validation::Checker4_0 end end + #relationship_side: resource + # | resourceref + # | collection + # | variable + # | quotedtext + # | selector + # | casestatement + # | hasharrayaccesses + def check_RelationshipExpression(o) - rvalue(o.left_expr) - rvalue(o.right_expr) + relation(o.left_expr) + relation(o.right_expr) end def check_ResourceExpression(o) @@ -433,14 +455,6 @@ class Puppet::Pops::Validation::Checker4_0 def rvalue_BlockExpression(o) ; acceptor.accept(Issues::NOT_RVALUE, o) ; end -# def rvalue_CaseExpression(o) ; acceptor.accept(Issues::NOT_RVALUE, o) ; end - -# def rvalue_IfExpression(o) ; acceptor.accept(Issues::NOT_RVALUE, o) ; end - -# def rvalue_UnlessExpression(o) ; acceptor.accept(Issues::NOT_RVALUE, o) ; end - -# def rvalue_ResourceExpression(o) ; acceptor.accept(Issues::NOT_RVALUE, o) ; end - def rvalue_ResourceDefaultsExpression(o); acceptor.accept(Issues::NOT_RVALUE, o) ; end def rvalue_ResourceOverrideExpression(o); acceptor.accept(Issues::NOT_RVALUE, o) ; end diff --git a/spec/integration/parser/future_compiler_spec.rb b/spec/integration/parser/future_compiler_spec.rb index a84d411bd..b2c606e0f 100644 --- a/spec/integration/parser/future_compiler_spec.rb +++ b/spec/integration/parser/future_compiler_spec.rb @@ -15,7 +15,7 @@ describe "Puppet::Parser::Compiler" do before :each do Puppet[:parser] = 'future' - # This is in the original test - what is this for? Does not seem to make a differencese at all + # This is in the original test - what is this for? Does not seem to make a difference at all @scope_resource = stub 'scope_resource', :builtin? => true, :finish => nil, :ref => 'Class[main]' @scope = stub 'scope', :resource => @scope_resource, :source => mock("source") end From 47075397133d5ba3f6e947b5af617fb4dd4220cc Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 6 Dec 2013 04:19:36 +0100 Subject: [PATCH 163/800] (#22363) Fix faulty references to Puppet::AST There were references to Puppet::AST, the name is actually Puppet::Parser::AST - but the long references were not needed - the class it is instantiating is in the same name space. --- lib/puppet/parser/ast/pops_bridge.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/puppet/parser/ast/pops_bridge.rb b/lib/puppet/parser/ast/pops_bridge.rb index 5bf94e3a7..7b697b031 100644 --- a/lib/puppet/parser/ast/pops_bridge.rb +++ b/lib/puppet/parser/ast/pops_bridge.rb @@ -133,7 +133,7 @@ class Puppet::Parser::AST::PopsBridge args = { :module_name => modname } unless is_nop?(o.body) - args[:code] = Puppet::AST::Bridge::PopsBridge::Expression.new(:value => o.body) + args[:code] = Expression.new(:value => o.body) end unless is_nop?(o.parent) @@ -148,7 +148,7 @@ class Puppet::Parser::AST::PopsBridge end def code() - Puppet::AST::Bridge::PopsBridge::Expression.new(:value => @value) + Expression.new(:value => @value) end def is_nop?(o) From 9f0fbb34dd198d290067fcc9268ef8513dd51cda Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 6 Dec 2013 10:09:10 -0800 Subject: [PATCH 164/800] (#23373) Update acceptance test to use config set This is the first, simple case of using a `config set` subcommand to manipulate the puppet.conf file. --- .../tests/config/apply_file_metadata_specified_in_config.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/acceptance/tests/config/apply_file_metadata_specified_in_config.rb b/acceptance/tests/config/apply_file_metadata_specified_in_config.rb index 89e191466..a974e2349 100644 --- a/acceptance/tests/config/apply_file_metadata_specified_in_config.rb +++ b/acceptance/tests/config/apply_file_metadata_specified_in_config.rb @@ -16,10 +16,7 @@ agents.each do |agent| } SITE - create_test_file(agent, 'puppet.conf', <<-CONF) - [user] - logdir = #{logdir} { owner = root, group = root, mode = 0700 } - CONF + on(agent, puppet('config', 'set', 'logdir', "'#{logdir} { owner = root, group = root, mode = 0700 }'", '--configdir', get_test_file_path(agent, ''))) on(agent, puppet('apply', get_test_file_path(agent, 'site.pp'), '--confdir', get_test_file_path(agent, ''))) From 625b3dcf0fc722a20f5feaa5f1255da754fb6f09 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 6 Dec 2013 11:07:48 -0800 Subject: [PATCH 165/800] (Maint) Allow specifying tests to run to rake task --- Rakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rakefile b/Rakefile index 97172a50d..b6111380d 100644 --- a/Rakefile +++ b/Rakefile @@ -64,5 +64,5 @@ task :default do end task :spec do - sh %{rspec spec} + sh %{rspec #{ENV['TESTS'] || 'spec'}} end From 6505d15477ccbb901e5af0006bbb262899d5cde3 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 6 Dec 2013 11:10:30 -0800 Subject: [PATCH 166/800] (#23373) Allow basic control of puppet.conf This provides a new action on the puppet config subcommand to allow setting configuration values in the puppet.conf file. This implemenation does not understand sections and so will simply change the first instance of the configuration parameter that it is trying to change. --- lib/puppet/face/config.rb | 25 ++++++++ lib/puppet/settings.rb | 42 +++++++------ lib/puppet/settings/config_file.rb | 85 ++++++++++++++++++++++++++ spec/unit/settings/config_file_spec.rb | 57 +++++++++++++++++ 4 files changed, 189 insertions(+), 20 deletions(-) diff --git a/lib/puppet/face/config.rb b/lib/puppet/face/config.rb index b30c5baf8..84b2e704a 100644 --- a/lib/puppet/face/config.rb +++ b/lib/puppet/face/config.rb @@ -45,4 +45,29 @@ Puppet::Face.define(:config, '0.0.1') do nil end end + + action(:set) do + summary "Set Puppet's configuration settings." + arguments "[setting_name] [setting_value]" + description <<-'EOT' + Update values in the `puppet.conf` configuration file. + EOT + examples <<-'EOT' + Set puppet's runfile directory: + + $ puppet config set rundir /var/run/puppet + EOT + + when_invoked do |*args| + name, value = args + + file = Puppet::FileSystem::File.new(Puppet.settings.which_configuration_file) + file.open(nil, 'a+') do |file| + Puppet::Settings::ConfigFile.update(file) do |config| + config.set(name, value) + end + end + nil + end + end end diff --git a/lib/puppet/settings.rb b/lib/puppet/settings.rb index ca017ae1e..e688202de 100644 --- a/lib/puppet/settings.rb +++ b/lib/puppet/settings.rb @@ -1030,6 +1030,28 @@ Generated on #{Time.now}. val end + ## + # (#15337) All of the logic to determine the configuration file to use + # should be centralized into this method. The simplified approach is: + # + # 1. If there is an explicit configuration file, use that. (--confdir or + # --config) + # 2. If we're running as a root process, use the system puppet.conf + # (usually /etc/puppet/puppet.conf) + # 3. Otherwise, use the user puppet.conf (usually ~/.puppet/puppet.conf) + # + # @api private + # @todo this code duplicates {Puppet::Util::RunMode#which_dir} as described + # in {http://projects.puppetlabs.com/issues/16637 #16637} + def which_configuration_file + if explicit_config_file? or Puppet.features.root? then + return main_config_file + else + return user_config_file + end + end + + private def get_config_file_default(default) @@ -1114,26 +1136,6 @@ Generated on #{Time.now}. end private :clear_everything_for_tests - ## - # (#15337) All of the logic to determine the configuration file to use - # should be centralized into this method. The simplified approach is: - # - # 1. If there is an explicit configuration file, use that. (--confdir or - # --config) - # 2. If we're running as a root process, use the system puppet.conf - # (usually /etc/puppet/puppet.conf) - # 3. Otherwise, use the user puppet.conf (usually ~/.puppet/puppet.conf) - # - # @todo this code duplicates {Puppet::Util::RunMode#which_dir} as described - # in {http://projects.puppetlabs.com/issues/16637 #16637} - def which_configuration_file - if explicit_config_file? or Puppet.features.root? then - return main_config_file - else - return user_config_file - end - end - def explicit_config_file? # Figure out if the user has provided an explicit configuration file. If # so, return the path to the file, if not return nil. diff --git a/lib/puppet/settings/config_file.rb b/lib/puppet/settings/config_file.rb index b1120105b..b3756bcf9 100644 --- a/lib/puppet/settings/config_file.rb +++ b/lib/puppet/settings/config_file.rb @@ -12,6 +12,91 @@ class Puppet::Settings::ConfigFile @value_converter = value_converter end + def self.update(config_fh, &block) + config = parse(config_fh) + manipulator = Puppet::Settings::ConfigFile::Manipulator.new(config) + yield manipulator + config.write(config_fh) + end + + Line = Struct.new(:text) do + def write(fh) + fh.puts(text) + end + end + + SettingLine = Struct.new(:prefix, :name, :infix, :value, :suffix) do + def write(fh) + fh.write(prefix) + fh.write(name) + fh.write(infix) + fh.write(value) + fh.puts(suffix) + end + end + + class Config + def initialize + @lines = [] + end + + def <<(line) + @lines << line + end + + def each_setting + @lines.each do |line| + if line.is_a?(SettingLine) + yield line + end + end + end + + def setting(name) + @lines.find do |line| + line.is_a?(SettingLine) && line.name == name + end + end + + def write(fh) + fh.truncate(0) + fh.rewind + @lines.each do |line| + line.write(fh) + end + fh.flush + end + end + + def self.parse(config_fh) + config = Config.new + config_fh.each_line do |line| + case line + when /^(\s*)(\w+)(\s*=\s*)(.*?)(\s*)$/ # settings + config << SettingLine.new($1, $2, $3, $4, $5) + else + config << Line.new(line) + end + end + + config + end + + class Manipulator + def initialize(config) + @config = config + end + + def set(name, value) + setting = @config.setting(name) + if setting + setting.value = value + else + @config << SettingLine.new("", name, "=", value, "") + end + end + end + def parse_file(file, text) result = {} count = 0 diff --git a/spec/unit/settings/config_file_spec.rb b/spec/unit/settings/config_file_spec.rb index 0d3112097..e59f2c790 100644 --- a/spec/unit/settings/config_file_spec.rb +++ b/spec/unit/settings/config_file_spec.rb @@ -96,5 +96,62 @@ describe Puppet::Settings::ConfigFile do config.parse_file(filename, "mode = value").should == { :main => section_containing(:mode => "value") } end + + context "when updating" do + it "preserves the file when no changes are made" do + original_config = <<-CONFIG + # comment + [section] + name = value + CONFIG + config = a_config_file_containing(original_config) + + Puppet::Settings::ConfigFile.update(config) do; end + + expect(config.string).to eq original_config + end + + it "adds a set name and value to an empty file" do + config = a_config_file_containing("") + + Puppet::Settings::ConfigFile.update(config) do |config| + config.set("name", "value") + end + + expect(config.string).to eq "name=value\n" + end + + it "preserves comments when writing a new name and value" do + config = a_config_file_containing("# this is a comment") + + Puppet::Settings::ConfigFile.update(config) do |config| + config.set("name", "value") + end + + expect(config.string).to eq "# this is a comment\nname=value\n" + end + + it "updates existing names and values in place" do + config = a_config_file_containing(<<-CONFIG) + # this is the preceeding comment + name = original value + # this is the trailing comment + CONFIG + + Puppet::Settings::ConfigFile.update(config) do |config| + config.set("name", "changed value") + end + + expect(config.string).to eq <<-CONFIG + # this is the preceeding comment + name = changed value + # this is the trailing comment + CONFIG + end + + def a_config_file_containing(text) + StringIO.new(text) + end + end end From e4b7556947ea6b15eead13c45723445ef80bde84 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 6 Dec 2013 11:32:13 -0800 Subject: [PATCH 167/800] (#23373) Remove duplicate config file parsers This merges together the two concrete parsers for the puppet config file. The transformation into the section groups is now done as a second pass that works on the individual line objects. In doing so it became clear that the error reporting was supposed to be issuing line numbers, but was instead outputing the entire line that was wrong. This also fixes up that mistake. --- lib/puppet/settings/config_file.rb | 109 +++++++++++++------------ spec/unit/settings/config_file_spec.rb | 4 +- 2 files changed, 61 insertions(+), 52 deletions(-) diff --git a/lib/puppet/settings/config_file.rb b/lib/puppet/settings/config_file.rb index b3756bcf9..a2964ef09 100644 --- a/lib/puppet/settings/config_file.rb +++ b/lib/puppet/settings/config_file.rb @@ -13,29 +13,56 @@ class Puppet::Settings::ConfigFile end def self.update(config_fh, &block) - config = parse(config_fh) + config = Config.parse(config_fh) manipulator = Puppet::Settings::ConfigFile::Manipulator.new(config) yield manipulator config.write(config_fh) end - Line = Struct.new(:text) do - def write(fh) - fh.puts(text) - end - end - - SettingLine = Struct.new(:prefix, :name, :infix, :value, :suffix) do - def write(fh) - fh.write(prefix) - fh.write(name) - fh.write(infix) - fh.write(value) - fh.puts(suffix) - end - end - class Config + Line = Struct.new(:line, :text) do + def write(fh) + fh.puts(text) + end + end + + SettingLine = Struct.new(:line, :prefix, :name, :infix, :value, :suffix) do + def write(fh) + fh.write(prefix) + fh.write(name) + fh.write(infix) + fh.write(value) + fh.puts(suffix) + end + end + + SectionLine = Struct.new(:line, :prefix, :name, :suffix) do + def write(fh) + fh.write(prefix) + fh.write("[") + fh.write(name) + fh.write("]") + fh.puts(suffix) + end + end + + def self.parse(config_fh) + config = Config.new + line_number = 1 + config_fh.each_line do |line| + case line + when /^(\s*)\[(\w+)\](\s*)$/ + config << SectionLine.new(line_number, $1, $2, $3) + when /^(\s*)(\w+)(\s*=\s*)(.*?)(\s*)$/ + config << SettingLine.new(line_number, $1, $2, $3, $4, $5) + else + config << Line.new(line_number, line) + end + end + + config + end + def initialize @lines = [] end @@ -44,12 +71,8 @@ class Puppet::Settings::ConfigFile @lines << line end - def each_setting - @lines.each do |line| - if line.is_a?(SettingLine) - yield line - end - end + def each(&block) + @lines.each(&block) end def setting(name) @@ -68,20 +91,6 @@ class Puppet::Settings::ConfigFile end end - def self.parse(config_fh) - config = Config.new - config_fh.each_line do |line| - case line - when /^(\s*)(\w+)(\s*=\s*)(.*?)(\s*)$/ # settings - config << SettingLine.new($1, $2, $3, $4, $5) - else - config << Line.new(line) - end - end - - config - end - class Manipulator def initialize(config) @config = config @@ -92,38 +101,34 @@ class Puppet::Settings::ConfigFile if setting setting.value = value else - @config << SettingLine.new("", name, "=", value, "") + @config << Config::SettingLine.new("", name, "=", value, "") end end end def parse_file(file, text) result = {} - count = 0 # Default to 'main' for the section. section_name = :main result[section_name] = empty_section - text.split(/\n/).each do |line| - count += 1 + Config.parse(StringIO.new(text)).each do |line| case line - when /^\s*\[(\w+)\]\s*$/ - section_name = $1.intern - fail_when_illegal_section_name(section_name, file, line) + when Config::SectionLine + section_name = line.name.intern + fail_when_illegal_section_name(section_name, file, line.line) if result[section_name].nil? result[section_name] = empty_section end - when /^\s*#/; next # Skip comments - when /^\s*$/; next # Skip blanks - when /^\s*(\w+)\s*=\s*(.*?)\s*$/ # settings - var = $1.intern + when Config::SettingLine + var = line.name.intern # We don't want to munge modes, because they're specified in octal, so we'll # just leave them as a String, since Puppet handles that case correctly. if var == :mode - value = $2 + value = line.value else - value = @value_converter[$2] + value = @value_converter[line.value] end # Check to see if this is a file argument and it has extra options @@ -138,7 +143,9 @@ class Puppet::Settings::ConfigFile raise Puppet::Settings::ParseError.new(detail.message, file, line, detail) end else - raise Puppet::Settings::ParseError.new("Could not match line #{line}", file, line) + if line.text !~ /^\s*#|^\s*$/ + raise Puppet::Settings::ParseError.new("Could not match line #{line.text}", file, line.line) + end end end diff --git a/spec/unit/settings/config_file_spec.rb b/spec/unit/settings/config_file_spec.rb index e59f2c790..484f13680 100644 --- a/spec/unit/settings/config_file_spec.rb +++ b/spec/unit/settings/config_file_spec.rb @@ -82,7 +82,7 @@ describe Puppet::Settings::ConfigFile do it "errors when an application_defaults section is created" do expect { the_parse_of("[application_defaults]") }. to raise_error Puppet::Error, - "Illegal section 'application_defaults' in config file #{filename} at line [application_defaults]" + "Illegal section 'application_defaults' in config file #{filename} at line 1" end it "transforms values with the given function" do @@ -134,6 +134,7 @@ describe Puppet::Settings::ConfigFile do it "updates existing names and values in place" do config = a_config_file_containing(<<-CONFIG) # this is the preceeding comment + [section] name = original value # this is the trailing comment CONFIG @@ -144,6 +145,7 @@ describe Puppet::Settings::ConfigFile do expect(config.string).to eq <<-CONFIG # this is the preceeding comment + [section] name = changed value # this is the trailing comment CONFIG From 056f65b2d75405e6ea5cf5ecb305d040cd967b64 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 6 Dec 2013 11:45:55 -0800 Subject: [PATCH 168/800] (#23373) Pull apart ConfigFile and IniFile The ConfigFile was getting muddled and large. This pulls out the new file manipulation code into IniFile and has ConfigFile use that. --- lib/puppet/face/config.rb | 3 +- lib/puppet/settings/config_file.rb | 102 ++----------------------- lib/puppet/settings/ini_file.rb | 95 +++++++++++++++++++++++ spec/unit/settings/config_file_spec.rb | 59 -------------- spec/unit/settings/ini_file_spec.rb | 63 +++++++++++++++ 5 files changed, 165 insertions(+), 157 deletions(-) create mode 100644 lib/puppet/settings/ini_file.rb create mode 100644 spec/unit/settings/ini_file_spec.rb diff --git a/lib/puppet/face/config.rb b/lib/puppet/face/config.rb index 84b2e704a..3369b98b5 100644 --- a/lib/puppet/face/config.rb +++ b/lib/puppet/face/config.rb @@ -1,4 +1,5 @@ require 'puppet/face' +require 'puppet/settings/ini_file' Puppet::Face.define(:config, '0.0.1') do copyright "Puppet Labs", 2011 @@ -63,7 +64,7 @@ Puppet::Face.define(:config, '0.0.1') do file = Puppet::FileSystem::File.new(Puppet.settings.which_configuration_file) file.open(nil, 'a+') do |file| - Puppet::Settings::ConfigFile.update(file) do |config| + Puppet::Settings::IniFile.update(file) do |config| config.set(name, value) end end diff --git a/lib/puppet/settings/config_file.rb b/lib/puppet/settings/config_file.rb index a2964ef09..b7ee904d3 100644 --- a/lib/puppet/settings/config_file.rb +++ b/lib/puppet/settings/config_file.rb @@ -1,3 +1,5 @@ +require 'puppet/settings/ini_file' + ## # @api private # @@ -12,115 +14,21 @@ class Puppet::Settings::ConfigFile @value_converter = value_converter end - def self.update(config_fh, &block) - config = Config.parse(config_fh) - manipulator = Puppet::Settings::ConfigFile::Manipulator.new(config) - yield manipulator - config.write(config_fh) - end - - class Config - Line = Struct.new(:line, :text) do - def write(fh) - fh.puts(text) - end - end - - SettingLine = Struct.new(:line, :prefix, :name, :infix, :value, :suffix) do - def write(fh) - fh.write(prefix) - fh.write(name) - fh.write(infix) - fh.write(value) - fh.puts(suffix) - end - end - - SectionLine = Struct.new(:line, :prefix, :name, :suffix) do - def write(fh) - fh.write(prefix) - fh.write("[") - fh.write(name) - fh.write("]") - fh.puts(suffix) - end - end - - def self.parse(config_fh) - config = Config.new - line_number = 1 - config_fh.each_line do |line| - case line - when /^(\s*)\[(\w+)\](\s*)$/ - config << SectionLine.new(line_number, $1, $2, $3) - when /^(\s*)(\w+)(\s*=\s*)(.*?)(\s*)$/ - config << SettingLine.new(line_number, $1, $2, $3, $4, $5) - else - config << Line.new(line_number, line) - end - end - - config - end - - def initialize - @lines = [] - end - - def <<(line) - @lines << line - end - - def each(&block) - @lines.each(&block) - end - - def setting(name) - @lines.find do |line| - line.is_a?(SettingLine) && line.name == name - end - end - - def write(fh) - fh.truncate(0) - fh.rewind - @lines.each do |line| - line.write(fh) - end - fh.flush - end - end - - class Manipulator - def initialize(config) - @config = config - end - - def set(name, value) - setting = @config.setting(name) - if setting - setting.value = value - else - @config << Config::SettingLine.new("", name, "=", value, "") - end - end - end - def parse_file(file, text) result = {} # Default to 'main' for the section. section_name = :main result[section_name] = empty_section - Config.parse(StringIO.new(text)).each do |line| + Puppet::Settings::IniFile.parse(StringIO.new(text)).each do |line| case line - when Config::SectionLine + when Puppet::Settings::IniFile::SectionLine section_name = line.name.intern fail_when_illegal_section_name(section_name, file, line.line) if result[section_name].nil? result[section_name] = empty_section end - when Config::SettingLine + when Puppet::Settings::IniFile::SettingLine var = line.name.intern # We don't want to munge modes, because they're specified in octal, so we'll diff --git a/lib/puppet/settings/ini_file.rb b/lib/puppet/settings/ini_file.rb new file mode 100644 index 000000000..0ef4d0843 --- /dev/null +++ b/lib/puppet/settings/ini_file.rb @@ -0,0 +1,95 @@ +# @api private +class Puppet::Settings::IniFile + def self.update(config_fh, &block) + config = parse(config_fh) + manipulator = Manipulator.new(config) + yield manipulator + config.write(config_fh) + end + + def self.parse(config_fh) + config = new + line_number = 1 + config_fh.each_line do |line| + case line + when /^(\s*)\[(\w+)\](\s*)$/ + config << SectionLine.new(line_number, $1, $2, $3) + when /^(\s*)(\w+)(\s*=\s*)(.*?)(\s*)$/ + config << SettingLine.new(line_number, $1, $2, $3, $4, $5) + else + config << Line.new(line_number, line) + end + end + + config + end + + def initialize + @lines = [] + end + + def <<(line) + @lines << line + end + + def each(&block) + @lines.each(&block) + end + + def setting(name) + @lines.find do |line| + line.is_a?(SettingLine) && line.name == name + end + end + + def write(fh) + fh.truncate(0) + fh.rewind + @lines.each do |line| + line.write(fh) + end + fh.flush + end + + class Manipulator + def initialize(config) + @config = config + end + + def set(name, value) + setting = @config.setting(name) + if setting + setting.value = value + else + @config << SettingLine.new("", name, "=", value, "") + end + end + end + + Line = Struct.new(:line, :text) do + def write(fh) + fh.puts(text) + end + end + + SettingLine = Struct.new(:line, :prefix, :name, :infix, :value, :suffix) do + def write(fh) + fh.write(prefix) + fh.write(name) + fh.write(infix) + fh.write(value) + fh.puts(suffix) + end + end + + SectionLine = Struct.new(:line, :prefix, :name, :suffix) do + def write(fh) + fh.write(prefix) + fh.write("[") + fh.write(name) + fh.write("]") + fh.puts(suffix) + end + end + +end diff --git a/spec/unit/settings/config_file_spec.rb b/spec/unit/settings/config_file_spec.rb index 484f13680..ec9f33bbf 100644 --- a/spec/unit/settings/config_file_spec.rb +++ b/spec/unit/settings/config_file_spec.rb @@ -96,64 +96,5 @@ describe Puppet::Settings::ConfigFile do config.parse_file(filename, "mode = value").should == { :main => section_containing(:mode => "value") } end - - context "when updating" do - it "preserves the file when no changes are made" do - original_config = <<-CONFIG - # comment - [section] - name = value - CONFIG - config = a_config_file_containing(original_config) - - Puppet::Settings::ConfigFile.update(config) do; end - - expect(config.string).to eq original_config - end - - it "adds a set name and value to an empty file" do - config = a_config_file_containing("") - - Puppet::Settings::ConfigFile.update(config) do |config| - config.set("name", "value") - end - - expect(config.string).to eq "name=value\n" - end - - it "preserves comments when writing a new name and value" do - config = a_config_file_containing("# this is a comment") - - Puppet::Settings::ConfigFile.update(config) do |config| - config.set("name", "value") - end - - expect(config.string).to eq "# this is a comment\nname=value\n" - end - - it "updates existing names and values in place" do - config = a_config_file_containing(<<-CONFIG) - # this is the preceeding comment - [section] - name = original value - # this is the trailing comment - CONFIG - - Puppet::Settings::ConfigFile.update(config) do |config| - config.set("name", "changed value") - end - - expect(config.string).to eq <<-CONFIG - # this is the preceeding comment - [section] - name = changed value - # this is the trailing comment - CONFIG - end - - def a_config_file_containing(text) - StringIO.new(text) - end - end end diff --git a/spec/unit/settings/ini_file_spec.rb b/spec/unit/settings/ini_file_spec.rb new file mode 100644 index 000000000..c175ed6c7 --- /dev/null +++ b/spec/unit/settings/ini_file_spec.rb @@ -0,0 +1,63 @@ +require 'spec_helper' +require 'stringio' + +require 'puppet/settings/ini_file' + +describe Puppet::Settings::IniFile do + it "preserves the file when no changes are made" do + original_config = <<-CONFIG + # comment + [section] + name = value + CONFIG + config = a_config_file_containing(original_config) + + Puppet::Settings::IniFile.update(config) do; end + + expect(config.string).to eq original_config + end + + it "adds a set name and value to an empty file" do + config = a_config_file_containing("") + + Puppet::Settings::IniFile.update(config) do |config| + config.set("name", "value") + end + + expect(config.string).to eq "name=value\n" + end + + it "preserves comments when writing a new name and value" do + config = a_config_file_containing("# this is a comment") + + Puppet::Settings::IniFile.update(config) do |config| + config.set("name", "value") + end + + expect(config.string).to eq "# this is a comment\nname=value\n" + end + + it "updates existing names and values in place" do + config = a_config_file_containing(<<-CONFIG) + # this is the preceeding comment + [section] + name = original value + # this is the trailing comment + CONFIG + + Puppet::Settings::IniFile.update(config) do |config| + config.set("name", "changed value") + end + + expect(config.string).to eq <<-CONFIG + # this is the preceeding comment + [section] + name = changed value + # this is the trailing comment + CONFIG + end + + def a_config_file_containing(text) + StringIO.new(text) + end +end From 62cd3aa79c244022b3efe09a467de1947900166a Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Fri, 6 Dec 2013 14:23:51 -0800 Subject: [PATCH 169/800] (#23373) Fix line number tracking for settings config Ensure we incremenet line count when parsing an ini_file, and tests that we are correctly tracking line count in multi line config errors. --- lib/puppet/settings/ini_file.rb | 1 + spec/unit/settings/config_file_spec.rb | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/lib/puppet/settings/ini_file.rb b/lib/puppet/settings/ini_file.rb index 0ef4d0843..51ecaa1bc 100644 --- a/lib/puppet/settings/ini_file.rb +++ b/lib/puppet/settings/ini_file.rb @@ -19,6 +19,7 @@ class Puppet::Settings::IniFile else config << Line.new(line_number, line) end + line_number += 1 end config diff --git a/spec/unit/settings/config_file_spec.rb b/spec/unit/settings/config_file_spec.rb index ec9f33bbf..1733ced2c 100644 --- a/spec/unit/settings/config_file_spec.rb +++ b/spec/unit/settings/config_file_spec.rb @@ -57,6 +57,20 @@ describe Puppet::Settings::ConfigFile do expect { the_parse_of("unknown") }.to raise_error Puppet::Settings::ParseError, /Could not match line/ end + it "errors providing correct line number when line is not a known form" do + multi_line_config = <<-EOF +[main] +foo=bar +badline + EOF + expect { the_parse_of(multi_line_config) } + .to( + raise_error(Puppet::Settings::ParseError, /Could not match line/) do |exception| + expect(exception.line).to eq(3) + end + ) + end + it "stores file meta information in the _meta section" do the_parse_of("var = value { owner = me, group = you, mode = 0666 }").should == { :main => section_containing(:var => "value", :meta => { :var => { :owner => "me", @@ -85,6 +99,12 @@ describe Puppet::Settings::ConfigFile do "Illegal section 'application_defaults' in config file #{filename} at line 1" end + it "errors when a global_defaults section is created" do + expect { the_parse_of("[main]\n[global_defaults]") }. + to raise_error Puppet::Error, + "Illegal section 'global_defaults' in config file #{filename} at line 2" + end + it "transforms values with the given function" do config = Puppet::Settings::ConfigFile.new(Proc.new { |value| value + " changed" }) From 987b8eabce1a5fdc7dfa50a28c49f5221fc06dfc Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 7 Dec 2013 01:08:24 +0100 Subject: [PATCH 170/800] (maint) Move require pops_bridge to pops.rb The requirement of pops_bridge was made from ast.rb which caused rgen to be a hard requirement. This moves the require of pops_bridge to pops.rb. --- lib/puppet/parser/ast.rb | 1 - lib/puppet/pops.rb | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/parser/ast.rb b/lib/puppet/parser/ast.rb index 4cb1f3705..438cb49b4 100644 --- a/lib/puppet/parser/ast.rb +++ b/lib/puppet/parser/ast.rb @@ -128,4 +128,3 @@ require 'puppet/parser/ast/resourceparam' require 'puppet/parser/ast/selector' require 'puppet/parser/ast/tag' require 'puppet/parser/ast/vardef' -require 'puppet/parser/ast/pops_bridge' diff --git a/lib/puppet/pops.rb b/lib/puppet/pops.rb index 62e2cd138..80d018cec 100644 --- a/lib/puppet/pops.rb +++ b/lib/puppet/pops.rb @@ -91,5 +91,6 @@ module Puppet end end + require 'puppet/parser/ast/pops_bridge' require 'puppet/bindings' end From ef3309ec1e4e8e8e486d8494805f33b9dbdb3d0d Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 7 Dec 2013 04:22:50 +0100 Subject: [PATCH 171/800] (pup-954) Correct flaws in the Pattern Type implementation * it used AND semantics instead of OR * it did not accept a Pattern or Regexp as pattern parameter * it was not assignable by an Enum with matching strings --- lib/puppet/pops/evaluator/access_operator.rb | 9 ++-- lib/puppet/pops/model/model_label_provider.rb | 15 ++++--- lib/puppet/pops/types/type_calculator.rb | 15 +++---- lib/puppet/pops/types/type_factory.rb | 16 +++++-- .../pops/evaluator/evaluating_parser_spec.rb | 3 +- spec/unit/pops/types/type_calculator_spec.rb | 43 ++++++++++++++++--- 6 files changed, 74 insertions(+), 27 deletions(-) diff --git a/lib/puppet/pops/evaluator/access_operator.rb b/lib/puppet/pops/evaluator/access_operator.rb index aedb122c5..6ef7dbcb9 100644 --- a/lib/puppet/pops/evaluator/access_operator.rb +++ b/lib/puppet/pops/evaluator/access_operator.rb @@ -179,7 +179,7 @@ class Puppet::Pops::Evaluator::AccessOperator end keys.each_with_index do |k, i| unless allowed_classes.any? {|clazz| k.is_a?(clazz) } - bad_type_specialization_key_type(o, i, k, allowed_classes) + bad_type_specialization_key_type(o, i, k, *allowed_classes) end end end @@ -204,14 +204,17 @@ class Puppet::Pops::Evaluator::AccessOperator end def bad_type_specialization_key_type(type, key_index, actual, *expected_classes) + label_provider = Puppet::Pops::Model::ModelLabelProvider.new() + # require 'debugger'; debugger + expected = expected_classes.map {|c| label_provider.label(c) }.join(' or ') fail(Puppet::Pops::Issues::BAD_TYPE_SPECIALIZATION, @semantic.keys[key_index], { :type => type, - :message => "Cannot use #{bad_key_type_name(actual)} where #{expected_classes.join(' or ')} is expected" + :message => "Cannot use #{bad_key_type_name(actual)} where #{expected} is expected" }) end def access_PPatternType(o, scope, keys) - assert_keys(keys, o, 1, INFINITY, String, Regexp) + assert_keys(keys, o, 1, INFINITY, String, Regexp, Puppet::Pops::Types::PPatternType, Puppet::Pops::Types::PRegexpType) Puppet::Pops::Types::TypeFactory.pattern(*keys) end diff --git a/lib/puppet/pops/model/model_label_provider.rb b/lib/puppet/pops/model/model_label_provider.rb index 9744421e7..260545bc3 100644 --- a/lib/puppet/pops/model/model_label_provider.rb +++ b/lib/puppet/pops/model/model_label_provider.rb @@ -11,11 +11,7 @@ class Puppet::Pops::Model::ModelLabelProvider < Puppet::Pops::LabelProvider # If a Class is given, its name is used as label # def label o - if o.is_a?(Class) - o.name - else - @@label_visitor.visit(o) - end + @@label_visitor.visit(o) end def label_Factory o ; label(o.current) end @@ -93,4 +89,13 @@ class Puppet::Pops::Model::ModelLabelProvider < Puppet::Pops::LabelProvider "#{Puppet::Pops::Types::TypeCalculator.string(o)}-Type" end end + + def label_Class o + if o <= Puppet::Pops::Types::PAbstractType + simple_name = o.name.split('::').last + simple_name[1..-5] + "-Type" + else + o.name + end + end end diff --git a/lib/puppet/pops/types/type_calculator.rb b/lib/puppet/pops/types/type_calculator.rb index b7c714a7f..a40b3c845 100644 --- a/lib/puppet/pops/types/type_calculator.rb +++ b/lib/puppet/pops/types/type_calculator.rb @@ -328,7 +328,8 @@ class Puppet::Pops::Types::TypeCalculator if t1.is_a?(Types::PPatternType) && t2.is_a?(Types::PPatternType) t = Types::PPatternType.new() - t.patterns = t1.patterns | t2.patterns + # must make copies since patterns are contained types, not data-types + t.patterns = (t1.patterns | t2.patterns).map {|p| p.copy } return t end @@ -632,18 +633,16 @@ class Puppet::Pops::Types::TypeCalculator # @api private def assignable_PPatternType(t, t2) return true if t == t2 - return false unless t2.is_a? Types::PStringType + return false unless t2.is_a?(Types::PStringType) || t2.is_a?(Types::PEnumType) if t2.values.empty? - # Strings (unknown which ones) cannot all match a pattern, but if there is no pattern it is ok + # Strings / Enums (unknown which ones) cannot all match a pattern, but if there is no pattern it is ok # (There should really always be a pattern, but better safe than sorry). return t.patterns.empty? ? true : false end - # all strings in String type must match all patterns in Pattern type - t.patterns.all? do |p| - re = p.regexp - t2.values.all? {|v| re.match(v) } - end + # all strings in String/Enum type must match one of the patterns in Pattern type + regexps = t.patterns.map {|p| p.regexp } + t2.values.all? { |v| regexps.any? {|re| re.match(v) } } end # @api private diff --git a/lib/puppet/pops/types/type_factory.rb b/lib/puppet/pops/types/type_factory.rb index f45c7500f..b334f8a60 100644 --- a/lib/puppet/pops/types/type_factory.rb +++ b/lib/puppet/pops/types/type_factory.rb @@ -115,15 +115,25 @@ module Puppet::Pops::Types::TypeFactory re_T = Types::PRegexpType.new() re_T.pattern = re t.addPatterns(re_T) + when Regexp - re_T = Type::PRegexpType.new() + re_T = Types::PRegexpType.new() # Regep.to_s includes options user did not enter and does not escape source # to work either as a string or as a // regexp. The inspect method does a better # job, but includes the // re_T.pattern = re.inspect[1..-2] t.addPatterns(re_T) - else - raise ArgumentError, "Only String and Regexp are allowed: got '#{re.class}" + + when Types::PRegexpType + t.addPatterns(re.copy) + + when Types::PPatternType + re.patterns.each do |p| + t.addPatterns(p.copy) + end + + else + raise ArgumentError, "Only String, Regexp, Pattern-Type, and Regexp-Type are allowed: got '#{re.class}" end end t diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index 6fc772367..d202451db 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -552,6 +552,7 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do "Resource['File']" => types.resource(types.resource('File')), "File[foo]" => types.resource('file', 'foo'), "File[foo, bar]" => [types.resource('file', 'foo'), types.resource('file', 'bar')], + "Pattern[a, /b/, Pattern[c], Regexp[d]]" => types.pattern('a', 'b', 'c', 'd'), }.each do |source, result| it "should parse and evaluate the expression '#{source}' to #{result}" do parser.evaluate_string(scope, source, __FILE__).should == result @@ -589,7 +590,7 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do "Resource[a, 0]" => 'Error creating type specialization of a Resource-Type, Cannot use Fixnum where String is expected', "File[0]" => 'Error creating type specialization of a File-Type, Cannot use Fixnum where String is expected', "String[0]" => 'Error creating type specialization of a String-Type, Cannot use Fixnum where String is expected', - "Pattern[0]" => 'Error creating type specialization of a Pattern-Type, Cannot use Fixnum where String or Regexp is expected', + "Pattern[0]" => 'Error creating type specialization of a Pattern-Type, Cannot use Fixnum where String or Regexp or Pattern-Type or Regexp-Type is expected', "Regexp[0]" => 'Error creating type specialization of a Regexp-Type, Cannot use Fixnum where String or Regexp is expected', "Regexp[a,b]" => 'A Regexp-Type[] accepts 1 argument. Got 2', "true[0]" => "Operator '[]' is not applicable to a Boolean", diff --git a/spec/unit/pops/types/type_calculator_spec.rb b/spec/unit/pops/types/type_calculator_spec.rb index cf68b67ab..6906c7718 100644 --- a/spec/unit/pops/types/type_calculator_spec.rb +++ b/spec/unit/pops/types/type_calculator_spec.rb @@ -15,6 +15,10 @@ describe 'The type calculator' do Puppet::Pops::Types::TypeFactory.pattern(*patterns) end + def regexp_t(pattern) + Puppet::Pops::Types::TypeFactory.regexp(pattern) + end + def string_t(*strings) Puppet::Pops::Types::TypeFactory.string(*strings) end @@ -577,29 +581,54 @@ describe 'The type calculator' do calculator.assignable?(p_t, p_s).should == true end + it 'should accept a regexp matching a pattern' do + p_t = pattern_t(/abc/) + p_s = string_t('XabcY') + calculator.assignable?(p_t, p_s).should == true + end + + it 'should accept a pattern matching a pattern' do + p_t = pattern_t(pattern_t('abc')) + p_s = string_t('XabcY') + calculator.assignable?(p_t, p_s).should == true + end + + it 'should accept a regexp matching a pattern' do + p_t = pattern_t(regexp_t('abc')) + p_s = string_t('XabcY') + calculator.assignable?(p_t, p_s).should == true + end + it 'should accept a string matching all patterns' do p_t = pattern_t('abc', 'ab', 'c') p_s = string_t('XabcY') calculator.assignable?(p_t, p_s).should == true end - it 'should accept multiple strings if they all match all patterns' do - p_t = pattern_t('abc', 'ab', 'c') - p_s = string_t('XabcY', 'abcde') + it 'should accept multiple strings if they all match any patterns' do + p_t = pattern_t('X', 'Y', 'abc') + p_s = string_t('Xa', 'aY', 'abc') calculator.assignable?(p_t, p_s).should == true end - it 'should reject a string not matching all patterns' do - p_t = pattern_t('abc', 'ab', 'c', 'q') + it 'should reject a string not matching any patterns' do + p_t = pattern_t('abc', 'ab', 'c') p_s = string_t('XqqqY') calculator.assignable?(p_t, p_s).should == false end - it 'should reject multiple strings if not all match all patterns' do + it 'should reject multiple strings if not all match any patterns' do p_t = pattern_t('abc', 'ab', 'c', 'q') - p_s = string_t('abc', 'XqqqY') + p_s = string_t('X', 'Y', 'Z') calculator.assignable?(p_t, p_s).should == false end + + it 'should accept enum matching patterns as instanceof' do + enum = enum_t('XS', 'S', 'M', 'L' 'XL', 'XXL') + pattern = pattern_t('S', 'M', 'L') + calculator.assignable?(pattern, enum) == true + end + end it 'should recognize ruby type inheritance' do From 4ad3536fee82b63dd723d0e2c515259310e917b2 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 8 Dec 2013 22:32:51 +0100 Subject: [PATCH 172/800] (maint) Fix type calculations for Variant Type calculations for variant were wrong (and one of the test where also wrong). This corrects the logic; when combining two variants, does types that are assignable to one in the other are not included (the more general "shadows" the more specific). Also, when calculating assignability involving the Data type, a compatible variant must be considered equal; a Data type is after all just a pre-packaged variant. --- lib/puppet/pops/types/type_calculator.rb | 46 +++++++++++++++++--- lib/puppet/pops/types/types.rb | 13 ++++-- spec/unit/pops/types/type_calculator_spec.rb | 33 +++++++++++--- 3 files changed, 75 insertions(+), 17 deletions(-) diff --git a/lib/puppet/pops/types/type_calculator.rb b/lib/puppet/pops/types/type_calculator.rb index a40b3c845..1ded060c1 100644 --- a/lib/puppet/pops/types/type_calculator.rb +++ b/lib/puppet/pops/types/type_calculator.rb @@ -118,6 +118,14 @@ class Puppet::Pops::Types::TypeCalculator @literal_t = Types::PLiteralType.new() @numeric_t = Types::PNumericType.new() @t = Types::PObjectType.new() + + # Variant type compatible with Data + data_variant = Types::PVariantType.new() + data_variant.addTypes(@data_hash.copy) + data_variant.addTypes(@data_array.copy) + data_variant.addTypes(Types::PLiteralType.new) + @data_variant_t = data_variant + end # Convenience method to get a data type for comparisons @@ -127,6 +135,13 @@ class Puppet::Pops::Types::TypeCalculator @data_t end + # Convenience method to get a variant compatible with the Data type. + # @api private the returned value may not be contained in another element + # + def data_variant + @data_variant_t + end + # Answers the question 'is it possible to inject an instance of the given class' # A class is injectable if it has a special *assisted inject* class method called `inject` taking # an injector and a scope as argument, or if it has a zero args `initialize` method. @@ -602,8 +617,24 @@ class Puppet::Pops::Types::TypeCalculator # @api private def assignable_PVariantType(t, t2) - # A variant is assignable if t2 is assignable to any of its types - t.types.any? { |option_t| assignable?(option_t, t2) } + # Data is a specific variant + t2 = @data_variant_t if t2.is_a?(Types::PDataType) + if t2.is_a?(Types::PVariantType) + # A variant is assignable if all of its options are assignable to one of this type's options + return true if t == t2 + t2.types.all? do |other| + # if the other is a Variant, all if its options, but be assignable to one of this type's options + other = other.is_a?(Types::PDataType) ? @data_variant_t : other + if other.is_a?(Types::PVariantType) + assignable?(t, other) + else + t.types.any? {|option_t| assignable?(option_t, other) } + end + end + else + # A variant is assignable if t2 is assignable to any of its types + t.types.any? { |option_t| assignable?(option_t, t2) } + end end def assignable_PEnumType(t, t2) @@ -712,11 +743,12 @@ class Puppet::Pops::Types::TypeCalculator # Data is assignable by other Data and by Array[Data] and Hash[Literal, Data] # @api private def assignable_PDataType(t, t2) - t2.is_a?(Types::PDataType) || - t2.is_a?(Types::PLiteralType) || - assignable?(@data_array, t2) || - assignable?(@data_hash, t2) || - (t2.is_a?(Types::PVariantType) && !t2.types.empty? && t2.types.all? {|t| assignable?(data, t) } ) + t2.is_a?(Types::PDataType) || assignable?(@data_variant_t, t2) +# +# t2.is_a?(Types::PLiteralType) || +# assignable?(@data_array, t2) || +# assignable?(@data_hash, t2) || +# (t2.is_a?(Types::PVariantType) && !t2.types.empty? && t2.types.all? {|t| assignable?(data, t) } ) end # Assignable if t2's ruby class is same or subclass of t1's ruby class diff --git a/lib/puppet/pops/types/types.rb b/lib/puppet/pops/types/types.rb index b12f44f2e..e6bfc302a 100644 --- a/lib/puppet/pops/types/types.rb +++ b/lib/puppet/pops/types/types.rb @@ -35,7 +35,7 @@ module Puppet::Pops::Types alias eql? == def to_s - Puppet::Pops::Types::TypeCalculator.new.string(self) + Puppet::Pops::Types::TypeCalculator.string(self) end end end @@ -72,6 +72,12 @@ module Puppet::Pops::Types # # @api public class PDataType < PObjectType + module ClassModule + def ==(o) + self.class == o.class || + o.class == PVariantType && o == Puppet::Pops::Types::TypeCalculator.instance.data_variant() + end + end end # A flexible type describing an any? of other types @@ -86,14 +92,15 @@ module Puppet::Pops::Types end def ==(o) - self.class == o.class && Set.new(types) == Set.new(o.types) + (self.class == o.class && Set.new(types) == Set.new(o.types)) || + (o.class == PDataType && self == Puppet::Pops::Types::TypeCalculator.instance.data_variant()) end end end # Type that is PDataType compatible, but is not a PCollectionType. # @api public - class PLiteralType < PDataType + class PLiteralType < PObjectType end # A string type describing the set of strings having one of the given values diff --git a/spec/unit/pops/types/type_calculator_spec.rb b/spec/unit/pops/types/type_calculator_spec.rb index 6906c7718..52a3e5877 100644 --- a/spec/unit/pops/types/type_calculator_spec.rb +++ b/spec/unit/pops/types/type_calculator_spec.rb @@ -111,7 +111,15 @@ describe 'The type calculator' do end def data_compatible_types - literal_types + [Puppet::Pops::Types::PHashType, Puppet::Pops::Types::PArrayType, Puppet::Pops::Types::PDataType] + result = literal_types + result << Puppet::Pops::Types::PDataType + result << array_t(types::PDataType.new) + result << types::TypeFactory.hash_of_data + result + end + + def type_from_class(c) + c.is_a?(Class) ? c.new : c end end @@ -361,7 +369,18 @@ describe 'The type calculator' do common_t.should == enum_t('a', 'b', 'c', 'x', 'y', 'z') end - it 'computed variant commonality to type union' do + it 'computed variant commonality to type union where added types are not assignable' do + a_t1 = integer_t() + a_t2 = enum_t('b') + v_a = variant_t(a_t1, a_t2) + b_t1 = enum_t('a') + v_b = variant_t(b_t1) + common_t = calculator.common_type(v_a, v_b) + common_t.class.should == Puppet::Pops::Types::PVariantType + Set.new(common_t.types).should == Set.new([a_t1, a_t2, b_t1]) + end + + it 'computed variant commonality to type union where added types are assignable' do a_t1 = integer_t() a_t2 = string_t() v_a = variant_t(a_t1, a_t2) @@ -369,7 +388,7 @@ describe 'The type calculator' do v_b = variant_t(b_t1) common_t = calculator.common_type(v_a, v_b) common_t.class.should == Puppet::Pops::Types::PVariantType - Set.new(common_t.types).should == Set.new([a_t1, a_t2, b_t1]) + Set.new(common_t.types).should == Set.new([a_t1, a_t2]) end end @@ -392,24 +411,24 @@ describe 'The type calculator' do context "for Data, such that" do it 'all literals + array and hash are assignable to Data' do t = Puppet::Pops::Types::PDataType.new() - data_compatible_types.each { |t2| t2.new.should be_assignable_to(t) } + data_compatible_types.each { |t2| type_from_class(t2).should be_assignable_to(t) } end it 'a Variant of literal, hash, or array is assignable to Data' do t = Puppet::Pops::Types::PDataType.new() - data_compatible_types.each { |t2| variant_t(t2.new).should be_assignable_to(t) } + data_compatible_types.each { |t2| variant_t(type_from_class(t2)).should be_assignable_to(t) } end it 'Data is not assignable to any of its subtypes' do t = Puppet::Pops::Types::PDataType.new() types_to_test = data_compatible_types- [Puppet::Pops::Types::PDataType] - types_to_test.each {|t2| t.should_not be_assignable_to(t2.new) } + types_to_test.each {|t2| t.should_not be_assignable_to(type_from_class(t2)) } end it 'Data is not assignable to a Variant of Data subtype' do t = Puppet::Pops::Types::PDataType.new() types_to_test = data_compatible_types- [Puppet::Pops::Types::PDataType] - types_to_test.each { |t2| t.should_not be_assignable_to(variant_t(t2.new)) } + types_to_test.each { |t2| t.should_not be_assignable_to(variant_t(type_from_class(t2))) } end it 'Data is not assignable to any disjunct type' do From 7cc05f543971852f5a3b47c691261a06a8f49f72 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Fri, 6 Dec 2013 15:27:43 -0800 Subject: [PATCH 173/800] (#23373) Always open config so we are reading from the beginning Not all operating systems start reading from the same position with 'a+' when opening the file. This ensures that we create the file with default permissions, and begin reading from the first line. We also make some acceptance changes for testing puppet config set, and remove an acceptance test which was of dubious value. --- .../allow_symlinks_as_config_directories.rb | 9 +-- ...apply_file_metadata_specified_in_config.rb | 2 +- .../key_compare_puppet_conf_configprint.rb | 65 ------------------- lib/puppet/face/config.rb | 3 +- lib/puppet/settings/config_file.rb | 8 ++- lib/puppet/settings/ini_file.rb | 14 ++-- spec/unit/settings/config_file_spec.rb | 3 +- spec/unit/settings/ini_file_spec.rb | 24 +++---- 8 files changed, 30 insertions(+), 98 deletions(-) delete mode 100644 acceptance/tests/key_compare_puppet_conf_configprint.rb diff --git a/acceptance/tests/allow_symlinks_as_config_directories.rb b/acceptance/tests/allow_symlinks_as_config_directories.rb index c8c8bbbf3..2d45d4fef 100644 --- a/acceptance/tests/allow_symlinks_as_config_directories.rb +++ b/acceptance/tests/allow_symlinks_as_config_directories.rb @@ -11,16 +11,13 @@ agents.each do |agent| on agent, "mkdir #{confdir}" on agent, "ln -s #{confdir} #{conflink}" - create_remote_file agent, "#{confdir}/puppet.conf", < manifest do - assert_match("My certname is awesome_certname", stdout) + assert_match(/My certname is awesome_certname[^\w]/, stdout) end step "Check that the symlink and confdir are unchanged" diff --git a/acceptance/tests/config/apply_file_metadata_specified_in_config.rb b/acceptance/tests/config/apply_file_metadata_specified_in_config.rb index a974e2349..932ec74f3 100644 --- a/acceptance/tests/config/apply_file_metadata_specified_in_config.rb +++ b/acceptance/tests/config/apply_file_metadata_specified_in_config.rb @@ -16,7 +16,7 @@ agents.each do |agent| } SITE - on(agent, puppet('config', 'set', 'logdir', "'#{logdir} { owner = root, group = root, mode = 0700 }'", '--configdir', get_test_file_path(agent, ''))) + on(agent, puppet('config', 'set', 'logdir', "'#{logdir} { owner = root, group = root, mode = 0700 }'", '--confdir', get_test_file_path(agent, ''))) on(agent, puppet('apply', get_test_file_path(agent, 'site.pp'), '--confdir', get_test_file_path(agent, ''))) diff --git a/acceptance/tests/key_compare_puppet_conf_configprint.rb b/acceptance/tests/key_compare_puppet_conf_configprint.rb deleted file mode 100644 index 38b639463..000000000 --- a/acceptance/tests/key_compare_puppet_conf_configprint.rb +++ /dev/null @@ -1,65 +0,0 @@ -# Check for the existance of keys found in puppet.conf in -# --configprint all output -# -# Checking against key=>val pairs will cause erroneous errors: -# -# classfile -# Puppet.conf --configprint -# $vardir/classes.txt /var/opt/lib/pe-puppet/classes.txt - -test_name "Validate keys found in puppet.conf vs.--configprint all" - -puppet_conf_h = Hash.new -config_print_h = Hash.new - -# Run tests against Master first -step "Master: get puppet.conf file contents" -on master, "cat #{master['puppetpath']}/puppet.conf | tr -d \" \"" do - stdout.split("\n").select{ |v| v =~ /=/ }.each do |line| - k,v = line.split("=") - puppet_conf_h[k]=v - end -end - -step "Master: get --configprint all output" -on master, puppet_master("--configprint all | tr -d \" \"") do - stdout.split("\n").select{ |v| v =~ /=/ }.each do |line| - k,v = line.split("=") - config_print_h[k]=v - end -end - -step "Master: compare puppet.conf to --configprint output" -puppet_conf_h.each do |k,v| - puts "#{k}: #{puppet_conf_h[k]} #{config_print_h[k]}" - fail_test "puppet.conf contains a key not found in configprint" unless config_print_h.include?(k) - # fail_test "puppet.conf: #{puppet_conf_h[k]} differs from --configprintall: #{config_print_h[k]}" if ( puppet_conf_h[k] != config_print_h[k] ) -end - -# Run test on Agents -agents.each { |agent| - puppet_conf_h.clear - config_print_h.clear - step "Agent #{agent}: get puppet.conf file contents" - on agent, "cat #{master['puppetpath']}/puppet.conf | tr -d \" \"" do - stdout.split("\n").select{ |v| v =~ /=/ }.each do |line| - k,v = line.split("=") - puppet_conf_h[k]=v - end - end - - step "Agent #{agent}: get --configprint all output" - on agent, puppet_agent("--configprint all | tr -d \" \"") do - stdout.split("\n").select{ |v| v =~ /=/ }.each do |line| - k,v = line.split("=") - config_print_h[k]=v - end - end - - step "Agent #{agent}: compare puppet.conf to --configprint output" - puppet_conf_h.each do |k,v| - puts "#{k}: #{puppet_conf_h[k]} #{config_print_h[k]}" - fail_test "puppet.conf contains a key not found in configprint" unless config_print_h.include?(k) - # fail_test "puppet.conf: #{puppet_conf_h[k]} differs from --configprintall: #{config_print_h[k]}" if ( puppet_conf_h[k] != config_print_h[k] ) - end -} diff --git a/lib/puppet/face/config.rb b/lib/puppet/face/config.rb index 3369b98b5..3100426af 100644 --- a/lib/puppet/face/config.rb +++ b/lib/puppet/face/config.rb @@ -63,7 +63,8 @@ Puppet::Face.define(:config, '0.0.1') do name, value = args file = Puppet::FileSystem::File.new(Puppet.settings.which_configuration_file) - file.open(nil, 'a+') do |file| + file.touch + file.open(nil, 'r+') do |file| Puppet::Settings::IniFile.update(file) do |config| config.set(name, value) end diff --git a/lib/puppet/settings/config_file.rb b/lib/puppet/settings/config_file.rb index b7ee904d3..77db99977 100644 --- a/lib/puppet/settings/config_file.rb +++ b/lib/puppet/settings/config_file.rb @@ -20,11 +20,12 @@ class Puppet::Settings::ConfigFile # Default to 'main' for the section. section_name = :main result[section_name] = empty_section + line_number = 1 Puppet::Settings::IniFile.parse(StringIO.new(text)).each do |line| case line when Puppet::Settings::IniFile::SectionLine section_name = line.name.intern - fail_when_illegal_section_name(section_name, file, line.line) + fail_when_illegal_section_name(section_name, file, line_number) if result[section_name].nil? result[section_name] = empty_section end @@ -48,13 +49,14 @@ class Puppet::Settings::ConfigFile end result[section_name][var] = value rescue Puppet::Error => detail - raise Puppet::Settings::ParseError.new(detail.message, file, line, detail) + raise Puppet::Settings::ParseError.new(detail.message, file, line_number, detail) end else if line.text !~ /^\s*#|^\s*$/ - raise Puppet::Settings::ParseError.new("Could not match line #{line.text}", file, line.line) + raise Puppet::Settings::ParseError.new("Could not match line #{line.text}", file, line_number) end end + line_number += 1 end result diff --git a/lib/puppet/settings/ini_file.rb b/lib/puppet/settings/ini_file.rb index 51ecaa1bc..5e1aebf29 100644 --- a/lib/puppet/settings/ini_file.rb +++ b/lib/puppet/settings/ini_file.rb @@ -9,17 +9,15 @@ class Puppet::Settings::IniFile def self.parse(config_fh) config = new - line_number = 1 config_fh.each_line do |line| case line when /^(\s*)\[(\w+)\](\s*)$/ - config << SectionLine.new(line_number, $1, $2, $3) + config << SectionLine.new($1, $2, $3) when /^(\s*)(\w+)(\s*=\s*)(.*?)(\s*)$/ - config << SettingLine.new(line_number, $1, $2, $3, $4, $5) + config << SettingLine.new($1, $2, $3, $4, $5) else - config << Line.new(line_number, line) + config << Line.new(line) end - line_number += 1 end config @@ -67,13 +65,13 @@ class Puppet::Settings::IniFile end end - Line = Struct.new(:line, :text) do + Line = Struct.new(:text) do def write(fh) fh.puts(text) end end - SettingLine = Struct.new(:line, :prefix, :name, :infix, :value, :suffix) do + SettingLine = Struct.new(:prefix, :name, :infix, :value, :suffix) do def write(fh) fh.write(prefix) fh.write(name) @@ -83,7 +81,7 @@ class Puppet::Settings::IniFile end end - SectionLine = Struct.new(:line, :prefix, :name, :suffix) do + SectionLine = Struct.new(:prefix, :name, :suffix) do def write(fh) fh.write(prefix) fh.write("[") diff --git a/spec/unit/settings/config_file_spec.rb b/spec/unit/settings/config_file_spec.rb index 1733ced2c..2883e38e5 100644 --- a/spec/unit/settings/config_file_spec.rb +++ b/spec/unit/settings/config_file_spec.rb @@ -63,8 +63,7 @@ describe Puppet::Settings::ConfigFile do foo=bar badline EOF - expect { the_parse_of(multi_line_config) } - .to( + expect { the_parse_of(multi_line_config) }.to( raise_error(Puppet::Settings::ParseError, /Could not match line/) do |exception| expect(exception.line).to eq(3) end diff --git a/spec/unit/settings/ini_file_spec.rb b/spec/unit/settings/ini_file_spec.rb index c175ed6c7..d11e010fa 100644 --- a/spec/unit/settings/ini_file_spec.rb +++ b/spec/unit/settings/ini_file_spec.rb @@ -10,46 +10,46 @@ describe Puppet::Settings::IniFile do [section] name = value CONFIG - config = a_config_file_containing(original_config) + config_fh = a_config_file_containing(original_config) - Puppet::Settings::IniFile.update(config) do; end + Puppet::Settings::IniFile.update(config_fh) do; end - expect(config.string).to eq original_config + expect(config_fh.string).to eq original_config end it "adds a set name and value to an empty file" do - config = a_config_file_containing("") + config_fh = a_config_file_containing("") - Puppet::Settings::IniFile.update(config) do |config| + Puppet::Settings::IniFile.update(config_fh) do |config| config.set("name", "value") end - expect(config.string).to eq "name=value\n" + expect(config_fh.string).to eq "name=value\n" end it "preserves comments when writing a new name and value" do - config = a_config_file_containing("# this is a comment") + config_fh = a_config_file_containing("# this is a comment") - Puppet::Settings::IniFile.update(config) do |config| + Puppet::Settings::IniFile.update(config_fh) do |config| config.set("name", "value") end - expect(config.string).to eq "# this is a comment\nname=value\n" + expect(config_fh.string).to eq "# this is a comment\nname=value\n" end it "updates existing names and values in place" do - config = a_config_file_containing(<<-CONFIG) + config_fh = a_config_file_containing(<<-CONFIG) # this is the preceeding comment [section] name = original value # this is the trailing comment CONFIG - Puppet::Settings::IniFile.update(config) do |config| + Puppet::Settings::IniFile.update(config_fh) do |config| config.set("name", "changed value") end - expect(config.string).to eq <<-CONFIG + expect(config_fh.string).to eq <<-CONFIG # this is the preceeding comment [section] name = changed value From f5d08807823779f1d4b7c44a295cf97a2a1b2604 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 10 Dec 2013 00:26:14 +0100 Subject: [PATCH 174/800] (pup-954) Fix issues with typesystem's handling of inference. This adds additional inference methods: * infer_generic - a non instance specific result * infer_set - a non reducing/combining inference * generalize! - changes instance specific to generic This makes it possible to evaluate expressions like: [1,a] =~ Array[Variant[Integer, String]] This failed previously as the result was that Array[Literal] is not assignable to either of the two more specific types. --- .../pops/binder/hiera2/bindings_provider.rb | 2 +- lib/puppet/pops/types/type_calculator.rb | 175 +++++++++++++++++- lib/puppet/pops/types/type_factory.rb | 2 +- lib/puppet/pops/types/types.rb | 4 +- spec/unit/pops/types/type_calculator_spec.rb | 39 ++++ 5 files changed, 210 insertions(+), 12 deletions(-) diff --git a/lib/puppet/pops/binder/hiera2/bindings_provider.rb b/lib/puppet/pops/binder/hiera2/bindings_provider.rb index 06285bb1c..be39f3ce8 100644 --- a/lib/puppet/pops/binder/hiera2/bindings_provider.rb +++ b/lib/puppet/pops/binder/hiera2/bindings_provider.rb @@ -61,7 +61,7 @@ module Puppet::Pops::Binder::Hiera2 expr = build_expr(value, hiera_data_file_path) if is_constant?(expr) # The value is constant so toss the expression - b.type(@type_calculator.infer(value)).to(value) + b.type(@type_calculator.infer_generic(value)).to(value) else # Use an evaluating producer for the binding b.to(expr) diff --git a/lib/puppet/pops/types/type_calculator.rb b/lib/puppet/pops/types/type_calculator.rb index 1ded060c1..11b3ef6c9 100644 --- a/lib/puppet/pops/types/type_calculator.rb +++ b/lib/puppet/pops/types/type_calculator.rb @@ -10,6 +10,21 @@ # Inference # --------- # The `infer(o)` method infers a Puppet type for literal Ruby objects, and for Arrays and Hashes. +# The inference result is instance specific for single typed collections +# and allows answering questions about its embedded type. It does not however preserve multiple types in +# a collection, and can thus not answer questions like `[1,a].infer() =~ Array[Integer, String]` since the inference +# computes the common type Literal when combining Integer and String. +# +# The `infer_generic(o)` method infers a generic Puppet type for literal Ruby object, Arrays and Hashes. +# This inference result does not contain instance specific information; e.g. Array[Integer] where the integer +# range is the generic default. Just `infer` it also combines types into a common type. +# +# The `infer_set(o)` method works like `infer` but preserves all type information. It does not do any +# reduction into common types or ranges. This method of inference is best suited for answering questions +# about an object being an instance of a type. It correctly answers: `[1,a].infer_set() =~ Array[Integer, String]` +# +# The `generalize!(t)` method modifies an instance specific inference result to a generic. The method mutates +# the given argument. Basically, this removes string instances from String, and range from Integer and Float. # # Assignability # ------------- @@ -35,6 +50,9 @@ # instance, such as if it may be nil, be empty, contain a certain count etc. Or put differently, the puppet types are not # singletons. # +# All types support `copy` which should be used when assigning a type where it is unknown if it is bound or not +# to a parent type. A check can be made with `t.eContainer().nil?` +# # Equality and Hash # ----------------- # Type instances are equal in terms of Ruby eql? and `==` if they describe the same type, but they are not `equal?` if they are not @@ -64,6 +82,14 @@ # @see Puppet::Pops::Types::TypeParser TypeParser how to construct a type instance from a String # @see Puppet::Pops::Types Types for details about the type model # +# Using the Type Calculator +# ----- +# The type calculator can be directly used via its class methods. If doing time critical work and doing many +# calls to the type calculator, it is more performant to create an instance and invoke the corresponding +# instance methods. Note that inference is an expensive operation, rather than infering the same thing +# several times, it is in general better to infer once and then copy the result if mutation to a more generic form is +# required. +# # @api public # class Puppet::Pops::Types::TypeCalculator @@ -71,27 +97,43 @@ class Puppet::Pops::Types::TypeCalculator Types = Puppet::Pops::Types TheInfinity = 1.0 / 0.0 # because the Infinity symbol is not defined + # @api public def self.assignable?(t1, t2) - instance.assignable?(t1,t2) + singleton.assignable?(t1,t2) end + # @api public def self.string(t) - instance.string(t) + singleton.string(t) end + # @api public def self.infer(o) - instance.infer(o) + singleton.infer(o) end + # @api public + def self.generalize!(o) + singleton.generalize!(o) + end + + # @api public + def self.infer_set(o) + singleton.infer_set(o) + end + + # @api public def self.debug_string(t) - instance.debug_string(t) + singleton.debug_string(t) end + # @api public def self.enumerable(t) - instance.enumerable(t) + singleton.enumerable(t) end - def self.instance() + # @api private + def self.singleton() @tc_instance ||= new end @@ -100,10 +142,13 @@ class Puppet::Pops::Types::TypeCalculator def initialize @@assignable_visitor ||= Puppet::Pops::Visitor.new(nil,"assignable",1,1) @@infer_visitor ||= Puppet::Pops::Visitor.new(nil,"infer",0,0) + @@infer_set_visitor ||= Puppet::Pops::Visitor.new(nil,"infer_set",0,0) + @@instance_of_visitor ||= Puppet::Pops::Visitor.new(nil,"instance_of",1,1) @@string_visitor ||= Puppet::Pops::Visitor.new(nil,"string",0,0) @@inspect_visitor ||= Puppet::Pops::Visitor.new(nil,"debug_string",0,0) @@enumerable_visitor ||= Puppet::Pops::Visitor.new(nil,"enumerable",0,0) @@extract_visitor ||= Puppet::Pops::Visitor.new(nil,"extract",0,0) + @@generalize_visitor ||= Puppet::Pops::Visitor.new(nil,"generalize",0,0) da = Types::PArrayType.new() da.element_type = Types::PDataType.new() @@ -142,6 +187,10 @@ class Puppet::Pops::Types::TypeCalculator @data_variant_t end + def self.data_variant + singleton.data_variant + end + # Answers the question 'is it possible to inject an instance of the given class' # A class is injectable if it has a special *assisted inject* class method called `inject` taking # an injector and a scope as argument, or if it has a zero args `initialize` method. @@ -231,18 +280,88 @@ class Puppet::Pops::Types::TypeCalculator type end - # Answers 'what is the Puppet Type of o' + # Generalizes value specific types. The given type is mutated and returned. + # @api public + def generalize!(o) + @@generalize_visitor.visit_this_0(self, o) + o.eAllContents.each { |x| @@generalize_visitor.visit_this_0(self, x) } + o + end + + def generalize_Object(o) + # do nothing, there is nothing to change for most types + end + + def generalize_PStringType(o) + o.values = [] + [] + end + + def generalize_PFloatTye(o) + o.to = nil + o.from = nil + end + + def generalize_PIntegerType(o) + o.to = nil + o.from = nil + end + + # Answers 'what is the single common Puppet Type describing o', or if o is an Array or Hash, what is the + # single common type of the elements (or keys and elements for a Hash). # @api public # def infer(o) @@infer_visitor.visit_this_0(self, o) end + def infer_generic(o) + result = generalize!(infer(o)) + result + end + + # Answers 'what is the set of Puppet Types of o' + # @api public + # + def infer_set(o) + @@infer_set_visitor.visit_this_0(self, o) + end + + def instance_of(t, o) + return true if o.nil? + @@instance_of_visitor.visit_this_1(self, t, o) + end + + def instance_of_Object(t, o) + assignable?(t, infer(o)) + end + + def instance_of_PArrayType(t, o) + return false unless o.is_a?(Array) + o.all? {|element| instance_of(t.element_type, element) } + end + + def instance_of_PHashType(t, o) + return false unless o.is_a?(Hash) + key_t = t.key_type + element_t = t.element_type + o.keys.all? {|key| instance_of(key_t, key) } && o.values.all? {|value| instance_of(element_t, value) } + end + + def instance_of_PDataType(t, o) + instance_of(@data_variant_t, o) + end + + def instance_of_PVariableType(t, o) + # calculate types without reducing, Array and Hash will have Variant type as parameters + assignable?(t, infer_set(o)) + end + # Answers 'is o an instance of type t' # @api public # def instance?(t, o) - assignable?(t, infer(o)) + instance_of(t,o) end # Answers if t is a puppet type @@ -568,6 +687,46 @@ class Puppet::Pops::Types::TypeCalculator type end + # Common case for everything that intrinsically only has a single type + def infer_set_Object(o) + infer(o) + end + + def infer_set_Array(o) + type = Types::PArrayType.new() + type.element_type = if o.empty? + Types::PNilType.new() + else + t = Types::PVariantType.new() + t.types = o.map() {|x| infer_set(x) } + t.types.size == 1 ? t.types[0] : t + end + type + end + + def infer_set_Hash(o) + type = Types::PHashType.new() + if o.empty? + ktype = Types::PNilType.new() + etype = Types::PNilType.new() + else + ktype = Types::PVariantType.new() + ktype.types = o.keys.map() {|k| infer_set(k) } + etype = Types::PVariantType.new() + etype.types = o.values.map() {|e| infer_set(e) } + end + type.key_type = unwrap_single_variant(ktype) + type.element_type = unwrap_single_variant(vtype) + type + end + + def unwrap_single_variant(possible_variant) + if possible_variant.is_a?(Types::PVariantType) && possible_variant.types.size == 1 + possible_variant.types[0] + else + possible_variant + end + end # False in general type calculator # @api private def assignable_Object(t, t2) diff --git a/lib/puppet/pops/types/type_factory.rb b/lib/puppet/pops/types/type_factory.rb index b334f8a60..44811d476 100644 --- a/lib/puppet/pops/types/type_factory.rb +++ b/lib/puppet/pops/types/type_factory.rb @@ -245,7 +245,7 @@ module Puppet::Pops::Types::TypeFactory type.ruby_class = o type else - @type_calculator.infer(o) + @type_calculator.infer_generic(o) end end diff --git a/lib/puppet/pops/types/types.rb b/lib/puppet/pops/types/types.rb index e6bfc302a..999c4fd21 100644 --- a/lib/puppet/pops/types/types.rb +++ b/lib/puppet/pops/types/types.rb @@ -75,7 +75,7 @@ module Puppet::Pops::Types module ClassModule def ==(o) self.class == o.class || - o.class == PVariantType && o == Puppet::Pops::Types::TypeCalculator.instance.data_variant() + o.class == PVariantType && o == Puppet::Pops::Types::TypeCalculator.data_variant() end end end @@ -93,7 +93,7 @@ module Puppet::Pops::Types def ==(o) (self.class == o.class && Set.new(types) == Set.new(o.types)) || - (o.class == PDataType && self == Puppet::Pops::Types::TypeCalculator.instance.data_variant()) + (o.class == PDataType && self == Puppet::Pops::Types::TypeCalculator.data_variant()) end end end diff --git a/spec/unit/pops/types/type_calculator_spec.rb b/spec/unit/pops/types/type_calculator_spec.rb index 52a3e5877..293c46263 100644 --- a/spec/unit/pops/types/type_calculator_spec.rb +++ b/spec/unit/pops/types/type_calculator_spec.rb @@ -1035,6 +1035,45 @@ describe 'The type calculator' do end end + context "when dealing with different types of inference" do + it "an instance specific inference is produced by infer" do + calculator.infer(['a','b']).element_type.values.should == ['a', 'b'] + end + + it "a generic inference is produced using infer_generic" do + calculator.infer_generic(['a','b']).element_type.values.should == [] + end + + it "a generic result is created by generalize! given an instance specific result for an Array" do + generic = calculator.infer(['a','b']) + generic.element_type.values.should == ['a', 'b'] + calculator.generalize!(generic) + generic.element_type.values.should == [] + end + + it "a generic result is created by generalize! given an instance specific result for a Hash" do + generic = calculator.infer({'a' =>1,'b' => 2}) + generic.key_type.values.should == ['a', 'b'] + generic.element_type.from.should == 1 + generic.element_type.to.should == 2 + calculator.generalize!(generic) + generic.key_type.values.should == [] + generic.element_type.from.should == nil + generic.element_type.to.should == nil + end + + it "does not reduce by combining types when using infer_set" do + element_type = calculator.infer(['a','b',1,2]).element_type + element_type.class.should == Puppet::Pops::Types::PLiteralType + element_type = calculator.infer_set(['a','b',1,2]).element_type + element_type.class.should == Puppet::Pops::Types::PVariantType + element_type.types[0].class.should == Puppet::Pops::Types::PStringType + element_type.types[1].class.should == Puppet::Pops::Types::PStringType + element_type.types[2].class.should == Puppet::Pops::Types::PIntegerType + element_type.types[3].class.should == Puppet::Pops::Types::PIntegerType + end + end + matcher :be_assignable_to do |type| calc = Puppet::Pops::Types::TypeCalculator.new From 11fd446af1085cf81ae76e69f5eb114287dd8775 Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Mon, 2 Dec 2013 14:12:19 -0800 Subject: [PATCH 175/800] (#23141) Fix remount on bsd if options are specified The initial fix for 23141 didn't account for a remount when options were specified. This caused integration tests to fail because a) the integration tests assumed mounted is always false when the mountcmd is called (not true for a remount), and b) the integration tests weren't updated to consider the 'update' option. This patch updates the remount method to append ',update' if there are already options, and updates the integration tests accordingly. --- lib/puppet/provider/mount.rb | 7 ++++++- spec/integration/provider/mount_spec.rb | 17 +++++++++++++++-- spec/unit/provider/mount_spec.rb | 1 + 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/lib/puppet/provider/mount.rb b/lib/puppet/provider/mount.rb index 0bad58f30..f8d24becc 100644 --- a/lib/puppet/provider/mount.rb +++ b/lib/puppet/provider/mount.rb @@ -28,7 +28,12 @@ module Puppet::Provider::Mount if resource[:remounts] == :true mountcmd "-o", "remount", resource[:name] elsif ["FreeBSD", "Darwin", "DragonFly", "OpenBSD"].include?(Facter.value(:operatingsystem)) - mountcmd "-o", "update", resource[:name] + if self.options && !self.options.empty? + options = self.options + ",update" + else + options = "update" + end + mountcmd "-o", options, resource[:name] else unmount mount diff --git a/spec/integration/provider/mount_spec.rb b/spec/integration/provider/mount_spec.rb index 310b58d1f..8e8581153 100755 --- a/spec/integration/provider/mount_spec.rb +++ b/spec/integration/provider/mount_spec.rb @@ -35,9 +35,22 @@ describe "mount provider (integration)", :unless => Puppet.features.microsoft_wi else command.length.should == 4 command[1].should == '-o' + + # update is a special option, used on bsd's + # strip it out and track as a local bool here + update = false + tmp_options = command[2].split(",") + + if tmp_options.include?("update") + update = true + tmp_options.delete("update") + end + @current_options = tmp_options.join(",") + + if !update + @mounted.should == false # verify that we don't try to call "mount" redundantly + end command[3].should == '/Volumes/foo_disk' - @mounted.should == false # verify that we don't try to call "mount" redundantly - @current_options = command[2] @current_device = check_fstab(true) @mounted = true '' diff --git a/spec/unit/provider/mount_spec.rb b/spec/unit/provider/mount_spec.rb index 27d576023..8b18f7224 100755 --- a/spec/unit/provider/mount_spec.rb +++ b/spec/unit/provider/mount_spec.rb @@ -81,6 +81,7 @@ describe Puppet::Provider::Mount do it "should unmount and mount if the resource does not specify it supports remounting" do @mounter.stubs(:info) + @mounter.stubs(:options) @resource.stubs(:[]).with(:remounts).returns(false) Facter.expects(:value).with(:operatingsystem).returns 'Darwin' @mounter.expects(:mountcmd).with("-o", "update", @name) From a04d4282fd350d6dcf9a05ed168f866229dca777 Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Mon, 9 Dec 2013 21:20:23 -0800 Subject: [PATCH 176/800] (maint) Remove obsolete line of code --- lib/puppet/provider/mount/parsed.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/puppet/provider/mount/parsed.rb b/lib/puppet/provider/mount/parsed.rb index d935d7d13..dd765db8c 100644 --- a/lib/puppet/provider/mount/parsed.rb +++ b/lib/puppet/provider/mount/parsed.rb @@ -23,7 +23,6 @@ Puppet::Type.type(:mount).provide( @fields = [:device, :blockdevice, :name, :fstype, :pass, :atboot, :options] else @fields = [:device, :name, :fstype, :options, :dump, :pass] - @fielddefaults = [ nil ] * 4 + [ "0", "2" ] end text_line :comment, :match => /^\s*#/ From 5e7ad8b23b3afa23b2bb63ab6e7f72d8a87f2ea2 Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Mon, 9 Dec 2013 21:47:53 -0800 Subject: [PATCH 177/800] (#23141) Split spec test into two for the two non-remounts scenarios There are now two sets of platforms that don't support "remounts" by default: those that do a 'mount -o update' and those that do a 'umount' followed by a 'mount'. This patch splits the spec test into two accordingly. --- spec/unit/provider/mount_spec.rb | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/spec/unit/provider/mount_spec.rb b/spec/unit/provider/mount_spec.rb index 8b18f7224..7c3714478 100755 --- a/spec/unit/provider/mount_spec.rb +++ b/spec/unit/provider/mount_spec.rb @@ -79,12 +79,22 @@ describe Puppet::Provider::Mount do @mounter.remount end + it "should mount with '-o update' on OpenBSD" do + @mounter.stubs(:info) + @mounter.stubs(:options) + @resource.stubs(:[]).with(:remounts).returns(false) + Facter.expects(:value).with(:operatingsystem).returns 'OpenBSD' + @mounter.expects(:mountcmd).with("-o", "update", @name) + @mounter.remount + end + it "should unmount and mount if the resource does not specify it supports remounting" do @mounter.stubs(:info) @mounter.stubs(:options) @resource.stubs(:[]).with(:remounts).returns(false) - Facter.expects(:value).with(:operatingsystem).returns 'Darwin' - @mounter.expects(:mountcmd).with("-o", "update", @name) + Facter.expects(:value).with(:operatingsystem).returns 'AIX' + @mounter.expects(:mount) + @mounter.expects(:unmount) @mounter.remount end From cbe63d3c2f88e4c02a958a6b15dc76d145be466f Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Mon, 9 Dec 2013 21:59:07 -0800 Subject: [PATCH 178/800] (#23141) Remove Darwin from the list of OSs with the new 'update' support --- lib/puppet/provider/mount.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/provider/mount.rb b/lib/puppet/provider/mount.rb index f8d24becc..0f3bc1023 100644 --- a/lib/puppet/provider/mount.rb +++ b/lib/puppet/provider/mount.rb @@ -27,7 +27,7 @@ module Puppet::Provider::Mount info "Remounting" if resource[:remounts] == :true mountcmd "-o", "remount", resource[:name] - elsif ["FreeBSD", "Darwin", "DragonFly", "OpenBSD"].include?(Facter.value(:operatingsystem)) + elsif ["FreeBSD", "DragonFly", "OpenBSD"].include?(Facter.value(:operatingsystem)) if self.options && !self.options.empty? options = self.options + ",update" else From 9a9bf1bcef1d4df7656e3e0dd722505328eddb02 Mon Sep 17 00:00:00 2001 From: Nick Fagerlund Date: Tue, 10 Dec 2013 14:14:20 -0800 Subject: [PATCH 179/800] Maint: Be more clear that puppet queue is deprecated - Remove link to old wiki docs - Link to PuppetDB docs - Say "deprecated" again --- lib/puppet/application/queue.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/puppet/application/queue.rb b/lib/puppet/application/queue.rb index e3820e621..0f6e95f7b 100644 --- a/lib/puppet/application/queue.rb +++ b/lib/puppet/application/queue.rb @@ -42,13 +42,13 @@ class Puppet::Application::Queue < Puppet::Application def help <<-HELP -puppet-queue(8) -- Queuing daemon for asynchronous storeconfigs +puppet-queue(8) -- Deprecated queuing daemon for asynchronous storeconfigs ======== SYNOPSIS -------- Retrieves serialized storeconfigs records from a queue and processes -them in order. +them in order. THIS FEATURE IS DEPRECATED; use PuppetDB instead. USAGE @@ -60,12 +60,12 @@ DESCRIPTION ----------- This application runs as a daemon and processes storeconfigs data, retrieving the data from a stomp server message queue and writing it to -a database. +a database. It was once necessary as a workaround for the poor performance +of ActiveRecord-based storeconfigs, but has been supplanted by the PuppetDB +service, which gives better performance with less complexity. -For more information, including instructions for properly setting up -your puppet master and message queue, see the documentation on setting -up asynchronous storeconfigs at: -http://projects.puppetlabs.com/projects/1/wiki/Using_Stored_Configuration +For more information, see the PuppetDB documentation at +http://docs.puppetlabs.com/puppetdb/latest OPTIONS From ef6943ce963eb00c34979d90dea5669a41636f56 Mon Sep 17 00:00:00 2001 From: Nick Fagerlund Date: Tue, 10 Dec 2013 16:20:14 -0800 Subject: [PATCH 180/800] Maint: Add link to list of lenses in Augeas type's lens parameter --- lib/puppet/type/augeas.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/puppet/type/augeas.rb b/lib/puppet/type/augeas.rb index 930235f0c..47cb58f17 100644 --- a/lib/puppet/type/augeas.rb +++ b/lib/puppet/type/augeas.rb @@ -139,7 +139,8 @@ Puppet::Type.newtype(:augeas) do newparam(:lens) do desc "Use a specific lens, e.g. `Hosts.lns`. When this parameter is set, you - must also set the `incl` parameter to indicate which file to load." + must also set the `incl` parameter to indicate which file to load. + The Augeas documentation includes [a list of available lenses](http://augeas.net/stock_lenses.html)." end newparam(:incl) do From bad6c0ef946901a1519a5aa2825998adc7ebb56e Mon Sep 17 00:00:00 2001 From: Nick Fagerlund Date: Tue, 10 Dec 2013 16:53:08 -0800 Subject: [PATCH 181/800] Maint: Catch a notice/notify confusion in a function description --- lib/puppet/parser/functions/defined.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/puppet/parser/functions/defined.rb b/lib/puppet/parser/functions/defined.rb index 314cc45a0..63fbdcc31 100644 --- a/lib/puppet/parser/functions/defined.rb +++ b/lib/puppet/parser/functions/defined.rb @@ -18,9 +18,9 @@ Puppet::Parser::Functions::newfunction(:defined, :type => :rvalue, :arity => -2, the configuration, and the following code will not work: if defined(File['/tmp/foo']) { - notify(\"This configuration includes the /tmp/foo file.\") + notify { \"This configuration includes the /tmp/foo file.\":} } - file {\"/tmp/foo\": + file { \"/tmp/foo\": ensure => present, } From c5029751581f49ed1d1510b7e986c32e9abe869c Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Wed, 11 Dec 2013 02:41:02 +0100 Subject: [PATCH 182/800] (pup-954) Fix failing test for Ruby 1.8.7 (hash entry order) One test relied on the order of hash elements which Ruby 1.8.7 does not guarantee. --- spec/unit/pops/types/type_calculator_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/unit/pops/types/type_calculator_spec.rb b/spec/unit/pops/types/type_calculator_spec.rb index 293c46263..b6a7084ea 100644 --- a/spec/unit/pops/types/type_calculator_spec.rb +++ b/spec/unit/pops/types/type_calculator_spec.rb @@ -1053,7 +1053,7 @@ describe 'The type calculator' do it "a generic result is created by generalize! given an instance specific result for a Hash" do generic = calculator.infer({'a' =>1,'b' => 2}) - generic.key_type.values.should == ['a', 'b'] + generic.key_type.values.sort.should == ['a', 'b'] generic.element_type.from.should == 1 generic.element_type.to.should == 2 calculator.generalize!(generic) From b69a3e2f42bd2c201ee4722f8f2d9c7da1d0f05b Mon Sep 17 00:00:00 2001 From: Jasper Lievisse Adriaanse Date: Fri, 6 Dec 2013 22:20:25 +0100 Subject: [PATCH 183/800] (#23376) Add support for ssh-ed25519 keys to ssh_authorized_key type --- lib/puppet/type/ssh_authorized_key.rb | 3 ++- spec/unit/provider/ssh_authorized_key/parsed_spec.rb | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/puppet/type/ssh_authorized_key.rb b/lib/puppet/type/ssh_authorized_key.rb index 1bdb5863b..12c8294b1 100644 --- a/lib/puppet/type/ssh_authorized_key.rb +++ b/lib/puppet/type/ssh_authorized_key.rb @@ -20,9 +20,10 @@ module Puppet newproperty(:type) do desc "The encryption type used: ssh-dss or ssh-rsa." - newvalues :'ssh-dss', :'ssh-rsa', :'ecdsa-sha2-nistp256', :'ecdsa-sha2-nistp384', :'ecdsa-sha2-nistp521' + newvalues :'ssh-dss', :'ssh-rsa', :'ecdsa-sha2-nistp256', :'ecdsa-sha2-nistp384', :'ecdsa-sha2-nistp521', :'ssh-ed25519' aliasvalue(:dsa, :'ssh-dss') + aliasvalue(:ed25519, :'ssh-ed25519') aliasvalue(:rsa, :'ssh-rsa') end diff --git a/spec/unit/provider/ssh_authorized_key/parsed_spec.rb b/spec/unit/provider/ssh_authorized_key/parsed_spec.rb index 4d17ffe51..65c94bfad 100755 --- a/spec/unit/provider/ssh_authorized_key/parsed_spec.rb +++ b/spec/unit/provider/ssh_authorized_key/parsed_spec.rb @@ -119,6 +119,8 @@ describe provider_class, :unless => Puppet.features.microsoft_windows? do 'AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBJIfxNoVK4FX3RuMlkHOwwxXwAh6Fqx5uAp4ftXrJ+64qYuIzb+/zSAkJV698Sre1b1lb0G4LyDdVAvXwaYK9kN25vy8umV3WdfZeHKXJGCcrplMCbbOERWARlpiPNEblg==' when 'ecdsa-sha2-nistp521' #ssh-keygen -t ecdsa -b 521 'AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBADLK+u12xwB0JOwpmaxYXv8KnPK4p+SE2405qoo+vpAQ569fMwPMgKzltd770amdeuFogw/MJu17PN9LDdrD3o0uwHMjWee6TpHQDkuEetaxiou6K0WAzgbxx9QsY0MsJgXf1BuMLqdK+xT183wOSXwwumv99G7T32dOJZ5tYrH0y4XMw==' + when 'ssh-ed25519' #ssh-keygen -t ed25519 + 'AAAAC3NzaC1lZDI1NTE5AAAAIBWvu7D1KHBPaNXQcEuBsp48+JyPelXAq8ds6K5Du9gd' else pending("No sample key for #{keytype} yet") end From c052a47b4cb6b29b7bf9915875080af7f9873f25 Mon Sep 17 00:00:00 2001 From: Nick Fagerlund Date: Fri, 6 Dec 2013 14:45:29 -0800 Subject: [PATCH 184/800] Maint: (#18294) Update docs for ip property of Zone type (allows defrouters since 2.6) Per Redmine 18294, the Zone type has allowed specifying default routers as part of the IP string since at least 2.6 -- see Redmine 4296 for deets. This commit updates the doc string to show how to do that. --- lib/puppet/type/zone.rb | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/puppet/type/zone.rb b/lib/puppet/type/zone.rb index 04c807383..c61debada 100644 --- a/lib/puppet/type/zone.rb +++ b/lib/puppet/type/zone.rb @@ -148,9 +148,17 @@ autorequire that directory." newproperty(:ip, :parent => Puppet::Property::List) do require 'ipaddr' - desc "The IP address of the zone. IP addresses must be specified - with the interface, separated by a colon, e.g.: bge0:192.168.0.1. - For multiple interfaces, specify them in an array." + desc "The IP address of the zone. IP addresses **must** be specified + with an interface, and may optionally be specified with a default router + (sometimes called a defrouter). The interface, IP address, and default + router should be separated by colons to form a complete IP address string. + For example: `bge0:192.168.178.200` would be a valid IP address string + without a default router, and `bge0:192.168.178.200:192.168.178.1` adds a + default router to it. + + For zones with multiple interfaces, the value of this attribute should be + an array of IP address strings (each of which must include an interface + and may include a default router)." # The default action of list should is to lst.join(' '). By specifying # @should, we ensure the should remains an array. If we override should, we From f6b77cc25c0998c10cedf54f24baefd3ce69db76 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Wed, 11 Dec 2013 22:32:04 +0100 Subject: [PATCH 185/800] (PUP-992) Add support for Array as operand in relationships This adds support for using Arrays with references as operands in a relationship. --- lib/puppet/pops/evaluator/relationship_operator.rb | 5 +++++ spec/unit/pops/evaluator/evaluating_parser_spec.rb | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/lib/puppet/pops/evaluator/relationship_operator.rb b/lib/puppet/pops/evaluator/relationship_operator.rb index e81a22b23..eaec97d7e 100644 --- a/lib/puppet/pops/evaluator/relationship_operator.rb +++ b/lib/puppet/pops/evaluator/relationship_operator.rb @@ -75,6 +75,11 @@ class Puppet::Pops::Evaluator::RelationshipOperator o end + # Array content needs to be transformed + def transform_Array(o, scope) + o.map{|x| transform(x, scope) } + end + # Asserts (and returns) the type if it is a PCatalogEntryType # (A PCatalogEntryType is the base class of PHostClassType, and PResourceType). # diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index 6fc772367..9a219305e 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -830,6 +830,15 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do scope.compiler.should have_relationship(['Notify', 'a', '->', 'Notify', 'b']) end + it 'should form a relation with [File[a], File[b]] -> [File[x], File[y]]' do + source = "[File[a], File[b]] -> [File[x], File[y]]" + parser.evaluate_string(scope, source, __FILE__) + scope.compiler.should have_relationship(['File', 'a', '->', 'File', 'x']) + scope.compiler.should have_relationship(['File', 'b', '->', 'File', 'x']) + scope.compiler.should have_relationship(['File', 'a', '->', 'File', 'y']) + scope.compiler.should have_relationship(['File', 'b', '->', 'File', 'y']) + end + it 'should form a relation with <-' do source = "File[a] <- File[b]" parser.evaluate_string(scope, source, __FILE__) From 26da689bedeb600c580fbd4bc53cfe2597b03fe2 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Wed, 11 Dec 2013 22:46:53 +0100 Subject: [PATCH 186/800] (PUP-994) Ensure relationship operands have uniq content The relationship operands when containing duplicate entries caused multiple relationships to be formed between the same entries. Ensuring that input to the compiler is free of duplicates reduces the amount of work at a later stage. --- lib/puppet/pops/evaluator/relationship_operator.rb | 3 +++ spec/unit/pops/evaluator/evaluating_parser_spec.rb | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/lib/puppet/pops/evaluator/relationship_operator.rb b/lib/puppet/pops/evaluator/relationship_operator.rb index eaec97d7e..7ffd20c8c 100644 --- a/lib/puppet/pops/evaluator/relationship_operator.rb +++ b/lib/puppet/pops/evaluator/relationship_operator.rb @@ -127,10 +127,13 @@ class Puppet::Pops::Evaluator::RelationshipOperator # real is [left, right], and both the left and right may be a single value or an array. In each case all content # should be flattened, and then transformed to a type. left or right may also be a value that is transformed # into an array, and thus the resulting left and right must be flattened individually + # Once flattened, the operands should be sets (to remove duplicate entries) # real = left_right_evaluated.collect {|x| [x].flatten.collect {|x| transform(x, scope) }} real[0].flatten! real[1].flatten! + real[0].uniq! + real[1].uniq! # reverse order if operator is Right to Left source, target = reverse_operator?(relationship_expression) ? real.reverse : real diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index 9a219305e..804a65403 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -839,6 +839,13 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do scope.compiler.should have_relationship(['File', 'b', '->', 'File', 'y']) end + it 'should tolerate (eliminiate) duplicates in operands' do + source = "[File[a], File[a]] -> File[x]" + parser.evaluate_string(scope, source, __FILE__) + scope.compiler.should have_relationship(['File', 'a', '->', 'File', 'x']) + scope.compiler.relationships.size.should == 1 + end + it 'should form a relation with <-' do source = "File[a] <- File[b]" parser.evaluate_string(scope, source, __FILE__) From d74dd9cea874ad0a5a9fe6d4d7674c10911d3d1d Mon Sep 17 00:00:00 2001 From: Nick Fagerlund Date: Tue, 10 Dec 2013 14:15:12 -0800 Subject: [PATCH 187/800] Maint: Move pkgdmg documentation into provider description; document changed behavior This wiki page was more longwinded than it needed to be; you can really sum it up in about four bullet points. So I did that, and removed the wiki link. Also, while investigating what the wiki said, it became clear that this provider no longer actually uses open-uri: a Kernel.open call was changed to File.open, but apparently nobody was using file:/// URLs (or anything weirder), so it went unnoticed. So I added some comments for future maintainers of this code. --- lib/puppet/provider/package/pkgdmg.rb | 33 +++++++++++++++++++-------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/lib/puppet/provider/package/pkgdmg.rb b/lib/puppet/provider/package/pkgdmg.rb index f14b53158..70b825cef 100644 --- a/lib/puppet/provider/package/pkgdmg.rb +++ b/lib/puppet/provider/package/pkgdmg.rb @@ -13,13 +13,27 @@ require 'facter/util/plist' require 'puppet/util/http_proxy' Puppet::Type.type(:package).provide :pkgdmg, :parent => Puppet::Provider::Package do - desc "Package management based on Apple's Installer.app and - DiskUtility.app. This package works by checking the contents of a - DMG image for Apple pkg or mpkg files. Any number of pkg or mpkg - files may exist in the root directory of the DMG file system. - Subdirectories are not checked for packages. See - [the wiki docs on this provider](http://projects.puppetlabs.com/projects/puppet/wiki/Package_Management_With_Dmg_Patterns) - for more detail." + desc "Package management based on Apple's Installer.app and DiskUtility.app. + + This provider works by checking the contents of a DMG image for Apple pkg or + mpkg files. Any number of pkg or mpkg files may exist in the root directory + of the DMG file system, and Puppet will install all of them. Subdirectories + are not checked for packages. + + This provider can also accept plain .pkg (but not .mpkg) files in addition + to .dmg files. + + Notes: + + * The `source` attribute is mandatory. It must be either a local disk path + or an HTTP, HTTPS, or FTP URL to the package. + * The `name` of the resource must be the filename (without path) of the DMG file. + * When installing the packages from a DMG, this provider writes a file to + disk at `/var/db/.puppet_pkgdmg_installed_NAME`. If that file is present, + Puppet assumes all packages from that DMG are already installed. + * This provider is not versionable and uses DMG filenames to determine + whether a package has been installed. Thus, to install new a version of a + package, you must create a new DMG with a different filename." confine :operatingsystem => :darwin defaultfor :operatingsystem => :darwin @@ -60,7 +74,7 @@ Puppet::Type.type(:package).provide :pkgdmg, :parent => Puppet::Provider::Packag unless source =~ /\.dmg$/i || source =~ /\.pkg$/i 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' + require 'open-uri' # Dead code; this is never used. The File.open call 20-ish lines south of here used to be Kernel.open but changed in '09. -NF cached_source = source tmpdir = Dir.mktmpdir ext = /(\.dmg|\.pkg)$/i.match(source)[0] @@ -77,12 +91,13 @@ Puppet::Type.type(:package).provide :pkgdmg, :parent => Puppet::Provider::Packag curl *args Puppet.debug "Success: curl transfered [#{name}] (via: curl #{args.join(" ")})" rescue Puppet::ExecutionFailure - Puppet.debug "curl #{args.join(" ")} did not transfer [#{name}]. Falling back to slower open-uri transfer methods." + Puppet.debug "curl #{args.join(" ")} did not transfer [#{name}]. Falling back to local file." # This used to fall back to open-uri. -NF cached_source = source end end if source =~ /\.dmg$/i + # If you fix this to use open-uri again, you must update the docs above. -NF File.open(cached_source) do |dmg| xml_str = hdiutil "mount", "-plist", "-nobrowse", "-readonly", "-noidme", "-mountrandom", "/tmp", dmg.path hdiutil_info = Plist::parse_xml(xml_str) From 5f2dc7173cbad94017486fdd854305f9009331f4 Mon Sep 17 00:00:00 2001 From: Nick Fagerlund Date: Tue, 10 Dec 2013 14:16:02 -0800 Subject: [PATCH 188/800] Maint: Remove remaining links to wiki pages in Puppet source These pages now all have better-maintained counterparts in the main docs site. --- lib/puppet/indirector/node/ldap.rb | 2 +- lib/puppet/provider/user/aix.rb | 2 +- lib/puppet/reference/report.rb | 2 +- lib/puppet/reports/tagmail.rb | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/puppet/indirector/node/ldap.rb b/lib/puppet/indirector/node/ldap.rb index f1a79ef6e..0075b5b4b 100644 --- a/lib/puppet/indirector/node/ldap.rb +++ b/lib/puppet/indirector/node/ldap.rb @@ -3,7 +3,7 @@ require 'puppet/indirector/ldap' class Puppet::Node::Ldap < Puppet::Indirector::Ldap desc "Search in LDAP for node configuration information. See - the [LDAP Nodes](http://projects.puppetlabs.com/projects/puppet/wiki/Ldap_Nodes) page for more information. This will first + the [LDAP Nodes](http://docs.puppetlabs.com/guides/ldap_nodes.html) page for more information. This will first search for whatever the certificate name is, then (if that name contains a `.`) for the short name, then `default`." diff --git a/lib/puppet/provider/user/aix.rb b/lib/puppet/provider/user/aix.rb index 0831f2e26..f48a943ad 100644 --- a/lib/puppet/provider/user/aix.rb +++ b/lib/puppet/provider/user/aix.rb @@ -7,7 +7,7 @@ # but puppet does not allow it. There is a ticket open for that (#5431) # - AIX maximum password age is in WEEKs, not days # -# See http://projects.puppetlabs.com/projects/puppet/wiki/Development_Provider_Development +# See http://docs.puppetlabs.com/guides/provider_development.html # for more information # # Author:: Hector Rivas Gandara diff --git a/lib/puppet/reference/report.rb b/lib/puppet/reference/report.rb index 47fc779ab..b161a4aa5 100644 --- a/lib/puppet/reference/report.rb +++ b/lib/puppet/reference/report.rb @@ -9,7 +9,7 @@ Puppet clients can report back to the server after each transaction. This transaction report is sent as a YAML dump of the `Puppet::Transaction::Report` class and includes every log message that was generated during the transaction along with as many metrics as Puppet knows how -to collect. See [Reports and Reporting](http://projects.puppetlabs.com/projects/puppet/wiki/Reports_And_Reporting) for more information on how to use reports. +to collect. See [Reports and Reporting](http://docs.puppetlabs.com/guides/reporting.html) for more information on how to use reports. Currently, clients default to not sending in reports; you can enable reporting by setting the `report` parameter to true. diff --git a/lib/puppet/reports/tagmail.rb b/lib/puppet/reports/tagmail.rb index a07f98986..c72fa24df 100644 --- a/lib/puppet/reports/tagmail.rb +++ b/lib/puppet/reports/tagmail.rb @@ -8,7 +8,7 @@ Puppet::Reports.register_report(:tagmail) do desc "This report sends specific log messages to specific email addresses based on the tags in the log messages. - See the [documentation on tags](http://projects.puppetlabs.com/projects/puppet/wiki/Using_Tags) for more information. + See the [documentation on tags](http://docs.puppetlabs.com/puppet/latest/reference/lang_tags.html) for more information. To use this report, you must create a `tagmail.conf` file in the location specified by the `tagmap` setting. This is a simple file that maps tags to From 024df9992d753c719aee6ac0dda9a3d0ca1552b0 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Wed, 11 Dec 2013 17:11:49 -0800 Subject: [PATCH 189/800] (#23373) Support selecting the section The --section option to the subcommand now allows a specific section of the configuration file to be manipulated. --- lib/puppet/face/config.rb | 9 ++-- lib/puppet/settings/ini_file.rb | 26 ++++++++--- spec/unit/settings/ini_file_spec.rb | 69 ++++++++++++++++++++++++++--- 3 files changed, 91 insertions(+), 13 deletions(-) diff --git a/lib/puppet/face/config.rb b/lib/puppet/face/config.rb index 3100426af..d1ff86672 100644 --- a/lib/puppet/face/config.rb +++ b/lib/puppet/face/config.rb @@ -59,14 +59,17 @@ Puppet::Face.define(:config, '0.0.1') do $ puppet config set rundir /var/run/puppet EOT - when_invoked do |*args| - name, value = args + option "--section SECTION_NAME" do + default_to { "main" } + summary "The section of the configuration file to change." + end + when_invoked do |name, value, options| file = Puppet::FileSystem::File.new(Puppet.settings.which_configuration_file) file.touch file.open(nil, 'r+') do |file| Puppet::Settings::IniFile.update(file) do |config| - config.set(name, value) + config.set(options[:section], name, value) end end nil diff --git a/lib/puppet/settings/ini_file.rb b/lib/puppet/settings/ini_file.rb index 5e1aebf29..e99dce4ba 100644 --- a/lib/puppet/settings/ini_file.rb +++ b/lib/puppet/settings/ini_file.rb @@ -1,5 +1,7 @@ # @api private class Puppet::Settings::IniFile + DEFAULT_SECTION_NAME = "main" + def self.update(config_fh, &block) config = parse(config_fh) manipulator = Manipulator.new(config) @@ -35,12 +37,26 @@ class Puppet::Settings::IniFile @lines.each(&block) end - def setting(name) - @lines.find do |line| + def setting(section, name) + settings_in(section).find do |line| line.is_a?(SettingLine) && line.name == name end end + def settings_in(section) + section_lines = [] + current_section = DEFAULT_SECTION_NAME + @lines.each do |line| + if line.is_a?(SectionLine) + current_section = line.name + elsif current_section == section + section_lines << line + end + end + + section_lines + end + def write(fh) fh.truncate(0) fh.rewind @@ -55,11 +71,12 @@ class Puppet::Settings::IniFile @config = config end - def set(name, value) - setting = @config.setting(name) + def set(section, name, value) + setting = @config.setting(section, name) if setting setting.value = value else + @config << SectionLine.new("", section, "") @config << SettingLine.new("", name, "=", value, "") end end @@ -90,5 +107,4 @@ class Puppet::Settings::IniFile fh.puts(suffix) end end - end diff --git a/spec/unit/settings/ini_file_spec.rb b/spec/unit/settings/ini_file_spec.rb index d11e010fa..aa5beca18 100644 --- a/spec/unit/settings/ini_file_spec.rb +++ b/spec/unit/settings/ini_file_spec.rb @@ -21,20 +21,20 @@ describe Puppet::Settings::IniFile do config_fh = a_config_file_containing("") Puppet::Settings::IniFile.update(config_fh) do |config| - config.set("name", "value") + config.set("the_section", "name", "value") end - expect(config_fh.string).to eq "name=value\n" + expect(config_fh.string).to eq "[the_section]\nname=value\n" end it "preserves comments when writing a new name and value" do config_fh = a_config_file_containing("# this is a comment") Puppet::Settings::IniFile.update(config_fh) do |config| - config.set("name", "value") + config.set("the_section", "name", "value") end - expect(config_fh.string).to eq "# this is a comment\nname=value\n" + expect(config_fh.string).to eq "# this is a comment\n[the_section]\nname=value\n" end it "updates existing names and values in place" do @@ -46,7 +46,7 @@ describe Puppet::Settings::IniFile do CONFIG Puppet::Settings::IniFile.update(config_fh) do |config| - config.set("name", "changed value") + config.set("section", "name", "changed value") end expect(config_fh.string).to eq <<-CONFIG @@ -57,6 +57,65 @@ describe Puppet::Settings::IniFile do CONFIG end + it "updates only the value in the selected section" do + config_fh = a_config_file_containing(<<-CONFIG) + [other_section] + name = does not change + [section] + name = original value + CONFIG + + Puppet::Settings::IniFile.update(config_fh) do |config| + config.set("section", "name", "changed value") + end + + expect(config_fh.string).to eq <<-CONFIG + [other_section] + name = does not change + [section] + name = changed value + CONFIG + end + + it "considers settings outside a section to be in section 'main'" do + config_fh = a_config_file_containing(<<-CONFIG) + name = original value + CONFIG + + Puppet::Settings::IniFile.update(config_fh) do |config| + config.set("main", "name", "changed value") + end + + expect(config_fh.string).to eq <<-CONFIG + name = changed value + CONFIG + end + + it "finds settings when the section is split up" do + config_fh = a_config_file_containing(<<-CONFIG) + [section] + name = original value + [different] + name = other value + [section] + other_name = different original value + CONFIG + + Puppet::Settings::IniFile.update(config_fh) do |config| + config.set("section", "name", "changed value") + config.set("section", "other_name", "other changed value") + end + + expect(config_fh.string).to eq <<-CONFIG + [section] + name = changed value + [different] + name = other value + [section] + other_name = other changed value + CONFIG + end + def a_config_file_containing(text) StringIO.new(text) end From ff96ef32cc188e03e7d403b243f23b93d81529b7 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Wed, 11 Dec 2013 17:57:09 -0800 Subject: [PATCH 190/800] (maint) Add an acceptance pre-suite step to set the parser Allows us to run acceptance tests with the parser set to the value of $PARSER from the environment. We can use this to test how the acceptance tests fair against the current iteration of the future parser, for instance. --- .../setup/packages/pre-suite/05_SetParser.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 acceptance/config/el6/setup/packages/pre-suite/05_SetParser.rb diff --git a/acceptance/config/el6/setup/packages/pre-suite/05_SetParser.rb b/acceptance/config/el6/setup/packages/pre-suite/05_SetParser.rb new file mode 100644 index 000000000..99d0d67bd --- /dev/null +++ b/acceptance/config/el6/setup/packages/pre-suite/05_SetParser.rb @@ -0,0 +1,17 @@ +test_name "add parser=#{ENV['PARSER']} to all puppet.conf (only if $PARSER is set)" do + + parser = ENV['PARSER'] + next if parser.nil? + + hosts.each do |host| + step "adjust #{host} puppet.conf" do + temp = host.tmpdir('parser-set') + opts = { + 'main' => { + 'parser' => parser + } + } + lay_down_new_puppet_conf(host, opts, temp) + end + end +end From f066e74a65a8d4755bf199c10372b127a99f3238 Mon Sep 17 00:00:00 2001 From: Adrien Thebo Date: Wed, 11 Dec 2013 11:59:14 -0800 Subject: [PATCH 191/800] (#22744) Filter virtual resources in the static compiler Previously (on Lost), the static compiler was directly creating and invoking the regular compiler and then modifying the file resources to get the desired behavior. Because the static compiler was directly invoking the regular compiler, it was not invoking all the methods that would normally get called when the regular compiler was called by the indirector. This commit resolves this problem by making the static compiler inherit from the regular compiler instead of delegating to the compiler. If there are future changes to how the indirector invokes termini, the static compiler will still be able to generate catalogs in the same manner as the regular compiler. Original patch by Chris Spence --- .../indirector/catalog/static_compiler.rb | 10 ++--- .../catalog/static_compiler_spec.rb | 43 ++++++++++++++++--- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/lib/puppet/indirector/catalog/static_compiler.rb b/lib/puppet/indirector/catalog/static_compiler.rb index fe3bfb5d7..a8b6bb582 100644 --- a/lib/puppet/indirector/catalog/static_compiler.rb +++ b/lib/puppet/indirector/catalog/static_compiler.rb @@ -1,8 +1,8 @@ require 'puppet/node' require 'puppet/resource/catalog' -require 'puppet/indirector/code' +require 'puppet/indirector/catalog/compiler' -class Puppet::Resource::Catalog::StaticCompiler < Puppet::Indirector::Code +class Puppet::Resource::Catalog::StaticCompiler < Puppet::Resource::Catalog::Compiler desc %q{Compiles catalogs on demand using the optional static compiler. This functions similarly to the normal compiler, but it replaces puppet:/// file @@ -35,12 +35,8 @@ class Puppet::Resource::Catalog::StaticCompiler < Puppet::Indirector::Code that compiled a given catalog may not have stored the required file contents in their filebuckets.} - def compiler - @compiler ||= indirection.terminus(:compiler) - end - def find(request) - return nil unless catalog = compiler.find(request) + return nil unless catalog = super raise "Did not get catalog back" unless catalog.is_a?(model) diff --git a/spec/unit/indirector/catalog/static_compiler_spec.rb b/spec/unit/indirector/catalog/static_compiler_spec.rb index 36480f0a8..e42ea777f 100644 --- a/spec/unit/indirector/catalog/static_compiler_spec.rb +++ b/spec/unit/indirector/catalog/static_compiler_spec.rb @@ -10,6 +10,11 @@ describe Puppet::Resource::Catalog::StaticCompiler do @num_file_resources = 10 end + before :each do + Facter.stubs(:to_hash).returns({}) + Facter.stubs(:value) + end + let(:request) do Puppet::Indirector::Request.new(:the_indirection_named_foo, :find, @@ -23,10 +28,7 @@ describe Puppet::Resource::Catalog::StaticCompiler do end it "returns nil if there is no compiled catalog" do - compiler = mock('compiler indirection') - compiler.expects(:find).with(request).returns(nil) - - subject.expects(:compiler).returns(compiler) + subject.expects(:compile).returns(nil) subject.find(request).should be_nil end @@ -67,9 +69,6 @@ describe Puppet::Resource::Catalog::StaticCompiler do describe "(#15193) when storing content to the filebucket" do it "explicitly uses the indirection method" do - # Do not stub the store_content method which is stubbed by default in the - # value of the stub_methods option. - stub_the_compiler(:stub_methods => false) # We expect the content to be retrieved from the FileServer ... fake_content = mock('FileServer Content') @@ -96,6 +95,7 @@ describe Puppet::Resource::Catalog::StaticCompiler do end # Obtain the Static Catalog + subject.stubs(:compile).returns(build_catalog) resource_catalog = subject.find(request) # Ensure all of the file resources were filtered @@ -171,6 +171,35 @@ describe Puppet::Resource::Catalog::StaticCompiler do catalog end + describe "(#22744) when filtering resources" do + let(:catalog) { stub_everything 'catalog' } + + it "should delegate to the catalog instance filtering" do + catalog.expects(:filter) + subject.filter(catalog) + end + + it "should filter out virtual resources" do + resource = mock 'resource', :virtual? => true + catalog.stubs(:filter).yields(resource) + + subject.filter(catalog) + end + + it "should return the same catalog if it doesn't support filtering" do + catalog.stubs(:respond_to?).with(:filter) + subject.filter(catalog).should == catalog + end + + it "should return the filtered catalog" do + filtered_catalog = stub 'filtered catalog' + catalog.stubs(:filter).returns(filtered_catalog) + + subject.filter(catalog).should == filtered_catalog + end + + end + def fileserver_metadata(options = {}) yaml = < Date: Thu, 12 Dec 2013 19:13:37 +0100 Subject: [PATCH 192/800] (PUP-490) Remove partial import-support from future evaluator The support is removed (basically a mapping of a function call to the function "import" to a ImportExpression + removing tests. The intermediate future parser is still using ImportExpression, so model and related logic not changed, but marked with deprecation and "to be removed in Puppet 4" (or whenever the intermediate parser is removed from the code base). The grammar/parser is modified, but this is only a removal of a comment. --- lib/puppet/pops/model/factory.rb | 18 +- lib/puppet/pops/model/model.rb | 2 +- lib/puppet/pops/model/model_label_provider.rb | 2 +- lib/puppet/pops/model/model_tree_dumper.rb | 1 + lib/puppet/pops/parser/egrammar.ra | 4 - lib/puppet/pops/parser/eparser.rb | 154 +++++++++--------- .../pops/parser/parse_conditionals_spec.rb | 9 - .../transform_conditionals_spec.rb | 9 - 8 files changed, 88 insertions(+), 111 deletions(-) diff --git a/lib/puppet/pops/model/factory.rb b/lib/puppet/pops/model/factory.rb index c224a0ba0..055374fc9 100644 --- a/lib/puppet/pops/model/factory.rb +++ b/lib/puppet/pops/model/factory.rb @@ -180,6 +180,7 @@ class Puppet::Pops::Model::Factory o end + # @deprecated, to be removed in Puppet 4 def build_ImportExpression(o, files) # The argument files has already been built files.each {|f| o.addFiles(to_ops(f)) } @@ -639,6 +640,7 @@ class Puppet::Pops::Model::Factory # new(Model::CollectExpression, Puppet::Pops::Model::Factory.fqr(type_expr), query_expr, attribute_operations) end + # @deprecated, to be removed in Puppet 4 def self.IMPORT(files) new(Model::ImportExpression, files) end @@ -700,16 +702,12 @@ class Puppet::Pops::Model::Factory expr = expr.current if expr.is_a?(Puppet::Pops::Model::Factory) name = memo[-1] if name.is_a? Model::QualifiedName - if name.value() == 'import' - memo[-1] = Puppet::Pops::Model::Factory.IMPORT(expr.is_a?(Array) ? expr : [expr]) - else - memo[-1] = Puppet::Pops::Model::Factory.CALL_NAMED(name, false, expr.is_a?(Array) ? expr : [expr]) - if expr.is_a?(Model::CallNamedFunctionExpression) - # Patch statement function call to expression style - # This is needed because it is first parsed as a "statement" and the requirement changes as it becomes - # an argument to the name to call transform above. - expr.rval_required = true - end + memo[-1] = Puppet::Pops::Model::Factory.CALL_NAMED(name, false, expr.is_a?(Array) ? expr : [expr]) + if expr.is_a?(Model::CallNamedFunctionExpression) + # Patch statement function call to expression style + # This is needed because it is first parsed as a "statement" and the requirement changes as it becomes + # an argument to the name to call transform above. + expr.rval_required = true end else memo << expr diff --git a/lib/puppet/pops/model/model.rb b/lib/puppet/pops/model/model.rb index 6f7d58f75..dea500358 100644 --- a/lib/puppet/pops/model/model.rb +++ b/lib/puppet/pops/model/model.rb @@ -72,7 +72,7 @@ module Puppet::Pops::Model class ParenthesizedExpression < UnaryExpression; end # An import of one or several files. - # + # @deprecated, to be removed in Puppet 4 class ImportExpression < Expression contains_many_uni 'files', Expression, :lowerBound => 1 end diff --git a/lib/puppet/pops/model/model_label_provider.rb b/lib/puppet/pops/model/model_label_provider.rb index 9744421e7..6a6782fe3 100644 --- a/lib/puppet/pops/model/model_label_provider.rb +++ b/lib/puppet/pops/model/model_label_provider.rb @@ -33,7 +33,7 @@ class Puppet::Pops::Model::ModelLabelProvider < Puppet::Pops::LabelProvider def label_AndExpression o ; "'and' expression" end def label_OrExpression o ; "'or' expression" end def label_InExpression o ; "'in' expression" end - def label_ImportExpression o ; "'import' expression" end + def label_ImportExpression o ; "'import' expression" end # @deprecated, to be removed in Puppet 4 def label_AssignmentExpression o ; "'#{o.operator}' expression" end def label_AttributeOperation o ; "'#{o.operator}' expression" end def label_LiteralList o ; "Array Expression" end diff --git a/lib/puppet/pops/model/model_tree_dumper.rb b/lib/puppet/pops/model/model_tree_dumper.rb index 14ad52caf..15f1a6e5c 100644 --- a/lib/puppet/pops/model/model_tree_dumper.rb +++ b/lib/puppet/pops/model/model_tree_dumper.rb @@ -90,6 +90,7 @@ class Puppet::Pops::Model::ModelTreeDumper < Puppet::Pops::Model::TreeDumper ["in", do_dump(o.left_expr), do_dump(o.right_expr)] end + # @deprecated, to be removed in Puppet 4 def dump_ImportExpression o ["import"] + o.files.collect {|f| do_dump(f) } end diff --git a/lib/puppet/pops/parser/egrammar.ra b/lib/puppet/pops/parser/egrammar.ra index c283302b5..fdea774e9 100644 --- a/lib/puppet/pops/parser/egrammar.ra +++ b/lib/puppet/pops/parser/egrammar.ra @@ -338,10 +338,6 @@ case_expression selector_entry : expression FARROW expression { result = Factory.MAP(val[0], val[2]) ; loc result, val[1] } -# --- IMPORT -# IMPORT is handled as a non parenthesized call and is transformed to an ImportExpression. -# i.e. there is no special grammar for it - it is just a "call statement". - #---RESOURCE # # Produces [Model::ResourceExpression, Model::ResourceDefaultsExpression] diff --git a/lib/puppet/pops/parser/eparser.rb b/lib/puppet/pops/parser/eparser.rb index 12f3f2c89..eaa23d226 100644 --- a/lib/puppet/pops/parser/eparser.rb +++ b/lib/puppet/pops/parser/eparser.rb @@ -20,7 +20,7 @@ module Puppet module Parser class Parser < Racc::Parser -module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 709) +module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 705) # Make emacs happy # Local Variables: @@ -1707,7 +1707,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 338) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 354) +module_eval(<<'.,.,', 'egrammar.ra', 350) def _reduce_105(val, _values, result) result = val[0] @@ -1715,7 +1715,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 354) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 357) +module_eval(<<'.,.,', 'egrammar.ra', 353) def _reduce_106(val, _values, result) result = case Factory.resource_shape(val[1]) when :resource, :class @@ -1735,7 +1735,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 357) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 372) +module_eval(<<'.,.,', 'egrammar.ra', 368) def _reduce_107(val, _values, result) result = case Factory.resource_shape(val[1]) when :resource, :class, :defaults, :override @@ -1748,7 +1748,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 372) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 380) +module_eval(<<'.,.,', 'egrammar.ra', 376) def _reduce_108(val, _values, result) result = case Factory.resource_shape(val[0]) when :resource, :class @@ -1766,7 +1766,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 380) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 393) +module_eval(<<'.,.,', 'egrammar.ra', 389) def _reduce_109(val, _values, result) result = case Factory.resource_shape(val[0]) when :resource, :class @@ -1786,7 +1786,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 393) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 408) +module_eval(<<'.,.,', 'egrammar.ra', 404) def _reduce_110(val, _values, result) result = Factory.RESOURCE(Factory.fqn(token_text(val[0])), val[2]) loc result, val[0], val[4] @@ -1795,56 +1795,56 @@ module_eval(<<'.,.,', 'egrammar.ra', 408) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 413) +module_eval(<<'.,.,', 'egrammar.ra', 409) def _reduce_111(val, _values, result) result = Factory.RESOURCE_BODY(val[0], val[2]) result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 415) +module_eval(<<'.,.,', 'egrammar.ra', 411) def _reduce_112(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 418) +module_eval(<<'.,.,', 'egrammar.ra', 414) def _reduce_113(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 419) +module_eval(<<'.,.,', 'egrammar.ra', 415) def _reduce_114(val, _values, result) result = val[0].push val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 424) +module_eval(<<'.,.,', 'egrammar.ra', 420) def _reduce_115(val, _values, result) result = :virtual result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 425) +module_eval(<<'.,.,', 'egrammar.ra', 421) def _reduce_116(val, _values, result) result = :exported result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 426) +module_eval(<<'.,.,', 'egrammar.ra', 422) def _reduce_117(val, _values, result) result = :exported result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 438) +module_eval(<<'.,.,', 'egrammar.ra', 434) def _reduce_118(val, _values, result) result = Factory.COLLECT(val[0], val[1], val[3]) loc result, val[0], val[5] @@ -1853,7 +1853,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 438) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 442) +module_eval(<<'.,.,', 'egrammar.ra', 438) def _reduce_119(val, _values, result) result = Factory.COLLECT(val[0], val[1], []) loc result, val[0], val[1] @@ -1862,14 +1862,14 @@ module_eval(<<'.,.,', 'egrammar.ra', 442) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 447) +module_eval(<<'.,.,', 'egrammar.ra', 443) def _reduce_120(val, _values, result) result = Factory.VIRTUAL_QUERY(val[1]) ; loc result, val[0], val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 448) +module_eval(<<'.,.,', 'egrammar.ra', 444) def _reduce_121(val, _values, result) result = Factory.EXPORTED_QUERY(val[1]) ; loc result, val[0], val[2] result @@ -1880,21 +1880,21 @@ module_eval(<<'.,.,', 'egrammar.ra', 448) # reduce 123 omitted -module_eval(<<'.,.,', 'egrammar.ra', 461) +module_eval(<<'.,.,', 'egrammar.ra', 457) def _reduce_124(val, _values, result) result = [] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 462) +module_eval(<<'.,.,', 'egrammar.ra', 458) def _reduce_125(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 463) +module_eval(<<'.,.,', 'egrammar.ra', 459) def _reduce_126(val, _values, result) result = val[0].push(val[2]) result @@ -1907,7 +1907,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 463) # reduce 129 omitted -module_eval(<<'.,.,', 'egrammar.ra', 479) +module_eval(<<'.,.,', 'egrammar.ra', 475) def _reduce_130(val, _values, result) result = Factory.ATTRIBUTE_OP(val[0][:value], :'=>', val[2]) loc result, val[0], val[2] @@ -1916,7 +1916,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 479) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 483) +module_eval(<<'.,.,', 'egrammar.ra', 479) def _reduce_131(val, _values, result) result = Factory.ATTRIBUTE_OP(val[0][:value], :'+>', val[2]) loc result, val[0], val[2] @@ -1925,7 +1925,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 483) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 493) +module_eval(<<'.,.,', 'egrammar.ra', 489) def _reduce_132(val, _values, result) result = add_definition(Factory.DEFINITION(classname(val[1][:value]), val[2], val[4])) loc result, val[0], val[5] @@ -1938,7 +1938,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 493) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 507) +module_eval(<<'.,.,', 'egrammar.ra', 503) def _reduce_133(val, _values, result) # Remove this class' name from the namestack as all nested classes have been parsed namepop @@ -1949,7 +1949,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 507) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 517) +module_eval(<<'.,.,', 'egrammar.ra', 513) def _reduce_134(val, _values, result) namestack(val[0][:value]) ; result = val[0] result @@ -1962,7 +1962,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 517) # reduce 137 omitted -module_eval(<<'.,.,', 'egrammar.ra', 526) +module_eval(<<'.,.,', 'egrammar.ra', 522) def _reduce_138(val, _values, result) result = val[1] result @@ -1973,7 +1973,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 526) # reduce 140 omitted -module_eval(<<'.,.,', 'egrammar.ra', 543) +module_eval(<<'.,.,', 'egrammar.ra', 539) def _reduce_141(val, _values, result) result = add_definition(Factory.NODE(val[1], val[2], val[4])) loc result, val[0], val[5] @@ -1982,7 +1982,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 543) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 547) +module_eval(<<'.,.,', 'egrammar.ra', 543) def _reduce_142(val, _values, result) result = add_definition(Factory.NODE(val[1], val[2], nil)) loc result, val[0], val[4] @@ -1991,35 +1991,35 @@ module_eval(<<'.,.,', 'egrammar.ra', 547) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 557) +module_eval(<<'.,.,', 'egrammar.ra', 553) def _reduce_143(val, _values, result) result = [result] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 558) +module_eval(<<'.,.,', 'egrammar.ra', 554) def _reduce_144(val, _values, result) result = val[0].push(val[2]) result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 563) +module_eval(<<'.,.,', 'egrammar.ra', 559) def _reduce_145(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 564) +module_eval(<<'.,.,', 'egrammar.ra', 560) def _reduce_146(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 565) +module_eval(<<'.,.,', 'egrammar.ra', 561) def _reduce_147(val, _values, result) result = Factory.literal(:default); loc result, val[0] result @@ -2028,14 +2028,14 @@ module_eval(<<'.,.,', 'egrammar.ra', 565) # reduce 148 omitted -module_eval(<<'.,.,', 'egrammar.ra', 569) +module_eval(<<'.,.,', 'egrammar.ra', 565) def _reduce_149(val, _values, result) result = Factory.literal(val[0][:value]); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 570) +module_eval(<<'.,.,', 'egrammar.ra', 566) def _reduce_150(val, _values, result) result = Factory.concat(val[0], '.', val[2][:value]); loc result, val[0], val[2] result @@ -2044,161 +2044,161 @@ module_eval(<<'.,.,', 'egrammar.ra', 570) # reduce 151 omitted -module_eval(<<'.,.,', 'egrammar.ra', 575) +module_eval(<<'.,.,', 'egrammar.ra', 571) def _reduce_152(val, _values, result) result = val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 581) +module_eval(<<'.,.,', 'egrammar.ra', 577) def _reduce_153(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 582) +module_eval(<<'.,.,', 'egrammar.ra', 578) def _reduce_154(val, _values, result) error val[0], "'class' is not a valid classname" result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 586) +module_eval(<<'.,.,', 'egrammar.ra', 582) def _reduce_155(val, _values, result) result = [] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 587) +module_eval(<<'.,.,', 'egrammar.ra', 583) def _reduce_156(val, _values, result) result = [] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 588) +module_eval(<<'.,.,', 'egrammar.ra', 584) def _reduce_157(val, _values, result) result = val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 592) +module_eval(<<'.,.,', 'egrammar.ra', 588) def _reduce_158(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 593) +module_eval(<<'.,.,', 'egrammar.ra', 589) def _reduce_159(val, _values, result) result = val[0].push(val[2]) result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 597) +module_eval(<<'.,.,', 'egrammar.ra', 593) def _reduce_160(val, _values, result) result = Factory.PARAM(val[0][:value], val[2]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 598) +module_eval(<<'.,.,', 'egrammar.ra', 594) def _reduce_161(val, _values, result) result = Factory.PARAM(val[0][:value]); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 611) +module_eval(<<'.,.,', 'egrammar.ra', 607) def _reduce_162(val, _values, result) result = Factory.fqn(val[0][:value]).var ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 617) +module_eval(<<'.,.,', 'egrammar.ra', 613) def _reduce_163(val, _values, result) result = Factory.LIST(val[1]); loc result, val[0], val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 618) +module_eval(<<'.,.,', 'egrammar.ra', 614) def _reduce_164(val, _values, result) result = Factory.LIST(val[1]); loc result, val[0], val[3] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 619) +module_eval(<<'.,.,', 'egrammar.ra', 615) def _reduce_165(val, _values, result) result = Factory.literal([]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 620) +module_eval(<<'.,.,', 'egrammar.ra', 616) def _reduce_166(val, _values, result) result = Factory.LIST(val[1]); loc result, val[0], val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 621) +module_eval(<<'.,.,', 'egrammar.ra', 617) def _reduce_167(val, _values, result) result = Factory.LIST(val[1]); loc result, val[0], val[3] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 622) +module_eval(<<'.,.,', 'egrammar.ra', 618) def _reduce_168(val, _values, result) result = Factory.literal([]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 625) +module_eval(<<'.,.,', 'egrammar.ra', 621) def _reduce_169(val, _values, result) result = Factory.HASH(val[1]); loc result, val[0], val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 626) +module_eval(<<'.,.,', 'egrammar.ra', 622) def _reduce_170(val, _values, result) result = Factory.HASH(val[1]); loc result, val[0], val[3] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 627) +module_eval(<<'.,.,', 'egrammar.ra', 623) def _reduce_171(val, _values, result) result = Factory.literal({}) ; loc result, val[0], val[3] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 630) +module_eval(<<'.,.,', 'egrammar.ra', 626) def _reduce_172(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 631) +module_eval(<<'.,.,', 'egrammar.ra', 627) def _reduce_173(val, _values, result) result = val[0].push val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 634) +module_eval(<<'.,.,', 'egrammar.ra', 630) def _reduce_174(val, _values, result) result = Factory.KEY_ENTRY(val[0], val[2]); loc result, val[1] result @@ -2209,112 +2209,112 @@ module_eval(<<'.,.,', 'egrammar.ra', 634) # reduce 176 omitted -module_eval(<<'.,.,', 'egrammar.ra', 640) +module_eval(<<'.,.,', 'egrammar.ra', 636) def _reduce_177(val, _values, result) result = Factory.literal(val[0][:value]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 641) +module_eval(<<'.,.,', 'egrammar.ra', 637) def _reduce_178(val, _values, result) result = Factory.string(val[0], *val[1]) ; loc result, val[0], val[1][-1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 642) +module_eval(<<'.,.,', 'egrammar.ra', 638) def _reduce_179(val, _values, result) result = Factory.literal(val[0][:value]); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 643) +module_eval(<<'.,.,', 'egrammar.ra', 639) def _reduce_180(val, _values, result) result = Factory.literal(val[0][:value]); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 644) +module_eval(<<'.,.,', 'egrammar.ra', 640) def _reduce_181(val, _values, result) result = Factory.literal(val[0][:value]); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 645) +module_eval(<<'.,.,', 'egrammar.ra', 641) def _reduce_182(val, _values, result) result = [val[0]] + val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 646) +module_eval(<<'.,.,', 'egrammar.ra', 642) def _reduce_183(val, _values, result) result = Factory.TEXT(val[0]) result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 649) +module_eval(<<'.,.,', 'egrammar.ra', 645) def _reduce_184(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 650) +module_eval(<<'.,.,', 'egrammar.ra', 646) def _reduce_185(val, _values, result) result = [val[0]] + val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 652) +module_eval(<<'.,.,', 'egrammar.ra', 648) def _reduce_186(val, _values, result) result = Factory.NUMBER(val[0][:value]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 653) +module_eval(<<'.,.,', 'egrammar.ra', 649) def _reduce_187(val, _values, result) result = Factory.QNAME_OR_NUMBER(val[0][:value]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 654) +module_eval(<<'.,.,', 'egrammar.ra', 650) def _reduce_188(val, _values, result) result = Factory.QREF(val[0][:value]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 655) +module_eval(<<'.,.,', 'egrammar.ra', 651) def _reduce_189(val, _values, result) result = Factory.literal(:undef); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 656) +module_eval(<<'.,.,', 'egrammar.ra', 652) def _reduce_190(val, _values, result) result = Factory.literal(:default); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 661) +module_eval(<<'.,.,', 'egrammar.ra', 657) def _reduce_191(val, _values, result) result = Factory.literal(val[0][:value]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 664) +module_eval(<<'.,.,', 'egrammar.ra', 660) def _reduce_192(val, _values, result) result = Factory.literal(val[0][:value]); loc result, val[0] result @@ -2323,7 +2323,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 664) # reduce 193 omitted -module_eval(<<'.,.,', 'egrammar.ra', 670) +module_eval(<<'.,.,', 'egrammar.ra', 666) def _reduce_194(val, _values, result) result = nil result @@ -2362,7 +2362,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 670) # reduce 210 omitted -module_eval(<<'.,.,', 'egrammar.ra', 693) +module_eval(<<'.,.,', 'egrammar.ra', 689) def _reduce_211(val, _values, result) result = nil result diff --git a/spec/unit/pops/parser/parse_conditionals_spec.rb b/spec/unit/pops/parser/parse_conditionals_spec.rb index b0cb36b96..b8b8d9c8e 100644 --- a/spec/unit/pops/parser/parse_conditionals_spec.rb +++ b/spec/unit/pops/parser/parse_conditionals_spec.rb @@ -147,13 +147,4 @@ describe "egrammar parsing conditionals" do end end - context "When parsing imports" do - it "import 'foo'" do - dump(parse("import 'foo'")).should == "(import 'foo')" - end - - it "import 'foo', 'bar'" do - dump(parse("import 'foo', 'bar'")).should == "(import 'foo' 'bar')" - end - end end diff --git a/spec/unit/pops/transformer/transform_conditionals_spec.rb b/spec/unit/pops/transformer/transform_conditionals_spec.rb index 6eefa51a3..02b0de4a8 100644 --- a/spec/unit/pops/transformer/transform_conditionals_spec.rb +++ b/spec/unit/pops/transformer/transform_conditionals_spec.rb @@ -120,13 +120,4 @@ describe "transformation to Puppet AST for conditionals" do end end - context "When transforming imports" do - it "import 'foo'" do - astdump(parse("import 'foo'")).should == ":nop" - end - - it "import 'foo', 'bar'" do - astdump(parse("import 'foo', 'bar'")).should == ":nop" - end - end end From 39f54e8d4a0b641c045e63d5e5ce67bd6f41ecf2 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Thu, 12 Dec 2013 10:43:43 -0800 Subject: [PATCH 193/800] (#23373) Have config parsing use IniFile's sections Since the IniFile code now tracks the sections the config parsing can utilize it and simplify its flow some --- lib/puppet/settings/config_file.rb | 71 ++++++++++++++---------------- lib/puppet/settings/ini_file.rb | 67 +++++++++++++++++++++------- 2 files changed, 85 insertions(+), 53 deletions(-) diff --git a/lib/puppet/settings/config_file.rb b/lib/puppet/settings/config_file.rb index 77db99977..d584f31e6 100644 --- a/lib/puppet/settings/config_file.rb +++ b/lib/puppet/settings/config_file.rb @@ -17,46 +17,19 @@ class Puppet::Settings::ConfigFile def parse_file(file, text) result = {} - # Default to 'main' for the section. - section_name = :main - result[section_name] = empty_section - line_number = 1 - Puppet::Settings::IniFile.parse(StringIO.new(text)).each do |line| - case line - when Puppet::Settings::IniFile::SectionLine - section_name = line.name.intern - fail_when_illegal_section_name(section_name, file, line_number) - if result[section_name].nil? - result[section_name] = empty_section - end - when Puppet::Settings::IniFile::SettingLine - var = line.name.intern + ini = Puppet::Settings::IniFile.parse(StringIO.new(text)) + ini.sections.each do |section| + section_name = section.name.intern + fail_when_illegal_section_name(section_name, file, section.line_number) + result[section_name] = empty_section - # We don't want to munge modes, because they're specified in octal, so we'll - # just leave them as a String, since Puppet handles that case correctly. - if var == :mode - value = line.value - else - value = @value_converter[line.value] - end - - # Check to see if this is a file argument and it has extra options - begin - if value.is_a?(String) and options = extract_fileinfo(value) - value = options[:value] - options.delete(:value) - result[section_name][:_meta][var] = options - end - result[section_name][var] = value - rescue Puppet::Error => detail - raise Puppet::Settings::ParseError.new(detail.message, file, line_number, detail) - end - else - if line.text !~ /^\s*#|^\s*$/ - raise Puppet::Settings::ParseError.new("Could not match line #{line.text}", file, line_number) + ini.lines_in(section.name).each do |line| + if line.is_a?(Puppet::Settings::IniFile::SettingLine) + parse_setting(line, result[section_name]) + elsif line.text !~ /^\s*#|^\s*$/ + raise Puppet::Settings::ParseError.new("Could not match line #{line.text}", file, line.line_number) end end - line_number += 1 end result @@ -64,6 +37,30 @@ class Puppet::Settings::ConfigFile private + def parse_setting(setting, result) + var = setting.name.intern + + # We don't want to munge modes, because they're specified in octal, so we'll + # just leave them as a String, since Puppet handles that case correctly. + if var == :mode + value = setting.value + else + value = @value_converter[setting.value] + end + + # Check to see if this is a file argument and it has extra options + begin + if value.is_a?(String) and options = extract_fileinfo(value) + value = options[:value] + options.delete(:value) + result[:_meta][var] = options + end + result[var] = value + rescue Puppet::Error => detail + raise Puppet::Settings::ParseError.new(detail.message, file, setting.line_number, detail) + end + end + def empty_section { :_meta => {} } end diff --git a/lib/puppet/settings/ini_file.rb b/lib/puppet/settings/ini_file.rb index e99dce4ba..11b727217 100644 --- a/lib/puppet/settings/ini_file.rb +++ b/lib/puppet/settings/ini_file.rb @@ -10,40 +10,48 @@ class Puppet::Settings::IniFile end def self.parse(config_fh) - config = new + lines = [DefaultSection.new] config_fh.each_line do |line| case line when /^(\s*)\[(\w+)\](\s*)$/ - config << SectionLine.new($1, $2, $3) + lines << SectionLine.new(lines[-1], $1, $2, $3) when /^(\s*)(\w+)(\s*=\s*)(.*?)(\s*)$/ - config << SettingLine.new($1, $2, $3, $4, $5) + lines << SettingLine.new(lines[-1], $1, $2, $3, $4, $5) else - config << Line.new(line) + lines << Line.new(lines[-1], line) end end - config + new(lines) end - def initialize - @lines = [] + def initialize(lines = []) + @lines = lines end - def <<(line) - @lines << line + def add_section(name) + @lines << SectionLine.new(@lines[-1], "", name, "") + end + + def add_setting(name, value) + @lines << SettingLine.new(@lines[-1], "", name, "=", value, "") end def each(&block) @lines.each(&block) end + def sections + sections = @lines.select { |line| line.is_a?(SectionLine) } + end + def setting(section, name) - settings_in(section).find do |line| + lines_in(section).find do |line| line.is_a?(SettingLine) && line.name == name end end - def settings_in(section) + def lines_in(section) section_lines = [] current_section = DEFAULT_SECTION_NAME @lines.each do |line| @@ -76,19 +84,35 @@ class Puppet::Settings::IniFile if setting setting.value = value else - @config << SectionLine.new("", section, "") - @config << SettingLine.new("", name, "=", value, "") + @config.add_section(section) + @config.add_setting(name, value) end end end - Line = Struct.new(:text) do + module LineNumber + def line_number + line = 0 + previous_line = previous + while previous_line + line += 1 + previous_line = previous_line.previous + end + line + end + end + + Line = Struct.new(:previous, :text) do + include LineNumber + def write(fh) fh.puts(text) end end - SettingLine = Struct.new(:prefix, :name, :infix, :value, :suffix) do + SettingLine = Struct.new(:previous, :prefix, :name, :infix, :value, :suffix) do + include LineNumber + def write(fh) fh.write(prefix) fh.write(name) @@ -98,7 +122,9 @@ class Puppet::Settings::IniFile end end - SectionLine = Struct.new(:prefix, :name, :suffix) do + SectionLine = Struct.new(:previous, :prefix, :name, :suffix) do + include LineNumber + def write(fh) fh.write(prefix) fh.write("[") @@ -107,4 +133,13 @@ class Puppet::Settings::IniFile fh.puts(suffix) end end + + class DefaultSection < SectionLine + def initialize + super(nil, "", DEFAULT_SECTION_NAME, "") + end + + def write(fh) + end + end end From 64f1a1aeee29eb6e67e6fc9cac07ed5194d01d4c Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Wed, 30 Oct 2013 21:31:52 +0100 Subject: [PATCH 194/800] (#7659) Fix commentstack when parsing hashes We need to pop from commentstack in the grammar whenever we parse a hash; an LBRACE pushes to commentstack, yet an AST::ASTHash does not have a doc string which would get the comment popped from the stack again. This left an extra item on the commentstack which caused all subsequent Lexer#getcomment to return "". Make the test for "comments before statement" contain a more complex example to demonstrate that nested comments should not be returned. --- lib/puppet/parser/grammar.ra | 3 +++ lib/puppet/parser/parser.rb | 27 ++++++++++++++------------ spec/integration/parser/parser_spec.rb | 10 +++++++++- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/lib/puppet/parser/grammar.ra b/lib/puppet/parser/grammar.ra index 6c1228d71..c7b83fe6b 100644 --- a/lib/puppet/parser/grammar.ra +++ b/lib/puppet/parser/grammar.ra @@ -738,6 +738,7 @@ regex: REGEX { } hash: LBRACE hashpairs RBRACE { + @lexer.commentpop if val[1].instance_of?(AST::ASTHash) result = val[1] else @@ -745,12 +746,14 @@ hash: LBRACE hashpairs RBRACE { end } | LBRACE hashpairs COMMA RBRACE { + @lexer.commentpop if val[1].instance_of?(AST::ASTHash) result = val[1] else result = ast AST::ASTHash, { :value => val[1] } end } | LBRACE RBRACE { + @lexer.commentpop result = ast AST::ASTHash } diff --git a/lib/puppet/parser/parser.rb b/lib/puppet/parser/parser.rb index cb6790ef1..16872ee34 100644 --- a/lib/puppet/parser/parser.rb +++ b/lib/puppet/parser/parser.rb @@ -20,7 +20,7 @@ module Puppet module Parser class Parser < Racc::Parser -module_eval(<<'...end grammar.ra/module_eval...', 'grammar.ra', 797) +module_eval(<<'...end grammar.ra/module_eval...', 'grammar.ra', 799) # It got too annoying having code in a file that needs to be compiled. require 'puppet/parser/parser_support' @@ -2462,7 +2462,8 @@ module_eval(<<'.,.,', 'grammar.ra', 736) module_eval(<<'.,.,', 'grammar.ra', 740) def _reduce_230(val, _values, result) - if val[1].instance_of?(AST::ASTHash) + @lexer.commentpop + if val[1].instance_of?(AST::ASTHash) result = val[1] else result = ast AST::ASTHash, { :value => val[1] } @@ -2472,9 +2473,10 @@ module_eval(<<'.,.,', 'grammar.ra', 740) end .,., -module_eval(<<'.,.,', 'grammar.ra', 747) +module_eval(<<'.,.,', 'grammar.ra', 748) def _reduce_231(val, _values, result) - if val[1].instance_of?(AST::ASTHash) + @lexer.commentpop + if val[1].instance_of?(AST::ASTHash) result = val[1] else result = ast AST::ASTHash, { :value => val[1] } @@ -2484,9 +2486,10 @@ module_eval(<<'.,.,', 'grammar.ra', 747) end .,., -module_eval(<<'.,.,', 'grammar.ra', 753) +module_eval(<<'.,.,', 'grammar.ra', 755) def _reduce_232(val, _values, result) - result = ast AST::ASTHash + @lexer.commentpop + result = ast AST::ASTHash result end @@ -2494,7 +2497,7 @@ module_eval(<<'.,.,', 'grammar.ra', 753) # reduce 233 omitted -module_eval(<<'.,.,', 'grammar.ra', 758) +module_eval(<<'.,.,', 'grammar.ra', 761) def _reduce_234(val, _values, result) if val[0].instance_of?(AST::ASTHash) result = val[0].merge(val[2]) @@ -2507,7 +2510,7 @@ module_eval(<<'.,.,', 'grammar.ra', 758) end .,., -module_eval(<<'.,.,', 'grammar.ra', 767) +module_eval(<<'.,.,', 'grammar.ra', 770) def _reduce_235(val, _values, result) result = ast AST::ASTHash, { :value => { val[0] => val[2] } } @@ -2515,21 +2518,21 @@ module_eval(<<'.,.,', 'grammar.ra', 767) end .,., -module_eval(<<'.,.,', 'grammar.ra', 770) +module_eval(<<'.,.,', 'grammar.ra', 773) def _reduce_236(val, _values, result) result = val[0][:value] result end .,., -module_eval(<<'.,.,', 'grammar.ra', 771) +module_eval(<<'.,.,', 'grammar.ra', 774) def _reduce_237(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'grammar.ra', 774) +module_eval(<<'.,.,', 'grammar.ra', 777) def _reduce_238(val, _values, result) result = ast AST::HashOrArrayAccess, :variable => val[0][:value], :key => val[2] @@ -2539,7 +2542,7 @@ module_eval(<<'.,.,', 'grammar.ra', 774) # reduce 239 omitted -module_eval(<<'.,.,', 'grammar.ra', 779) +module_eval(<<'.,.,', 'grammar.ra', 782) def _reduce_240(val, _values, result) result = ast AST::HashOrArrayAccess, :variable => val[0], :key => val[2] diff --git a/spec/integration/parser/parser_spec.rb b/spec/integration/parser/parser_spec.rb index 09c90de8e..b9bf999d8 100755 --- a/spec/integration/parser/parser_spec.rb +++ b/spec/integration/parser/parser_spec.rb @@ -87,7 +87,15 @@ describe "Puppet::Parser::Parser" do end ast = @parser.parse(""" # comment - class test {} + class test { + $foo = {bar => 23} + $bar = [23, 42] + $x = 'argument' + # this comment should not be returned + some_function('with', {a => 'hash'}, + ['and', 1, 'array', $argument], + ) # not? + } """) ast.code[0].should be_a(Puppet::Parser::AST::Hostclass) From a83892987ed13722966759aeaa353e8605c4497a Mon Sep 17 00:00:00 2001 From: Felix Frank Date: Thu, 7 Nov 2013 22:14:29 +0100 Subject: [PATCH 195/800] (#7659) add a test for puppetdoc in the presence of hash literals Add a test to confirm the effect of the fix for a puppetdoc bug. --- spec/integration/parser/parser_spec.rb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/spec/integration/parser/parser_spec.rb b/spec/integration/parser/parser_spec.rb index b9bf999d8..4b6ad6baa 100755 --- a/spec/integration/parser/parser_spec.rb +++ b/spec/integration/parser/parser_spec.rb @@ -80,7 +80,7 @@ describe "Puppet::Parser::Parser" do # @parser = Puppet::Parser::Parser.new "development" end shared_examples_for 'a puppet parser' do - describe "when parsing comments before statement" do + describe "when parsing comments before a statement" do it "should associate the documentation to the statement AST node" do if Puppet[:parser] == 'future' pending "egrammar does not yet process comments" @@ -102,6 +102,14 @@ describe "Puppet::Parser::Parser" do ast.code[0].name.should == 'test' ast.code[0].instantiate('')[0].doc.should == "comment\n" end + + it "should recognize docs when hash literals are in use" do + ast = @parser.parse(""" + # comment + class test($param={}) {} + """) + ast.code[0].instantiate('')[0].doc.should == "comment\n" + end end describe "when parsing" do From 90e103fb8db584986b8191b2c4133e4d76ef644b Mon Sep 17 00:00:00 2001 From: Felix Frank Date: Sat, 9 Nov 2013 17:15:07 +0100 Subject: [PATCH 196/800] (#7659) add more tests for possible hash use syntaxes --- spec/integration/parser/parser_spec.rb | 38 ++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/spec/integration/parser/parser_spec.rb b/spec/integration/parser/parser_spec.rb index 4b6ad6baa..a6709fc31 100755 --- a/spec/integration/parser/parser_spec.rb +++ b/spec/integration/parser/parser_spec.rb @@ -103,12 +103,38 @@ describe "Puppet::Parser::Parser" do ast.code[0].instantiate('')[0].doc.should == "comment\n" end - it "should recognize docs when hash literals are in use" do - ast = @parser.parse(""" - # comment - class test($param={}) {} - """) - ast.code[0].instantiate('')[0].doc.should == "comment\n" + { "an empty hash" => "{}", + "a simple hash" => "{ 'key' => 'value' }", + "a nested hash" => "{ 'first' => $x, 'second' => { a => 1, b => 2 } }" + }.each_pair do |hash_desc, hash_expr| + context "in the presence of #{hash_desc}" do + { "a parameter default" => "class test($param = #{hash_expr}) { }", + "a parameter value" => "foo { 'bar': options => #{hash_expr} }", + "an plusignment rvalue" => "Foo['bar'] { options +> #{hash_expr} }", + "an assignment rvalue" => "$x = #{hash_expr}", + "an inequality rvalue" => "if $x != #{hash_expr} { }", + "an function argument in parenthesis" => "flatten(#{hash_expr})", + "a second argument" => "merge($x, #{hash_expr})", + }.each_pair do |dsl_desc, dsl_expr| + context "as #{dsl_desc}" do + it "should associate the docstring to the container" do + ast = @parser.parse("# comment\nclass container { #{dsl_expr} }\n") + ast.code[0].instantiate('')[0].doc.should == "comment\n" + end + end + end + # Pending, these syntaxes are not yet supported in 3.x + # + # @todo Merge these into the test above after the migration to the new + # parser is complete. + { "a selector alternative" => "$opt ? { { 'a' => 1 } => true, default => false }", + "an argument without parenthesis" => "flatten { 'a' => 1 }", + }.each_pair do |dsl_desc, dsl_expr| + context "as #{dsl_desc}" do + it "should associate the docstring to the container" + end + end + end end end From 96caba12ea54db67ebf93dabacd604d99b934338 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Thu, 12 Dec 2013 11:26:51 -0800 Subject: [PATCH 197/800] (maint) Share common acceptance setup There are several (three now with SetParser) setup steps which are the same between git and package acceptance runs. This commit shifts them to a common dir and uses symlinks to reduce duplication. --- .../pre-suite/05_SetParser.rb => common/SetParser.rb} | 0 acceptance/config/el6/setup/common/StopFirewall.rb | 9 +++++++++ acceptance/config/el6/setup/common/ValidateSignCert.rb | 6 ++++++ .../config/el6/setup/git/pre-suite/01_TestSetup.rb | 0 .../config/el6/setup/git/pre-suite/035_StopFirewall.rb | 10 +--------- .../el6/setup/git/pre-suite/03_PuppetMasterSanity.rb | 0 .../el6/setup/git/pre-suite/04_ValidateSignCert.rb | 7 +------ .../config/el6/setup/git/pre-suite/10_SetParser.rb | 1 + .../el6/setup/packages/pre-suite/02_StopFirewall.rb | 10 +--------- .../setup/packages/pre-suite/04_ValidateSignCert.rb | 7 +------ .../el6/setup/packages/pre-suite/10_SetParser.rb | 1 + 11 files changed, 21 insertions(+), 30 deletions(-) rename acceptance/config/el6/setup/{packages/pre-suite/05_SetParser.rb => common/SetParser.rb} (100%) create mode 100644 acceptance/config/el6/setup/common/StopFirewall.rb create mode 100644 acceptance/config/el6/setup/common/ValidateSignCert.rb mode change 100755 => 100644 acceptance/config/el6/setup/git/pre-suite/01_TestSetup.rb mode change 100644 => 120000 acceptance/config/el6/setup/git/pre-suite/035_StopFirewall.rb mode change 100755 => 100644 acceptance/config/el6/setup/git/pre-suite/03_PuppetMasterSanity.rb mode change 100755 => 120000 acceptance/config/el6/setup/git/pre-suite/04_ValidateSignCert.rb create mode 120000 acceptance/config/el6/setup/git/pre-suite/10_SetParser.rb mode change 100644 => 120000 acceptance/config/el6/setup/packages/pre-suite/02_StopFirewall.rb mode change 100755 => 120000 acceptance/config/el6/setup/packages/pre-suite/04_ValidateSignCert.rb create mode 120000 acceptance/config/el6/setup/packages/pre-suite/10_SetParser.rb diff --git a/acceptance/config/el6/setup/packages/pre-suite/05_SetParser.rb b/acceptance/config/el6/setup/common/SetParser.rb similarity index 100% rename from acceptance/config/el6/setup/packages/pre-suite/05_SetParser.rb rename to acceptance/config/el6/setup/common/SetParser.rb diff --git a/acceptance/config/el6/setup/common/StopFirewall.rb b/acceptance/config/el6/setup/common/StopFirewall.rb new file mode 100644 index 000000000..0d651ba01 --- /dev/null +++ b/acceptance/config/el6/setup/common/StopFirewall.rb @@ -0,0 +1,9 @@ +require 'puppet/acceptance/install_utils' + +extend Puppet::Acceptance::InstallUtils + +test_name "Stop firewall" do + hosts.each do |host| + stop_firewall_on(host) + end +end diff --git a/acceptance/config/el6/setup/common/ValidateSignCert.rb b/acceptance/config/el6/setup/common/ValidateSignCert.rb new file mode 100644 index 000000000..549432427 --- /dev/null +++ b/acceptance/config/el6/setup/common/ValidateSignCert.rb @@ -0,0 +1,6 @@ +test_name "Validate Sign Cert" + +require 'puppet/acceptance/common_utils' +extend Puppet::Acceptance::CAUtils + +initialize_ssl diff --git a/acceptance/config/el6/setup/git/pre-suite/01_TestSetup.rb b/acceptance/config/el6/setup/git/pre-suite/01_TestSetup.rb old mode 100755 new mode 100644 diff --git a/acceptance/config/el6/setup/git/pre-suite/035_StopFirewall.rb b/acceptance/config/el6/setup/git/pre-suite/035_StopFirewall.rb deleted file mode 100644 index 0d651ba01..000000000 --- a/acceptance/config/el6/setup/git/pre-suite/035_StopFirewall.rb +++ /dev/null @@ -1,9 +0,0 @@ -require 'puppet/acceptance/install_utils' - -extend Puppet::Acceptance::InstallUtils - -test_name "Stop firewall" do - hosts.each do |host| - stop_firewall_on(host) - end -end diff --git a/acceptance/config/el6/setup/git/pre-suite/035_StopFirewall.rb b/acceptance/config/el6/setup/git/pre-suite/035_StopFirewall.rb new file mode 120000 index 000000000..8137a325a --- /dev/null +++ b/acceptance/config/el6/setup/git/pre-suite/035_StopFirewall.rb @@ -0,0 +1 @@ +../../common/StopFirewall.rb \ No newline at end of file diff --git a/acceptance/config/el6/setup/git/pre-suite/03_PuppetMasterSanity.rb b/acceptance/config/el6/setup/git/pre-suite/03_PuppetMasterSanity.rb old mode 100755 new mode 100644 diff --git a/acceptance/config/el6/setup/git/pre-suite/04_ValidateSignCert.rb b/acceptance/config/el6/setup/git/pre-suite/04_ValidateSignCert.rb deleted file mode 100755 index 549432427..000000000 --- a/acceptance/config/el6/setup/git/pre-suite/04_ValidateSignCert.rb +++ /dev/null @@ -1,6 +0,0 @@ -test_name "Validate Sign Cert" - -require 'puppet/acceptance/common_utils' -extend Puppet::Acceptance::CAUtils - -initialize_ssl diff --git a/acceptance/config/el6/setup/git/pre-suite/04_ValidateSignCert.rb b/acceptance/config/el6/setup/git/pre-suite/04_ValidateSignCert.rb new file mode 120000 index 000000000..5f5a2f09e --- /dev/null +++ b/acceptance/config/el6/setup/git/pre-suite/04_ValidateSignCert.rb @@ -0,0 +1 @@ +../../common/ValidateSignCert.rb \ No newline at end of file diff --git a/acceptance/config/el6/setup/git/pre-suite/10_SetParser.rb b/acceptance/config/el6/setup/git/pre-suite/10_SetParser.rb new file mode 120000 index 000000000..f3048323f --- /dev/null +++ b/acceptance/config/el6/setup/git/pre-suite/10_SetParser.rb @@ -0,0 +1 @@ +../../common/SetParser.rb \ No newline at end of file diff --git a/acceptance/config/el6/setup/packages/pre-suite/02_StopFirewall.rb b/acceptance/config/el6/setup/packages/pre-suite/02_StopFirewall.rb deleted file mode 100644 index 0d651ba01..000000000 --- a/acceptance/config/el6/setup/packages/pre-suite/02_StopFirewall.rb +++ /dev/null @@ -1,9 +0,0 @@ -require 'puppet/acceptance/install_utils' - -extend Puppet::Acceptance::InstallUtils - -test_name "Stop firewall" do - hosts.each do |host| - stop_firewall_on(host) - end -end diff --git a/acceptance/config/el6/setup/packages/pre-suite/02_StopFirewall.rb b/acceptance/config/el6/setup/packages/pre-suite/02_StopFirewall.rb new file mode 120000 index 000000000..8137a325a --- /dev/null +++ b/acceptance/config/el6/setup/packages/pre-suite/02_StopFirewall.rb @@ -0,0 +1 @@ +../../common/StopFirewall.rb \ No newline at end of file diff --git a/acceptance/config/el6/setup/packages/pre-suite/04_ValidateSignCert.rb b/acceptance/config/el6/setup/packages/pre-suite/04_ValidateSignCert.rb deleted file mode 100755 index 549432427..000000000 --- a/acceptance/config/el6/setup/packages/pre-suite/04_ValidateSignCert.rb +++ /dev/null @@ -1,6 +0,0 @@ -test_name "Validate Sign Cert" - -require 'puppet/acceptance/common_utils' -extend Puppet::Acceptance::CAUtils - -initialize_ssl diff --git a/acceptance/config/el6/setup/packages/pre-suite/04_ValidateSignCert.rb b/acceptance/config/el6/setup/packages/pre-suite/04_ValidateSignCert.rb new file mode 120000 index 000000000..5f5a2f09e --- /dev/null +++ b/acceptance/config/el6/setup/packages/pre-suite/04_ValidateSignCert.rb @@ -0,0 +1 @@ +../../common/ValidateSignCert.rb \ No newline at end of file diff --git a/acceptance/config/el6/setup/packages/pre-suite/10_SetParser.rb b/acceptance/config/el6/setup/packages/pre-suite/10_SetParser.rb new file mode 120000 index 000000000..f3048323f --- /dev/null +++ b/acceptance/config/el6/setup/packages/pre-suite/10_SetParser.rb @@ -0,0 +1 @@ +../../common/SetParser.rb \ No newline at end of file From 3ffabc7db6ed82db158357384f3d3602838d3ff9 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Thu, 12 Dec 2013 13:51:58 -0800 Subject: [PATCH 198/800] (#23373) Provide a more structure config file parse The previous parsing of the config file was a hierarchy of hashes, which made it hard to discover what the parts were. This changes it to use a more structured system and lays the groundwork for moving lookup behavior elsewhere. --- lib/puppet/settings.rb | 30 ++++-------- lib/puppet/settings/base_setting.rb | 3 ++ lib/puppet/settings/config_file.rb | 44 ++++++++++++++---- lib/puppet/settings/file_setting.rb | 6 +++ spec/unit/settings/config_file_spec.rb | 64 ++++++++++++++++++++------ 5 files changed, 103 insertions(+), 44 deletions(-) diff --git a/lib/puppet/settings.rb b/lib/puppet/settings.rb index e688202de..22fea9a89 100644 --- a/lib/puppet/settings.rb +++ b/lib/puppet/settings.rb @@ -548,21 +548,10 @@ class Puppet::Settings # Unsafely parse the file -- this isn't thread-safe and causes plenty of problems if used directly. def unsafe_parse(file) - # build up a single data structure that contains the values from all of the parsed files. - data = {} + data = nil if Puppet::FileSystem::File.exist?(file) begin - file_data = parse_file(file) - - # This is a little kludgy; basically we are merging a hash of hashes. We can't use "merge" at the - # outermost level or we risking losing data from the hash we're merging into. - file_data.keys.each do |key| - if data.has_key?(key) - data[key].merge!(file_data[key]) - else - data[key] = file_data[key] - end - end + data = parse_file(file) rescue => detail Puppet.log_exception(detail, "Could not parse #{file}: #{detail}") return @@ -570,17 +559,19 @@ class Puppet::Settings end # If we get here and don't have any data, we just return and don't muck with the current state of the world. - return if data.empty? + return if data.nil? # If we get here then we have some data, so we need to clear out any previous settings that may have come from # config files. unsafe_clear(false, false) # And now we can repopulate with the values from our last parsing of the config files. - data.each do |area, values| - @metas[area] = values.delete(:_meta) - values.each do |key,value| - set_value(key, value, area, :dont_trigger_handles => true, :ignore_bad_settings => true ) + data.sections.each do |name, section| + section.settings.each do |setting| + set_value(setting.name, setting.value, name, :dont_trigger_handles => true, :ignore_bad_settings => true ) + if type = @config[setting.name] + type.set_meta(setting.meta) + end end end @@ -609,9 +600,6 @@ class Puppet::Settings end end end - - # Take a best guess at metadata based on uninitialized run_mode - apply_metadata end private :unsafe_parse diff --git a/lib/puppet/settings/base_setting.rb b/lib/puppet/settings/base_setting.rb index ac38b35f3..3be95b0db 100644 --- a/lib/puppet/settings/base_setting.rb +++ b/lib/puppet/settings/base_setting.rb @@ -159,4 +159,7 @@ class Puppet::Settings::BaseSetting def munge(value) value end + + def set_meta(meta) + end end diff --git a/lib/puppet/settings/config_file.rb b/lib/puppet/settings/config_file.rb index d584f31e6..aef3faecb 100644 --- a/lib/puppet/settings/config_file.rb +++ b/lib/puppet/settings/config_file.rb @@ -15,17 +15,18 @@ class Puppet::Settings::ConfigFile end def parse_file(file, text) - result = {} + result = Conf.new ini = Puppet::Settings::IniFile.parse(StringIO.new(text)) ini.sections.each do |section| section_name = section.name.intern fail_when_illegal_section_name(section_name, file, section.line_number) - result[section_name] = empty_section + section_config = Section.new(section_name) + result.with_section(section_config) ini.lines_in(section.name).each do |line| if line.is_a?(Puppet::Settings::IniFile::SettingLine) - parse_setting(line, result[section_name]) + parse_setting(line, section_config) elsif line.text !~ /^\s*#|^\s*$/ raise Puppet::Settings::ParseError.new("Could not match line #{line.text}", file, line.line_number) end @@ -35,9 +36,35 @@ class Puppet::Settings::ConfigFile result end + Conf = Struct.new(:sections) do + def initialize + super({}) + end + + def with_section(section) + sections[section.name] = section + self + end + end + + Section = Struct.new(:name, :settings) do + def initialize(name) + super(name, []) + end + + def with_setting(name, value, meta) + settings << Setting.new(name, value, meta) + self + end + end + + Setting = Struct.new(:name, :value, :meta) + Meta = Struct.new(:owner, :group, :mode) + NO_META = Meta.new(nil, nil, nil) + private - def parse_setting(setting, result) + def parse_setting(setting, section) var = setting.name.intern # We don't want to munge modes, because they're specified in octal, so we'll @@ -51,11 +78,12 @@ private # Check to see if this is a file argument and it has extra options begin if value.is_a?(String) and options = extract_fileinfo(value) - value = options[:value] - options.delete(:value) - result[:_meta][var] = options + section.with_setting(var, options[:value], Meta.new(options[:owner], + options[:group], + options[:mode])) + else + section.with_setting(var, value, NO_META) end - result[var] = value rescue Puppet::Error => detail raise Puppet::Settings::ParseError.new(detail.message, file, setting.line_number, detail) end diff --git a/lib/puppet/settings/file_setting.rb b/lib/puppet/settings/file_setting.rb index 243805edb..7ebb03f48 100644 --- a/lib/puppet/settings/file_setting.rb +++ b/lib/puppet/settings/file_setting.rb @@ -106,6 +106,12 @@ class Puppet::Settings::FileSetting < Puppet::Settings::StringSetting @owner.value end + def set_meta(meta) + self.owner = meta.owner if meta.owner + self.group = meta.group if meta.group + self.mode = meta.mode if meta.mode + end + def munge(value) if value.is_a?(String) and value != ':memory:' # for sqlite3 in-memory tests value = File.expand_path(value) diff --git a/spec/unit/settings/config_file_spec.rb b/spec/unit/settings/config_file_spec.rb index 2883e38e5..bfd56c9a0 100644 --- a/spec/unit/settings/config_file_spec.rb +++ b/spec/unit/settings/config_file_spec.rb @@ -20,8 +20,15 @@ describe Puppet::Settings::ConfigFile do let(:filename) { "a/fake/filename.conf" } + Conf = Puppet::Settings::ConfigFile::Conf + Section = Puppet::Settings::ConfigFile::Section + Meta = Puppet::Settings::ConfigFile::Meta + NO_META = Puppet::Settings::ConfigFile::NO_META + it "interprets an empty file to contain a main section with no entries" do - the_parse_of("").should == { :main => section_containing(NOTHING) } + result = the_parse_of("") + + expect(result).to eq(Conf.new.with_section(Section.new(:main))) end it "interprets an empty main section the same as an empty file" do @@ -29,28 +36,46 @@ describe Puppet::Settings::ConfigFile do end it "places an entry in no section in main" do - the_parse_of("var = value").should == { :main => section_containing(:var => "value") } + result = the_parse_of("var = value") + + expect(result).to eq(Conf.new.with_section(Section.new(:main).with_setting(:var, "value", NO_META))) end it "places an entry after a section header in that section" do - the_parse_of("[section]", "var = value").should == { :main => section_containing(NOTHING), - :section => section_containing(:var => "value") } + result = the_parse_of("[section]", "var = value") + + expect(result).to eq(Conf.new. + with_section(Section.new(:main)). + with_section(Section.new(:section). + with_setting(:var, "value", NO_META))) end it "does not include trailing whitespace in the value" do - the_parse_of("var = value\t ").should == { :main => section_containing(:var => "value") } + result = the_parse_of("var = value\t ") + + expect(result).to eq(Conf.new. + with_section(Section.new(:main). + with_setting(:var, "value", NO_META))) end it "does not include leading whitespace in the name" do - the_parse_of(" \t var=value").should == { :main => section_containing(:var => "value") } + result = the_parse_of(" \t var=value") + + expect(result).to eq(Conf.new. + with_section(Section.new(:main). + with_setting(:var, "value", NO_META))) end it "skips lines that are commented out" do - the_parse_of("#var = value").should == { :main => section_containing(NOTHING) } + result = the_parse_of("#var = value") + + expect(result).to eq(Conf.new.with_section(Section.new(:main))) end it "skips lines that are entirely whitespace" do - the_parse_of(" \t ").should == { :main => section_containing(NOTHING) } + result = the_parse_of(" \t ") + + expect(result).to eq(Conf.new.with_section(Section.new(:main))) end it "errors when a line is not a known form" do @@ -66,15 +91,16 @@ badline expect { the_parse_of(multi_line_config) }.to( raise_error(Puppet::Settings::ParseError, /Could not match line/) do |exception| expect(exception.line).to eq(3) - end + end ) end it "stores file meta information in the _meta section" do - the_parse_of("var = value { owner = me, group = you, mode = 0666 }").should == - { :main => section_containing(:var => "value", :meta => { :var => { :owner => "me", - :group => "you", - :mode => "0666" } }) } + result = the_parse_of("var = value { owner = me, group = you, mode = 0666 }") + + expect(result).to eq(Conf.new.with_section(Section.new(:main). + with_setting(:var, "value", + Meta.new("me", "you", "0666")))) end it "errors when there is unknown meta information" do @@ -107,13 +133,21 @@ badline it "transforms values with the given function" do config = Puppet::Settings::ConfigFile.new(Proc.new { |value| value + " changed" }) - config.parse_file(filename, "var = value").should == { :main => section_containing(:var => "value changed") } + result = config.parse_file(filename, "var = value") + + expect(result).to eq(Conf.new. + with_section(Section.new(:main). + with_setting(:var, "value changed", NO_META))) end it "does not try to transform an entry named 'mode'" do config = Puppet::Settings::ConfigFile.new(Proc.new { raise "Should not transform" }) - config.parse_file(filename, "mode = value").should == { :main => section_containing(:mode => "value") } + result = config.parse_file(filename, "mode = value") + + expect(result).to eq(Conf.new. + with_section(Section.new(:main). + with_setting(:mode, "value", NO_META))) end end From f09ea598d7b30d1d71ded50aba8831c89bf739c6 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 12 Dec 2013 23:14:58 +0100 Subject: [PATCH 199/800] (PUP-490) Remove partial support for import (the sequel) This also removes the parts that were first marked as deprecated as they are actually unused. --- lib/puppet/pops/model/factory.rb | 12 ------------ lib/puppet/pops/model/model.rb | 6 ------ lib/puppet/pops/model/model_label_provider.rb | 1 - lib/puppet/pops/model/model_tree_dumper.rb | 5 ----- 4 files changed, 24 deletions(-) diff --git a/lib/puppet/pops/model/factory.rb b/lib/puppet/pops/model/factory.rb index 055374fc9..b671a6dfb 100644 --- a/lib/puppet/pops/model/factory.rb +++ b/lib/puppet/pops/model/factory.rb @@ -180,13 +180,6 @@ class Puppet::Pops::Model::Factory o end - # @deprecated, to be removed in Puppet 4 - def build_ImportExpression(o, files) - # The argument files has already been built - files.each {|f| o.addFiles(to_ops(f)) } - o - end - def build_IfExpression(o, t, ift, els) o.test = build(t) o.then_expr = build(ift) @@ -640,11 +633,6 @@ class Puppet::Pops::Model::Factory # new(Model::CollectExpression, Puppet::Pops::Model::Factory.fqr(type_expr), query_expr, attribute_operations) end - # @deprecated, to be removed in Puppet 4 - def self.IMPORT(files) - new(Model::ImportExpression, files) - end - def self.NAMED_ACCESS(type_name, bodies) new(Model::NamedAccessExpression, type_name, bodies) end diff --git a/lib/puppet/pops/model/model.rb b/lib/puppet/pops/model/model.rb index dea500358..5f7509fd8 100644 --- a/lib/puppet/pops/model/model.rb +++ b/lib/puppet/pops/model/model.rb @@ -71,12 +71,6 @@ module Puppet::Pops::Model # class ParenthesizedExpression < UnaryExpression; end - # An import of one or several files. - # @deprecated, to be removed in Puppet 4 - class ImportExpression < Expression - contains_many_uni 'files', Expression, :lowerBound => 1 - end - # A boolean not expression, reversing the truth of the unary expr. # class NotExpression < UnaryExpression; end diff --git a/lib/puppet/pops/model/model_label_provider.rb b/lib/puppet/pops/model/model_label_provider.rb index 6a6782fe3..9a67b5362 100644 --- a/lib/puppet/pops/model/model_label_provider.rb +++ b/lib/puppet/pops/model/model_label_provider.rb @@ -33,7 +33,6 @@ class Puppet::Pops::Model::ModelLabelProvider < Puppet::Pops::LabelProvider def label_AndExpression o ; "'and' expression" end def label_OrExpression o ; "'or' expression" end def label_InExpression o ; "'in' expression" end - def label_ImportExpression o ; "'import' expression" end # @deprecated, to be removed in Puppet 4 def label_AssignmentExpression o ; "'#{o.operator}' expression" end def label_AttributeOperation o ; "'#{o.operator}' expression" end def label_LiteralList o ; "Array Expression" end diff --git a/lib/puppet/pops/model/model_tree_dumper.rb b/lib/puppet/pops/model/model_tree_dumper.rb index 15f1a6e5c..eca11ced8 100644 --- a/lib/puppet/pops/model/model_tree_dumper.rb +++ b/lib/puppet/pops/model/model_tree_dumper.rb @@ -90,11 +90,6 @@ class Puppet::Pops::Model::ModelTreeDumper < Puppet::Pops::Model::TreeDumper ["in", do_dump(o.left_expr), do_dump(o.right_expr)] end - # @deprecated, to be removed in Puppet 4 - def dump_ImportExpression o - ["import"] + o.files.collect {|f| do_dump(f) } - end - def dump_AssignmentExpression o [o.operator.to_s, do_dump(o.left_expr), do_dump(o.right_expr)] end From dbdbde2d20f208f2796618eaaf119730b728bb53 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 13 Dec 2013 00:14:32 +0100 Subject: [PATCH 200/800] (PUP-866) Deprecate Import This deprecates the use of import. A deprecation warning is generated with a URL to a link that in turn points to the original Redmine issue. A test is added. Note this does not deprecate the underlying "import" mechanism as it is used for all types of loading of manifests. The deprecation is for the puppet language construct. --- lib/puppet/parser/parser_support.rb | 10 ++++++++++ spec/unit/parser/parser_spec.rb | 8 ++++++++ 2 files changed, 18 insertions(+) diff --git a/lib/puppet/parser/parser_support.rb b/lib/puppet/parser/parser_support.rb index b67f3c752..00f35b9f2 100644 --- a/lib/puppet/parser/parser_support.rb +++ b/lib/puppet/parser/parser_support.rb @@ -98,6 +98,16 @@ class Puppet::Parser::Parser def_delegators :known_resource_types, :watch_file, :version def import(file) + deprecation_location_text = + if @lexer.file && @lexer.line + " at #{@lexer.file}:#{@lexer.line}" + elsif @lexer.file + " in file #{@lexer.file}" + elsif @lexer.line + " at #{@lexer.line}" + end + + Puppet.deprecation_warning("The use of 'import' is deprecated#{deprecation_location_text}. See http://links.puppetlabs.com/puppet-import-deprecation") if @lexer.file # use a path relative to the file doing the importing dir = File.dirname(@lexer.file) diff --git a/spec/unit/parser/parser_spec.rb b/spec/unit/parser/parser_spec.rb index 2c4cf50df..c87881f72 100755 --- a/spec/unit/parser/parser_spec.rb +++ b/spec/unit/parser/parser_spec.rb @@ -528,4 +528,12 @@ describe Puppet::Parser do @parser.known_resource_types.hostclass('funtest'). should == @parser.find_hostclass("", "fUntEst") end + + context "deprecations" do + it "should flag use of import as deprecated" do + Puppet.expects(:deprecation_warning).once + @parser.known_resource_types.loader.expects(:import).with('foo', Dir.pwd) + @parser.parse("import 'foo'") + end + end end From 2b5d183b20dc4be46de0ba179e664ee2587b3c66 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Thu, 12 Dec 2013 13:39:17 -0800 Subject: [PATCH 201/800] (maint) Ensure rgen gem installed for future parser tests Acceptance hosts need rgen gem installed if we are testing with future parser. Also json for binder, though this should be deprecated soon. --- acceptance/config/el6/setup/common/SetParser.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/acceptance/config/el6/setup/common/SetParser.rb b/acceptance/config/el6/setup/common/SetParser.rb index 99d0d67bd..a373911a9 100644 --- a/acceptance/config/el6/setup/common/SetParser.rb +++ b/acceptance/config/el6/setup/common/SetParser.rb @@ -12,6 +12,13 @@ test_name "add parser=#{ENV['PARSER']} to all puppet.conf (only if $PARSER is se } } lay_down_new_puppet_conf(host, opts, temp) + + if !options[:install].empty? and parser == 'future' + # We are installing from source rather than packages and need the following: + on(host, "gem install rgen") + # deprecated - remove when binder is removed from pops + on(host, "gem install json") + end end end end From 1285d403244414d99883a4c37c53fd530166aabc Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Thu, 12 Dec 2013 17:30:47 -0800 Subject: [PATCH 202/800] (#23373) Pull apart settings into a few more classes The monolithic settings infrastructure is really hard to work with. In order to implement the functionality for puppet config print to reliably be able to print the value of any given section of the configuration I need to be able to split it apart from the very static structure that exists inside puppet. This takes a step in that direction by starting the process of extracting the lookup logic into smaller classes. --- lib/puppet/settings.rb | 325 +++++++++++++++++----------- lib/puppet/settings/base_setting.rb | 8 +- lib/puppet/settings/config_file.rb | 4 + spec/unit/module_tool_spec.rb | 7 +- spec/unit/node/environment_spec.rb | 6 +- spec/unit/settings_spec.rb | 4 +- spec/unit/util/autoload_spec.rb | 12 +- 7 files changed, 224 insertions(+), 142 deletions(-) diff --git a/lib/puppet/settings.rb b/lib/puppet/settings.rb index 22fea9a89..6bfd3dcbc 100644 --- a/lib/puppet/settings.rb +++ b/lib/puppet/settings.rb @@ -73,13 +73,14 @@ class Puppet::Settings @shortnames = {} @created = [] - @searchpath = nil # Keep track of set values. - @values = Hash.new { |hash, key| hash[key] = {} } - - # Hold parsed metadata until run_mode is known - @metas = {} + @value_sets = { + :cli => Values.new(:cli, @config), + :memory => Values.new(:memory, @config), + :application_defaults => Values.new(:application_defaults, @config), + } + @configuration_file = nil # And keep a per-environment cache @cache = Hash.new { |hash, key| hash[key] = {} } @@ -106,7 +107,8 @@ class Puppet::Settings # Set a config value. This doesn't set the defaults, it sets the value itself. def []=(param, value) - set_value(param, value, :memory) + @value_sets[:memory].set(param, value) + unsafe_flush_cache end # Generate the list of valid arguments, in a format that GetoptLong can @@ -144,18 +146,21 @@ class Puppet::Settings # Remove all set values, potentially skipping cli values. def unsafe_clear(clear_cli = true, clear_application_defaults = false) - @values.each do |name, values| - next if ((name == :application_defaults) and !clear_application_defaults) - next if ((name == :cli) and !clear_cli) - @values.delete(name) + if clear_application_defaults + @value_sets[:application_defaults] = Values.new(:application_defaults, @config) + @app_defaults_initialized = false end - # Only clear the 'used' values if we were explicitly asked to clear out - # :cli values; otherwise, it may be just a config file reparse, - # and we want to retain this cli values. - @used = [] if clear_cli + if clear_cli + @value_sets[:cli] = Values.new(:cli, @config) - @app_defaults_initialized = false if clear_application_defaults + # Only clear the 'used' values if we were explicitly asked to clear out + # :cli values; otherwise, it may be just a config file reparse, + # and we want to retain this cli values. + @used = [] + end + + @value_sets[:memory] = Values.new(:memory, @config) @cache.clear end @@ -277,7 +282,8 @@ class Puppet::Settings if key == :run_mode self.preferred_run_mode = value else - set_value(key, value, :application_defaults) + @value_sets[:application_defaults].set(key, value) + unsafe_flush_cache end end apply_metadata @@ -379,7 +385,8 @@ class Puppet::Settings end end - set_value(str, value, :cli) + @value_sets[:cli].set(str, value) + unsafe_flush_cache end def include?(name) @@ -505,12 +512,76 @@ class Puppet::Settings end end - # Parse the configuration file. Just provides thread safety. - def parse_config_files - unsafe_parse(which_configuration_file) + def parse_config(text, file = "text") + begin + data = @config_file_parser.parse_file(file, text) + rescue => detail + Puppet.log_exception(detail, "Could not parse #{file}: #{detail}") + return + end + + # If we get here and don't have any data, we just return and don't muck with the current state of the world. + return if data.nil? + + # If we get here then we have some data, so we need to clear out any previous settings that may have come from + # config files. + unsafe_clear(false, false) + + # And now we can repopulate with the values from our last parsing of the config files. + @configuration_file = data + data.sections.each do |name, section| + section.settings.each do |setting| + if type = @config[setting.name] + type.set_meta(setting.meta) + end + end + end + + # Determine our environment, if we have one. + if @config[:environment] + env = self.value(:environment).to_sym + else + env = "none" + end + + # Call any hooks we should be calling. + @config.values.each do |setting| + value_sets_for(env).each do |source| + if source.include?(setting.name) + # We still have to use value to retrieve the value, since + # we want the fully interpolated value, not $vardir/lib or whatever. + # This results in extra work, but so few of the settings + # will have associated hooks that it ends up being less work this + # way overall. + if setting.call_hook_on_initialize? + @hooks_to_call_on_application_initialization << setting + else + setting.handle(self.value(setting.name, env)) + end + break + end + end + end call_hooks_deferred_to_application_initialization :ignore_interpolation_dependency_errors => true end + + # Parse the configuration file. Just provides thread safety. + def parse_config_files + file = which_configuration_file + if Puppet::FileSystem::File.exist?(file) + begin + text = read_file(file) + rescue => detail + Puppet.log_exception(detail, "Could not load #{file}: #{detail}") + return + end + else + return + end + + parse_config(text, file) + end private :parse_config_files def main_config_file @@ -546,72 +617,18 @@ class Puppet::Settings end private :config_file_name - # Unsafely parse the file -- this isn't thread-safe and causes plenty of problems if used directly. - def unsafe_parse(file) - data = nil - if Puppet::FileSystem::File.exist?(file) - begin - data = parse_file(file) - rescue => detail - Puppet.log_exception(detail, "Could not parse #{file}: #{detail}") - return - end - end - - # If we get here and don't have any data, we just return and don't muck with the current state of the world. - return if data.nil? - - # If we get here then we have some data, so we need to clear out any previous settings that may have come from - # config files. - unsafe_clear(false, false) - - # And now we can repopulate with the values from our last parsing of the config files. - data.sections.each do |name, section| - section.settings.each do |setting| - set_value(setting.name, setting.value, name, :dont_trigger_handles => true, :ignore_bad_settings => true ) - if type = @config[setting.name] - type.set_meta(setting.meta) - end - end - end - - # Determine our environment, if we have one. - if @config[:environment] - env = self.value(:environment).to_sym - else - env = "none" - end - - # Call any hooks we should be calling. - settings_with_hooks.each do |setting| - each_source(env) do |source| - if @values[source][setting.name] - # We still have to use value to retrieve the value, since - # we want the fully interpolated value, not $vardir/lib or whatever. - # This results in extra work, but so few of the settings - # will have associated hooks that it ends up being less work this - # way overall. - if setting.call_hook_on_initialize? - @hooks_to_call_on_application_initialization << setting - else - setting.handle(self.value(setting.name, env)) - end - break - end - end - end - end - private :unsafe_parse - def apply_metadata # We have to do it in the reverse of the search path, # because multiple sections could set the same value # and I'm too lazy to only set the metadata once. - searchpath.reverse.each do |source| - source = preferred_run_mode if source == :run_mode - source = @name if (@name && source == :name) - if meta = @metas[source] - set_metadata(meta) + if @configuration_file + searchpath.reverse.each do |source| + source = preferred_run_mode if source == :run_mode + if section = @configuration_file.sections[source] + section.settings.each do |setting| + @config[setting.name].set_meta(setting.meta) + end + end end end end @@ -761,32 +778,16 @@ class Puppet::Settings # `dns_alt_names` option during cert generate. --daniel 2011-10-18 def set_by_cli?(param) param = param.to_sym - !@values[:cli][param].nil? + !@value_sets[:cli].lookup(param).nil? end def set_value(param, value, type, options = {}) - param = param.to_sym - - if !(setting = @config[param]) - if options[:ignore_bad_settings] - return - else - raise ArgumentError, - "Attempt to assign a value to unknown configuration parameter #{param.inspect}" - end + Puppet.deprecation_warning("Puppet.settings.set_value is deprecated. Use Puppet[] instead.") + if @value_sets[type] + @value_sets[type].set(param, value) end - - setting.handle(value) if setting.has_hook? and not options[:dont_trigger_handles] - - @values[type][param] = value - unsafe_flush_cache - - value end - - - # Deprecated; use #define_settings instead def setdefaults(section, defs) Puppet.deprecation_warning("'setdefaults' is deprecated and will be removed; please call 'define_settings' instead") @@ -958,12 +959,7 @@ Generated on #{Time.now}. end def find_value(environment, param) - each_source(environment) do |source| - # Look for the value. We have to test the hash for whether - # it exists, because the value might be false. - return @values[source][param] if @values[source].include?(param) - end - return nil + ChainedValues.new(value_sets_for(environment) + [ValuesFromDefaults.new(@config)]).lookup(param) end private :find_value @@ -1074,19 +1070,27 @@ Generated on #{Time.now}. end # Yield each search source in turn. - def each_source(environment) - searchpath(environment).each do |source| - - # Modify the source as necessary. - source = self.preferred_run_mode if source == :run_mode - yield source - end - end - - # Return all settings that have associated hooks; this is so - # we can call them after parsing the configuration file. - def settings_with_hooks - @config.values.find_all { |setting| setting.has_hook? } + def value_sets_for(environment) + searchpath(environment).collect do |name| + case name + when :cli, :memory, :application_defaults + @value_sets[name] + when :run_mode + if @configuration_file + section = @configuration_file.sections[self.preferred_run_mode] + if section + ValuesFromSection.new(self.preferred_run_mode, section) + end + end + else + if @configuration_file + section = @configuration_file.sections[name] + if section + ValuesFromSection.new(name, section) + end + end + end + end.compact end # This method just turns a file in to a hash of hashes. @@ -1105,15 +1109,6 @@ Generated on #{Time.now}. end end - # Set file metadata. - def set_metadata(meta) - meta.each do |var, values| - values.each do |param, value| - @config[var].send(param.to_s + "=", value) - end - end - end - # Private method for internal test use only; allows to do a comprehensive clear of all settings between tests. # # @return nil @@ -1148,4 +1143,74 @@ Generated on #{Time.now}. end private :explicit_config_file? + class ChainedValues + def initialize(value_sets) + @value_sets = value_sets + end + + def lookup(name) + @value_sets.each do |set| + if set.include?(name) + return set.lookup(name) + end + end + nil + end + end + + class ValuesFromDefaults + def initialize(defaults) + @defaults = defaults + end + + def include?(name) + @defaults.include?(name) + end + + def lookup(name) + @defaults[name].default + end + end + + class Values + def initialize(name, defaults) + @name = name + @values = {} + @defaults = defaults + end + + def include?(name) + @values.include?(name) + end + + def set(name, value) + if !@defaults[name] + raise ArgumentError, + "Attempt to assign a value to unknown configuration parameter #{name.inspect}" + end + + @defaults[name].handle(value) + + @values[name] = value + end + + def lookup(name) + @values[name] + end + end + + class ValuesFromSection + def initialize(name, section) + @name = name + @section = section + end + + def include?(name) + !@section.setting(name).nil? + end + + def lookup(name) + @section.setting(name).value + end + end end diff --git a/lib/puppet/settings/base_setting.rb b/lib/puppet/settings/base_setting.rb index 3be95b0db..f0ccd49c1 100644 --- a/lib/puppet/settings/base_setting.rb +++ b/lib/puppet/settings/base_setting.rb @@ -72,14 +72,13 @@ class Puppet::Settings::BaseSetting end end - def has_hook? - respond_to? :handle - end - def hook=(block) meta_def :handle, &block end + def handle(value) + end + # Create the new element. Pretty much just sets the name. def initialize(args = {}) unless @settings = args.delete(:settings) @@ -161,5 +160,6 @@ class Puppet::Settings::BaseSetting end def set_meta(meta) + Puppet.notice("#{name} does not support meta data. Ignoring.") end end diff --git a/lib/puppet/settings/config_file.rb b/lib/puppet/settings/config_file.rb index aef3faecb..3b53a202b 100644 --- a/lib/puppet/settings/config_file.rb +++ b/lib/puppet/settings/config_file.rb @@ -56,6 +56,10 @@ class Puppet::Settings::ConfigFile settings << Setting.new(name, value, meta) self end + + def setting(name) + settings.find { |setting| setting.name == name } + end end Setting = Struct.new(:name, :value, :meta) diff --git a/spec/unit/module_tool_spec.rb b/spec/unit/module_tool_spec.rb index 93db99afe..ddee6bf68 100755 --- a/spec/unit/module_tool_spec.rb +++ b/spec/unit/module_tool_spec.rb @@ -217,11 +217,12 @@ TREE let (:options) { {:environment => :pmttestenv} } before(:each) do + Puppet.settings.parse_config(<<-CONF) + [pmttestenv] + modulepath = #{["/foo", "/bar", "/no"].join(File::PATH_SEPARATOR)} + CONF Puppet[:modulepath] = "/no" Puppet[:environment] = :pmttestenv - Puppet.settings.set_value(:modulepath, - ["/foo", "/bar", "/no"].join(File::PATH_SEPARATOR), - :pmttestenv) end it 'Puppet[:modulepath] should be reset to the module path of the current environment' do diff --git a/spec/unit/node/environment_spec.rb b/spec/unit/node/environment_spec.rb index be8ae1a94..f71a37841 100755 --- a/spec/unit/node/environment_spec.rb +++ b/spec/unit/node/environment_spec.rb @@ -144,7 +144,11 @@ describe Puppet::Node::Environment do end it "should ask the Puppet settings instance for the setting qualified with the environment name" do - Puppet.settings.set_value(:server, "myval", :testing) + Puppet.settings.parse_config(<<-CONF) + [testing] + server = myval + CONF + env[:server].should == "myval" end diff --git a/spec/unit/settings_spec.rb b/spec/unit/settings_spec.rb index b3dc31f3d..d132c8d2d 100755 --- a/spec/unit/settings_spec.rb +++ b/spec/unit/settings_spec.rb @@ -269,7 +269,7 @@ describe Puppet::Settings do describe "setbycli" do it "should generate a deprecation warning" do - Puppet.expects(:deprecation_warning) + Puppet.expects(:deprecation_warning).at_least(1) @settings.setting(:myval).setbycli = true end it "should set the value" do @@ -1064,7 +1064,7 @@ describe Puppet::Settings do it "reparses if the file has changed" do @watched_file.expects(:changed?).returns true - @settings.expects(:unsafe_parse).with(@file) + @settings.expects(:parse_config_files) @settings.reparse_config_files end diff --git a/spec/unit/util/autoload_spec.rb b/spec/unit/util/autoload_spec.rb index 4a0782ac7..2f8b5353f 100755 --- a/spec/unit/util/autoload_spec.rb +++ b/spec/unit/util/autoload_spec.rb @@ -24,8 +24,12 @@ describe Puppet::Util::Autoload do end it "should collect all of the lib directories that exist in the current environment's module path" do + Puppet.settings.parse_config(<<-CONF) + [foo] + modulepath = #{@dira}#{File::PATH_SEPARATOR}#{@dirb}#{File::PATH_SEPARATOR}#{@dirc} + CONF + Puppet[:environment] = "foo" - Puppet.settings.set_value(:modulepath, "#{@dira}#{File::PATH_SEPARATOR}#{@dirb}#{File::PATH_SEPARATOR}#{@dirc}", :foo) Dir.expects(:entries).with(@dira).returns %w{one two} Dir.expects(:entries).with(@dirb).returns %w{one two} @@ -40,8 +44,12 @@ describe Puppet::Util::Autoload do end it "should not look for lib directories in directories starting with '.'" do + Puppet.settings.parse_config(<<-CONF) + [foo] + modulepath = #{@dira} + CONF + Puppet[:environment] = "foo" - Puppet.settings.set_value(:modulepath, @dira, :foo) Dir.expects(:entries).with(@dira).returns %w{. ..} FileTest.expects(:directory?).with(@dira).returns true From 00c2abfa65352afbc5947d967d11a876b765d61d Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Thu, 12 Dec 2013 18:41:13 -0800 Subject: [PATCH 203/800] (#23373) Add section control to the config print action This exposes the lookup facilities that are used inside the settings infrastructure in a manner that allows looking up values from the perspective of individual sections. This functionality is then exposed to the end user as the --section option on the `config print` subcommand. --- lib/puppet/face/config.rb | 43 +++++--- lib/puppet/node/environment.rb | 2 +- lib/puppet/settings.rb | 151 ++++++++++++++++++----------- lib/puppet/settings/config_file.rb | 7 +- spec/unit/face/config_spec.rb | 41 +++++--- spec/unit/settings_spec.rb | 14 --- 6 files changed, 151 insertions(+), 107 deletions(-) diff --git a/lib/puppet/face/config.rb b/lib/puppet/face/config.rb index d1ff86672..d0239ef7e 100644 --- a/lib/puppet/face/config.rb +++ b/lib/puppet/face/config.rb @@ -7,13 +7,14 @@ Puppet::Face.define(:config, '0.0.1') do summary "Interact with Puppet's configuration options." + option "--section SECTION_NAME" do + default_to { "main" } + summary "The section of the configuration file to interact with." + end + action(:print) do summary "Examine Puppet's current configuration settings." arguments "(all | [ ...]" - returns <<-'EOT' - A single value when called with one config setting, and a list of - settings and values when called with multiple options or "all." - EOT description <<-'EOT' Prints the value of a single configuration option or a list of configuration options. @@ -22,8 +23,8 @@ Puppet::Face.define(:config, '0.0.1') do `puppet --configprint`. EOT notes <<-'EOT' - By default, this action reads the configuration in agent mode. - Use the '--run_mode' and '--environment' flags to examine other + By default, this action reads the general configuration in in the 'main' + section. Use the '--section' and '--environment' flags to examine other configuration domains. EOT examples <<-'EOT' @@ -33,16 +34,22 @@ Puppet::Face.define(:config, '0.0.1') do Get a list of important directories from the master's config: - $ puppet config print all --run_mode master | grep -E "(path|dir)" + $ puppet config print all --section master | grep -E "(path|dir)" EOT when_invoked do |*args| - args.pop + options = args.pop - args = [ "all" ] if args.empty? + args = Puppet.settings.to_a.collect(&:first) if args.empty? - Puppet.settings[:configprint] = args.join(",") - Puppet.settings.print_config_options + values = Puppet.settings.values(Puppet[:environment].to_sym, options[:section].to_sym) + if args.length == 1 + puts values.interpolate(args[0].to_sym) + else + args.each do |setting_name| + puts "#{setting_name} = #{values.interpolate(setting_name.to_sym)}" + end + end nil end end @@ -53,16 +60,20 @@ Puppet::Face.define(:config, '0.0.1') do description <<-'EOT' Update values in the `puppet.conf` configuration file. EOT + notes <<-'EOT' + By default, this action manipulates the configuration in the + 'main' section. Use the '--section' flag to manipulate other + configuration domains. + EOT examples <<-'EOT' Set puppet's runfile directory: $ puppet config set rundir /var/run/puppet - EOT - option "--section SECTION_NAME" do - default_to { "main" } - summary "The section of the configuration file to change." - end + Set the vardir for only the agent: + + $ puppet config set vardir /var/lib/puppetagent --section agent + EOT when_invoked do |name, value, options| file = Puppet::FileSystem::File.new(Puppet.settings.which_configuration_file) diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb index f79b8b35b..441b4e5e6 100644 --- a/lib/puppet/node/environment.rb +++ b/lib/puppet/node/environment.rb @@ -457,7 +457,7 @@ class Puppet::Node::Environment return empty_parse_result if Puppet.settings[:ignoreimport] # parser = Puppet::Parser::Parser.new(self) parser = Puppet::Parser::ParserFactory.parser(self) - if code = Puppet.settings.uninterpolated_value(:code, name.to_s) and code != "" + if code = Puppet.settings.value(:code, name.to_s) and code != "" parser.string = code else file = Puppet.settings.value(:manifest, name.to_s) diff --git a/lib/puppet/settings.rb b/lib/puppet/settings.rb index 6bfd3dcbc..90d3003c3 100644 --- a/lib/puppet/settings.rb +++ b/lib/puppet/settings.rb @@ -305,26 +305,6 @@ class Puppet::Settings end private :call_hooks_deferred_to_application_initialization - # Do variable interpolation on the value. - def convert(value, environment = nil) - return nil if value.nil? - return value unless value.is_a? String - newval = value.gsub(/\$(\w+)|\$\{(\w+)\}/) do |value| - varname = $2 || $1 - if varname == "environment" and environment - environment - elsif varname == "run_mode" - preferred_run_mode - elsif pval = self.value(varname, environment) - pval - else - raise InterpolationError, "Could not find value for #{value}" - end - end - - newval - end - # Return a value's description. def description(name) if obj = @config[name.to_sym] @@ -531,7 +511,7 @@ class Puppet::Settings @configuration_file = data data.sections.each do |name, section| section.settings.each do |setting| - if type = @config[setting.name] + if setting.has_metadata? && type = @config[setting.name] type.set_meta(setting.meta) end end @@ -546,7 +526,7 @@ class Puppet::Settings # Call any hooks we should be calling. @config.values.each do |setting| - value_sets_for(env).each do |source| + value_sets_for(env, self.preferred_run_mode).each do |source| if source.include?(setting.name) # We still have to use value to retrieve the value, since # we want the fully interpolated value, not $vardir/lib or whatever. @@ -946,22 +926,28 @@ Generated on #{Time.now}. end def uninterpolated_value(param, environment = nil) + Puppet.deprecation_warning("Puppet.settings.uninterpolated_value is deprecated. Use Puppet.settings.value instead") param = param.to_sym environment &&= environment.to_sym - # See if we can find it within our searchable list of values - val = find_value(environment, param) - - # If we didn't get a value, use the default - val = @config[param].default if val.nil? - - val + values(environment, self.preferred_run_mode).lookup(param) end - def find_value(environment, param) - ChainedValues.new(value_sets_for(environment) + [ValuesFromDefaults.new(@config)]).lookup(param) + # Retrieve an object that can be used for looking up values of configuration + # settings. + # + # @param environment [Symbol] The name of the environment in which to lookup + # @param section [Symbol] The name of the configuration section in which to lookup + # @return [Puppet::Settings::ChainedValues] An object to perform lookups + # @api public + def values(environment, section) + ChainedValues.new( + section, + environment, + value_sets_for(environment, section) + + [ValuesFromDefaults.new(@config)], + @config) end - private :find_value # Find the correct value using our search path. # @@ -979,37 +965,19 @@ Generated on #{Time.now}. setting = @config[param] # Short circuit to nil for undefined parameters. - return nil unless @config.include?(param) - - # Yay, recursion. - #self.reparse unless [:config, :filetimeout].include?(param) + return nil if setting.nil? # Check the cache first. It needs to be a per-environment # cache so that we don't spread values from one env # to another. if @cache[environment||"none"].has_key?(param) return @cache[environment||"none"][param] + elsif bypass_interpolation + val = values(environment, self.preferred_run_mode).lookup(param) + else + val = values(environment, self.preferred_run_mode).interpolate(param) end - val = uninterpolated_value(param, environment) - - return val if bypass_interpolation - if param == :code - # if we interpolate code, all hell breaks loose. - return val - end - - # Convert it if necessary - begin - val = convert(val, environment) - rescue InterpolationError => err - # This happens because we don't have access to the param name when the - # exception is originally raised, but we want it in the message - raise InterpolationError, "Error converting value for param '#{param}': #{err}", err.backtrace - end - - val = setting.munge(val) if setting.respond_to?(:munge) - # And cache it @cache[environment||"none"][param] = val val end @@ -1070,16 +1038,16 @@ Generated on #{Time.now}. end # Yield each search source in turn. - def value_sets_for(environment) + def value_sets_for(environment, mode) searchpath(environment).collect do |name| case name when :cli, :memory, :application_defaults @value_sets[name] when :run_mode if @configuration_file - section = @configuration_file.sections[self.preferred_run_mode] + section = @configuration_file.sections[mode] if section - ValuesFromSection.new(self.preferred_run_mode, section) + ValuesFromSection.new(mode, section) end end else @@ -1143,11 +1111,24 @@ Generated on #{Time.now}. end private :explicit_config_file? + # Lookup configuration setting value through a chain of different value sources. + # + # @api public class ChainedValues - def initialize(value_sets) + # @see Puppet::Settings.values + # @api private + def initialize(mode, environment, value_sets, defaults) + @mode = mode + @environment = environment @value_sets = value_sets + @defaults = defaults end + # Lookup the uninterpolated value. + # + # @param name [Symbol] The configuration setting name to look up + # @return [Object] The configuration setting value or nil if the setting is not known + # @api public def lookup(name) @value_sets.each do |set| if set.include?(name) @@ -1156,6 +1137,58 @@ Generated on #{Time.now}. end nil end + + # Lookup the interpolated value. All instances of `$name` in the value will + # be replaced by performing a lookup of `name` and substituting the text + # for `$name` in the original value. This interpolation is only performed + # if the looked up value is a String. + # + # @param name [Symbol] The configuration setting name to look up + # @return [Object] The configuration setting value or nil if the setting is not known + # @api public + def interpolate(name) + setting = @defaults[name] + + if setting + val = lookup(name) + # if we interpolate code, all hell breaks loose. + if name == :code + val + else + # Convert it if necessary + begin + val = convert(val) + rescue InterpolationError => err + # This happens because we don't have access to the param name when the + # exception is originally raised, but we want it in the message + raise InterpolationError, "Error converting value for param '#{name}': #{err}", err.backtrace + end + + setting.munge(val) + end + else + nil + end + end + + private + + def convert(value) + return nil if value.nil? + return value unless value.is_a? String + value.gsub(/\$(\w+)|\$\{(\w+)\}/) do |value| + varname = $2 || $1 + if varname == "environment" + @environment + elsif varname == "run_mode" + @mode + elsif pval = interpolate(varname.to_sym) + pval + else + raise InterpolationError, "Could not find value for #{value}" + end + end + end end class ValuesFromDefaults diff --git a/lib/puppet/settings/config_file.rb b/lib/puppet/settings/config_file.rb index 3b53a202b..12f31fd9f 100644 --- a/lib/puppet/settings/config_file.rb +++ b/lib/puppet/settings/config_file.rb @@ -62,7 +62,12 @@ class Puppet::Settings::ConfigFile end end - Setting = Struct.new(:name, :value, :meta) + Setting = Struct.new(:name, :value, :meta) do + def has_metadata? + meta != NO_META + end + end + Meta = Struct.new(:owner, :group, :mode) NO_META = Meta.new(nil, nil, nil) diff --git a/spec/unit/face/config_spec.rb b/spec/unit/face/config_spec.rb index 1d1aee43f..cb6ef7490 100755 --- a/spec/unit/face/config_spec.rb +++ b/spec/unit/face/config_spec.rb @@ -3,29 +3,38 @@ require 'spec_helper' require 'puppet/face' describe Puppet::Face[:config, '0.0.1'] do - it "should use Settings#print_config_options when asked to print" do - Puppet.settings.stubs(:puts) - Puppet.settings.expects(:print_config_options) - subject.print + it "prints a single setting without the name" do + Puppet[:trace] = true + + subject.expects(:puts).with(true) + + subject.print("trace").should be_nil end - it "should set 'configprint' to all desired values and call print_config_options when a specific value is provided" do - Puppet.settings.stubs(:puts) - Puppet.settings.expects(:print_config_options) - subject.print("libdir", "ssldir") - Puppet.settings[:configprint].should == "libdir,ssldir" + it "prints multiple settings with the names" do + Puppet[:trace] = true + Puppet[:syslogfacility] = "file" + + subject.expects(:puts).with("trace = true") + subject.expects(:puts).with("syslogfacility = file") + + subject.print("trace", "syslogfacility") end - it "should always return nil" do - Puppet.settings.stubs(:puts) - Puppet.settings.expects(:print_config_options) - subject.print("libdir").should be_nil + it "prints the setting from the selected section" do + Puppet.settings.parse_config(<<-CONF) + [other] + syslogfacility = file + CONF + + subject.expects(:puts).with("file") + + subject.print("syslogfacility", :section => "other") end it "should default to all when no arguments are given" do - Puppet.settings.stubs(:puts) - Puppet.settings.expects(:print_config_options) + subject.expects(:puts).times(Puppet.settings.to_a.length) + subject.print - Puppet.settings[:configprint].should == "all" end end diff --git a/spec/unit/settings_spec.rb b/spec/unit/settings_spec.rb index d132c8d2d..4306514b1 100755 --- a/spec/unit/settings_spec.rb +++ b/spec/unit/settings_spec.rb @@ -542,20 +542,6 @@ describe Puppet::Settings do @settings[:two].should == "one TWO" end - describe "caching values that evaluate to false" do - it "caches nil" do - @settings.expects(:convert).once.returns nil - @settings[:five].should be_nil - @settings[:five].should be_nil - end - - it "caches false" do - @settings.expects(:convert).once.returns false - @settings[:five].should == false - @settings[:five].should == false - end - end - it "should not cache values such that information from one environment is returned for another environment" do text = "[env1]\none = oneval\n[env2]\none = twoval\n" @settings.stubs(:read_file).returns(text) From 3b486fb922f35e5dff9fe625f96f46b66142c975 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Thu, 12 Dec 2013 19:43:53 -0800 Subject: [PATCH 204/800] (#23373) Reuse existing section headers Previously the setting manipulation code would always create a section header if the setting was not already present in the file. This changes it to reuse and existing header if the section is present but the setting is not. --- lib/puppet/settings/ini_file.rb | 70 +++++++++++++++++++++-------- spec/unit/settings/ini_file_spec.rb | 48 +++++++++++++++++++- 2 files changed, 98 insertions(+), 20 deletions(-) diff --git a/lib/puppet/settings/ini_file.rb b/lib/puppet/settings/ini_file.rb index 11b727217..e465c420a 100644 --- a/lib/puppet/settings/ini_file.rb +++ b/lib/puppet/settings/ini_file.rb @@ -10,44 +10,59 @@ class Puppet::Settings::IniFile end def self.parse(config_fh) - lines = [DefaultSection.new] + config = new([DefaultSection.new]) config_fh.each_line do |line| case line when /^(\s*)\[(\w+)\](\s*)$/ - lines << SectionLine.new(lines[-1], $1, $2, $3) + config.append(SectionLine.new($1, $2, $3)) when /^(\s*)(\w+)(\s*=\s*)(.*?)(\s*)$/ - lines << SettingLine.new(lines[-1], $1, $2, $3, $4, $5) + config.append(SettingLine.new($1, $2, $3, $4, $5)) else - lines << Line.new(lines[-1], line) + config.append(Line.new(line)) end end - new(lines) + config end def initialize(lines = []) @lines = lines end - def add_section(name) - @lines << SectionLine.new(@lines[-1], "", name, "") + def append(line) + line.previous = @lines.last + @lines << line end - def add_setting(name, value) - @lines << SettingLine.new(@lines[-1], "", name, "=", value, "") + def insert_after(line, new_line) + new_line.previous = line + + insertion_point = @lines.index(line) || @lines.index(section) + @lines.insert(insertion_point + 1, new_line) + if @lines.length > insertion_point + 2 + @lines[insertion_point + 2].previous = new_line + end + end + + def last + @lines.last end def each(&block) @lines.each(&block) end + def section(name) + sections.find { |section| section.name == name } + end + def sections - sections = @lines.select { |line| line.is_a?(SectionLine) } + @lines.select { |line| line.is_a?(SectionLine) } end def setting(section, name) - lines_in(section).find do |line| - line.is_a?(SettingLine) && line.name == name + settings_in(lines_in(section)).find do |line| + line.name == name end end @@ -65,6 +80,10 @@ class Puppet::Settings::IniFile section_lines end + def settings_in(lines) + lines.select { |line| line.is_a?(SettingLine) } + end + def write(fh) fh.truncate(0) fh.rewind @@ -84,13 +103,28 @@ class Puppet::Settings::IniFile if setting setting.value = value else - @config.add_section(section) - @config.add_setting(name, value) + add_setting(section, name, value) end end + + private + + def add_setting(section_name, name, value) + section = @config.section(section_name) + if section.nil? + previous_line = SectionLine.new("", section_name, "") + @config.append(previous_line) + else + previous_line = @config.settings_in(@config.lines_in(section_name)).last || section + end + + @config.insert_after(previous_line, SettingLine.new("", name, " = ", value, "")) + end end module LineNumber + attr_accessor :previous + def line_number line = 0 previous_line = previous @@ -102,7 +136,7 @@ class Puppet::Settings::IniFile end end - Line = Struct.new(:previous, :text) do + Line = Struct.new(:text) do include LineNumber def write(fh) @@ -110,7 +144,7 @@ class Puppet::Settings::IniFile end end - SettingLine = Struct.new(:previous, :prefix, :name, :infix, :value, :suffix) do + SettingLine = Struct.new(:prefix, :name, :infix, :value, :suffix) do include LineNumber def write(fh) @@ -122,7 +156,7 @@ class Puppet::Settings::IniFile end end - SectionLine = Struct.new(:previous, :prefix, :name, :suffix) do + SectionLine = Struct.new(:prefix, :name, :suffix) do include LineNumber def write(fh) @@ -136,7 +170,7 @@ class Puppet::Settings::IniFile class DefaultSection < SectionLine def initialize - super(nil, "", DEFAULT_SECTION_NAME, "") + super("", DEFAULT_SECTION_NAME, "") end def write(fh) diff --git a/spec/unit/settings/ini_file_spec.rb b/spec/unit/settings/ini_file_spec.rb index aa5beca18..a0c3edfa6 100644 --- a/spec/unit/settings/ini_file_spec.rb +++ b/spec/unit/settings/ini_file_spec.rb @@ -24,7 +24,7 @@ describe Puppet::Settings::IniFile do config.set("the_section", "name", "value") end - expect(config_fh.string).to eq "[the_section]\nname=value\n" + expect(config_fh.string).to eq "[the_section]\nname = value\n" end it "preserves comments when writing a new name and value" do @@ -34,7 +34,7 @@ describe Puppet::Settings::IniFile do config.set("the_section", "name", "value") end - expect(config_fh.string).to eq "# this is a comment\n[the_section]\nname=value\n" + expect(config_fh.string).to eq "# this is a comment\n[the_section]\nname = value\n" end it "updates existing names and values in place" do @@ -91,6 +91,50 @@ describe Puppet::Settings::IniFile do CONFIG end + it "adds new settings to an existing section" do + config_fh = a_config_file_containing(<<-CONFIG) + [section] + original = value + + # comment about 'other' section + [other] + dont = change + CONFIG + + Puppet::Settings::IniFile.update(config_fh) do |config| + config.set("section", "updated", "new") + end + + expect(config_fh.string).to eq <<-CONFIG + [section] + original = value +updated = new + + # comment about 'other' section + [other] + dont = change + CONFIG + end + + it "adds a new setting into an existing, yet empty section" do + config_fh = a_config_file_containing(<<-CONFIG) + [section] + [other] + dont = change + CONFIG + + Puppet::Settings::IniFile.update(config_fh) do |config| + config.set("section", "updated", "new") + end + + expect(config_fh.string).to eq <<-CONFIG + [section] +updated = new + [other] + dont = change + CONFIG + end + it "finds settings when the section is split up" do config_fh = a_config_file_containing(<<-CONFIG) [section] From a5a9c014b768dce907448dc441161bb63d3c8dd5 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Fri, 13 Dec 2013 10:50:10 -0800 Subject: [PATCH 205/800] (maint) Windows ruby commands need a cmd /c prefix Beaker handles this automagically for the puppet helpers, but we need to prepend it for gem. --- acceptance/config/el6/setup/common/SetParser.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/acceptance/config/el6/setup/common/SetParser.rb b/acceptance/config/el6/setup/common/SetParser.rb index a373911a9..d49ebf5e9 100644 --- a/acceptance/config/el6/setup/common/SetParser.rb +++ b/acceptance/config/el6/setup/common/SetParser.rb @@ -15,9 +15,10 @@ test_name "add parser=#{ENV['PARSER']} to all puppet.conf (only if $PARSER is se if !options[:install].empty? and parser == 'future' # We are installing from source rather than packages and need the following: - on(host, "gem install rgen") + win_cmd_prefix = 'cmd /c ' if host['platform'] =~ /windows/ + on(host, "#{win_cmd_prefix}gem install rgen") # deprecated - remove when binder is removed from pops - on(host, "gem install json") + on(host, "#{win_cmd_prefix}gem install json") end end end From c9ff5a9b87138cb304840181613e2007d4e8bb37 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Fri, 13 Dec 2013 12:58:57 -0800 Subject: [PATCH 206/800] (maint) Move everything out of el6 and into acceptance/ Originally began this as a harness for testing against el6, but now using this generically for all acceptance, so we can move it back into acceptance/ proper and not keep it nested under config. --- acceptance/{config/el6 => }/.gitignore | 0 acceptance/{config/el6 => }/Gemfile | 0 acceptance/{config/el6 => }/README.md | 0 acceptance/{config/el6 => }/Rakefile | 4 ++-- acceptance/config/{el6/config => }/git/options.rb | 0 acceptance/config/{el6/config => }/nodes/all.yaml | 0 acceptance/config/{el6/config => }/nodes/all_but_windows.yaml | 0 acceptance/config/{el6/config => }/nodes/all_linux.yaml | 0 acceptance/config/{el6/config => }/nodes/centos5.yaml | 0 acceptance/config/{el6/config => }/nodes/centos6.yaml | 0 acceptance/config/{el6/config => }/nodes/fedora18.yaml | 0 acceptance/config/{el6/config => }/nodes/fedora19.yaml | 0 .../{el6/config => }/nodes/foss-win-2003r2-ruby193.yaml | 0 acceptance/config/{el6/config => }/nodes/lucid.yaml | 0 .../config/{el6/config => }/nodes/pe/centos-5-32ma-32da-32da | 0 acceptance/config/{el6/config => }/nodes/pe/centos-5-32mda | 0 .../config/{el6/config => }/nodes/pe/centos-5-64ma-64da-64da | 0 acceptance/config/{el6/config => }/nodes/pe/centos-5-64mda | 0 .../config/{el6/config => }/nodes/pe/centos-6-32ma-32da-32da | 0 acceptance/config/{el6/config => }/nodes/pe/centos-6-32mda | 0 .../config/{el6/config => }/nodes/pe/centos-6-64ma-64da-64da | 0 acceptance/config/{el6/config => }/nodes/pe/centos-6-64mda | 0 .../{el6/config => }/nodes/pe/centos-6-64mda-sol-10-64a | 0 .../config/{el6/config => }/nodes/pe/debian-6-32ma-32da-32da | 0 acceptance/config/{el6/config => }/nodes/pe/debian-6-32mda | 0 .../config/{el6/config => }/nodes/pe/debian-6-64ma-64da-64da | 0 acceptance/config/{el6/config => }/nodes/pe/debian-6-64mda | 0 .../config/{el6/config => }/nodes/pe/debian-7-32ma-32da-32da | 0 acceptance/config/{el6/config => }/nodes/pe/debian-7-32mda | 0 .../config/{el6/config => }/nodes/pe/debian-7-64ma-64da-64da | 0 acceptance/config/{el6/config => }/nodes/pe/debian-7-64mda | 0 acceptance/config/{el6/config => }/nodes/pe/debian-7-i386 | 0 .../config/{el6/config => }/nodes/pe/oracle-5-32ma-32da-32da | 0 acceptance/config/{el6/config => }/nodes/pe/oracle-5-32mda | 0 .../config/{el6/config => }/nodes/pe/oracle-5-64ma-64da-64da | 0 acceptance/config/{el6/config => }/nodes/pe/oracle-5-64mda | 0 .../config/{el6/config => }/nodes/pe/oracle-6-32ma-32da-32da | 0 acceptance/config/{el6/config => }/nodes/pe/oracle-6-32mda | 0 .../config/{el6/config => }/nodes/pe/oracle-6-64ma-64da-64da | 0 acceptance/config/{el6/config => }/nodes/pe/oracle-6-64mda | 0 .../config/{el6/config => }/nodes/pe/redhat-5-32ma-32da-32da | 0 acceptance/config/{el6/config => }/nodes/pe/redhat-5-32mda | 0 .../config/{el6/config => }/nodes/pe/redhat-5-64ma-64da-64da | 0 acceptance/config/{el6/config => }/nodes/pe/redhat-5-64mda | 0 .../config/{el6/config => }/nodes/pe/redhat-6-32ma-32da-32da | 0 acceptance/config/{el6/config => }/nodes/pe/redhat-6-32mda | 0 .../config/{el6/config => }/nodes/pe/redhat-6-64ma-64da-64da | 0 acceptance/config/{el6/config => }/nodes/pe/redhat-6-64mda | 0 .../{el6/config => }/nodes/pe/scientific-5-32ma-32da-32da | 0 .../config/{el6/config => }/nodes/pe/scientific-5-32mda | 0 .../{el6/config => }/nodes/pe/scientific-5-64ma-64da-64da | 0 .../config/{el6/config => }/nodes/pe/scientific-5-64mda | 0 .../{el6/config => }/nodes/pe/scientific-6-32ma-32da-32da | 0 .../config/{el6/config => }/nodes/pe/scientific-6-32mda | 0 .../{el6/config => }/nodes/pe/scientific-6-64ma-64da-64da | 0 .../config/{el6/config => }/nodes/pe/scientific-6-64mda | 0 .../config/{el6/config => }/nodes/pe/sles-11-32ma-32da-32da | 0 acceptance/config/{el6/config => }/nodes/pe/sles-11-32mda | 0 .../config/{el6/config => }/nodes/pe/sles-11-64ma-64da-64da | 0 acceptance/config/{el6/config => }/nodes/pe/sles-11-64mda | 0 .../{el6/config => }/nodes/pe/ubuntu-1004-32ma-32da-32da | 0 acceptance/config/{el6/config => }/nodes/pe/ubuntu-1004-32mda | 0 .../{el6/config => }/nodes/pe/ubuntu-1004-64ma-64da-64da | 0 acceptance/config/{el6/config => }/nodes/pe/ubuntu-1004-64mda | 0 .../{el6/config => }/nodes/pe/ubuntu-1204-32ma-32da-32da | 0 acceptance/config/{el6/config => }/nodes/pe/ubuntu-1204-32mda | 0 .../{el6/config => }/nodes/pe/ubuntu-1204-64ma-64da-64da | 0 acceptance/config/{el6/config => }/nodes/pe/ubuntu-1204-64mda | 0 acceptance/config/{el6/config => }/nodes/precise.yaml | 0 .../config/{el6/config => }/nodes/precise_and_lucid.yaml | 0 acceptance/config/{el6/config => }/nodes/rhel5.yaml | 0 acceptance/config/{el6/config => }/nodes/rhel6.yaml | 0 acceptance/config/{el6/config => }/nodes/rhel6_and_rhel5.yaml | 0 acceptance/config/{el6/config => }/nodes/solaris10.yaml | 0 acceptance/config/{el6/config => }/nodes/solaris11.yaml | 0 acceptance/config/{el6/config => }/nodes/squeeze.yaml | 0 .../{el6/config => }/nodes/standalone/rhel6-and-precise.yaml | 0 acceptance/config/{el6/config => }/nodes/wheezy.yaml | 0 acceptance/config/{el6/config => }/nodes/win2003-all.yaml | 0 acceptance/config/{el6/config => }/nodes/win2003.yaml | 0 acceptance/config/{el6/config => }/nodes/win2003r2.yaml | 0 acceptance/config/{el6/config => }/nodes/win2008-all.yaml | 0 acceptance/config/{el6/config => }/nodes/win2008r2.yaml | 0 acceptance/config/{el6/config => }/nodes/win2012-all.yaml | 0 acceptance/config/{el6/config => }/nodes/windows-all.yaml | 0 acceptance/config/{el6/config => }/packages/options.rb | 0 acceptance/{config/el6 => }/setup/common/SetParser.rb | 0 acceptance/{config/el6 => }/setup/common/StopFirewall.rb | 0 acceptance/{config/el6 => }/setup/common/ValidateSignCert.rb | 0 .../{config/el6 => }/setup/git/pre-suite/00_EnvSetup.rb | 0 .../{config/el6 => }/setup/git/pre-suite/01_TestSetup.rb | 0 .../el6 => }/setup/git/pre-suite/02_PuppetUserAndGroup.rb | 0 .../{config/el6 => }/setup/git/pre-suite/035_StopFirewall.rb | 0 .../el6 => }/setup/git/pre-suite/03_PuppetMasterSanity.rb | 0 .../el6 => }/setup/git/pre-suite/04_ValidateSignCert.rb | 0 .../{config/el6 => }/setup/git/pre-suite/05_HieraSetup.rb | 0 .../{config/el6 => }/setup/git/pre-suite/06_InstallModules.rb | 0 .../{config/el6 => }/setup/git/pre-suite/07_InstallCACerts.rb | 0 .../{config/el6 => }/setup/git/pre-suite/10_SetParser.rb | 0 .../{config/el6 => }/setup/packages/pre-suite/01_Install.rb | 0 .../el6 => }/setup/packages/pre-suite/02_StopFirewall.rb | 0 .../el6 => }/setup/packages/pre-suite/04_ValidateSignCert.rb | 0 .../{config/el6 => }/setup/packages/pre-suite/10_SetParser.rb | 0 acceptance/{config/el6 => }/setup/pe/pre-suite/00_install.rb | 0 .../el6 => }/setup/rsync/pre-suite/00_PurgeAndReinstall.rb | 0 .../{config/el6 => }/setup/rsync/pre-suite/01_RsyncSource.rb | 2 +- 106 files changed, 3 insertions(+), 3 deletions(-) rename acceptance/{config/el6 => }/.gitignore (100%) rename acceptance/{config/el6 => }/Gemfile (100%) rename acceptance/{config/el6 => }/README.md (100%) rename acceptance/{config/el6 => }/Rakefile (99%) rename acceptance/config/{el6/config => }/git/options.rb (100%) rename acceptance/config/{el6/config => }/nodes/all.yaml (100%) rename acceptance/config/{el6/config => }/nodes/all_but_windows.yaml (100%) rename acceptance/config/{el6/config => }/nodes/all_linux.yaml (100%) rename acceptance/config/{el6/config => }/nodes/centos5.yaml (100%) rename acceptance/config/{el6/config => }/nodes/centos6.yaml (100%) rename acceptance/config/{el6/config => }/nodes/fedora18.yaml (100%) rename acceptance/config/{el6/config => }/nodes/fedora19.yaml (100%) rename acceptance/config/{el6/config => }/nodes/foss-win-2003r2-ruby193.yaml (100%) rename acceptance/config/{el6/config => }/nodes/lucid.yaml (100%) rename acceptance/config/{el6/config => }/nodes/pe/centos-5-32ma-32da-32da (100%) rename acceptance/config/{el6/config => }/nodes/pe/centos-5-32mda (100%) rename acceptance/config/{el6/config => }/nodes/pe/centos-5-64ma-64da-64da (100%) rename acceptance/config/{el6/config => }/nodes/pe/centos-5-64mda (100%) rename acceptance/config/{el6/config => }/nodes/pe/centos-6-32ma-32da-32da (100%) rename acceptance/config/{el6/config => }/nodes/pe/centos-6-32mda (100%) rename acceptance/config/{el6/config => }/nodes/pe/centos-6-64ma-64da-64da (100%) rename acceptance/config/{el6/config => }/nodes/pe/centos-6-64mda (100%) rename acceptance/config/{el6/config => }/nodes/pe/centos-6-64mda-sol-10-64a (100%) rename acceptance/config/{el6/config => }/nodes/pe/debian-6-32ma-32da-32da (100%) rename acceptance/config/{el6/config => }/nodes/pe/debian-6-32mda (100%) rename acceptance/config/{el6/config => }/nodes/pe/debian-6-64ma-64da-64da (100%) rename acceptance/config/{el6/config => }/nodes/pe/debian-6-64mda (100%) rename acceptance/config/{el6/config => }/nodes/pe/debian-7-32ma-32da-32da (100%) rename acceptance/config/{el6/config => }/nodes/pe/debian-7-32mda (100%) rename acceptance/config/{el6/config => }/nodes/pe/debian-7-64ma-64da-64da (100%) rename acceptance/config/{el6/config => }/nodes/pe/debian-7-64mda (100%) rename acceptance/config/{el6/config => }/nodes/pe/debian-7-i386 (100%) rename acceptance/config/{el6/config => }/nodes/pe/oracle-5-32ma-32da-32da (100%) rename acceptance/config/{el6/config => }/nodes/pe/oracle-5-32mda (100%) rename acceptance/config/{el6/config => }/nodes/pe/oracle-5-64ma-64da-64da (100%) rename acceptance/config/{el6/config => }/nodes/pe/oracle-5-64mda (100%) rename acceptance/config/{el6/config => }/nodes/pe/oracle-6-32ma-32da-32da (100%) rename acceptance/config/{el6/config => }/nodes/pe/oracle-6-32mda (100%) rename acceptance/config/{el6/config => }/nodes/pe/oracle-6-64ma-64da-64da (100%) rename acceptance/config/{el6/config => }/nodes/pe/oracle-6-64mda (100%) rename acceptance/config/{el6/config => }/nodes/pe/redhat-5-32ma-32da-32da (100%) rename acceptance/config/{el6/config => }/nodes/pe/redhat-5-32mda (100%) rename acceptance/config/{el6/config => }/nodes/pe/redhat-5-64ma-64da-64da (100%) rename acceptance/config/{el6/config => }/nodes/pe/redhat-5-64mda (100%) rename acceptance/config/{el6/config => }/nodes/pe/redhat-6-32ma-32da-32da (100%) rename acceptance/config/{el6/config => }/nodes/pe/redhat-6-32mda (100%) rename acceptance/config/{el6/config => }/nodes/pe/redhat-6-64ma-64da-64da (100%) rename acceptance/config/{el6/config => }/nodes/pe/redhat-6-64mda (100%) rename acceptance/config/{el6/config => }/nodes/pe/scientific-5-32ma-32da-32da (100%) rename acceptance/config/{el6/config => }/nodes/pe/scientific-5-32mda (100%) rename acceptance/config/{el6/config => }/nodes/pe/scientific-5-64ma-64da-64da (100%) rename acceptance/config/{el6/config => }/nodes/pe/scientific-5-64mda (100%) rename acceptance/config/{el6/config => }/nodes/pe/scientific-6-32ma-32da-32da (100%) rename acceptance/config/{el6/config => }/nodes/pe/scientific-6-32mda (100%) rename acceptance/config/{el6/config => }/nodes/pe/scientific-6-64ma-64da-64da (100%) rename acceptance/config/{el6/config => }/nodes/pe/scientific-6-64mda (100%) rename acceptance/config/{el6/config => }/nodes/pe/sles-11-32ma-32da-32da (100%) rename acceptance/config/{el6/config => }/nodes/pe/sles-11-32mda (100%) rename acceptance/config/{el6/config => }/nodes/pe/sles-11-64ma-64da-64da (100%) rename acceptance/config/{el6/config => }/nodes/pe/sles-11-64mda (100%) rename acceptance/config/{el6/config => }/nodes/pe/ubuntu-1004-32ma-32da-32da (100%) rename acceptance/config/{el6/config => }/nodes/pe/ubuntu-1004-32mda (100%) rename acceptance/config/{el6/config => }/nodes/pe/ubuntu-1004-64ma-64da-64da (100%) rename acceptance/config/{el6/config => }/nodes/pe/ubuntu-1004-64mda (100%) rename acceptance/config/{el6/config => }/nodes/pe/ubuntu-1204-32ma-32da-32da (100%) rename acceptance/config/{el6/config => }/nodes/pe/ubuntu-1204-32mda (100%) rename acceptance/config/{el6/config => }/nodes/pe/ubuntu-1204-64ma-64da-64da (100%) rename acceptance/config/{el6/config => }/nodes/pe/ubuntu-1204-64mda (100%) rename acceptance/config/{el6/config => }/nodes/precise.yaml (100%) rename acceptance/config/{el6/config => }/nodes/precise_and_lucid.yaml (100%) rename acceptance/config/{el6/config => }/nodes/rhel5.yaml (100%) rename acceptance/config/{el6/config => }/nodes/rhel6.yaml (100%) rename acceptance/config/{el6/config => }/nodes/rhel6_and_rhel5.yaml (100%) rename acceptance/config/{el6/config => }/nodes/solaris10.yaml (100%) rename acceptance/config/{el6/config => }/nodes/solaris11.yaml (100%) rename acceptance/config/{el6/config => }/nodes/squeeze.yaml (100%) rename acceptance/config/{el6/config => }/nodes/standalone/rhel6-and-precise.yaml (100%) rename acceptance/config/{el6/config => }/nodes/wheezy.yaml (100%) rename acceptance/config/{el6/config => }/nodes/win2003-all.yaml (100%) rename acceptance/config/{el6/config => }/nodes/win2003.yaml (100%) rename acceptance/config/{el6/config => }/nodes/win2003r2.yaml (100%) rename acceptance/config/{el6/config => }/nodes/win2008-all.yaml (100%) rename acceptance/config/{el6/config => }/nodes/win2008r2.yaml (100%) rename acceptance/config/{el6/config => }/nodes/win2012-all.yaml (100%) rename acceptance/config/{el6/config => }/nodes/windows-all.yaml (100%) rename acceptance/config/{el6/config => }/packages/options.rb (100%) rename acceptance/{config/el6 => }/setup/common/SetParser.rb (100%) rename acceptance/{config/el6 => }/setup/common/StopFirewall.rb (100%) rename acceptance/{config/el6 => }/setup/common/ValidateSignCert.rb (100%) rename acceptance/{config/el6 => }/setup/git/pre-suite/00_EnvSetup.rb (100%) rename acceptance/{config/el6 => }/setup/git/pre-suite/01_TestSetup.rb (100%) rename acceptance/{config/el6 => }/setup/git/pre-suite/02_PuppetUserAndGroup.rb (100%) rename acceptance/{config/el6 => }/setup/git/pre-suite/035_StopFirewall.rb (100%) rename acceptance/{config/el6 => }/setup/git/pre-suite/03_PuppetMasterSanity.rb (100%) rename acceptance/{config/el6 => }/setup/git/pre-suite/04_ValidateSignCert.rb (100%) rename acceptance/{config/el6 => }/setup/git/pre-suite/05_HieraSetup.rb (100%) rename acceptance/{config/el6 => }/setup/git/pre-suite/06_InstallModules.rb (100%) rename acceptance/{config/el6 => }/setup/git/pre-suite/07_InstallCACerts.rb (100%) rename acceptance/{config/el6 => }/setup/git/pre-suite/10_SetParser.rb (100%) rename acceptance/{config/el6 => }/setup/packages/pre-suite/01_Install.rb (100%) rename acceptance/{config/el6 => }/setup/packages/pre-suite/02_StopFirewall.rb (100%) rename acceptance/{config/el6 => }/setup/packages/pre-suite/04_ValidateSignCert.rb (100%) rename acceptance/{config/el6 => }/setup/packages/pre-suite/10_SetParser.rb (100%) rename acceptance/{config/el6 => }/setup/pe/pre-suite/00_install.rb (100%) rename acceptance/{config/el6 => }/setup/rsync/pre-suite/00_PurgeAndReinstall.rb (100%) rename acceptance/{config/el6 => }/setup/rsync/pre-suite/01_RsyncSource.rb (91%) diff --git a/acceptance/config/el6/.gitignore b/acceptance/.gitignore similarity index 100% rename from acceptance/config/el6/.gitignore rename to acceptance/.gitignore diff --git a/acceptance/config/el6/Gemfile b/acceptance/Gemfile similarity index 100% rename from acceptance/config/el6/Gemfile rename to acceptance/Gemfile diff --git a/acceptance/config/el6/README.md b/acceptance/README.md similarity index 100% rename from acceptance/config/el6/README.md rename to acceptance/README.md diff --git a/acceptance/config/el6/Rakefile b/acceptance/Rakefile similarity index 99% rename from acceptance/config/el6/Rakefile rename to acceptance/Rakefile index 6b6fa89ab..5800a493f 100644 --- a/acceptance/config/el6/Rakefile +++ b/acceptance/Rakefile @@ -10,8 +10,8 @@ module HarnessOptions DEFAULTS = { :type => 'git', - :helper => ['../../lib/helper.rb'], - :tests => ['../../tests'], + :helper => ['lib/helper.rb'], + :tests => ['tests'], :debug => true, :color => false, :root_keys => true, diff --git a/acceptance/config/el6/config/git/options.rb b/acceptance/config/git/options.rb similarity index 100% rename from acceptance/config/el6/config/git/options.rb rename to acceptance/config/git/options.rb diff --git a/acceptance/config/el6/config/nodes/all.yaml b/acceptance/config/nodes/all.yaml similarity index 100% rename from acceptance/config/el6/config/nodes/all.yaml rename to acceptance/config/nodes/all.yaml diff --git a/acceptance/config/el6/config/nodes/all_but_windows.yaml b/acceptance/config/nodes/all_but_windows.yaml similarity index 100% rename from acceptance/config/el6/config/nodes/all_but_windows.yaml rename to acceptance/config/nodes/all_but_windows.yaml diff --git a/acceptance/config/el6/config/nodes/all_linux.yaml b/acceptance/config/nodes/all_linux.yaml similarity index 100% rename from acceptance/config/el6/config/nodes/all_linux.yaml rename to acceptance/config/nodes/all_linux.yaml diff --git a/acceptance/config/el6/config/nodes/centos5.yaml b/acceptance/config/nodes/centos5.yaml similarity index 100% rename from acceptance/config/el6/config/nodes/centos5.yaml rename to acceptance/config/nodes/centos5.yaml diff --git a/acceptance/config/el6/config/nodes/centos6.yaml b/acceptance/config/nodes/centos6.yaml similarity index 100% rename from acceptance/config/el6/config/nodes/centos6.yaml rename to acceptance/config/nodes/centos6.yaml diff --git a/acceptance/config/el6/config/nodes/fedora18.yaml b/acceptance/config/nodes/fedora18.yaml similarity index 100% rename from acceptance/config/el6/config/nodes/fedora18.yaml rename to acceptance/config/nodes/fedora18.yaml diff --git a/acceptance/config/el6/config/nodes/fedora19.yaml b/acceptance/config/nodes/fedora19.yaml similarity index 100% rename from acceptance/config/el6/config/nodes/fedora19.yaml rename to acceptance/config/nodes/fedora19.yaml diff --git a/acceptance/config/el6/config/nodes/foss-win-2003r2-ruby193.yaml b/acceptance/config/nodes/foss-win-2003r2-ruby193.yaml similarity index 100% rename from acceptance/config/el6/config/nodes/foss-win-2003r2-ruby193.yaml rename to acceptance/config/nodes/foss-win-2003r2-ruby193.yaml diff --git a/acceptance/config/el6/config/nodes/lucid.yaml b/acceptance/config/nodes/lucid.yaml similarity index 100% rename from acceptance/config/el6/config/nodes/lucid.yaml rename to acceptance/config/nodes/lucid.yaml diff --git a/acceptance/config/el6/config/nodes/pe/centos-5-32ma-32da-32da b/acceptance/config/nodes/pe/centos-5-32ma-32da-32da similarity index 100% rename from acceptance/config/el6/config/nodes/pe/centos-5-32ma-32da-32da rename to acceptance/config/nodes/pe/centos-5-32ma-32da-32da diff --git a/acceptance/config/el6/config/nodes/pe/centos-5-32mda b/acceptance/config/nodes/pe/centos-5-32mda similarity index 100% rename from acceptance/config/el6/config/nodes/pe/centos-5-32mda rename to acceptance/config/nodes/pe/centos-5-32mda diff --git a/acceptance/config/el6/config/nodes/pe/centos-5-64ma-64da-64da b/acceptance/config/nodes/pe/centos-5-64ma-64da-64da similarity index 100% rename from acceptance/config/el6/config/nodes/pe/centos-5-64ma-64da-64da rename to acceptance/config/nodes/pe/centos-5-64ma-64da-64da diff --git a/acceptance/config/el6/config/nodes/pe/centos-5-64mda b/acceptance/config/nodes/pe/centos-5-64mda similarity index 100% rename from acceptance/config/el6/config/nodes/pe/centos-5-64mda rename to acceptance/config/nodes/pe/centos-5-64mda diff --git a/acceptance/config/el6/config/nodes/pe/centos-6-32ma-32da-32da b/acceptance/config/nodes/pe/centos-6-32ma-32da-32da similarity index 100% rename from acceptance/config/el6/config/nodes/pe/centos-6-32ma-32da-32da rename to acceptance/config/nodes/pe/centos-6-32ma-32da-32da diff --git a/acceptance/config/el6/config/nodes/pe/centos-6-32mda b/acceptance/config/nodes/pe/centos-6-32mda similarity index 100% rename from acceptance/config/el6/config/nodes/pe/centos-6-32mda rename to acceptance/config/nodes/pe/centos-6-32mda diff --git a/acceptance/config/el6/config/nodes/pe/centos-6-64ma-64da-64da b/acceptance/config/nodes/pe/centos-6-64ma-64da-64da similarity index 100% rename from acceptance/config/el6/config/nodes/pe/centos-6-64ma-64da-64da rename to acceptance/config/nodes/pe/centos-6-64ma-64da-64da diff --git a/acceptance/config/el6/config/nodes/pe/centos-6-64mda b/acceptance/config/nodes/pe/centos-6-64mda similarity index 100% rename from acceptance/config/el6/config/nodes/pe/centos-6-64mda rename to acceptance/config/nodes/pe/centos-6-64mda diff --git a/acceptance/config/el6/config/nodes/pe/centos-6-64mda-sol-10-64a b/acceptance/config/nodes/pe/centos-6-64mda-sol-10-64a similarity index 100% rename from acceptance/config/el6/config/nodes/pe/centos-6-64mda-sol-10-64a rename to acceptance/config/nodes/pe/centos-6-64mda-sol-10-64a diff --git a/acceptance/config/el6/config/nodes/pe/debian-6-32ma-32da-32da b/acceptance/config/nodes/pe/debian-6-32ma-32da-32da similarity index 100% rename from acceptance/config/el6/config/nodes/pe/debian-6-32ma-32da-32da rename to acceptance/config/nodes/pe/debian-6-32ma-32da-32da diff --git a/acceptance/config/el6/config/nodes/pe/debian-6-32mda b/acceptance/config/nodes/pe/debian-6-32mda similarity index 100% rename from acceptance/config/el6/config/nodes/pe/debian-6-32mda rename to acceptance/config/nodes/pe/debian-6-32mda diff --git a/acceptance/config/el6/config/nodes/pe/debian-6-64ma-64da-64da b/acceptance/config/nodes/pe/debian-6-64ma-64da-64da similarity index 100% rename from acceptance/config/el6/config/nodes/pe/debian-6-64ma-64da-64da rename to acceptance/config/nodes/pe/debian-6-64ma-64da-64da diff --git a/acceptance/config/el6/config/nodes/pe/debian-6-64mda b/acceptance/config/nodes/pe/debian-6-64mda similarity index 100% rename from acceptance/config/el6/config/nodes/pe/debian-6-64mda rename to acceptance/config/nodes/pe/debian-6-64mda diff --git a/acceptance/config/el6/config/nodes/pe/debian-7-32ma-32da-32da b/acceptance/config/nodes/pe/debian-7-32ma-32da-32da similarity index 100% rename from acceptance/config/el6/config/nodes/pe/debian-7-32ma-32da-32da rename to acceptance/config/nodes/pe/debian-7-32ma-32da-32da diff --git a/acceptance/config/el6/config/nodes/pe/debian-7-32mda b/acceptance/config/nodes/pe/debian-7-32mda similarity index 100% rename from acceptance/config/el6/config/nodes/pe/debian-7-32mda rename to acceptance/config/nodes/pe/debian-7-32mda diff --git a/acceptance/config/el6/config/nodes/pe/debian-7-64ma-64da-64da b/acceptance/config/nodes/pe/debian-7-64ma-64da-64da similarity index 100% rename from acceptance/config/el6/config/nodes/pe/debian-7-64ma-64da-64da rename to acceptance/config/nodes/pe/debian-7-64ma-64da-64da diff --git a/acceptance/config/el6/config/nodes/pe/debian-7-64mda b/acceptance/config/nodes/pe/debian-7-64mda similarity index 100% rename from acceptance/config/el6/config/nodes/pe/debian-7-64mda rename to acceptance/config/nodes/pe/debian-7-64mda diff --git a/acceptance/config/el6/config/nodes/pe/debian-7-i386 b/acceptance/config/nodes/pe/debian-7-i386 similarity index 100% rename from acceptance/config/el6/config/nodes/pe/debian-7-i386 rename to acceptance/config/nodes/pe/debian-7-i386 diff --git a/acceptance/config/el6/config/nodes/pe/oracle-5-32ma-32da-32da b/acceptance/config/nodes/pe/oracle-5-32ma-32da-32da similarity index 100% rename from acceptance/config/el6/config/nodes/pe/oracle-5-32ma-32da-32da rename to acceptance/config/nodes/pe/oracle-5-32ma-32da-32da diff --git a/acceptance/config/el6/config/nodes/pe/oracle-5-32mda b/acceptance/config/nodes/pe/oracle-5-32mda similarity index 100% rename from acceptance/config/el6/config/nodes/pe/oracle-5-32mda rename to acceptance/config/nodes/pe/oracle-5-32mda diff --git a/acceptance/config/el6/config/nodes/pe/oracle-5-64ma-64da-64da b/acceptance/config/nodes/pe/oracle-5-64ma-64da-64da similarity index 100% rename from acceptance/config/el6/config/nodes/pe/oracle-5-64ma-64da-64da rename to acceptance/config/nodes/pe/oracle-5-64ma-64da-64da diff --git a/acceptance/config/el6/config/nodes/pe/oracle-5-64mda b/acceptance/config/nodes/pe/oracle-5-64mda similarity index 100% rename from acceptance/config/el6/config/nodes/pe/oracle-5-64mda rename to acceptance/config/nodes/pe/oracle-5-64mda diff --git a/acceptance/config/el6/config/nodes/pe/oracle-6-32ma-32da-32da b/acceptance/config/nodes/pe/oracle-6-32ma-32da-32da similarity index 100% rename from acceptance/config/el6/config/nodes/pe/oracle-6-32ma-32da-32da rename to acceptance/config/nodes/pe/oracle-6-32ma-32da-32da diff --git a/acceptance/config/el6/config/nodes/pe/oracle-6-32mda b/acceptance/config/nodes/pe/oracle-6-32mda similarity index 100% rename from acceptance/config/el6/config/nodes/pe/oracle-6-32mda rename to acceptance/config/nodes/pe/oracle-6-32mda diff --git a/acceptance/config/el6/config/nodes/pe/oracle-6-64ma-64da-64da b/acceptance/config/nodes/pe/oracle-6-64ma-64da-64da similarity index 100% rename from acceptance/config/el6/config/nodes/pe/oracle-6-64ma-64da-64da rename to acceptance/config/nodes/pe/oracle-6-64ma-64da-64da diff --git a/acceptance/config/el6/config/nodes/pe/oracle-6-64mda b/acceptance/config/nodes/pe/oracle-6-64mda similarity index 100% rename from acceptance/config/el6/config/nodes/pe/oracle-6-64mda rename to acceptance/config/nodes/pe/oracle-6-64mda diff --git a/acceptance/config/el6/config/nodes/pe/redhat-5-32ma-32da-32da b/acceptance/config/nodes/pe/redhat-5-32ma-32da-32da similarity index 100% rename from acceptance/config/el6/config/nodes/pe/redhat-5-32ma-32da-32da rename to acceptance/config/nodes/pe/redhat-5-32ma-32da-32da diff --git a/acceptance/config/el6/config/nodes/pe/redhat-5-32mda b/acceptance/config/nodes/pe/redhat-5-32mda similarity index 100% rename from acceptance/config/el6/config/nodes/pe/redhat-5-32mda rename to acceptance/config/nodes/pe/redhat-5-32mda diff --git a/acceptance/config/el6/config/nodes/pe/redhat-5-64ma-64da-64da b/acceptance/config/nodes/pe/redhat-5-64ma-64da-64da similarity index 100% rename from acceptance/config/el6/config/nodes/pe/redhat-5-64ma-64da-64da rename to acceptance/config/nodes/pe/redhat-5-64ma-64da-64da diff --git a/acceptance/config/el6/config/nodes/pe/redhat-5-64mda b/acceptance/config/nodes/pe/redhat-5-64mda similarity index 100% rename from acceptance/config/el6/config/nodes/pe/redhat-5-64mda rename to acceptance/config/nodes/pe/redhat-5-64mda diff --git a/acceptance/config/el6/config/nodes/pe/redhat-6-32ma-32da-32da b/acceptance/config/nodes/pe/redhat-6-32ma-32da-32da similarity index 100% rename from acceptance/config/el6/config/nodes/pe/redhat-6-32ma-32da-32da rename to acceptance/config/nodes/pe/redhat-6-32ma-32da-32da diff --git a/acceptance/config/el6/config/nodes/pe/redhat-6-32mda b/acceptance/config/nodes/pe/redhat-6-32mda similarity index 100% rename from acceptance/config/el6/config/nodes/pe/redhat-6-32mda rename to acceptance/config/nodes/pe/redhat-6-32mda diff --git a/acceptance/config/el6/config/nodes/pe/redhat-6-64ma-64da-64da b/acceptance/config/nodes/pe/redhat-6-64ma-64da-64da similarity index 100% rename from acceptance/config/el6/config/nodes/pe/redhat-6-64ma-64da-64da rename to acceptance/config/nodes/pe/redhat-6-64ma-64da-64da diff --git a/acceptance/config/el6/config/nodes/pe/redhat-6-64mda b/acceptance/config/nodes/pe/redhat-6-64mda similarity index 100% rename from acceptance/config/el6/config/nodes/pe/redhat-6-64mda rename to acceptance/config/nodes/pe/redhat-6-64mda diff --git a/acceptance/config/el6/config/nodes/pe/scientific-5-32ma-32da-32da b/acceptance/config/nodes/pe/scientific-5-32ma-32da-32da similarity index 100% rename from acceptance/config/el6/config/nodes/pe/scientific-5-32ma-32da-32da rename to acceptance/config/nodes/pe/scientific-5-32ma-32da-32da diff --git a/acceptance/config/el6/config/nodes/pe/scientific-5-32mda b/acceptance/config/nodes/pe/scientific-5-32mda similarity index 100% rename from acceptance/config/el6/config/nodes/pe/scientific-5-32mda rename to acceptance/config/nodes/pe/scientific-5-32mda diff --git a/acceptance/config/el6/config/nodes/pe/scientific-5-64ma-64da-64da b/acceptance/config/nodes/pe/scientific-5-64ma-64da-64da similarity index 100% rename from acceptance/config/el6/config/nodes/pe/scientific-5-64ma-64da-64da rename to acceptance/config/nodes/pe/scientific-5-64ma-64da-64da diff --git a/acceptance/config/el6/config/nodes/pe/scientific-5-64mda b/acceptance/config/nodes/pe/scientific-5-64mda similarity index 100% rename from acceptance/config/el6/config/nodes/pe/scientific-5-64mda rename to acceptance/config/nodes/pe/scientific-5-64mda diff --git a/acceptance/config/el6/config/nodes/pe/scientific-6-32ma-32da-32da b/acceptance/config/nodes/pe/scientific-6-32ma-32da-32da similarity index 100% rename from acceptance/config/el6/config/nodes/pe/scientific-6-32ma-32da-32da rename to acceptance/config/nodes/pe/scientific-6-32ma-32da-32da diff --git a/acceptance/config/el6/config/nodes/pe/scientific-6-32mda b/acceptance/config/nodes/pe/scientific-6-32mda similarity index 100% rename from acceptance/config/el6/config/nodes/pe/scientific-6-32mda rename to acceptance/config/nodes/pe/scientific-6-32mda diff --git a/acceptance/config/el6/config/nodes/pe/scientific-6-64ma-64da-64da b/acceptance/config/nodes/pe/scientific-6-64ma-64da-64da similarity index 100% rename from acceptance/config/el6/config/nodes/pe/scientific-6-64ma-64da-64da rename to acceptance/config/nodes/pe/scientific-6-64ma-64da-64da diff --git a/acceptance/config/el6/config/nodes/pe/scientific-6-64mda b/acceptance/config/nodes/pe/scientific-6-64mda similarity index 100% rename from acceptance/config/el6/config/nodes/pe/scientific-6-64mda rename to acceptance/config/nodes/pe/scientific-6-64mda diff --git a/acceptance/config/el6/config/nodes/pe/sles-11-32ma-32da-32da b/acceptance/config/nodes/pe/sles-11-32ma-32da-32da similarity index 100% rename from acceptance/config/el6/config/nodes/pe/sles-11-32ma-32da-32da rename to acceptance/config/nodes/pe/sles-11-32ma-32da-32da diff --git a/acceptance/config/el6/config/nodes/pe/sles-11-32mda b/acceptance/config/nodes/pe/sles-11-32mda similarity index 100% rename from acceptance/config/el6/config/nodes/pe/sles-11-32mda rename to acceptance/config/nodes/pe/sles-11-32mda diff --git a/acceptance/config/el6/config/nodes/pe/sles-11-64ma-64da-64da b/acceptance/config/nodes/pe/sles-11-64ma-64da-64da similarity index 100% rename from acceptance/config/el6/config/nodes/pe/sles-11-64ma-64da-64da rename to acceptance/config/nodes/pe/sles-11-64ma-64da-64da diff --git a/acceptance/config/el6/config/nodes/pe/sles-11-64mda b/acceptance/config/nodes/pe/sles-11-64mda similarity index 100% rename from acceptance/config/el6/config/nodes/pe/sles-11-64mda rename to acceptance/config/nodes/pe/sles-11-64mda diff --git a/acceptance/config/el6/config/nodes/pe/ubuntu-1004-32ma-32da-32da b/acceptance/config/nodes/pe/ubuntu-1004-32ma-32da-32da similarity index 100% rename from acceptance/config/el6/config/nodes/pe/ubuntu-1004-32ma-32da-32da rename to acceptance/config/nodes/pe/ubuntu-1004-32ma-32da-32da diff --git a/acceptance/config/el6/config/nodes/pe/ubuntu-1004-32mda b/acceptance/config/nodes/pe/ubuntu-1004-32mda similarity index 100% rename from acceptance/config/el6/config/nodes/pe/ubuntu-1004-32mda rename to acceptance/config/nodes/pe/ubuntu-1004-32mda diff --git a/acceptance/config/el6/config/nodes/pe/ubuntu-1004-64ma-64da-64da b/acceptance/config/nodes/pe/ubuntu-1004-64ma-64da-64da similarity index 100% rename from acceptance/config/el6/config/nodes/pe/ubuntu-1004-64ma-64da-64da rename to acceptance/config/nodes/pe/ubuntu-1004-64ma-64da-64da diff --git a/acceptance/config/el6/config/nodes/pe/ubuntu-1004-64mda b/acceptance/config/nodes/pe/ubuntu-1004-64mda similarity index 100% rename from acceptance/config/el6/config/nodes/pe/ubuntu-1004-64mda rename to acceptance/config/nodes/pe/ubuntu-1004-64mda diff --git a/acceptance/config/el6/config/nodes/pe/ubuntu-1204-32ma-32da-32da b/acceptance/config/nodes/pe/ubuntu-1204-32ma-32da-32da similarity index 100% rename from acceptance/config/el6/config/nodes/pe/ubuntu-1204-32ma-32da-32da rename to acceptance/config/nodes/pe/ubuntu-1204-32ma-32da-32da diff --git a/acceptance/config/el6/config/nodes/pe/ubuntu-1204-32mda b/acceptance/config/nodes/pe/ubuntu-1204-32mda similarity index 100% rename from acceptance/config/el6/config/nodes/pe/ubuntu-1204-32mda rename to acceptance/config/nodes/pe/ubuntu-1204-32mda diff --git a/acceptance/config/el6/config/nodes/pe/ubuntu-1204-64ma-64da-64da b/acceptance/config/nodes/pe/ubuntu-1204-64ma-64da-64da similarity index 100% rename from acceptance/config/el6/config/nodes/pe/ubuntu-1204-64ma-64da-64da rename to acceptance/config/nodes/pe/ubuntu-1204-64ma-64da-64da diff --git a/acceptance/config/el6/config/nodes/pe/ubuntu-1204-64mda b/acceptance/config/nodes/pe/ubuntu-1204-64mda similarity index 100% rename from acceptance/config/el6/config/nodes/pe/ubuntu-1204-64mda rename to acceptance/config/nodes/pe/ubuntu-1204-64mda diff --git a/acceptance/config/el6/config/nodes/precise.yaml b/acceptance/config/nodes/precise.yaml similarity index 100% rename from acceptance/config/el6/config/nodes/precise.yaml rename to acceptance/config/nodes/precise.yaml diff --git a/acceptance/config/el6/config/nodes/precise_and_lucid.yaml b/acceptance/config/nodes/precise_and_lucid.yaml similarity index 100% rename from acceptance/config/el6/config/nodes/precise_and_lucid.yaml rename to acceptance/config/nodes/precise_and_lucid.yaml diff --git a/acceptance/config/el6/config/nodes/rhel5.yaml b/acceptance/config/nodes/rhel5.yaml similarity index 100% rename from acceptance/config/el6/config/nodes/rhel5.yaml rename to acceptance/config/nodes/rhel5.yaml diff --git a/acceptance/config/el6/config/nodes/rhel6.yaml b/acceptance/config/nodes/rhel6.yaml similarity index 100% rename from acceptance/config/el6/config/nodes/rhel6.yaml rename to acceptance/config/nodes/rhel6.yaml diff --git a/acceptance/config/el6/config/nodes/rhel6_and_rhel5.yaml b/acceptance/config/nodes/rhel6_and_rhel5.yaml similarity index 100% rename from acceptance/config/el6/config/nodes/rhel6_and_rhel5.yaml rename to acceptance/config/nodes/rhel6_and_rhel5.yaml diff --git a/acceptance/config/el6/config/nodes/solaris10.yaml b/acceptance/config/nodes/solaris10.yaml similarity index 100% rename from acceptance/config/el6/config/nodes/solaris10.yaml rename to acceptance/config/nodes/solaris10.yaml diff --git a/acceptance/config/el6/config/nodes/solaris11.yaml b/acceptance/config/nodes/solaris11.yaml similarity index 100% rename from acceptance/config/el6/config/nodes/solaris11.yaml rename to acceptance/config/nodes/solaris11.yaml diff --git a/acceptance/config/el6/config/nodes/squeeze.yaml b/acceptance/config/nodes/squeeze.yaml similarity index 100% rename from acceptance/config/el6/config/nodes/squeeze.yaml rename to acceptance/config/nodes/squeeze.yaml diff --git a/acceptance/config/el6/config/nodes/standalone/rhel6-and-precise.yaml b/acceptance/config/nodes/standalone/rhel6-and-precise.yaml similarity index 100% rename from acceptance/config/el6/config/nodes/standalone/rhel6-and-precise.yaml rename to acceptance/config/nodes/standalone/rhel6-and-precise.yaml diff --git a/acceptance/config/el6/config/nodes/wheezy.yaml b/acceptance/config/nodes/wheezy.yaml similarity index 100% rename from acceptance/config/el6/config/nodes/wheezy.yaml rename to acceptance/config/nodes/wheezy.yaml diff --git a/acceptance/config/el6/config/nodes/win2003-all.yaml b/acceptance/config/nodes/win2003-all.yaml similarity index 100% rename from acceptance/config/el6/config/nodes/win2003-all.yaml rename to acceptance/config/nodes/win2003-all.yaml diff --git a/acceptance/config/el6/config/nodes/win2003.yaml b/acceptance/config/nodes/win2003.yaml similarity index 100% rename from acceptance/config/el6/config/nodes/win2003.yaml rename to acceptance/config/nodes/win2003.yaml diff --git a/acceptance/config/el6/config/nodes/win2003r2.yaml b/acceptance/config/nodes/win2003r2.yaml similarity index 100% rename from acceptance/config/el6/config/nodes/win2003r2.yaml rename to acceptance/config/nodes/win2003r2.yaml diff --git a/acceptance/config/el6/config/nodes/win2008-all.yaml b/acceptance/config/nodes/win2008-all.yaml similarity index 100% rename from acceptance/config/el6/config/nodes/win2008-all.yaml rename to acceptance/config/nodes/win2008-all.yaml diff --git a/acceptance/config/el6/config/nodes/win2008r2.yaml b/acceptance/config/nodes/win2008r2.yaml similarity index 100% rename from acceptance/config/el6/config/nodes/win2008r2.yaml rename to acceptance/config/nodes/win2008r2.yaml diff --git a/acceptance/config/el6/config/nodes/win2012-all.yaml b/acceptance/config/nodes/win2012-all.yaml similarity index 100% rename from acceptance/config/el6/config/nodes/win2012-all.yaml rename to acceptance/config/nodes/win2012-all.yaml diff --git a/acceptance/config/el6/config/nodes/windows-all.yaml b/acceptance/config/nodes/windows-all.yaml similarity index 100% rename from acceptance/config/el6/config/nodes/windows-all.yaml rename to acceptance/config/nodes/windows-all.yaml diff --git a/acceptance/config/el6/config/packages/options.rb b/acceptance/config/packages/options.rb similarity index 100% rename from acceptance/config/el6/config/packages/options.rb rename to acceptance/config/packages/options.rb diff --git a/acceptance/config/el6/setup/common/SetParser.rb b/acceptance/setup/common/SetParser.rb similarity index 100% rename from acceptance/config/el6/setup/common/SetParser.rb rename to acceptance/setup/common/SetParser.rb diff --git a/acceptance/config/el6/setup/common/StopFirewall.rb b/acceptance/setup/common/StopFirewall.rb similarity index 100% rename from acceptance/config/el6/setup/common/StopFirewall.rb rename to acceptance/setup/common/StopFirewall.rb diff --git a/acceptance/config/el6/setup/common/ValidateSignCert.rb b/acceptance/setup/common/ValidateSignCert.rb similarity index 100% rename from acceptance/config/el6/setup/common/ValidateSignCert.rb rename to acceptance/setup/common/ValidateSignCert.rb diff --git a/acceptance/config/el6/setup/git/pre-suite/00_EnvSetup.rb b/acceptance/setup/git/pre-suite/00_EnvSetup.rb similarity index 100% rename from acceptance/config/el6/setup/git/pre-suite/00_EnvSetup.rb rename to acceptance/setup/git/pre-suite/00_EnvSetup.rb diff --git a/acceptance/config/el6/setup/git/pre-suite/01_TestSetup.rb b/acceptance/setup/git/pre-suite/01_TestSetup.rb similarity index 100% rename from acceptance/config/el6/setup/git/pre-suite/01_TestSetup.rb rename to acceptance/setup/git/pre-suite/01_TestSetup.rb diff --git a/acceptance/config/el6/setup/git/pre-suite/02_PuppetUserAndGroup.rb b/acceptance/setup/git/pre-suite/02_PuppetUserAndGroup.rb similarity index 100% rename from acceptance/config/el6/setup/git/pre-suite/02_PuppetUserAndGroup.rb rename to acceptance/setup/git/pre-suite/02_PuppetUserAndGroup.rb diff --git a/acceptance/config/el6/setup/git/pre-suite/035_StopFirewall.rb b/acceptance/setup/git/pre-suite/035_StopFirewall.rb similarity index 100% rename from acceptance/config/el6/setup/git/pre-suite/035_StopFirewall.rb rename to acceptance/setup/git/pre-suite/035_StopFirewall.rb diff --git a/acceptance/config/el6/setup/git/pre-suite/03_PuppetMasterSanity.rb b/acceptance/setup/git/pre-suite/03_PuppetMasterSanity.rb similarity index 100% rename from acceptance/config/el6/setup/git/pre-suite/03_PuppetMasterSanity.rb rename to acceptance/setup/git/pre-suite/03_PuppetMasterSanity.rb diff --git a/acceptance/config/el6/setup/git/pre-suite/04_ValidateSignCert.rb b/acceptance/setup/git/pre-suite/04_ValidateSignCert.rb similarity index 100% rename from acceptance/config/el6/setup/git/pre-suite/04_ValidateSignCert.rb rename to acceptance/setup/git/pre-suite/04_ValidateSignCert.rb diff --git a/acceptance/config/el6/setup/git/pre-suite/05_HieraSetup.rb b/acceptance/setup/git/pre-suite/05_HieraSetup.rb similarity index 100% rename from acceptance/config/el6/setup/git/pre-suite/05_HieraSetup.rb rename to acceptance/setup/git/pre-suite/05_HieraSetup.rb diff --git a/acceptance/config/el6/setup/git/pre-suite/06_InstallModules.rb b/acceptance/setup/git/pre-suite/06_InstallModules.rb similarity index 100% rename from acceptance/config/el6/setup/git/pre-suite/06_InstallModules.rb rename to acceptance/setup/git/pre-suite/06_InstallModules.rb diff --git a/acceptance/config/el6/setup/git/pre-suite/07_InstallCACerts.rb b/acceptance/setup/git/pre-suite/07_InstallCACerts.rb similarity index 100% rename from acceptance/config/el6/setup/git/pre-suite/07_InstallCACerts.rb rename to acceptance/setup/git/pre-suite/07_InstallCACerts.rb diff --git a/acceptance/config/el6/setup/git/pre-suite/10_SetParser.rb b/acceptance/setup/git/pre-suite/10_SetParser.rb similarity index 100% rename from acceptance/config/el6/setup/git/pre-suite/10_SetParser.rb rename to acceptance/setup/git/pre-suite/10_SetParser.rb diff --git a/acceptance/config/el6/setup/packages/pre-suite/01_Install.rb b/acceptance/setup/packages/pre-suite/01_Install.rb similarity index 100% rename from acceptance/config/el6/setup/packages/pre-suite/01_Install.rb rename to acceptance/setup/packages/pre-suite/01_Install.rb diff --git a/acceptance/config/el6/setup/packages/pre-suite/02_StopFirewall.rb b/acceptance/setup/packages/pre-suite/02_StopFirewall.rb similarity index 100% rename from acceptance/config/el6/setup/packages/pre-suite/02_StopFirewall.rb rename to acceptance/setup/packages/pre-suite/02_StopFirewall.rb diff --git a/acceptance/config/el6/setup/packages/pre-suite/04_ValidateSignCert.rb b/acceptance/setup/packages/pre-suite/04_ValidateSignCert.rb similarity index 100% rename from acceptance/config/el6/setup/packages/pre-suite/04_ValidateSignCert.rb rename to acceptance/setup/packages/pre-suite/04_ValidateSignCert.rb diff --git a/acceptance/config/el6/setup/packages/pre-suite/10_SetParser.rb b/acceptance/setup/packages/pre-suite/10_SetParser.rb similarity index 100% rename from acceptance/config/el6/setup/packages/pre-suite/10_SetParser.rb rename to acceptance/setup/packages/pre-suite/10_SetParser.rb diff --git a/acceptance/config/el6/setup/pe/pre-suite/00_install.rb b/acceptance/setup/pe/pre-suite/00_install.rb similarity index 100% rename from acceptance/config/el6/setup/pe/pre-suite/00_install.rb rename to acceptance/setup/pe/pre-suite/00_install.rb diff --git a/acceptance/config/el6/setup/rsync/pre-suite/00_PurgeAndReinstall.rb b/acceptance/setup/rsync/pre-suite/00_PurgeAndReinstall.rb similarity index 100% rename from acceptance/config/el6/setup/rsync/pre-suite/00_PurgeAndReinstall.rb rename to acceptance/setup/rsync/pre-suite/00_PurgeAndReinstall.rb diff --git a/acceptance/config/el6/setup/rsync/pre-suite/01_RsyncSource.rb b/acceptance/setup/rsync/pre-suite/01_RsyncSource.rb similarity index 91% rename from acceptance/config/el6/setup/rsync/pre-suite/01_RsyncSource.rb rename to acceptance/setup/rsync/pre-suite/01_RsyncSource.rb index c903fd745..1bfbda871 100644 --- a/acceptance/config/el6/setup/rsync/pre-suite/01_RsyncSource.rb +++ b/acceptance/setup/rsync/pre-suite/01_RsyncSource.rb @@ -14,7 +14,7 @@ test_name "Rsync Source" do else raise "We should actually do some #{host['platform']} platform specific rsyncing here..." end - cmd = "rsync -r --exclude '.*.swp' #{filter_opt} --size-only -i -e'ssh -i id_rsa-acceptance' ../../../lib/* root@#{host}:#{destination_dir}" + cmd = "rsync -r --exclude '.*.swp' #{filter_opt} --size-only -i -e'ssh -i id_rsa-acceptance' ../lib/* root@#{host}:#{destination_dir}" puts "RSYNC: #{cmd}" result = `#{cmd}` raise("Failed rsync execution:\n#{result}") if $? != 0 From 4ce17f781b28ee12886c1ef68bd8bc7dcea4d71a Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Fri, 13 Dec 2013 13:02:13 -0800 Subject: [PATCH 207/800] (maint) Pin acceptance to beaker ~> 1.2 --- acceptance/Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance/Gemfile b/acceptance/Gemfile index 6a5f4e971..fb817b197 100644 --- a/acceptance/Gemfile +++ b/acceptance/Gemfile @@ -1,6 +1,6 @@ source ENV['GEM_SOURCE'] || "https://rubygems.org" -gem "beaker", :git => "git://github.com/puppetlabs/beaker.git" +gem "beaker", "~> 1.2" gem "rake" group(:test) do From 6ba2acb256c264c49c1bcba7e5e57ba0775afe9a Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Fri, 13 Dec 2013 13:46:25 -0800 Subject: [PATCH 208/800] (maint) Script package pipeline submission for Jenkins --- acceptance/bin/ci-package.sh | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100755 acceptance/bin/ci-package.sh diff --git a/acceptance/bin/ci-package.sh b/acceptance/bin/ci-package.sh new file mode 100755 index 000000000..44a2c24a5 --- /dev/null +++ b/acceptance/bin/ci-package.sh @@ -0,0 +1,16 @@ +#! /usr/bin/env bash + +set -e +set -x + +JOB_NAME=$1 +[[ (-z "$JOB_NAME") ]] && echo "No job name passed in" && exit 1 + +rake --trace package:implode +rake --trace package:bootstrap + +# This obtains either the sha or tag if the commit is tagged +REF=`rake pl:print_build_params |grep "^ref: " |cut -d ":" -f 2 | tr -d ' '` +rake --trace pl:jenkins:uber_build DOWNSTREAM_JOB="http://jenkins-foss.delivery.puppetlabs.net/job/$JOB_NAME/buildWithParameters?token=iheartjenkins&SHA=$REF&BUILD_SELECTOR=$BUILD_NUMBER&FORK=$GIT_FORK" + +rake ci:acceptance_artifacts SHA=$REF From 2179b4a8c62a1bf4f5b1f32ed7d690c025af64e9 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Fri, 13 Dec 2013 14:11:42 -0800 Subject: [PATCH 209/800] (maint) Disable repo_proxy when running solaris acceptance By pulling this platform specific test into the bootstrap script we can remove it from the Solaris job scripts in Jenkins. --- acceptance/bin/ci-bootstrap-from-artifacts.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/acceptance/bin/ci-bootstrap-from-artifacts.sh b/acceptance/bin/ci-bootstrap-from-artifacts.sh index ee40076dc..681144d5d 100755 --- a/acceptance/bin/ci-bootstrap-from-artifacts.sh +++ b/acceptance/bin/ci-bootstrap-from-artifacts.sh @@ -13,6 +13,7 @@ set -x echo "SHA: ${SHA}" +echo "FORK: ${FORK}" echo "BUILD_SELECTOR: ${BUILD_SELECTOR}" echo "PACKAGE_BUILD_STATUS: ${PACKAGE_BUILD_STATUS}" @@ -27,12 +28,17 @@ cat creator.txt cd config/el6 bundle install --without=development --path=.bundle/gems +if [[ "${platform}" =~ 'solaris' ]]; then + repo_proxy=" :repo_proxy => false,\n" +fi + cat > local_options.rb <<-EOF { :hosts_file => 'config/nodes/${platform}.yaml', :ssh => { :keys => ["${HOME}/.ssh/id_rsa-old.private"], }, +${repo_proxy} } EOF From 9ce3dcaf0b6eeaa5896245d0e1d84dade858145e Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 14 Dec 2013 01:14:56 +0100 Subject: [PATCH 210/800] (PUP-490) Remove comment that mentions "import" from transform_calls --- lib/puppet/pops/model/factory.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/pops/model/factory.rb b/lib/puppet/pops/model/factory.rb index b671a6dfb..716c031f2 100644 --- a/lib/puppet/pops/model/factory.rb +++ b/lib/puppet/pops/model/factory.rb @@ -683,7 +683,7 @@ class Puppet::Pops::Model::Factory end # Transforms an array of expressions containing literal name expressions to calls if followed by an - # expression, or expression list. Also transforms a "call" to `import` into an ImportExpression. + # expression, or expression list. # def self.transform_calls(expressions) expressions.reduce([]) do |memo, expr| From 69d4f568cc86da9cecb7c7da10a8985db97c609c Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 14 Dec 2013 01:20:05 +0100 Subject: [PATCH 211/800] (PUP-490) Remove dead code after removal of ImportExpression The AstTransformer's transform_ImportExpression will never be called since the expression was removed from the model. This removes the transformation as well. --- lib/puppet/pops/model/ast_transformer.rb | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/lib/puppet/pops/model/ast_transformer.rb b/lib/puppet/pops/model/ast_transformer.rb index 1a64b5195..f1d270c04 100644 --- a/lib/puppet/pops/model/ast_transformer.rb +++ b/lib/puppet/pops/model/ast_transformer.rb @@ -269,25 +269,6 @@ class Puppet::Pops::Model::AstTransformer ast o, AST::InOperator, :lval => transform(o.left_expr), :rval => transform(o.right_expr) end - # This is a complex transformation from a modeled import to a Nop result (where the import took place), - # and calls to perform import/parsing etc. during the transformation. - # When testing syntax, the @importer does not have to be set, but it is not possible to check - # the actual import without inventing a new AST::ImportExpression with nop effect when evaluating. - def transform_ImportExpression(o) - if importer - o.files.each {|f| - unless f.is_a? Model::LiteralString - raise "Illegal import file expression. Must be a single quoted string" - end - importer.import(f.value) - } - end - # Crazy stuff - # Transformation of "import" needs to parse the other files at the time of transformation. - # Then produce a :nop, since nothing should be evaluated. - ast o, AST::Nop, {} - end - # Assignment in AST 3.1 is to variable or hasharray accesses !!! See Bug #16116 def transform_AssignmentExpression(o) args = {:value => transform(o.right_expr) } From 32197e70c7d9d213db24d20aee68530ecb8b13c6 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 14 Dec 2013 01:29:11 +0100 Subject: [PATCH 212/800] (maint) Fix typo in example title --- spec/unit/pops/evaluator/evaluating_parser_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index 804a65403..6e3066372 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -839,7 +839,7 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do scope.compiler.should have_relationship(['File', 'b', '->', 'File', 'y']) end - it 'should tolerate (eliminiate) duplicates in operands' do + it 'should tolerate (eliminate) duplicates in operands' do source = "[File[a], File[a]] -> File[x]" parser.evaluate_string(scope, source, __FILE__) scope.compiler.should have_relationship(['File', 'a', '->', 'File', 'x']) From ce6b415ac2ab744a653ee34e98ab7bb444e21309 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 13 Dec 2013 16:40:44 -0800 Subject: [PATCH 213/800] (#23373) Check for minimal use of [main] When inserting settings into [main], there isn't any need to add a section header. --- spec/unit/settings/ini_file_spec.rb | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/spec/unit/settings/ini_file_spec.rb b/spec/unit/settings/ini_file_spec.rb index a0c3edfa6..11e698a52 100644 --- a/spec/unit/settings/ini_file_spec.rb +++ b/spec/unit/settings/ini_file_spec.rb @@ -27,6 +27,24 @@ describe Puppet::Settings::IniFile do expect(config_fh.string).to eq "[the_section]\nname = value\n" end + it "does not add a [main] section to a file when it isn't needed" do + config_fh = a_config_file_containing(<<-CONF) + [section] + name = different value + CONF + + + Puppet::Settings::IniFile.update(config_fh) do |config| + config.set("main", "name", "value") + end + + expect(config_fh.string).to eq(<<-CONF) +name = value + [section] + name = different value + CONF + end + it "preserves comments when writing a new name and value" do config_fh = a_config_file_containing("# this is a comment") From 2479210319b0947f3ac395e384c69370d46b8d50 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Fri, 13 Dec 2013 16:50:57 -0800 Subject: [PATCH 214/800] (maint) Don't cd to nonexistent el6 directory Acceptance harness has moved to acceptance/ from acceptance/config/el6, but this path was left behind in the script. --- acceptance/bin/ci-bootstrap-from-artifacts.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/acceptance/bin/ci-bootstrap-from-artifacts.sh b/acceptance/bin/ci-bootstrap-from-artifacts.sh index 681144d5d..dd64d0286 100755 --- a/acceptance/bin/ci-bootstrap-from-artifacts.sh +++ b/acceptance/bin/ci-bootstrap-from-artifacts.sh @@ -25,7 +25,6 @@ tar -xzvf ../acceptance-artifacts.tar.gz echo "===== This artifact is from =====" cat creator.txt -cd config/el6 bundle install --without=development --path=.bundle/gems if [[ "${platform}" =~ 'solaris' ]]; then From 07abcba280b4edc19d304dbdeeabbe1525716755 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 14 Dec 2013 02:05:01 +0100 Subject: [PATCH 215/800] (PUP-954) Remove commented out debugger breakpoint --- lib/puppet/pops/evaluator/access_operator.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/puppet/pops/evaluator/access_operator.rb b/lib/puppet/pops/evaluator/access_operator.rb index 6ef7dbcb9..df683b3a5 100644 --- a/lib/puppet/pops/evaluator/access_operator.rb +++ b/lib/puppet/pops/evaluator/access_operator.rb @@ -205,7 +205,6 @@ class Puppet::Pops::Evaluator::AccessOperator def bad_type_specialization_key_type(type, key_index, actual, *expected_classes) label_provider = Puppet::Pops::Model::ModelLabelProvider.new() - # require 'debugger'; debugger expected = expected_classes.map {|c| label_provider.label(c) }.join(' or ') fail(Puppet::Pops::Issues::BAD_TYPE_SPECIALIZATION, @semantic.keys[key_index], { :type => type, From 6f2c8338cd8c51ad0b8e81eec309a853d33ba5d3 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 13 Dec 2013 17:39:31 -0800 Subject: [PATCH 216/800] (#23373) Don't set metadata when it isn't supported Not all settings have metadata, this prevents the settings code from trying to set it on ones that don't support it. --- lib/puppet/settings.rb | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/puppet/settings.rb b/lib/puppet/settings.rb index 90d3003c3..cf2f99cff 100644 --- a/lib/puppet/settings.rb +++ b/lib/puppet/settings.rb @@ -510,11 +510,7 @@ class Puppet::Settings # And now we can repopulate with the values from our last parsing of the config files. @configuration_file = data data.sections.each do |name, section| - section.settings.each do |setting| - if setting.has_metadata? && type = @config[setting.name] - type.set_meta(setting.meta) - end - end + apply_metadata_from_section(section) end # Determine our environment, if we have one. @@ -605,15 +601,21 @@ class Puppet::Settings searchpath.reverse.each do |source| source = preferred_run_mode if source == :run_mode if section = @configuration_file.sections[source] - section.settings.each do |setting| - @config[setting.name].set_meta(setting.meta) - end + apply_metadata_from_section(section) end end end end private :apply_metadata + def apply_metadata_from_section(section) + section.settings.each do |setting| + if setting.has_metadata? && type = @config[setting.name] + type.set_meta(setting.meta) + end + end + end + SETTING_TYPES = { :string => StringSetting, :file => FileSetting, From 53a4203f728ed5d79cc5c32bcd9ec057e777f806 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 13 Dec 2013 19:00:34 -0800 Subject: [PATCH 217/800] (#23373) Use puppet config set in tests This uses puppet config set to modify the puppet.conf in a test rather than writing the puppet.conf file in place. --- acceptance/tests/face/loadable_from_modules.rb | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/acceptance/tests/face/loadable_from_modules.rb b/acceptance/tests/face/loadable_from_modules.rb index 9acb3946e..272d84c26 100644 --- a/acceptance/tests/face/loadable_from_modules.rb +++ b/acceptance/tests/face/loadable_from_modules.rb @@ -12,15 +12,10 @@ agents.each do |agent| user_modulepath = get_test_file_path(agent, 'user/modules') # make sure that we use the modulepath from the dev environment - create_test_file(agent, 'puppet.conf', <<"END") -[user] -environment=dev -modulepath=#{user_modulepath} - -[dev] -modulepath=#{dev_modulepath} -END puppetconf = get_test_file_path(agent, 'puppet.conf') + on agent, puppet("config", "set", "environment", "dev", "--section", "user", "--config", puppetconf) + on agent, puppet("config", "set", "modulepath", user_modulepath, "--section", "user", "--config", puppetconf) + on agent, puppet("config", "set", "modulepath", dev_modulepath, "--section", "user", "--config", puppetconf) on agent, 'rm -rf puppetlabs-helloworld' on agent, puppet("module", "generate", "puppetlabs-helloworld") From 341bb399e0125f732bae39b10ac4f852ddd43e2a Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 14 Dec 2013 01:12:16 +0100 Subject: [PATCH 218/800] (PUP-865) Add ability to parse a directory of manifests This adds the ability to run a directory of manifests instead of just a single file. Simply assign a directory to Puppet[:manifest] via settings or from the command line. The files are parsed and combined in alphabetical order as if the files were to be concatenated into a single file. The support is in environment, and it is only support for the initial import. This also means that both current and future parser can be used (i.e. the support for this is not in the parser). Testing is done in integration/node/environment_spec. --- lib/puppet/node/environment.rb | 17 +++++-- lib/puppet/parser/ast/pops_bridge.rb | 8 +++ lib/puppet/parser/code_merger.rb | 12 +++++ lib/puppet/parser/parser_factory.rb | 13 +++++ lib/puppet/parser/parser_support.rb | 2 +- lib/puppet/pops/parser/code_merger.rb | 16 ++++++ .../node/environment/sitedir/00_a.pp | 2 + .../node/environment/sitedir/01_b.pp | 2 + .../node/environment/sitedir/03_empty.pp | 0 spec/integration/node/environment_spec.rb | 51 +++++++++++++++++++ spec/unit/node/environment_spec.rb | 2 + 11 files changed, 121 insertions(+), 4 deletions(-) create mode 100644 lib/puppet/parser/code_merger.rb create mode 100644 lib/puppet/pops/parser/code_merger.rb create mode 100644 spec/fixtures/integration/node/environment/sitedir/00_a.pp create mode 100644 spec/fixtures/integration/node/environment/sitedir/01_b.pp create mode 100644 spec/fixtures/integration/node/environment/sitedir/03_empty.pp diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb index f79b8b35b..07e263316 100644 --- a/lib/puppet/node/environment.rb +++ b/lib/puppet/node/environment.rb @@ -455,15 +455,26 @@ class Puppet::Node::Environment # representing the 'main' hostclass def perform_initial_import return empty_parse_result if Puppet.settings[:ignoreimport] -# parser = Puppet::Parser::Parser.new(self) parser = Puppet::Parser::ParserFactory.parser(self) if code = Puppet.settings.uninterpolated_value(:code, name.to_s) and code != "" parser.string = code + parser.parse else file = Puppet.settings.value(:manifest, name.to_s) - parser.file = file + # if the manifest file is a reference to a directory, parse and combine all .pp files in that + # directory + if File.directory?(file) + parse_results = Dir.entries(file).find_all { |f| f =~ /\.pp$/ }.sort.map do |pp_file| + parser.file = File.join(file, pp_file) + parser.parse + end + # Use a parser type specific merger to concatenate the results + Puppet::Parser::ParserFactory.code_merger.concatenate(parse_results) + else + parser.file = file + parser.parse + end end - parser.parse rescue => detail known_resource_types.parse_failed = true diff --git a/lib/puppet/parser/ast/pops_bridge.rb b/lib/puppet/parser/ast/pops_bridge.rb index 7b697b031..8eaab172d 100644 --- a/lib/puppet/parser/ast/pops_bridge.rb +++ b/lib/puppet/parser/ast/pops_bridge.rb @@ -45,6 +45,14 @@ class Puppet::Parser::AST::PopsBridge # Puppet::Parser::AST::BlockExpression.new(:children => [self] + other.children) end end + + # The 3x requires code plugged in to an AST to have this in certain positions in the tree. The purpose + # is to either print the content, or to look for things that needs to be defined. This implementation + # cheats by always returning an empty array. (This allows simple files to not require a "Program" at the top. + # + def children + [] + end end # Bridges the top level "Program" produced by the pops parser. diff --git a/lib/puppet/parser/code_merger.rb b/lib/puppet/parser/code_merger.rb new file mode 100644 index 000000000..b27f6c317 --- /dev/null +++ b/lib/puppet/parser/code_merger.rb @@ -0,0 +1,12 @@ + +class Puppet::Parser::CodeMerger + + # Concatenates the logic in the array of parse results into one parse result + def concatenate(parse_results) + children = parse_results.select {|x| !x.nil? && x.code}.reduce([]) do |memo, parsed_class| + memo + parsed_class.code.children + end + main = Puppet::Parser::AST::BlockExpression.new(:children => children) + Puppet::Parser::AST::Hostclass.new('', :code => main) + end +end \ No newline at end of file diff --git a/lib/puppet/parser/parser_factory.rb b/lib/puppet/parser/parser_factory.rb index 7e76bf652..3e5ad81e8 100644 --- a/lib/puppet/parser/parser_factory.rb +++ b/lib/puppet/parser/parser_factory.rb @@ -22,6 +22,8 @@ module Puppet::Parser # def self.classic_parser(environment) require 'puppet/parser' + require 'puppet/parser/code_merger' + Puppet::Parser::Parser.new(environment) end @@ -32,6 +34,7 @@ module Puppet::Parser assert_rgen_installed() unless @@asserted @@asserted = true require 'puppet/parser/e4_parser_adapter' + require 'puppet/pops/parser/code_merger' E4ParserAdapter.new() end @@ -69,6 +72,16 @@ module Puppet::Parser raise Puppet::DevError.new("The gem 'rgen' version >= 0.6.1 is required when using '--parser future'. An older version is installed, please update.") end end + + def self.code_merger + case Puppet[:parser] + when 'future' + Puppet::Pops::Parser::CodeMerger.new + else + Puppet::Parser::CodeMerger.new + end + end + end end diff --git a/lib/puppet/parser/parser_support.rb b/lib/puppet/parser/parser_support.rb index b67f3c752..f8a2c1686 100644 --- a/lib/puppet/parser/parser_support.rb +++ b/lib/puppet/parser/parser_support.rb @@ -87,7 +87,7 @@ class Puppet::Parser::Parser file = file + ".pp" end end - raise Puppet::AlreadyImportedError, "Import loop detected" if known_resource_types.watching_file?(file) + raise Puppet::AlreadyImportedError, "Import loop detected for #{file}" if known_resource_types.watching_file?(file) watch_file(file) @lexer.file = file diff --git a/lib/puppet/pops/parser/code_merger.rb b/lib/puppet/pops/parser/code_merger.rb new file mode 100644 index 000000000..d49244279 --- /dev/null +++ b/lib/puppet/pops/parser/code_merger.rb @@ -0,0 +1,16 @@ + +class Puppet::Pops::Parser::CodeMerger + + # Concatenates the logic in the array of parse results into one parse result + def concatenate(parse_results) + # this is a bit brute force as the result is already 3x ast with wrapped 4x content + # this could be combined in a more elegant way, but it is only used to process a handful of files + # at the beginning of a puppet run. TODO: Revisit for Puppet 4x when there is no 3x ast at the top. + # + children = parse_results.select {|x| !x.nil? && x.code}.reduce([]) do |memo, parsed_class| + memo << parsed_class.code + end + main = Puppet::Parser::AST::BlockExpression.new(:children => children) + Puppet::Parser::AST::Hostclass.new('', :code => main) + end +end \ No newline at end of file diff --git a/spec/fixtures/integration/node/environment/sitedir/00_a.pp b/spec/fixtures/integration/node/environment/sitedir/00_a.pp new file mode 100644 index 000000000..508992fc3 --- /dev/null +++ b/spec/fixtures/integration/node/environment/sitedir/00_a.pp @@ -0,0 +1,2 @@ +class a {} +$a = 10 \ No newline at end of file diff --git a/spec/fixtures/integration/node/environment/sitedir/01_b.pp b/spec/fixtures/integration/node/environment/sitedir/01_b.pp new file mode 100644 index 000000000..4dce5eb83 --- /dev/null +++ b/spec/fixtures/integration/node/environment/sitedir/01_b.pp @@ -0,0 +1,2 @@ +class b {} +$b = $a # error if $a not set in strict mode \ No newline at end of file diff --git a/spec/fixtures/integration/node/environment/sitedir/03_empty.pp b/spec/fixtures/integration/node/environment/sitedir/03_empty.pp new file mode 100644 index 000000000..e69de29bb diff --git a/spec/integration/node/environment_spec.rb b/spec/integration/node/environment_spec.rb index f41a5988a..0c6336cc6 100755 --- a/spec/integration/node/environment_spec.rb +++ b/spec/integration/node/environment_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' require 'puppet_spec/files' +require 'puppet_spec/scope' describe Puppet::Node::Environment do include PuppetSpec::Files @@ -54,4 +55,54 @@ describe Puppet::Node::Environment do mods.length.should == 1 mods[0].path.should == File.join(base, "dir1", "mod") end + + shared_examples_for 'the environment' do + include PuppetSpec::Scope + + let(:node) { Puppet::Node.new('testnode') } + + it "a manifest referring to a directory invokes parsing of all its files in sorted order" do + # fixture has three files 00_a.pp, 01_b.pp, and 02_c.pp. The 'b' file depends on 'a' + # being evaluated first. The 'c' file is empty (to ensure empty things do not break the directory import). + # if the files are evaluated in the wrong order, the file 'b' has a reference to $a (set in file 'a') + # and with strict variable lookup should raise an error and fail this test. + # + dirname = my_fixture('sitedir') + # Set the manifest to the directory to make it parse and combine them when compiling + Puppet[:manifest] = dirname + + # include the classes that are in the fixture files + node.stubs(:classes).returns(['a', 'b']) + + # compile, to make the initial_import in the environment take place the correct way + catalog = Puppet::Parser::Compiler.compile(node) + class_a = catalog.resource('Class[a]') + class_b = catalog.resource('Class[b]') + expect(class_a).to_not be_nil + expect(class_b).to_not be_nil + end + end + + describe 'using classic parser' do + before :each do + Puppet[:parser] = 'current' + # fixture uses variables that are set in a particular order (this ensures that files are parsed + # and combined in the right order or an error will be raised if 'b' is evaluated before 'a'). + Puppet[:strict_variables] = true + end + it_behaves_like 'the environment' do + end + end + describe 'using future parser' do + before :each do + Puppet[:parser] = 'future' + # Turned off because currently future parser turns on the binder which causes lookup of facts + # that are uninitialized and it will fail with errors for 'osfamily' etc. + # This can be turned back on when the binder is taken out of the equation. + # Puppet[:strict_variables] = true + end + it_behaves_like 'the environment' do + end +end + end diff --git a/spec/unit/node/environment_spec.rb b/spec/unit/node/environment_spec.rb index be8ae1a94..81264a389 100755 --- a/spec/unit/node/environment_spec.rb +++ b/spec/unit/node/environment_spec.rb @@ -439,12 +439,14 @@ describe Puppet::Node::Environment do end end end + describe 'with classic parser' do before :each do Puppet[:parser] = 'current' end it_behaves_like 'the environment' end + describe 'with future parser' do before :each do Puppet[:parser] = 'future' From 9cde175466985bb342909e584f6d2b9696fd08e8 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 14 Dec 2013 04:25:55 +0100 Subject: [PATCH 219/800] (PUP-865) Change name of shared test group to one not already claimed --- spec/integration/node/environment_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/integration/node/environment_spec.rb b/spec/integration/node/environment_spec.rb index 0c6336cc6..c7d9c91a1 100755 --- a/spec/integration/node/environment_spec.rb +++ b/spec/integration/node/environment_spec.rb @@ -56,7 +56,7 @@ describe Puppet::Node::Environment do mods[0].path.should == File.join(base, "dir1", "mod") end - shared_examples_for 'the environment' do + shared_examples_for "the environment's initial import" do include PuppetSpec::Scope let(:node) { Puppet::Node.new('testnode') } @@ -90,7 +90,7 @@ describe Puppet::Node::Environment do # and combined in the right order or an error will be raised if 'b' is evaluated before 'a'). Puppet[:strict_variables] = true end - it_behaves_like 'the environment' do + it_behaves_like "the environment's initial import" do end end describe 'using future parser' do @@ -101,7 +101,7 @@ describe Puppet::Node::Environment do # This can be turned back on when the binder is taken out of the equation. # Puppet[:strict_variables] = true end - it_behaves_like 'the environment' do + it_behaves_like "the environment's initial import" do end end From 0dacd1e323ba1364093b045a7072594d1c85a8b2 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 14 Dec 2013 17:14:52 +0100 Subject: [PATCH 220/800] (PUP-865) Update defaults text for :manifest to show use of directory --- lib/puppet/defaults.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index 5f716f163..cec555b27 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -840,7 +840,8 @@ EOT :manifest => { :default => "$manifestdir/site.pp", :type => :file, - :desc => "The entry-point manifest for puppet master.", + :desc => "The entry-point manifest file for puppet master or a directory of manifests + to be evaluated in alphabetical order.", }, :code => { :default => "", From 3bba6db9dd8e6320c8b6a713036bce4bb4a50f4d Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Sun, 15 Dec 2013 15:09:11 -0800 Subject: [PATCH 221/800] (maint) Remove an escaped return char that was breaking solaris options Unnecessary return char was causing Ruby parsing of local_options.rb to fail on solaris runs. --- acceptance/bin/ci-bootstrap-from-artifacts.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance/bin/ci-bootstrap-from-artifacts.sh b/acceptance/bin/ci-bootstrap-from-artifacts.sh index dd64d0286..ece0f34aa 100755 --- a/acceptance/bin/ci-bootstrap-from-artifacts.sh +++ b/acceptance/bin/ci-bootstrap-from-artifacts.sh @@ -28,7 +28,7 @@ cat creator.txt bundle install --without=development --path=.bundle/gems if [[ "${platform}" =~ 'solaris' ]]; then - repo_proxy=" :repo_proxy => false,\n" + repo_proxy=" :repo_proxy => false," fi cat > local_options.rb <<-EOF From 32bae2d969771c23b918d3e7f37b474b3829fbb5 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 16 Dec 2013 00:53:01 +0100 Subject: [PATCH 222/800] (PUP-864) Add deprecation warning for mutation of array/hash This adds a deprecation warning to operations that mutates array and hash - i.e. $a = [1,2,4] $a[3] = 5, and equivalent for hash. The warning message has a URL that leads to PUP-864. Existing tests that test that mutation is possible now also checks that a warning is issued. --- lib/puppet/parser/ast/leaf.rb | 15 +++++++++++++++ spec/unit/parser/ast/leaf_spec.rb | 2 ++ 2 files changed, 17 insertions(+) diff --git a/lib/puppet/parser/ast/leaf.rb b/lib/puppet/parser/ast/leaf.rb index af9f792cc..b8049afbf 100644 --- a/lib/puppet/parser/ast/leaf.rb +++ b/lib/puppet/parser/ast/leaf.rb @@ -169,6 +169,8 @@ class Puppet::Parser::AST raise Puppet::ParseError, "Assigning to the hash '#{variable}' with an existing key '#{accesskey}' is forbidden" end + mutation_deprecation() + # assign to hash or array object[array_index_or_key(object, accesskey)] = value end @@ -176,6 +178,19 @@ class Puppet::Parser::AST def to_s "\$#{variable.to_s}[#{key.to_s}]" end + + def mutation_deprecation + deprecation_location_text = + if file && line + " at #{file}:#{line}" + elsif file + " in file #{file}" + elsif line + " at #{line}" + end + Puppet.warning(["The use of mutating operations on Array/Hash is deprecated#{deprecation_location_text}.", + " See http://links.puppetlabs.com/puppet-mutation-deprecation"].join('')) + end end class Regex < AST::Leaf diff --git a/spec/unit/parser/ast/leaf_spec.rb b/spec/unit/parser/ast/leaf_spec.rb index bee9fc2bd..9cbbbf978 100755 --- a/spec/unit/parser/ast/leaf_spec.rb +++ b/spec/unit/parser/ast/leaf_spec.rb @@ -280,6 +280,7 @@ describe Puppet::Parser::AST::HashOrArrayAccess do describe "when assigning" do it "should add a new key and value" do + Puppet.expects(:warning).once node = Puppet::Node.new('localhost') compiler = Puppet::Parser::Compiler.new(node) scope = Puppet::Parser::Scope.new(compiler) @@ -301,6 +302,7 @@ describe Puppet::Parser::AST::HashOrArrayAccess do end it "should be able to return an array member when index is a stringified number" do + Puppet.expects(:warning).once node = Puppet::Node.new('localhost') compiler = Puppet::Parser::Compiler.new(node) scope = Puppet::Parser::Scope.new(compiler) From 08e490a428acba8f10d81b5f3e09067b8bf0d7cf Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 16 Dec 2013 03:09:44 +0100 Subject: [PATCH 223/800] (PUP-939) Add ability to filter an enumerable type. This adds the ability to filter the sequence of elements produced by an enumerable type (just like for array and hash). --- lib/puppet/parser/functions/filter.rb | 59 ++++++++++++++++++++++--- spec/unit/parser/methods/filter_spec.rb | 26 +++++++++++ 2 files changed, 80 insertions(+), 5 deletions(-) diff --git a/lib/puppet/parser/functions/filter.rb b/lib/puppet/parser/functions/filter.rb index ca17a386d..a04a86f99 100644 --- a/lib/puppet/parser/functions/filter.rb +++ b/lib/puppet/parser/functions/filter.rb @@ -6,11 +6,11 @@ Puppet::Parser::Functions::newfunction( :arity => 2, :doc => <<-'ENDHEREDOC') do |args| Applies a parameterized block to each element in a sequence of entries from the first - argument and returns an array or hash (same type as left operand) - with the entries for which the block evaluates to true. + argument and returns an array or hash (same type as left operand for array/hash, and array for + other enumerable types) with the entries for which the block evaluates to true. - This function takes two mandatory arguments: the first should be an Array or a Hash, and the second - a parameterized block as produced by the puppet syntax: + This function takes two mandatory arguments: the first should be an Array, a Hash, or Enumerable Type, + and the second a parameterized block as produced by the puppet syntax: $a.filter |$x| { ... } @@ -25,10 +25,55 @@ Puppet::Parser::Functions::newfunction( $a = ["raspberry", "blueberry", "orange"] $a.filter |$x| { $x =~ /berry$/ } + If the first argument is a Type that is enumerable, the second should be a parameterized block on one + of the two forms below. + + *Examples* + + Integer[1,10].filter |$x| { ... } + Integer[1,10].filter |$index, $x| { ... } + + The first form will give the block each element from the enumeration, and the second will get the index + and value for each element from the enumeration. The index always starts from 0. + - Since 3.4 - requires `parser = future`. ENDHEREDOC + def filter_Type(o, scope, pblock) + return nil unless pblock + tc = Puppet::Pops::Types::TypeCalculator.new() + enumerable = tc.enumerable(o) + if enumerable.nil? + raise ArgumentError, ("filter(): given type '#{tc.string(o)}' is not enumerable") + end + serving_size = pblock.parameter_count + if serving_size == 0 + raise ArgumentError, "Block must define at least one parameter; value." + end + if serving_size > 2 + raise ArgumentError, "Block must define at most two parameters; index, value" + end + enumerator = enumerable.each + result = [] + index = 0 + if serving_size == 1 + begin + loop { pblock.call(scope, it = enumerator.next) == true ? result << it : nil } + rescue StopIteration + end + else + begin + loop do + pblock.call(scope, index, it = enumerator.next) == true ? result << it : nil + index = index +1 + end + rescue StopIteration + end + end + result + end + receiver = args[0] pblock = args[1] @@ -43,6 +88,10 @@ Puppet::Parser::Functions::newfunction( result = Hash[result] unless result.is_a? Hash result else - raise ArgumentError, ("filter(): wrong argument type (#{receiver.class}; must be an Array or a Hash.") + if receiver.is_a?(Puppet::Pops::Types::PAbstractType) + filter_Type(receiver, self, pblock) + else + raise ArgumentError, ("filter(): wrong argument type (#{receiver.class}; must be an Array or a Hash.") + end end end diff --git a/spec/unit/parser/methods/filter_spec.rb b/spec/unit/parser/methods/filter_spec.rb index 89fb15bbb..7293d505c 100644 --- a/spec/unit/parser/methods/filter_spec.rb +++ b/spec/unit/parser/methods/filter_spec.rb @@ -23,6 +23,32 @@ describe 'the filter method' do catalog.resource(:file, "/file_blueberry")['ensure'].should == 'present' end + it 'should filter on enumerable type (Integer)' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = Integer[1,10] + $a.filter |$x|{ $x % 3 == 0}.each |$v|{ + file { "/file_$v": ensure => present } + } + MANIFEST + + catalog.resource(:file, "/file_3")['ensure'].should == 'present' + catalog.resource(:file, "/file_6")['ensure'].should == 'present' + catalog.resource(:file, "/file_9")['ensure'].should == 'present' + end + + it 'should filter on enumerable type (Integer) using two args index/value' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = Integer[10,18] + $a.filter |$i, $x|{ $i % 3 == 0}.each |$v|{ + file { "/file_$v": ensure => present } + } + MANIFEST + + catalog.resource(:file, "/file_10")['ensure'].should == 'present' + catalog.resource(:file, "/file_13")['ensure'].should == 'present' + catalog.resource(:file, "/file_16")['ensure'].should == 'present' + end + it 'should produce an array when acting on an array' do catalog = compile_to_catalog(<<-MANIFEST) $a = ['strawberry','blueberry','orange'] From fdcee69568498ad06bff45b68fbcccd334aa0819 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 16 Dec 2013 03:12:07 +0100 Subject: [PATCH 224/800] (PUP-939) Update documentation of filter function. Word-smithing. --- lib/puppet/parser/functions/filter.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/puppet/parser/functions/filter.rb b/lib/puppet/parser/functions/filter.rb index a04a86f99..6377153ce 100644 --- a/lib/puppet/parser/functions/filter.rb +++ b/lib/puppet/parser/functions/filter.rb @@ -33,11 +33,11 @@ Puppet::Parser::Functions::newfunction( Integer[1,10].filter |$x| { ... } Integer[1,10].filter |$index, $x| { ... } - The first form will give the block each element from the enumeration, and the second will get the index - and value for each element from the enumeration. The index always starts from 0. + The first form will pass each element from the enumeration to the block, and the second will pass the index + and value for each element. The index always starts from 0. - Since 3.4 - - requires `parser = future`. + - requires `parser = future` ENDHEREDOC def filter_Type(o, scope, pblock) From 4d7a26e1bd4dc20687015f0771da72374d10f128 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 16 Dec 2013 16:52:24 +0100 Subject: [PATCH 225/800] (PUP-939) Add support for enumerable type in reduce function. This adds support to perform reduce on an enumerable type (Integer range). This also corrects a typo in an error message (said 'collect' instead of 'reduce'). --- lib/puppet/parser/functions/reduce.rb | 34 ++++++++++++++++++++----- spec/unit/parser/methods/reduce_spec.rb | 10 ++++++++ 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/lib/puppet/parser/functions/reduce.rb b/lib/puppet/parser/functions/reduce.rb index 824f564c4..ddd2d8537 100644 --- a/lib/puppet/parser/functions/reduce.rb +++ b/lib/puppet/parser/functions/reduce.rb @@ -6,14 +6,15 @@ Puppet::Parser::Functions::newfunction( Applies a parameterized block to each element in a sequence of entries from the first argument (_the collection_) and returns the last result of the invocation of the parameterized block. - This function takes two mandatory arguments: the first should be an Array or a Hash, and the last - a parameterized block as produced by the puppet syntax: + This function takes two mandatory arguments: the first should be an Array, Hash, or enumerable type, and + the last a parameterized block as produced by the puppet syntax: $a.reduce |$memo, $x| { ... } - When the first argument is an Array, the block is called with each entry in turn. When the first argument - is a hash each entry is converted to an array with `[key, value]` before being fed to the block. An optional - 'start memo' value may be supplied as an argument between the array/hash and mandatory block. + When the first argument is an Array or an enumerable type, the block is called with each entry in turn. + When the first argument is a hash each entry is converted to an array with `[key, value]` before being + fed to the block. An optional 'start memo' value may be supplied as an argument between the array/hash + and mandatory block. If no 'start memo' is given, the first invocation of the parameterized block will be given the first and second elements of the collection, and if the collection has fewer than 2 elements, the first @@ -48,11 +49,26 @@ Puppet::Parser::Functions::newfunction( $a.reduce([na, 4]) |$memo, $entry| { [sum, $memo[1]+$entry[1]] } #=> [sum, 10] + *Examples* + + Integer[1,4].reduce |$memo, $x| { $memo + $x } + #=> 10 + - Since 3.2 - requires `parser = future`. ENDHEREDOC require 'puppet/parser/ast/lambda' + + def enumerable_Type(o) + tc = Puppet::Pops::Types::TypeCalculator.new() + enumerable = tc.enumerable(o) + if enumerable.nil? + raise ArgumentError, ("reduce(): given type '#{tc.string(o)}' is not enumerable") + end + enumerable.each + end + case args.length when 2 pblock = args[1] @@ -65,8 +81,12 @@ Puppet::Parser::Functions::newfunction( raise ArgumentError, ("reduce(): wrong argument type (#{args[1].class}; must be a parameterized block.") end receiver = args[0] - unless [Array, Hash].include?(receiver.class) - raise ArgumentError, ("collect(): wrong argument type (#{args[0].class}; must be an Array or a Hash.") + case receiver + when Array, Hash + when Puppet::Pops::Types::PAbstractType + receiver = enumerable_Type(receiver) + else + raise ArgumentError, ("reduce(): wrong argument type (#{args[0].class}; must be an Array, Hash, or enumerable type.") end if args.length == 3 receiver.reduce(args[1]) {|memo, x| pblock.call(self, memo, x) } diff --git a/spec/unit/parser/methods/reduce_spec.rb b/spec/unit/parser/methods/reduce_spec.rb index 5d4549b54..4f0c14e5e 100644 --- a/spec/unit/parser/methods/reduce_spec.rb +++ b/spec/unit/parser/methods/reduce_spec.rb @@ -35,6 +35,16 @@ describe 'the reduce method' do catalog.resource(:file, "/file_6")['ensure'].should == 'present' end + it 'reduce on enumerable type' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = Integer[1,3] + $b = $a.reduce |$memo, $x| { $memo + $x } + file { "/file_$b": ensure => present } + MANIFEST + + catalog.resource(:file, "/file_6")['ensure'].should == 'present' + end + it 'reduce on an array with start value' do catalog = compile_to_catalog(<<-MANIFEST) $a = [1,2,3] From 32b1f615785d2c64bceeb3ea28756496b9e64db8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Dal=C3=A9n?= Date: Mon, 16 Dec 2013 16:52:27 +0100 Subject: [PATCH 226/800] (#16570) Don't load the node object again in configurer Avoid loading the node object again if we already have a catalog. --- lib/puppet/configurer.rb | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/lib/puppet/configurer.rb b/lib/puppet/configurer.rb index 6547f7458..97e42fd6e 100644 --- a/lib/puppet/configurer.rb +++ b/lib/puppet/configurer.rb @@ -148,18 +148,21 @@ class Puppet::Configurer query_options = get_facts(options) end - begin - if node = Puppet::Node.indirection.find(Puppet[:node_name_value], - :environment => @environment, :ignore_cache => true) - if node.environment.to_s != @environment - Puppet.warning "Local environment: \"#{@environment}\" doesn't match server specified node environment \"#{node.environment}\", switching agent to \"#{node.environment}\"." - @environment = node.environment.to_s - query_options = nil + # We only need to find out the environment to run in if we don't already have a catalog + unless options[:catalog] + begin + if node = Puppet::Node.indirection.find(Puppet[:node_name_value], + :environment => @environment, :ignore_cache => true) + if node.environment.to_s != @environment + Puppet.warning "Local environment: \"#{@environment}\" doesn't match server specified node environment \"#{node.environment}\", switching agent to \"#{node.environment}\"." + @environment = node.environment.to_s + query_options = nil + end end + rescue Puppet::Error, Net::HTTPError => detail + Puppet.warning("Unable to fetch my node definition, but the agent run will continue:") + Puppet.warning(detail) end - rescue Puppet::Error, Net::HTTPError => detail - Puppet.warning("Unable to fetch my node definition, but the agent run will continue:") - Puppet.warning(detail) end query_options = get_facts(options) unless query_options From bff9f8a150e05440b5cdd0dd73235982c5265582 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 16 Dec 2013 17:03:03 +0100 Subject: [PATCH 227/800] (PUP-939) Add support to iterate over enumerable type in map function This adds support to iterate / map an enumerable type (Integer range). --- lib/puppet/parser/functions/map.rb | 17 ++++++++++++----- spec/unit/parser/methods/map_spec.rb | 13 +++++++++++++ spec/unit/parser/methods/shared.rb | 2 +- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/lib/puppet/parser/functions/map.rb b/lib/puppet/parser/functions/map.rb index 94ef4a0b7..c67107f40 100644 --- a/lib/puppet/parser/functions/map.rb +++ b/lib/puppet/parser/functions/map.rb @@ -8,13 +8,13 @@ Puppet::Parser::Functions::newfunction( Applies a parameterized block to each element in a sequence of entries from the first argument and returns an array with the result of each invocation of the parameterized block. - This function takes two mandatory arguments: the first should be an Array or a Hash, and the second - a parameterized block as produced by the puppet syntax: + This function takes two mandatory arguments: the first should be an Array, Hash, or enumerable type, + and the second a parameterized block as produced by the puppet syntax: $a.map |$x| { ... } - When the first argument `$a` is an Array, the block is called with each entry in turn. When the first argument - is a hash the entry is an array with `[key, value]`. + When the first argument `$a` is an Array or enumerable type, the block is called with each entry in turn. + When the first argument is a hash the entry is an array with `[key, value]`. *Examples* @@ -36,8 +36,15 @@ Puppet::Parser::Functions::newfunction( case receiver when Array when Hash + when Puppet::Pops::Types::PAbstractType + tc = Puppet::Pops::Types::TypeCalculator.new() + enumerable = tc.enumerable(receiver) + if enumerable.nil? + raise ArgumentError, ("map(): given type '#{tc.string(receiver)}' is not enumerable") + end + receiver = enumerable.each else - raise ArgumentError, ("map(): wrong argument type (#{receiver.class}; must be an Array or a Hash.") + raise ArgumentError, ("map(): wrong argument type (#{receiver.class}; must be an Array, Hash, or enumerable type.") end receiver.to_a.map {|x| pblock.call(self, x) } diff --git a/spec/unit/parser/methods/map_spec.rb b/spec/unit/parser/methods/map_spec.rb index 200037ba7..4fd120a32 100644 --- a/spec/unit/parser/methods/map_spec.rb +++ b/spec/unit/parser/methods/map_spec.rb @@ -25,6 +25,19 @@ describe 'the map method' do catalog.resource(:file, "/file_6")['ensure'].should == 'present' end + it 'map on an enumerable type (multiplying each value by 2)' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = Integer[1,3] + $a.map |$x|{ $x*2}.each |$v|{ + file { "/file_$v": ensure => present } + } + MANIFEST + + catalog.resource(:file, "/file_2")['ensure'].should == 'present' + catalog.resource(:file, "/file_4")['ensure'].should == 'present' + catalog.resource(:file, "/file_6")['ensure'].should == 'present' + end + it 'map on a hash selecting keys' do catalog = compile_to_catalog(<<-MANIFEST) $a = {'a'=>1,'b'=>2,'c'=>3} diff --git a/spec/unit/parser/methods/shared.rb b/spec/unit/parser/methods/shared.rb index 704769d83..b06cccf5a 100644 --- a/spec/unit/parser/methods/shared.rb +++ b/spec/unit/parser/methods/shared.rb @@ -32,7 +32,7 @@ shared_examples_for 'all iterative functions argument checks' do |func| compile_to_catalog(<<-MANIFEST) "not correct".#{func} |$v| { } MANIFEST - end.to raise_error(Puppet::Error, /must be an Array or a Hash/) + end.to raise_error(Puppet::Error, /must be an Array.*Hash/) end it 'raises an error when called with any parameters besides a block' do From 58e885ce26310f4bfa1be342361b7ec4b8ed434c Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 16 Dec 2013 17:55:21 +0100 Subject: [PATCH 228/800] (PUP-939) Add support for enumerable type to slice function. This adds support for iterating / slicing an enumerable type (Integer range). This also corrects the size calculation done in Integer range (off by one since it is an end inclusive range). --- lib/puppet/parser/functions/slice.rb | 29 +++++++++++++++++--------- lib/puppet/pops/types/types.rb | 2 +- spec/unit/parser/methods/slice_spec.rb | 20 ++++++++++++++++-- 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/lib/puppet/parser/functions/slice.rb b/lib/puppet/parser/functions/slice.rb index f721c984f..db7d9d20b 100644 --- a/lib/puppet/parser/functions/slice.rb +++ b/lib/puppet/parser/functions/slice.rb @@ -7,8 +7,8 @@ Puppet::Parser::Functions::newfunction( argument and returns the first argument, or if no block is given returns a new array with a concatenation of the slices. - This function takes two mandatory arguments: the first, `$a`, should be an Array or a Hash, and the second, `$n`, - the number of elements to include in each slice. The optional third argument should be a + This function takes two mandatory arguments: the first, `$a`, should be an Array, Hash, or enumerable type, + and the second, `$n`, the number of elements to include in each slice. The optional third argument should be a a parameterized block as produced by the puppet syntax: $a.slice($n) |$x| { ... } @@ -16,8 +16,8 @@ Puppet::Parser::Functions::newfunction( The parameterized block should have either one parameter (receiving an array with the slice), or the same number of parameters as specified by the slice size (each parameter receiving its part of the slice). In case there are fewer remaining elements than the slice size for the last slice it will contain the remaining - elements. When the block has multiple parameters, excess parameters are set to :undef for an array, and to - empty arrays for a Hash. + elements. When the block has multiple parameters, excess parameters are set to :undef for an array or + enumerable type, and to empty arrays for a Hash. $a.slice(2) |$first, $second| { ... } @@ -29,7 +29,8 @@ Puppet::Parser::Functions::newfunction( When called without a block, the function produces a concatenated result of the slices. - slice($[1,2,3,4,5,6], 2) # produces [[1,2], [3,4], [5,6]] + slice([1,2,3,4,5,6], 2) # produces [[1,2], [3,4], [5,6]] + slice(Integer[1,6], 2) # produces [[1,2], [3,4], [5,6]] - Since 3.2 - requires `parser = future`. @@ -37,7 +38,7 @@ Puppet::Parser::Functions::newfunction( require 'puppet/parser/ast/lambda' require 'puppet/parser/scope' - def each_Common(o, slice_size, filler, scope, pblock) + def each_Common(o, size, slice_size, filler, scope, pblock) serving_size = pblock ? pblock.parameter_count : 1 if serving_size == 0 raise ArgumentError, "Block must define at least one parameter." @@ -48,7 +49,7 @@ Puppet::Parser::Functions::newfunction( enumerator = o.each_slice(slice_size) result = [] if serving_size == 1 - ((o.size.to_f / slice_size).ceil).times do + ((size.to_f / slice_size).ceil).times do if pblock pblock.call(scope, enumerator.next) else @@ -56,7 +57,7 @@ Puppet::Parser::Functions::newfunction( end end else - ((o.size.to_f / slice_size).ceil).times do + ((size.to_f / slice_size).ceil).times do a = enumerator.next if a.size < serving_size a = a.dup.fill(filler, a.length...serving_size) @@ -88,9 +89,17 @@ Puppet::Parser::Functions::newfunction( case receiver when Array - each_Common(receiver, slice_size, :undef, self, pblock) + each_Common(receiver, receiver.size, slice_size, :undef, self, pblock) when Hash - each_Common(receiver, slice_size, [], self, pblock) + each_Common(receiver, receiver.size, slice_size, [], self, pblock) + when Puppet::Pops::Types::PAbstractType + tc = Puppet::Pops::Types::TypeCalculator.new() + enumerable = tc.enumerable(receiver) + if enumerable.nil? + raise ArgumentError, ("slice(): given type '#{tc.string(receiver)}' is not enumerable") + end + result = each_Common(enumerable.each, receiver.size, slice_size, [], self, pblock) + pblock ? receiver : result else raise ArgumentError, ("slice(): wrong argument type (#{args[0].class}; must be an Array or a Hash.") end diff --git a/lib/puppet/pops/types/types.rb b/lib/puppet/pops/types/types.rb index 999c4fd21..6829df6c1 100644 --- a/lib/puppet/pops/types/types.rb +++ b/lib/puppet/pops/types/types.rb @@ -151,7 +151,7 @@ module Puppet::Pops::Types # Returns Float.Infinity if one end of the range is unbound def size return 1.0 / 0.0 if from.nil? || to.nil? - (to-from).abs + 1+(to-from).abs end # Returns Enumerator if no block is given diff --git a/spec/unit/parser/methods/slice_spec.rb b/spec/unit/parser/methods/slice_spec.rb index 1069bc75a..0d29dc44a 100644 --- a/spec/unit/parser/methods/slice_spec.rb +++ b/spec/unit/parser/methods/slice_spec.rb @@ -40,6 +40,7 @@ describe 'methods' do catalog.resource(:file, "/file_2")['ensure'].should == 'absent' catalog.resource(:file, "/file_3")['ensure'].should == 'present' end + it 'slice with one parameter' do catalog = compile_to_catalog(<<-MANIFEST) $a = [1, present, 2, absent, 3, present] @@ -52,6 +53,7 @@ describe 'methods' do catalog.resource(:file, "/file_2")['ensure'].should == 'absent' catalog.resource(:file, "/file_3")['ensure'].should == 'present' end + it 'slice with shorter last slice' do catalog = compile_to_catalog(<<-MANIFEST) $a = [1, present, 2, present, 3, absent] @@ -64,8 +66,8 @@ describe 'methods' do catalog.resource(:file, "/file_3.")['ensure'].should == 'absent' end end - context "should be callable on hash as" do + context "should be callable on hash as" do it 'slice with explicit parameters, missing are empty' do catalog = compile_to_catalog(<<-MANIFEST) $a = {1=>present, 2=>present, 3=>absent} @@ -77,8 +79,22 @@ describe 'methods' do catalog.resource(:file, "/file_1.2")['ensure'].should == 'present' catalog.resource(:file, "/file_3.")['ensure'].should == 'absent' end - end + + context "should be callable on enumerable type as" do + it 'slice with explicit parameters' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = Integer[1,4] + $a.slice(2) |$a,$b| { + file { "/file_${a}.${b}": ensure => present } + } + MANIFEST + + catalog.resource(:file, "/file_1.2")['ensure'].should == 'present' + catalog.resource(:file, "/file_3.4")['ensure'].should == 'present' + end + end + context "when called without a block" do it "should produce an array with the result" do catalog = compile_to_catalog(<<-MANIFEST) From 09a44c6ecfcc665baa496d9f1117d94f3bd94044 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 16 Dec 2013 19:59:32 +0100 Subject: [PATCH 229/800] (maint) Fix "should_not_reassign" acceptance test for future parser The future evaluator emits a different error message. --- acceptance/tests/apply/hashes/should_not_reassign.rb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/acceptance/tests/apply/hashes/should_not_reassign.rb b/acceptance/tests/apply/hashes/should_not_reassign.rb index 2b0f9cc13..d0358ab1f 100755 --- a/acceptance/tests/apply/hashes/should_not_reassign.rb +++ b/acceptance/tests/apply/hashes/should_not_reassign.rb @@ -5,6 +5,12 @@ $my_hash['one']='1.5' } apply_manifest_on(agents, manifest, :acceptable_exit_codes => [1]) do - fail_test "didn't find the failure" unless - stderr.include? "Assigning to the hash 'my_hash' with an existing key 'one'" + expected_error_message = + case Puppet[:parser] + when 'future' + "Illegal attempt to assign via [index/key]. Not an assignable reference" + else + "Assigning to the hash 'my_hash' with an existing key 'one'" + end + fail_test("didn't find the failure") unless stderr.include?(expected_error_message) end From 2ad6b8d15d87a975d675dced176bf0102e2ba182 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Mon, 16 Dec 2013 16:21:22 -0800 Subject: [PATCH 230/800] (#23373) Don't apply all metadata The previous code would always apply all of the metadata that it got from the configuration file. This caused unused sections to override default metadata, when the section of the config file that was really being used did not specify any metadata. Instead of trying to apply everything, this now just tries to use the data from the section that is currently in use. --- lib/puppet/settings.rb | 15 +++++------- spec/unit/settings_spec.rb | 47 +++++++++++++++++++++++++++++++------- 2 files changed, 45 insertions(+), 17 deletions(-) diff --git a/lib/puppet/settings.rb b/lib/puppet/settings.rb index cf2f99cff..bd360e921 100644 --- a/lib/puppet/settings.rb +++ b/lib/puppet/settings.rb @@ -436,12 +436,11 @@ class Puppet::Settings # Return a given object's file metadata. def metadata(param) if obj = @config[param.to_sym] and obj.is_a?(FileSetting) - return [:owner, :group, :mode].inject({}) do |meta, p| - if v = obj.send(p) - meta[p] = v - end - meta - end + { + :owner => obj.owner, + :group => obj.group, + :mode => obj.mode + }.delete_if { |key, value| value.nil? } else nil end @@ -509,9 +508,6 @@ class Puppet::Settings # And now we can repopulate with the values from our last parsing of the config files. @configuration_file = data - data.sections.each do |name, section| - apply_metadata_from_section(section) - end # Determine our environment, if we have one. if @config[:environment] @@ -540,6 +536,7 @@ class Puppet::Settings end call_hooks_deferred_to_application_initialization :ignore_interpolation_dependency_errors => true + apply_metadata end # Parse the configuration file. Just provides thread safety. diff --git a/spec/unit/settings_spec.rb b/spec/unit/settings_spec.rb index 4306514b1..8ace005d8 100755 --- a/spec/unit/settings_spec.rb +++ b/spec/unit/settings_spec.rb @@ -763,11 +763,11 @@ describe Puppet::Settings do @settings.define_settings :section, :myfile => { :type => :file, :default => make_absolute("/myfile"), :desc => "a" } otherfile = make_absolute("/other/file") - text = "[main] + @settings.parse_config(<<-CONF) + [main] myfile = #{otherfile} {owner = service, group = service, mode = 644} - " - @settings.expects(:read_file).returns(text) - @settings.send(:parse_config_files) + CONF + @settings[:myfile].should == otherfile @settings.metadata(:myfile).should == {:owner => "suser", :group => "sgroup", :mode => "644"} end @@ -776,11 +776,11 @@ describe Puppet::Settings do @settings.define_settings :section, :myfile => { :type => :file, :default => make_absolute("/myfile"), :desc => "a" } otherfile = make_absolute("/other/file") - text = "[main] + @settings.parse_config(<<-CONF) + [main] myfile = #{otherfile} {owner = service} - " - @settings.expects(:read_file).returns(text) - @settings.send(:parse_config_files) + CONF + @settings[:myfile].should == otherfile @settings.metadata(:myfile).should == {:owner => "suser"} end @@ -812,6 +812,37 @@ describe Puppet::Settings do @settings.metadata(:myfile).should == {:mode => "664"} end + it "does not use the metadata from the same setting in a different section" do + default_values = {} + PuppetSpec::Settings::TEST_APP_DEFAULT_DEFINITIONS.keys.each do |key| + default_values[key] = 'default value' + end + + file = make_absolute("/file") + default_mode = "0600" + @settings.define_settings :main, PuppetSpec::Settings::TEST_APP_DEFAULT_DEFINITIONS + @settings.define_settings :master, :myfile => { :type => :file, :default => file, :desc => "a", :mode => default_mode } + + text = "[master] + myfile = #{file}/foo + [agent] + myfile = #{file} {mode = 664} + " + @settings.expects(:read_file).returns(text) + + # will start initialization as user + @settings.preferred_run_mode.should == :user + @settings.send(:parse_config_files) + + # change app run_mode to master + @settings.initialize_app_defaults(default_values.merge(:run_mode => :master)) + @settings.preferred_run_mode.should == :master + + # initializing the app should have reloaded the metadata based on run_mode + @settings[:myfile].should == "#{file}/foo" + @settings.metadata(:myfile).should == { :mode => default_mode } + end + it "should call hooks associated with values set in the configuration file" do values = [] @settings.define_settings :section, :mysetting => {:default => "defval", :desc => "a", :hook => proc { |v| values << v }} From fbdc1cfaebf7e9a56113193a7bcd6ae445657c49 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 17 Dec 2013 01:34:31 +0100 Subject: [PATCH 231/800] (maint) Fix future parser inability to parse virtual class instantiation The future parser removed the support to name a class "class". This led to missing that it is possible to create a virtual or exported class using resource expression syntax e.g. @class { 'theunreal': }. This is now fixed by adding an explicit parer rule for the @/@@ class case. --- lib/puppet/pops/parser/egrammar.ra | 5 + lib/puppet/pops/parser/eparser.rb | 1578 +++++++++--------- spec/unit/pops/parser/parse_resource_spec.rb | 14 + 3 files changed, 816 insertions(+), 781 deletions(-) diff --git a/lib/puppet/pops/parser/egrammar.ra b/lib/puppet/pops/parser/egrammar.ra index fdea774e9..6887e8d66 100644 --- a/lib/puppet/pops/parser/egrammar.ra +++ b/lib/puppet/pops/parser/egrammar.ra @@ -401,6 +401,11 @@ resource_expression end loc result, val[0], val[4] } + | at CLASS LBRACE resourceinstances endsemi RBRACE { + result = Factory.RESOURCE(Factory.fqn(token_text(val[1])), val[3]) + result.form = val[0] + loc result, val[1], val[5] + } | CLASS LBRACE resourceinstances endsemi RBRACE { result = Factory.RESOURCE(Factory.fqn(token_text(val[0])), val[2]) loc result, val[0], val[4] diff --git a/lib/puppet/pops/parser/eparser.rb b/lib/puppet/pops/parser/eparser.rb index eaa23d226..8fc9a10ea 100644 --- a/lib/puppet/pops/parser/eparser.rb +++ b/lib/puppet/pops/parser/eparser.rb @@ -20,7 +20,7 @@ module Puppet module Parser class Parser < Racc::Parser -module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 705) +module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 710) # Make emacs happy # Local Variables: @@ -30,114 +30,64 @@ module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 705) ##### State transition tables begin ### clist = [ -'54,56,-209,240,49,118,51,54,56,117,230,239,334,287,72,229,240,273,54', -'56,-127,-200,210,13,-129,210,235,234,95,39,99,46,94,48,43,250,47,62', -'58,272,41,61,44,45,-209,127,59,12,125,67,60,54,56,11,98,49,127,51,363', -'125,207,63,-127,-200,79,78,-129,40,63,74,75,57,240,228,13,50,73,251', -'227,63,39,325,46,114,48,43,80,47,62,58,303,41,61,44,45,247,118,59,12', -'223,117,60,54,56,11,320,49,319,51,54,56,118,63,118,305,117,72,117,40', -'118,54,56,57,117,225,13,50,320,249,319,95,39,99,46,94,48,43,307,47,62', -'58,65,41,61,44,45,226,127,59,12,125,293,60,54,56,11,98,49,127,51,361', -'125,232,63,118,233,79,78,117,40,63,74,75,57,220,249,13,50,73,220,312', -'63,39,313,46,314,48,43,80,47,62,58,210,41,61,44,45,263,317,59,12,290', -'321,60,54,56,11,323,49,289,51,359,206,247,63,249,330,331,72,286,40,271', -'247,197,57,173,67,13,50,140,341,264,95,39,99,46,94,48,43,249,47,62,58', -'119,41,61,44,45,265,344,59,12,107,108,60,54,56,11,98,49,107,51,269,107', -'348,63,76,77,79,78,323,40,350,74,75,57,351,352,13,50,73,353,104,355', -'39,356,46,357,48,43,80,47,62,58,271,41,61,44,45,67,64,59,12,364,365', -'60,54,56,11,72,49,366,51,367,,,63,,,,72,,40,95,,99,57,94,,13,50,,,,95', +'54,56,-130,242,49,119,51,54,56,118,241,230,338,290,72,242,229,275,54', +'56,-210,-201,211,13,-128,211,237,236,95,39,99,46,94,48,43,252,47,62', +'58,326,41,61,44,45,-130,128,59,12,126,222,60,54,56,11,98,49,128,51,368', +'126,208,63,-210,-201,79,78,-128,40,63,74,75,57,242,232,13,50,73,253', +'231,63,39,328,46,115,48,43,80,47,62,58,306,41,61,44,45,218,119,59,12', +'225,118,60,54,56,11,323,49,322,51,54,56,119,63,119,289,118,72,118,40', +'308,54,56,57,119,227,13,50,118,249,310,95,39,99,46,94,48,43,292,47,62', +'58,65,41,61,44,45,67,128,59,12,126,207,60,54,56,11,98,49,128,51,366', +'126,234,63,119,235,79,78,118,40,63,74,75,57,251,119,13,50,73,118,274', +'63,39,323,46,322,48,43,80,47,62,58,315,41,61,44,45,316,317,59,12,211', +'273,60,54,56,11,320,49,228,51,364,324,326,63,107,267,249,72,251,40,249', +'334,335,57,266,273,13,50,198,174,67,95,39,99,46,94,48,43,141,47,62,58', +'265,41,61,44,45,345,251,59,12,251,120,60,54,56,11,98,49,249,51,348,107', +'108,63,76,77,79,78,293,40,107,74,75,57,352,222,13,50,73,354,355,356', +'39,357,46,358,48,101,80,47,62,58,104,41,61,68,70,69,71,59,12,360,361', +'60,54,56,11,72,49,362,51,296,67,64,63,369,370,371,72,372,40,95,,99,57', +'94,,13,50,,,,95,39,99,46,94,48,101,,47,62,58,,41,61,,98,,,59,12,,,60', +'54,56,11,98,49,,51,,,,63,76,77,79,78,,40,,74,75,57,,,13,50,73,,,,39', +',46,,48,101,80,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,72,49,,51,,,', +'63,,,,72,,40,95,,99,57,94,,13,50,,,,95,39,99,46,94,48,101,,47,62,58', +',41,61,,98,,,59,12,,,60,54,56,11,98,49,,51,74,75,,63,,,,73,,40,,74,75', +'57,,,13,50,73,,,,39,,46,,48,43,,47,62,58,,41,61,44,45,,,59,12,,,60,54', +'56,11,72,49,,51,,,,63,,,,72,,40,95,,99,57,94,,13,50,,,,95,39,99,46,94', +'48,101,,47,62,58,,41,61,,98,,,59,12,,,60,54,56,11,98,49,,51,74,75,,63', +',,,73,,40,,,,57,,,13,50,73,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59', +'12,,,60,54,56,11,72,49,,51,,,,63,,,,72,,40,95,,99,57,94,,13,50,,,,95', '39,99,46,94,48,101,,47,62,58,,41,61,,98,,,59,12,,,60,54,56,11,98,49', -',51,,,,63,76,77,79,78,,40,,74,75,57,,,13,50,73,,,,39,,46,,48,101,80', -'47,62,58,,41,61,68,70,69,71,59,12,,,60,54,56,11,72,49,,51,,,,63,,,,72', -',40,95,,99,57,94,,13,50,,,,95,39,99,46,94,48,101,,47,62,58,,41,61,,98', -',,59,12,,,60,54,56,11,98,49,,51,74,75,,63,,,,73,,40,,,,57,,,13,50,73', -',,,39,,46,,48,43,,47,62,58,,41,61,44,45,,,59,12,,,60,54,56,11,72,49', -',51,,,,63,,,,72,,40,95,,99,57,94,,13,50,,,,95,39,99,46,94,48,101,,47', -'62,58,,41,61,,98,,,59,12,,,60,54,56,11,98,49,,51,277,,,63,,,,72,,40', -',74,75,57,,,13,50,73,,,95,39,99,46,94,48,43,,47,62,58,,41,61,44,45,', -',59,12,,,60,54,56,11,98,49,,51,,,,63,,,,72,,40,,74,75,57,,,13,50,73', -',,95,39,99,46,94,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,98', -'49,,51,,,,63,,,,72,,40,,,,57,,,13,50,73,,,95,39,99,46,94,48,101,,47', -'62,58,,41,61,,,,,59,12,,,60,54,56,11,98,49,,51,,,,63,,,,,,40,,,,57,', -',13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,', -'49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41', -'61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39', -',46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,', -',,,,40,,,,57,,,13,50,,,,,175,192,186,193,48,187,195,188,184,182,,177', -'190,,,,,59,12,196,191,189,54,56,11,,49,,51,,,,63,,,,,194,176,,,,57,', -',13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,', -'49,295,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,', -'41,61,,,,,59,12,,,60,54,56,11,,49,130,51,,,,63,,,,,,40,,,,57,,,13,50', -',,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,132', -'51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,', -',,,59,12,,,60,54,56,11,,49,,51,135,,,63,,,,,,40,,,,57,,,13,50,,,,,39', -',46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,', -',,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,', -',60,54,56,11,,49,296,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101', -',47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57', -',,13,50,,,,,39,,46,,48,43,,47,62,58,,41,61,44,45,,,59,12,,,60,54,56', -'11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58', +',51,,,,63,,,,73,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61', +',,,,59,12,,,60,54,56,11,72,49,,51,,,,63,,,,,,40,95,,99,57,94,,13,50', +',,,,39,,46,,48,101,,47,62,58,,41,61,,98,,,59,12,,,60,54,56,11,,49,,51', +',,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,114,,47,62,58,,41,61,,,,', +'59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,', +'48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,271,,,63,,,', +',,40,,,,57,,,13,50,,,,,39,,46,,48,43,,47,62,58,,41,61,44,45,,,59,12', +',,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101', +',47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,277,,,63,,,,,,40,,', +',57,,,13,50,,,,,39,,46,,48,43,,47,62,58,,41,61,44,45,,,59,12,,,60,54', +'56,11,,49,131,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47', +'62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,133,51,,,,63,,,,,,40,,,,57', +',,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11', +',49,,51,136,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58', ',41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,', -',,,39,,46,,48,43,,47,62,58,,41,61,44,45,,,59,12,,,60,54,56,11,,49,,51', -',,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,43,,47,62,58,,41,61,44,45', +',,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,279', +',,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,43,,47,62,58,,41,61,44,45', ',,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46', ',48,43,,47,62,58,,41,61,44,45,,,59,12,,,60,54,56,11,,49,,51,,,,63,,', -',,,40,,,,57,,,13,50,,,,,39,,46,,48,43,,47,62,58,,41,61,44,45,,,59,12', -',,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,43', -',47,62,58,,41,61,44,45,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,', -',,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56', -'11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58', -',41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,', -',,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,', -',,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59', -'12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48', -'101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,', -',,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56', -'11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58', -',41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,', -',,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,', -',,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59', -'12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48', -'101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,', -',,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56', -'11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58', -',41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,', -',,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,', -',,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59', -'12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48', -'101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,', -',,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56', -'11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58', -',41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,', -',,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,', -',,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59', -'12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48', -'101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,', -',,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56', -'11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58', -',41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,', -',,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,', -',,63,,,,,,40,,,172,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,', -',,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,175,192', -'186,193,48,187,195,188,184,182,,177,190,,,,,59,12,196,191,189,54,56', -'11,,49,,51,340,,,63,,,,,194,176,,,,57,,,13,50,,,,,39,,46,,48,43,,47', +',,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,', +'60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,43,,47', '62,58,,41,61,44,45,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57', -',,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11', -',49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41', -'61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39', +',,13,50,,,,,39,,46,,48,43,,47,62,58,,41,61,44,45,,,59,12,,,60,54,56', +'11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,43,,47,62,58,', +'41,61,44,45,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50', +',,,,39,,46,,48,43,,47,62,58,,41,61,44,45,,,59,12,,,60,54,56,11,,49,', +'51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,43,,47,62,58,,41,61,44', +'45,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39', ',46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,', -',,,,40,,,,57,,,13,50,,,,,39,,46,,48,43,,47,62,58,,41,61,44,45,,,59,12', -',,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101', -',47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57', -',,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,,,11,54,56', -',,49,,51,63,,,,,,40,,,,57,,,,50,,13,204,,,,,39,,46,,48,101,,47,62,58', -',41,61,,,,,59,12,,,60,54,56,11,,49,,51,326,,,63,,,,,,40,,,,57,,,13,50', -',,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51', -',,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,', -'59,12,,,60,,,11,54,56,,,49,,51,63,,,,,,40,,,,57,,,,50,,13,212,,,,,39', -',46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,298,,,63', -',,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12', -',,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101', +',,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,', +',60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101', ',47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57', ',,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11', ',49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41', @@ -147,57 +97,108 @@ clist = [ ',60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101', ',47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57', ',,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11', -',49,,51,275,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,43,,47,62,58,', -'41,61,44,45,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50', -',,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,,,11,,,,,,,,63,,', -',,,40,72,,,57,,,,50,,91,92,93,88,83,95,,99,,94,,,84,86,85,87,,,,,,,', -',,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,72,,96,,,73,,,,91,92', -'93,88,83,95,80,99,,94,,,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76', -'77,79,78,81,82,,74,75,,,,,,73,,72,,,,,231,,,80,91,92,93,88,83,95,,99', -',94,,,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74', -'75,72,,216,,,73,,,,91,92,93,88,83,95,80,99,,94,,,84,86,85,87,,,,,,,', -',,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,72,,,,,73,,,,91,92,93', -'88,83,95,80,99,,94,,,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77', -'79,78,81,82,,74,75,72,,215,,,73,,,,91,92,93,88,83,95,80,99,,94,,,84', -'86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,72,,214', -',,73,,,,91,92,93,88,83,95,80,99,,94,,,84,86,85,87,,,,,,,,,,,,,,,,98', -',,,90,89,,,76,77,79,78,81,82,,74,75,72,,213,,,73,,,,91,92,93,88,83,95', -'80,99,,94,,,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81', -'82,,74,75,72,,,,,73,,,,91,92,93,88,83,95,80,99,,94,,,84,86,85,87,,,', -',,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,72,,,,,73,,,,91,92', -'93,88,83,95,80,99,,94,,,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76', -'77,79,78,81,82,,74,75,72,,,,,73,,,,91,92,93,88,83,95,80,99,,94,,,84', -'86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,72,,', -',,73,,,,91,92,93,88,83,95,80,99,,94,,,84,86,85,87,,,,,,,,,,,,,,,,98', -',,,90,89,,,76,77,79,78,81,82,,74,75,72,,,,,73,,,,91,92,93,88,83,95,80', -'99,,94,,,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82', -',74,75,72,,,,,73,,,,91,92,93,88,83,95,80,99,,94,,,84,86,85,87,72,,,', -',,,,,,,,,,95,98,99,,94,90,89,,,76,77,79,78,81,82,,74,75,,,,,,73,,,98', -',,72,,,,80,76,77,79,78,81,82,,74,75,95,,99,,94,73,,,,,,72,,,,80,,,,', -',,,,83,95,98,99,,94,,,84,,76,77,79,78,81,82,,74,75,,,,,,73,,,98,,,,', -'72,,80,76,77,79,78,81,82,,74,75,,83,95,,99,73,94,,,84,,,,72,,80,,,,', -',,,,,,83,95,98,99,,94,,,84,,76,77,79,78,81,82,,74,75,,,,,,73,,,98,,', -',,72,,80,76,77,79,78,81,82,,74,75,,83,95,,99,73,94,,,84,,,,,,80,,,,', -',,,,,,,,98,,,,,72,,,76,77,79,78,81,82,,74,75,88,83,95,,99,73,94,,,84', -'86,85,87,,,80,,,,,,,,,,,,,98,,,,,72,,,76,77,79,78,81,82,,74,75,88,83', -'95,,99,73,94,,,84,86,85,87,,,80,,,,,,,,,,,,,98,,,,,89,,,76,77,79,78', -'81,82,,74,75,72,,,,,73,,,,91,92,93,88,83,95,80,99,,94,,,84,86,85,87', -',,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,72,,,,,73,,,,91', +',49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41', +'61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39', +',46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,', +',,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,', +',60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101', +',47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57', +',,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11', +',49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41', +'61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39', +',46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,', +',,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,', +',60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101', +',47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57', +',,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11', +',49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41', +'61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39', +',46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,', +',,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,', +',60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101', +',47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,344,,,63,,,,,,40,,', +'173,57,,,13,50,,,,,39,,46,,48,43,,47,62,58,,41,61,44,45,,,59,12,,,60', +'54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,176,193,187,194,48', +'188,196,189,185,183,,178,191,,,,,59,12,197,192,190,54,56,11,,49,,51', +',,,63,,,,,195,177,,,,57,,,13,50,,,,,39,,46,,48,43,,47,62,58,,41,61,44', +'45,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39', +',46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,', +',,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,', +',60,54,56,11,,49,,51,329,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101', +',47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57', +',,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11', +',49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41', +'61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,176', +'193,187,194,48,188,196,189,185,183,,178,191,,,,,59,12,197,192,190,,', +'11,54,56,,,49,,51,63,,,,,195,177,,,,57,,,,50,,13,205,,,,,39,,46,,48', +'101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,', +',,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56', +'11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58', +',41,61,,,,,59,12,,,60,,,11,54,56,,,49,,51,63,,,,,,40,,,,57,,,,50,,13', +'213,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49', +',51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61', +',,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,', +'46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,299,51,,,,63', +',,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12', +',,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101', +',47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57', +',,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11', +',49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41', +'61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39', +',46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,', +',,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,', +',60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101', +',47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,301,,,63,,,,,,40,,', +',57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56', +'11,,49,298,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62', +'58,,41,61,,,,,59,12,,,60,,,11,,,,,,,,63,,,,,,40,72,,217,57,,,,50,,91', +'92,93,88,83,95,,99,,94,,,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76', +'77,79,78,81,82,,74,75,72,,216,,,73,,,,91,92,93,88,83,95,80,99,,94,,', +'84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,,', +',,,73,,72,,,,,233,,,80,91,92,93,88,83,95,,99,,94,,,84,86,85,87,,,,,', +',,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,72,,215,,,73,,,,91', '92,93,88,83,95,80,99,,94,,,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,', '76,77,79,78,81,82,,74,75,72,,,,,73,,,,91,92,93,88,83,95,80,99,,94,,', -'84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,,', -',,,73,,72,,,,,241,,,80,91,92,93,88,83,95,,99,,94,,,84,86,85,87,,,,,', -',,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,72,,,,,73,,,,91,92', -'93,88,83,95,80,99,,94,,202,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,', -'76,77,79,78,81,82,,74,75,,,,,,73,,72,,,,,,,,80,91,92,93,88,83,95,245', -'99,,94,,,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82', -',74,75,72,,,,,73,,,,91,92,93,88,83,95,80,99,,94,,,84,86,85,87,,,,,,', -',,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,,259,192,258,193,73', -'256,195,260,254,253,,255,257,,80,,,,,196,191,261,259,192,258,193,,256', -'195,260,254,253,,255,257,,,194,262,,,196,191,261,259,192,258,193,,256', -'195,260,254,253,,255,257,,,194,262,,,196,191,261,,,,,,,,,,,,,,,,194', -'262' ] - racc_action_table = arr = ::Array.new(5601, nil) +'84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,72', +',214,,,73,,,,91,92,93,88,83,95,80,99,,94,,,84,86,85,87,,,,,,,,,,,,,', +',,98,,,,90,89,,,76,77,79,78,81,82,,74,75,72,,,,,73,,,,91,92,93,88,83', +'95,80,99,,94,,203,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79', +'78,81,82,,74,75,72,,,,,73,,,,91,92,93,88,83,95,80,99,,94,,,84,86,85', +'87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,72,,,,,73,', +',,91,92,93,88,83,95,80,99,,94,,,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90', +'89,,,76,77,79,78,81,82,,74,75,72,,,,,73,,,,91,92,93,88,83,95,80,99,', +'94,,,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74', +'75,72,,,,,73,,,,91,92,93,88,83,95,80,99,,94,,,84,86,85,87,,,,,,,,,,', +',,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,72,,,,,73,,,,91,92,93,88', +'83,95,80,99,,94,,,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79', +'78,81,82,,74,75,72,,,,,73,,,,91,92,93,88,83,95,80,99,,94,,,84,86,85', +'87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,72,,,,,73,', +',,91,92,93,88,83,95,80,99,,94,,,84,86,85,87,72,,,,,,,,,,,,,,95,98,99', +',94,90,89,,,76,77,79,78,81,82,,74,75,,,,,,73,,,98,,,72,,,,80,76,77,79', +'78,81,82,,74,75,95,,99,,94,73,,,,,,72,,,,80,,,,,,,,,83,95,98,99,,94', +',,84,,76,77,79,78,81,82,,74,75,,,,,,73,,,98,,,,,72,,80,76,77,79,78,81', +'82,,74,75,,83,95,,99,73,94,,,84,,,,72,,80,,,,,,,,,,,83,95,98,99,,94', +',,84,,76,77,79,78,81,82,,74,75,,,,,,73,,,98,,,,,72,,80,76,77,79,78,81', +'82,,74,75,,83,95,,99,73,94,,,84,,,,,,80,,,,,,,,,,,,,98,,,,,72,,,76,77', +'79,78,81,82,,74,75,88,83,95,,99,73,94,,,84,86,85,87,,,80,,,,,,,,,,,', +',98,,,,,72,,,76,77,79,78,81,82,,74,75,88,83,95,,99,73,94,,,84,86,85', +'87,,,80,,,,,,,,,,,,,98,,,,,89,,,76,77,79,78,81,82,,74,75,72,,,,,73,', +',,91,92,93,88,83,95,80,99,,94,,,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90', +'89,,,76,77,79,78,81,82,,74,75,72,,,,,73,,,,91,92,93,88,83,95,80,99,', +'94,,,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74', +'75,72,,,,,73,,,,91,92,93,88,83,95,80,99,,94,,,84,86,85,87,,,,,,,,,,', +',,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,,,,,,73,,72,,,,,243,,,80', +'91,92,93,88,83,95,,99,,94,,,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,', +',76,77,79,78,81,82,,74,75,72,,96,,,73,,,,91,92,93,88,83,95,80,99,,94', +',,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75', +',,,,,73,,72,,,,,,,,80,91,92,93,88,83,95,247,99,,94,,,84,86,85,87,,,', +',,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,72,,,,,73,,,,91,92', +'93,88,83,95,80,99,,94,,,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76', +'77,79,78,81,82,,74,75,,261,193,260,194,73,258,196,262,256,255,,257,259', +',80,,,,,197,192,263,261,193,260,194,,258,196,262,256,255,,257,259,,', +'195,264,,,197,192,263,261,193,260,194,,258,196,262,256,255,,257,259', +',,195,264,,,197,192,263,,,,,,,,,,,,,,,,195,264' ] + racc_action_table = arr = ::Array.new(5652, nil) idx = 0 clist.each do |str| str.split(',', -1).each do |i| @@ -207,192 +208,194 @@ clist = [ end clist = [ -'0,0,189,211,0,286,0,47,47,286,131,147,286,220,151,131,147,211,188,188', -'182,190,220,0,184,107,139,139,151,0,151,0,151,0,0,185,0,0,0,210,0,0', -'0,0,189,47,0,0,47,141,0,351,351,0,151,351,188,351,351,188,107,0,182', -'190,151,151,184,0,47,151,151,0,278,129,351,0,151,185,129,188,351,278', -'351,43,351,351,151,351,351,351,242,351,351,351,351,178,186,351,351,121', -'186,351,4,4,351,275,4,275,4,223,223,43,351,46,246,43,152,46,351,187', -'225,225,351,187,121,4,351,317,179,317,152,4,152,4,152,4,4,248,4,4,4', -'4,4,4,4,4,123,223,4,4,223,226,4,350,350,4,152,350,225,350,350,225,134', -'4,101,134,152,152,101,4,223,152,152,4,120,252,350,4,152,115,266,225', -'350,268,350,270,350,350,152,350,350,350,271,350,350,350,350,199,274', -'350,350,222,276,350,348,348,350,277,348,221,348,348,106,281,350,282', -'283,284,156,218,350,288,217,97,350,95,66,348,350,64,302,201,156,348', -'156,348,156,348,348,304,348,348,348,44,348,348,348,348,203,311,348,348', -'312,38,348,206,206,348,156,206,204,206,206,37,320,348,156,156,156,156', -'321,348,323,156,156,348,324,328,206,348,156,329,36,335,206,336,206,339', -'206,206,156,206,206,206,208,206,206,206,206,5,1,206,206,354,358,206', -'11,11,206,100,11,360,11,362,,,206,,,,157,,206,100,,100,206,100,,11,206', -',,,157,11,157,11,157,11,11,,11,11,11,,11,11,,100,,,11,11,,,11,12,12', -'11,157,12,,12,,,,11,157,157,157,157,,11,,157,157,11,,,12,11,157,,,,12', -',12,,12,12,157,12,12,12,,12,12,7,7,7,7,12,12,,,12,13,13,12,153,13,,13', -',,,12,,,,149,,12,153,,153,12,153,,13,12,,,,149,13,149,13,149,13,13,', -'13,13,13,,13,13,,153,,,13,13,,,13,331,331,13,149,331,,331,153,153,,13', -',,,153,,13,,,,13,,,331,13,149,,,,331,,331,,331,331,,331,331,331,,331', -'331,331,331,,,331,331,,,331,319,319,331,148,319,,319,,,,331,,,,155,', -'331,148,,148,331,148,,319,331,,,,155,319,155,319,155,319,319,,319,319', -'319,,319,319,,148,,,319,319,,,319,214,214,319,155,214,,214,214,,,319', -',,,154,,319,,155,155,319,,,214,319,155,,,154,214,154,214,154,214,214', -',214,214,214,,214,214,214,214,,,214,214,,,214,39,39,214,154,39,,39,', -',,214,,,,150,,214,,154,154,214,,,39,214,154,,,150,39,150,39,150,39,39', -',39,39,39,,39,39,,,,,39,39,,,39,40,40,39,150,40,,40,,,,39,,,,102,,39', -',,,39,,,40,39,150,,,102,40,102,40,102,40,40,,40,40,40,,40,40,,,,,40', -'40,,,40,41,41,40,102,41,,41,,,,40,,,,,,40,,,,40,,,41,40,,,,,41,,41,', -'41,41,,41,41,41,,41,41,,,,,41,41,,,41,42,42,41,,42,,42,,,,41,,,,,,41', -',,,41,,,42,41,,,,,42,,42,,42,42,,42,42,42,,42,42,,,,,42,42,,,42,215', -'215,42,,215,,215,,,,42,,,,,,42,,,,42,,,215,42,,,,,215,,215,,215,215', -',215,215,215,,215,215,,,,,215,215,,,215,216,216,215,,216,,216,,,,215', -',,,,,215,,,,215,,,216,215,,,,,216,216,216,216,216,216,216,216,216,216', -',216,216,,,,,216,216,216,216,216,303,303,216,,303,,303,,,,216,,,,,216', -'216,,,,216,,,303,216,,,,,303,,303,,303,303,,303,303,303,,303,303,,,', -',303,303,,,303,227,227,303,,227,227,227,,,,303,,,,,,303,,,,303,,,227', -'303,,,,,227,,227,,227,227,,227,227,227,,227,227,,,,,227,227,,,227,49', -'49,227,,49,49,49,,,,227,,,,,,227,,,,227,,,49,227,,,,,49,,49,,49,49,', -'49,49,49,,49,49,,,,,49,49,,,49,50,50,49,,50,50,50,,,,49,,,,,,49,,,,49', -',,50,49,,,,,50,,50,,50,50,,50,50,50,,50,50,,,,,50,50,,,50,51,51,50,', -'51,,51,51,,,50,,,,,,50,,,,50,,,51,50,,,,,51,,51,,51,51,,51,51,51,,51', -'51,,,,,51,51,,,51,55,55,51,,55,,55,,,,51,,,,,,51,,,,51,,,55,51,,,,,55', -',55,,55,55,,55,55,55,,55,55,,,,,55,55,,,55,229,229,55,,229,229,229,', -',,55,,,,,,55,,,,55,,,229,55,,,,,229,,229,,229,229,,229,229,229,,229', -'229,,,,,229,229,,,229,65,65,229,,65,,65,,,,229,,,,,,229,,,,229,,,65', -'229,,,,,65,,65,,65,65,,65,65,65,,65,65,65,65,,,65,65,,,65,231,231,65', -',231,,231,,,,65,,,,,,65,,,,65,,,231,65,,,,,231,,231,,231,231,,231,231', -'231,,231,231,,,,,231,231,,,231,67,67,231,,67,,67,,,,231,,,,,,231,,,', -'231,,,67,231,,,,,67,,67,,67,67,,67,67,67,,67,67,67,67,,,67,67,,,67,68', -'68,67,,68,,68,,,,67,,,,,,67,,,,67,,,68,67,,,,,68,,68,,68,68,,68,68,68', -',68,68,68,68,,,68,68,,,68,69,69,68,,69,,69,,,,68,,,,,,68,,,,68,,,69', -'68,,,,,69,,69,,69,69,,69,69,69,,69,69,69,69,,,69,69,,,69,70,70,69,,70', -',70,,,,69,,,,,,69,,,,69,,,70,69,,,,,70,,70,,70,70,,70,70,70,,70,70,70', +'0,0,185,212,0,289,0,189,189,289,148,130,289,222,152,148,130,212,47,47', +'190,191,222,0,183,107,140,140,152,0,152,0,152,0,0,186,0,0,0,324,0,0', +'0,0,185,189,0,0,189,116,0,355,355,0,152,355,47,355,355,47,107,0,190', +'191,152,152,183,0,189,152,152,0,280,132,355,0,152,186,132,47,355,280', +'355,43,355,355,152,355,355,355,244,355,355,355,355,114,101,355,355,122', +'101,355,4,4,355,277,4,277,4,225,225,43,355,187,220,43,153,187,355,248', +'227,227,355,114,122,4,355,114,219,250,153,4,153,4,153,4,4,223,4,4,4', +'4,4,4,4,4,142,225,4,4,225,106,4,354,354,4,153,354,227,354,354,227,135', +'4,188,135,153,153,188,4,225,153,153,4,254,46,354,4,153,46,211,227,354', +'320,354,320,354,354,153,354,354,354,268,354,354,354,354,270,272,354', +'354,273,209,354,352,352,354,276,352,124,352,352,278,279,354,205,204', +'283,157,284,354,285,286,287,354,202,291,352,354,97,95,66,157,352,157', +'352,157,352,352,64,352,352,352,200,352,352,352,352,305,180,352,352,307', +'44,352,173,173,352,157,173,179,173,314,315,38,352,157,157,157,157,224', +'352,37,157,157,352,323,121,173,352,157,326,327,331,173,332,173,333,173', +'173,157,173,173,173,36,173,173,7,7,7,7,173,173,339,340,173,11,11,173', +'149,11,343,11,228,5,1,173,359,363,365,158,367,173,149,,149,173,149,', +'11,173,,,,158,11,158,11,158,11,11,,11,11,11,,11,11,,149,,,11,11,,,11', +'12,12,11,158,12,,12,,,,11,158,158,158,158,,11,,158,158,11,,,12,11,158', +',,,12,,12,,12,12,158,12,12,12,,12,12,,,,,12,12,,,12,13,13,12,156,13', +',13,,,,12,,,,155,,12,156,,156,12,156,,13,12,,,,155,13,155,13,155,13', +'13,,13,13,13,,13,13,,156,,,13,13,,,13,335,335,13,155,335,,335,156,156', +',13,,,,156,,13,,155,155,13,,,335,13,155,,,,335,,335,,335,335,,335,335', +'335,,335,335,335,335,,,335,335,,,335,322,322,335,154,322,,322,,,,335', +',,,150,,335,154,,154,335,154,,322,335,,,,150,322,150,322,150,322,322', +',322,322,322,,322,322,,154,,,322,322,,,322,177,177,322,150,177,,177', +'154,154,,322,,,,154,,322,,,,322,,,177,322,150,,,,177,,177,,177,177,', +'177,177,177,,177,177,,,,,177,177,,,177,39,39,177,151,39,,39,,,,177,', +',,100,,177,151,,151,177,151,,39,177,,,,100,39,100,39,100,39,39,,39,39', +'39,,39,39,,151,,,39,39,,,39,40,40,39,100,40,,40,,,,39,,,,151,,39,,,', +'39,,,40,39,,,,,40,,40,,40,40,,40,40,40,,40,40,,,,,40,40,,,40,41,41,40', +'102,41,,41,,,,40,,,,,,40,102,,102,40,102,,41,40,,,,,41,,41,,41,41,,41', +'41,41,,41,41,,102,,,41,41,,,41,42,42,41,,42,,42,,,,41,,,,,,41,,,,41', +',,42,41,,,,,42,,42,,42,42,,42,42,42,,42,42,,,,,42,42,,,42,178,178,42', +',178,,178,,,,42,,,,,,42,,,,42,,,178,42,,,,,178,,178,,178,178,,178,178', +'178,,178,178,,,,,178,178,,,178,207,207,178,,207,,207,207,,,178,,,,,', +'178,,,,178,,,207,178,,,,,207,,207,,207,207,,207,207,207,,207,207,207', +'207,,,207,207,,,207,306,306,207,,306,,306,,,,207,,,,,,207,,,,207,,,306', +'207,,,,,306,,306,,306,306,,306,306,306,,306,306,,,,,306,306,,,306,214', +'214,306,,214,,214,214,,,306,,,,,,306,,,,306,,,214,306,,,,,214,,214,', +'214,214,,214,214,214,,214,214,214,214,,,214,214,,,214,49,49,214,,49', +'49,49,,,,214,,,,,,214,,,,214,,,49,214,,,,,49,,49,,49,49,,49,49,49,,49', +'49,,,,,49,49,,,49,50,50,49,,50,50,50,,,,49,,,,,,49,,,,49,,,50,49,,,', +',50,,50,,50,50,,50,50,50,,50,50,,,,,50,50,,,50,51,51,50,,51,,51,51,', +',50,,,,,,50,,,,50,,,51,50,,,,,51,,51,,51,51,,51,51,51,,51,51,,,,,51', +'51,,,51,55,55,51,,55,,55,,,,51,,,,,,51,,,,51,,,55,51,,,,,55,,55,,55', +'55,,55,55,55,,55,55,,,,,55,55,,,55,215,215,55,,215,,215,215,,,55,,,', +',,55,,,,55,,,215,55,,,,,215,,215,,215,215,,215,215,215,,215,215,215', +'215,,,215,215,,,215,65,65,215,,65,,65,,,,215,,,,,,215,,,,215,,,65,215', +',,,,65,,65,,65,65,,65,65,65,,65,65,65,65,,,65,65,,,65,216,216,65,,216', +',216,,,,65,,,,,,65,,,,65,,,216,65,,,,,216,,216,,216,216,,216,216,216', +',216,216,,,,,216,216,,,216,67,67,216,,67,,67,,,,216,,,,,,216,,,,216', +',,67,216,,,,,67,,67,,67,67,,67,67,67,,67,67,67,67,,,67,67,,,67,68,68', +'67,,68,,68,,,,67,,,,,,67,,,,67,,,68,67,,,,,68,,68,,68,68,,68,68,68,', +'68,68,68,68,,,68,68,,,68,69,69,68,,69,,69,,,,68,,,,,,68,,,,68,,,69,68', +',,,,69,,69,,69,69,,69,69,69,,69,69,69,69,,,69,69,,,69,70,70,69,,70,', +'70,,,,69,,,,,,69,,,,69,,,70,69,,,,,70,,70,,70,70,,70,70,70,,70,70,70', '70,,,70,70,,,70,71,71,70,,71,,71,,,,70,,,,,,70,,,,70,,,71,70,,,,,71', -',71,,71,71,,71,71,71,,71,71,71,71,,,71,71,,,71,247,247,71,,247,,247', -',,,71,,,,,,71,,,,71,,,247,71,,,,,247,,247,,247,247,,247,247,247,,247', -'247,,,,,247,247,,,247,73,73,247,,73,,73,,,,247,,,,,,247,,,,247,,,73', -'247,,,,,73,,73,,73,73,,73,73,73,,73,73,,,,,73,73,,,73,74,74,73,,74,', -'74,,,,73,,,,,,73,,,,73,,,74,73,,,,,74,,74,,74,74,,74,74,74,,74,74,,', -',,74,74,,,74,75,75,74,,75,,75,,,,74,,,,,,74,,,,74,,,75,74,,,,,75,,75', -',75,75,,75,75,75,,75,75,,,,,75,75,,,75,76,76,75,,76,,76,,,,75,,,,,,75', -',,,75,,,76,75,,,,,76,,76,,76,76,,76,76,76,,76,76,,,,,76,76,,,76,77,77', -'76,,77,,77,,,,76,,,,,,76,,,,76,,,77,76,,,,,77,,77,,77,77,,77,77,77,', -'77,77,,,,,77,77,,,77,78,78,77,,78,,78,,,,77,,,,,,77,,,,77,,,78,77,,', -',,78,,78,,78,78,,78,78,78,,78,78,,,,,78,78,,,78,79,79,78,,79,,79,,,', -'78,,,,,,78,,,,78,,,79,78,,,,,79,,79,,79,79,,79,79,79,,79,79,,,,,79,79', -',,79,80,80,79,,80,,80,,,,79,,,,,,79,,,,79,,,80,79,,,,,80,,80,,80,80', -',80,80,80,,80,80,,,,,80,80,,,80,81,81,80,,81,,81,,,,80,,,,,,80,,,,80', -',,81,80,,,,,81,,81,,81,81,,81,81,81,,81,81,,,,,81,81,,,81,82,82,81,', -'82,,82,,,,81,,,,,,81,,,,81,,,82,81,,,,,82,,82,,82,82,,82,82,82,,82,82', -',,,,82,82,,,82,83,83,82,,83,,83,,,,82,,,,,,82,,,,82,,,83,82,,,,,83,', -'83,,83,83,,83,83,83,,83,83,,,,,83,83,,,83,84,84,83,,84,,84,,,,83,,,', -',,83,,,,83,,,84,83,,,,,84,,84,,84,84,,84,84,84,,84,84,,,,,84,84,,,84', -'85,85,84,,85,,85,,,,84,,,,,,84,,,,84,,,85,84,,,,,85,,85,,85,85,,85,85', -'85,,85,85,,,,,85,85,,,85,86,86,85,,86,,86,,,,85,,,,,,85,,,,85,,,86,85', -',,,,86,,86,,86,86,,86,86,86,,86,86,,,,,86,86,,,86,87,87,86,,87,,87,', -',,86,,,,,,86,,,,86,,,87,86,,,,,87,,87,,87,87,,87,87,87,,87,87,,,,,87', -'87,,,87,88,88,87,,88,,88,,,,87,,,,,,87,,,,87,,,88,87,,,,,88,,88,,88', -'88,,88,88,88,,88,88,,,,,88,88,,,88,89,89,88,,89,,89,,,,88,,,,,,88,,', -',88,,,89,88,,,,,89,,89,,89,89,,89,89,89,,89,89,,,,,89,89,,,89,90,90', -'89,,90,,90,,,,89,,,,,,89,,,,89,,,90,89,,,,,90,,90,,90,90,,90,90,90,', -'90,90,,,,,90,90,,,90,91,91,90,,91,,91,,,,90,,,,,,90,,,,90,,,91,90,,', -',,91,,91,,91,91,,91,91,91,,91,91,,,,,91,91,,,91,92,92,91,,92,,92,,,', -'91,,,,,,91,,,,91,,,92,91,,,,,92,,92,,92,92,,92,92,92,,92,92,,,,,92,92', -',,92,93,93,92,,93,,93,,,,92,,,,,,92,,,,92,,,93,92,,,,,93,,93,,93,93', -',93,93,93,,93,93,,,,,93,93,,,93,94,94,93,,94,,94,,,,93,,,,,,93,,,,93', -',,94,93,,,,,94,,94,,94,94,,94,94,94,,94,94,,,,,94,94,,,94,177,177,94', -',177,,177,,,,94,,,,,,94,,,94,94,,,177,94,,,,,177,,177,,177,177,,177', -'177,177,,177,177,,,,,177,177,,,177,96,96,177,,96,,96,,,,177,,,,,,177', -',,,177,,,96,177,,,,,96,96,96,96,96,96,96,96,96,96,,96,96,,,,,96,96,96', -'96,96,290,290,96,,290,,290,290,,,96,,,,,96,96,,,,96,,,290,96,,,,,290', -',290,,290,290,,290,290,290,,290,290,290,290,,,290,290,,,290,98,98,290', -',98,,98,,,,290,,,,,,290,,,,290,,,98,290,,,,,98,,98,,98,98,,98,98,98', -',98,98,,,,,98,98,,,98,99,99,98,,99,,99,,,,98,,,,,,98,,,,98,,,99,98,', -',,,99,,99,,99,99,,99,99,99,,99,99,,,,,99,99,,,99,176,176,99,,176,,176', -',,,99,,,,,,99,,,,99,,,176,99,,,,,176,,176,,176,176,,176,176,176,,176', -'176,,,,,176,176,,,176,289,289,176,,289,,289,,,,176,,,,,,176,,,,176,', -',289,176,,,,,289,,289,,289,289,,289,289,289,,289,289,289,289,,,289,289', -',,289,175,175,289,,175,,175,,,,289,,,,,,289,,,,289,,,175,289,,,,,175', -',175,,175,175,,175,175,175,,175,175,,,,,175,175,,,175,172,172,175,,172', -',172,,,,175,,,,,,175,,,,175,,,172,175,,,,,172,,172,,172,172,,172,172', -'172,,172,172,,,,,172,172,,,172,,,172,104,104,,,104,,104,172,,,,,,172', -',,,172,,,,172,,104,104,,,,,104,,104,,104,104,,104,104,104,,104,104,', -',,,104,104,,,104,279,279,104,,279,,279,279,,,104,,,,,,104,,,,104,,,279', -'104,,,,,279,,279,,279,279,,279,279,279,,279,279,,,,,279,279,,,279,272', -'272,279,,272,,272,,,,279,,,,,,279,,,,279,,,272,279,,,,,272,,272,,272', -'272,,272,272,272,,272,272,,,,,272,272,,,272,,,272,108,108,,,108,,108', -'272,,,,,,272,,,,272,,,,272,,108,108,,,,,108,,108,,108,108,,108,108,108', -',108,108,,,,,108,108,,,108,233,233,108,,233,,233,233,,,108,,,,,,108', -',,,108,,,233,108,,,,,233,,233,,233,233,,233,233,233,,233,233,,,,,233', -'233,,,233,238,238,233,,238,,238,,,,233,,,,,,233,,,,233,,,238,233,,,', -',238,,238,,238,238,,238,238,238,,238,238,,,,,238,238,,,238,240,240,238', -',240,,240,,,,238,,,,,,238,,,,238,,,240,238,,,,,240,,240,,240,240,,240', -'240,240,,240,240,,,,,240,240,,,240,241,241,240,,241,,241,,,,240,,,,', -',240,,,,240,,,241,240,,,,,241,,241,,241,241,,241,241,241,,241,241,,', -',,241,241,,,241,114,114,241,,114,,114,,,,241,,,,,,241,,,,241,,,114,241', -',,,,114,,114,,114,114,,114,114,114,,114,114,,,,,114,114,,,114,265,265', -'114,,265,,265,,,,114,,,,,,114,,,,114,,,265,114,,,,,265,,265,,265,265', -',265,265,265,,265,265,,,,,265,265,,,265,251,251,265,,251,,251,,,,265', -',,,,,265,,,,265,,,251,265,,,,,251,,251,,251,251,,251,251,251,,251,251', -',,,,251,251,,,251,250,250,251,,250,,250,,,,251,,,,,,251,,,,251,,,250', -'251,,,,,250,,250,,250,250,,250,250,250,,250,250,,,,,250,250,,,250,213', -'213,250,,213,,213,213,,,250,,,,,,250,,,,250,,,213,250,,,,,213,,213,', -'213,213,,213,213,213,,213,213,213,213,,,213,213,,,213,72,72,213,,72', -',72,,,,213,,,,,,213,,,,213,,,72,213,,,,,72,,72,,72,72,,72,72,72,,72', -'72,,,,,72,72,,,72,,,72,,,,,,,,72,,,,,,72,128,,,72,,,,72,,128,128,128', -'128,128,128,,128,,128,,,128,128,128,128,,,,,,,,,,,,,,,,128,,,,128,128', -',,128,128,128,128,128,128,,128,128,10,,10,,,128,,,,10,10,10,10,10,10', -'128,10,,10,,,10,10,10,10,,,,,,,,,,,,,,,,10,,,,10,10,,,10,10,10,10,10', -'10,,10,10,,,,,,10,,133,,,,,133,,,10,133,133,133,133,133,133,,133,,133', -',,133,133,133,133,,,,,,,,,,,,,,,,133,,,,133,133,,,133,133,133,133,133', -'133,,133,133,113,,113,,,133,,,,113,113,113,113,113,113,133,113,,113', -',,113,113,113,113,,,,,,,,,,,,,,,,113,,,,113,113,,,113,113,113,113,113', -'113,,113,113,137,,,,,113,,,,137,137,137,137,137,137,113,137,,137,,,137', -'137,137,137,,,,,,,,,,,,,,,,137,,,,137,137,,,137,137,137,137,137,137', -',137,137,112,,112,,,137,,,,112,112,112,112,112,112,137,112,,112,,,112', -'112,112,112,,,,,,,,,,,,,,,,112,,,,112,112,,,112,112,112,112,112,112', -',112,112,111,,111,,,112,,,,111,111,111,111,111,111,112,111,,111,,,111', -'111,111,111,,,,,,,,,,,,,,,,111,,,,111,111,,,111,111,111,111,111,111', -',111,111,109,,109,,,111,,,,109,109,109,109,109,109,111,109,,109,,,109', -'109,109,109,,,,,,,,,,,,,,,,109,,,,109,109,,,109,109,109,109,109,109', -',109,109,316,,,,,109,,,,316,316,316,316,316,316,109,316,,316,,,316,316', -'316,316,,,,,,,,,,,,,,,,316,,,,316,316,,,316,316,316,316,316,316,,316', -'316,310,,,,,316,,,,310,310,310,310,310,310,316,310,,310,,,310,310,310', -'310,,,,,,,,,,,,,,,,310,,,,310,310,,,310,310,310,310,310,310,,310,310', -'309,,,,,310,,,,309,309,309,309,309,309,310,309,,309,,,309,309,309,309', -',,,,,,,,,,,,,,,309,,,,309,309,,,309,309,309,309,309,309,,309,309,198', -',,,,309,,,,198,198,198,198,198,198,309,198,,198,,,198,198,198,198,,', -',,,,,,,,,,,,,198,,,,198,198,,,198,198,198,198,198,198,,198,198,301,', -',,,198,,,,301,301,301,301,301,301,198,301,,301,,,301,301,301,301,,,', -',,,,,,,,,,,,301,,,,301,301,,,301,301,301,301,301,301,,301,301,297,,', -',,301,,,,297,297,297,297,297,297,301,297,,297,,,297,297,297,297,158', -',,,,,,,,,,,,,158,297,158,,158,297,297,,,297,297,297,297,297,297,,297', -'297,,,,,,297,,,158,,,159,,,,297,158,158,158,158,158,158,,158,158,159', -',159,,159,158,,,,,,160,,,,158,,,,,,,,,160,160,159,160,,160,,,160,,159', -'159,159,159,159,159,,159,159,,,,,,159,,,160,,,,,161,,159,160,160,160', -'160,160,160,,160,160,,161,161,,161,160,161,,,161,,,,162,,160,,,,,,,', -',,,162,162,161,162,,162,,,162,,161,161,161,161,161,161,,161,161,,,,', -',161,,,162,,,,,163,,161,162,162,162,162,162,162,,162,162,,163,163,,163', -'162,163,,,163,,,,,,162,,,,,,,,,,,,,163,,,,,164,,,163,163,163,163,163', -'163,,163,163,164,164,164,,164,163,164,,,164,164,164,164,,,163,,,,,,', -',,,,,,164,,,,,165,,,164,164,164,164,164,164,,164,164,165,165,165,,165', -'164,165,,,165,165,165,165,,,164,,,,,,,,,,,,,165,,,,,165,,,165,165,165', -'165,165,165,,165,165,294,,,,,165,,,,294,294,294,294,294,294,165,294', -',294,,,294,294,294,294,,,,,,,,,,,,,,,,294,,,,294,294,,,294,294,294,294', -'294,294,,294,294,167,,,,,294,,,,167,167,167,167,167,167,294,167,,167', -',,167,167,167,167,,,,,,,,,,,,,,,,167,,,,167,167,,,167,167,167,167,167', -'167,,167,167,168,,,,,167,,,,168,168,168,168,168,168,167,168,,168,,,168', -'168,168,168,,,,,,,,,,,,,,,,168,,,,168,168,,,168,168,168,168,168,168', -',168,168,,,,,,168,,169,,,,,169,,,168,169,169,169,169,169,169,,169,,169', -',,169,169,169,169,,,,,,,,,,,,,,,,169,,,,169,169,,,169,169,169,169,169', -'169,,169,169,103,,,,,169,,,,103,103,103,103,103,103,169,103,,103,,103', -'103,103,103,103,,,,,,,,,,,,,,,,103,,,,103,103,,,103,103,103,103,103', -'103,,103,103,,,,,,103,,174,,,,,,,,103,174,174,174,174,174,174,174,174', -',174,,,174,174,174,174,,,,,,,,,,,,,,,,174,,,,174,174,,,174,174,174,174', -'174,174,,174,174,166,,,,,174,,,,166,166,166,166,166,166,174,166,,166', -',,166,166,166,166,,,,,,,,,,,,,,,,166,,,,166,166,,,166,166,166,166,166', -'166,,166,166,,197,197,197,197,166,197,197,197,197,197,,197,197,,166', -',,,,197,197,197,244,244,244,244,,244,244,244,244,244,,244,244,,,197', -'197,,,244,244,244,249,249,249,249,,249,249,249,249,249,,249,249,,,244', -'244,,,249,249,249,,,,,,,,,,,,,,,,249,249' ] - racc_action_check = arr = ::Array.new(5601, nil) +',71,,71,71,,71,71,71,,71,71,71,71,,,71,71,,,71,72,72,71,,72,,72,,,,71', +',,,,,71,,,,71,,,72,71,,,,,72,,72,,72,72,,72,72,72,,72,72,,,,,72,72,', +',72,73,73,72,,73,,73,,,,72,,,,,,72,,,,72,,,73,72,,,,,73,,73,,73,73,', +'73,73,73,,73,73,,,,,73,73,,,73,74,74,73,,74,,74,,,,73,,,,,,73,,,,73', +',,74,73,,,,,74,,74,,74,74,,74,74,74,,74,74,,,,,74,74,,,74,75,75,74,', +'75,,75,,,,74,,,,,,74,,,,74,,,75,74,,,,,75,,75,,75,75,,75,75,75,,75,75', +',,,,75,75,,,75,76,76,75,,76,,76,,,,75,,,,,,75,,,,75,,,76,75,,,,,76,', +'76,,76,76,,76,76,76,,76,76,,,,,76,76,,,76,77,77,76,,77,,77,,,,76,,,', +',,76,,,,76,,,77,76,,,,,77,,77,,77,77,,77,77,77,,77,77,,,,,77,77,,,77', +'78,78,77,,78,,78,,,,77,,,,,,77,,,,77,,,78,77,,,,,78,,78,,78,78,,78,78', +'78,,78,78,,,,,78,78,,,78,79,79,78,,79,,79,,,,78,,,,,,78,,,,78,,,79,78', +',,,,79,,79,,79,79,,79,79,79,,79,79,,,,,79,79,,,79,80,80,79,,80,,80,', +',,79,,,,,,79,,,,79,,,80,79,,,,,80,,80,,80,80,,80,80,80,,80,80,,,,,80', +'80,,,80,81,81,80,,81,,81,,,,80,,,,,,80,,,,80,,,81,80,,,,,81,,81,,81', +'81,,81,81,81,,81,81,,,,,81,81,,,81,82,82,81,,82,,82,,,,81,,,,,,81,,', +',81,,,82,81,,,,,82,,82,,82,82,,82,82,82,,82,82,,,,,82,82,,,82,83,83', +'82,,83,,83,,,,82,,,,,,82,,,,82,,,83,82,,,,,83,,83,,83,83,,83,83,83,', +'83,83,,,,,83,83,,,83,84,84,83,,84,,84,,,,83,,,,,,83,,,,83,,,84,83,,', +',,84,,84,,84,84,,84,84,84,,84,84,,,,,84,84,,,84,85,85,84,,85,,85,,,', +'84,,,,,,84,,,,84,,,85,84,,,,,85,,85,,85,85,,85,85,85,,85,85,,,,,85,85', +',,85,86,86,85,,86,,86,,,,85,,,,,,85,,,,85,,,86,85,,,,,86,,86,,86,86', +',86,86,86,,86,86,,,,,86,86,,,86,87,87,86,,87,,87,,,,86,,,,,,86,,,,86', +',,87,86,,,,,87,,87,,87,87,,87,87,87,,87,87,,,,,87,87,,,87,88,88,87,', +'88,,88,,,,87,,,,,,87,,,,87,,,88,87,,,,,88,,88,,88,88,,88,88,88,,88,88', +',,,,88,88,,,88,89,89,88,,89,,89,,,,88,,,,,,88,,,,88,,,89,88,,,,,89,', +'89,,89,89,,89,89,89,,89,89,,,,,89,89,,,89,90,90,89,,90,,90,,,,89,,,', +',,89,,,,89,,,90,89,,,,,90,,90,,90,90,,90,90,90,,90,90,,,,,90,90,,,90', +'91,91,90,,91,,91,,,,90,,,,,,90,,,,90,,,91,90,,,,,91,,91,,91,91,,91,91', +'91,,91,91,,,,,91,91,,,91,92,92,91,,92,,92,,,,91,,,,,,91,,,,91,,,92,91', +',,,,92,,92,,92,92,,92,92,92,,92,92,,,,,92,92,,,92,93,93,92,,93,,93,', +',,92,,,,,,92,,,,92,,,93,92,,,,,93,,93,,93,93,,93,93,93,,93,93,,,,,93', +'93,,,93,94,94,93,,94,,94,,,,93,,,,,,93,,,,93,,,94,93,,,,,94,,94,,94', +'94,,94,94,94,,94,94,,,,,94,94,,,94,293,293,94,,293,,293,293,,,94,,,', +',,94,,,94,94,,,293,94,,,,,293,,293,,293,293,,293,293,293,,293,293,293', +'293,,,293,293,,,293,96,96,293,,96,,96,,,,293,,,,,,293,,,,293,,,96,293', +',,,,96,96,96,96,96,96,96,96,96,96,,96,96,,,,,96,96,96,96,96,292,292', +'96,,292,,292,,,,96,,,,,96,96,,,,96,,,292,96,,,,,292,,292,,292,292,,292', +'292,292,,292,292,292,292,,,292,292,,,292,98,98,292,,98,,98,,,,292,,', +',,,292,,,,292,,,98,292,,,,,98,,98,,98,98,,98,98,98,,98,98,,,,,98,98', +',,98,99,99,98,,99,,99,,,,98,,,,,,98,,,,98,,,99,98,,,,,99,,99,,99,99', +',99,99,99,,99,99,,,,,99,99,,,99,281,281,99,,281,,281,281,,,99,,,,,,99', +',,,99,,,281,99,,,,,281,,281,,281,281,,281,281,281,,281,281,,,,,281,281', +',,281,274,274,281,,274,,274,,,,281,,,,,,281,,,,281,,,274,281,,,,,274', +',274,,274,274,,274,274,274,,274,274,,,,,274,274,,,274,267,267,274,,267', +',267,,,,274,,,,,,274,,,,274,,,267,274,,,,,267,,267,,267,267,,267,267', +'267,,267,267,,,,,267,267,,,267,217,217,267,,217,,217,,,,267,,,,,,267', +',,,267,,,217,267,,,,,217,217,217,217,217,217,217,217,217,217,,217,217', +',,,,217,217,217,217,217,,,217,104,104,,,104,,104,217,,,,,217,217,,,', +'217,,,,217,,104,104,,,,,104,,104,,104,104,,104,104,104,,104,104,,,,', +'104,104,,,104,253,253,104,,253,,253,,,,104,,,,,,104,,,,104,,,253,104', +',,,,253,,253,,253,253,,253,253,253,,253,253,,,,,253,253,,,253,252,252', +'253,,252,,252,,,,253,,,,,,253,,,,253,,,252,253,,,,,252,,252,,252,252', +',252,252,252,,252,252,,,,,252,252,,,252,,,252,108,108,,,108,,108,252', +',,,,,252,,,,252,,,,252,,108,108,,,,,108,,108,,108,108,,108,108,108,', +'108,108,,,,,108,108,,,108,218,218,108,,218,,218,,,,108,,,,,,108,,,,108', +',,218,108,,,,,218,,218,,218,218,,218,218,218,,218,218,,,,,218,218,,', +'218,176,176,218,,176,,176,,,,218,,,,,,218,,,,218,,,176,218,,,,,176,', +'176,,176,176,,176,176,176,,176,176,,,,,176,176,,,176,231,231,176,,231', +'231,231,,,,176,,,,,,176,,,,176,,,231,176,,,,,231,,231,,231,231,,231', +'231,231,,231,231,,,,,231,231,,,231,233,233,231,,233,,233,,,,231,,,,', +',231,,,,231,,,233,231,,,,,233,,233,,233,233,,233,233,233,,233,233,,', +',,233,233,,,233,249,249,233,,249,,249,,,,233,,,,,,233,,,,233,,,249,233', +',,,,249,,249,,249,249,,249,249,249,,249,249,,,,,249,249,,,249,115,115', +'249,,115,,115,,,,249,,,,,,249,,,,249,,,115,249,,,,,115,,115,,115,115', +',115,115,115,,115,115,,,,,115,115,,,115,243,243,115,,243,,243,,,,115', +',,,,,115,,,,115,,,243,115,,,,,243,,243,,243,243,,243,243,243,,243,243', +',,,,243,243,,,243,242,242,243,,242,,242,,,,243,,,,,,243,,,,243,,,242', +'243,,,,,242,,242,,242,242,,242,242,242,,242,242,,,,,242,242,,,242,240', +'240,242,,240,,240,,,,242,,,,,,242,,,,242,,,240,242,,,,,240,,240,,240', +'240,,240,240,240,,240,240,,,,,240,240,,,240,235,235,240,,235,,235,235', +',,240,,,,,,240,,,,240,,,235,240,,,,,235,,235,,235,235,,235,235,235,', +'235,235,,,,,235,235,,,235,229,229,235,,229,229,229,,,,235,,,,,,235,', +',,235,,,229,235,,,,,229,,229,,229,229,,229,229,229,,229,229,,,,,229', +'229,,,229,,,229,,,,,,,,229,,,,,,229,113,,113,229,,,,229,,113,113,113', +'113,113,113,,113,,113,,,113,113,113,113,,,,,,,,,,,,,,,,113,,,,113,113', +',,113,113,113,113,113,113,,113,113,112,,112,,,113,,,,112,112,112,112', +'112,112,113,112,,112,,,112,112,112,112,,,,,,,,,,,,,,,,112,,,,112,112', +',,112,112,112,112,112,112,,112,112,,,,,,112,,134,,,,,134,,,112,134,134', +'134,134,134,134,,134,,134,,,134,134,134,134,,,,,,,,,,,,,,,,134,,,,134', +'134,,,134,134,134,134,134,134,,134,134,111,,111,,,134,,,,111,111,111', +'111,111,111,134,111,,111,,,111,111,111,111,,,,,,,,,,,,,,,,111,,,,111', +'111,,,111,111,111,111,111,111,,111,111,138,,,,,111,,,,138,138,138,138', +'138,138,111,138,,138,,,138,138,138,138,,,,,,,,,,,,,,,,138,,,,138,138', +',,138,138,138,138,138,138,,138,138,109,,109,,,138,,,,109,109,109,109', +'109,109,138,109,,109,,,109,109,109,109,,,,,,,,,,,,,,,,109,,,,109,109', +',,109,109,109,109,109,109,,109,109,103,,,,,109,,,,103,103,103,103,103', +'103,109,103,,103,,103,103,103,103,103,,,,,,,,,,,,,,,,103,,,,103,103', +',,103,103,103,103,103,103,,103,103,297,,,,,103,,,,297,297,297,297,297', +'297,103,297,,297,,,297,297,297,297,,,,,,,,,,,,,,,,297,,,,297,297,,,297', +'297,297,297,297,297,,297,297,300,,,,,297,,,,300,300,300,300,300,300', +'297,300,,300,,,300,300,300,300,,,,,,,,,,,,,,,,300,,,,300,300,,,300,300', +'300,300,300,300,,300,300,304,,,,,300,,,,304,304,304,304,304,304,300', +'304,,304,,,304,304,304,304,,,,,,,,,,,,,,,,304,,,,304,304,,,304,304,304', +'304,304,304,,304,304,312,,,,,304,,,,312,312,312,312,312,312,304,312', +',312,,,312,312,312,312,,,,,,,,,,,,,,,,312,,,,312,312,,,312,312,312,312', +'312,312,,312,312,199,,,,,312,,,,199,199,199,199,199,199,312,199,,199', +',,199,199,199,199,,,,,,,,,,,,,,,,199,,,,199,199,,,199,199,199,199,199', +'199,,199,199,313,,,,,199,,,,313,313,313,313,313,313,199,313,,313,,,313', +'313,313,313,,,,,,,,,,,,,,,,313,,,,313,313,,,313,313,313,313,313,313', +',313,313,319,,,,,313,,,,319,319,319,319,319,319,313,319,,319,,,319,319', +'319,319,159,,,,,,,,,,,,,,159,319,159,,159,319,319,,,319,319,319,319', +'319,319,,319,319,,,,,,319,,,159,,,160,,,,319,159,159,159,159,159,159', +',159,159,160,,160,,160,159,,,,,,161,,,,159,,,,,,,,,161,161,160,161,', +'161,,,161,,160,160,160,160,160,160,,160,160,,,,,,160,,,161,,,,,162,', +'160,161,161,161,161,161,161,,161,161,,162,162,,162,161,162,,,162,,,', +'163,,161,,,,,,,,,,,163,163,162,163,,163,,,163,,162,162,162,162,162,162', +',162,162,,,,,,162,,,163,,,,,164,,162,163,163,163,163,163,163,,163,163', +',164,164,,164,163,164,,,164,,,,,,163,,,,,,,,,,,,,164,,,,,165,,,164,164', +'164,164,164,164,,164,164,165,165,165,,165,164,165,,,165,165,165,165', +',,164,,,,,,,,,,,,,165,,,,,166,,,165,165,165,165,165,165,,165,165,166', +'166,166,,166,165,166,,,166,166,166,166,,,165,,,,,,,,,,,,,166,,,,,166', +',,166,166,166,166,166,166,,166,166,167,,,,,166,,,,167,167,167,167,167', +'167,166,167,,167,,,167,167,167,167,,,,,,,,,,,,,,,,167,,,,167,167,,,167', +'167,167,167,167,167,,167,167,168,,,,,167,,,,168,168,168,168,168,168', +'167,168,,168,,,168,168,168,168,,,,,,,,,,,,,,,,168,,,,168,168,,,168,168', +'168,168,168,168,,168,168,169,,,,,168,,,,169,169,169,169,169,169,168', +'169,,169,,,169,169,169,169,,,,,,,,,,,,,,,,169,,,,169,169,,,169,169,169', +'169,169,169,,169,169,,,,,,169,,170,,,,,170,,,169,170,170,170,170,170', +'170,,170,,170,,,170,170,170,170,,,,,,,,,,,,,,,,170,,,,170,170,,,170', +'170,170,170,170,170,,170,170,10,,10,,,170,,,,10,10,10,10,10,10,170,10', +',10,,,10,10,10,10,,,,,,,,,,,,,,,,10,,,,10,10,,,10,10,10,10,10,10,,10', +'10,,,,,,10,,175,,,,,,,,10,175,175,175,175,175,175,175,175,,175,,,175', +'175,175,175,,,,,,,,,,,,,,,,175,,,,175,175,,,175,175,175,175,175,175', +',175,175,129,,,,,175,,,,129,129,129,129,129,129,175,129,,129,,,129,129', +'129,129,,,,,,,,,,,,,,,,129,,,,129,129,,,129,129,129,129,129,129,,129', +'129,,246,246,246,246,129,246,246,246,246,246,,246,246,,129,,,,,246,246', +'246,198,198,198,198,,198,198,198,198,198,,198,198,,,246,246,,,198,198', +'198,251,251,251,251,,251,251,251,251,251,,251,251,,,198,198,,,251,251', +'251,,,,,,,,,,,,,,,,251,251' ] + racc_action_check = arr = ::Array.new(5652, nil) idx = 0 clist.each do |str| str.split(',', -1).each do |i| @@ -402,180 +405,182 @@ clist = [ end racc_action_pointer = [ - -2, 300, nil, nil, 100, 287, nil, 335, nil, nil, - 4052, 304, 355, 406, nil, nil, nil, nil, nil, nil, + -2, 315, nil, nil, 100, 302, nil, 233, nil, nil, + 5384, 304, 355, 406, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, 257, 193, 228, 610, - 661, 712, 763, 75, 199, nil, 77, 5, nil, 1018, + nil, nil, nil, nil, nil, nil, 269, 202, 239, 610, + 661, 712, 763, 75, 209, nil, 139, 16, nil, 1018, 1069, 1120, nil, nil, nil, 1171, nil, nil, nil, nil, - nil, nil, nil, nil, 229, 1273, 214, 1375, 1426, 1477, - 1528, 1579, 3931, 1681, 1732, 1783, 1834, 1885, 1936, 1987, + nil, nil, nil, nil, 239, 1273, 219, 1375, 1426, 1477, + 1528, 1579, 1630, 1681, 1732, 1783, 1834, 1885, 1936, 1987, 2038, 2089, 2140, 2191, 2242, 2293, 2344, 2395, 2446, 2497, - 2548, 2599, 2650, 2701, 2752, 185, 2854, 215, 2956, 3007, - 303, 128, 671, 5333, 3265, nil, 204, -10, 3421, 4401, - nil, 4344, 4287, 4173, 3676, 154, nil, nil, nil, nil, - 149, 87, nil, 126, nil, nil, nil, nil, 3995, 66, - nil, 3, nil, 4116, 153, nil, nil, 4230, nil, 22, - nil, 37, nil, nil, nil, nil, nil, 4, 507, 416, - 620, 8, 110, 405, 569, 518, 212, 314, 4768, 4811, - 4836, 4881, 4906, 4951, 4996, 5041, 5454, 5155, 5212, 5276, - nil, nil, 3211, nil, 5397, 3160, 3058, 2803, 54, 116, - nil, nil, 9, nil, 13, 24, 60, 83, 16, -9, - 10, nil, nil, nil, nil, nil, nil, 5487, 4629, 150, - nil, 208, nil, 236, 190, nil, 253, nil, 282, nil, - 24, -9, nil, 3880, 559, 814, 865, 181, 182, nil, - -13, 201, 193, 107, nil, 118, 111, 967, nil, 1222, - nil, 1324, nil, 3472, nil, nil, nil, nil, 3523, nil, - 3574, 3625, 78, nil, 5509, nil, 105, 1630, 128, 5531, - 3829, 3778, 163, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, 3727, 154, nil, 174, nil, - 115, 157, 3367, nil, 189, 73, 193, 175, 60, 3316, - nil, 172, 203, 207, 209, nil, -31, nil, 209, 3109, - 2905, nil, nil, nil, 5098, nil, nil, 4743, nil, nil, - nil, 4686, 221, 916, 227, nil, nil, nil, nil, 4572, - 4515, 240, 182, nil, nil, nil, 4458, 95, nil, 508, - 256, 238, nil, 264, 268, nil, nil, nil, 268, 272, - nil, 457, nil, nil, nil, 257, 276, nil, nil, 278, - nil, nil, nil, nil, nil, nil, nil, nil, 202, nil, - 151, 49, nil, nil, 294, nil, nil, nil, 295, nil, - 302, nil, 304, nil, nil, nil, nil, nil ] + 2548, 2599, 2650, 2701, 2752, 190, 2854, 221, 2956, 3007, + 620, 60, 711, 4395, 3265, nil, 143, -10, 3421, 4338, + nil, 4224, 4103, 4046, 87, 3727, 24, nil, nil, nil, + nil, 252, 87, nil, 189, nil, nil, nil, nil, 5505, + 4, nil, 66, nil, 4167, 153, nil, nil, 4281, nil, + 22, nil, 134, nil, nil, nil, nil, nil, 3, 303, + 518, 609, 8, 110, 507, 416, 405, 212, 314, 4819, + 4862, 4887, 4932, 4957, 5002, 5047, 5092, 5149, 5206, 5263, + 5327, nil, nil, 253, nil, 5448, 3523, 559, 814, 219, + 237, nil, nil, 13, nil, -9, 24, 77, 128, 5, + 9, 10, nil, nil, nil, nil, nil, nil, 5560, 4680, + 196, nil, 202, nil, 204, 145, nil, 865, nil, 190, + nil, 165, -9, nil, 967, 1222, 1324, 3211, 3472, 87, + 77, nil, -13, 129, 262, 107, nil, 118, 273, 3982, + nil, 3574, nil, 3625, nil, 3931, nil, nil, nil, nil, + 3880, nil, 3829, 3778, 78, nil, 5538, nil, 110, 3676, + 120, 5582, 3367, 3316, 162, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, 3160, 166, nil, + 188, nil, 128, 166, 3109, nil, 198, 73, 203, 181, + 60, 3058, nil, 176, 207, 180, 213, 215, nil, -31, + nil, 214, 2905, 2803, nil, nil, nil, 4452, nil, nil, + 4509, nil, nil, nil, 4566, 239, 916, 240, nil, nil, + nil, nil, 4623, 4737, 253, 193, nil, nil, nil, 4794, + 151, nil, 508, 268, 7, nil, 273, 274, nil, nil, + nil, 274, 276, 278, nil, 457, nil, nil, nil, 277, + 295, nil, nil, 302, nil, nil, nil, nil, nil, nil, + nil, nil, 202, nil, 151, 49, nil, nil, nil, 308, + nil, nil, nil, 309, nil, 310, nil, 312, nil, nil, + nil, nil, nil ] racc_action_default = [ - -211, -212, -1, -2, -3, -4, -7, -9, -10, -15, - -105, -212, -212, -212, -44, -45, -46, -47, -48, -49, + -212, -213, -1, -2, -3, -4, -7, -9, -10, -15, + -105, -213, -213, -213, -44, -45, -46, -47, -48, -49, -50, -51, -52, -53, -54, -55, -56, -57, -58, -59, - -60, -61, -62, -63, -64, -65, -70, -71, -75, -212, - -212, -212, -212, -212, -115, -117, -212, -212, -162, -212, - -212, -212, -175, -176, -177, -212, -179, -186, -187, -188, - -189, -190, -191, -192, -212, -212, -6, -212, -212, -212, - -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, - -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, - -212, -212, -212, -212, -212, -212, -124, -119, -211, -211, - -27, -212, -34, -212, -212, -72, -212, -212, -212, -212, - -82, -212, -212, -212, -212, -211, -134, -153, -154, -116, - -211, -211, -143, -145, -146, -147, -148, -149, -42, -212, - -165, -212, -168, -212, -212, -171, -172, -183, -178, -212, - 368, -5, -8, -11, -12, -13, -14, -212, -17, -18, - -19, -20, -21, -22, -23, -24, -25, -26, -28, -29, - -30, -31, -32, -33, -35, -36, -37, -38, -39, -212, - -40, -100, -212, -76, -212, -204, -210, -198, -195, -193, - -113, -125, -187, -128, -191, -212, -201, -199, -207, -189, - -190, -197, -202, -203, -205, -206, -208, -124, -123, -212, - -122, -212, -41, -193, -67, -77, -212, -80, -193, -158, - -161, -212, -74, -212, -212, -212, -124, -195, -211, -155, - -212, -212, -212, -212, -151, -212, -212, -212, -163, -212, - -166, -212, -169, -212, -180, -181, -182, -184, -212, -16, - -212, -212, -193, -102, -124, -112, -212, -196, -212, -194, - -212, -212, -193, -127, -129, -198, -199, -200, -201, -204, - -207, -209, -210, -120, -121, -194, -212, -69, -212, -79, - -212, -194, -212, -73, -212, -85, -212, -91, -212, -212, - -95, -195, -193, -212, -212, -137, -212, -156, -193, -211, - -212, -144, -152, -150, -43, -164, -167, -174, -170, -173, - -185, -104, -212, -194, -193, -108, -114, -109, -126, -130, - -131, -212, -66, -78, -81, -159, -160, -85, -84, -212, - -212, -91, -90, -212, -212, -99, -94, -96, -212, -212, - -110, -211, -138, -139, -140, -212, -212, -135, -136, -212, - -142, -101, -103, -111, -118, -68, -83, -86, -212, -89, - -212, -212, -106, -107, -212, -157, -132, -141, -212, -88, - -212, -93, -212, -98, -133, -87, -92, -97 ] + -60, -61, -62, -63, -64, -65, -70, -71, -75, -213, + -213, -213, -213, -213, -116, -118, -213, -213, -163, -213, + -213, -213, -176, -177, -178, -213, -180, -187, -188, -189, + -190, -191, -192, -193, -213, -213, -6, -213, -213, -213, + -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, + -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, + -213, -213, -213, -213, -213, -213, -125, -120, -212, -212, + -27, -213, -34, -213, -213, -72, -213, -213, -213, -213, + -82, -213, -213, -213, -213, -213, -212, -135, -154, -155, + -117, -212, -212, -144, -146, -147, -148, -149, -150, -42, + -213, -166, -213, -169, -213, -213, -172, -173, -184, -179, + -213, 373, -5, -8, -11, -12, -13, -14, -213, -17, + -18, -19, -20, -21, -22, -23, -24, -25, -26, -28, + -29, -30, -31, -32, -33, -35, -36, -37, -38, -39, + -213, -40, -100, -213, -76, -213, -205, -211, -199, -196, + -194, -114, -126, -188, -129, -192, -213, -202, -200, -208, + -190, -191, -198, -203, -204, -206, -207, -209, -125, -124, + -213, -123, -213, -41, -194, -67, -77, -213, -80, -194, + -159, -162, -213, -74, -213, -213, -213, -125, -213, -196, + -212, -156, -213, -213, -213, -213, -152, -213, -213, -213, + -164, -213, -167, -213, -170, -213, -181, -182, -183, -185, + -213, -16, -213, -213, -194, -102, -125, -113, -213, -197, + -213, -195, -213, -213, -194, -128, -130, -199, -200, -201, + -202, -205, -208, -210, -211, -121, -122, -195, -213, -69, + -213, -79, -213, -195, -213, -73, -213, -85, -213, -91, + -213, -213, -95, -196, -194, -196, -213, -213, -138, -213, + -157, -194, -212, -213, -145, -153, -151, -43, -165, -168, + -175, -171, -174, -186, -104, -213, -195, -194, -108, -115, + -109, -127, -131, -132, -213, -66, -78, -81, -160, -161, + -85, -84, -213, -213, -91, -90, -213, -213, -99, -94, + -96, -213, -213, -213, -111, -212, -139, -140, -141, -213, + -213, -136, -137, -213, -143, -101, -103, -112, -119, -68, + -83, -86, -213, -89, -213, -213, -106, -107, -110, -213, + -158, -133, -142, -213, -88, -213, -93, -213, -98, -134, + -87, -92, -97 ] racc_goto_table = [ - 2, 120, 3, 100, 102, 103, 105, 138, 126, 171, - 136, 124, 179, 208, 322, 246, 218, 178, 318, 336, - 242, 221, 143, 144, 145, 146, 248, 291, 280, 292, - 306, 109, 111, 112, 113, 217, 66, 199, 201, 129, - 131, 128, 128, 133, 244, 324, 308, 137, 279, 347, - 266, 205, 284, 332, 283, 270, 222, 170, 349, 315, - 346, 354, 147, 134, 128, 148, 149, 150, 151, 152, - 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, - 163, 164, 165, 166, 167, 168, 169, 243, 174, 302, - 198, 198, 327, 142, 203, 1, 128, 141, 211, 311, - 128, 237, 238, 236, nil, nil, 174, nil, nil, nil, - nil, nil, nil, 252, nil, nil, nil, 219, 328, nil, - nil, nil, 219, 224, nil, nil, 288, nil, nil, 329, - nil, nil, 282, nil, nil, 335, nil, 281, nil, nil, - nil, 120, nil, nil, nil, nil, nil, nil, nil, 126, - nil, 343, 124, nil, nil, nil, nil, nil, nil, nil, - 304, nil, nil, nil, 169, nil, nil, 109, 111, 112, - nil, nil, nil, 267, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, 126, nil, 126, 124, nil, 124, - 300, nil, 299, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, 268, 128, 174, nil, - nil, nil, nil, 274, 276, nil, nil, nil, 342, 294, - 285, 294, nil, 297, nil, 133, nil, nil, nil, nil, - 137, nil, 294, 301, nil, nil, nil, nil, nil, 174, - nil, 333, 309, 310, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, 294, nil, nil, - nil, nil, nil, nil, 316, nil, nil, nil, nil, nil, - nil, 128, nil, nil, nil, nil, nil, nil, nil, nil, - nil, 345, nil, nil, nil, nil, nil, nil, nil, nil, - 339, 338, nil, nil, nil, 169, nil, nil, nil, nil, + 2, 139, 3, 100, 102, 103, 105, 121, 172, 137, + 127, 125, 180, 209, 325, 248, 179, 321, 340, 294, + 220, 295, 200, 202, 282, 223, 244, 309, 250, 246, + 327, 109, 111, 112, 113, 219, 66, 311, 281, 130, + 132, 129, 129, 134, 351, 206, 287, 138, 144, 145, + 146, 147, 268, 336, 224, 286, 171, 272, 318, 353, + 350, 359, 148, 135, 129, 149, 150, 151, 152, 153, + 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, + 164, 165, 166, 167, 168, 169, 170, 245, 175, 330, + 199, 199, 305, 143, 204, 1, 129, 142, 212, 239, + 129, 240, 314, 238, nil, nil, nil, 175, nil, nil, + nil, nil, nil, nil, 254, nil, nil, nil, 221, 331, + nil, 333, nil, 221, 226, nil, nil, nil, 291, nil, + nil, nil, 332, 284, nil, nil, nil, 283, 285, 339, + nil, nil, nil, nil, nil, nil, nil, nil, 121, nil, + nil, nil, 127, 125, nil, 347, nil, nil, nil, nil, + nil, nil, 307, nil, nil, 170, nil, nil, 109, 111, + 112, nil, nil, nil, 269, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, 303, nil, 127, 125, + 127, 125, nil, 302, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, 270, 129, 175, + 175, nil, nil, nil, 276, 278, nil, nil, nil, nil, + 346, 297, 288, 297, nil, 300, nil, 134, nil, nil, + nil, nil, 138, nil, 297, 304, nil, nil, nil, nil, + nil, 175, nil, nil, 312, 313, nil, nil, nil, nil, + 337, nil, nil, nil, nil, nil, nil, nil, nil, 297, + nil, nil, nil, nil, nil, nil, 319, nil, nil, nil, + nil, nil, nil, 129, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, 349, nil, nil, nil, nil, nil, + nil, nil, nil, 343, 342, nil, nil, nil, 170, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, 109, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, 109, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, 338, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, 358, nil, - 360, 362 ] + nil, nil, nil, nil, nil, nil, nil, 342, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, 363, nil, 365, 367 ] racc_goto_check = [ - 2, 62, 3, 9, 9, 9, 37, 78, 29, 49, - 74, 35, 54, 42, 45, 53, 63, 52, 44, 64, - 50, 63, 7, 7, 7, 7, 36, 70, 47, 70, - 55, 9, 9, 9, 9, 52, 5, 58, 58, 11, - 11, 9, 9, 9, 56, 48, 59, 9, 46, 43, - 36, 41, 66, 67, 53, 36, 69, 12, 45, 72, + 2, 78, 3, 9, 9, 9, 37, 62, 49, 74, + 29, 35, 54, 42, 45, 53, 52, 44, 64, 70, + 63, 70, 58, 58, 47, 63, 50, 55, 36, 56, + 48, 9, 9, 9, 9, 52, 5, 59, 46, 11, + 11, 9, 9, 9, 43, 41, 66, 9, 7, 7, + 7, 7, 36, 67, 69, 53, 12, 36, 72, 45, 44, 64, 11, 73, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 49, 9, 36, - 9, 9, 47, 6, 11, 1, 9, 5, 11, 36, - 9, 79, 80, 82, nil, nil, 9, nil, nil, nil, - nil, nil, nil, 54, nil, nil, nil, 3, 53, nil, - nil, nil, 3, 3, nil, nil, 42, nil, nil, 36, - nil, nil, 54, nil, nil, 36, nil, 52, nil, nil, - nil, 62, nil, nil, nil, nil, nil, nil, nil, 29, - nil, 36, 35, nil, nil, nil, nil, nil, nil, nil, - 54, nil, nil, nil, 9, nil, nil, 9, 9, 9, - nil, nil, nil, 37, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, 29, nil, 29, 35, nil, 35, - 78, nil, 74, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, 2, 9, 9, nil, - nil, nil, nil, 2, 2, nil, nil, nil, 49, 9, - 3, 9, nil, 9, nil, 9, nil, nil, nil, nil, - 9, nil, 9, 9, nil, nil, nil, nil, nil, 9, - nil, 62, 9, 9, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, 9, nil, nil, + 9, 9, 9, 9, 9, 9, 9, 49, 9, 47, + 9, 9, 36, 6, 11, 1, 9, 5, 11, 79, + 9, 80, 36, 82, nil, nil, nil, 9, nil, nil, + nil, nil, nil, nil, 54, nil, nil, nil, 3, 53, + nil, 53, nil, 3, 3, nil, nil, nil, 42, nil, + nil, nil, 36, 54, nil, nil, nil, 52, 52, 36, + nil, nil, nil, nil, nil, nil, nil, nil, 62, nil, + nil, nil, 29, 35, nil, 36, nil, nil, nil, nil, + nil, nil, 54, nil, nil, 9, nil, nil, 9, 9, + 9, nil, nil, nil, 37, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, 78, nil, 29, 35, + 29, 35, nil, 74, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, 2, 9, 9, + 9, nil, nil, nil, 2, 2, nil, nil, nil, nil, + 49, 9, 3, 9, nil, 9, nil, 9, nil, nil, + nil, nil, 9, nil, 9, 9, nil, nil, nil, nil, + nil, 9, nil, nil, 9, 9, nil, nil, nil, nil, + 62, nil, nil, nil, nil, nil, nil, nil, nil, 9, + nil, nil, nil, nil, nil, nil, 9, nil, nil, nil, + nil, nil, nil, 9, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, 37, nil, nil, nil, nil, nil, + nil, nil, nil, 2, 3, nil, nil, nil, 9, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 9, nil, nil, nil, nil, nil, - nil, 9, nil, nil, nil, nil, nil, nil, nil, nil, - nil, 37, nil, nil, nil, nil, nil, nil, nil, nil, - 2, 3, nil, nil, nil, 9, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, 9, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, 3, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, 3, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, 2, nil, - 2, 2 ] + nil, nil, 2, nil, 2, 2 ] racc_goto_pointer = [ - nil, 95, 0, 2, nil, 32, 26, -46, nil, -8, - nil, -10, -37, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, -39, - nil, nil, nil, nil, nil, -36, -153, -31, nil, nil, - nil, -55, -94, -270, -257, -263, -167, -187, -233, -85, - -152, nil, -79, -163, -84, -217, -130, nil, -61, -203, - nil, nil, -45, -99, -270, nil, -166, -233, nil, -65, - -196, nil, -212, 12, -41, nil, nil, nil, -48, -38, - -37, nil, -36 ] + nil, 95, 0, 2, nil, 32, 26, -20, nil, -8, + nil, -10, -38, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, -37, + nil, nil, nil, nil, nil, -36, -152, -31, nil, nil, + nil, -61, -94, -278, -260, -265, -178, -192, -250, -86, + -147, nil, -80, -164, -84, -222, -146, nil, -76, -214, + nil, nil, -39, -96, -274, nil, -174, -236, nil, -68, + -206, nil, -215, 12, -42, nil, nil, nil, -54, -41, + -39, nil, -37 ] racc_goto_default = [ - nil, nil, 337, 200, 4, 5, 6, 7, 8, 10, - 9, 278, nil, 14, 36, 15, 16, 17, 18, 19, + nil, nil, 341, 201, 4, 5, 6, 7, 8, 10, + 9, 280, nil, 14, 36, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, nil, nil, 37, 38, 106, nil, nil, 110, nil, nil, nil, nil, nil, nil, - nil, 42, nil, nil, nil, 180, nil, 97, nil, 181, - 185, 183, 116, nil, nil, 115, nil, nil, 121, nil, - 122, 123, 209, nil, nil, 52, 53, 55, nil, nil, - nil, 139, nil ] + nil, 42, nil, nil, nil, 181, nil, 97, nil, 182, + 186, 184, 117, nil, nil, 116, nil, nil, 122, nil, + 123, 124, 210, nil, nil, 52, 53, 55, nil, nil, + nil, 140, nil ] racc_reduce_table = [ 0, 0, :racc_error, @@ -688,91 +693,92 @@ racc_reduce_table = [ 6, 89, :_reduce_107, 5, 89, :_reduce_108, 5, 89, :_reduce_109, - 5, 89, :_reduce_110, - 4, 136, :_reduce_111, - 1, 137, :_reduce_112, - 1, 133, :_reduce_113, - 3, 133, :_reduce_114, - 1, 132, :_reduce_115, - 2, 132, :_reduce_116, - 1, 132, :_reduce_117, - 6, 99, :_reduce_118, - 2, 99, :_reduce_119, - 3, 138, :_reduce_120, + 6, 89, :_reduce_110, + 5, 89, :_reduce_111, + 4, 136, :_reduce_112, + 1, 137, :_reduce_113, + 1, 133, :_reduce_114, + 3, 133, :_reduce_115, + 1, 132, :_reduce_116, + 2, 132, :_reduce_117, + 1, 132, :_reduce_118, + 6, 99, :_reduce_119, + 2, 99, :_reduce_120, 3, 138, :_reduce_121, + 3, 138, :_reduce_122, 1, 139, :_reduce_none, 1, 139, :_reduce_none, - 0, 135, :_reduce_124, - 1, 135, :_reduce_125, - 3, 135, :_reduce_126, + 0, 135, :_reduce_125, + 1, 135, :_reduce_126, + 3, 135, :_reduce_127, 1, 141, :_reduce_none, 1, 141, :_reduce_none, 1, 141, :_reduce_none, - 3, 140, :_reduce_130, 3, 140, :_reduce_131, - 6, 103, :_reduce_132, - 7, 104, :_reduce_133, - 1, 146, :_reduce_134, + 3, 140, :_reduce_132, + 6, 103, :_reduce_133, + 7, 104, :_reduce_134, + 1, 146, :_reduce_135, 1, 145, :_reduce_none, 1, 145, :_reduce_none, 1, 147, :_reduce_none, - 2, 147, :_reduce_138, + 2, 147, :_reduce_139, 1, 148, :_reduce_none, 1, 148, :_reduce_none, - 6, 105, :_reduce_141, - 5, 105, :_reduce_142, - 1, 149, :_reduce_143, - 3, 149, :_reduce_144, - 1, 151, :_reduce_145, + 6, 105, :_reduce_142, + 5, 105, :_reduce_143, + 1, 149, :_reduce_144, + 3, 149, :_reduce_145, 1, 151, :_reduce_146, 1, 151, :_reduce_147, + 1, 151, :_reduce_148, 1, 151, :_reduce_none, - 1, 152, :_reduce_149, - 3, 152, :_reduce_150, + 1, 152, :_reduce_150, + 3, 152, :_reduce_151, 1, 150, :_reduce_none, - 2, 150, :_reduce_152, - 1, 143, :_reduce_153, + 2, 150, :_reduce_153, 1, 143, :_reduce_154, - 1, 144, :_reduce_155, - 2, 144, :_reduce_156, - 4, 144, :_reduce_157, - 1, 123, :_reduce_158, - 3, 123, :_reduce_159, - 3, 153, :_reduce_160, - 1, 153, :_reduce_161, - 1, 97, :_reduce_162, - 3, 106, :_reduce_163, - 4, 106, :_reduce_164, - 2, 106, :_reduce_165, - 3, 106, :_reduce_166, - 4, 106, :_reduce_167, - 2, 106, :_reduce_168, - 3, 109, :_reduce_169, - 4, 109, :_reduce_170, - 2, 109, :_reduce_171, - 1, 154, :_reduce_172, - 3, 154, :_reduce_173, - 3, 155, :_reduce_174, + 1, 143, :_reduce_155, + 1, 144, :_reduce_156, + 2, 144, :_reduce_157, + 4, 144, :_reduce_158, + 1, 123, :_reduce_159, + 3, 123, :_reduce_160, + 3, 153, :_reduce_161, + 1, 153, :_reduce_162, + 1, 97, :_reduce_163, + 3, 106, :_reduce_164, + 4, 106, :_reduce_165, + 2, 106, :_reduce_166, + 3, 106, :_reduce_167, + 4, 106, :_reduce_168, + 2, 106, :_reduce_169, + 3, 109, :_reduce_170, + 4, 109, :_reduce_171, + 2, 109, :_reduce_172, + 1, 154, :_reduce_173, + 3, 154, :_reduce_174, + 3, 155, :_reduce_175, 1, 116, :_reduce_none, 1, 116, :_reduce_none, - 1, 156, :_reduce_177, - 2, 157, :_reduce_178, - 1, 158, :_reduce_179, - 1, 160, :_reduce_180, - 1, 161, :_reduce_181, - 2, 159, :_reduce_182, - 1, 162, :_reduce_183, - 1, 163, :_reduce_184, - 2, 163, :_reduce_185, - 1, 112, :_reduce_186, - 1, 115, :_reduce_187, - 1, 113, :_reduce_188, - 1, 114, :_reduce_189, - 1, 108, :_reduce_190, - 1, 107, :_reduce_191, - 1, 110, :_reduce_192, + 1, 156, :_reduce_178, + 2, 157, :_reduce_179, + 1, 158, :_reduce_180, + 1, 160, :_reduce_181, + 1, 161, :_reduce_182, + 2, 159, :_reduce_183, + 1, 162, :_reduce_184, + 1, 163, :_reduce_185, + 2, 163, :_reduce_186, + 1, 112, :_reduce_187, + 1, 115, :_reduce_188, + 1, 113, :_reduce_189, + 1, 114, :_reduce_190, + 1, 108, :_reduce_191, + 1, 107, :_reduce_192, + 1, 110, :_reduce_193, 0, 117, :_reduce_none, - 1, 117, :_reduce_194, + 1, 117, :_reduce_195, 0, 134, :_reduce_none, 1, 134, :_reduce_none, 1, 142, :_reduce_none, @@ -789,11 +795,11 @@ racc_reduce_table = [ 1, 142, :_reduce_none, 1, 142, :_reduce_none, 1, 142, :_reduce_none, - 0, 84, :_reduce_211 ] + 0, 84, :_reduce_212 ] -racc_reduce_n = 212 +racc_reduce_n = 213 -racc_shift_n = 368 +racc_shift_n = 373 racc_token_table = { false => 0, @@ -1788,8 +1794,9 @@ module_eval(<<'.,.,', 'egrammar.ra', 389) module_eval(<<'.,.,', 'egrammar.ra', 404) def _reduce_110(val, _values, result) - result = Factory.RESOURCE(Factory.fqn(token_text(val[0])), val[2]) - loc result, val[0], val[4] + result = Factory.RESOURCE(Factory.fqn(token_text(val[1])), val[3]) + result.form = val[0] + loc result, val[1], val[5] result end @@ -1797,55 +1804,64 @@ module_eval(<<'.,.,', 'egrammar.ra', 404) module_eval(<<'.,.,', 'egrammar.ra', 409) def _reduce_111(val, _values, result) - result = Factory.RESOURCE_BODY(val[0], val[2]) - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 411) - def _reduce_112(val, _values, result) - result = val[0] + result = Factory.RESOURCE(Factory.fqn(token_text(val[0])), val[2]) + loc result, val[0], val[4] + result end .,., module_eval(<<'.,.,', 'egrammar.ra', 414) - def _reduce_113(val, _values, result) - result = [val[0]] + def _reduce_112(val, _values, result) + result = Factory.RESOURCE_BODY(val[0], val[2]) result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 415) +module_eval(<<'.,.,', 'egrammar.ra', 416) + def _reduce_113(val, _values, result) + result = val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 419) def _reduce_114(val, _values, result) - result = val[0].push val[2] + result = [val[0]] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 420) def _reduce_115(val, _values, result) + result = val[0].push val[2] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 425) + def _reduce_116(val, _values, result) result = :virtual result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 421) - def _reduce_116(val, _values, result) - result = :exported - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 422) +module_eval(<<'.,.,', 'egrammar.ra', 426) def _reduce_117(val, _values, result) result = :exported result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 434) +module_eval(<<'.,.,', 'egrammar.ra', 427) def _reduce_118(val, _values, result) + result = :exported + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 439) + def _reduce_119(val, _values, result) result = Factory.COLLECT(val[0], val[1], val[3]) loc result, val[0], val[5] @@ -1853,8 +1869,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 434) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 438) - def _reduce_119(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 443) + def _reduce_120(val, _values, result) result = Factory.COLLECT(val[0], val[1], []) loc result, val[0], val[1] @@ -1862,53 +1878,53 @@ module_eval(<<'.,.,', 'egrammar.ra', 438) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 443) - def _reduce_120(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 448) + def _reduce_121(val, _values, result) result = Factory.VIRTUAL_QUERY(val[1]) ; loc result, val[0], val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 444) - def _reduce_121(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 449) + def _reduce_122(val, _values, result) result = Factory.EXPORTED_QUERY(val[1]) ; loc result, val[0], val[2] result end .,., -# reduce 122 omitted - # reduce 123 omitted -module_eval(<<'.,.,', 'egrammar.ra', 457) - def _reduce_124(val, _values, result) +# reduce 124 omitted + +module_eval(<<'.,.,', 'egrammar.ra', 462) + def _reduce_125(val, _values, result) result = [] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 458) - def _reduce_125(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 463) + def _reduce_126(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 459) - def _reduce_126(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 464) + def _reduce_127(val, _values, result) result = val[0].push(val[2]) result end .,., -# reduce 127 omitted - # reduce 128 omitted # reduce 129 omitted -module_eval(<<'.,.,', 'egrammar.ra', 475) - def _reduce_130(val, _values, result) +# reduce 130 omitted + +module_eval(<<'.,.,', 'egrammar.ra', 480) + def _reduce_131(val, _values, result) result = Factory.ATTRIBUTE_OP(val[0][:value], :'=>', val[2]) loc result, val[0], val[2] @@ -1916,8 +1932,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 475) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 479) - def _reduce_131(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 484) + def _reduce_132(val, _values, result) result = Factory.ATTRIBUTE_OP(val[0][:value], :'+>', val[2]) loc result, val[0], val[2] @@ -1925,8 +1941,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 479) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 489) - def _reduce_132(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 494) + def _reduce_133(val, _values, result) result = add_definition(Factory.DEFINITION(classname(val[1][:value]), val[2], val[4])) loc result, val[0], val[5] # New lexer does not keep track of this, this is done in validation @@ -1938,8 +1954,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 489) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 503) - def _reduce_133(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 508) + def _reduce_134(val, _values, result) # Remove this class' name from the namestack as all nested classes have been parsed namepop result = add_definition(Factory.HOSTCLASS(classname(val[1][:value]), val[2], token_text(val[3]), val[5])) @@ -1949,32 +1965,32 @@ module_eval(<<'.,.,', 'egrammar.ra', 503) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 513) - def _reduce_134(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 518) + def _reduce_135(val, _values, result) namestack(val[0][:value]) ; result = val[0] result end .,., -# reduce 135 omitted - # reduce 136 omitted # reduce 137 omitted -module_eval(<<'.,.,', 'egrammar.ra', 522) - def _reduce_138(val, _values, result) +# reduce 138 omitted + +module_eval(<<'.,.,', 'egrammar.ra', 527) + def _reduce_139(val, _values, result) result = val[1] result end .,., -# reduce 139 omitted - # reduce 140 omitted -module_eval(<<'.,.,', 'egrammar.ra', 539) - def _reduce_141(val, _values, result) +# reduce 141 omitted + +module_eval(<<'.,.,', 'egrammar.ra', 544) + def _reduce_142(val, _values, result) result = add_definition(Factory.NODE(val[1], val[2], val[4])) loc result, val[0], val[5] @@ -1982,8 +1998,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 539) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 543) - def _reduce_142(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 548) + def _reduce_143(val, _values, result) result = add_definition(Factory.NODE(val[1], val[2], nil)) loc result, val[0], val[4] @@ -1991,347 +2007,345 @@ module_eval(<<'.,.,', 'egrammar.ra', 543) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 553) - def _reduce_143(val, _values, result) - result = [result] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 554) +module_eval(<<'.,.,', 'egrammar.ra', 558) def _reduce_144(val, _values, result) - result = val[0].push(val[2]) + result = [result] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 559) def _reduce_145(val, _values, result) - result = val[0] + result = val[0].push(val[2]) result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 560) +module_eval(<<'.,.,', 'egrammar.ra', 564) def _reduce_146(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 561) - def _reduce_147(val, _values, result) - result = Factory.literal(:default); loc result, val[0] - result - end -.,., - -# reduce 148 omitted - module_eval(<<'.,.,', 'egrammar.ra', 565) - def _reduce_149(val, _values, result) - result = Factory.literal(val[0][:value]); loc result, val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 566) - def _reduce_150(val, _values, result) - result = Factory.concat(val[0], '.', val[2][:value]); loc result, val[0], val[2] - result - end -.,., - -# reduce 151 omitted - -module_eval(<<'.,.,', 'egrammar.ra', 571) - def _reduce_152(val, _values, result) - result = val[1] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 577) - def _reduce_153(val, _values, result) + def _reduce_147(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 578) - def _reduce_154(val, _values, result) - error val[0], "'class' is not a valid classname" +module_eval(<<'.,.,', 'egrammar.ra', 566) + def _reduce_148(val, _values, result) + result = Factory.literal(:default); loc result, val[0] + result + end +.,., + +# reduce 149 omitted + +module_eval(<<'.,.,', 'egrammar.ra', 570) + def _reduce_150(val, _values, result) + result = Factory.literal(val[0][:value]); loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 571) + def _reduce_151(val, _values, result) + result = Factory.concat(val[0], '.', val[2][:value]); loc result, val[0], val[2] + result + end +.,., + +# reduce 152 omitted + +module_eval(<<'.,.,', 'egrammar.ra', 576) + def _reduce_153(val, _values, result) + result = val[1] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 582) - def _reduce_155(val, _values, result) - result = [] + def _reduce_154(val, _values, result) + result = val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 583) + def _reduce_155(val, _values, result) + error val[0], "'class' is not a valid classname" + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 587) def _reduce_156(val, _values, result) result = [] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 584) - def _reduce_157(val, _values, result) - result = val[1] - result - end -.,., - module_eval(<<'.,.,', 'egrammar.ra', 588) - def _reduce_158(val, _values, result) - result = [val[0]] + def _reduce_157(val, _values, result) + result = [] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 589) - def _reduce_159(val, _values, result) - result = val[0].push(val[2]) + def _reduce_158(val, _values, result) + result = val[1] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 593) - def _reduce_160(val, _values, result) - result = Factory.PARAM(val[0][:value], val[2]) ; loc result, val[0] + def _reduce_159(val, _values, result) + result = [val[0]] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 594) + def _reduce_160(val, _values, result) + result = val[0].push(val[2]) + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 598) def _reduce_161(val, _values, result) + result = Factory.PARAM(val[0][:value], val[2]) ; loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 599) + def _reduce_162(val, _values, result) result = Factory.PARAM(val[0][:value]); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 607) - def _reduce_162(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 612) + def _reduce_163(val, _values, result) result = Factory.fqn(val[0][:value]).var ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 613) - def _reduce_163(val, _values, result) - result = Factory.LIST(val[1]); loc result, val[0], val[2] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 614) - def _reduce_164(val, _values, result) - result = Factory.LIST(val[1]); loc result, val[0], val[3] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 615) - def _reduce_165(val, _values, result) - result = Factory.literal([]) ; loc result, val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 616) - def _reduce_166(val, _values, result) - result = Factory.LIST(val[1]); loc result, val[0], val[2] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 617) - def _reduce_167(val, _values, result) - result = Factory.LIST(val[1]); loc result, val[0], val[3] - result - end -.,., - module_eval(<<'.,.,', 'egrammar.ra', 618) - def _reduce_168(val, _values, result) + def _reduce_164(val, _values, result) + result = Factory.LIST(val[1]); loc result, val[0], val[2] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 619) + def _reduce_165(val, _values, result) + result = Factory.LIST(val[1]); loc result, val[0], val[3] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 620) + def _reduce_166(val, _values, result) result = Factory.literal([]) ; loc result, val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 621) - def _reduce_169(val, _values, result) - result = Factory.HASH(val[1]); loc result, val[0], val[2] + def _reduce_167(val, _values, result) + result = Factory.LIST(val[1]); loc result, val[0], val[2] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 622) - def _reduce_170(val, _values, result) - result = Factory.HASH(val[1]); loc result, val[0], val[3] + def _reduce_168(val, _values, result) + result = Factory.LIST(val[1]); loc result, val[0], val[3] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 623) - def _reduce_171(val, _values, result) - result = Factory.literal({}) ; loc result, val[0], val[3] + def _reduce_169(val, _values, result) + result = Factory.literal([]) ; loc result, val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 626) - def _reduce_172(val, _values, result) - result = [val[0]] + def _reduce_170(val, _values, result) + result = Factory.HASH(val[1]); loc result, val[0], val[2] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 627) + def _reduce_171(val, _values, result) + result = Factory.HASH(val[1]); loc result, val[0], val[3] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 628) + def _reduce_172(val, _values, result) + result = Factory.literal({}) ; loc result, val[0], val[3] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 631) def _reduce_173(val, _values, result) + result = [val[0]] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 632) + def _reduce_174(val, _values, result) result = val[0].push val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 630) - def _reduce_174(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 635) + def _reduce_175(val, _values, result) result = Factory.KEY_ENTRY(val[0], val[2]); loc result, val[1] result end .,., -# reduce 175 omitted - # reduce 176 omitted -module_eval(<<'.,.,', 'egrammar.ra', 636) - def _reduce_177(val, _values, result) +# reduce 177 omitted + +module_eval(<<'.,.,', 'egrammar.ra', 641) + def _reduce_178(val, _values, result) result = Factory.literal(val[0][:value]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 637) - def _reduce_178(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 642) + def _reduce_179(val, _values, result) result = Factory.string(val[0], *val[1]) ; loc result, val[0], val[1][-1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 638) - def _reduce_179(val, _values, result) - result = Factory.literal(val[0][:value]); loc result, val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 639) +module_eval(<<'.,.,', 'egrammar.ra', 643) def _reduce_180(val, _values, result) result = Factory.literal(val[0][:value]); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 640) +module_eval(<<'.,.,', 'egrammar.ra', 644) def _reduce_181(val, _values, result) result = Factory.literal(val[0][:value]); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 641) - def _reduce_182(val, _values, result) - result = [val[0]] + val[1] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 642) - def _reduce_183(val, _values, result) - result = Factory.TEXT(val[0]) - result - end -.,., - module_eval(<<'.,.,', 'egrammar.ra', 645) - def _reduce_184(val, _values, result) - result = [val[0]] + def _reduce_182(val, _values, result) + result = Factory.literal(val[0][:value]); loc result, val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 646) - def _reduce_185(val, _values, result) + def _reduce_183(val, _values, result) result = [val[0]] + val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 648) - def _reduce_186(val, _values, result) - result = Factory.NUMBER(val[0][:value]) ; loc result, val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 649) - def _reduce_187(val, _values, result) - result = Factory.QNAME_OR_NUMBER(val[0][:value]) ; loc result, val[0] +module_eval(<<'.,.,', 'egrammar.ra', 647) + def _reduce_184(val, _values, result) + result = Factory.TEXT(val[0]) result end .,., module_eval(<<'.,.,', 'egrammar.ra', 650) - def _reduce_188(val, _values, result) - result = Factory.QREF(val[0][:value]) ; loc result, val[0] + def _reduce_185(val, _values, result) + result = [val[0]] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 651) - def _reduce_189(val, _values, result) - result = Factory.literal(:undef); loc result, val[0] + def _reduce_186(val, _values, result) + result = [val[0]] + val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 652) +module_eval(<<'.,.,', 'egrammar.ra', 653) + def _reduce_187(val, _values, result) + result = Factory.NUMBER(val[0][:value]) ; loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 654) + def _reduce_188(val, _values, result) + result = Factory.QNAME_OR_NUMBER(val[0][:value]) ; loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 655) + def _reduce_189(val, _values, result) + result = Factory.QREF(val[0][:value]) ; loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 656) def _reduce_190(val, _values, result) - result = Factory.literal(:default); loc result, val[0] + result = Factory.literal(:undef); loc result, val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 657) def _reduce_191(val, _values, result) + result = Factory.literal(:default); loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 662) + def _reduce_192(val, _values, result) result = Factory.literal(val[0][:value]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 660) - def _reduce_192(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 665) + def _reduce_193(val, _values, result) result = Factory.literal(val[0][:value]); loc result, val[0] result end .,., -# reduce 193 omitted +# reduce 194 omitted -module_eval(<<'.,.,', 'egrammar.ra', 666) - def _reduce_194(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 671) + def _reduce_195(val, _values, result) result = nil result end .,., -# reduce 195 omitted - # reduce 196 omitted # reduce 197 omitted @@ -2362,8 +2376,10 @@ module_eval(<<'.,.,', 'egrammar.ra', 666) # reduce 210 omitted -module_eval(<<'.,.,', 'egrammar.ra', 689) - def _reduce_211(val, _values, result) +# reduce 211 omitted + +module_eval(<<'.,.,', 'egrammar.ra', 694) + def _reduce_212(val, _values, result) result = nil result end diff --git a/spec/unit/pops/parser/parse_resource_spec.rb b/spec/unit/pops/parser/parse_resource_spec.rb index 7d2b54d10..ee7e13445 100644 --- a/spec/unit/pops/parser/parse_resource_spec.rb +++ b/spec/unit/pops/parser/parse_resource_spec.rb @@ -126,6 +126,20 @@ describe "egrammar parsing resource declarations" do ].join("\n") end + it "@class { 'cname': }" do + dump(parse("@class { 'cname': }")).should == [ + "(virtual-resource class", + " ('cname'))" + ].join("\n") + end + + it "@@class { 'cname': }" do + dump(parse("@@class { 'cname': }")).should == [ + "(exported-resource class", + " ('cname'))" + ].join("\n") + end + it "class { 'cname': x => 1, y => 2}" do dump(parse("class { 'cname': x => 1, y => 2}")).should == [ "(resource class", From 513ece66ef32788f012da0be5c26c4f20586a48b Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 17 Dec 2013 01:39:02 +0100 Subject: [PATCH 232/800] (maint) Add Issue for reassignment of variable --- lib/puppet/pops/issues.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/puppet/pops/issues.rb b/lib/puppet/pops/issues.rb index f5b9017b2..c5cb59288 100644 --- a/lib/puppet/pops/issues.rb +++ b/lib/puppet/pops/issues.rb @@ -159,6 +159,11 @@ module Puppet::Pops::Issues "Illegal attempt to assign to '#{label.a_an(semantic)}'. Not an assignable reference" end + # Variables are immutable, cannot reassign in the same assignment scope + ILLEGAL_REASSIGNMENT = hard_issue :ILLEGAL_REASSIGNMENT, :name do + "Cannot reassign variable #{name}" + end + # Assignment cannot be made to numeric match result variables ILLEGAL_NUMERIC_ASSIGNMENT = issue :ILLEGAL_NUMERIC_ASSIGNMENT, :varname do "Illegal attempt to assign to the numeric match result variable '$#{varname}'. Numeric variables are not assignable" From 65e47396f84763317f5fdd4a7b426441dc26215e Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 17 Dec 2013 01:47:46 +0100 Subject: [PATCH 233/800] (maint) Move 'jeff_append_to_array' from acceptance to integration test The test only checked if it was possible to append to an array in an outer scope by creating two notify resource based on the array. This test does not have to run as part of the acceptance tests and was moved to catalog_spec in integration. This also makes catalog_spec run for both current and future parser. (The future parser still has issues at this point; to be fixed in later commits). --- acceptance/tests/jeff_append_to_array.rb | 20 --- spec/integration/parser/catalog_spec.rb | 150 ++++++++++++++--------- 2 files changed, 95 insertions(+), 75 deletions(-) delete mode 100644 acceptance/tests/jeff_append_to_array.rb diff --git a/acceptance/tests/jeff_append_to_array.rb b/acceptance/tests/jeff_append_to_array.rb deleted file mode 100644 index 1fab8ad3c..000000000 --- a/acceptance/tests/jeff_append_to_array.rb +++ /dev/null @@ -1,20 +0,0 @@ -# Ported from the acceptance test suite. -test_name "Jeff: Append to Array" - -manifest = %q{ - class parent { - $arr1 = [ "parent array element" ] - } - class parent::child inherits parent { - $arr1 += [ "child array element" ] - notify { $arr1: } - } - include parent::child -} - -agents.each do |host| - apply_manifest_on(host, manifest) do - assert_match(/parent array element/, stdout, "#{host}: parent missing") - assert_match(/child array element/, stdout, "#{host}: child missing") - end -end diff --git a/spec/integration/parser/catalog_spec.rb b/spec/integration/parser/catalog_spec.rb index c11e251dd..e37eb591a 100644 --- a/spec/integration/parser/catalog_spec.rb +++ b/spec/integration/parser/catalog_spec.rb @@ -3,73 +3,113 @@ require 'matchers/include_in_order' require 'puppet_spec/compiler' require 'puppet/indirector/catalog/compiler' -describe "Transmission of the catalog to the agent" do +describe "A catalog" do include PuppetSpec::Compiler - it "preserves the order in which the resources are added to the catalog" do - resources_in_declaration_order = ["Class[First]", - "Second[position]", - "Class[Third]", - "Fourth[position]"] + shared_examples_for "when compiled" do + context "when transmitted to the agent" do - master_catalog, agent_catalog = master_and_agent_catalogs_for(<<-EOM) - define fourth() { } - class third { } + it "preserves the order in which the resources are added to the catalog" do + resources_in_declaration_order = ["Class[First]", + "Second[position]", + "Class[Third]", + "Fourth[position]"] - define second() { - fourth { "position": } - } + master_catalog, agent_catalog = master_and_agent_catalogs_for(<<-EOM) + define fourth() { } + class third { } - class first { - second { "position": } - class { "third": } - } + define second() { + fourth { "position": } + } - include first - EOM + class first { + second { "position": } + class { "third": } + } - expect(resources_in(master_catalog)). - to include_in_order(*resources_in_declaration_order) - expect(resources_in(agent_catalog)). - to include_in_order(*resources_in_declaration_order) + include first + EOM + + expect(resources_in(master_catalog)). + to include_in_order(*resources_in_declaration_order) + expect(resources_in(agent_catalog)). + to include_in_order(*resources_in_declaration_order) + end + + it "does not contain unrealized, virtual resources" do + virtual_resources = ["Unrealized[unreal]", "Class[Unreal]"] + + master_catalog, agent_catalog = master_and_agent_catalogs_for(<<-EOM) + class unreal { } + define unrealized() { } + + class real { + @unrealized { "unreal": } + @class { "unreal": } + } + + include real + EOM + + expect(resources_in(master_catalog)).to_not include(*virtual_resources) + expect(resources_in(agent_catalog)).to_not include(*virtual_resources) + end + + it "does not contain unrealized, exported resources" do + exported_resources = ["Unrealized[unreal]", "Class[Unreal]"] + + master_catalog, agent_catalog = master_and_agent_catalogs_for(<<-EOM) + class unreal { } + define unrealized() { } + + class real { + @@unrealized { "unreal": } + @@class { "unreal": } + } + + include real + EOM + + expect(resources_in(master_catalog)).to_not include(*exported_resources) + expect(resources_in(agent_catalog)).to_not include(*exported_resources) + end + end + + it "compiles resource creation from appended array as two separate resources" do + # moved here from acceptance test "jeff_append_to_array.rb" + master_catalog = master_catalog_for(<<-EOM) + class parent { + $arr1 = [ "parent array element" ] + } + class parent::child inherits parent { + $arr1 += ["child array element"] + notify { $arr1: } + } + include parent::child + EOM + expect(resources_in(master_catalog)).to include('Notify[parent array element]', 'Notify[child array element]') + end end - it "does not contain unrealized, virtual resources" do - virtual_resources = ["Unrealized[unreal]", "Class[Unreal]"] - - master_catalog, agent_catalog = master_and_agent_catalogs_for(<<-EOM) - class unreal { } - define unrealized() { } - - class real { - @unrealized { "unreal": } - @class { "unreal": } - } - - include real - EOM - - expect(resources_in(master_catalog)).to_not include(*virtual_resources) - expect(resources_in(agent_catalog)).to_not include(*virtual_resources) + describe 'using classic parser' do + before :each do + Puppet[:parser] = 'current' + end + it_behaves_like 'when compiled' do + end end - it "does not contain unrealized, exported resources" do - exported_resources = ["Unrealized[unreal]", "Class[Unreal]"] + describe 'using future parser' do + before :each do + Puppet[:parser] = 'future' + end + it_behaves_like 'when compiled' do + end + end - master_catalog, agent_catalog = master_and_agent_catalogs_for(<<-EOM) - class unreal { } - define unrealized() { } - - class real { - @@unrealized { "unreal": } - @@class { "unreal": } - } - - include real - EOM - - expect(resources_in(master_catalog)).to_not include(*exported_resources) - expect(resources_in(agent_catalog)).to_not include(*exported_resources) + def master_catalog_for(manifest) + master_catalog = Puppet::Resource::Catalog::Compiler.new.filter(compile_to_catalog(manifest)) end def master_and_agent_catalogs_for(manifest) From a475153c361cce5ef4ff25a5edc90612a25a5054 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 17 Dec 2013 01:49:33 +0100 Subject: [PATCH 234/800] (maint) Make integratin/scope_spec run for both current & future parser This changes scope_spec to run for both current and future parser. (At this point, the future evaluator still has issues; to be fixed in coming committs). --- spec/integration/parser/scope_spec.rb | 1274 +++++++++++++------------ 1 file changed, 663 insertions(+), 611 deletions(-) diff --git a/spec/integration/parser/scope_spec.rb b/spec/integration/parser/scope_spec.rb index 8df54393b..8692323f8 100644 --- a/spec/integration/parser/scope_spec.rb +++ b/spec/integration/parser/scope_spec.rb @@ -13,659 +13,711 @@ describe "Two step scoping for variables" do Puppet.expects(:deprecation_warning).never end - describe "fully qualified variable names" do - it "keeps nodescope separate from topscope" do - expect_the_message_to_be('topscope') do <<-MANIFEST - $c = "topscope" - node default { - $c = "nodescope" - notify { 'something': message => $::c } - } - MANIFEST - end - end - end - - describe "when colliding class and variable names" do - it "finds a topscope variable with the same name as a class" do - expect_the_message_to_be('topscope') do <<-MANIFEST - $c = "topscope" - class c { } - node default { - include c - notify { 'something': message => $c } - } - MANIFEST - end - end - - it "finds a node scope variable with the same name as a class" do - expect_the_message_to_be('nodescope') do <<-MANIFEST - class c { } - node default { - $c = "nodescope" - include c - notify { 'something': message => $c } - } - MANIFEST - end - end - - it "finds a class variable when the class collides with a nodescope variable" do - expect_the_message_to_be('class') do <<-MANIFEST - class c { $b = "class" } - node default { - $c = "nodescope" - include c - notify { 'something': message => $c::b } - } - MANIFEST - end - end - - it "finds a class variable when the class collides with a topscope variable" do - expect_the_message_to_be('class') do <<-MANIFEST - $c = "topscope" - class c { $b = "class" } - node default { - include c - notify { 'something': message => $::c::b } - } - MANIFEST - end - end - end - - describe "when using shadowing and inheritance" do - it "finds value define in the inherited node" do - expect_the_message_to_be('parent_msg') do <<-MANIFEST - $var = "top_msg" - node parent { - $var = "parent_msg" - } - node default inherits parent { - include foo - } - class foo { - notify { 'something': message => $var, } - } - MANIFEST - end - end - - it "finds top scope when the class is included before the node defines the var" do - expect_the_message_to_be('top_msg') do <<-MANIFEST - $var = "top_msg" - node parent { - include foo - } - node default inherits parent { - $var = "default_msg" - } - class foo { - notify { 'something': message => $var, } - } - MANIFEST - end - end - - it "finds top scope when the class is included before the node defines the var" do - expect_the_message_to_be('top_msg') do <<-MANIFEST - $var = "top_msg" - node parent { - include foo - } - node default inherits parent { - $var = "default_msg" - } - class foo { - notify { 'something': message => $var, } - } - MANIFEST - end - end - - it "finds values in its local scope" do - expect_the_message_to_be('local_msg') do <<-MANIFEST - node default { - include baz - } - class foo { - } - class bar inherits foo { - $var = "local_msg" - notify { 'something': message => $var, } - } - class baz { - include bar - } - MANIFEST - end - end - - it "finds values in its inherited scope" do - expect_the_message_to_be('foo_msg') do <<-MANIFEST - node default { - include baz - } - class foo { - $var = "foo_msg" - } - class bar inherits foo { - notify { 'something': message => $var, } - } - class baz { - include bar - } - MANIFEST - end - end - - it "prefers values in its local scope over values in the inherited scope" do - expect_the_message_to_be('local_msg') do <<-MANIFEST - include bar - - class foo { - $var = "inherited" - } - - class bar inherits foo { - $var = "local_msg" - notify { 'something': message => $var, } - } - MANIFEST - end - end - - it "finds a qualified variable by following parent scopes of the specified scope" do - expect_the_message_to_be("from node") do <<-MANIFEST - class c { - notify { 'something': message => "$a::b" } - } - - class a { } - - node default { - $b = "from node" - include a - include c - } - MANIFEST - end - end - - it "finds values in its inherited scope when the inherited class is qualified to the top" do - expect_the_message_to_be('foo_msg') do <<-MANIFEST - node default { - include baz - } - class foo { - $var = "foo_msg" - } - class bar inherits ::foo { - notify { 'something': message => $var, } - } - class baz { - include bar - } - MANIFEST - end - end - - it "prefers values in its local scope over values in the inherited scope when the inherited class is fully qualified" do - expect_the_message_to_be('local_msg') do <<-MANIFEST - include bar - - class foo { - $var = "inherited" - } - - class bar inherits ::foo { - $var = "local_msg" - notify { 'something': message => $var, } - } - MANIFEST - end - end - - it "finds values in top scope when the inherited class is qualified to the top" do - expect_the_message_to_be('top msg') do <<-MANIFEST - $var = "top msg" - class foo { - } - - class bar inherits ::foo { - notify { 'something': message => $var, } - } - - include bar - MANIFEST - end - end - - it "finds values in its inherited scope when the inherited class is a nested class that shadows another class at the top" do - expect_the_message_to_be('inner baz') do <<-MANIFEST - node default { - include foo::bar - } - class baz { - $var = "top baz" - } - class foo { - class baz { - $var = "inner baz" + context 'using current parser' do + describe "using plussignment to change in a new scope" do + it "does not change a string in the parent scope" do + # Expects to be able to concatenate string using += + expect_the_message_to_be('top_msg') do <<-MANIFEST + $var = "top_msg" + class override { + $var += "override" + include foo + } + class foo { + notify { 'something': message => $var, } } + include override + MANIFEST + end + end + end + end + + context 'using future parser' do + before(:each) do + Puppet[:parser] = 'future' + end + + describe "using plussignment to change in a new scope" do + it "does not change a string in the parent scope" do + # Expects to be able to concatenate string using += + expect do + catalog = compile_to_catalog(<<-MANIFEST, Puppet::Node.new('the node')) + $var = "top_msg" + class override { + $var += "override" + include foo + } + class foo { + notify { 'something': message => $var, } + } + + include override + MANIFEST + end.to raise_error(/The value 'top_msg' cannot be converted to Numeric/) + end + end + end + + shared_examples_for "the scope" do + + describe "fully qualified variable names" do + it "keeps nodescope separate from topscope" do + expect_the_message_to_be('topscope') do <<-MANIFEST + $c = "topscope" + node default { + $c = "nodescope" + notify { 'something': message => $::c } + } + MANIFEST + end + end + end + + describe "when colliding class and variable names" do + it "finds a topscope variable with the same name as a class" do + expect_the_message_to_be('topscope') do <<-MANIFEST + $c = "topscope" + class c { } + node default { + include c + notify { 'something': message => $c } + } + MANIFEST + end + end + + it "finds a node scope variable with the same name as a class" do + expect_the_message_to_be('nodescope') do <<-MANIFEST + class c { } + node default { + $c = "nodescope" + include c + notify { 'something': message => $c } + } + MANIFEST + end + end + + it "finds a class variable when the class collides with a nodescope variable" do + expect_the_message_to_be('class') do <<-MANIFEST + class c { $b = "class" } + node default { + $c = "nodescope" + include c + notify { 'something': message => $c::b } + } + MANIFEST + end + end + + it "finds a class variable when the class collides with a topscope variable" do + expect_the_message_to_be('class') do <<-MANIFEST + $c = "topscope" + class c { $b = "class" } + node default { + include c + notify { 'something': message => $::c::b } + } + MANIFEST + end + end + end + + describe "when using shadowing and inheritance" do + it "finds value define in the inherited node" do + expect_the_message_to_be('parent_msg') do <<-MANIFEST + $var = "top_msg" + node parent { + $var = "parent_msg" + } + node default inherits parent { + include foo + } + class foo { + notify { 'something': message => $var, } + } + MANIFEST + end + end + + it "finds top scope when the class is included before the node defines the var" do + expect_the_message_to_be('top_msg') do <<-MANIFEST + $var = "top_msg" + node parent { + include foo + } + node default inherits parent { + $var = "default_msg" + } + class foo { + notify { 'something': message => $var, } + } + MANIFEST + end + end + + it "finds top scope when the class is included before the node defines the var" do + expect_the_message_to_be('top_msg') do <<-MANIFEST + $var = "top_msg" + node parent { + include foo + } + node default inherits parent { + $var = "default_msg" + } + class foo { + notify { 'something': message => $var, } + } + MANIFEST + end + end + + it "finds values in its local scope" do + expect_the_message_to_be('local_msg') do <<-MANIFEST + node default { + include baz + } + class foo { + } + class bar inherits foo { + $var = "local_msg" + notify { 'something': message => $var, } + } + class baz { + include bar + } + MANIFEST + end + end + + it "finds values in its inherited scope" do + expect_the_message_to_be('foo_msg') do <<-MANIFEST + node default { + include baz + } + class foo { + $var = "foo_msg" + } + class bar inherits foo { + notify { 'something': message => $var, } + } + class baz { + include bar + } + MANIFEST + end + end + + it "prefers values in its local scope over values in the inherited scope" do + expect_the_message_to_be('local_msg') do <<-MANIFEST + include bar + + class foo { + $var = "inherited" + } + + class bar inherits foo { + $var = "local_msg" + notify { 'something': message => $var, } + } + MANIFEST + end + end + + it "finds a qualified variable by following parent scopes of the specified scope" do + expect_the_message_to_be("from node") do <<-MANIFEST + class c { + notify { 'something': message => "$a::b" } + } + + class a { } + + node default { + $b = "from node" + include a + include c + } + MANIFEST + end + end + + it "finds values in its inherited scope when the inherited class is qualified to the top" do + expect_the_message_to_be('foo_msg') do <<-MANIFEST + node default { + include baz + } + class foo { + $var = "foo_msg" + } + class bar inherits ::foo { + notify { 'something': message => $var, } + } + class baz { + include bar + } + MANIFEST + end + end + + it "prefers values in its local scope over values in the inherited scope when the inherited class is fully qualified" do + expect_the_message_to_be('local_msg') do <<-MANIFEST + include bar + + class foo { + $var = "inherited" + } + + class bar inherits ::foo { + $var = "local_msg" + notify { 'something': message => $var, } + } + MANIFEST + end + end + + it "finds values in top scope when the inherited class is qualified to the top" do + expect_the_message_to_be('top msg') do <<-MANIFEST + $var = "top msg" + class foo { + } + + class bar inherits ::foo { + notify { 'something': message => $var, } + } + + include bar + MANIFEST + end + end + + it "finds values in its inherited scope when the inherited class is a nested class that shadows another class at the top" do + expect_the_message_to_be('inner baz') do <<-MANIFEST + node default { + include foo::bar + } + class baz { + $var = "top baz" + } + class foo { + class baz { + $var = "inner baz" + } + + class bar inherits baz { + notify { 'something': message => $var, } + } + } + MANIFEST + end + end + + it "finds values in its inherited scope when the inherited class is qualified to a nested class and qualified to the top" do + expect_the_message_to_be('top baz') do <<-MANIFEST + node default { + include foo::bar + } + class baz { + $var = "top baz" + } + class foo { + class baz { + $var = "inner baz" + } + + class bar inherits ::baz { + notify { 'something': message => $var, } + } + } + MANIFEST + end + end + + it "finds values in its inherited scope when the inherited class is qualified" do + expect_the_message_to_be('foo_msg') do <<-MANIFEST + node default { + include bar + } + class foo { + class baz { + $var = "foo_msg" + } + } + class bar inherits foo::baz { + notify { 'something': message => $var, } + } + MANIFEST + end + end + + it "prefers values in its inherited scope over those in the node (with intermediate inclusion)" do + expect_the_message_to_be('foo_msg') do <<-MANIFEST + node default { + $var = "node_msg" + include baz + } + class foo { + $var = "foo_msg" + } + class bar inherits foo { + notify { 'something': message => $var, } + } + class baz { + include bar + } + MANIFEST + end + end + + it "prefers values in its inherited scope over those in the node (without intermediate inclusion)" do + expect_the_message_to_be('foo_msg') do <<-MANIFEST + node default { + $var = "node_msg" + include bar + } + class foo { + $var = "foo_msg" + } + class bar inherits foo { + notify { 'something': message => $var, } + } + MANIFEST + end + end + + it "prefers values in its inherited scope over those from where it is included" do + expect_the_message_to_be('foo_msg') do <<-MANIFEST + node default { + include baz + } + class foo { + $var = "foo_msg" + } + class bar inherits foo { + notify { 'something': message => $var, } + } + class baz { + $var = "baz_msg" + include bar + } + MANIFEST + end + end + + it "does not used variables from classes included in the inherited scope" do + expect_the_message_to_be('node_msg') do <<-MANIFEST + node default { + $var = "node_msg" + include bar + } + class quux { + $var = "quux_msg" + } + class foo inherits quux { + } + class baz { + include foo + } class bar inherits baz { notify { 'something': message => $var, } } - } - MANIFEST + MANIFEST + end end - end - it "finds values in its inherited scope when the inherited class is qualified to a nested class and qualified to the top" do - expect_the_message_to_be('top baz') do <<-MANIFEST - node default { - include foo::bar - } - class baz { - $var = "top baz" - } - class foo { - class baz { - $var = "inner baz" + it "does not use a variable from a scope lexically enclosing it" do + expect_the_message_to_be('node_msg') do <<-MANIFEST + node default { + $var = "node_msg" + include other::bar } + class other { + $var = "other_msg" + class bar { + notify { 'something': message => $var, } + } + } + MANIFEST + end + end - class bar inherits ::baz { + it "finds values in its node scope" do + expect_the_message_to_be('node_msg') do <<-MANIFEST + node default { + $var = "node_msg" + include baz + } + class foo { + } + class bar inherits foo { notify { 'something': message => $var, } } - } - MANIFEST - end - end - - it "finds values in its inherited scope when the inherited class is qualified" do - expect_the_message_to_be('foo_msg') do <<-MANIFEST - node default { - include bar - } - class foo { class baz { - $var = "foo_msg" + include bar } - } - class bar inherits foo::baz { - notify { 'something': message => $var, } - } - MANIFEST + MANIFEST + end + end + + it "finds values in its top scope" do + expect_the_message_to_be('top_msg') do <<-MANIFEST + $var = "top_msg" + node default { + include baz + } + class foo { + } + class bar inherits foo { + notify { 'something': message => $var, } + } + class baz { + include bar + } + MANIFEST + end + end + + it "prefers variables from the node over those in the top scope" do + expect_the_message_to_be('node_msg') do <<-MANIFEST + $var = "top_msg" + node default { + $var = "node_msg" + include foo + } + class foo { + notify { 'something': message => $var, } + } + MANIFEST + end + end + + it "finds top scope variables referenced inside a defined type" do + expect_the_message_to_be('top_msg') do <<-MANIFEST + $var = "top_msg" + node default { + foo { "testing": } + } + define foo() { + notify { 'something': message => $var, } + } + MANIFEST + end + end + + it "finds node scope variables referenced inside a defined type" do + expect_the_message_to_be('node_msg') do <<-MANIFEST + $var = "top_msg" + node default { + $var = "node_msg" + foo { "testing": } + } + define foo() { + notify { 'something': message => $var, } + } + MANIFEST + end end end - it "prefers values in its inherited scope over those in the node (with intermediate inclusion)" do - expect_the_message_to_be('foo_msg') do <<-MANIFEST - node default { - $var = "node_msg" - include baz - } - class foo { - $var = "foo_msg" - } - class bar inherits foo { - notify { 'something': message => $var, } - } - class baz { - include bar - } - MANIFEST - end - end - - it "prefers values in its inherited scope over those in the node (without intermediate inclusion)" do - expect_the_message_to_be('foo_msg') do <<-MANIFEST - node default { - $var = "node_msg" - include bar - } - class foo { - $var = "foo_msg" - } - class bar inherits foo { - notify { 'something': message => $var, } - } - MANIFEST - end - end - - it "prefers values in its inherited scope over those from where it is included" do - expect_the_message_to_be('foo_msg') do <<-MANIFEST - node default { - include baz - } - class foo { - $var = "foo_msg" - } - class bar inherits foo { - notify { 'something': message => $var, } - } - class baz { - $var = "baz_msg" - include bar - } - MANIFEST - end - end - - it "does not used variables from classes included in the inherited scope" do - expect_the_message_to_be('node_msg') do <<-MANIFEST - node default { - $var = "node_msg" - include bar - } - class quux { - $var = "quux_msg" - } - class foo inherits quux { - } - class baz { - include foo - } - class bar inherits baz { - notify { 'something': message => $var, } - } - MANIFEST - end - end - - it "does not use a variable from a scope lexically enclosing it" do - expect_the_message_to_be('node_msg') do <<-MANIFEST - node default { - $var = "node_msg" - include other::bar - } - class other { - $var = "other_msg" + describe "in situations that used to have dynamic lookup" do + it "ignores the dynamic value of the var" do + expect_the_message_to_be('node_msg') do <<-MANIFEST + node default { + $var = "node_msg" + include foo + } + class baz { + $var = "baz_msg" + include bar + } + class foo inherits baz { + } class bar { notify { 'something': message => $var, } } - } - MANIFEST + MANIFEST + end + end + + it "finds nil when the only set variable is in the dynamic scope" do + expect_the_message_to_be(nil) do <<-MANIFEST + node default { + include baz + } + class foo { + } + class bar inherits foo { + notify { 'something': message => $var, } + } + class baz { + $var = "baz_msg" + include bar + } + MANIFEST + end + end + + it "ignores the value in the dynamic scope for a defined type" do + expect_the_message_to_be('node_msg') do <<-MANIFEST + node default { + $var = "node_msg" + include foo + } + class foo { + $var = "foo_msg" + bar { "testing": } + } + define bar() { + notify { 'something': message => $var, } + } + MANIFEST + end end end - it "finds values in its node scope" do - expect_the_message_to_be('node_msg') do <<-MANIFEST - node default { - $var = "node_msg" - include baz - } - class foo { - } - class bar inherits foo { - notify { 'something': message => $var, } - } - class baz { - include bar - } - MANIFEST + describe "using plussignment to change in a new scope" do + + it "does not change an array in the parent scope" do + expect_the_message_to_be('top_msg') do <<-MANIFEST + $var = ["top_msg"] + class override { + $var += ["override"] + include foo + } + class foo { + notify { 'something': message => $var, } + } + + include override + MANIFEST + end + end + + it "concatenates two arrays" do + expect_the_message_to_be(['top_msg', 'override']) do <<-MANIFEST + $var = ["top_msg"] + class override { + $var += ["override"] + notify { 'something': message => $var, } + } + + include override + MANIFEST + end + end + + it "leaves an array of arrays unflattened" do + expect_the_message_to_be([['top_msg'], ['override']]) do <<-MANIFEST + $var = [["top_msg"]] + class override { + $var += [["override"]] + notify { 'something': message => $var, } + } + + include override + MANIFEST + end + end + + it "does not change a hash in the parent scope" do + expect_the_message_to_be({"key"=>"top_msg"}) do <<-MANIFEST + $var = { "key" => "top_msg" } + class override { + $var += { "other" => "override" } + include foo + } + class foo { + notify { 'something': message => $var, } + } + + include override + MANIFEST + end + end + + it "replaces a value of a key in the hash instead of merging the values" do + expect_the_message_to_be({"key"=>"override"}) do <<-MANIFEST + $var = { "key" => "top_msg" } + class override { + $var += { "key" => "override" } + notify { 'something': message => $var, } + } + + include override + MANIFEST + end end end - it "finds values in its top scope" do - expect_the_message_to_be('top_msg') do <<-MANIFEST - $var = "top_msg" - node default { - include baz - } - class foo { - } - class bar inherits foo { - notify { 'something': message => $var, } - } - class baz { - include bar - } - MANIFEST - end - end + describe "when using an enc" do + it "places enc parameters in top scope" do + enc_node = Puppet::Node.new("the node", { :parameters => { "var" => 'from_enc' } }) - it "prefers variables from the node over those in the top scope" do - expect_the_message_to_be('node_msg') do <<-MANIFEST - $var = "top_msg" - node default { - $var = "node_msg" - include foo - } - class foo { + expect_the_message_to_be('from_enc', enc_node) do <<-MANIFEST notify { 'something': message => $var, } - } - MANIFEST + MANIFEST + end end - end - it "finds top scope variables referenced inside a defined type" do - expect_the_message_to_be('top_msg') do <<-MANIFEST - $var = "top_msg" - node default { - foo { "testing": } - } - define foo() { - notify { 'something': message => $var, } - } - MANIFEST + it "does not allow the enc to specify an existing top scope var" do + enc_node = Puppet::Node.new("the_node", { :parameters => { "var" => 'from_enc' } }) + + expect { + compile_to_catalog("$var = 'top scope'", enc_node) + }.to raise_error( + Puppet::Error, + /Cannot reassign variable var at line 1(\:6)? on node the_node/ + ) end - end - it "finds node scope variables referenced inside a defined type" do - expect_the_message_to_be('node_msg') do <<-MANIFEST - $var = "top_msg" - node default { - $var = "node_msg" - foo { "testing": } - } - define foo() { - notify { 'something': message => $var, } - } - MANIFEST + it "evaluates enc classes in top scope when there is no node" do + enc_node = Puppet::Node.new("the node", { :classes => ['foo'], :parameters => { "var" => 'from_enc' } }) + + expect_the_message_to_be('from_enc', enc_node) do <<-MANIFEST + class foo { + notify { 'something': message => $var, } + } + MANIFEST + end + end + + it "evaluates enc classes in the node scope when there is a matching node" do + enc_node = Puppet::Node.new("the_node", { :classes => ['foo'] }) + + expect_the_message_to_be('from matching node', enc_node) do <<-MANIFEST + node inherited { + $var = "from inherited" + } + + node the_node inherits inherited { + $var = "from matching node" + } + + class foo { + notify { 'something': message => $var, } + } + MANIFEST + end end end end - describe "in situations that used to have dynamic lookup" do - it "ignores the dynamic value of the var" do - expect_the_message_to_be('node_msg') do <<-MANIFEST - node default { - $var = "node_msg" - include foo - } - class baz { - $var = "baz_msg" - include bar - } - class foo inherits baz { - } - class bar { - notify { 'something': message => $var, } - } - MANIFEST - end + describe 'using classic parser' do + before :each do + Puppet[:parser] = 'current' end - - it "finds nil when the only set variable is in the dynamic scope" do - expect_the_message_to_be(nil) do <<-MANIFEST - node default { - include baz - } - class foo { - } - class bar inherits foo { - notify { 'something': message => $var, } - } - class baz { - $var = "baz_msg" - include bar - } - MANIFEST - end - end - - it "ignores the value in the dynamic scope for a defined type" do - expect_the_message_to_be('node_msg') do <<-MANIFEST - node default { - $var = "node_msg" - include foo - } - class foo { - $var = "foo_msg" - bar { "testing": } - } - define bar() { - notify { 'something': message => $var, } - } - MANIFEST - end + it_behaves_like 'the scope' do end end - describe "using plussignment to change in a new scope" do - it "does not change a string in the parent scope" do - expect_the_message_to_be('top_msg') do <<-MANIFEST - $var = "top_msg" - class override { - $var += "override" - include foo - } - class foo { - notify { 'something': message => $var, } - } - - include override - MANIFEST - end + describe 'using future parser' do + before :each do + Puppet[:parser] = 'future' end - - it "does not change an array in the parent scope" do - expect_the_message_to_be('top_msg') do <<-MANIFEST - $var = ["top_msg"] - class override { - $var += ["override"] - include foo - } - class foo { - notify { 'something': message => $var, } - } - - include override - MANIFEST - end - end - - it "concatenates two arrays" do - expect_the_message_to_be(['top_msg', 'override']) do <<-MANIFEST - $var = ["top_msg"] - class override { - $var += ["override"] - notify { 'something': message => $var, } - } - - include override - MANIFEST - end - end - - it "leaves an array of arrays unflattened" do - expect_the_message_to_be([['top_msg'], ['override']]) do <<-MANIFEST - $var = [["top_msg"]] - class override { - $var += [["override"]] - notify { 'something': message => $var, } - } - - include override - MANIFEST - end - end - - it "does not change a hash in the parent scope" do - expect_the_message_to_be({"key"=>"top_msg"}) do <<-MANIFEST - $var = { "key" => "top_msg" } - class override { - $var += { "other" => "override" } - include foo - } - class foo { - notify { 'something': message => $var, } - } - - include override - MANIFEST - end - end - - it "replaces a value of a key in the hash instead of merging the values" do - expect_the_message_to_be({"key"=>"override"}) do <<-MANIFEST - $var = { "key" => "top_msg" } - class override { - $var += { "key" => "override" } - notify { 'something': message => $var, } - } - - include override - MANIFEST - end + it_behaves_like 'the scope' do end end - describe "when using an enc" do - it "places enc parameters in top scope" do - enc_node = Puppet::Node.new("the node", { :parameters => { "var" => 'from_enc' } }) - - expect_the_message_to_be('from_enc', enc_node) do <<-MANIFEST - notify { 'something': message => $var, } - MANIFEST - end - end - - it "does not allow the enc to specify an existing top scope var" do - enc_node = Puppet::Node.new("the_node", { :parameters => { "var" => 'from_enc' } }) - - expect { - compile_to_catalog("$var = 'top scope'", enc_node) - }.to raise_error( - Puppet::Error, - "Cannot reassign variable var at line 1 on node the_node" - ) - end - - it "evaluates enc classes in top scope when there is no node" do - enc_node = Puppet::Node.new("the node", { :classes => ['foo'], :parameters => { "var" => 'from_enc' } }) - - expect_the_message_to_be('from_enc', enc_node) do <<-MANIFEST - class foo { - notify { 'something': message => $var, } - } - MANIFEST - end - end - - it "evaluates enc classes in the node scope when there is a matching node" do - enc_node = Puppet::Node.new("the_node", { :classes => ['foo'] }) - - expect_the_message_to_be('from matching node', enc_node) do <<-MANIFEST - node inherited { - $var = "from inherited" - } - - node the_node inherits inherited { - $var = "from matching node" - } - - class foo { - notify { 'something': message => $var, } - } - MANIFEST - end - end - end end From e07333c1bf28c485925ea75b5797cfd75b9cf527 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 17 Dec 2013 01:51:43 +0100 Subject: [PATCH 235/800] (maint) Fix issue with dumping a future evaluator Expression When an expression to be evaluated by the future evaluator is embedded in a 3x AST node, it is done so by using an instance of AST::PopsBridge::Expression. This class had a to_s that called dump on the wrong thing thus causing dumping of expressions to fail. --- lib/puppet/parser/ast/pops_bridge.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/parser/ast/pops_bridge.rb b/lib/puppet/parser/ast/pops_bridge.rb index 7b697b031..82f120785 100644 --- a/lib/puppet/parser/ast/pops_bridge.rb +++ b/lib/puppet/parser/ast/pops_bridge.rb @@ -20,7 +20,7 @@ class Puppet::Parser::AST::PopsBridge end def to_s - Puppet::Pops::Model::ModelTreeDumper.new(dump(@value)) + Puppet::Pops::Model::ModelTreeDumper.new.dump(@value) end def evaluate(scope) From a07ee79c070cb4d5d0f9cd88e604ff71c23de4f5 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 17 Dec 2013 01:55:09 +0100 Subject: [PATCH 236/800] (maint) Fix dumping of ast (PopsBridge::Expression). If an ast was dumped using the AstTreeDumper it was not possible to see that it was a wrapped pops expression. This is mostly relevant when debugging and manually dumping results. --- lib/puppet/pops/model/ast_tree_dumper.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/puppet/pops/model/ast_tree_dumper.rb b/lib/puppet/pops/model/ast_tree_dumper.rb index b5c7ce692..00c4ae40a 100644 --- a/lib/puppet/pops/model/ast_tree_dumper.rb +++ b/lib/puppet/pops/model/ast_tree_dumper.rb @@ -25,6 +25,10 @@ class Puppet::Pops::Model::AstTreeDumper < Puppet::Pops::Model::TreeDumper end end + def dump_Expression(o) + "(pops-expression #{Puppet::Pops::Model::ModelTreeDumper.new().dump(o.value)})" + end + def dump_Factory o do_dump(o.current) end From 71e9217136628bc4b14f87dd769977036b3cba1c Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 17 Dec 2013 02:11:33 +0100 Subject: [PATCH 237/800] (maint) Fix flawed test for future evaluator A test expected that $a -= x would return the result of the delete. That is wrong, the expectation is that the RHS should be returned to allow chaining of assignments. The test should have also evaluated $a at the end to get the assigned value. --- spec/unit/pops/evaluator/variables_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/unit/pops/evaluator/variables_spec.rb b/spec/unit/pops/evaluator/variables_spec.rb index b5833cb44..fe93842c4 100644 --- a/spec/unit/pops/evaluator/variables_spec.rb +++ b/spec/unit/pops/evaluator/variables_spec.rb @@ -53,10 +53,10 @@ describe 'Puppet::Pops::Impl::EvaluatorImpl' do # Also see collections_ops_spec.rb where delete via - is fully tested, here only the # the -= operation itself is tested (there are many combinations) # - it 'deleting from non existing value produces nil, nil -= ?' do + it 'deleting from non existing value produces :undef, nil -= ?' do top_scope_block = var('b').set([1,2,3]) - local_scope_block = var('a').minus_set([4]) - evaluate_l(top_scope_block, local_scope_block).should == nil + local_scope_block = block(var('a').minus_set([4]), fqn('a').var) + evaluate_l(top_scope_block, local_scope_block).should == :undef end it 'deletes from a list' do From 1741f3295e424303ee426d86ae7eae6164301de7 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 17 Dec 2013 02:15:01 +0100 Subject: [PATCH 238/800] (maint) Fix problems with scope bound? and exist? methods The exist? method did not consider inherited and containing scopes when checking for existence. The bound? method should have checked if the variable is bound in the scope used for assignment, not just the emphemeral-most scope (which may not be assignable). Fix also guards against ephemeral stack being empty and then switching to the root @symtable (a corner case that probably never occurs). --- lib/puppet/parser/scope.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb index 3eb92e967..9641b41fe 100644 --- a/lib/puppet/parser/scope.rb +++ b/lib/puppet/parser/scope.rb @@ -179,13 +179,15 @@ class Puppet::Parser::Scope # Returns true if the variable of the given name is set to any value (including nil) # def exist?(name) - effective_symtable(true).include?(name) + next_scope = inherited_scope || enclosing_scope + effective_symtable(true).include?(name) || next_scope && next_scope.exist?(name) end - # Returns true if the given name is bound in the current (most nested) scope. + # Returns true if the given name is bound in the current (most nested) scope for assignments. # def bound?(name) - effective_symtable(true).bound?(name) + # Do not look in ephemeral (match scope), the semantics is to answer if an assignable variable is bound + effective_symtable(false).bound?(name) end # Is the value true? This allows us to control the definition of truth @@ -604,7 +606,7 @@ class Puppet::Parser::Scope # @param use_ephemeral [Boolean] whether the top most ephemeral (of any kind) should be used or not def effective_symtable use_ephemeral s = @ephemeral.last - return s if use_ephemeral + return s || @symtable if use_ephemeral # Why check if ephemeral is a Hash ??? Not needed, a hash cannot be a parent scope ??? while s && !(s.is_a?(Hash) || s.is_local_scope?()) From f11b917bbf694d018d17756860a7196adc4e1e29 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 17 Dec 2013 02:18:34 +0100 Subject: [PATCH 239/800] (maint) Fix issues in runtime3 support for future evaluator There were problems with the runtime support: * conversion of nil did not take place, it is now translated to :undef This caused assignment to 3x parmeters to fail since it is passed in a hash, and nil values were not seen as bona fide values. * Assignment to a variable now checks if it is a re-assignment and then issues a propoer failure. This did not work as expected earlier because the 4x evaluator does not pass file/line information when setting the variable in scope. (This because it is expensive to compute for every expression). --- lib/puppet/pops/evaluator/runtime3_support.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/puppet/pops/evaluator/runtime3_support.rb b/lib/puppet/pops/evaluator/runtime3_support.rb index 472827730..92b0e11a8 100644 --- a/lib/puppet/pops/evaluator/runtime3_support.rb +++ b/lib/puppet/pops/evaluator/runtime3_support.rb @@ -26,6 +26,14 @@ module Puppet::Pops::Evaluator::Runtime3Support # setting (extraction is somewhat expensive since 3x requires line instead of offset). # def set_variable(name, value, o, scope) + # Scope also checks this but requires that location information are passed as options. + # Those are expensive to calculate and a test is instead made here to enable failing with better information. + # The error is not specific enough to allow catching it - need to check the actual message text. + # TODO: Improve the messy implementation in Scope. + # + if scope.bound?(name) + fail(Puppet::Pops::Issues::ILLEGAL_REASSIGNMENT, o, {:name => name} ) + end scope.setvar(name, value) end @@ -367,6 +375,10 @@ module Puppet::Pops::Evaluator::Runtime3Support @@convert_visitor.visit_this_1(self, o, scope) end + def convert_NilClass(o, scope) + :undef + end + def convert_Object(o, scope) o end @@ -444,4 +456,5 @@ module Puppet::Pops::Evaluator::Runtime3Support raise ArgumentError, "Internal Error: Configuration of runtime error handling wrong: should have raised exception" end end + end \ No newline at end of file From 1659b26d793067ec5a42b3698831dafe0321e5bc Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 17 Dec 2013 02:22:10 +0100 Subject: [PATCH 240/800] (maint) Fix various issues with +=, -= in future evaluator This fixes several problems with += and -= evaluation in the future evaluator. * -= returned the result, not the RHS * += returned the result if LHS did not exist * neither operator checked for non existing LHS var in strict mode * operating on non existing variable did not assign the RHS --- lib/puppet/pops/evaluator/evaluator_impl.rb | 25 ++++++++++++--------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index 8219678f2..2590ec29d 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -298,14 +298,16 @@ class Puppet::Pops::Evaluator::EvaluatorImpl assign(name, value, o, scope) when :'+=' - # if value does not exist, return RHS (note that type check has already been made so correct type is ensured) - if !variable_exists?(name, scope) - return value - end + # if value does not exist and strict is on, looking it up fails, else it is nil or :undef + existing_value = get_variable_value(name, o, scope) begin - # Delegate to calculate function to deal with check of LHS, and perform ´+´ as arithmetic or concatenation the - # same way as ArithmeticExpression performs `+`. - assign(name, calculate(get_variable_value(name, o, scope), value, :'+', o.left_expr, o.right_expr, scope), o, scope) + if existing_value.nil? || existing_value == :undef + assign(name, value, o, scope) + else + # Delegate to calculate function to deal with check of LHS, and perform ´+´ as arithmetic or concatenation the + # same way as ArithmeticExpression performs `+`. + assign(name, calculate(existing_value, value, :'+', o.left_expr, o.right_expr, scope), o, scope) + end rescue ArgumentError => e fail(Issues::APPEND_FAILED, o, {:message => e.message}) end @@ -314,12 +316,15 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # If an attempt is made to delete values from something that does not exists, the value is :undef (it is guaranteed to not # include any values the user wants deleted anyway :-) # - if !variable_exists?(name, scope) - return nil - end + # if value does not exist and strict is on, looking it up fails, else it is nil or :undef + existing_value = get_variable_value(name, o, scope) begin + if existing_value.nil? || existing_value == :undef + assign(name, :undef, o, scope) + else # Delegate to delete function to deal with check of LHS, and perform deletion assign(name, delete(get_variable_value(name, o, scope), value), o, scope) + end rescue ArgumentError => e fail(Issues::APPEND_FAILED, o, {:message => e.message}) end From 8a68b3d273013b1f5f46a5614bab3a95ea39825b Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 17 Dec 2013 02:38:57 +0100 Subject: [PATCH 241/800] (maint) Differentiate between re-assignment and reserved var assignment When moving the illegal assignment check to the puppet3runtime support for the future evaluator there was no difference between an illegal re-assignment, and an attempt to assign to a reserved variable name (currently only $trusted). --- lib/puppet/pops/evaluator/runtime3_support.rb | 6 +++++- lib/puppet/pops/issues.rb | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/puppet/pops/evaluator/runtime3_support.rb b/lib/puppet/pops/evaluator/runtime3_support.rb index 92b0e11a8..bf92e6b72 100644 --- a/lib/puppet/pops/evaluator/runtime3_support.rb +++ b/lib/puppet/pops/evaluator/runtime3_support.rb @@ -32,7 +32,11 @@ module Puppet::Pops::Evaluator::Runtime3Support # TODO: Improve the messy implementation in Scope. # if scope.bound?(name) - fail(Puppet::Pops::Issues::ILLEGAL_REASSIGNMENT, o, {:name => name} ) + if Puppet::Parser::Scope::RESERVED_VARIABLE_NAMES.include?(name) + fail(Puppet::Pops::Issues::ILLEGAL_RESERVED_ASSIGNMENT, o, {:name => name} ) + else + fail(Puppet::Pops::Issues::ILLEGAL_REASSIGNMENT, o, {:name => name} ) + end end scope.setvar(name, value) end diff --git a/lib/puppet/pops/issues.rb b/lib/puppet/pops/issues.rb index c5cb59288..215853187 100644 --- a/lib/puppet/pops/issues.rb +++ b/lib/puppet/pops/issues.rb @@ -164,6 +164,10 @@ module Puppet::Pops::Issues "Cannot reassign variable #{name}" end + ILLEGAL_RESERVED_ASSIGNMENT = hard_issue :ILLEGAL_RESERVED_ASSIGNMENT, :name do + "Attempt to assign to a reserved variable name: '#{name}'" + end + # Assignment cannot be made to numeric match result variables ILLEGAL_NUMERIC_ASSIGNMENT = issue :ILLEGAL_NUMERIC_ASSIGNMENT, :varname do "Illegal attempt to assign to the numeric match result variable '$#{varname}'. Numeric variables are not assignable" From 6a5adf06e805eceeaa9317c15f6371a544b8cd15 Mon Sep 17 00:00:00 2001 From: Derek Yarnell Date: Mon, 16 Dec 2013 20:47:48 -0500 Subject: [PATCH 242/800] Adds support for building puppet on RHEL7 via spec. --- ext/redhat/puppet.spec.erb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ext/redhat/puppet.spec.erb b/ext/redhat/puppet.spec.erb index 22b3c44ff..3849b15e1 100644 --- a/ext/redhat/puppet.spec.erb +++ b/ext/redhat/puppet.spec.erb @@ -1,15 +1,15 @@ # Augeas and SELinux requirements may be disabled at build time by passing # --without augeas and/or --without selinux to rpmbuild or mock -# Fedora 17 ships with Ruby 1.9, which uses vendorlibdir instead of -# sitelibdir. Adjust our target if installing on f17. -%if 0%{?fedora} >= 17 +# Fedora 17 ships with ruby 1.9, RHEL 7 with ruby 2.0, which use vendorlibdir instead +# of sitelibdir. Adjust our target if installing on f17 or rhel7. +%if 0%{?fedora} >= 17 || 0%{?rhel} >= 7 %global puppet_libdir %(ruby -rrbconfig -e 'puts RbConfig::CONFIG["vendorlibdir"]') %else %global puppet_libdir %(ruby -rrbconfig -e 'puts RbConfig::CONFIG["sitelibdir"]') %endif -%if 0%{?fedora} >= 17 +%if 0%{?fedora} >= 17 || 0%{?rhel} >= 7 %global _with_systemd 1 %else %global _with_systemd 0 @@ -66,7 +66,7 @@ Requires: shadow-utils %if 0%{?_with_systemd} # Required for %%post, %%preun, %%postun Requires: systemd -%if 0%{?fedora} >= 18 +%if 0%{?fedora} >= 18 || 0%{?rhel} >= 7 BuildRequires: systemd %else BuildRequires: systemd-units @@ -120,7 +120,7 @@ install -d -m0755 %{buildroot}%{_localstatedir}/run/puppet install -d -m0750 %{buildroot}%{_localstatedir}/log/puppet %if 0%{?_with_systemd} -# Systemd for fedora >= 17 +# Systemd for fedora >= 17 or el 7 %{__install} -d -m0755 %{buildroot}%{_unitdir} install -Dp -m0644 ext/systemd/puppet.service %{buildroot}%{_unitdir}/puppet.service ln -s %{_unitdir}/puppet.service %{buildroot}%{_unitdir}/puppetagent.service @@ -163,7 +163,7 @@ vimdir=%{buildroot}%{_datadir}/vim/vimfiles install -Dp -m0644 ext/vim/ftdetect/puppet.vim $vimdir/ftdetect/puppet.vim install -Dp -m0644 ext/vim/syntax/puppet.vim $vimdir/syntax/puppet.vim -%if 0%{?fedora} >= 15 +%if 0%{?fedora} >= 15 || 0%{?rhel} >= 7 # Setup tmpfiles.d config mkdir -p %{buildroot}%{_sysconfdir}/tmpfiles.d echo "D /var/run/%{name} 0755 %{name} %{name} -" > \ @@ -198,7 +198,7 @@ cp -pr ext/puppet-nm-dispatcher \ %endif %dir %{_sysconfdir}/puppet %dir %{_sysconfdir}/%{name}/modules -%if 0%{?fedora} >= 15 +%if 0%{?fedora} >= 15 || 0%{?rhel} >= 7 %config(noreplace) %{_sysconfdir}/tmpfiles.d/%{name}.conf %endif %config(noreplace) %{_sysconfdir}/puppet/puppet.conf From 9059da00db24220f43f959c03a2b31cbf8cff699 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Mon, 16 Dec 2013 18:12:20 -0800 Subject: [PATCH 243/800] (Maint) Remove unused code --- lib/puppet/settings/ini_file.rb | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/lib/puppet/settings/ini_file.rb b/lib/puppet/settings/ini_file.rb index e465c420a..a3c2a75df 100644 --- a/lib/puppet/settings/ini_file.rb +++ b/lib/puppet/settings/ini_file.rb @@ -37,42 +37,34 @@ class Puppet::Settings::IniFile def insert_after(line, new_line) new_line.previous = line - insertion_point = @lines.index(line) || @lines.index(section) + insertion_point = @lines.index(line) @lines.insert(insertion_point + 1, new_line) if @lines.length > insertion_point + 2 @lines[insertion_point + 2].previous = new_line end end - def last - @lines.last - end - - def each(&block) - @lines.each(&block) + def sections + @lines.select { |line| line.is_a?(SectionLine) } end def section(name) sections.find { |section| section.name == name } end - def sections - @lines.select { |line| line.is_a?(SectionLine) } - end - def setting(section, name) settings_in(lines_in(section)).find do |line| line.name == name end end - def lines_in(section) + def lines_in(section_name) section_lines = [] - current_section = DEFAULT_SECTION_NAME + current_section_name = DEFAULT_SECTION_NAME @lines.each do |line| if line.is_a?(SectionLine) - current_section = line.name - elsif current_section == section + current_section_name = line.name + elsif current_section_name == section_name section_lines << line end end From 262f5b4028b6a8403436afc4b17b24512ab41ae9 Mon Sep 17 00:00:00 2001 From: "Ethan J. Brown" Date: Mon, 16 Dec 2013 13:31:39 -0800 Subject: [PATCH 244/800] (maint) Report errs correctly on settings apply - Previously when the `settings` catalog was built, it was possible for a `transaction` to fail, but not generate any useful failure information in `report.log` -- resulting in a message to the user of "Got 0 failure(s) while initializing:" - Furthermore, the `any_failed?` check against a `transaction` checks against the `resource_statuses` of the `transaction`, but the code to surface the error was looking in the `report.log`, which was a source of confusion - This commit examines `event` objects inside of `resource_statuses` for those with a `status` of `failure`, and uses them to report off of instead - resulting in clear error messages - Additionally, some minor corresponding doc changes were made Paired-with: Andrew Parker --- lib/puppet/settings.rb | 10 ++++++++-- lib/puppet/transaction/report.rb | 3 +-- spec/unit/settings_spec.rb | 17 ++++++++++++----- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/lib/puppet/settings.rb b/lib/puppet/settings.rb index e688202de..e6474b509 100644 --- a/lib/puppet/settings.rb +++ b/lib/puppet/settings.rb @@ -942,8 +942,14 @@ Generated on #{Time.now}. catalog.apply do |transaction| if transaction.any_failed? report = transaction.report - failures = report.logs.find_all { |log| log.level == :err } - raise "Got #{failures.length} failure(s) while initializing: #{failures.collect { |l| l.to_s }.join("; ")}" + status_failures = report.resource_statuses.values.select { |r| r.failed? } + status_fail_msg = status_failures. + collect(&:events). + flatten. + select { |event| event.status == 'failure' }. + collect { |event| "#{event.resource}: #{event.message}" }.join("; ") + + raise "Got #{status_failures.length} failure(s) while initializing: #{status_fail_msg}" end end diff --git a/lib/puppet/transaction/report.rb b/lib/puppet/transaction/report.rb index 0469965cd..25144ef68 100644 --- a/lib/puppet/transaction/report.rb +++ b/lib/puppet/transaction/report.rb @@ -57,8 +57,7 @@ class Puppet::Transaction::Report attr_accessor :environment # A hash with a map from resource to status - # @return [Hash<{String => String}>] Resource name to status string. - # @todo Uncertain if the types in the hash are correct... + # @return [Hash{String => Puppet::Resource::Status}] Resource name to status. attr_reader :resource_statuses # A list of log messages. diff --git a/spec/unit/settings_spec.rb b/spec/unit/settings_spec.rb index b3dc31f3d..e48d5bf81 100755 --- a/spec/unit/settings_spec.rb +++ b/spec/unit/settings_spec.rb @@ -1372,13 +1372,20 @@ describe Puppet::Settings do @trans.expects(:any_failed?).returns(true) - report = mock 'report' + resource = Puppet::Type.type(:notify).new(:title => 'failed') + status = Puppet::Resource::Status.new(resource) + event = Puppet::Transaction::Event.new( + :name => 'failure', + :status => 'failure', + :message => 'My failure') + status.add_event(event) + + report = Puppet::Transaction::Report.new('apply') + report.add_resource_status(status) + @trans.expects(:report).returns report - log = mock 'log', :to_s => "My failure", :level => :err - report.expects(:logs).returns [log] - - @settings.expects(:raise).with { |msg| msg.include?("My failure") } + @settings.expects(:raise).with(includes("My failure")) @settings.use(:whatever) end end From caf92e6a7ac17778b1a55cc7913a9055249bcf65 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Tue, 17 Dec 2013 07:31:34 -0800 Subject: [PATCH 245/800] (maint) Check --parser setting on the test host Tests was incorrectly trying to check Puppet[:parser] on host executing beaker rather than the remote test hosts which were executing Puppet. --- .../tests/apply/hashes/should_not_reassign.rb | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/acceptance/tests/apply/hashes/should_not_reassign.rb b/acceptance/tests/apply/hashes/should_not_reassign.rb index d0358ab1f..115e54b2b 100755 --- a/acceptance/tests/apply/hashes/should_not_reassign.rb +++ b/acceptance/tests/apply/hashes/should_not_reassign.rb @@ -4,13 +4,16 @@ $my_hash = {'one' => '1', 'two' => '2' } $my_hash['one']='1.5' } -apply_manifest_on(agents, manifest, :acceptable_exit_codes => [1]) do - expected_error_message = - case Puppet[:parser] - when 'future' - "Illegal attempt to assign via [index/key]. Not an assignable reference" - else - "Assigning to the hash 'my_hash' with an existing key 'one'" +agents.each do |host| + parser = on(host, puppet('agent --configprint parser')).stdout.chomp + apply_manifest_on(host, manifest, :acceptable_exit_codes => [1]) do + expected_error_message = + case parser + when 'future' + "Illegal attempt to assign via [index/key]. Not an assignable reference" + else + "Assigning to the hash 'my_hash' with an existing key 'one'" + end + fail_test("didn't find the failure") unless stderr.include?(expected_error_message) end - fail_test("didn't find the failure") unless stderr.include?(expected_error_message) end From 7fb721d42408cd724968087b93fce441ca0736c4 Mon Sep 17 00:00:00 2001 From: Luis Fernandez Alvarez Date: Wed, 20 Nov 2013 17:09:58 +0100 Subject: [PATCH 246/800] (#23219) - Fix support of extra arguments in windows service Without this patch, extra arguments specified in the service definition are not parsed correctly by the service class. --- ext/windows/service/daemon.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ext/windows/service/daemon.rb b/ext/windows/service/daemon.rb index 93701c2a6..8aa6a3ca4 100755 --- a/ext/windows/service/daemon.rb +++ b/ext/windows/service/daemon.rb @@ -25,9 +25,10 @@ class WindowsDaemon < Win32::Daemon FileUtils.mkdir_p(File.dirname(LOG_FILE)) end - def service_main(*argv) - args = argv.join(' ') - @loglevel = LEVELS.index(argv.index('--debug') ? :debug : :notice) + def service_main(*argsv) + argsv = (argsv << ARGV).flatten.compact + args = argsv.join(' ') + @loglevel = LEVELS.index(argsv.index('--debug') ? :debug : :notice) log_notice("Starting service: #{args}") From 1b26c1179d6496fc70496b2b60a7c98a8690678e Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Tue, 17 Dec 2013 14:01:51 -0800 Subject: [PATCH 247/800] (#23373) Restore 'all' functionality The "puppet config print" command used to take a special "all" parameter, but it was lost at some point during the refactoring of the implementation. This restores the ability to print "all" settings. --- lib/puppet/face/config.rb | 2 +- spec/unit/face/config_spec.rb | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/puppet/face/config.rb b/lib/puppet/face/config.rb index d0239ef7e..7d18e8a52 100644 --- a/lib/puppet/face/config.rb +++ b/lib/puppet/face/config.rb @@ -40,7 +40,7 @@ Puppet::Face.define(:config, '0.0.1') do when_invoked do |*args| options = args.pop - args = Puppet.settings.to_a.collect(&:first) if args.empty? + args = Puppet.settings.to_a.collect(&:first) if args.empty? || args == ['all'] values = Puppet.settings.values(Puppet[:environment].to_sym, options[:section].to_sym) if args.length == 1 diff --git a/spec/unit/face/config_spec.rb b/spec/unit/face/config_spec.rb index cb6ef7490..338b532a3 100755 --- a/spec/unit/face/config_spec.rb +++ b/spec/unit/face/config_spec.rb @@ -37,4 +37,10 @@ describe Puppet::Face[:config, '0.0.1'] do subject.print end + + it "prints out all of the settings when asked for 'all'" do + subject.expects(:puts).times(Puppet.settings.to_a.length) + + subject.print('all') + end end From e195de6cfbe162e09f702df22501a7de12bb0605 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Tue, 17 Dec 2013 15:11:28 -0800 Subject: [PATCH 248/800] (#23373) Only parse section lines once The previous code would parse all of the settings of a section for each section header. This meant that if a section showed up in the puppet.conf twice the settings in that section would be parsed twice. This didn't cause any behavioral problems, but is inefficient. This makes sure that we only look at unique sections names. Instead of putting this logic in IniFile it is in ConfigFile in order to preserve IniFile's somewhat naive view of the world. --- lib/puppet/settings/config_file.rb | 27 ++++++++++++++------------- lib/puppet/settings/ini_file.rb | 8 ++++---- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/lib/puppet/settings/config_file.rb b/lib/puppet/settings/config_file.rb index 12f31fd9f..2fab65a84 100644 --- a/lib/puppet/settings/config_file.rb +++ b/lib/puppet/settings/config_file.rb @@ -18,15 +18,13 @@ class Puppet::Settings::ConfigFile result = Conf.new ini = Puppet::Settings::IniFile.parse(StringIO.new(text)) - ini.sections.each do |section| - section_name = section.name.intern - fail_when_illegal_section_name(section_name, file, section.line_number) - section_config = Section.new(section_name) - result.with_section(section_config) + unique_sections_in(ini, file).each do |section_name| + section = Section.new(section_name.to_sym) + result.with_section(section) - ini.lines_in(section.name).each do |line| + ini.lines_in(section_name).each do |line| if line.is_a?(Puppet::Settings::IniFile::SettingLine) - parse_setting(line, section_config) + parse_setting(line, section) elsif line.text !~ /^\s*#|^\s*$/ raise Puppet::Settings::ParseError.new("Could not match line #{line.text}", file, line.line_number) end @@ -73,6 +71,15 @@ class Puppet::Settings::ConfigFile private + def unique_sections_in(ini, file) + ini.section_lines.collect do |section| + if section.name == "application_defaults" || section.name == "global_defaults" + raise Puppet::Error, "Illegal section '#{section.name}' in config file #{file} at line #{section.line_number}" + end + section.name + end.uniq + end + def parse_setting(setting, section) var = setting.name.intern @@ -102,12 +109,6 @@ private { :_meta => {} } end - def fail_when_illegal_section_name(section, file, line) - if section == :application_defaults or section == :global_defaults - raise Puppet::Error, "Illegal section '#{section}' in config file #{file} at line #{line}" - end - end - def extract_fileinfo(string) result = {} value = string.sub(/\{\s*([^}]+)\s*\}/) do diff --git a/lib/puppet/settings/ini_file.rb b/lib/puppet/settings/ini_file.rb index a3c2a75df..5bf4bc057 100644 --- a/lib/puppet/settings/ini_file.rb +++ b/lib/puppet/settings/ini_file.rb @@ -44,12 +44,12 @@ class Puppet::Settings::IniFile end end - def sections + def section_lines @lines.select { |line| line.is_a?(SectionLine) } end - def section(name) - sections.find { |section| section.name == name } + def section_line(name) + section_lines.find { |section| section.name == name } end def setting(section, name) @@ -102,7 +102,7 @@ class Puppet::Settings::IniFile private def add_setting(section_name, name, value) - section = @config.section(section_name) + section = @config.section_line(section_name) if section.nil? previous_line = SectionLine.new("", section_name, "") @config.append(previous_line) From 69ad509389f05c9b9f1f18e68490f8ccd7798b45 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Tue, 17 Dec 2013 15:34:07 -0800 Subject: [PATCH 249/800] (Maint) Fix deprecation message to point to the right alternative --- lib/puppet/settings.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/settings.rb b/lib/puppet/settings.rb index bd360e921..43b463b52 100644 --- a/lib/puppet/settings.rb +++ b/lib/puppet/settings.rb @@ -761,7 +761,7 @@ class Puppet::Settings end def set_value(param, value, type, options = {}) - Puppet.deprecation_warning("Puppet.settings.set_value is deprecated. Use Puppet[] instead.") + Puppet.deprecation_warning("Puppet.settings.set_value is deprecated. Use Puppet[]= instead.") if @value_sets[type] @value_sets[type].set(param, value) end From 74bda42be5fba7c8bffe8524df478005bf5eebf0 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Tue, 17 Dec 2013 15:50:31 -0800 Subject: [PATCH 250/800] (maint) Switch to setting Beaker log_level to debug Beaker 1.3.0 introduces a new log_level, and deprecates the debug setting, so we are using log_level now to get debug (full debug logs and backtrace output). --- acceptance/Rakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance/Rakefile b/acceptance/Rakefile index 5800a493f..073628005 100644 --- a/acceptance/Rakefile +++ b/acceptance/Rakefile @@ -12,7 +12,7 @@ module HarnessOptions :type => 'git', :helper => ['lib/helper.rb'], :tests => ['tests'], - :debug => true, + :log_level => 'debug', :color => false, :root_keys => true, :ssh => { From 1d9692dd648a64765b14878312a8581ff0c590d1 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 17 Dec 2013 18:40:42 +0100 Subject: [PATCH 251/800] (maint) Make [] operator flatten keys for all LHS types except hash. It is expected in the Puppet Language that it is possible to write: $x = ['a', 'b'] SomeType[$x] --- lib/puppet/pops/evaluator/access_operator.rb | 17 +++++++- spec/unit/pops/evaluator/access_ops_spec.rb | 42 ++++++++++++++++++-- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/lib/puppet/pops/evaluator/access_operator.rb b/lib/puppet/pops/evaluator/access_operator.rb index df683b3a5..b3535ac4c 100644 --- a/lib/puppet/pops/evaluator/access_operator.rb +++ b/lib/puppet/pops/evaluator/access_operator.rb @@ -31,6 +31,7 @@ class Puppet::Pops::Evaluator::AccessOperator end def access_String(o, scope, keys) + keys.flatten! result = case keys.size when 0 fail(Puppet::Pops::Issues::BAD_STRING_SLICE_ARITY, @semantic.left_expr, {:actual => keys.size}) @@ -69,6 +70,7 @@ class Puppet::Pops::Evaluator::AccessOperator # Parameterizes a PRegexp Type with a pattern string or r ruby egexp # def access_PRegexpType(o, scope, keys) + keys.flatten! unless keys.size == 1 blamed = keys.size == 0 ? @semantic : @semantic.keys[2] fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_ARITY, blamed, :base_type => o, :min=>1, :actual => keys.size) @@ -80,6 +82,7 @@ class Puppet::Pops::Evaluator::AccessOperator # Evaluates [] with 1 or 2 arguments. One argument is an index lookup, two arguments is a slice from/to. # def access_Array(o, scope, keys) + keys.flatten! case keys.size when 0 fail(Puppet::Pops::Issues::BAD_ARRAY_SLICE_ARITY, @semantic.left_expr, {:actual => keys.size}) @@ -116,10 +119,12 @@ class Puppet::Pops::Evaluator::AccessOperator # Evaluates [] with support for one or more arguments. If more than one argument is used, the result # is an array with each lookup. + # @note + # Does not flatten its keys to enable looking up with a structure # def access_Hash(o, scope, keys) # Look up key in hash, if key is nil or :undef, try alternate form before giving up. - # This makes :undef and nil "be the same key". (The laternative is to always only write one or the other + # This makes :undef and nil "be the same key". (The alternative is to always only write one or the other # in all hashes - that is much harder to guarantee since the Hash is a regular Ruby hash. # result = keys.collect do |k| @@ -150,16 +155,19 @@ class Puppet::Pops::Evaluator::AccessOperator INFINITY = 1.0 / 0.0 def access_PEnumType(o, scope, keys) + keys.flatten! assert_keys(keys, o, 1, INFINITY, String) Puppet::Pops::Types::TypeFactory.enum(*keys) end def access_PVariantType(o, scope, keys) + keys.flatten! assert_keys(keys, o, 1, INFINITY, Puppet::Pops::Types::PAbstractType) Puppet::Pops::Types::TypeFactory.variant(*keys) end def access_PStringType(o, scope, keys) + keys.flatten! assert_keys(keys, o, 1, INFINITY, String) Puppet::Pops::Types::TypeFactory.string(*keys) end @@ -213,11 +221,13 @@ class Puppet::Pops::Evaluator::AccessOperator end def access_PPatternType(o, scope, keys) + keys.flatten! assert_keys(keys, o, 1, INFINITY, String, Regexp, Puppet::Pops::Types::PPatternType, Puppet::Pops::Types::PRegexpType) Puppet::Pops::Types::TypeFactory.pattern(*keys) end def access_PIntegerType(o, scope, keys) + keys.flatten! unless keys.size.between?(1, 2) fail(Puppet::Pops::Issues::BAD_INTEGER_SLICE_ARITY, @semantic, {:actual => keys.size}) end @@ -233,6 +243,7 @@ class Puppet::Pops::Evaluator::AccessOperator end def access_PFloatType(o, scope, keys) + keys.flatten! unless keys.size.between?(1, 2) fail(Puppet::Pops::Issues::BAD_FLOAT_SLICE_ARITY, @semantic, {:actual => keys.size}) end @@ -251,6 +262,7 @@ class Puppet::Pops::Evaluator::AccessOperator # It is not possible to create a collection of Hash types. # def access_PHashType(o, scope, keys) + keys.flatten! keys.each_with_index do |k, index| unless k.is_a?(Puppet::Pops::Types::PAbstractType) fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[index], {:base_type => 'Hash-Type', :actual => k.class}) @@ -275,6 +287,7 @@ class Puppet::Pops::Evaluator::AccessOperator # An Array can create a new Array type. It is not possible to create a collection of Array types. # def access_PArrayType(o, scope, keys) + keys.flatten! if keys.size == 1 unless keys[0].is_a?(Puppet::Pops::Types::PAbstractType) fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[0], {:base_type => 'Array-Type', :actual => keys[0].class}) @@ -300,6 +313,7 @@ class Puppet::Pops::Evaluator::AccessOperator # Resource[File, 'foo']['bar'] # => Value of the 'bar' parameter in the File['foo'] resource # def access_PResourceType(o, scope, keys) + keys.flatten! if keys.size == 0 fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_ARITY, o, :base_type => Puppet::Pops::Types::TypeCalculator.new().string(o), :min => 1, :actual => 0) @@ -361,6 +375,7 @@ class Puppet::Pops::Evaluator::AccessOperator end def access_PHostClassType(o, scope, keys) + keys.flatten! if keys.size == 0 fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_ARITY, o, :base_type => Puppet::Pops::Types::TypeCalculator.new().string(o), :min => 1, :actual => 0) diff --git a/spec/unit/pops/evaluator/access_ops_spec.rb b/spec/unit/pops/evaluator/access_ops_spec.rb index 100d195dc..45472c8fc 100644 --- a/spec/unit/pops/evaluator/access_ops_spec.rb +++ b/spec/unit/pops/evaluator/access_ops_spec.rb @@ -31,6 +31,8 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do it 'can get a substring by giving two keys' do expect(evaluate(literal('abcd')[1,2])).to eql('bc') + # flattens keys + expect(evaluate(literal('abcd')[[1,2]])).to eql('bc') end it 'produces empty string for a substring out of range' do @@ -58,6 +60,8 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do it 'can get a slice of elements using two keys' do expect(evaluate(literal([1,2,3,4])[1,2])).to eql([2,3]) + # flattens keys + expect(evaluate(literal([1,2,3,4])[[1,2]])).to eql([2,3]) end it 'produces nil for a missing entry' do @@ -75,6 +79,10 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do expect(evaluate(literal({'a'=>1,'b'=>2,'c'=>3})['b'])).to eql(2) end + it 'can lookup an array' do + expect(evaluate(literal({[1]=>10,[2]=>20})[[2]])).to eql(20) + end + it 'produces nil for a missing key' do expect(evaluate(literal({'a'=>1,'b'=>2,'c'=>3})['x'])).to eql(nil) end @@ -104,6 +112,10 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do it 'produces an Integer[from, to]' do expr = fqr('Integer')[1, 3] expect(evaluate(expr)).to eql(range(1,3)) + + # arguments are flattened + expr = fqr('Integer')[[1, 3]] + expect(evaluate(expr)).to eql(range(1,3)) end it 'produces an Integer[1]' do @@ -126,6 +138,10 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do it 'produces a Float[from, to]' do expr = fqr('Float')[1, 3] expect(evaluate(expr)).to eql(float_range(1.0,3.0)) + + # arguments are flattened + expr = fqr('Float')[[1, 3]] + expect(evaluate(expr)).to eql(float_range(1.0,3.0)) end it 'produces a Float[1.0]' do @@ -148,11 +164,15 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do expect { evaluate(expr)}.to raise_error(/with one or two arguments/) end - # Hash + # Hash Type # it 'produces a Hash[Literal,String] from the expression Hash[String]' do expr = fqr('Hash')[fqr('String')] expect(evaluate(expr)).to be_the_type(types.hash_of(types.string, types.literal)) + + # arguments are flattened + expr = fqr('Hash')[[fqr('String')]] + expect(evaluate(expr)).to be_the_type(types.hash_of(types.string, types.literal)) end it 'produces a Hash[String,String] from the expression Hash[String, String]' do @@ -170,11 +190,15 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do expect { evaluate(expr)}.to raise_error(/Hash-Type\[\] arguments must be types/) end - # Array + # Array Type # it 'produces an Array[String] from the expression Array[String]' do expr = fqr('Array')[fqr('String')] expect(evaluate(expr)).to be_the_type(types.array_of(types.string)) + + # arguments are flattened + expr = fqr('Array')[[fqr('String')]] + expect(evaluate(expr)).to be_the_type(types.array_of(types.string)) end it 'produces an Array[String] from the expression Array[Integer][String]' do @@ -195,6 +219,10 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do it 'creates a Regexp instance when applied to a Pattern' do regexp_expr = fqr('Regexp')['foo'] expect(evaluate(regexp_expr)).to eql(Puppet::Pops::Types::TypeFactory.regexp('foo')) + + # arguments are flattened + regexp_expr = fqr('Regexp')[['foo']] + expect(evaluate(regexp_expr)).to eql(Puppet::Pops::Types::TypeFactory.regexp('foo')) end # Class @@ -204,7 +232,11 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do expect(evaluate(expr)).to be_the_type(types.host_class('apache')) expr = fqr('Class')[literal('apache')] expect(evaluate(expr)).to be_the_type(types.host_class('apache')) - end + + # arguments are flattened + expr = fqr('Class')[[fqn('apache')]] + expect(evaluate(expr)).to be_the_type(types.host_class('apache')) + end it 'produces same class if no class name is given' do expr = fqr('Class')[fqn('apache')][] @@ -229,6 +261,10 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do expect(evaluate(expr)).to be_the_type(types.resource('File')) expr = fqr('Resource')[literal('File')] expect(evaluate(expr)).to be_the_type(types.resource('File')) + + # arguments are flattened + expr = fqr('Resource')[[fqr('File')]] + expect(evaluate(expr)).to be_the_type(types.resource('File')) end it 'produces a specific resource reference type from File[title]' do From 8c8e101c1c90d07a2fe66af14745990df3006042 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 17 Dec 2013 22:05:21 +0100 Subject: [PATCH 252/800] (maint) Fix failing test for access operator since array is now ok --- spec/unit/pops/evaluator/evaluating_parser_spec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index a9cacc4fa..4ca0b7e0b 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -654,7 +654,6 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do "Class[0]" => /An Integer cannot be used where a String is expected/, "Class[/.*/]" => /A Regexp cannot be used where a String is expected/, "Class[4.1415]" => /A Float cannot be used where a String is expected/, - "Class[[1,2,3]]" => /An Array cannot be used where a String is expected/, "Class[Integer]" => /An Integer-Type cannot be used where a String is expected/, "Class[File['tmp']]" => /A File\['tmp'\] Resource-Reference cannot be used where a String is expected/, }.each do | source, error_pattern| From bc4d7b9a70ade7397c0f77c0c2df682d57ce171c Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 17 Dec 2013 22:23:58 +0100 Subject: [PATCH 253/800] (PUP-910) Convert args from 4x representation to 3x This is a partial solution that is ok as long as there are no functions written that wants to use the 4x representation. To be changed in 4.0.0 where functions should be able to opt in. --- lib/puppet/pops/evaluator/runtime3_support.rb | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/puppet/pops/evaluator/runtime3_support.rb b/lib/puppet/pops/evaluator/runtime3_support.rb index bf92e6b72..534149a1a 100644 --- a/lib/puppet/pops/evaluator/runtime3_support.rb +++ b/lib/puppet/pops/evaluator/runtime3_support.rb @@ -212,8 +212,8 @@ module Puppet::Pops::Evaluator::Runtime3Support end def call_function(name, args, o, scope) - # Should arguments be mapped from :undef to '' (3x functions expects this - but it is bad) - mapped_args = args.map {|a| a == :undef ? '' : a } + # Arguments must be mapped since functions are unaware of the new and magical creatures in 4x. + mapped_args = args.map {|a| convert(a, scope) } scope.send("function_#{name}", mapped_args) end @@ -403,6 +403,15 @@ module Puppet::Pops::Evaluator::Runtime3Support o.inspect end + def convert_Symbol(o, scope) + case o + when :undef + '' # 3x wants :undef as empty string in function + else + o # :default, and all others are verbatim since they are new in future evaluator + end + end + def convert_PAbstractType(o, scope) # Convert all other types to their string forms o.to_s From 7d1cf4cd343854c86c0cd37599c28dc709371095 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 17 Dec 2013 22:56:43 +0100 Subject: [PATCH 254/800] (maint) Make 3x conversion use verbatim types for non Host/Resource. --- lib/puppet/pops/evaluator/runtime3_support.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/puppet/pops/evaluator/runtime3_support.rb b/lib/puppet/pops/evaluator/runtime3_support.rb index 534149a1a..5ca43d42e 100644 --- a/lib/puppet/pops/evaluator/runtime3_support.rb +++ b/lib/puppet/pops/evaluator/runtime3_support.rb @@ -413,8 +413,7 @@ module Puppet::Pops::Evaluator::Runtime3Support end def convert_PAbstractType(o, scope) - # Convert all other types to their string forms - o.to_s + o end def convert_PResourceType(o,scope) From 29b705d3f1debd928eee585457ee4ee6366b3dbc Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Wed, 18 Dec 2013 00:38:08 +0100 Subject: [PATCH 255/800] (maint) Make Resource::Type use the CodeMerger strategy This makes the Type.merge method use the CodeMerger instead of relying on that code knows how to merge itself with any other kind of code. The CodeMerger is parser specific and is obtained from the ParserFactory. This way the choice of parser is hidden from the Type.merge logic. This also changes the CodeMerger.concatenate to only be responsible for the actual concatenation (it used to also create a HostClass called '' as it was used in only one specific use case). --- lib/puppet/node/environment.rb | 2 +- lib/puppet/parser/ast.rb | 1 + lib/puppet/parser/code_merger.rb | 5 +++-- lib/puppet/parser/parser_factory.rb | 1 - lib/puppet/pops/parser/code_merger.rb | 5 +++-- lib/puppet/resource/type.rb | 4 ++-- 6 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb index fb0dea652..b3ea64ab7 100644 --- a/lib/puppet/node/environment.rb +++ b/lib/puppet/node/environment.rb @@ -469,7 +469,7 @@ class Puppet::Node::Environment parser.parse end # Use a parser type specific merger to concatenate the results - Puppet::Parser::ParserFactory.code_merger.concatenate(parse_results) + Puppet::Parser::AST::Hostclass.new('', :code => Puppet::Parser::ParserFactory.code_merger.concatenate(parse_results)) else parser.file = file parser.parse diff --git a/lib/puppet/parser/ast.rb b/lib/puppet/parser/ast.rb index 438cb49b4..bf60c75a0 100644 --- a/lib/puppet/parser/ast.rb +++ b/lib/puppet/parser/ast.rb @@ -128,3 +128,4 @@ require 'puppet/parser/ast/resourceparam' require 'puppet/parser/ast/selector' require 'puppet/parser/ast/tag' require 'puppet/parser/ast/vardef' +require 'puppet/parser/code_merger' diff --git a/lib/puppet/parser/code_merger.rb b/lib/puppet/parser/code_merger.rb index b27f6c317..62d72dd05 100644 --- a/lib/puppet/parser/code_merger.rb +++ b/lib/puppet/parser/code_merger.rb @@ -2,11 +2,12 @@ class Puppet::Parser::CodeMerger # Concatenates the logic in the array of parse results into one parse result + # @return Puppet::Parser::AST::BlockExpression + # def concatenate(parse_results) children = parse_results.select {|x| !x.nil? && x.code}.reduce([]) do |memo, parsed_class| memo + parsed_class.code.children end - main = Puppet::Parser::AST::BlockExpression.new(:children => children) - Puppet::Parser::AST::Hostclass.new('', :code => main) + Puppet::Parser::AST::BlockExpression.new(:children => children) end end \ No newline at end of file diff --git a/lib/puppet/parser/parser_factory.rb b/lib/puppet/parser/parser_factory.rb index 3e5ad81e8..dfeca628f 100644 --- a/lib/puppet/parser/parser_factory.rb +++ b/lib/puppet/parser/parser_factory.rb @@ -22,7 +22,6 @@ module Puppet::Parser # def self.classic_parser(environment) require 'puppet/parser' - require 'puppet/parser/code_merger' Puppet::Parser::Parser.new(environment) end diff --git a/lib/puppet/pops/parser/code_merger.rb b/lib/puppet/pops/parser/code_merger.rb index d49244279..8634b329d 100644 --- a/lib/puppet/pops/parser/code_merger.rb +++ b/lib/puppet/pops/parser/code_merger.rb @@ -1,7 +1,9 @@ class Puppet::Pops::Parser::CodeMerger - # Concatenates the logic in the array of parse results into one parse result + # Concatenates the logic in the array of parse results into one parse result. + # @return Puppet::Parser::AST::BlockExpression + # def concatenate(parse_results) # this is a bit brute force as the result is already 3x ast with wrapped 4x content # this could be combined in a more elegant way, but it is only used to process a handful of files @@ -11,6 +13,5 @@ class Puppet::Pops::Parser::CodeMerger memo << parsed_class.code end main = Puppet::Parser::AST::BlockExpression.new(:children => children) - Puppet::Parser::AST::Hostclass.new('', :code => main) end end \ No newline at end of file diff --git a/lib/puppet/resource/type.rb b/lib/puppet/resource/type.rb index 4cf841475..cc09ecbba 100644 --- a/lib/puppet/resource/type.rb +++ b/lib/puppet/resource/type.rb @@ -179,7 +179,8 @@ class Puppet::Resource::Type return end - self.code = self.code.sequence_with(other.code) + self.code = Puppet::Parser::ParserFactory.code_merger.concatenate([self, other]) +# self.code = self.code.sequence_with(other.code) end # Make an instance of the resource type, and place it in the catalog @@ -382,4 +383,3 @@ class Puppet::Resource::Type end end end - From c35ae4ffa447c4370fb4620e7a10f2c322cc6593 Mon Sep 17 00:00:00 2001 From: Dominic Cleal Date: Mon, 11 Nov 2013 09:09:32 +0000 Subject: [PATCH 256/800] (PUP-1158) Only output load warning when file given by user has an error There are two optimised paths through the Augeas loading code: * when context parameter is given * when lens and incl parameters are given In the first case, all files for the lens are still loaded, including the one the user intends to edit. In the second, only one file and lens is loaded. When optimising, a warning is logged for the user if it's thought that loading of the user's file has failed. Previously this code would output the warning if there were any errors at all, so a file handled by the same Augeas lens that errored would trigger the warning, even though the targeted file was fine. Now errors for the specific context given by the user are checked in order to issue the warning message. --- lib/puppet/provider/augeas/augeas.rb | 10 +++---- spec/unit/provider/augeas/augeas_spec.rb | 33 +++++++++++++++--------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/lib/puppet/provider/augeas/augeas.rb b/lib/puppet/provider/augeas/augeas.rb index f0369a420..5a6ca6875 100644 --- a/lib/puppet/provider/augeas/augeas.rb +++ b/lib/puppet/provider/augeas/augeas.rb @@ -169,7 +169,7 @@ Puppet::Type.type(:augeas).provide(:augeas) do if resource[:incl] aug.set("/augeas/load/Xfm/lens", resource[:lens]) aug.set("/augeas/load/Xfm/incl", resource[:incl]) - restricted = true + restricted_metadata = "/augeas//error" elsif glob_avail and opt_ctx # Optimize loading if the context is given, requires the glob function # from Augeas 0.8.2 or up @@ -178,14 +178,14 @@ Puppet::Type.type(:augeas).provide(:augeas) do if aug.match(load_path).size < aug.match("/augeas/load/*").size aug.rm(load_path) - restricted = true + restricted_metadata = "/augeas/files#{ctx_path}/error" else # This will occur if the context is less specific than any glob debug("Unable to optimize files loaded by context path, no glob matches") end end aug.load - print_load_errors(:warning => restricted) + print_load_errors(restricted_metadata) end @aug end @@ -323,10 +323,10 @@ Puppet::Type.type(:augeas).provide(:augeas) do @aug.set("/augeas/save", mode) end - def print_load_errors(args={}) + def print_load_errors(path) errors = @aug.match("/augeas//error") unless errors.empty? - if args[:warning] + if path && !@aug.match(path).empty? warning("Loading failed for one or more files, see debug for /augeas//error output") else debug("Loading failed for one or more files, output from /augeas//error:") diff --git a/spec/unit/provider/augeas/augeas_spec.rb b/spec/unit/provider/augeas/augeas_spec.rb index 9b6e88ce8..3593d69f6 100755 --- a/spec/unit/provider/augeas/augeas_spec.rb +++ b/spec/unit/provider/augeas/augeas_spec.rb @@ -710,25 +710,34 @@ describe provider_class do @augeas.expects(:get).with("/augeas/files/foo/error/message").returns("Failed to...") end - it "and output to debug" do + it "and output only to debug when no path supplied" do @provider.expects(:debug).times(5) - @provider.print_load_errors + @provider.expects(:warning).never() + @provider.print_load_errors(nil) end - it "and output a warning and to debug" do + it "and output a warning and to debug when path supplied" do + @augeas.expects(:match).with("/augeas/files/foo//error").returns(["/augeas/files/foo/error"]) @provider.expects(:warning).once() @provider.expects(:debug).times(4) - @provider.print_load_errors(:warning => true) + @provider.print_load_errors('/augeas/files/foo//error') + end + + it "and output only to debug when path doesn't match" do + @augeas.expects(:match).with("/augeas/files/foo//error").returns([]) + @provider.expects(:warning).never() + @provider.expects(:debug).times(5) + @provider.print_load_errors('/augeas/files/foo//error') end end it "should find load errors from lenses" do - @augeas.expects(:match).with("/augeas//error").returns(["/augeas/load/Xfm/error"]) + @augeas.expects(:match).with("/augeas//error").twice.returns(["/augeas/load/Xfm/error"]) @augeas.expects(:match).with("/augeas/load/Xfm/error/*").returns([]) @augeas.expects(:get).with("/augeas/load/Xfm/error").returns(["Could not find lens php.aug"]) @provider.expects(:warning).once() @provider.expects(:debug).twice() - @provider.print_load_errors(:warning => true) + @provider.print_load_errors('/augeas//error') end it "should find save errors and output to debug" do @@ -756,7 +765,7 @@ describe provider_class do end it "should report load errors to debug only" do - @provider.expects(:print_load_errors).with(:warning => false) + @provider.expects(:print_load_errors).with(nil) aug = @provider.open_augeas aug.should_not == nil end @@ -766,7 +775,7 @@ describe provider_class do @resource[:incl] = "/etc/hosts" @resource[:lens] = "Hosts.lns" - @provider.expects(:print_load_errors).with(:warning => true) + @provider.expects(:print_load_errors).with('/augeas//error') aug = @provider.open_augeas aug.should_not == nil aug.match("/files/etc/fstab").should == [] @@ -799,7 +808,7 @@ describe provider_class do it "should only load one file if relevant context given" do @resource[:context] = "/files/etc/fstab" - @provider.expects(:print_load_errors).with(:warning => true) + @provider.expects(:print_load_errors).with('/augeas/files/etc/fstab//error') aug = @provider.open_augeas aug.should_not == nil aug.match("/files/etc/fstab").should == ["/files/etc/fstab"] @@ -810,7 +819,7 @@ describe provider_class do @resource[:context] = "/files/etc/test" @resource[:load_path] = my_fixture_dir - @provider.expects(:print_load_errors).with(:warning => true) + @provider.expects(:print_load_errors).with('/augeas/files/etc/test//error') aug = @provider.open_augeas aug.should_not == nil aug.match("/files/etc/fstab").should == [] @@ -821,7 +830,7 @@ describe provider_class do it "should load standard files if context isn't specific" do @resource[:context] = "/files/etc" - @provider.expects(:print_load_errors).with(:warning => false) + @provider.expects(:print_load_errors).with(nil) aug = @provider.open_augeas aug.should_not == nil aug.match("/files/etc/fstab").should == ["/files/etc/fstab"] @@ -831,7 +840,7 @@ describe provider_class do it "should not optimise if the context is a complex path" do @resource[:context] = "/files/*[label()='etc']" - @provider.expects(:print_load_errors).with(:warning => false) + @provider.expects(:print_load_errors).with(nil) aug = @provider.open_augeas aug.should_not == nil aug.match("/files/etc/fstab").should == ["/files/etc/fstab"] From e1fde8c457caca33cd693ff1051b36e7143de75a Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Wed, 18 Dec 2013 08:48:41 -0800 Subject: [PATCH 257/800] (maint) Pin beaker to 1.2.0 --- acceptance/Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance/Gemfile b/acceptance/Gemfile index fb817b197..967e5a84d 100644 --- a/acceptance/Gemfile +++ b/acceptance/Gemfile @@ -1,6 +1,6 @@ source ENV['GEM_SOURCE'] || "https://rubygems.org" -gem "beaker", "~> 1.2" +gem "beaker", "1.2.0" gem "rake" group(:test) do From da5d5a2295a530e9ab37adf02a1a1ddc43a6868c Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Wed, 18 Dec 2013 12:39:03 -0800 Subject: [PATCH 258/800] (Maint) Revert including augeas Including augeas in the tests requires that we have libaugeas installed on any machine that is going to run the tests with the :extra group. Too many of our internal systems don't have that kind of setup. It will have to be up to the maintainer of the augeas code (likely Dominic Cleal) to make sure that the augeas code works until we can get more complete testing infrastructure in place. The ruby-augeas gem can be included from a Gemfile.local for anyone who wants to develop against augeas in the meantime. --- .travis.yml | 1 - Gemfile | 1 - 2 files changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 50ac9f693..09f7db6e5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: ruby bundler_args: --without development -before_install: sudo apt-get update && sudo apt-get install libaugeas-dev libxml2-dev script: "bundle exec rake spec" notifications: email: false diff --git a/Gemfile b/Gemfile index d7297573c..5607f1fc5 100644 --- a/Gemfile +++ b/Gemfile @@ -16,7 +16,6 @@ platforms :ruby do gem 'yard', :group => :development gem 'redcarpet', '~> 2.0', :group => :development gem "racc", "1.4.9", :group => :development - gem "ruby-augeas", '~> 0.3', :require => false, :group => :extra end gem "puppet", :path => File.dirname(__FILE__), :require => false From 9efd7263a4f38ccd4760c79b2f6a62b7ba8ceb89 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 3 Oct 2013 05:38:19 +0200 Subject: [PATCH 259/800] (pup-546) Remove hiera2 support from binder This removes hiera2 support from binder. Tests are updated to use ruby based bindings for the same data bindings as the removed hiera2 based bindings. --- lib/puppet/pops.rb | 2 +- lib/puppet/pops/binder/bindings_composer.rb | 15 +- lib/puppet/pops/binder/bindings_loader.rb | 13 +- .../pops/binder/config/binder_config.rb | 15 +- .../binder/config/binder_config_checker.rb | 9 +- lib/puppet/pops/binder/config/issues.rb | 2 +- .../pops/binder/hiera2/bindings_provider.rb | 152 ------------------ lib/puppet/pops/binder/hiera2/config.rb | 69 -------- .../pops/binder/hiera2/config_checker.rb | 68 -------- .../pops/binder/hiera2/diagnostic_producer.rb | 36 ----- lib/puppet/pops/binder/hiera2/issues.rb | 67 -------- lib/puppet/pops/binder/hiera2/json_backend.rb | 18 --- lib/puppet/pops/binder/hiera2/yaml_backend.rb | 21 --- .../scheme_handler/confdir_hiera_scheme.rb | 67 -------- .../scheme_handler/module_hiera_scheme.rb | 92 ----------- lib/puppet/pops/issues.rb | 1 + lib/puppetx.rb | 20 --- lib/puppetx/puppet/hiera2_backend.rb | 31 ---- .../hiera1config/binder_config.yaml | 10 +- .../hiera1config/modules/good/common.yaml | 1 - .../hiera1config/modules/good/hiera.yaml | 10 -- .../good/lib/puppet/bindings/good/default.rb | 6 + .../bindings_composer/ok/binder_config.yaml | 9 +- .../binder/bindings_composer/ok/common.yaml | 1 - .../binder/bindings_composer/ok/hiera.yaml | 11 -- .../ok/lib/puppet/bindings/confdirtest.rb | 12 ++ .../bindings_composer/ok/localhost.yaml | 1 - .../ok/modules/awesome/common.yaml | 3 - .../ok/modules/awesome/hiera.yaml | 13 -- .../lib/puppet/bindings/awesome/default.rb | 4 - .../lib/puppetx/awesome/echo_backend.rb | 11 -- .../ok/modules/awesome/localhost.yaml | 1 - .../lib/puppet/bindings/awesome2/default.rb | 22 +++ .../puppetx/awesome2}/echo_scheme_handler.rb | 2 +- .../ok/modules/bad/common.yaml | 3 - .../ok/modules/bad/hiera_config.yaml | 9 -- .../bad/lib/puppet/bindings/bad/default.rb | 5 + .../ok/modules/good/common.yaml | 2 - .../ok/modules/good/hiera.yaml | 11 -- .../good/lib/puppet/bindings/good/default.rb | 10 ++ .../binder_config/ok/binder_config.yaml | 4 +- .../hiera2/bindings_provider/ok/hiera.yaml | 9 -- .../ok/node.example.com.json | 9 -- .../ok/node.example.com.yaml | 5 - .../hiera2/config/bad_syntax/hiera.yaml | 10 -- .../config/malformed_hierarchy/hiera.yaml | 8 - .../pops/binder/hiera2/config/missing/foo.txt | 1 - .../hiera2/config/no_backends/hiera.yaml | 7 - .../hiera2/config/no_hierarchy/hiera.yaml | 4 - .../hiera2/config/not_a_hash/hiera.yaml | 2 - .../pops/binder/hiera2/config/ok/hiera.yaml | 8 - .../hiera2/yaml_backend/empty/common.yaml | 0 .../hiera2/yaml_backend/invalid/common.yaml | 1 - .../binder/hiera2/yaml_backend/ok/common.yaml | 2 - .../pops/binder/bindings_composer_spec.rb | 14 +- .../pops/binder/config/binder_config_spec.rb | 10 +- .../binder/hiera2/bindings_provider_spec.rb | 77 --------- spec/unit/pops/binder/hiera2/config_spec.rb | 61 ------- .../pops/binder/hiera2/yaml_backend_spec.rb | 33 ---- 59 files changed, 97 insertions(+), 1023 deletions(-) delete mode 100644 lib/puppet/pops/binder/hiera2/bindings_provider.rb delete mode 100644 lib/puppet/pops/binder/hiera2/config.rb delete mode 100644 lib/puppet/pops/binder/hiera2/config_checker.rb delete mode 100644 lib/puppet/pops/binder/hiera2/diagnostic_producer.rb delete mode 100644 lib/puppet/pops/binder/hiera2/issues.rb delete mode 100644 lib/puppet/pops/binder/hiera2/json_backend.rb delete mode 100644 lib/puppet/pops/binder/hiera2/yaml_backend.rb delete mode 100644 lib/puppet/pops/binder/scheme_handler/confdir_hiera_scheme.rb delete mode 100644 lib/puppet/pops/binder/scheme_handler/module_hiera_scheme.rb delete mode 100644 lib/puppetx/puppet/hiera2_backend.rb delete mode 100644 spec/fixtures/unit/pops/binder/bindings_composer/hiera1config/modules/good/common.yaml delete mode 100644 spec/fixtures/unit/pops/binder/bindings_composer/hiera1config/modules/good/hiera.yaml create mode 100644 spec/fixtures/unit/pops/binder/bindings_composer/hiera1config/modules/good/lib/puppet/bindings/good/default.rb delete mode 100644 spec/fixtures/unit/pops/binder/bindings_composer/ok/common.yaml delete mode 100644 spec/fixtures/unit/pops/binder/bindings_composer/ok/hiera.yaml create mode 100644 spec/fixtures/unit/pops/binder/bindings_composer/ok/lib/puppet/bindings/confdirtest.rb delete mode 100644 spec/fixtures/unit/pops/binder/bindings_composer/ok/localhost.yaml delete mode 100644 spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome/common.yaml delete mode 100644 spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome/hiera.yaml delete mode 100644 spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome/lib/puppet/bindings/awesome/default.rb delete mode 100644 spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome/lib/puppetx/awesome/echo_backend.rb delete mode 100644 spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome/localhost.yaml create mode 100644 spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome2/lib/puppet/bindings/awesome2/default.rb rename spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/{awesome/lib/puppetx/awesome => awesome2/lib/puppetx/awesome2}/echo_scheme_handler.rb (97%) delete mode 100644 spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/bad/common.yaml delete mode 100644 spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/bad/hiera_config.yaml create mode 100644 spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/bad/lib/puppet/bindings/bad/default.rb delete mode 100644 spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/good/common.yaml delete mode 100644 spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/good/hiera.yaml create mode 100644 spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/good/lib/puppet/bindings/good/default.rb delete mode 100644 spec/fixtures/unit/pops/binder/hiera2/bindings_provider/ok/hiera.yaml delete mode 100644 spec/fixtures/unit/pops/binder/hiera2/bindings_provider/ok/node.example.com.json delete mode 100644 spec/fixtures/unit/pops/binder/hiera2/bindings_provider/ok/node.example.com.yaml delete mode 100644 spec/fixtures/unit/pops/binder/hiera2/config/bad_syntax/hiera.yaml delete mode 100644 spec/fixtures/unit/pops/binder/hiera2/config/malformed_hierarchy/hiera.yaml delete mode 100644 spec/fixtures/unit/pops/binder/hiera2/config/missing/foo.txt delete mode 100644 spec/fixtures/unit/pops/binder/hiera2/config/no_backends/hiera.yaml delete mode 100644 spec/fixtures/unit/pops/binder/hiera2/config/no_hierarchy/hiera.yaml delete mode 100644 spec/fixtures/unit/pops/binder/hiera2/config/not_a_hash/hiera.yaml delete mode 100644 spec/fixtures/unit/pops/binder/hiera2/config/ok/hiera.yaml delete mode 100644 spec/fixtures/unit/pops/binder/hiera2/yaml_backend/empty/common.yaml delete mode 100644 spec/fixtures/unit/pops/binder/hiera2/yaml_backend/invalid/common.yaml delete mode 100644 spec/fixtures/unit/pops/binder/hiera2/yaml_backend/ok/common.yaml delete mode 100644 spec/unit/pops/binder/hiera2/bindings_provider_spec.rb delete mode 100644 spec/unit/pops/binder/hiera2/config_spec.rb delete mode 100644 spec/unit/pops/binder/hiera2/yaml_backend_spec.rb diff --git a/lib/puppet/pops.rb b/lib/puppet/pops.rb index 80d018cec..bfd14aa0f 100644 --- a/lib/puppet/pops.rb +++ b/lib/puppet/pops.rb @@ -54,7 +54,7 @@ module Puppet require 'puppet/pops/binder/injector_entry' require 'puppet/pops/binder/key_factory' require 'puppet/pops/binder/injector' - require 'puppet/pops/binder/hiera2' +# require 'puppet/pops/binder/hiera2' require 'puppet/pops/binder/bindings_composer' require 'puppet/pops/binder/bindings_model_dumper' require 'puppet/pops/binder/system_bindings' diff --git a/lib/puppet/pops/binder/bindings_composer.rb b/lib/puppet/pops/binder/bindings_composer.rb index 7284f90c8..ee25816e5 100644 --- a/lib/puppet/pops/binder/bindings_composer.rb +++ b/lib/puppet/pops/binder/bindings_composer.rb @@ -62,7 +62,6 @@ class Puppet::Pops::Binder::BindingsComposer # get extensions from the config # ------------------------------ scheme_extensions = @config.scheme_extensions - hiera_backends = @config.hiera_backends # Define a named bindings that are known by the SystemBindings boot_bindings = Puppet::Pops::Binder::BindingsFactory.named_bindings(Puppet::Pops::Binder::SystemBindings::ENVIRONMENT_BOOT_BINDINGS_NAME) do @@ -71,21 +70,11 @@ class Puppet::Pops::Binder::BindingsComposer # do this in category 'extensions' to allow them to override the 'default' when_in_category('extension', 'true').bind do name(scheme) - instance_of(Puppetx::BINDINGS_SCHEMES_TYPE) - in_multibind(Puppetx::BINDINGS_SCHEMES) + instance_of(::Puppetx::BINDINGS_SCHEMES_TYPE) + in_multibind(::Puppetx::BINDINGS_SCHEMES) to_instance(class_name) end end - hiera_backends.each_pair do |symbolic, class_name| - # turn each symbolic => class_name into a binding (contribute to the hiera backends multibind). - # do this in category 'extensions' to allow them to override the 'default' - when_in_category('extension', 'true').bind do - name(symbolic) - instance_of(Puppetx::HIERA2_BACKENDS_TYPE) - in_multibind(Puppetx::HIERA2_BACKENDS) - to_instance(class_name) - end - end end @injector = scope.compiler.create_boot_injector(boot_bindings.model) diff --git a/lib/puppet/pops/binder/bindings_loader.rb b/lib/puppet/pops/binder/bindings_loader.rb index eca05c1e9..4e37c7e87 100644 --- a/lib/puppet/pops/binder/bindings_loader.rb +++ b/lib/puppet/pops/binder/bindings_loader.rb @@ -5,7 +5,8 @@ require 'rgen/metamodel_builder' # This means it can load a class from a gem, or from puppet modules. # class Puppet::Pops::Binder::BindingsLoader - @autoloader = Puppet::Util::Autoload.new("BindingsLoader", "puppet/bindings", :wrap => false) + @confdir = Puppet.settings[:confdir] +# @autoloader = Puppet::Util::Autoload.new("BindingsLoader", "puppet/bindings", :wrap => false) # Returns a XXXXX given a fully qualified class name. # Lookup of class is never relative to the calling namespace. @@ -40,6 +41,14 @@ class Puppet::Pops::Binder::BindingsLoader private + def self.loader() + unless Puppet.settings[:confdir] == @confdir + @confdir = Puppet.settings[:confdir] == @confdir + @autoloader = Puppet::Util::Autoload.new("BindingsLoader", "puppet/bindings", :wrap => false) + end + @autoloader + end + def self.provide_from_string(scope, name) name_path = name.split('::') # always from the root, so remove an empty first segment @@ -55,7 +64,7 @@ class Puppet::Pops::Binder::BindingsLoader unless result # Attempt to load it using the auto loader - paths_for_name(name).find {|path| @autoloader.load(path) } + paths_for_name(name).find {|path| loader.load(path) } result = Puppet::Bindings.resolve(scope, name) end result diff --git a/lib/puppet/pops/binder/config/binder_config.rb b/lib/puppet/pops/binder/config/binder_config.rb index a4fb7796a..c37efc1ee 100644 --- a/lib/puppet/pops/binder/config/binder_config.rb +++ b/lib/puppet/pops/binder/config/binder_config.rb @@ -24,15 +24,13 @@ module Puppet::Pops::Binder::Config # @return ] ({}) optional mapping of bindings-scheme to handler class name attr_reader :scheme_extensions - # @return ] ({}) optional mapping of hiera backend name to backend class name - attr_reader :hiera_backends # @return [String] the loaded config file attr_accessor :config_file DEFAULT_LAYERS = [ - { 'name' => 'site', 'include' => ['confdir-hiera:/', 'confdir:/default?optional'] }, - { 'name' => 'modules', 'include' => ['module-hiera:/*/', 'module:/*::default'] }, + { 'name' => 'site', 'include' => [ 'confdir:/default?optional'] }, + { 'name' => 'modules', 'include' => [ 'module:/*::default'] }, ] DEFAULT_CATEGORIES = [ @@ -44,8 +42,6 @@ module Puppet::Pops::Binder::Config DEFAULT_SCHEME_EXTENSIONS = {} - DEFAULT_HIERA_BACKENDS_EXTENSIONS = {} - def default_config() # This is hardcoded now, but may be a user supplied default configuration later {'version' => 1, 'layers' => default_layers, 'categories' => default_categories} @@ -105,12 +101,10 @@ module Puppet::Pops::Binder::Config @layering_config = data['layers'] or default_layers @categorization = data['categories'] or default_categories @scheme_extensions = (data['extensions'] and data['extensions']['scheme_handlers'] or default_scheme_extensions) - @hiera_backends = (data['extensions'] and data['extensions']['hiera_backends'] or default_hiera_backends_extensions) else @layering_config = [] @categorization = {} @scheme_extensions = {} - @hiera_backends = {} end end @@ -130,10 +124,5 @@ module Puppet::Pops::Binder::Config def default_scheme_extensions DEFAULT_SCHEME_EXTENSIONS end - - # @api private - def default_hiera_backends_extensions - DEFAULT_HIERA_BACKENDS_EXTENSIONS - end end end diff --git a/lib/puppet/pops/binder/config/binder_config_checker.rb b/lib/puppet/pops/binder/config/binder_config_checker.rb index 24270ae14..41deb3b6a 100644 --- a/lib/puppet/pops/binder/config/binder_config_checker.rb +++ b/lib/puppet/pops/binder/config/binder_config_checker.rb @@ -161,7 +161,7 @@ module Puppet::Pops::Binder::Config end # check known extensions extensions.each_key do |key| - unless ['scheme_handlers', 'hiera_backends'].include? key + unless ['scheme_handlers'].include? key accept(Issues::UNKNOWN_EXTENSION, config_file, :extension => key) end end @@ -171,13 +171,6 @@ module Puppet::Pops::Binder::Config accept(Issues::EXTENSION_BINDING_NOT_HASH, config_file, :extension => 'scheme_handlers', :actual => binding_schemes.class.name) end end - - if hiera_backends = extensions['hiera_backends'] - unless hiera_backends.is_a?(Hash) - accept(Issues::EXTENSION_BINDING_NOT_HASH, config_file, :extension => 'hiera_backends', :actual => hiera_backends.class.name) - end - end - end end end diff --git a/lib/puppet/pops/binder/config/issues.rb b/lib/puppet/pops/binder/config/issues.rb index d67df45ee..221184eda 100644 --- a/lib/puppet/pops/binder/config/issues.rb +++ b/lib/puppet/pops/binder/config/issues.rb @@ -100,7 +100,7 @@ module Puppet::Pops::Binder::Config::Issues "The configuration file '#{semantic}' contains '#{extension}', expected: Hash, but got: #{actual}." end - UNKNOWN_EXTENSION = issue :UNKNOWN_EXTENSION, :actual do + UNKNOWN_EXTENSION = issue :UNKNOWN_EXTENSION, :extension do "The configuration file '#{semantic}' contains the unknown extension: #{extension}." end end diff --git a/lib/puppet/pops/binder/hiera2/bindings_provider.rb b/lib/puppet/pops/binder/hiera2/bindings_provider.rb deleted file mode 100644 index be39f3ce8..000000000 --- a/lib/puppet/pops/binder/hiera2/bindings_provider.rb +++ /dev/null @@ -1,152 +0,0 @@ -module Puppet::Pops::Binder::Hiera2 - Model = Puppet::Pops::Model - - # A BindingsProvider instance is used for creating a bindings model from a module directory - # @api public - # - class BindingsProvider - # The resulting name of loaded bindings (given when initializing) - attr_reader :name - - # Creates a new BindingsProvider by reading the hiera_conf.yaml configuration file. Problems - # with the configuration are reported propagated to the acceptor - # - # @param name [String] the name to assign to the result (and in error messages if there is no result) - # @param hiera_config_dir [String] Path to the directory containing a hiera_config - # @param acceptor [Puppet::Pops::Validation::Acceptor] Acceptor that will receive diagnostics - def initialize(name, hiera_config_dir, acceptor) - @name = name - @parser = Puppet::Pops::Parser::EvaluatingParser.new() - @diagnostics = DiagnosticProducer.new(acceptor) - @type_calculator = Puppet::Pops::Types::TypeCalculator.new() - @config = Config.new(hiera_config_dir, @diagnostics) - end - - # Loads a bindings model using the hierarchy and backends configured for this instance. - # - # @param scope [Puppet::Parser::Scope] The hash used when expanding - # @return [Puppet::Pops::Binder::Bindings::ContributedBindings] A bindings model with effective categories - def load_bindings(scope) - backends = BackendHelper.new(scope) - factory = Puppet::Pops::Binder::BindingsFactory - result = factory.named_bindings(name) - - hierarchy = {} - precedence = [] - - @config.hierarchy.each do |key, value, path| - source_file = File.join(@config.module_dir, 'hiera.yaml') - category_value = @parser.evaluate_string(scope, @parser.quote(value), source_file) - - hierarchy[key] = { - :bindings => result.when_in_category(key, category_value), - :path => @parser.evaluate_string(scope, @parser.quote(path)), - :unique_keys =>Set.new()} - - precedence << [key, category_value] - end - - @config.backends.each do |backend_key| - backend = backends[backend_key] - - hierarchy.each_pair do |hier_key, hier_val| - bindings = hier_val[:bindings] - unique_keys = hier_val[:unique_keys] - - hiera_data_file_path = hier_val[:path] - backend.read_data(@config.module_dir, hiera_data_file_path).each_pair do |key, value| - if unique_keys.add?(key) - b = bindings.bind().name(key) - # Transform value into a Model::Expression - expr = build_expr(value, hiera_data_file_path) - if is_constant?(expr) - # The value is constant so toss the expression - b.type(@type_calculator.infer_generic(value)).to(value) - else - # Use an evaluating producer for the binding - b.to(expr) - end - end - end - end - end - - factory.contributed_bindings(name, result.model, factory.categories(precedence)) - end - - private - - # @return true unless the expression is a Model::ConcatenatedString or - # somehow contains one - def is_constant?(expr) - if expr.is_a?(Model::ConcatenatedString) - false - else - !expr.eAllContents.any? { |v| v.is_a?(Model::ConcatenatedString) } - end - end - - # Transform the value into a Model::Expression. Strings are parsed using - # the Pops::Parser::Parser to produce either Model::LiteralString or Model::ConcatenatedString - # - # @param value [Object] May be an String, Number, TrueClass, FalseClass, or NilClass nested to any depth using Hash or Array. - # @param hiera_data_file_path [String] The source_file used when reporting errors - # @return [Model::Expression] The expression that corresponds to the value - def build_expr(value, hiera_data_file_path) - case value - when Symbol - value.to_s - when String - @parser.parse_string(@parser.quote(value)).current - when Hash - value.inject(Model::LiteralHash.new) do |h,(k,v)| - e = Model::KeyedEntry.new - e.key = build_expr(k, hiera_data_file_path) - e.value = build_expr(v, hiera_data_file_path) - h.addEntries(e) - h - end - when Enumerable - value.inject(Model::LiteralList.new) {|a,v| a.addValues(build_expr(v, hiera_data_file_path)); a } - when Integer - expr = Model::LiteralInteger.new - expr.value = value; - expr - when Float - expr = Model::LiteralFloat.new - expr.value = value; - expr - when TrueClass, FalseClass - expr = Model::LiteralBoolean.new - expr.value = value; - expr - when NilClass - Model::Nop.new - else - @diagnostics.accept(Issues::UNABLE_TO_PARSE_INSTANCE, value.class.name) - nil - end - end - end - - # @api private - class BackendHelper - T = Puppet::Pops::Types::TypeFactory - HASH_OF_BACKENDS = T.hash_of(T.type_of('Puppetx::Puppet::Hiera2Backend')) - def initialize(scope) - @scope = scope - @cache = nil - end - - def [] (backend_key) - load_backends unless @cache - @cache[backend_key] - end - - def load_backends - @cache = @scope.compiler.boot_injector.lookup(@scope, HASH_OF_BACKENDS, Puppetx::HIERA2_BACKENDS) || {} - end - end - -end - diff --git a/lib/puppet/pops/binder/hiera2/config.rb b/lib/puppet/pops/binder/hiera2/config.rb deleted file mode 100644 index 104cb5531..000000000 --- a/lib/puppet/pops/binder/hiera2/config.rb +++ /dev/null @@ -1,69 +0,0 @@ -module Puppet::Pops::Binder::Hiera2 - - # Class holding the Hiera2 Configuration - # The configuration is obtained from the file 'hiera.yaml' - # that must reside in the root directory of the module - # @api public - # - class Puppet::Pops::Binder::Hiera2::Config - DEFAULT_HIERARCHY = [ ['osfamily', '${osfamily}', 'data/osfamily/${osfamily}'], ['common', 'true', 'data/common']] - DEFAULT_BACKENDS = ['yaml', 'json'] - - if defined?(::Psych::SyntaxError) - YamlLoadExceptions = [::StandardError, ::ArgumentError, ::Psych::SyntaxError] - else - YamlLoadExceptions = [::StandardError, ::ArgumentError] - end - - # Returns a list of configured backends. - # - # @return [Array] backend names - attr_reader :backends - - # Root directory of the module holding the configuration - # - # @return [String] An absolute path - attr_reader :module_dir - - # The bindings hierarchy is an array of categorizations where the - # array for each category has exactly three elements - the categorization name, - # category value, and the path that is later used by the backend to read - # the bindings for that category - # - # @return [Array] - # @api public - attr_reader :hierarchy - - # Creates a new Config. The configuration is loaded from the file 'hiera.yaml' which - # is expected to be found in the given module_dir. - # - # @param module_dir [String] The module directory - # @param diagnostics [DiagnosticProducer] collector of diagnostics - # @api public - # - def initialize(module_dir, diagnostics) - @module_dir = module_dir - config_file = File.join(module_dir, 'hiera.yaml') - validator = ConfigChecker.new(diagnostics) - begin - data = YAML.load_file(config_file) - validator.validate(data, config_file) - unless diagnostics.errors? - # if these are missing the result is nil, and they get default values later - @hierarchy = data['hierarchy'] - @backends = data['backends'] - end - rescue Errno::ENOENT - diagnostics.accept(Issues::CONFIG_FILE_NOT_FOUND, config_file) - rescue Errno::ENOTDIR - diagnostics.accept(Issues::CONFIG_FILE_NOT_FOUND, config_file) - rescue ::SyntaxError => e - diagnostics.accept(Issues::CONFIG_FILE_SYNTAX_ERROR, e) - rescue *YamlLoadExceptions => e - diagnostics.accept(Issues::CONFIG_FILE_SYNTAX_ERROR, e) - end - @hierarchy ||= DEFAULT_HIERARCHY - @backends ||= DEFAULT_BACKENDS - end - end -end diff --git a/lib/puppet/pops/binder/hiera2/config_checker.rb b/lib/puppet/pops/binder/hiera2/config_checker.rb deleted file mode 100644 index 53b32ed81..000000000 --- a/lib/puppet/pops/binder/hiera2/config_checker.rb +++ /dev/null @@ -1,68 +0,0 @@ -module Puppet::Pops::Binder::Hiera2 - - # Validates the consistency of a Hiera2::Config - class ConfigChecker - - # Create an instance with a diagnostic producer that will receive the result during validation - # @param diangostics [DiagnosticProducer] The producer that will receive the diagnostic - def initialize(diagnostics) - @diagnostics = diagnostics - end - - # Validate the consistency of the given data. Diagnostics will be emitted to the DiagnosticProducer - # that was set when this checker was created - # - # @param data [Object] The data read from the config file - # @param config_file [String] The full path of the file. Used in error messages - def validate(data, config_file) - if data.is_a?(Hash) - # If the version is missing, it is not meaningful to continue - return unless check_version(data['version'], config_file) - check_hierarchy(data['hierarchy'], config_file) - check_backends(data['backends'], config_file) - else - @diagnostics.accept(Issues::CONFIG_IS_NOT_HASH, config_file) - end - end - - private - - # Version is required and must be >= 2. A warning is issued if version > 2 as this checker is - # for version 2 only. - # @return [Boolean] false if it is meaningless to continue checking - def check_version(version, config_file) - if version.nil? - # This is not hiera2 compatible - @diagnostics.accept(Issues::MISSING_VERSION, config_file) - return false - end - unless version >= 2 - @diagnostics.accept(Issues::WRONG_VERSION, config_file, :expected => 2, :actual => version) - return false - end - unless version == 2 - # it may have a sane subset, hence a different error (configured as warning) - @diagnostics.accept(Issues::LATER_VERSION, config_file, :expected => 2, :actual => version) - end - return true - end - - def check_hierarchy(hierarchy, config_file) - if !hierarchy.is_a?(Array) || hierarchy.empty? - @diagnostics.accept(Issues::MISSING_HIERARCHY, config_file) - else - hierarchy.each do |value| - unless value.is_a?(Array) && value.length() == 3 - @diagnostics.accept(Issues::CATEGORY_MUST_BE_THREE_ELEMENT_ARRAY, config_file) - end - end - end - end - - def check_backends(backends, config_file) - if !backends.is_a?(Array) || backends.empty? - @diagnostics.accept(Issues::MISSING_BACKENDS, config_file) - end - end - end -end diff --git a/lib/puppet/pops/binder/hiera2/diagnostic_producer.rb b/lib/puppet/pops/binder/hiera2/diagnostic_producer.rb deleted file mode 100644 index 0ae2d4611..000000000 --- a/lib/puppet/pops/binder/hiera2/diagnostic_producer.rb +++ /dev/null @@ -1,36 +0,0 @@ -module Puppet::Pops::Binder::Hiera2 - # Generates validation diagnostics - class Puppet::Pops::Binder::Hiera2::DiagnosticProducer - attr_reader :acceptor - def initialize(an_acceptor) - raise ArgumentError, "Not an acceptor" unless an_acceptor.is_a?(Puppet::Pops::Validation::Acceptor) - @acceptor = an_acceptor - @severity_producer = Puppet::Pops::Validation::SeverityProducer.new - end - - def accept(issue, semantic, arguments={}) - arguments[:semantic] ||= semantic - severity = severity_producer.severity(issue) - acceptor.accept(Puppet::Pops::Validation::Diagnostic.new(severity, issue, nil, nil, arguments)) - end - - def errors?() - acceptor.errors? - end - - def severity_producer - p = @severity_producer - p[Issues::UNRESOLVED_STRING_VARIABLE] = :warning - - # Warning since if it does not blow up on anything else, a sane subset of later version was used - p[Issues::LATER_VERSION] = :warning - - # Ignore MISSING_BACKENDS because a default will be provided - p[Issues::MISSING_BACKENDS] = :ignore - - # Ignore MISSING_HIERARCHY because a default will be provided - p[Issues::MISSING_HIERARCHY] = :ignore - p - end - end -end diff --git a/lib/puppet/pops/binder/hiera2/issues.rb b/lib/puppet/pops/binder/hiera2/issues.rb deleted file mode 100644 index b3b0a10d0..000000000 --- a/lib/puppet/pops/binder/hiera2/issues.rb +++ /dev/null @@ -1,67 +0,0 @@ -module Puppet::Pops::Binder::Hiera2::Issues - # (see Puppet::Pops::Issues#issue) - def self.issue (issue_code, *args, &block) - Puppet::Pops::Issues.issue(issue_code, *args, &block) - end - - CONFIG_IS_NOT_HASH = issue :CONFIG_IS_NOT_HASH do - "The configuration file '#{semantic}' has no hash at the top level" - end - - MISSING_HIERARCHY = issue :MISSING_HIERARCHY do - "The configuration file '#{semantic}' contains no hierarchy" - end - - MISSING_BACKENDS = issue :MISSING_BACKENDS do - "The configuration file '#{semantic}' contains no backends" - end - - CATEGORY_MUST_BE_THREE_ELEMENT_ARRAY = issue :CATEGORY_MUST_BE_THREE_ELEMENT_ARRAY do - "The configuration file '#{semantic}' has a malformed hierarchy (should consist of arrays with three string entries)" - end - - CONFIG_FILE_NOT_FOUND = issue :CONFIG_FILE_NOT_FOUND do - "The configuration file '#{semantic}' does not exist" - end - - CONFIG_FILE_SYNTAX_ERROR = issue :CONFIG_FILE_SYNTAX_ERROR do - "Unable to parse: #{semantic}" - end - - CANNOT_LOAD_BACKEND = issue :CANNOT_LOAD_BACKEND, :key, :error do - "Backend '#{key}' in configuration file '#{semantic}' cannot be loaded: #{error}" - end - - BACKEND_FILE_DOES_NOT_DEFINE_CLASS = issue :BACKEND_FILE_DOES_NOT_DEFINE_CLASS, :class_name do - "The file '#{semantic}' does not define class #{class_name}" - end - - NOT_A_BACKEND_CLASS = issue :NOT_A_BACKEND_CLASS, :key, :class_name do - "Class #{class_name}, loaded using key #{key} in file '#{semantic}' is not a subclass of Backend" - end - - METADATA_JSON_NOT_FOUND = issue :METADATA_JSON_NOT_FOUND do - "The metadata file '#{semantic}' does not exist" - end - - UNSUPPORTED_STRING_EXPRESSION = issue :UNSUPPORTED_STRING_EXPRESSION, :expr do - "String '#{semantic}' contains an unsupported expression (type was #{expr.class.name})" - end - - UNRESOLVED_STRING_VARIABLE = issue :UNRESOLVED_STRING_VARIABLE, :key do - "Variable '#{key}' found in string '#{semantic}' cannot be resolved" - end - - MISSING_VERSION = issue :MISSING_VERSION do - "The configuration file '#{semantic}' does not have a version." - end - - WRONG_VERSION = issue :WRONG_VERSION do - "The configuration file '#{semantic}' has the wrong version, expected: #{expected}, actual: #{actual}" - end - - LATER_VERSION = issue :LATER_VERSION do - "The configuration file '#{semantic}' has a version that is newer (features may not work), expected: #{expected}, actual: #{actual}" - end - -end diff --git a/lib/puppet/pops/binder/hiera2/json_backend.rb b/lib/puppet/pops/binder/hiera2/json_backend.rb deleted file mode 100644 index dd4c0f220..000000000 --- a/lib/puppet/pops/binder/hiera2/json_backend.rb +++ /dev/null @@ -1,18 +0,0 @@ -require 'json' - -# A Backend implementation capable of reading JSON syntax -class Puppet::Pops::Binder::Hiera2::JsonBackend < Puppetx::Puppet::Hiera2Backend - def read_data(module_dir, source) - begin - source_file = File.join(module_dir, "#{source}.json") - JSON.parse(File.read(source_file)) - rescue Errno::ENOTDIR - # This is OK, the file doesn't need to be present. Return an empty hash - {} - rescue Errno::ENOENT - # This is OK, the file doesn't need to be present. Return an empty hash - {} - end - end -end - diff --git a/lib/puppet/pops/binder/hiera2/yaml_backend.rb b/lib/puppet/pops/binder/hiera2/yaml_backend.rb deleted file mode 100644 index d24a0dc5b..000000000 --- a/lib/puppet/pops/binder/hiera2/yaml_backend.rb +++ /dev/null @@ -1,21 +0,0 @@ -# A Backend implementation capable of reading YAML syntax -class Puppet::Pops::Binder::Hiera2::YamlBackend < Puppetx::Puppet::Hiera2Backend - def read_data(module_dir, source) - begin - source_file = File.join(module_dir, "#{source}.yaml") - # if file is present but empty or has only "---", YAML.load_file returns false, - # in which case fall back to returning an empty hash - YAML.load_file(source_file) || {} - rescue TypeError => e - # SafeYaml chokes when trying to load using utf-8 and the file is empty - raise e if File.size?(source_file) - {} - rescue Errno::ENOTDIR - # This is OK, the file doesn't need to be present. Return an empty hash - {} - rescue Errno::ENOENT - # This is OK, the file doesn't need to be present. Return an empty hash - {} - end - end -end diff --git a/lib/puppet/pops/binder/scheme_handler/confdir_hiera_scheme.rb b/lib/puppet/pops/binder/scheme_handler/confdir_hiera_scheme.rb deleted file mode 100644 index d2ae152f7..000000000 --- a/lib/puppet/pops/binder/scheme_handler/confdir_hiera_scheme.rb +++ /dev/null @@ -1,67 +0,0 @@ -# Similar to {Puppet::Pops::Binder::SchemeHandler::ModuleHieraScheme ModuleHieraScheme} but path is -# relative to the `$confdir` instead of relative to a module root. -# -# Does not handle wild-cards. -# @api public -class Puppet::Pops::Binder::SchemeHandler::ConfdirHieraScheme < Puppetx::Puppet::BindingsSchemeHandler - - # (Puppetx::Puppet::BindingsSchemeHandler.contributed_bindings) - # - def contributed_bindings(uri, scope, composer) - split_path = uri.path.split('/') - name = split_path[1] - confdir = composer.confdir - provider = Puppet::Pops::Binder::Hiera2::BindingsProvider.new(uri.to_s, File.join(confdir, uri.path), composer.acceptor) - provider.load_bindings(scope) - end - - # This handler does not support wildcards. - # The given uri is simply returned in an array. - # @param uri [URI] the uri to expand - # @return [Array] the uri wrapped in an array - # @todo Handle optional and possibly hiera-1 hiera.yaml config file in the expected location (the same as missing) - # @api public - # - def expand_included(uri, composer) - result = [] - if config_exist?(uri, composer) - result << uri unless is_ignored_hiera_version?(uri, composer) - else - result << uri unless is_optional?(uri) - end - result - end - - # This handler does not support wildcards. - # The given uri is simply returned in an array. - # @param uri [URI] the uri to expand - # @return [Array] the uri wrapped in an array - # @api public - # - def expand_excluded(uri, composer) - [uri] - end - - def config_exist?(uri, composer) - Puppet::FileSystem::File.exist?(File.join(composer.confdir, uri.path, 'hiera.yaml')) - end - - # A hiera.yaml that exists, is readable, can be loaded, and does not have version >= 2 set is ignored. - # All other conditions are reported as 'not ignored' even if there are errors; these will be handled later - # as if the hiera.yaml is a hiera-2 file. - # @api private - def is_ignored_hiera_version?(uri, composer) - config_file = File.join(composer.confdir, uri.path, 'hiera.yaml') - begin - data = YAML.load_file(config_file) - if data.is_a?(Hash) - ver = data[:version] || data['version'] - return ver.nil? || ver < 2 - end - rescue Errno::ENOENT - rescue Errno::ENOTDIR - rescue ::SyntaxError => e - end - return false - end -end diff --git a/lib/puppet/pops/binder/scheme_handler/module_hiera_scheme.rb b/lib/puppet/pops/binder/scheme_handler/module_hiera_scheme.rb deleted file mode 100644 index 0663ee2cb..000000000 --- a/lib/puppet/pops/binder/scheme_handler/module_hiera_scheme.rb +++ /dev/null @@ -1,92 +0,0 @@ -# The `module-hiera:` scheme uses the path to denote a directory relative to a module root -# The path starts with the name of the module, or '*' to denote *any module*. -# -# @example All root hiera.yaml from all modules. -# module-hiera:/* -# -# @example The hiera.yaml from the module `foo`'s relative path `/bar`. -# module-hiera:/foo/bar -# -class Puppet::Pops::Binder::SchemeHandler::ModuleHieraScheme < Puppetx::Puppet::BindingsSchemeHandler - # (Puppetx::Puppet::BindingsSchemeHandler.contributed_bindings) - # @api public - def contributed_bindings(uri, scope, composer) - split_path = uri.path.split('/') - name = split_path[1] - mod = composer.name_to_module[name] - provider = Puppet::Pops::Binder::Hiera2::BindingsProvider.new(uri.to_s, File.join(mod.path, split_path[ 2..-1 ]), composer.acceptor) - provider.load_bindings(scope) - end - - # Expands URIs with wildcards and checks optionality. - # @param uri [URI] the uri to possibly expand - # @return [Array] the URIs to include - # @api public - # - def expand_included(uri, composer) - result = [] - split_path = uri.path.split('/') - if split_path.size > 1 && split_path[-1].empty? - split_path.delete_at(-1) - end - - # 0 = "", since a URI with a path must start with '/' - # 1 = '*' or the module name - case split_path[ 1 ] - when '*' - # create new URIs, one per module name that has a hiera.yaml file relative to its root - composer.name_to_module.each_pair do | name, mod | - if Puppet::FileSystem::File.exist?(File.join(mod.path, split_path[ 2..-1 ], 'hiera.yaml' )) - path_parts =["", name] + split_path[2..-1] - result << URI.parse('module-hiera:'+File.join(path_parts)) - end - end - when nil - raise ArgumentError, "Bad bindings uri, the #{uri} has neither module name or wildcard '*' in its first path position" - else - # If uri has query that is empty, or the text 'optional' skip this uri if it does not exist - if query = uri.query() - if query == '' || query == 'optional' - if Puppet::FileSystem::File.exist?(File.join(mod.path, split_path[ 2..-1 ], 'hiera.yaml' )) - result << URI.parse('module-hiera:' + uri.path) - end - end - else - # assume it exists (do not give error since it may be excluded later) - result << URI.parse('module-hiera:' + File.join(split_path)) - end - end - result - end - - # Expands URIs with wildcards and checks optionality. - # @param uri [URI] the uri to possibly expand - # @return [Array] the URIs to exclude - # @api public - # - def expand_excluded(uri, composer) - result = [] - split_path = uri.path.split('/') - if split_path.size > 1 && split_path[-1].empty? - split_path.delete_at(-1) - end - - # 0 = "", since a URI with a path must start with '/' - # 1 = '*' or the module name - case split_path[ 1 ] - when '*' - # create new URIs, one per module name that has a hiera.yaml file relative to its root - composer.name_to_module.each_pair do | name, mod | - path_parts =["", mod.name] + split_path[2..-1] - result << URI.parse('module-hiera:'+File.join(path_parts)) - end - - when nil - raise ArgumentError, "Bad bindings uri, the #{uri} has neither module name or wildcard '*' in its first path position" - else - # create a clean copy (get rid of optional, fragments etc. and a trailing "/") - result << URI.parse('module-hiera:' + File.join(split_path)) - end - result - end -end diff --git a/lib/puppet/pops/issues.rb b/lib/puppet/pops/issues.rb index 215853187..9f901a6c1 100644 --- a/lib/puppet/pops/issues.rb +++ b/lib/puppet/pops/issues.rb @@ -48,6 +48,7 @@ module Puppet::Pops::Issues # Evaluate the message block in the msg data's binding msgdata.format(hash, &message_block) rescue StandardError => e + Puppet::Pops::Issues::MessageData raise RuntimeError, "Error while reporting issue: #{issue_code}. #{e.message}", caller end end diff --git a/lib/puppetx.rb b/lib/puppetx.rb index b5f30fc69..884e2b8f2 100644 --- a/lib/puppetx.rb +++ b/lib/puppetx.rb @@ -27,14 +27,6 @@ module Puppetx # @api public BINDINGS_SCHEMES_TYPE = 'Puppetx::Puppet::BindingsSchemeHandler' - # The lookup **key** for the multibind containing a map from hiera-2 backend name to class implementing the backend. - # @api public - HIERA2_BACKENDS = 'puppetx::puppet::hiera2::backends' - - # The lookup **type** for the multibind containing a map from hiera-2 backend name to class implementing the backend. - # @api public - HIERA2_BACKENDS_TYPE = 'Puppetx::Puppet::Hiera2Backend' - # This module is the name space for extension points # @api public module Puppet @@ -53,7 +45,6 @@ module Puppetx extensions = system_bindings.extensions() extensions.multibind(SYNTAX_CHECKERS).name(SYNTAX_CHECKERS).hash_of(SYNTAX_CHECKERS_TYPE) extensions.multibind(BINDINGS_SCHEMES).name(BINDINGS_SCHEMES).hash_of(BINDINGS_SCHEMES_TYPE) - extensions.multibind(HIERA2_BACKENDS).name(HIERA2_BACKENDS).hash_of(HIERA2_BACKENDS_TYPE) # Register injector boot bindings # ------------------------------- @@ -63,21 +54,10 @@ module Puppetx require 'puppetx/puppet/bindings_scheme_handler' { 'module' => 'ModuleScheme', 'confdir' => 'ConfdirScheme', - 'module-hiera' => 'ModuleHieraScheme', - 'confdir-hiera' => 'ConfdirHieraScheme' }.each do |scheme, class_name| boot_bindings.bind.name(scheme).instance_of(BINDINGS_SCHEMES_TYPE).in_multibind(BINDINGS_SCHEMES). to_instance("Puppet::Pops::Binder::SchemeHandler::#{class_name}") end - - # Register the default hiera2 backends - require 'puppetx/puppet/hiera2_backend' - { 'json' => 'JsonBackend', - 'yaml' => 'YamlBackend' - }.each do |symbolic, class_name| - boot_bindings.bind.name(symbolic).instance_of(HIERA2_BACKENDS_TYPE).in_multibind(HIERA2_BACKENDS). - to_instance("Puppet::Pops::Binder::Hiera2::#{class_name}") - end end end diff --git a/lib/puppetx/puppet/hiera2_backend.rb b/lib/puppetx/puppet/hiera2_backend.rb deleted file mode 100644 index d4985d602..000000000 --- a/lib/puppetx/puppet/hiera2_backend.rb +++ /dev/null @@ -1,31 +0,0 @@ -module Puppetx::Puppet - - # Hiera2Backend is a Puppet Extension Point for the purpose of extending Puppet with a hiera data compatible - # backend. The intended use is to create a class derived from this class and then register it with the - # Puppet Binder under a backend name in the `binder_config.yaml` file to map symbolic name to class name. - # - # The responsibility of a Hiera2 backend is minimal. It should read the given file (with some extesion(s) determined by - # the backend, and return a hash of the content. If the directory does not exist, or the file does not exist an empty - # hash should be produced. - # - # @abstract - # @api public - # - class Hiera2Backend - # Produces a hash with data read from the file in the given - # directory having the given file_name (with extensions appended under the discretion of this - # backend). - # - # Should return an empty hash if the directory or the file does not exist. May raise exception on other types of errors, but - # not return nil. - # - # @param directory [String] the path to the directory containing the file to read - # @param file_name [String] the file name (without extension) that should be read - # @return [Hash, Hash] the produced hash with data, may be empty if there was no file - # @api public - # - def read_data(directory, file_name) - raise NotImplementedError, "The class #{self.class.name} should have implemented the method 'read_data(directory, file_name)'" - end - end -end diff --git a/spec/fixtures/unit/pops/binder/bindings_composer/hiera1config/binder_config.yaml b/spec/fixtures/unit/pops/binder/bindings_composer/hiera1config/binder_config.yaml index 9088193a4..13ef73157 100644 --- a/spec/fixtures/unit/pops/binder/bindings_composer/hiera1config/binder_config.yaml +++ b/spec/fixtures/unit/pops/binder/bindings_composer/hiera1config/binder_config.yaml @@ -1,8 +1,8 @@ --- version: 1 layers: - [{name: site, include: 'confdir-hiera:/'}, - {name: modules, include: ['module-hiera:/*/', 'module:/*::default'] } + [{name: site, include: 'confdir:/default?optional'}, + {name: modules, include: ['module:/*::default'] } ] categories: [['node', '$fqdn'], @@ -10,9 +10,3 @@ categories: ['osfamily', '${osfamily}'], ['common', 'true'] ] -#extensions: -# scheme_handlers: -# echo: 'Puppetx::Awesome::EchoSchemeHandler' -# -# hiera_backends: -# echo: 'Puppetx::Awesome::EchoBackend' diff --git a/spec/fixtures/unit/pops/binder/bindings_composer/hiera1config/modules/good/common.yaml b/spec/fixtures/unit/pops/binder/bindings_composer/hiera1config/modules/good/common.yaml deleted file mode 100644 index 38494c2d5..000000000 --- a/spec/fixtures/unit/pops/binder/bindings_composer/hiera1config/modules/good/common.yaml +++ /dev/null @@ -1 +0,0 @@ -the_meaning_of_life: 300 \ No newline at end of file diff --git a/spec/fixtures/unit/pops/binder/bindings_composer/hiera1config/modules/good/hiera.yaml b/spec/fixtures/unit/pops/binder/bindings_composer/hiera1config/modules/good/hiera.yaml deleted file mode 100644 index 5d3ea65a0..000000000 --- a/spec/fixtures/unit/pops/binder/bindings_composer/hiera1config/modules/good/hiera.yaml +++ /dev/null @@ -1,10 +0,0 @@ ---- -version: 2 -hierarchy: - [ ['node', '${fqdn}', '${fqdn}' ], - ['common', 'true', 'common' ] - ] - -backends: - - yaml - - json diff --git a/spec/fixtures/unit/pops/binder/bindings_composer/hiera1config/modules/good/lib/puppet/bindings/good/default.rb b/spec/fixtures/unit/pops/binder/bindings_composer/hiera1config/modules/good/lib/puppet/bindings/good/default.rb new file mode 100644 index 000000000..4bcd500b0 --- /dev/null +++ b/spec/fixtures/unit/pops/binder/bindings_composer/hiera1config/modules/good/lib/puppet/bindings/good/default.rb @@ -0,0 +1,6 @@ +Puppet::Bindings.newbindings('good::default') do |scope| + bind { + name 'the_meaning_of_life' + to 3000 + } +end \ No newline at end of file diff --git a/spec/fixtures/unit/pops/binder/bindings_composer/ok/binder_config.yaml b/spec/fixtures/unit/pops/binder/bindings_composer/ok/binder_config.yaml index 46148bca6..1a1de27f6 100644 --- a/spec/fixtures/unit/pops/binder/bindings_composer/ok/binder_config.yaml +++ b/spec/fixtures/unit/pops/binder/bindings_composer/ok/binder_config.yaml @@ -1,9 +1,9 @@ --- version: 1 layers: - [{name: site, include: 'confdir-hiera:/'}, + [{name: site, include: 'confdir:/confdirtest'}, {name: test, include: 'echo:/quick/brown/fox'}, - {name: modules, include: ['module-hiera:/*/', 'module:/*::default'], exclude: 'module-hiera:/bad/' } + {name: modules, include: ['module:/*::default'], exclude: 'module:/bad::default/' } ] categories: [['node', '$fqdn'], @@ -13,7 +13,4 @@ categories: ] extensions: scheme_handlers: - echo: 'Puppetx::Awesome::EchoSchemeHandler' - - hiera_backends: - echo: 'Puppetx::Awesome::EchoBackend' + echo: 'Puppetx::Awesome2::EchoSchemeHandler' diff --git a/spec/fixtures/unit/pops/binder/bindings_composer/ok/common.yaml b/spec/fixtures/unit/pops/binder/bindings_composer/ok/common.yaml deleted file mode 100644 index e412898ba..000000000 --- a/spec/fixtures/unit/pops/binder/bindings_composer/ok/common.yaml +++ /dev/null @@ -1 +0,0 @@ -has_funny_hat: 'the pope' \ No newline at end of file diff --git a/spec/fixtures/unit/pops/binder/bindings_composer/ok/hiera.yaml b/spec/fixtures/unit/pops/binder/bindings_composer/ok/hiera.yaml deleted file mode 100644 index a56580ac6..000000000 --- a/spec/fixtures/unit/pops/binder/bindings_composer/ok/hiera.yaml +++ /dev/null @@ -1,11 +0,0 @@ ---- -version: 2 - -hierarchy: - [ ['node', '${fqdn}', '${fqdn}' ], - ['common', 'true', 'common' ] - ] - -backends: - - yaml - - json diff --git a/spec/fixtures/unit/pops/binder/bindings_composer/ok/lib/puppet/bindings/confdirtest.rb b/spec/fixtures/unit/pops/binder/bindings_composer/ok/lib/puppet/bindings/confdirtest.rb new file mode 100644 index 000000000..6973c4775 --- /dev/null +++ b/spec/fixtures/unit/pops/binder/bindings_composer/ok/lib/puppet/bindings/confdirtest.rb @@ -0,0 +1,12 @@ +Puppet::Bindings.newbindings('confdirtest') do |scope| + bind { + name 'has_funny_hat' + to 'the pope' + } + when_in_category('node', 'localhost') { + bind { + name 'the_meaning_of_life' + to 42 + } + } +end \ No newline at end of file diff --git a/spec/fixtures/unit/pops/binder/bindings_composer/ok/localhost.yaml b/spec/fixtures/unit/pops/binder/bindings_composer/ok/localhost.yaml deleted file mode 100644 index 1976ccec8..000000000 --- a/spec/fixtures/unit/pops/binder/bindings_composer/ok/localhost.yaml +++ /dev/null @@ -1 +0,0 @@ -the_meaning_of_life: 42 \ No newline at end of file diff --git a/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome/common.yaml b/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome/common.yaml deleted file mode 100644 index 4bdf31981..000000000 --- a/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome/common.yaml +++ /dev/null @@ -1,3 +0,0 @@ -awesome_x: 'golden' -the_meaning_of_life: 100 -has_funny_hat: 'kkk' \ No newline at end of file diff --git a/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome/hiera.yaml b/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome/hiera.yaml deleted file mode 100644 index f83cb1194..000000000 --- a/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome/hiera.yaml +++ /dev/null @@ -1,13 +0,0 @@ ---- -version: 2 - -hierarchy: - [ ['node', '${fqdn}', '${fqdn}' ], -# ['osfamily', '${osfamily}', 'osfamily' ] - ['common', 'true', 'common' ] - ] - -backends: - - yaml - - json - - echo diff --git a/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome/lib/puppet/bindings/awesome/default.rb b/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome/lib/puppet/bindings/awesome/default.rb deleted file mode 100644 index 2517202aa..000000000 --- a/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome/lib/puppet/bindings/awesome/default.rb +++ /dev/null @@ -1,4 +0,0 @@ -Puppet::Bindings.newbindings('awesome::default') do |scope| - bind.name('all your base').to('are belong to us') - bind.name('env_meaning_of_life').to(puppet_string("$environment thinks it is 42", __FILE__)) -end \ No newline at end of file diff --git a/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome/lib/puppetx/awesome/echo_backend.rb b/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome/lib/puppetx/awesome/echo_backend.rb deleted file mode 100644 index 7de067a9e..000000000 --- a/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome/lib/puppetx/awesome/echo_backend.rb +++ /dev/null @@ -1,11 +0,0 @@ -require 'puppetx/puppet/hiera2_backend' - -module Puppetx - module Awesome - class EchoBackend < Puppetx::Puppet::Hiera2Backend - def read_data(directory, file_name) - {"echo::#{file_name}" => "echo... #{File.basename(directory)}/#{file_name}"} - end - end - end -end \ No newline at end of file diff --git a/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome/localhost.yaml b/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome/localhost.yaml deleted file mode 100644 index 01aba4d93..000000000 --- a/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome/localhost.yaml +++ /dev/null @@ -1 +0,0 @@ -good_x: 'golden' \ No newline at end of file diff --git a/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome2/lib/puppet/bindings/awesome2/default.rb b/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome2/lib/puppet/bindings/awesome2/default.rb new file mode 100644 index 000000000..5c671c82c --- /dev/null +++ b/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome2/lib/puppet/bindings/awesome2/default.rb @@ -0,0 +1,22 @@ +Puppet::Bindings.newbindings('awesome2::default') do |scope| + bind.name('all your base').to('are belong to us') + bind.name('env_meaning_of_life').to(puppet_string("$environment thinks it is 42", __FILE__)) + bind { + name 'awesome_x' + to 'golden' + } + bind { + name 'the_meaning_of_life' + to 100 + } + bind { + name 'has_funny_hat' + to 'kkk' + } + when_in_category('node', 'localhost') { + bind { + name 'good_x' + to 'golden' + } + } +end \ No newline at end of file diff --git a/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome/lib/puppetx/awesome/echo_scheme_handler.rb b/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome2/lib/puppetx/awesome2/echo_scheme_handler.rb similarity index 97% rename from spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome/lib/puppetx/awesome/echo_scheme_handler.rb rename to spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome2/lib/puppetx/awesome2/echo_scheme_handler.rb index 3f97a89ab..4fb29ee38 100644 --- a/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome/lib/puppetx/awesome/echo_scheme_handler.rb +++ b/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome2/lib/puppetx/awesome2/echo_scheme_handler.rb @@ -1,7 +1,7 @@ require 'puppetx/puppet/bindings_scheme_handler' module Puppetx - module Awesome + module Awesome2 # A binding scheme that echos its path # 'echo:/quick/brown/fox' becomes key '::quick::brown::fox' => 'echo: quick brown fox'. # (silly class for testing loading of extension) diff --git a/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/bad/common.yaml b/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/bad/common.yaml deleted file mode 100644 index bcb9f61ce..000000000 --- a/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/bad/common.yaml +++ /dev/null @@ -1,3 +0,0 @@ -bad_x: 'rotten' -the_meaning_of_life: 200 -has_funny_hat: 'the syldavians' \ No newline at end of file diff --git a/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/bad/hiera_config.yaml b/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/bad/hiera_config.yaml deleted file mode 100644 index bb00d74f9..000000000 --- a/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/bad/hiera_config.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# BROKEN ON PURPOSE ---- -hierarchyyyyyy: - - ['node', '${fqdn}', '${fqdn}' - - ['common', 'true', 'common' - -backendsssssss: - - yaml - - json diff --git a/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/bad/lib/puppet/bindings/bad/default.rb b/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/bad/lib/puppet/bindings/bad/default.rb new file mode 100644 index 000000000..aa9a1fef3 --- /dev/null +++ b/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/bad/lib/puppet/bindings/bad/default.rb @@ -0,0 +1,5 @@ +nil + nil + nil # broken on purpose, this file should never be loaded + +Puppet::Bindings.newbindings('bad::default') do |scope| + nil + nil + nil # broken on purpose, this should never be evaluated +end \ No newline at end of file diff --git a/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/good/common.yaml b/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/good/common.yaml deleted file mode 100644 index ad5a5d9aa..000000000 --- a/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/good/common.yaml +++ /dev/null @@ -1,2 +0,0 @@ -good_x: 'decent' -the_meaning_of_life: 300 \ No newline at end of file diff --git a/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/good/hiera.yaml b/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/good/hiera.yaml deleted file mode 100644 index a56580ac6..000000000 --- a/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/good/hiera.yaml +++ /dev/null @@ -1,11 +0,0 @@ ---- -version: 2 - -hierarchy: - [ ['node', '${fqdn}', '${fqdn}' ], - ['common', 'true', 'common' ] - ] - -backends: - - yaml - - json diff --git a/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/good/lib/puppet/bindings/good/default.rb b/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/good/lib/puppet/bindings/good/default.rb new file mode 100644 index 000000000..5f7139a53 --- /dev/null +++ b/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/good/lib/puppet/bindings/good/default.rb @@ -0,0 +1,10 @@ +Puppet::Bindings.newbindings('good::default') do |scope| + bind { + name 'good_x' + to 'decent' + } + bind { + name 'the_meaning_of_life' + to 300 + } +end \ No newline at end of file diff --git a/spec/fixtures/unit/pops/binder/config/binder_config/ok/binder_config.yaml b/spec/fixtures/unit/pops/binder/config/binder_config/ok/binder_config.yaml index c51ee4c01..4ad3555ea 100644 --- a/spec/fixtures/unit/pops/binder/config/binder_config/ok/binder_config.yaml +++ b/spec/fixtures/unit/pops/binder/config/binder_config/ok/binder_config.yaml @@ -1,8 +1,8 @@ --- version: 1 layers: - - {name: site, include: 'confdir-hiera:/'} - - {name: modules, include: 'module-hiera:/*/', exclude: 'module-hiera:/bad/' } + - {name: site, include: 'confdir:/'} + - {name: modules, include: 'module:/*::test/', exclude: 'module:/bad::test/' } categories: - ['node', '$fqn'] - ['environment', '$environment'] diff --git a/spec/fixtures/unit/pops/binder/hiera2/bindings_provider/ok/hiera.yaml b/spec/fixtures/unit/pops/binder/hiera2/bindings_provider/ok/hiera.yaml deleted file mode 100644 index 4ea7358a4..000000000 --- a/spec/fixtures/unit/pops/binder/hiera2/bindings_provider/ok/hiera.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -version: 2 - -hierarchy: - - ['node', '${node}', '${node}' ] - -backends: - - yaml - - json diff --git a/spec/fixtures/unit/pops/binder/hiera2/bindings_provider/ok/node.example.com.json b/spec/fixtures/unit/pops/binder/hiera2/bindings_provider/ok/node.example.com.json deleted file mode 100644 index 5b4a104d5..000000000 --- a/spec/fixtures/unit/pops/binder/hiera2/bindings_provider/ok/node.example.com.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "a_json_number": 142, - "a_string": "don't want this to override", - "a_json_string": "one hundred and forty two", - "a_json_eval": "the answer from \"json\" is ${a} and \\${a}.", - "a_json_eval2": "the answer\nfrom \\\"json\\\" is $a and \\$a", - "a_json_array": ["a", "b", 100], - "a_json_hash": { "a": 1, "b": 2 } -} diff --git a/spec/fixtures/unit/pops/binder/hiera2/bindings_provider/ok/node.example.com.yaml b/spec/fixtures/unit/pops/binder/hiera2/bindings_provider/ok/node.example.com.yaml deleted file mode 100644 index 01340d6c7..000000000 --- a/spec/fixtures/unit/pops/binder/hiera2/bindings_provider/ok/node.example.com.yaml +++ /dev/null @@ -1,5 +0,0 @@ -a_number: 42 -a_string: forty two -an_eval: "the answer from \"yaml\" is ${a}." -an_eval2: "the answer\nfrom \\\"yaml\\\" is $a and \\$a" - diff --git a/spec/fixtures/unit/pops/binder/hiera2/config/bad_syntax/hiera.yaml b/spec/fixtures/unit/pops/binder/hiera2/config/bad_syntax/hiera.yaml deleted file mode 100644 index 3c1c2d811..000000000 --- a/spec/fixtures/unit/pops/binder/hiera2/config/bad_syntax/hiera.yaml +++ /dev/null @@ -1,10 +0,0 @@ ---- -version: 2 - -hierarchy - os: - - ${osfamily} - - for_${osfamily} - -backends: - - yaml diff --git a/spec/fixtures/unit/pops/binder/hiera2/config/malformed_hierarchy/hiera.yaml b/spec/fixtures/unit/pops/binder/hiera2/config/malformed_hierarchy/hiera.yaml deleted file mode 100644 index 13869783f..000000000 --- a/spec/fixtures/unit/pops/binder/hiera2/config/malformed_hierarchy/hiera.yaml +++ /dev/null @@ -1,8 +0,0 @@ ---- -version: 2 - -hierarchy: - ['os', '${osfamily}' ] - -backends: - - yaml diff --git a/spec/fixtures/unit/pops/binder/hiera2/config/missing/foo.txt b/spec/fixtures/unit/pops/binder/hiera2/config/missing/foo.txt deleted file mode 100644 index e5ce6c87a..000000000 --- a/spec/fixtures/unit/pops/binder/hiera2/config/missing/foo.txt +++ /dev/null @@ -1 +0,0 @@ -# Do not delete diff --git a/spec/fixtures/unit/pops/binder/hiera2/config/no_backends/hiera.yaml b/spec/fixtures/unit/pops/binder/hiera2/config/no_backends/hiera.yaml deleted file mode 100644 index f1e9f4251..000000000 --- a/spec/fixtures/unit/pops/binder/hiera2/config/no_backends/hiera.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -version: 2 -hierarchy: - os: - - ${osfamily} - - for_${osfamily} - diff --git a/spec/fixtures/unit/pops/binder/hiera2/config/no_hierarchy/hiera.yaml b/spec/fixtures/unit/pops/binder/hiera2/config/no_hierarchy/hiera.yaml deleted file mode 100644 index 238752dd0..000000000 --- a/spec/fixtures/unit/pops/binder/hiera2/config/no_hierarchy/hiera.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -version: 2 -backends: - - yaml diff --git a/spec/fixtures/unit/pops/binder/hiera2/config/not_a_hash/hiera.yaml b/spec/fixtures/unit/pops/binder/hiera2/config/not_a_hash/hiera.yaml deleted file mode 100644 index a9e37cff5..000000000 --- a/spec/fixtures/unit/pops/binder/hiera2/config/not_a_hash/hiera.yaml +++ /dev/null @@ -1,2 +0,0 @@ -- first -- second diff --git a/spec/fixtures/unit/pops/binder/hiera2/config/ok/hiera.yaml b/spec/fixtures/unit/pops/binder/hiera2/config/ok/hiera.yaml deleted file mode 100644 index 4e2f493d1..000000000 --- a/spec/fixtures/unit/pops/binder/hiera2/config/ok/hiera.yaml +++ /dev/null @@ -1,8 +0,0 @@ ---- -version: 2 - -hierarchy: - - ['os', '${osfamily}', 'for_${osfamily}' ] - -backends: - - yaml diff --git a/spec/fixtures/unit/pops/binder/hiera2/yaml_backend/empty/common.yaml b/spec/fixtures/unit/pops/binder/hiera2/yaml_backend/empty/common.yaml deleted file mode 100644 index e69de29bb..000000000 diff --git a/spec/fixtures/unit/pops/binder/hiera2/yaml_backend/invalid/common.yaml b/spec/fixtures/unit/pops/binder/hiera2/yaml_backend/invalid/common.yaml deleted file mode 100644 index ed97d539c..000000000 --- a/spec/fixtures/unit/pops/binder/hiera2/yaml_backend/invalid/common.yaml +++ /dev/null @@ -1 +0,0 @@ ---- diff --git a/spec/fixtures/unit/pops/binder/hiera2/yaml_backend/ok/common.yaml b/spec/fixtures/unit/pops/binder/hiera2/yaml_backend/ok/common.yaml deleted file mode 100644 index 0aa0b6bcb..000000000 --- a/spec/fixtures/unit/pops/binder/hiera2/yaml_backend/ok/common.yaml +++ /dev/null @@ -1,2 +0,0 @@ ---- -brillig: slithy diff --git a/spec/unit/pops/binder/bindings_composer_spec.rb b/spec/unit/pops/binder/bindings_composer_spec.rb index a0986d4dd..52451593b 100644 --- a/spec/unit/pops/binder/bindings_composer_spec.rb +++ b/spec/unit/pops/binder/bindings_composer_spec.rb @@ -1,4 +1,5 @@ require 'spec_helper' +require 'puppet/pops' require 'puppet_spec/pops' describe 'BinderComposer' do @@ -32,7 +33,11 @@ describe 'BinderComposer' do it 'should load everything without errors' do Puppet.settings[:confdir] = config_directory + Puppet.settings[:libdir] = File.join(config_directory, 'lib') Puppet.settings[:modulepath] = File.join(config_directory, 'modules') + # this ensure the binder is active at the right time + # (issues with getting a /dev/null path for "confdir" / "libdir") + raise "Binder not active" unless scope.compiler.is_binder_active? diagnostics = diag composer = Puppet::Pops::Binder::BindingsComposer.new() @@ -46,7 +51,6 @@ describe 'BinderComposer' do binder.define_categories(factory.categories([['node', 'localhost'], ['environment', 'production']])) binder.define_layers(layered_bindings) injector = Puppet::Pops::Binder::Injector.new(binder) - expect(injector.lookup(scope, 'awesome_x')).to be == 'golden' expect(injector.lookup(scope, 'good_x')).to be == 'golden' expect(injector.lookup(scope, 'rotten_x')).to be == nil @@ -55,8 +59,6 @@ describe 'BinderComposer' do expect(injector.lookup(scope, 'all your base')).to be == 'are belong to us' expect(injector.lookup(scope, 'env_meaning_of_life')).to be == 'production thinks it is 42' expect(injector.lookup(scope, '::quick::brown::fox')).to be == 'echo: quick brown fox' - expect(injector.lookup(scope, 'echo::common')).to be == 'echo... awesome/common' - expect(injector.lookup(scope, 'echo::localhost')).to be == 'echo... awesome/localhost' end end @@ -65,7 +67,11 @@ describe 'BinderComposer' do it 'should load without errors by skipping the hiera.yaml' do Puppet.settings[:confdir] = config_directory + Puppet.settings[:libdir] = File.join(config_directory, 'lib') Puppet.settings[:modulepath] = File.join(config_directory, 'modules') + # this ensure the binder is active at the right time + # (issues with getting a /dev/null path for "confdir" / "libdir") + raise "Binder not active" unless scope.compiler.is_binder_active? diagnostics = diag composer = Puppet::Pops::Binder::BindingsComposer.new() @@ -80,7 +86,7 @@ describe 'BinderComposer' do binder.define_layers(layered_bindings) injector = Puppet::Pops::Binder::Injector.new(binder) - expect(injector.lookup(scope, 'the_meaning_of_life')).to be == 300 + expect(injector.lookup(scope, 'the_meaning_of_life')).to be == 3000 end end diff --git a/spec/unit/pops/binder/config/binder_config_spec.rb b/spec/unit/pops/binder/config/binder_config_spec.rb index dcd626737..5fd2681d8 100644 --- a/spec/unit/pops/binder/config/binder_config_spec.rb +++ b/spec/unit/pops/binder/config/binder_config_spec.rb @@ -14,9 +14,9 @@ describe 'BinderConfig' do config = Puppet::Pops::Binder::Config::BinderConfig.new(diagnostics) expect(acceptor.errors?()).to be == false expect(config.layering_config[0]['name']).to be == 'site' - expect(config.layering_config[0]['include']).to be == ['confdir-hiera:/', 'confdir:/default?optional'] + expect(config.layering_config[0]['include']).to be == ['confdir:/default?optional'] expect(config.layering_config[1]['name']).to be == 'modules' - expect(config.layering_config[1]['include']).to be == ['module-hiera:/*/', 'module:/*::default'] + expect(config.layering_config[1]['include']).to be == ['module:/*::default'] expect(config.categorization.is_a?(Array)).to be == true expect(config.categorization.size).to be == 4 @@ -31,10 +31,10 @@ describe 'BinderConfig' do config = Puppet::Pops::Binder::Config::BinderConfig.new(diag) expect(acceptor.errors?()).to be == false expect(config.layering_config[0]['name']).to be == 'site' - expect(config.layering_config[0]['include']).to be == 'confdir-hiera:/' + expect(config.layering_config[0]['include']).to be == 'confdir:/' expect(config.layering_config[1]['name']).to be == 'modules' - expect(config.layering_config[1]['include']).to be == 'module-hiera:/*/' - expect(config.layering_config[1]['exclude']).to be == 'module-hiera:/bad/' + expect(config.layering_config[1]['include']).to be == 'module:/*::test/' + expect(config.layering_config[1]['exclude']).to be == 'module:/bad::test/' expect(config.categorization.is_a?(Array)).to be == true expect(config.categorization.size).to be == 3 diff --git a/spec/unit/pops/binder/hiera2/bindings_provider_spec.rb b/spec/unit/pops/binder/hiera2/bindings_provider_spec.rb deleted file mode 100644 index b0924efab..000000000 --- a/spec/unit/pops/binder/hiera2/bindings_provider_spec.rb +++ /dev/null @@ -1,77 +0,0 @@ -require 'spec_helper' -require 'puppet/pops' -require 'puppet_spec/pops' -require 'puppet_spec/scope' - - -describe 'The hiera2 bindings provider' do - - include PuppetSpec::Pops - include PuppetSpec::Scope - - def config_dir(config_name) - File.dirname(my_fixture("#{config_name}/hiera.yaml")) - end - - before(:each) do - Puppet[:binder] = true - end - - context 'when loading ok bindings' do - - let(:node) { 'node.example.com' } - let(:acceptor) { Puppet::Pops::Validation::Acceptor.new() } - let(:scope) { s = create_test_scope_for_node(node); s['a'] = '42'; s['node'] = node; s } - let(:module_dir) { config_dir('ok') } - let(:node_binder) { b = Puppet::Pops::Binder::Binder.new(); b.define_categories(Puppet::Pops::Binder::BindingsFactory.categories(['node', node])); b } - let(:bindings) { Puppet::Pops::Binder::Hiera2::BindingsProvider.new('test', module_dir, acceptor).load_bindings(scope) } - let(:test_layer_with_bindings) { bindings } - - it 'should load and validate OK bindings' do - Puppet::Pops::Binder::BindingsValidatorFactory.new().validator(acceptor).validate(bindings) - acceptor.errors_or_warnings?.should() == false - end - - it 'should contain the expected effective categories' do - bindings.effective_categories.categories.collect {|c| [c.categorization, c.value] }.should == [['node', 'node.example.com']] - end - - it 'should produce the expected bindings model' do - bindings.class.should() == Puppet::Pops::Binder::Bindings::ContributedBindings - bindings.bindings.bindings.each do |cat| - cat.class.should() == Puppet::Pops::Binder::Bindings::CategorizedBindings - cat.predicates.length.should() == 1 - cat.predicates[0].categorization.should() == 'node' - cat.predicates[0].value.should() == node - cat.bindings.each do |b| - b.class.should() == Puppet::Pops::Binder::Bindings::Binding - ['a_number', 'a_string', 'an_eval', 'an_eval2', 'a_json_number', 'a_json_string', 'a_json_eval', - 'a_json_eval2', 'a_json_hash', 'a_json_array'].index(b.name).should() >= 0 - b.producer.class.should() == Puppet::Pops::Binder::Bindings::EvaluatingProducerDescriptor if b.name == 'an_eval' - end - end - end - - it 'should make the injector lookup expected constants' do - node_binder.define_layers(Puppet::Pops::Binder::BindingsFactory.layered_bindings(test_layer_with_bindings)) - injector = Puppet::Pops::Binder::Injector.new(node_binder) - - injector.lookup(scope, 'a_number').should == 42 - injector.lookup(scope, 'a_string').should == 'forty two' - injector.lookup(scope, 'a_json_number').should == 142 - injector.lookup(scope, 'a_json_string').should == 'one hundred and forty two' - expect(injector.lookup(scope, "a_json_array")).to be == ["a", "b", 100] - expect(injector.lookup(scope, "a_json_hash")).to be == {"a" => 1, "b" => 2} - end - - it 'should make the injector lookup and evaluate expressions' do - node_binder.define_layers(Puppet::Pops::Binder::BindingsFactory.layered_bindings(test_layer_with_bindings)) - injector = Puppet::Pops::Binder::Injector.new(node_binder) - - injector.lookup(scope, 'an_eval').should == 'the answer from "yaml" is 42.' - injector.lookup(scope, 'an_eval2').should == "the answer\nfrom \\\"yaml\\\" is 42 and $a" - injector.lookup(scope, 'a_json_eval').should == 'the answer from "json" is 42 and ${a}.' - injector.lookup(scope, 'a_json_eval2').should == "the answer\nfrom \\\"json\\\" is 42 and $a" - end - end -end diff --git a/spec/unit/pops/binder/hiera2/config_spec.rb b/spec/unit/pops/binder/hiera2/config_spec.rb deleted file mode 100644 index 970e72a9a..000000000 --- a/spec/unit/pops/binder/hiera2/config_spec.rb +++ /dev/null @@ -1,61 +0,0 @@ -require 'spec_helper' -require 'puppet/pops' -require 'puppet_spec/pops' - -# A Backend class that doesn't implement the needed API -class Puppet::Pops::Binder::Hiera2::Bad_backend -end - -describe 'The hiera2 config' do - - include PuppetSpec::Pops - - let(:acceptor) { Puppet::Pops::Validation::Acceptor.new() } - let(:diag) { Puppet::Pops::Binder::Hiera2::DiagnosticProducer.new(acceptor) } - - def config_dir(config_name) - File.dirname(my_fixture("#{config_name}/hiera.yaml")) - end - - def test_config_issue(config_name, issue) - Puppet::Pops::Binder::Hiera2::Config.new(config_dir(config_name), diag) - acceptor.should have_issue(issue) - end - - it 'should load and validate OK configuration' do - Puppet::Pops::Binder::Hiera2::Config.new(config_dir('ok'), diag) - acceptor.errors_or_warnings?.should() == false - end - - it 'should report missing config file' do - Puppet::Pops::Binder::Hiera2::Config.new(File.dirname(my_fixture('missing/foo.txt')), diag) - acceptor.should have_issue(Puppet::Pops::Binder::Hiera2::Issues::CONFIG_FILE_NOT_FOUND) - end - - it 'should report when config is not a hash' do - test_config_issue('not_a_hash', Puppet::Pops::Binder::Hiera2::Issues::CONFIG_IS_NOT_HASH) - end - - it 'should report when config has syntax problems' do - if RUBY_VERSION.start_with?("1.8") - # Yes, it is a lobotomy or 2 short of a full brain... - # if a hash key is not in quotes it continues on the next line and gobbles what is there instead - # of reporting an error - test_config_issue('bad_syntax', Puppet::Pops::Binder::Hiera2::Issues::MISSING_HIERARCHY) - else - test_config_issue('bad_syntax', Puppet::Pops::Binder::Hiera2::Issues::CONFIG_FILE_SYNTAX_ERROR) - end - end - - it 'should report when config has no hierarchy defined' do - test_config_issue('no_hierarchy', Puppet::Pops::Binder::Hiera2::Issues::MISSING_HIERARCHY) - end - - it 'should report when config has no backends defined' do - test_config_issue('no_backends', Puppet::Pops::Binder::Hiera2::Issues::MISSING_BACKENDS) - end - - it 'should report when config hierarchy is malformed' do - test_config_issue('malformed_hierarchy', Puppet::Pops::Binder::Hiera2::Issues::CATEGORY_MUST_BE_THREE_ELEMENT_ARRAY) - end -end diff --git a/spec/unit/pops/binder/hiera2/yaml_backend_spec.rb b/spec/unit/pops/binder/hiera2/yaml_backend_spec.rb deleted file mode 100644 index 702819c9a..000000000 --- a/spec/unit/pops/binder/hiera2/yaml_backend_spec.rb +++ /dev/null @@ -1,33 +0,0 @@ -require 'spec_helper' -require 'puppet/pops' - -describe "Hiera2 YAML backend" do - - include PuppetSpec::Files - - def fixture_dir(config_name) - my_fixture("#{config_name}") - end - - before(:all) do - Puppet[:binder] = true - require 'puppetx' - require 'puppet/pops/binder/hiera2/yaml_backend' - end - - after(:all) do - Puppet[:binder] = false - end - - it "returns the expected hash from a valid yaml file" do - Puppet::Pops::Binder::Hiera2::YamlBackend.new().read_data(fixture_dir("ok"), "common").should == {'brillig' => 'slithy'} - end - - it "returns an empty hash from an empty yaml file" do - Puppet::Pops::Binder::Hiera2::YamlBackend.new().read_data(fixture_dir("empty"), "common").should == {} - end - - it "returns an empty hash from an invalid yaml file" do - Puppet::Pops::Binder::Hiera2::YamlBackend.new().read_data(fixture_dir("invalid"), "common").should == {} - end -end From 11be2bf55b2092d69a60549b105bdbabd8f19b4d Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 3 Oct 2013 05:49:53 +0200 Subject: [PATCH 260/800] (pup-546) Remove automatic injector lookup for class parameters. --- lib/puppet/resource.rb | 11 +---------- spec/unit/resource_spec.rb | 33 --------------------------------- 2 files changed, 1 insertion(+), 43 deletions(-) diff --git a/lib/puppet/resource.rb b/lib/puppet/resource.rb index d4eac6f6d..1dd59a0ab 100644 --- a/lib/puppet/resource.rb +++ b/lib/puppet/resource.rb @@ -339,16 +339,7 @@ class Puppet::Resource return nil unless resource_type.type == :hostclass name = "#{resource_type.name}::#{param}" - # Lookup with injector (optionally), and if no value bound, lookup with "classic hiera" - result = nil - if scope.compiler.is_binder_active? - result = scope.compiler.injector.lookup(scope, name) - end - if result.nil? - lookup_with_databinding(name, scope) - else - result - end + lookup_with_databinding(name, scope) end private :lookup_external_default_for diff --git a/spec/unit/resource_spec.rb b/spec/unit/resource_spec.rb index a8f21d205..b6443865a 100755 --- a/spec/unit/resource_spec.rb +++ b/spec/unit/resource_spec.rb @@ -338,14 +338,6 @@ describe Puppet::Resource do resource.set_default_parameters(scope) end - it "should query the injector using a namespaced key" do - compiler.injector.expects(:lookup).with(scope, 'apache::port').returns("8081") - - resource.set_default_parameters(scope) - - resource[:port].should == "8081" - end - it "should use the value from the data_binding terminus" do Puppet::DataBinding.indirection.expects(:find).returns('443') @@ -354,23 +346,6 @@ describe Puppet::Resource do resource[:port].should == '443' end - it "should use the value from the injector" do - compiler.injector.expects(:lookup).with(scope, 'apache::port').returns('443') - - resource.set_default_parameters(scope) - - resource[:port].should == '443' - end - - it "should not call the DataBinding terminus when injector produces a value" do - compiler.injector.expects(:lookup).with(scope, 'apache::port').returns('443') - Puppet::DataBinding.indirection.expects(:find).never() - - resource.set_default_parameters(scope) - - resource[:port].should == '443' - end - it "should use the default value if the data_binding terminus returns nil" do Puppet::DataBinding.indirection.expects(:find).returns(nil) @@ -386,14 +361,6 @@ describe Puppet::Resource do }.to raise_error(Puppet::Error, /Error from DataBinding 'hiera' while looking up 'apache::port':.*Forgettabotit/) end - it "should use the default value if the injector returns nil" do - compiler.injector.expects(:lookup).returns(nil) - Puppet::DataBinding.indirection.expects(:find).returns(nil) - - resource.set_default_parameters(scope) - - resource[:port].should == '80' - end end context "when a value is provided" do From 3937029b70cc7ce5145067c7dd25378110837413 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 3 Oct 2013 06:11:44 +0200 Subject: [PATCH 261/800] (pup-546) Remove hiera2.rb (central file with require's for hiera2) --- lib/puppet/pops/binder/hiera2.rb | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 lib/puppet/pops/binder/hiera2.rb diff --git a/lib/puppet/pops/binder/hiera2.rb b/lib/puppet/pops/binder/hiera2.rb deleted file mode 100644 index e86299120..000000000 --- a/lib/puppet/pops/binder/hiera2.rb +++ /dev/null @@ -1,10 +0,0 @@ -# The Hiera2 Module contains the classes needed to configure a bindings producer -# to read module specific data. The configuration is expected to be found in -# a hiera.yaml file in the root of each module -module Puppet::Pops::Binder::Hiera2 - require 'puppet/pops/binder/hiera2/config_checker' - require 'puppet/pops/binder/hiera2/config' - require 'puppet/pops/binder/hiera2/diagnostic_producer' - require 'puppet/pops/binder/hiera2/bindings_provider' - require 'puppet/pops/binder/hiera2/issues' -end From c494b1e9e6e940369e458631634e2cab2cf0d65f Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 19 Dec 2013 00:18:13 +0100 Subject: [PATCH 262/800] (pup-546) Update default.rb and remove mention of hiera2 The documentation for the --binder setting is updated to reflect that hiera2-data-in-modules is no longer loaded, while still allowing bindings to be made in ruby, now referred to as "bindings in modules". --- lib/puppet/defaults.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index 80c13c824..c07334080 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -266,8 +266,9 @@ module Puppet }, :binder => { :default => false, - :desc => "Turns the binding system on or off. This includes hiera-2 and data in modules. The binding system aggregates data from - modules and other locations and makes them available for lookup. The binding system is experimental and any or all of it may change.", + :desc => "Turns the binding system on or off. This includes bindings in modules. + The binding system aggregates data from modules and other locations and makes them available for lookup. + The binding system is experimental and any or all of it may change.", :type => :boolean, }, :binder_config => { From a223088dc444459836720c4726137ef646b119a5 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 19 Dec 2013 16:00:22 +0100 Subject: [PATCH 263/800] (PUP-546) Replace lookup function with something useful With removed hiera2, the lookup function is not as useful and is now replaced with the lookup from (#22574) which first looks in bound data and then in hiera. The intent is to use the technolgy agnostic "lookup" in the future parser/evaluator to enable shorthand notation. The actual lookup mechanism/technology is thereby not visible to the user. --- lib/puppet/parser/functions/lookup.rb | 348 ++++++++++++++++++++-- spec/unit/parser/functions/lookup_spec.rb | 64 +++- 2 files changed, 376 insertions(+), 36 deletions(-) diff --git a/lib/puppet/parser/functions/lookup.rb b/lib/puppet/parser/functions/lookup.rb index 800c9bd3d..3b71e4bec 100644 --- a/lib/puppet/parser/functions/lookup.rb +++ b/lib/puppet/parser/functions/lookup.rb @@ -1,44 +1,340 @@ Puppet::Parser::Functions.newfunction(:lookup, :type => :rvalue, :arity => -2, :doc => <<-'ENDHEREDOC') do |args| -Looks up data defined using Puppet Bindings. -The function is callable with one or two arguments and optionally with a lambda to process the result. -The second argument can be a type specification; a String that describes the type of the produced result. -If a value is found, an assert is made that the value is compliant with the specified type. +Looks up data defined using Puppet Bindings and Hiera. +The function is callable with one to three arguments and optionally with a code block to further process the result. -When called with one argument; the name: +The lookup function can be called in one of these ways: - lookup('the_name') + lookup(name) + lookup(name, type) + lookup(name, type, default) + lookup(options_hash) + lookup(name, options_hash) -When called with two arguments; the name, and the expected type: +The function may optionally be called with a code block / lambda with the following signatures: - lookup('the_name', 'String') + lookup(...) |$result| { ... } + lookup(...) |$name, $result| { ... } + lookup(...) |$name, $result, $default| { ... } -Using a lambda to process the looked up result. +The longer signatures are useful when the block needs to raise an error (it can report the name), or +if it needs to know if the given default value was selected. - lookup('the_name') |$result| { if $result == undef { 'Jane Doe' } else { $result }} +The code block receives the following three arguments: + +* The `$name` is the last name that was looked up (*the* name if only one name was looked up) +* The `$result` is the looked up value (or the default value if not found). +* The `$default` is the given default value (`undef` if not given). + +The block, if present, is called with the result from the lookup. The value produced by the block is also what is +produced by the `lookup` function. +When a block is used, it is the users responsibility to call `error` if the result does not meet additional +criteria, or if an undef value is not acceptable. If a value is not found, and a default has been +specified, the default value is given to the block. + +The content of the options hash is: + +* `name` - The name to lookup. (Mutually exclusive with `first_found`) +* `type` - The type to assert (a Type or a type specification in string form) +* `default` - The default value if there was no value found (must comply with the data type) +* `first_found` - An array of names to search, the value of the first found is used. (Mutually + exclusive with `name`). +* `accept_undef` - (default `false`) An `undef` result is accepted if this options is set to `true`. +* `override` - a hash with map from names to values that are used instead of the underlying bindings. If the name + is found here it wins. Defaults to an empty hash. +* `extra` - a hash with map from names to values that are used as a last resort to obtain a value. Defaults to an + empty hash. + +When the call is on the form `lookup(name, options_hash)`, or `lookup(name, type, options_hash)`, the given name +argument wins over the `options_hash['name']`. + +The search order is `override` (if given), then `binder`, then `hiera` and finally `extra` (if given). The first to produce +a value other than undef for a given name wins. The type specification is one of: -* the basic types; 'Integer', 'String', 'Float', 'Boolean', or 'Pattern' (regular expression) -* an Array with an optional element type given in '[]', that when not given defaults to '[Data]' -* a Hash with optional key and value types given in '[]', where key type defaults to 'Literal' and value to 'Data', if - only one type is given, the key defaults to 'Literal' -* the abstract type 'Literal' which is one of the basic types -* the abstract type 'Data' which is 'Literal', or type compatible with Array[Data], or Hash[Literal, Data] -* the abstract type 'Collection' which is Array or Hash of any element type. -* the abstract type 'Object' which is any kind of type + * A type in the Puppet Type System, e.g.: + * `Integer`, an integral value with optional range e.g.: + * `Integer[0, default]` - 0 or positive + * `Integer[default, -1]` - negative, + * `Integer[1,100]` - value between 1 and 100 inclusive + * `String`- any string + * `Float` - floating point number (same signature as for Integer for `Integer` ranges) + * `Boolean` - true of false (strict) + * `Array` - an array (of Data by default), or parameterized as `Array[]`, where + `` is the expected type of elements + * `Hash`, - a hash (of default `Literal` keys and `Data` values), or parameterized as + `Hash[]`, `Hash[, ]`, where ``, and + `` are the types of the keys and values respectively + (key is `Literal` by default). + * `Data` - abstract type representing any `Literal`, `Array[Data]`, or `Hash[Literal, Data]` + * `Pattern[, , ..., ]` - an enumeration of valid patterns (one or more) where + a pattern is a regular expression string or regular expression, + e.g. `Pattern['.com$', '.net$']`, `Pattern[/[a-z]+[0-9]+/]` + * `Enum[, , ..., ]`, - an enumeration of exact string values (one or more) + e.g. `Enum[blue, red, green]`. + * `Variant[, ,...]` - matches one of the listed types (at least one must be given) + e.g. `Variant[Integer[8000,8999], Integer[20000, 99999]]` to accept a value in either range + * `Regexp`- a regular expression (i.e. the result is a regular expression, not a string + matching a regular expression). + * A string containing a type description - one of the types as shown above but in string form. + +If the function is called without specifying a default value, and nothing is bound to the given name +an error is raised unless the option `accept_undef` is true. If a block is given it must produce an acceptable +value (or call `error`). If the block does not produce an acceptable value an error is +raised. + +Examples: + +When called with one argument; **the name**, it +returns the bound value with the given name after having asserted it has the default datatype `Data`: + + lookup('the_name') + +When called with two arguments; **the name**, and **the expected type**, it +returns the the bound value with the given name after having asserted it has the given data +type ('String' in the example): + + lookup('the_name', 'String') # 3.x + lookup('the_name', String) # parser future + +When called with three arguments, **the name**, the **expected type**, and a **default**, it +returns the the bound value with the given name, or the default after having asserted the value +has the given data type (`String` in the example above): + + lookup('the_name', 'String', 'Fred') # 3x + lookup('the_name', String, 'Fred') # parser future + +Using a lambda to process the looked up result - asserting that it starts with an upper case letter: + + # only with parser future + lookup('the_size', Integer[1,100) |$result| { + if $large_value_allowed and $result > 10 + { error 'Values larger than 10 are not allowed'} + $result + } + +Including the name in the error + + # only with parser future + lookup('the_size', Integer[1,100) |$name, $result| { + if $large_value_allowed and $result > 10 + { error 'The bound value for '${name}' can not be larger than 10 in this configuration'} + $result + } + +When using a block, the value it produces is also asserted against the given type, and it may not be +`undef` unless the option `'accept_undef'` is `true`. + +All options work as the corresponding (direct) argument. The `first_found` option and +`accept_undef` are however only available as options. + +Using the `first_found` option to return the first name that has a bound value: + + lookup({ first_found => ['apache::port', 'nginx::port'], type => 'Integer', default => 80}) + +If you want to make lookup return undef when no value was found instead of raising an error: + + $are_you_there = lookup('peekaboo', { accept_undef => true} ) + $are_you_there = lookup('peekaboo', { accept_undef => true}) |$result| { $result } ENDHEREDOC unless Puppet[:binder] || Puppet[:parser] == 'future' raise Puppet::ParseError, "The lookup function is only available with settings --binder true, or --parser future" end - type_parser = Puppet::Pops::Types::TypeParser.new - pblock = args[-1] if args[-1].respond_to?(:puppet_lambda) - type_name = args[1] unless args[1].respond_to?(:puppet_lambda) - type = type_parser.parse( type_name || "Data") - result = compiler.injector.lookup(self, type, args[0]) - if pblock - result = pblock.call(self, result.nil? ? :undef : result) + + def parse_lookup_args(args) + options = {} + pblock = if args[-1].respond_to?(:puppet_lambda) + args.pop + end + + case args.size + when 1 + # name, or all options + if args[ 0 ].is_a?(Hash) + options = to_symbolic_hash(args[ 0 ]) + else + options[ :name ] = args[ 0 ] + end + + when 2 + # name and type, or name and options + if args[ 1 ].is_a?(Hash) + options = to_symbolic_hash(args[ 1 ]) + options[:name] = args[ 0 ] # silently overwrite option with given name + else + options[:name] = args[ 0 ] + options[:type] = args[ 1 ] + end + + when 3 + # name, type, default (no options) + options[ :name ] = args[ 0 ] + options[ :type ] = args[ 1 ] + options[ :default ] = args[ 2 ] + else + raise Puppet::PareError, "The lookup function accepts 1-3 arguments, got #{args.size}" + end + options[:pblock] = pblock + options + end + + def to_symbolic_hash(input) + names = [:name, :type, :first_found, :default, :accept_undef, :extra, :override] + options = {} + names.each {|n| options[n] = undef_as_nil(input[n.to_s] || input[n]) } + options + end + + def type_mismatch(type_calculator, expected, got) + "has wrong type, expected #{type_calculator.string(expected)}, got #{type_calculator.string(got)}" + end + + def fail(msg) + raise Puppet::ParseError, "Function lookup() " + msg + end + + def fail_lookup(names) + name_part = if names.size == 1 + "the name '#{names[0]}'" + else + "any of the names ['" + names.join(', ') + "']" + end + fail("did not find a value for #{name_part}") + end + + def validate_options(options, type_calculator) + type_parser = Puppet::Pops::Types::TypeParser.new + first_found_type = type_parser.parse('Array[String]') + + if options[:name].nil? && options[:first_found].nil? + fail ("requires a name, or sequence of names in first_found. Neither was given.") + end + + if options[:name] && options[:first_found] + fail("requires either a single name, or a sequence of names in first_found. Both were specified.") + end + + if options[:name] && ! options[:name].is_a?(String) + t = type_calculator.infer(options[:name]) + fail("name, expected String, got #{type_calculator.string(t)}") + end + + if options[:first_found] + t = type_calculator.infer(options[:first_found]) + if !type_calculator.assignable?(first_found_type, t) + fail("first_found #{type_mismatch(type_calculator, first_found_type, t)}") + end + end + + # unless a type is already given (future case), parse the type (or default 'Data'), fails if invalid type is given + unless options[:type].is_a?(Puppet::Pops::Types::PAbstractType) + options[:type] = type_parser.parse(options[:type] || 'Data') + end + + # default value must comply with the given type + if options[:default] + t = type_calculator.infer(options[:default]) + if ! type_calculator.assignable?(options[:type], t) + fail("'default' value #{type_mismatch(type_calculator, options[:type], t)}") + end + end + + if options[:extra] && !options[:extra].is_a?(Hash) + # do not perform inference here, it is enough to know that it is not a hash + fail("'extra' value must be a Hash, got #{options[:extra].class}") + end + options[:extra] = {} unless options[:extra] + + if options[:override] && !options[:override].is_a?(Hash) + # do not perform inference here, it is enough to know that it is not a hash + fail("'override' value must be a Hash, got #{options[:extra].class}") + end + options[:override] = {} unless options[:override] + + end + + def nil_as_undef(x) + x.nil? ? :undef : x + end + + def undef_as_nil(x) + is_nil_or_undef?(x) ? nil : x + end + + def is_nil_or_undef?(x) + x.nil? || x == :undef + end + + # This is used as a marker - a value that cannot (at least not easily) by mistake be found in + # hiera data. + # + class PrivateNotFoundMarker; end + + def search_for(type, name, options) + # search in order, override, injector, hiera, then extra + if !(result = options[:override][name]).nil? + result + elsif !(result = compiler.injector.lookup(self, type, name)).nil? + result + else + result = self.function_hiera([name, PrivateNotFoundMarker]) + if !result.nil? && result != PrivateNotFoundMarker + result + else + options[:extra][name] + end + end + end + + # THE FUNCTION STARTS HERE + + type_calculator = Puppet::Pops::Types::TypeCalculator.new + options = parse_lookup_args(args) + validate_options(options, type_calculator) + names = options[:name] ? [options[:name]] : options[:first_found] + type = options[:type] + + result_with_name = names.reduce([]) do |memo, name| + break memo if !memo[1].nil? + [name, search_for(type, name, options)] + end + + result = if result_with_name[1].nil? + # not found, use default (which may be nil), the default is already type checked + options[:default] + else + # injector.lookup is type-safe already do no need to type check the result + result_with_name[1] + end + + result = if pblock = options[:pblock] + result2 = case pblock.parameter_count + when 1 + pblock.call(self, nil_as_undef(result)) + when 2 + pblock.call(self, result_with_name[ 0 ], nil_as_undef(result)) + else + pblock.call(self, result_with_name[ 0 ], nil_as_undef(result), nil_as_undef(options[ :default ])) + end + + # if the given result was returned, there is not need to type-check it again + if !result2.equal?(result) + t = type_calculator.infer(undef_as_nil(result2)) + if !type_calculator.assignable?(type, t) + fail "the value produced by the given code block #{type_mismatch(type_calculator, type, t)}" + end + end + result2 + else + result + end + + # Finally, the result if nil must be acceptable or an error is raised + if is_nil_or_undef?(result) && !options[:accept_undef] + fail_lookup(names) + else + nil_as_undef(result) end - result.nil? ? :undef : result end diff --git a/spec/unit/parser/functions/lookup_spec.rb b/spec/unit/parser/functions/lookup_spec.rb index dd62926c7..11c6ec644 100644 --- a/spec/unit/parser/functions/lookup_spec.rb +++ b/spec/unit/parser/functions/lookup_spec.rb @@ -20,19 +20,51 @@ describe "lookup function" do it "looks up a value that exists" do scope = scope_with_injections_from(bound(bind_single("a_value", "something"))) - expect(scope.function_lookup(['a_value'])).to eq('something') end - it "returns :undef when the requested value is not bound" do + it "override wins over bound" do scope = scope_with_injections_from(bound(bind_single("a_value", "something"))) + options = {:override => { 'a_value' => 'something_overridden' }} + expect(scope.function_lookup(['a_value', options])).to eq('something_overridden') + end - expect(scope.function_lookup(['not_bound_value'])).to eq(:undef) + it "extra option is used if nothing is found" do + scope = scope_with_injections_from(bound(bind_single("another_value", "something"))) + options = {:extra => { 'a_value' => 'something_extra' }} + expect(scope.function_lookup(['a_value', options])).to eq('something_extra') + end + + it "hiera is called to lookup if value is not bound" do + Puppet::Parser::Scope.any_instance.stubs(:function_hiera).returns('from_hiera') + scope = scope_with_injections_from(bound(bind_single("another_value", "something"))) + expect(scope.function_lookup(['a_value'])).to eq('from_hiera') + end + + it "returns :undef when the requested value is not bound and undef is accepted" do + scope = scope_with_injections_from(bound(bind_single("a_value", "something"))) + expect(scope.function_lookup(['not_bound_value',{'accept_undef' => true}])).to eq(:undef) + end + + it "fails if the requested value is not bound and undef is not allowed" do + scope = scope_with_injections_from(bound(bind_single("a_value", "something"))) + expect do + scope.function_lookup(['not_bound_value']) + end.to raise_error(/did not find a value for the name 'not_bound_value'/) + end + + it "returns the given default value when the requested value is not bound" do + scope = scope_with_injections_from(bound(bind_single("a_value", "something"))) + expect(scope.function_lookup(['not_bound_value','String', 'cigar'])).to eq('cigar') + end + + it "accepts values given in a hash of options" do + scope = scope_with_injections_from(bound(bind_single("a_value", "something"))) + expect(scope.function_lookup(['not_bound_value',{'type' => 'String', 'default' => 'cigar'}])).to eq('cigar') end it "raises an error when the bound type is not assignable to the requested type" do scope = scope_with_injections_from(bound(bind_single("a_value", "something"))) - expect do scope.function_lookup(['a_value', 'Integer']) end.to raise_error(ArgumentError, /incompatible type, expected: Integer, got: String/) @@ -42,25 +74,39 @@ describe "lookup function" do typed_bindings = bindings typed_bindings.bind().string().name("a_value").to("something") scope = scope_with_injections_from(bound(typed_bindings)) - expect(scope.function_lookup(['a_value', 'Data'])).to eq("something") end it "yields to a given lambda and returns the result" do scope = scope_with_injections_from(bound(bind_single("a_value", "something"))) - expect(scope.function_lookup(['a_value', ast_lambda('|$x|{something_else}')])).to eq('something_else') end + it "fails if given lambda produces undef" do + scope = scope_with_injections_from(bound(bind_single("a_value", "something"))) + expect do + scope.function_lookup(['a_value', ast_lambda('|$x|{undef}')]) + end.to raise_error(/did not find a value for the name 'a_value'/) + end + + it "yields name and result to a given lambda" do + scope = scope_with_injections_from(bound(bind_single("a_value", "something"))) + expect(scope.function_lookup(['a_value', ast_lambda('|$name, $result|{[$name, $result]}')])).to eq(['a_value', 'something']) + end + + it "yields name and result and default to a given lambda" do + scope = scope_with_injections_from(bound(bind_single("a_value", "something"))) + expect(scope.function_lookup(['a_value', {'default' => 'cigar'}, + ast_lambda('|$name, $result, $d|{[$name, $result, $d]}')])).to eq(['a_value', 'something', 'cigar']) + end + it "yields to a given lambda and returns the result when giving name and type" do scope = scope_with_injections_from(bound(bind_single("a_value", "something"))) - expect(scope.function_lookup(['a_value', 'String', ast_lambda('|$x|{something_else}')])).to eq('something_else') end it "yields :undef when value is not found and using a lambda" do scope = scope_with_injections_from(bound(bind_single("a_value", "something"))) - expect(scope.function_lookup(['not_bound_value', ast_lambda('|$x|{ if $x == undef {good} else {bad}}')])).to eq('good') end @@ -68,7 +114,6 @@ describe "lookup function" do injector = Puppet::Pops::Binder::Injector.new(binder) scope = create_test_scope_for_node('testing') scope.compiler.injector = injector - scope end @@ -86,7 +131,6 @@ describe "lookup function" do binder = Puppet::Pops::Binder::Binder.new binder.define_categories(Puppet::Pops::Binder::BindingsFactory.categories([])) binder.define_layers(Puppet::Pops::Binder::BindingsFactory.layered_bindings(Puppet::Pops::Binder::BindingsFactory.named_layer('test layer', local_bindings.model))) - binder end From 230794600748320d1dfabc1a8ec50e94cb2fa183 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Thu, 19 Dec 2013 08:26:02 -0800 Subject: [PATCH 264/800] (PUP-665) Allow settings to be undefined Setting a value to nil used to result in falling back to the default (or whatever came later in the lookup chain). This behavior was lost when the settings were pulled apart into multiple classes to handle the lookup. The error showed up in tests for stdlib that use rspec-puppet, which depended on the "nil is default" behavior. --- lib/puppet/settings.rb | 10 ++++++---- spec/unit/settings_spec.rb | 6 ++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/puppet/settings.rb b/lib/puppet/settings.rb index e3e4376e9..8d1873bba 100644 --- a/lib/puppet/settings.rb +++ b/lib/puppet/settings.rb @@ -1136,9 +1136,8 @@ Generated on #{Time.now}. # @api public def lookup(name) @value_sets.each do |set| - if set.include?(name) - return set.lookup(name) - end + value = set.lookup(name) + return value if !value.nil? end nil end @@ -1248,7 +1247,10 @@ Generated on #{Time.now}. end def lookup(name) - @section.setting(name).value + setting = @section.setting(name) + if setting + setting.value + end end end end diff --git a/spec/unit/settings_spec.rb b/spec/unit/settings_spec.rb index a6c9a10b7..6a3f7cb6b 100755 --- a/spec/unit/settings_spec.rb +++ b/spec/unit/settings_spec.rb @@ -509,6 +509,12 @@ describe Puppet::Settings do @settings[:one].should == "other" end + it "setting a value to nil causes it to return to its default" do + @settings[:one] = "value will disappear" + @settings[:one] = nil + @settings[:one].should == "ONE" + end + it "should interpolate default values for other parameters into returned parameter values" do @settings[:one].should == "ONE" @settings[:two].should == "ONE TWO" From e25be73c59b2356251b661d58397125bad1f1454 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Thu, 19 Dec 2013 10:52:09 -0800 Subject: [PATCH 265/800] (PUP-665) Setting nil reverts to default The behavior of looking up settings was not entirely right from the previous commit. The true behavior is: * Search through layers in the order of [cli, memory, environment, run_mode, main, application_defaults] * Find the first layer that has ever had the setting manipulated for it. * If the value for that layer is not nil use that value. * If the value for that layer is nil use the value from puppet's defaults. This commit restores that behavior and adds a test to check that it happens. --- lib/puppet/settings.rb | 31 +++++++++++-------------------- spec/unit/settings_spec.rb | 8 ++++++++ 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/lib/puppet/settings.rb b/lib/puppet/settings.rb index 8d1873bba..7e57d2c21 100644 --- a/lib/puppet/settings.rb +++ b/lib/puppet/settings.rb @@ -949,8 +949,7 @@ Generated on #{Time.now}. ChainedValues.new( section, environment, - value_sets_for(environment, section) + - [ValuesFromDefaults.new(@config)], + value_sets_for(environment, section), @config) end @@ -1135,11 +1134,17 @@ Generated on #{Time.now}. # @return [Object] The configuration setting value or nil if the setting is not known # @api public def lookup(name) - @value_sets.each do |set| - value = set.lookup(name) - return value if !value.nil? + set = @value_sets.find do |set| + set.include?(name) end - nil + if set + value = set.lookup(name) + if !value.nil? + return value + end + end + + @defaults[name].default end # Lookup the interpolated value. All instances of `$name` in the value will @@ -1195,20 +1200,6 @@ Generated on #{Time.now}. end end - class ValuesFromDefaults - def initialize(defaults) - @defaults = defaults - end - - def include?(name) - @defaults.include?(name) - end - - def lookup(name) - @defaults[name].default - end - end - class Values def initialize(name, defaults) @name = name diff --git a/spec/unit/settings_spec.rb b/spec/unit/settings_spec.rb index 6a3f7cb6b..42bceb779 100755 --- a/spec/unit/settings_spec.rb +++ b/spec/unit/settings_spec.rb @@ -510,8 +510,16 @@ describe Puppet::Settings do end it "setting a value to nil causes it to return to its default" do + default_values = { :one => "skipped value" } + [:logdir, :confdir, :vardir].each do |key| + default_values[key] = 'default value' + end + @settings.define_settings :main, PuppetSpec::Settings::TEST_APP_DEFAULT_DEFINITIONS + @settings.initialize_app_defaults(default_values) @settings[:one] = "value will disappear" + @settings[:one] = nil + @settings[:one].should == "ONE" end From 107271bff6535b3a5cdd5777cf15de3ae5a5c04f Mon Sep 17 00:00:00 2001 From: "Ethan J. Brown" Date: Thu, 19 Dec 2013 11:29:27 -0800 Subject: [PATCH 266/800] (maint) Windows file provider :links => :follow - Previously, the Windows file provider would raise an error when applying a catalog that contained file resources with `:links => :follow`, when the given path was not actually a symlink - The behavior of readlink is essentially correct, as File.readlink on other operating systems yields Errno::EINVAL: Invalid argument - Therefore the appropriate behavior to match Puppet on other platforms is to behave like File.chown, which will operate on the link IFF it is one, or will operate on the file / dir directly if it is not a link - Corrected a minor issue in the creation of the error msg raised by the Windows implementation of readlink --- lib/puppet/provider/file/windows.rb | 17 +++++++----- lib/puppet/util/windows/file.rb | 5 ++-- spec/integration/type/file_spec.rb | 40 +++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 8 deletions(-) diff --git a/lib/puppet/provider/file/windows.rb b/lib/puppet/provider/file/windows.rb index 350948c47..e67791748 100644 --- a/lib/puppet/provider/file/windows.rb +++ b/lib/puppet/provider/file/windows.rb @@ -42,9 +42,7 @@ Puppet::Type.type(:file).provide :windows do def owner=(should) begin - path = resource[:links] == :manage ? file.path.to_s : file.readlink - - set_owner(should, path) + set_owner(should, resolved_path) rescue => detail raise Puppet::Error, "Failed to set owner to '#{should}': #{detail}" end @@ -57,9 +55,7 @@ Puppet::Type.type(:file).provide :windows do def group=(should) begin - path = resource[:links] == :manage ? file.path.to_s : file.readlink - - set_group(should, path) + set_group(should, resolved_path) rescue => detail raise Puppet::Error, "Failed to set group to '#{should}': #{detail}" end @@ -96,4 +92,13 @@ Puppet::Type.type(:file).provide :windows do def file @file ||= Puppet::FileSystem::File.new(resource[:path]) end + + def resolved_path + # under POSIX, :manage means use lchown - i.e. operate on the link + return file.path.to_s if resource[:links] == :manage + + # otherwise, use chown -- that will resolve the link IFF it is a link + # otherwise it will operate on the path + file.symlink? ? file.readlink : file.path.to_s + end end diff --git a/lib/puppet/util/windows/file.rb b/lib/puppet/util/windows/file.rb index 141873071..d43edd4d3 100644 --- a/lib/puppet/util/windows/file.rb +++ b/lib/puppet/util/windows/file.rb @@ -148,8 +148,9 @@ module Puppet::Util::Windows::File return out_buffer if result raise Puppet::Util::Windows::Error.new( - "DeviceIoControl(#{handle}, #{io_control_code}, #{in_buffer}, #{in_buffer.size}, " + - "#{out_buffer}, #{out_buffer.size}") + "DeviceIoControl(#{handle}, #{io_control_code}, " + + "#{in_buffer}, #{in_buffer ? in_buffer.size : ''}, " + + "#{out_buffer}, #{out_buffer ? out_buffer.size : ''}") end FILE_ATTRIBUTE_REPARSE_POINT = 0x400 diff --git a/spec/integration/type/file_spec.rb b/spec/integration/type/file_spec.rb index 7cd39aace..d1efc38b8 100755 --- a/spec/integration/type/file_spec.rb +++ b/spec/integration/type/file_spec.rb @@ -1154,6 +1154,20 @@ describe Puppet::Type.type(:file) do system_aces.size.should == 1 end end + + describe "with :links set to :follow" do + it "should not fail to apply" do + # at minimal, we need an owner and/or group + @file[:owner] = @sids[:users] + @file[:links] = :follow + + catalog.apply do |transaction| + if transaction.any_failed? + pretty_transaction_error(transaction) + end + end + end + end end describe "on directories" do @@ -1247,6 +1261,20 @@ describe Puppet::Type.type(:file) do system_aces.size.should == 1 end end + + describe "with :links set to :follow" do + it "should not fail to apply" do + # at minimal, we need an owner and/or group + @directory[:owner] = @sids[:users] + @directory[:links] = :follow + + catalog.apply do |transaction| + if transaction.any_failed? + pretty_transaction_error(transaction) + end + end + end + end end end end @@ -1313,4 +1341,16 @@ describe Puppet::Type.type(:file) do File.open(full_name, "w") { |f| f.write contents } full_name end + + def pretty_transaction_error(transaction) + report = transaction.report + status_failures = report.resource_statuses.values.select { |r| r.failed? } + status_fail_msg = status_failures. + collect(&:events). + flatten. + select { |event| event.status == 'failure' }. + collect { |event| "#{event.resource}: #{event.message}" }.join("; ") + + raise "Got #{status_failures.length} failure(s) while applying: #{status_fail_msg}" + end end From 16909de67fb1f774db30cc2e6d935fd5c1ac4ec0 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 19 Dec 2013 20:42:33 +0100 Subject: [PATCH 267/800] (PUP-546) Fix problems with lookup after removal of hiera2 Moved lookup functionality to separate helper class. --- lib/puppet/parser/functions/lookup.rb | 210 +--------------------- lib/puppet/pops.rb | 2 +- lib/puppet/pops/binder/lookup.rb | 191 ++++++++++++++++++++ spec/unit/parser/functions/lookup_spec.rb | 5 + 4 files changed, 204 insertions(+), 204 deletions(-) create mode 100644 lib/puppet/pops/binder/lookup.rb diff --git a/lib/puppet/parser/functions/lookup.rb b/lib/puppet/parser/functions/lookup.rb index 3b71e4bec..463abdf65 100644 --- a/lib/puppet/parser/functions/lookup.rb +++ b/lib/puppet/parser/functions/lookup.rb @@ -33,11 +33,9 @@ specified, the default value is given to the block. The content of the options hash is: -* `name` - The name to lookup. (Mutually exclusive with `first_found`) +* `name` - The name or array of names to lookup (first found is returned) * `type` - The type to assert (a Type or a type specification in string form) * `default` - The default value if there was no value found (must comply with the data type) -* `first_found` - An array of names to search, the value of the first found is used. (Mutually - exclusive with `name`). * `accept_undef` - (default `false`) An `undef` result is accepted if this options is set to `true`. * `override` - a hash with map from names to values that are used instead of the underlying bindings. If the name is found here it wins. Defaults to an empty hash. @@ -107,7 +105,7 @@ has the given data type (`String` in the example above): Using a lambda to process the looked up result - asserting that it starts with an upper case letter: # only with parser future - lookup('the_size', Integer[1,100) |$result| { + lookup('the_size', Integer[1,100]) |$result| { if $large_value_allowed and $result > 10 { error 'Values larger than 10 are not allowed'} $result @@ -116,21 +114,21 @@ Using a lambda to process the looked up result - asserting that it starts with a Including the name in the error # only with parser future - lookup('the_size', Integer[1,100) |$name, $result| { + lookup('the_size', Integer[1,100]) |$name, $result| { if $large_value_allowed and $result > 10 { error 'The bound value for '${name}' can not be larger than 10 in this configuration'} $result } -When using a block, the value it produces is also asserted against the given type, and it may not be +When using a block, the value it produces is also asserted against the given type, and it may not be `undef` unless the option `'accept_undef'` is `true`. All options work as the corresponding (direct) argument. The `first_found` option and `accept_undef` are however only available as options. -Using the `first_found` option to return the first name that has a bound value: +Using first_found semantics option to return the first name that has a bound value: - lookup({ first_found => ['apache::port', 'nginx::port'], type => 'Integer', default => 80}) + lookup(['apache::port', 'nginx::port'], 'Integer', 80) If you want to make lookup return undef when no value was found instead of raising an error: @@ -142,199 +140,5 @@ ENDHEREDOC unless Puppet[:binder] || Puppet[:parser] == 'future' raise Puppet::ParseError, "The lookup function is only available with settings --binder true, or --parser future" end - - def parse_lookup_args(args) - options = {} - pblock = if args[-1].respond_to?(:puppet_lambda) - args.pop - end - - case args.size - when 1 - # name, or all options - if args[ 0 ].is_a?(Hash) - options = to_symbolic_hash(args[ 0 ]) - else - options[ :name ] = args[ 0 ] - end - - when 2 - # name and type, or name and options - if args[ 1 ].is_a?(Hash) - options = to_symbolic_hash(args[ 1 ]) - options[:name] = args[ 0 ] # silently overwrite option with given name - else - options[:name] = args[ 0 ] - options[:type] = args[ 1 ] - end - - when 3 - # name, type, default (no options) - options[ :name ] = args[ 0 ] - options[ :type ] = args[ 1 ] - options[ :default ] = args[ 2 ] - else - raise Puppet::PareError, "The lookup function accepts 1-3 arguments, got #{args.size}" - end - options[:pblock] = pblock - options - end - - def to_symbolic_hash(input) - names = [:name, :type, :first_found, :default, :accept_undef, :extra, :override] - options = {} - names.each {|n| options[n] = undef_as_nil(input[n.to_s] || input[n]) } - options - end - - def type_mismatch(type_calculator, expected, got) - "has wrong type, expected #{type_calculator.string(expected)}, got #{type_calculator.string(got)}" - end - - def fail(msg) - raise Puppet::ParseError, "Function lookup() " + msg - end - - def fail_lookup(names) - name_part = if names.size == 1 - "the name '#{names[0]}'" - else - "any of the names ['" + names.join(', ') + "']" - end - fail("did not find a value for #{name_part}") - end - - def validate_options(options, type_calculator) - type_parser = Puppet::Pops::Types::TypeParser.new - first_found_type = type_parser.parse('Array[String]') - - if options[:name].nil? && options[:first_found].nil? - fail ("requires a name, or sequence of names in first_found. Neither was given.") - end - - if options[:name] && options[:first_found] - fail("requires either a single name, or a sequence of names in first_found. Both were specified.") - end - - if options[:name] && ! options[:name].is_a?(String) - t = type_calculator.infer(options[:name]) - fail("name, expected String, got #{type_calculator.string(t)}") - end - - if options[:first_found] - t = type_calculator.infer(options[:first_found]) - if !type_calculator.assignable?(first_found_type, t) - fail("first_found #{type_mismatch(type_calculator, first_found_type, t)}") - end - end - - # unless a type is already given (future case), parse the type (or default 'Data'), fails if invalid type is given - unless options[:type].is_a?(Puppet::Pops::Types::PAbstractType) - options[:type] = type_parser.parse(options[:type] || 'Data') - end - - # default value must comply with the given type - if options[:default] - t = type_calculator.infer(options[:default]) - if ! type_calculator.assignable?(options[:type], t) - fail("'default' value #{type_mismatch(type_calculator, options[:type], t)}") - end - end - - if options[:extra] && !options[:extra].is_a?(Hash) - # do not perform inference here, it is enough to know that it is not a hash - fail("'extra' value must be a Hash, got #{options[:extra].class}") - end - options[:extra] = {} unless options[:extra] - - if options[:override] && !options[:override].is_a?(Hash) - # do not perform inference here, it is enough to know that it is not a hash - fail("'override' value must be a Hash, got #{options[:extra].class}") - end - options[:override] = {} unless options[:override] - - end - - def nil_as_undef(x) - x.nil? ? :undef : x - end - - def undef_as_nil(x) - is_nil_or_undef?(x) ? nil : x - end - - def is_nil_or_undef?(x) - x.nil? || x == :undef - end - - # This is used as a marker - a value that cannot (at least not easily) by mistake be found in - # hiera data. - # - class PrivateNotFoundMarker; end - - def search_for(type, name, options) - # search in order, override, injector, hiera, then extra - if !(result = options[:override][name]).nil? - result - elsif !(result = compiler.injector.lookup(self, type, name)).nil? - result - else - result = self.function_hiera([name, PrivateNotFoundMarker]) - if !result.nil? && result != PrivateNotFoundMarker - result - else - options[:extra][name] - end - end - end - - # THE FUNCTION STARTS HERE - - type_calculator = Puppet::Pops::Types::TypeCalculator.new - options = parse_lookup_args(args) - validate_options(options, type_calculator) - names = options[:name] ? [options[:name]] : options[:first_found] - type = options[:type] - - result_with_name = names.reduce([]) do |memo, name| - break memo if !memo[1].nil? - [name, search_for(type, name, options)] - end - - result = if result_with_name[1].nil? - # not found, use default (which may be nil), the default is already type checked - options[:default] - else - # injector.lookup is type-safe already do no need to type check the result - result_with_name[1] - end - - result = if pblock = options[:pblock] - result2 = case pblock.parameter_count - when 1 - pblock.call(self, nil_as_undef(result)) - when 2 - pblock.call(self, result_with_name[ 0 ], nil_as_undef(result)) - else - pblock.call(self, result_with_name[ 0 ], nil_as_undef(result), nil_as_undef(options[ :default ])) - end - - # if the given result was returned, there is not need to type-check it again - if !result2.equal?(result) - t = type_calculator.infer(undef_as_nil(result2)) - if !type_calculator.assignable?(type, t) - fail "the value produced by the given code block #{type_mismatch(type_calculator, type, t)}" - end - end - result2 - else - result - end - - # Finally, the result if nil must be acceptable or an error is raised - if is_nil_or_undef?(result) && !options[:accept_undef] - fail_lookup(names) - else - nil_as_undef(result) - end + Puppet::Pops::Binder::Lookup.lookup(self, args) end diff --git a/lib/puppet/pops.rb b/lib/puppet/pops.rb index bfd14aa0f..54387d6a2 100644 --- a/lib/puppet/pops.rb +++ b/lib/puppet/pops.rb @@ -54,11 +54,11 @@ module Puppet require 'puppet/pops/binder/injector_entry' require 'puppet/pops/binder/key_factory' require 'puppet/pops/binder/injector' -# require 'puppet/pops/binder/hiera2' require 'puppet/pops/binder/bindings_composer' require 'puppet/pops/binder/bindings_model_dumper' require 'puppet/pops/binder/system_bindings' require 'puppet/pops/binder/bindings_loader' + require 'puppet/pops/binder/lookup' module Config require 'puppet/pops/binder/config/binder_config' diff --git a/lib/puppet/pops/binder/lookup.rb b/lib/puppet/pops/binder/lookup.rb new file mode 100644 index 000000000..0718095c7 --- /dev/null +++ b/lib/puppet/pops/binder/lookup.rb @@ -0,0 +1,191 @@ +# This class is the backing implementation of the Puppet function 'lookup'. +# See puppet/parser/functions/lookup.rb for documentation. +# +class Puppet::Pops::Binder::Lookup + + def self.parse_lookup_args(args) + options = {} + pblock = if args[-1].respond_to?(:puppet_lambda) + args.pop + end + + case args.size + when 1 + # name, or all options + if args[ 0 ].is_a?(Hash) + options = to_symbolic_hash(args[ 0 ]) + else + options[ :name ] = args[ 0 ] + end + + when 2 + # name and type, or name and options + if args[ 1 ].is_a?(Hash) + options = to_symbolic_hash(args[ 1 ]) + options[:name] = args[ 0 ] # silently overwrite option with given name + else + options[:name] = args[ 0 ] + options[:type] = args[ 1 ] + end + + when 3 + # name, type, default (no options) + options[ :name ] = args[ 0 ] + options[ :type ] = args[ 1 ] + options[ :default ] = args[ 2 ] + else + raise Puppet::PareError, "The lookup function accepts 1-3 arguments, got #{args.size}" + end + options[:pblock] = pblock + options + end + + def self.to_symbolic_hash(input) + names = [:name, :type, :default, :accept_undef, :extra, :override] + options = {} + names.each {|n| options[n] = undef_as_nil(input[n.to_s] || input[n]) } + options + end + + def self.type_mismatch(type_calculator, expected, got) + "has wrong type, expected #{type_calculator.string(expected)}, got #{type_calculator.string(got)}" + end + + def self.fail(msg) + raise Puppet::ParseError, "Function lookup() " + msg + end + + def self.fail_lookup(names) + name_part = if names.size == 1 + "the name '#{names[0]}'" + else + "any of the names ['" + names.join(', ') + "']" + end + fail("did not find a value for #{name_part}") + end + + def self.validate_options(options, type_calculator) + type_parser = Puppet::Pops::Types::TypeParser.new + name_type = type_parser.parse('Variant[Array[String], String]') + + if is_nil_or_undef?(options[:name]) || options[:name].is_a?(Array) && options[:name].empty? + fail ("requires a name, or array of names. Got nothing to lookup.") + end + + t = type_calculator.infer(options[:name]) + if ! type_calculator.assignable?(name_type, t) + fail("given 'name' argument, #{type_mismatch(type_calculator, options[:name], t)}") + end + + # unless a type is already given (future case), parse the type (or default 'Data'), fails if invalid type is given + unless options[:type].is_a?(Puppet::Pops::Types::PAbstractType) + options[:type] = type_parser.parse(options[:type] || 'Data') + end + + # default value must comply with the given type + if options[:default] + t = type_calculator.infer(options[:default]) + if ! type_calculator.assignable?(options[:type], t) + fail("'default' value #{type_mismatch(type_calculator, options[:type], t)}") + end + end + + if options[:extra] && !options[:extra].is_a?(Hash) + # do not perform inference here, it is enough to know that it is not a hash + fail("'extra' value must be a Hash, got #{options[:extra].class}") + end + options[:extra] = {} unless options[:extra] + + if options[:override] && !options[:override].is_a?(Hash) + # do not perform inference here, it is enough to know that it is not a hash + fail("'override' value must be a Hash, got #{options[:extra].class}") + end + options[:override] = {} unless options[:override] + + end + + def self.nil_as_undef(x) + x.nil? ? :undef : x + end + + def self.undef_as_nil(x) + is_nil_or_undef?(x) ? nil : x + end + + def self.is_nil_or_undef?(x) + x.nil? || x == :undef + end + + # This is used as a marker - a value that cannot (at least not easily) by mistake be found in + # hiera data. + # + class PrivateNotFoundMarker; end + + def self.search_for(scope, type, name, options) + # search in order, override, injector, hiera, then extra + if !(result = options[:override][name]).nil? + result + elsif !(result = scope.compiler.injector.lookup(scope, type, name)).nil? + result + else + result = scope.function_hiera([name, PrivateNotFoundMarker]) + if !result.nil? && result != PrivateNotFoundMarker + result + else + options[:extra][name] + end + end + end + + # This is the method called from the puppet/parser/functions/lookup.rb + # @param args [Array] array following the puppet function call conventions + def self.lookup(scope, args) + type_calculator = Puppet::Pops::Types::TypeCalculator.new + options = parse_lookup_args(args) + validate_options(options, type_calculator) + names = [options[:name]].flatten + type = options[:type] + + result_with_name = names.reduce([]) do |memo, name| + break memo if !memo[1].nil? + [name, search_for(scope, type, name, options)] + end + + result = if result_with_name[1].nil? + # not found, use default (which may be nil), the default is already type checked + options[:default] + else + # injector.lookup is type-safe already do no need to type check the result + result_with_name[1] + end + + result = if pblock = options[:pblock] + result2 = case pblock.parameter_count + when 1 + pblock.call(scope, nil_as_undef(result)) + when 2 + pblock.call(scope, result_with_name[ 0 ], nil_as_undef(result)) + else + pblock.call(scope, result_with_name[ 0 ], nil_as_undef(result), nil_as_undef(options[ :default ])) + end + + # if the given result was returned, there is not need to type-check it again + if !result2.equal?(result) + t = type_calculator.infer(undef_as_nil(result2)) + if !type_calculator.assignable?(type, t) + fail "the value produced by the given code block #{type_mismatch(type_calculator, type, t)}" + end + end + result2 + else + result + end + + # Finally, the result if nil must be acceptable or an error is raised + if is_nil_or_undef?(result) && !options[:accept_undef] + fail_lookup(names) + else + nil_as_undef(result) + end + end +end diff --git a/spec/unit/parser/functions/lookup_spec.rb b/spec/unit/parser/functions/lookup_spec.rb index 11c6ec644..18afcae69 100644 --- a/spec/unit/parser/functions/lookup_spec.rb +++ b/spec/unit/parser/functions/lookup_spec.rb @@ -23,6 +23,11 @@ describe "lookup function" do expect(scope.function_lookup(['a_value'])).to eq('something') end + it "searches for first found if given several names" do + scope = scope_with_injections_from(bound(bind_single("a_value", "something"))) + expect(scope.function_lookup([['b_value', 'a_value', 'c_value']])).to eq('something') + end + it "override wins over bound" do scope = scope_with_injections_from(bound(bind_single("a_value", "something"))) options = {:override => { 'a_value' => 'something_overridden' }} From 330ce178123ae730f65114336672e04a57b655a2 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Thu, 19 Dec 2013 13:52:43 -0800 Subject: [PATCH 268/800] (PUP-665) Don't call hooks unless absolutely necessary Calling hooks on configuration settings that don't have them forces the code to interpolate any `$variable`s in the value of the setting. There are a couple places where the hooks are called before everything has been fully prepared (such as in initialize_global_defaults) and so run mode settings such as confdir are not available. This restores the old behavior in which only settings with hooks will end up going down this path. This doesn't fully address the problem, but it does make the surface area much smaller. --- lib/puppet/settings.rb | 20 +++++++++++++------- lib/puppet/settings/base_setting.rb | 5 ++++- spec/unit/settings_spec.rb | 21 +++++++++++++++++++++ 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/lib/puppet/settings.rb b/lib/puppet/settings.rb index 7e57d2c21..822867f0b 100644 --- a/lib/puppet/settings.rb +++ b/lib/puppet/settings.rb @@ -517,7 +517,7 @@ class Puppet::Settings end # Call any hooks we should be calling. - @config.values.each do |setting| + @config.values.select(&:has_hook?).each do |setting| value_sets_for(env, self.preferred_run_mode).each do |source| if source.include?(setting.name) # We still have to use value to retrieve the value, since @@ -824,14 +824,18 @@ class Puppet::Settings # Collect the settings that need to have their hooks called immediately. # We have to collect them so that we can be sure we're fully initialized before # the hook is called. - if tryconfig.call_hook_on_define? - call << tryconfig - elsif tryconfig.call_hook_on_initialize? - @hooks_to_call_on_application_initialization << tryconfig + if tryconfig.has_hook? + if tryconfig.call_hook_on_define? + call << tryconfig + elsif tryconfig.call_hook_on_initialize? + @hooks_to_call_on_application_initialization << tryconfig + end end end - call.each { |setting| setting.handle(self.value(setting.name)) } + call.each do |setting| + setting.handle(self.value(setting.name)) + end end # Convert the settings we manage into a catalog full of resources that model those settings. @@ -1217,7 +1221,9 @@ Generated on #{Time.now}. "Attempt to assign a value to unknown configuration parameter #{name.inspect}" end - @defaults[name].handle(value) + if @defaults[name].has_hook? + @defaults[name].handle(value) + end @values[name] = value end diff --git a/lib/puppet/settings/base_setting.rb b/lib/puppet/settings/base_setting.rb index f0ccd49c1..e6c12de42 100644 --- a/lib/puppet/settings/base_setting.rb +++ b/lib/puppet/settings/base_setting.rb @@ -73,10 +73,12 @@ class Puppet::Settings::BaseSetting end def hook=(block) + @has_hook = true meta_def :handle, &block end - def handle(value) + def has_hook? + @has_hook end # Create the new element. Pretty much just sets the name. @@ -91,6 +93,7 @@ class Puppet::Settings::BaseSetting #set the default value for call_hook @call_hook = :on_write_only if args[:hook] and not args[:call_hook] + @has_hook = false raise ArgumentError, "Cannot reference :call_hook for :#{@name} if no :hook is defined" if args[:call_hook] and not args[:hook] diff --git a/spec/unit/settings_spec.rb b/spec/unit/settings_spec.rb index 42bceb779..6223e9ff6 100755 --- a/spec/unit/settings_spec.rb +++ b/spec/unit/settings_spec.rb @@ -939,6 +939,27 @@ describe Puppet::Settings do @settings[:deferred].should eq File.expand_path('/path/to/confdir/goose') end + it "does not require the value for a setting without a hook to resolve during global setup" do + hook_invoked = false + @settings.define_settings :section, :can_cause_problems => {:desc => '' } + + @settings.define_settings(:main, + :logdir => { :type => :directory, :default => nil, :desc => "logdir" }, + :confdir => { :type => :directory, :default => nil, :desc => "confdir" }, + :vardir => { :type => :directory, :default => nil, :desc => "vardir" }) + + text = <<-EOD + [main] + can_cause_problems=$confdir/goose + EOD + + @settings.stubs(:read_file).returns(text) + @settings.initialize_global_settings + @settings.initialize_app_defaults(:logdir => '/path/to/logdir', :confdir => '/path/to/confdir', :vardir => '/path/to/vardir') + + @settings[:can_cause_problems].should eq File.expand_path('/path/to/confdir/goose') + end + it "should allow empty values" do @settings.define_settings :section, :myarg => { :default => "myfile", :desc => "a" } From ba09bfe99d9ac8679b74259e55c7b31947d05d1b Mon Sep 17 00:00:00 2001 From: "Ethan J. Brown" Date: Thu, 19 Dec 2013 10:03:08 -0800 Subject: [PATCH 269/800] (PUP-1133) Windows tests write config to temp - Previously tests were writing to the shared %ALLUSERSPROFILE%\PuppetLabs\puppet directory, and would blow up if this directory did not exist - Because this directory was shared from one run to the next, failing tests could be masked by data generated / used by a locally installed Puppet - The stubbing of FileSystem::File.exist? to return true was causing some tests to treat files like c:\dev\null\foo to attempt to be loaded, which would cause these tests to fail in isolation, but not when run within the suite - Had to change some doc specs under Ruby 1.9 / OSX to verify a value instead of asserting a method was called, to prevent a stack overflow - In some unit tests, the process of reading / writing settings caused issues with test loading, so Puppet.settings.use has been stubbed --- spec/integration/configurer_spec.rb | 3 ++- spec/lib/puppet_spec/files.rb | 1 + spec/spec_helper.rb | 8 +++++++- spec/unit/application/agent_spec.rb | 7 ++++--- spec/unit/application/device_spec.rb | 1 - spec/unit/application/doc_spec.rb | 8 ++++---- spec/unit/reports/rrdgraph_spec.rb | 1 - spec/unit/util/rdoc/parser_spec.rb | 4 +++- 8 files changed, 21 insertions(+), 12 deletions(-) diff --git a/spec/integration/configurer_spec.rb b/spec/integration/configurer_spec.rb index ddb044a0b..191a79b06 100755 --- a/spec/integration/configurer_spec.rb +++ b/spec/integration/configurer_spec.rb @@ -8,6 +8,7 @@ describe Puppet::Configurer do describe "when downloading plugins" do it "should use the :pluginsignore setting, split on whitespace, for ignoring remote files" do + Puppet.settings.stubs(:use) resource = Puppet::Type.type(:notify).new :name => "yay" Puppet::Type.type(:file).expects(:new).at_most(2).with do |args| args[:ignore] == Puppet[:pluginsignore].split(/\s+/) @@ -24,7 +25,7 @@ describe Puppet::Configurer do @catalog = Puppet::Resource::Catalog.new @catalog.add_resource(Puppet::Type.type(:notify).new(:title => "testing")) - # Make sure we don't try to persist the local state after the transaction ran, + # Make sure we don't try to persist the local state after the transaction ran, # because it will fail during test (the state file is in a not-existing directory) # and we need the transaction to be successful to be able to produce a summary report @catalog.host_config = false diff --git a/spec/lib/puppet_spec/files.rb b/spec/lib/puppet_spec/files.rb index afe86fba2..65b04aab9 100755 --- a/spec/lib/puppet_spec/files.rb +++ b/spec/lib/puppet_spec/files.rb @@ -9,6 +9,7 @@ module PuppetSpec::Files $global_tempfiles ||= [] while path = $global_tempfiles.pop do begin + Dir.unstub(:entries) FileUtils.rm_rf path, :secure => true rescue Errno::ENOENT # nothing to do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 188ec9037..a79feb54c 100755 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -137,8 +137,14 @@ RSpec.configure do |config| @log_level = Puppet::Util::Log.level - Puppet::Test::TestHelper.before_each_test() + base = PuppetSpec::Files.tmpdir('tmp_settings') + Puppet[:vardir] = File.join(base, 'var') + Puppet[:confdir] = File.join(base, 'etc') + Puppet[:logdir] = "$vardir/log" + Puppet[:rundir] = "$vardir/run" + Puppet[:hiera_config] = File.join(base, 'hiera') + Puppet::Test::TestHelper.before_each_test() end config.after :each do diff --git a/spec/unit/application/agent_spec.rb b/spec/unit/application/agent_spec.rb index 4280a953c..0ce175b79 100755 --- a/spec/unit/application/agent_spec.rb +++ b/spec/unit/application/agent_spec.rb @@ -33,6 +33,8 @@ describe Puppet::Application::Agent do Puppet::Node::Facts.indirection.stubs(:terminus_class=) $stderr.expects(:puts).never + + Puppet.settings.stubs(:use) end it "should operate in agent run_mode" do @@ -198,7 +200,6 @@ describe Puppet::Application::Agent do describe "during setup" do before :each do Puppet.stubs(:info) - Puppet::FileSystem::File.stubs(:exist?).returns(true) Puppet[:libdir] = "/dev/null/lib" Puppet::Transaction::Report.indirection.stubs(:terminus_class=) Puppet::Transaction::Report.indirection.stubs(:cache_class=) @@ -291,6 +292,7 @@ describe Puppet::Application::Agent do end it "should use :main, :puppetd, and :ssl" do + Puppet.settings.unstub(:use) Puppet.settings.expects(:use).with(:main, :agent, :ssl) @puppetd.setup @@ -449,8 +451,7 @@ describe Puppet::Application::Agent do describe "when setting up listen" do before :each do - Puppet::FileSystem::File.stubs(:exist?).with('auth').returns(true) - Puppet::FileSystem::File.stubs(:exist?).returns(true) + Puppet::FileSystem::File.stubs(:exist?).with(Puppet[:rest_authconfig]).returns(true) @puppetd.options[:serve] = [] @server = stub_everything 'server' Puppet::Network::Server.stubs(:new).returns(@server) diff --git a/spec/unit/application/device_spec.rb b/spec/unit/application/device_spec.rb index bbbc011c1..7d3e05812 100755 --- a/spec/unit/application/device_spec.rb +++ b/spec/unit/application/device_spec.rb @@ -127,7 +127,6 @@ describe Puppet::Application::Device do before :each do @device.options.stubs(:[]) Puppet.stubs(:info) - Puppet::FileSystem::File.stubs(:exist?).returns(true) Puppet[:libdir] = "/dev/null/lib" Puppet::SSL::Host.stubs(:ca_location=) Puppet::Transaction::Report.indirection.stubs(:terminus_class=) diff --git a/spec/unit/application/doc_spec.rb b/spec/unit/application/doc_spec.rb index e2dc0fe69..d8e924c43 100755 --- a/spec/unit/application/doc_spec.rb +++ b/spec/unit/application/doc_spec.rb @@ -223,18 +223,18 @@ describe Puppet::Application::Doc do @doc.unknown_args = [ { :opt => "--modulepath", :arg => "path" } ] Puppet.settings.stubs(:handlearg) - File.expects(:expand_path).with("path") - @doc.setup_rdoc + + @doc.unknown_args[0][:arg].should == File.expand_path('path') end it "should expand --manifestdir if any" do @doc.unknown_args = [ { :opt => "--manifestdir", :arg => "path" } ] Puppet.settings.stubs(:handlearg) - File.expects(:expand_path).with("path") - @doc.setup_rdoc + + @doc.unknown_args[0][:arg].should == File.expand_path('path') end it "should give them to Puppet.settings" do diff --git a/spec/unit/reports/rrdgraph_spec.rb b/spec/unit/reports/rrdgraph_spec.rb index 0085667ac..ea5258ea6 100755 --- a/spec/unit/reports/rrdgraph_spec.rb +++ b/spec/unit/reports/rrdgraph_spec.rb @@ -9,7 +9,6 @@ describe processor do include PuppetSpec::Files before do Puppet[:rrddir] = tmpdir('rrdgraph') - Puppet.settings.use :master end after do diff --git a/spec/unit/util/rdoc/parser_spec.rb b/spec/unit/util/rdoc/parser_spec.rb index 1699020a7..eb2ecd248 100755 --- a/spec/unit/util/rdoc/parser_spec.rb +++ b/spec/unit/util/rdoc/parser_spec.rb @@ -27,7 +27,9 @@ describe "RDoc::Parser", :if => Puppet.features.rdoc1? do Puppet::Parser::Parser.stubs(:new).returns(parser) parser.expects(:parse).returns(Puppet::Parser::AST::Hostclass.new('')).at_least_once parser.expects(:file=).with("module/manifests/init.pp") - parser.expects(:file=).with(File.expand_path("/dev/null/manifests/site.pp")) + parser.expects(:file=).with do |args| + args =~ /.*\/etc\/manifests\/site.pp/ + end @parser.scan end From 776767cf68e41c9fe0e74b5c242cfbe2241b7bf2 Mon Sep 17 00:00:00 2001 From: "Ethan J. Brown" Date: Fri, 20 Dec 2013 08:31:12 -0800 Subject: [PATCH 270/800] (PUP-1133) Reset permission on Windows test files - Previously some security tests manipulated file permissions in a way that made it impossible for the top-level spec temp file cleanup to be performed. This patch resets the files after runs. --- spec/integration/type/file_spec.rb | 13 +++++++++++++ spec/integration/util/windows/security_spec.rb | 18 ++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/spec/integration/type/file_spec.rb b/spec/integration/type/file_spec.rb index d1efc38b8..00e1ba685 100755 --- a/spec/integration/type/file_spec.rb +++ b/spec/integration/type/file_spec.rb @@ -1179,6 +1179,19 @@ describe Puppet::Type.type(:file) do catalog.add_resource @directory end + def grant_everyone_full_access(path) + sd = Puppet::Util::Windows::Security.get_security_descriptor(path) + sd.dacl.allow( + 'S-1-1-0', #everyone + Windows::File::FILE_ALL_ACCESS, + Windows::File::OBJECT_INHERIT_ACE | Windows::File::CONTAINER_INHERIT_ACE) + Puppet::Util::Windows::Security.set_security_descriptor(path, sd) + end + + after :each do + grant_everyone_full_access(dir) + end + describe "when source permissions are ignored" do before :each do @directory[:source_permissions] = :ignore diff --git a/spec/integration/util/windows/security_spec.rb b/spec/integration/util/windows/security_spec.rb index 99d866f7f..f839ec230 100755 --- a/spec/integration/util/windows/security_spec.rb +++ b/spec/integration/util/windows/security_spec.rb @@ -43,6 +43,14 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win end end + def grant_everyone_full_access(path) + sd = winsec.get_security_descriptor(path) + everyone = 'S-1-1-0' + inherit = WindowsSecurityTester::OBJECT_INHERIT_ACE | WindowsSecurityTester::CONTAINER_INHERIT_ACE + sd.dacl.allow(everyone, Windows::File::FILE_ALL_ACCESS, inherit) + winsec.set_security_descriptor(path, sd) + end + shared_examples_for "only child owner" do it "should allow child owner" do winsec.set_owner(sids[:guest], parent) @@ -616,6 +624,11 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win path end + after :each do + # allow temp files to be cleaned up + grant_everyone_full_access(parent) + end + it_behaves_like "a securable object" do def check_access(mode, path) if (mode & WindowsSecurityTester::S_IRUSR).nonzero? @@ -682,6 +695,11 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win path end + after :each do + # allow temp files to be cleaned up + grant_everyone_full_access(parent) + end + it_behaves_like "a securable object" do def check_access(mode, path) if (mode & WindowsSecurityTester::S_IRUSR).nonzero? From d758620b99f21b088b2f3e3e57e375ffe9f3ae9f Mon Sep 17 00:00:00 2001 From: "Ethan J. Brown" Date: Fri, 20 Dec 2013 08:47:56 -0800 Subject: [PATCH 271/800] (PUP-1133) Clean up leftover parser generate temp - Files were created without going through tmpfile as they needed specific extensions --- spec/unit/parser/functions/generate_spec.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/unit/parser/functions/generate_spec.rb b/spec/unit/parser/functions/generate_spec.rb index 593703d63..f4b85b8f2 100755 --- a/spec/unit/parser/functions/generate_spec.rb +++ b/spec/unit/parser/functions/generate_spec.rb @@ -106,6 +106,10 @@ describe "the generate function" do cmd end + after :each do + File.delete(command) if Puppet::FileSystem::File.exist?(command) + end + it "should call generator with no arguments" do scope.function_generate([command]).should == "a- b-\n" end From 3e004665d4fe0a3f7e53bea2790b2d4cebadac54 Mon Sep 17 00:00:00 2001 From: "Ethan J. Brown" Date: Fri, 20 Dec 2013 10:59:33 -0800 Subject: [PATCH 272/800] (PUP-1133) Cleanup Windows symlink - Windows symlinks, when targeted at directories, do not get cleaned up if not unlinked, even when going through the global tmpfile --- spec/unit/file_system/file_spec.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spec/unit/file_system/file_spec.rb b/spec/unit/file_system/file_spec.rb index 564d4096c..fa0786b44 100644 --- a/spec/unit/file_system/file_spec.rb +++ b/spec/unit/file_system/file_spec.rb @@ -353,6 +353,9 @@ describe Puppet::FileSystem::File do stat = symlink.stat stat.should be_instance_of(File::Stat) stat.ftype.should == 'directory' + + # on Windows, this won't get cleaned up if still linked + symlink.unlink end it "should return a File::Stat of ftype 'file' when calling stat on a symlink pointing to another symlink" do From 3215d4501f4702a3520dc05f25f883ccd5b62a91 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 20 Dec 2013 22:41:47 +0100 Subject: [PATCH 273/800] (maint) Provide option to 4x->3x convert to handle :undef differently Functions requires :undef conversion to '', while other parts require :undef (verbatim). This changes the signature of the convert method to accept the value to use when :undef is encountered. --- lib/puppet/pops/evaluator/runtime3_support.rb | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/lib/puppet/pops/evaluator/runtime3_support.rb b/lib/puppet/pops/evaluator/runtime3_support.rb index 5ca43d42e..835c978d9 100644 --- a/lib/puppet/pops/evaluator/runtime3_support.rb +++ b/lib/puppet/pops/evaluator/runtime3_support.rb @@ -213,7 +213,8 @@ module Puppet::Pops::Evaluator::Runtime3Support def call_function(name, args, o, scope) # Arguments must be mapped since functions are unaware of the new and magical creatures in 4x. - mapped_args = args.map {|a| convert(a, scope) } + # NOTE: Passing an empty string last converts :undef to empty string + mapped_args = args.map {|a| convert(a, scope, '') } scope.send("function_#{name}", mapped_args) end @@ -227,7 +228,7 @@ module Puppet::Pops::Evaluator::Runtime3Support file, line = extract_file_line(o) Puppet::Parser::Resource::Param.new( :name => name, - :value => convert(value, scope), # converted to 3x since 4x supports additional objects / types + :value => convert(value, scope, :undef), # converted to 3x since 4x supports additional objects / types :source => scope.source, :line => line, :file => file, :add => operator == :'+>' ) @@ -367,7 +368,7 @@ module Puppet::Pops::Evaluator::Runtime3Support end def initialize - @@convert_visitor ||= Puppet::Pops::Visitor.new(self, "convert", 1, 1) + @@convert_visitor ||= Puppet::Pops::Visitor.new(self, "convert", 2, 2) end # Converts 4x supported values to 3x values. This is required because @@ -375,55 +376,56 @@ module Puppet::Pops::Evaluator::Runtime3Support # regular expressions. Unfortunately this has to be done for array and hash as well. # A complication is that catalog types needs to be resolved against the scope. # - def convert(o, scope) - @@convert_visitor.visit_this_1(self, o, scope) + def convert(o, scope, undef_value) + @@convert_visitor.visit_this_2(self, o, scope, undef_value) end - def convert_NilClass(o, scope) - :undef + + def convert_NilClass(o, scope, undef_value) + undef_value end - def convert_Object(o, scope) + def convert_Object(o, scope, undef_value) o end - def convert_Array(o, scope) - o.map {|x| convert(x, scope) } + def convert_Array(o, scope, undef_value) + o.map {|x| convert(x, scope, undef_value) } end - def convert_Hash(o, scope) + def convert_Hash(o, scope, undef_value) result = {} - o.each {|k,v| result[convert(k, scope)] = convert(v, scope) } + o.each {|k,v| result[convert(k, scope, undef_value)] = convert(v, scope, undef_value) } result end - def convert_Regexp(o, scope) + def convert_Regexp(o, scope, undef_value) # Puppet 3x cannot handle parameter values that are reqular expressions. Turn into regexp string in # source form o.inspect end - def convert_Symbol(o, scope) + def convert_Symbol(o, scope, undef_value) case o when :undef - '' # 3x wants :undef as empty string in function + undef_value # 3x wants :undef as empty string in function else o # :default, and all others are verbatim since they are new in future evaluator end end - def convert_PAbstractType(o, scope) + def convert_PAbstractType(o, scope, undef_value) o end - def convert_PResourceType(o,scope) + def convert_PResourceType(o,scope, undef_value) # Needs conversion by calling scope to resolve the name and possibly return a different name # Resolution can only be called with an array, and returns an array. Here there is only one name type, titles = scope.resolve_type_and_titles(o.type_name, [o.title]) Puppet::Resource.new(type, titles[0]) end - def convert_PHostClassType(o, scope) + def convert_PHostClassType(o, scope, undef_value) # Needs conversion by calling scope to resolve the name and possibly return a different name # Resolution can only be called with an array, and returns an array. Here there is only one name type, titles = scope.resolve_type_and_titles('class', [o.class_name]) From 6b5b08bd1b3f555519704f192b1c3198b99d5ecd Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 21 Dec 2013 00:55:32 +0100 Subject: [PATCH 274/800] (PUP-798) Add call to watch_files to ensure reparsing This adds a call to watch_file in the code path for the future parser/evaluator at the same place such call is made in the 3x implementation. The ParserFactory is given an environment, and when this is a real environment, its known_resource_types are given to the parser adapter being created to allow it to make the call to watch_file. --- lib/puppet/parser/e4_parser_adapter.rb | 6 +++++- lib/puppet/parser/parser_factory.rb | 7 ++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/puppet/parser/e4_parser_adapter.rb b/lib/puppet/parser/e4_parser_adapter.rb index 8afc9dc8a..c0bfaf236 100644 --- a/lib/puppet/parser/e4_parser_adapter.rb +++ b/lib/puppet/parser/e4_parser_adapter.rb @@ -6,7 +6,9 @@ module Puppet; module Parser; end; end; # class Puppet::Parser::E4ParserAdapter - def initialize() + # @param file_watcher [#watch_file] something that can watch a file + def initialize(file_watcher = nil) + @file_watcher = file_watcher @file = '' @string = '' @use = :undefined @@ -16,6 +18,8 @@ class Puppet::Parser::E4ParserAdapter def file=(file) @file = file @use = :file + # watch if possible, but only if the file is something worth watching + @file_watcher.watch_file(file) if @file_watcher.respond_to?(:watch_file) && !file.nil? && file != '' end def parse(string = nil) diff --git a/lib/puppet/parser/parser_factory.rb b/lib/puppet/parser/parser_factory.rb index dfeca628f..38d9a205e 100644 --- a/lib/puppet/parser/parser_factory.rb +++ b/lib/puppet/parser/parser_factory.rb @@ -11,7 +11,8 @@ module Puppet::Parser def self.parser(environment) case Puppet[:parser] when 'future' - evaluating_parser() + # must check if the given environment is real or just a string, in which case watching is not supported + evaluating_parser(environment.respond_to?(:known_resource_types) ? environment.known_resource_types : nil) # eparser(environment) else classic_parser(environment) @@ -27,14 +28,14 @@ module Puppet::Parser end # Returns an instance of an EvaluatingParser - def self.evaluating_parser + def self.evaluating_parser(file_watcher) # Since RGen is optional, test that it is installed @@asserted ||= false assert_rgen_installed() unless @@asserted @@asserted = true require 'puppet/parser/e4_parser_adapter' require 'puppet/pops/parser/code_merger' - E4ParserAdapter.new() + E4ParserAdapter.new(file_watcher) end # Creates an instance of the expression based parser 'eparser' From 35ada47889be2507518201fe4e99f87db8b4442c Mon Sep 17 00:00:00 2001 From: Dan Lidral-Porter Date: Thu, 19 Dec 2013 14:29:27 -0800 Subject: [PATCH 275/800] Add vagrant testing guide to acceptance/README. Point to the acceptance/README in the README_DEVELOPER, and also update the README_DEVELOPER to refer to beaker rather than the now-deprecated puppet-acceptance. --- README_DEVELOPER.md | 17 +++--- acceptance/README.md | 125 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 129 insertions(+), 13 deletions(-) diff --git a/README_DEVELOPER.md b/README_DEVELOPER.md index aac8b0c25..6ff7fafbb 100644 --- a/README_DEVELOPER.md +++ b/README_DEVELOPER.md @@ -539,15 +539,18 @@ rspec ./test.rb:16 # fixture data using instance variables should not keep state ### Puppet-acceptance -[puppet-acceptance]: https://github.com/puppetlabs/puppet-acceptance +[beaker]: https://github.com/puppetlabs/beaker [test::unit]: http://test-unit.rubyforge.org/ -Puppet has a custom acceptance testing framework called -[puppet-acceptance][puppet-acceptance] for running acceptance tests. -Puppet-acceptance runs the tests by configuring one or more VMs, copying the -test cases onto the VMs, performing the tests and collecting the results, and -ensuring that the results match the intended behavior. It uses -[test::unit][test::unit] to perform the actual assertions. +Puppet has a custom acceptance testing framework called [beaker][beaker] for +running acceptance tests. +Beaker runs the tests by configuring one or more VMs, copying the test cases +onto the VMs, performing the tests and collecting the results, and ensuring that +the results match the intended behavior. It uses [test::unit][test::unit] to +perform the actual assertions. + +For a detailed guide to running the acceptance tests locally on vagrant boxes, +see the `acceptance/README.md` document in this repo. # UTF-8 Handling # diff --git a/acceptance/README.md b/acceptance/README.md index 92790a9aa..13fc175a4 100644 --- a/acceptance/README.md +++ b/acceptance/README.md @@ -1,12 +1,25 @@ -Local Use -========= +Running Acceptance Tests Yourself +================================= -CI ---- +Table of Contents +----------------- -There are two ways to run the ci tests locally: against packages, or against git clones. +* [General Notes](#general-notes) +* [Running Tests on the vcloud](#running-tests-on-the-vcloud) +* [Running Tests on Vagrant Boxen](#running-tests-on-vagrant-boxen) -`rake -T` will give short descriptions, and a `rake -D` will give full descriptions with information on ENV options required and optional for the various tasks. +General Notes +------------- + +The rake tasks for running the tests are defined by the Rakefile in the same directory as this file. +These tasks come with some documentation: `rake -T` will give short descriptions, and a `rake -D` will give full descriptions with information on ENV options required and optional for the various tasks. + + +Running Tests on the vcloud +--------------------------- + +In order to use the Puppet Labs vcloud, you'll need to be a Puppet Labs employee. +Community members should see the [guide to running the tests on vagrant boxen](#running-tests-on-local-vagrant-boxen). ### Authentication @@ -74,3 +87,103 @@ If you run a number of jobs with --preserve_hosts or vi ci:test_and_preserve_hos bundle exec ci:destroy_preserved_hosts to clean them up sooner and free resources. + + +Running Tests on Vagrant Boxen +------------------------------ + +This guide assumes that you have an acceptable Ruby (i.e. 1.9+) installed along with the bundler gem, that you have the puppet repo checked out locally somewhere, and that the name of the checkout folder is `puppet`. +I used Ruby 1.9.3-p484 + +Change to the `acceptance` directory in the root of the puppet repo: +```sh +cd /path/to/repo/puppet/acceptance +``` +Install the necessary gems with bundler: +```sh +bundle install +``` + +Now you can get a list of test-related tasks you can run via rake: +```sh +bundle exec rake -T +``` +and view detailed information on the tasks with +```sh +bundle exec rake -D +``` + +As an example, let's try running the acceptance tests using git as the code deployment mechanism. +First, we'll have to create a beaker configuration file for a local vagrant box on which to run the tests. +Here's what such a file could look like: +```yaml +HOSTS: + all-in-one: + roles: + - master + - agent + platform: centos-64-x64 + hypervisor: vagrant + ip: 192.168.80.100 + box: centos-64-x64-vbox4210-nocm + box_url: http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-vbox4210-nocm.box + +CONFIG: +``` +This defines a 64-bit CentOS 6.4 vagrant box that serves as both a puppet master and a puppet agent for the test roles. +(For more information on beaker config files, see [beaker's README](https://github.com/puppetlabs/beaker/blob/master/README.md).) +Save this file as `config/nodes/centos6-local.yaml`; we'll be needing it later. + +Since we have only provided a CentOS box, we don't have anywhere to run windows tests, therefore we'll have to skip those tests. +That means we want to pass beaker a --tests argument that contains every directory and file in the `tests` directory besides the one called `windows`. +We could pass this option on the command line, but it will be gigantic, so instead let's create a `local_options.rb` file that beaker will automatically read in. +This file should contain a ruby hash of beaker's command-line flags to the corresponding flag arguments. +Our hash will only contain the `tests` key, and its value will be a comma-seperated list of the other files and directories in `tests`. +Here's an easy way to generate this file: +```sh +echo "{tests: \"$(echo tests/* | sed -e 's| *tests/windows *||' -e 's/ /,/g')\"}" > local_options.rb" +``` + +The last thing that needs to be done before we can run the tests is to set up a way for the test box to check out our local changes for testing. +We'll do this by starting a git daemon on our host. +In another session, navigate to the folder that contains your checkout of the puppet repo, and then create the following symlink: +```sh +ln -s . puppet.git +``` +This works around the inflexible checkout path used by the test prep code. + +Now start the git daemon with +```sh +git daemon --verbose --informative-errors --reuseaddr --export-all --base-path=. +``` +after which you should see a message like `[32963] Ready to rumble` echoed to the console. + +Now we can finally run the tests! +The rake task that we'll use is `ci:test:git`. +Run +``` +bundle exec rake -D ci:test:git +``` +to read the full description of this task. +From the description, we can see that we'll need to set a few environment variables: + + CONFIG should be set to point to the CentOS beaker config file we created above. + + SHA should be the SHA of the commit we want to test. + + GIT_SERVER should be the IP address of the host (i.e. your machine) in the vagrant private network created for the test box. + This is derived from the test box's ip by replacing the last octet with 1. + For our example above, the host IP is 192.168.80.1 + + FORK should be the path to a 'puppet.git' directory that points to the repo. + In our case, this is the path to the symlink we created before, which is inside your puppet repo checkout, so FORK should just be the name of your checkout. + We'll assume that the name is `puppet`. + +Putting it all together, we construct the following command-line invocation to run the tests: +```sh +CONFIG=config/nodes/centos6-local.yaml SHA=#{test-commit-sha} GIT_SERVER='192.168.80.1' FORK='puppet' bundle exec rake --trace ci:test:git +``` +Go ahead and run that sucker! + +Testing will take some time. +After the testing finishes, you'll either see this line +``` +systest completed successfully, thanks. +``` +near the end of the output, indicating that all tests completed succesfully, or you'll see the end of a stack trace, indicating failed tests further up. From fff10ad9b27cb532139f178378b1469641a83506 Mon Sep 17 00:00:00 2001 From: Jasper Lievisse Adriaanse Date: Sat, 21 Dec 2013 22:44:23 +0100 Subject: [PATCH 276/800] (PUP-1218) Add ssh-ed25519 support to the sshkey type --- lib/puppet/type/sshkey.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/puppet/type/sshkey.rb b/lib/puppet/type/sshkey.rb index 41948ed98..db3c52c85 100644 --- a/lib/puppet/type/sshkey.rb +++ b/lib/puppet/type/sshkey.rb @@ -9,9 +9,10 @@ module Puppet newproperty(:type) do desc "The encryption type used. Probably ssh-dss or ssh-rsa." - newvalues :'ssh-dss', :'ssh-rsa', :'ecdsa-sha2-nistp256', :'ecdsa-sha2-nistp384', :'ecdsa-sha2-nistp521' + newvalues :'ssh-dss', :'ssh-ed25519', :'ssh-rsa', :'ecdsa-sha2-nistp256', :'ecdsa-sha2-nistp384', :'ecdsa-sha2-nistp521' aliasvalue(:dsa, :'ssh-dss') + aliasvalue(:ed25519, :'ssh-ed25519') aliasvalue(:rsa, :'ssh-rsa') end From 208ca9b1a70ec17a27501cc537bc3f9eb419f03a Mon Sep 17 00:00:00 2001 From: Jasper Lievisse Adriaanse Date: Sat, 21 Dec 2013 22:45:28 +0100 Subject: [PATCH 277/800] (PUP-1218) Teach the ssh_authorized_key provider about ssh-ed25519 too. --- lib/puppet/provider/ssh_authorized_key/parsed.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/provider/ssh_authorized_key/parsed.rb b/lib/puppet/provider/ssh_authorized_key/parsed.rb index cb08b593f..ecc348cbd 100644 --- a/lib/puppet/provider/ssh_authorized_key/parsed.rb +++ b/lib/puppet/provider/ssh_authorized_key/parsed.rb @@ -15,7 +15,7 @@ Puppet::Type.type(:ssh_authorized_key).provide( :fields => %w{options type key name}, :optional => %w{options}, :rts => /^\s+/, - :match => /^(?:(.+) )?(ssh-dss|ssh-rsa|ecdsa-sha2-nistp256|ecdsa-sha2-nistp384|ecdsa-sha2-nistp521) ([^ ]+) ?(.*)$/, + :match => /^(?:(.+) )?(ssh-dss|ssh-ed25519|ssh-rsa|ecdsa-sha2-nistp256|ecdsa-sha2-nistp384|ecdsa-sha2-nistp521) ([^ ]+) ?(.*)$/, :post_parse => proc { |h| h[:name] = "" if h[:name] == :absent h[:options] ||= [:absent] From 187e0477225050f98ebccf2b2e612da320784624 Mon Sep 17 00:00:00 2001 From: "Ethan J. Brown" Date: Fri, 20 Dec 2013 11:01:54 -0800 Subject: [PATCH 278/800] (PUP-1133) Cleanup after Rails temp log files - With Rails code that establishes a logger that writes to a temp log file, the logger must be closed to release the file for cleanup on Windows, without having access denied messages --- lib/puppet/rails.rb | 23 ++++++++++++++--------- spec/unit/rails/param_value_spec.rb | 4 ++++ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/lib/puppet/rails.rb b/lib/puppet/rails.rb index 36b4043ea..1ea064dd2 100644 --- a/lib/puppet/rails.rb +++ b/lib/puppet/rails.rb @@ -115,17 +115,22 @@ module Puppet::Rails def self.teardown raise Puppet::DevError, "No activerecord, cannot init Puppet::Rails" unless Puppet.features.rails? - Puppet.settings.use(:master, :rails) - begin - ActiveRecord::Base.establish_connection(database_arguments) - rescue => detail - Puppet.log_exception(detail) - raise Puppet::Error, "Could not connect to database: #{detail}" - end + Puppet.settings.use(:master, :rails) - ActiveRecord::Base.connection.tables.each do |t| - ActiveRecord::Base.connection.drop_table t + begin + ActiveRecord::Base.establish_connection(database_arguments) + rescue => detail + Puppet.log_exception(detail) + raise Puppet::Error, "Could not connect to database: #{detail}" + end + + ActiveRecord::Base.connection.tables.each do |t| + ActiveRecord::Base.connection.drop_table t + end + ensure + # allow temp files to get cleaned up + ActiveRecord::Base.logger.close if ActiveRecord::Base.logger end end end diff --git a/spec/unit/rails/param_value_spec.rb b/spec/unit/rails/param_value_spec.rb index d29d17fbb..ace121be3 100755 --- a/spec/unit/rails/param_value_spec.rb +++ b/spec/unit/rails/param_value_spec.rb @@ -12,6 +12,10 @@ describe "Puppet::Rails::ParamValue", :if => can_use_scratch_database? do Puppet::Rails::ParamName.stubs(:find_or_create_by_name).returns(name) end + after do + Puppet::Rails.teardown + end + describe "when creating initial parameter values" do it "should return an array of hashes" do Puppet::Rails::ParamValue.from_parser_param(:myparam, %w{a b})[0].should be_instance_of(Hash) From 8f5a57589009c09711ce55d08335c3f773ae06a2 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Mon, 23 Dec 2013 14:44:06 -0800 Subject: [PATCH 279/800] (PUP-986) Update references to point to Jira Since the Puppet project has moved from Redmine to Jira, we need to update all of our references to point to the new place. --- CONTRIBUTING.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 681db4beb..aa27d05b2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,7 +9,7 @@ top of things. ## Getting Started -* Make sure you have a [Redmine account](http://projects.puppetlabs.com) +* Make sure you have a [Jira account](http://tickets.puppetlabs.com) * Make sure you have a [GitHub account](https://github.com/signup/free) * Submit a ticket for your issue, assuming one does not already exist. * Clearly describe the issue including steps to reproduce when it is a bug. @@ -31,7 +31,7 @@ top of things. * Make sure your commit messages are in the proper format. ```` - (#99999) Make the example in CONTRIBUTING imperative and concrete + (PUP-1234) Make the example in CONTRIBUTING imperative and concrete Without this patch applied the example commit message in the CONTRIBUTING document is not a concrete example. This is a problem because the @@ -52,7 +52,7 @@ top of things. ### Documentation For changes of a trivial nature to comments and documentation, it is not -always necessary to create a new ticket in Redmine. In this case, it is +always necessary to create a new ticket in Jira. In this case, it is appropriate to start the first line of a commit with '(doc)' instead of a ticket number. @@ -74,13 +74,13 @@ a ticket number. * Sign the [Contributor License Agreement](http://links.puppetlabs.com/cla). * Push your changes to a topic branch in your fork of the repository. * Submit a pull request to the repository in the puppetlabs organization. -* Update your Redmine ticket to mark that you have submitted code and are ready for it to be reviewed. - * Include a link to the pull request in the ticket +* Update your Jira ticket to mark that you have submitted code and are ready for it to be reviewed (Status: Ready for Merge). + * Include a link to the pull request in the ticket. # Additional Resources * [More information on contributing](http://links.puppetlabs.com/contribute-to-puppet) -* [Bug tracker (Redmine)](http://projects.puppetlabs.com) +* [Bug tracker (Jira)](http://tickets.puppetlabs.com) * [Contributor License Agreement](http://links.puppetlabs.com/cla) * [General GitHub documentation](http://help.github.com/) * [GitHub pull request documentation](http://help.github.com/send-pull-requests/) From cb5595098b929800da28811ed27e9890afae3153 Mon Sep 17 00:00:00 2001 From: Jeff McCune Date: Thu, 26 Dec 2013 00:43:49 -0600 Subject: [PATCH 280/800] (maint) Fix can't modify frozen Symbol error on Ruby 2.1.0 Without this patch we're getting the following error on Ruby 2.1.0: `can't modify frozen Symbol (Puppet::Error)`. This problem is caused by our monkey patching which attempts to cache Symbol#to_proc return values instead of creating new instances on every call. As it turns out, the caching optimization is not necessary on Ruby 1.9.3 and later because Symbol#to_proc implements its own caching system in these versions. Here is the full error this patch avoids by not monkey patching Symbol#to_proc on versions of Ruby prior to 1.9.3. bundle exec rspec -fd spec/integration/agent/logging_spec.rb /workspace/serious/src/puppet/lib/puppet/util/monkey_patches.rb:79:in `to_proc': Could not autoload puppet/type/component: can't modify frozen Symbol (Puppet::Error) from /workspace/serious/src/puppet/lib/puppet/transaction/event.rb:15:in `' from /workspace/serious/src/puppet/lib/puppet/transaction/event.rb:8:in `' from /workspace/serious/src/puppet/lib/puppet/transaction.rb:13:in `require' from /workspace/serious/src/puppet/lib/puppet/transaction.rb:13:in `' from /workspace/serious/src/puppet/lib/puppet/transaction.rb:11:in `' from /workspace/serious/src/puppet/lib/puppet/type/component.rb:4:in `require' from /workspace/serious/src/puppet/lib/puppet/type/component.rb:4:in `' from /workspace/serious/src/puppet/lib/puppet/util/autoload.rb:61:in `load' from /workspace/serious/src/puppet/lib/puppet/util/autoload.rb:61:in `load_file' from /workspace/serious/src/puppet/lib/puppet/util/autoload.rb:202:in `load' from /workspace/serious/src/puppet/lib/puppet/metatype/manager.rb:159:in `type' from /workspace/serious/src/puppet/lib/puppet/type.rb:2466:in `' from /workspace/serious/src/puppet/lib/puppet.rb:175:in `require' from /workspace/serious/src/puppet/lib/puppet.rb:175:in `' from /workspace/serious/src/puppet/spec/spec_helper.rb:16:in `require' from /workspace/serious/src/puppet/spec/spec_helper.rb:16:in `' from /workspace/serious/src/puppet/spec/integration/agent/logging_spec.rb:2:in `require' from /workspace/serious/src/puppet/spec/integration/agent/logging_spec.rb:2:in `' from /workspace/serious/src/puppet/.bundle/gems/ruby/2.1.0/gems/rspec-core-2.11.1/lib/rspec/core/configuration.rb:780:in `load' from /workspace/serious/src/puppet/.bundle/gems/ruby/2.1.0/gems/rspec-core-2.11.1/lib/rspec/core/configuration.rb:780:in `block in load_spec_files' from /workspace/serious/src/puppet/.bundle/gems/ruby/2.1.0/gems/rspec-core-2.11.1/lib/rspec/core/configuration.rb:780:in `map' from /workspace/serious/src/puppet/.bundle/gems/ruby/2.1.0/gems/rspec-core-2.11.1/lib/rspec/core/configuration.rb:780:in `load_spec_files' from /workspace/serious/src/puppet/.bundle/gems/ruby/2.1.0/gems/rspec-core-2.11.1/lib/rspec/core/command_line.rb:22:in `run' from /workspace/serious/src/puppet/.bundle/gems/ruby/2.1.0/gems/rspec-core-2.11.1/lib/rspec/core/runner.rb:69:in `run' from /workspace/serious/src/puppet/.bundle/gems/ruby/2.1.0/gems/rspec-core-2.11.1/lib/rspec/core/runner.rb:8:in `block in autorun' --- lib/puppet/util/monkey_patches.rb | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/lib/puppet/util/monkey_patches.rb b/lib/puppet/util/monkey_patches.rb index 3f0e68e8a..82dde69d6 100644 --- a/lib/puppet/util/monkey_patches.rb +++ b/lib/puppet/util/monkey_patches.rb @@ -67,19 +67,24 @@ class Object end class Symbol - # So, it turns out that one of the biggest memory allocation hot-spots in - # our code was using symbol-to-proc - because it allocated a new instance - # every time it was called, rather than caching. + # So, it turns out that one of the biggest memory allocation hot-spots in our + # code was using symbol-to-proc - because it allocated a new instance every + # time it was called, rather than caching (in Ruby 1.8.7 and earlier). + # + # In Ruby 1.9.3 and later Symbol#to_proc does implement a cache so we skip + # the change in behavior. our monkey patch. # # Changing this means we can see XX memory reduction... - if method_defined? :to_proc - alias __original_to_proc to_proc - def to_proc - @my_proc ||= __original_to_proc - end - else - def to_proc - @my_proc ||= Proc.new {|*args| args.shift.__send__(self, *args) } + if RUBY_VERSION < "1.9.3" + if method_defined? :to_proc + alias __original_to_proc to_proc + def to_proc + @my_proc ||= __original_to_proc + end + else + def to_proc + @my_proc ||= Proc.new {|*args| args.shift.__send__(self, *args) } + end end end From 81242661c28f5b62f92ac83f8b127047392c1cd3 Mon Sep 17 00:00:00 2001 From: Alexei Romanoff Date: Thu, 26 Dec 2013 17:37:22 +0300 Subject: [PATCH 281/800] (PUP-1246) Fixed hiding error details in fileserver.conf parser This patch fixes wrong parser behavior in case when fileserver.conf contains some broken lines. It helps to debug configuration problems and makes puppet's user life a little bit easier. Also added 2 unit tests for validating these buggy cases. --- lib/puppet/file_serving/configuration/parser.rb | 4 ++-- spec/unit/file_serving/configuration/parser_spec.rb | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/puppet/file_serving/configuration/parser.rb b/lib/puppet/file_serving/configuration/parser.rb index aa09aab50..4195b8828 100644 --- a/lib/puppet/file_serving/configuration/parser.rb +++ b/lib/puppet/file_serving/configuration/parser.rb @@ -37,10 +37,10 @@ class Puppet::FileServing::Configuration::Parser when "deny" deny(mount, value) else - raise ArgumentError.new("Invalid argument '#{var}'", @count, @file) + raise ArgumentError.new("Invalid argument '#{var}' in #{@file.filename}, line #{@count}") end else - raise ArgumentError.new("Invalid line '#{line.chomp}'", @count, @file) + raise ArgumentError.new("Invalid line '#{line.chomp}' at #{@file.filename}, line #{@count}") end } } diff --git a/spec/unit/file_serving/configuration/parser_spec.rb b/spec/unit/file_serving/configuration/parser_spec.rb index 75744d317..f9e16f55d 100755 --- a/spec/unit/file_serving/configuration/parser_spec.rb +++ b/spec/unit/file_serving/configuration/parser_spec.rb @@ -84,6 +84,12 @@ describe Puppet::FileServing::Configuration::Parser do lambda { @parser.parse }.should raise_error(RuntimeError) end + + it "should return comprehensible error message, if invalid line detected" do + write_config_file "[one]\n\xc2\xa0\xc2\xa0path /etc/puppet/files\n\xc2\xa0\xc2\xa0allow *\n" + + proc { @parser.parse }.should raise_error(ArgumentError, /Invalid line.*in.*, line.*/) + end end describe Puppet::FileServing::Configuration::Parser, " when parsing mount attributes" do @@ -131,6 +137,12 @@ describe Puppet::FileServing::Configuration::Parser do proc { @parser.parse }.should raise_error(ArgumentError) end + it "should return comprehensible error message, if failed on invalid attribute" do + write_config_file "[one]\ndo something\n" + + proc { @parser.parse }.should raise_error(ArgumentError, /Invalid argument 'do' in.*, line.*/) + end + end describe Puppet::FileServing::Configuration::Parser, " when parsing the modules mount" do From b08f57071401fe50be7d35a5e460434ea7b4dee2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20C=C3=B4rte-Real?= Date: Thu, 26 Dec 2013 18:02:56 +0000 Subject: [PATCH 282/800] Bug 1051 (platforms mistook for versions) Fix bug PUP-1051 by stripping out the extraneous platform info from the individual versions of the gems. --- lib/puppet/provider/package/gem.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/provider/package/gem.rb b/lib/puppet/provider/package/gem.rb index a13bbf35f..af85fc720 100644 --- a/lib/puppet/provider/package/gem.rb +++ b/lib/puppet/provider/package/gem.rb @@ -54,7 +54,7 @@ Puppet::Type.type(:package).provide :gem, :parent => Puppet::Provider::Package d versions = $2.split(/,\s*/) { :name => name, - :ensure => versions, + :ensure => versions.map{|v| v.split[0]}, :provider => :gem } else From 1b112a16bd86bce469f481fd90d94c50f07fc94c Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Thu, 26 Dec 2013 10:06:59 -0800 Subject: [PATCH 283/800] (maint) Fix a comment. --- lib/puppet/util/monkey_patches.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/util/monkey_patches.rb b/lib/puppet/util/monkey_patches.rb index 82dde69d6..f9110986e 100644 --- a/lib/puppet/util/monkey_patches.rb +++ b/lib/puppet/util/monkey_patches.rb @@ -72,7 +72,7 @@ class Symbol # time it was called, rather than caching (in Ruby 1.8.7 and earlier). # # In Ruby 1.9.3 and later Symbol#to_proc does implement a cache so we skip - # the change in behavior. our monkey patch. + # our monkey patch. # # Changing this means we can see XX memory reduction... if RUBY_VERSION < "1.9.3" From a595118a47bfe746bf0062ddf23ecf058c61f8ce Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Thu, 26 Dec 2013 10:28:44 -0800 Subject: [PATCH 284/800] (PUP-1246) Update tests to show line numbers The previous tests didn't check that the correct line number was being reported. This updates the checks so that we can verify the line number. --- spec/unit/file_serving/configuration/parser_spec.rb | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/spec/unit/file_serving/configuration/parser_spec.rb b/spec/unit/file_serving/configuration/parser_spec.rb index f9e16f55d..8a0e1f422 100755 --- a/spec/unit/file_serving/configuration/parser_spec.rb +++ b/spec/unit/file_serving/configuration/parser_spec.rb @@ -86,9 +86,9 @@ describe Puppet::FileServing::Configuration::Parser do end it "should return comprehensible error message, if invalid line detected" do - write_config_file "[one]\n\xc2\xa0\xc2\xa0path /etc/puppet/files\n\xc2\xa0\xc2\xa0allow *\n" + write_config_file "[one]\n\n\xc2\xa0\xc2\xa0path /etc/puppet/files\n\xc2\xa0\xc2\xa0allow *\n" - proc { @parser.parse }.should raise_error(ArgumentError, /Invalid line.*in.*, line.*/) + proc { @parser.parse }.should raise_error(ArgumentError, /Invalid line.*in.*, line 3/) end end @@ -132,17 +132,11 @@ describe Puppet::FileServing::Configuration::Parser do @parser.parse end - it "should fail on any attributes other than path, allow, and deny" do - write_config_file "[one]\ndo something\n" - - proc { @parser.parse }.should raise_error(ArgumentError) - end it "should return comprehensible error message, if failed on invalid attribute" do write_config_file "[one]\ndo something\n" - proc { @parser.parse }.should raise_error(ArgumentError, /Invalid argument 'do' in.*, line.*/) + proc { @parser.parse }.should raise_error(ArgumentError, /Invalid argument 'do' in .*, line 2/) end - end describe Puppet::FileServing::Configuration::Parser, " when parsing the modules mount" do From f879dbc935f8cfe4dcd32d7e31974c178b69b9d1 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Thu, 26 Dec 2013 10:56:01 -0800 Subject: [PATCH 285/800] (Maint) Remove duplicate test There are other tests that use the module tool to search for modules. This kind of check is better done in one of those where the system just "has to work" as part of some larger narrative arc. --- acceptance/tests/ssl/should_connect_github.rb | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 acceptance/tests/ssl/should_connect_github.rb diff --git a/acceptance/tests/ssl/should_connect_github.rb b/acceptance/tests/ssl/should_connect_github.rb deleted file mode 100644 index ef8c154ae..000000000 --- a/acceptance/tests/ssl/should_connect_github.rb +++ /dev/null @@ -1,8 +0,0 @@ -test_name "puppet should be able to authenticate the forge" - -# See #11276 -confine :except, :platform => 'windows' - -agents.each do |agent| - on(agent, puppet("module search stdlib")) -end From 6494a119a9fbe573d90bef0d831ab6b329281afa Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Thu, 26 Dec 2013 11:03:16 -0800 Subject: [PATCH 286/800] (maint) Fix rdoc generator for Ruby 2.1.0 Ruby 2.1.0 includes RDoc 4.1.0 which added a requirement that subclasses of ClassModule implement 'add_prefix'. This requirement was added in this commit: https://github.com/rdoc/rdoc/commit/1701151b5b14cd20d624c4ce75fd61cc60cd07f3 The rdoc docs for other classes mention that aref_prefix should be implemented by subclasses, but generally a base class implementation is provided. The rdoc doc for ClassModule hasn't been updated to reflect the new requirement. This patch simply implements the 'aref_prefix' method. --- lib/puppet/util/rdoc/code_objects.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/puppet/util/rdoc/code_objects.rb b/lib/puppet/util/rdoc/code_objects.rb index 7edd4134e..dd5cc2f32 100644 --- a/lib/puppet/util/rdoc/code_objects.rb +++ b/lib/puppet/util/rdoc/code_objects.rb @@ -146,6 +146,10 @@ module RDoc @childs = [] end + def aref_prefix + 'puppet_class' + end + def add_resource(resource) add_to(@resource_list, resource) end From 13fcb1ad255e1af0d460a675c6e2f9607838cd5c Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Thu, 26 Dec 2013 11:31:35 -0800 Subject: [PATCH 287/800] (maint) Update test to work on non-UTF-8 systems Not all systems run as UTF-8 (our solaris tests run as US-ASCII). In those cases \xc2\xa0 (NO-BREAK SPACE) is not a valid character. This causes the test to fail because of encoding problems. By using \x01 (a control character) we can check the same scenario, but using a character sequence that is valid in all of our setups. --- spec/unit/file_serving/configuration/parser_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/unit/file_serving/configuration/parser_spec.rb b/spec/unit/file_serving/configuration/parser_spec.rb index 8a0e1f422..d9d712999 100755 --- a/spec/unit/file_serving/configuration/parser_spec.rb +++ b/spec/unit/file_serving/configuration/parser_spec.rb @@ -86,7 +86,7 @@ describe Puppet::FileServing::Configuration::Parser do end it "should return comprehensible error message, if invalid line detected" do - write_config_file "[one]\n\n\xc2\xa0\xc2\xa0path /etc/puppet/files\n\xc2\xa0\xc2\xa0allow *\n" + write_config_file "[one]\n\n\x01path /etc/puppet/files\n\x01allow *\n" proc { @parser.parse }.should raise_error(ArgumentError, /Invalid line.*in.*, line 3/) end From 3c79f8a28ce6f9759070ec8c8f874a142aaf400e Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Thu, 26 Dec 2013 12:55:27 -0800 Subject: [PATCH 288/800] (maint) Drop -v flag when untarring acceptance artifacts --- acceptance/bin/ci-bootstrap-from-artifacts.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance/bin/ci-bootstrap-from-artifacts.sh b/acceptance/bin/ci-bootstrap-from-artifacts.sh index 365db7862..36fdfbd49 100755 --- a/acceptance/bin/ci-bootstrap-from-artifacts.sh +++ b/acceptance/bin/ci-bootstrap-from-artifacts.sh @@ -25,7 +25,7 @@ echo "PACKAGE_BUILD_STATUS: ${PACKAGE_BUILD_STATUS}" rm -rf acceptance mkdir acceptance cd acceptance -tar -xzvf ../acceptance-artifacts.tar.gz +tar -xzf ../acceptance-artifacts.tar.gz echo "===== This artifact is from =====" cat creator.txt From f5b2a41df41f3536b62a301a6ccdf7db73fb7ef7 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Thu, 26 Dec 2013 14:49:09 -0800 Subject: [PATCH 289/800] (PUP-672) Add acceptance test for trusted data This adds a check that the certificate extensions show up as trusted data. Does not yet pass as the feature isn't implemented yet. --- acceptance/tests/ssl/autosign_command.rb | 2 - .../tests/ssl/certificate_extensions.rb | 66 +++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 acceptance/tests/ssl/certificate_extensions.rb diff --git a/acceptance/tests/ssl/autosign_command.rb b/acceptance/tests/ssl/autosign_command.rb index 77c5832e7..72d9007e2 100644 --- a/acceptance/tests/ssl/autosign_command.rb +++ b/acceptance/tests/ssl/autosign_command.rb @@ -101,8 +101,6 @@ custom_attributes: END agents.each do |agent| -# next if agent == master - agent_csr_attributes[agent] = "#{testdirs[agent]}/csr_attributes.yaml" create_remote_file(agent, agent_csr_attributes[agent], csr_attributes) end diff --git a/acceptance/tests/ssl/certificate_extensions.rb b/acceptance/tests/ssl/certificate_extensions.rb new file mode 100644 index 000000000..aabfb06a9 --- /dev/null +++ b/acceptance/tests/ssl/certificate_extensions.rb @@ -0,0 +1,66 @@ +require 'puppet/acceptance/common_utils' +require 'puppet/acceptance/temp_file_utils' +extend Puppet::Acceptance::CAUtils +extend Puppet::Acceptance::TempFileUtils + +initialize_temp_dirs + +test_name "certificate extensions available as trusted data" do + teardown do + remove_temp_dirs + reset_agent_ssl + end + + hostname = master.execute('facter hostname') + fqdn = master.execute('facter fqdn') + site_pp = get_test_file_path(master, "site.pp") + master_config = { + 'master' => { + 'autosign' => '/bin/true', + 'dns_alt_names' => "puppet,#{hostname},#{fqdn}", + 'manifest' => site_pp, + 'trusted_node_data' => true, + } + } + + csr_attributes = YAML.dump({ + 'extension_requests' => { + # registered puppet extensions + 'pp_uuid' => 'b5e63090-5167-11e3-8f96-0800200c9a66', + 'pp_instance_id' => 'i-3fkva', + # private (arbitrary) extensions + '1.3.6.1.4.1.34380.1.2.1' => 'db-server', # node role + '1.3.6.1.4.1.34380.1.2.2' => 'webops' # node group + } + }) + + create_test_file(master, "site.pp", <<-SITE) + file { "$test_dir/trusted.yaml": + ensure => file, + content => inline_template("<%= YAML.dump(@trusted) %>") + } + SITE + + reset_agent_ssl(false) + with_puppet_running_on(master, master_config) do + agents.each do |agent| + next if agent == master + + agent_csr_attributes = get_test_file_path(agent, "csr_attributes.yaml") + create_remote_file(agent, agent_csr_attributes, csr_attributes) + + on(agent, puppet("agent", "--test", + "--server", master, + "--waitforcert", 0, + "--csr_attributes", agent_csr_attributes, + 'ENV' => { "FACTER_test_dir" => get_test_file_path(agent, "") }), + :acceptable_exit_codes => [0, 2]) + + trusted_data = YAML.load(on(agent, "cat #{get_test_file_path(agent, 'trusted.yaml')}").stdout) + assert_equal('b5e63090-5167-11e3-8f96-0800200c9a66', trusted_data['pp_uuid']) + assert_equal('i-3fkva', trusted_data['pp_instance_id']) + assert_equal('db-server', trusted_data['1.3.6.1.4.1.34380.1.2.1']) + assert_equal('webops', trusted_data['1.3.6.1.4.1.34380.1.2.2']) + end + end +end From d0c6e41323d522ee9540a19f55e2a512cb066e09 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Thu, 26 Dec 2013 15:42:02 -0800 Subject: [PATCH 290/800] (Maint) Remove use of mocking Using the mocks is not absolutely necessary in this case. It is easy to construct the certificate and using it makes it a little clearer what kind of object we are dealing with. --- spec/unit/network/http/webrick/rest_spec.rb | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/spec/unit/network/http/webrick/rest_spec.rb b/spec/unit/network/http/webrick/rest_spec.rb index 852fe1547..05747bede 100755 --- a/spec/unit/network/http/webrick/rest_spec.rb +++ b/spec/unit/network/http/webrick/rest_spec.rb @@ -135,6 +135,12 @@ describe Puppet::Network::HTTP::WEBrickREST do @request end + def certificate_with_subject(subj) + cert = OpenSSL::X509::Certificate.new + cert.subject = OpenSSL::X509::Name.parse(subj) + cert + end + it "has no parameters when there is no query string" do only_server_side_information = [:authenticated, :ip, :node] @request.stubs(:query).returns(nil) @@ -238,10 +244,8 @@ describe Puppet::Network::HTTP::WEBrickREST do end it "should pass the client's certificate name to model method if a certificate is present" do - subj = stub 'subj' - cert = stub 'cert', :subject => subj - @request.stubs(:client_cert).returns cert - Puppet::Util::SSL.expects(:cn_from_subject).with(subj).returns 'host.domain.com' + @request.stubs(:client_cert).returns(certificate_with_subject("/CN=host.domain.com")) + @handler.params(@request)[:node].should == "host.domain.com" end @@ -254,10 +258,7 @@ describe Puppet::Network::HTTP::WEBrickREST do end it "should resolve the node name with an ip address look-up if CN parsing fails" do - subj = stub 'subj' - cert = stub 'cert', :subject => subj - @request.stubs(:client_cert).returns cert - Puppet::Util::SSL.expects(:cn_from_subject).with(subj).returns nil + @request.stubs(:client_cert).returns(certificate_with_subject("/C=company")) @handler.expects(:resolve_node).returns(:resolved_node) From 48c73a2fea53ee31a3e841aba028ca17948d691f Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Wed, 25 Dec 2013 00:59:53 +0100 Subject: [PATCH 291/800] (maint) Add Optional Type To be able to more easily deal with undef (not matched by default except by Data) the convenience type Optional is introduced with the same semantics as Variant[T, Undef] (i.e. undef/nil) is accepted instead of real type). --- lib/puppet/pops/types/types.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/puppet/pops/types/types.rb b/lib/puppet/pops/types/types.rb index 6829df6c1..5763b9191 100644 --- a/lib/puppet/pops/types/types.rb +++ b/lib/puppet/pops/types/types.rb @@ -329,4 +329,20 @@ module Puppet::Pops::Types end end + # Represents a type that accept PNilType instead of the type parameter + # required_type - is a short hand for Variant[T, Undef] + # + class POptionalType < PAbstractType + contains_one_uni 'optional_type', PAbstractType + module ClassModule + def hash + [self.class, optional_type].hash + end + + def ==(o) + self.class == o.class && optional_type == o.optional_type + end + end + end + end From 092ee0022689a8b6850f6d9924f87b62db6e2240 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Wed, 25 Dec 2013 01:01:06 +0100 Subject: [PATCH 292/800] (maint) Add support for Optional in TypeParser and TypeFactory --- lib/puppet/pops/types/type_factory.rb | 7 +++++++ lib/puppet/pops/types/type_parser.rb | 10 ++++++++++ 2 files changed, 17 insertions(+) diff --git a/lib/puppet/pops/types/type_factory.rb b/lib/puppet/pops/types/type_factory.rb index 44811d476..9b77d6bc5 100644 --- a/lib/puppet/pops/types/type_factory.rb +++ b/lib/puppet/pops/types/type_factory.rb @@ -63,6 +63,13 @@ module Puppet::Pops::Types::TypeFactory t end + # Produces the Optional type, i.e. a short hand for Variant[T, Undef] + def self.optional(optional_type = nil) + t = Types::POptionalType.new + t.optional_type = optional_type + t + end + # Produces the Enum type, optionally with specific string values # @api public # diff --git a/lib/puppet/pops/types/type_parser.rb b/lib/puppet/pops/types/type_parser.rb index 33b7c1a06..e249dba07 100644 --- a/lib/puppet/pops/types/type_parser.rb +++ b/lib/puppet/pops/types/type_parser.rb @@ -149,6 +149,9 @@ class Puppet::Pops::Types::TypeParser when "variant" TYPES.variant() + when "optional" + TYPES.optional() + when "ruby", "type" # should not be interpreted as Resource type # TODO: these should not be errors @@ -249,6 +252,13 @@ class Puppet::Pops::Types::TypeParser TYPES.float_range(parameters[0] == :default ? nil : parameters[0], parameters[1] == :default ? nil : parameters[1]) end + when "optional" + if parameters.size != 1 + raise_invalid_parameters_error("Optional", 1, parameters.size) + end + assert_type(parameters[0]) + TYPES.optional(parameters[0]) + when "object", "collection", "data", "catalogentry", "boolean", "literal", "undef", "numeric", "pattern", "string" raise_unparameterized_type_error(parameterized_ast.left_expr) From bff6ed337969530f91186aa458af19901ba105eb Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Wed, 25 Dec 2013 01:04:00 +0100 Subject: [PATCH 293/800] (maint) Make handling of undef more strict as a type instace? should not return true for undef for all types. This means that [undef] =~ Array[Integer] is false. The Data type was relaxed to still accept undef/nil everywhere except as a key in a hash. --- lib/puppet/pops/evaluator/access_operator.rb | 14 +++ lib/puppet/pops/types/type_calculator.rb | 45 ++++++-- spec/unit/pops/types/type_calculator_spec.rb | 115 +++++++++++++++---- 3 files changed, 145 insertions(+), 29 deletions(-) diff --git a/lib/puppet/pops/evaluator/access_operator.rb b/lib/puppet/pops/evaluator/access_operator.rb index b3535ac4c..cdb71e286 100644 --- a/lib/puppet/pops/evaluator/access_operator.rb +++ b/lib/puppet/pops/evaluator/access_operator.rb @@ -226,6 +226,20 @@ class Puppet::Pops::Evaluator::AccessOperator Puppet::Pops::Types::TypeFactory.pattern(*keys) end + def access_POptionalType(o, scope, keys) + keys.flatten! + if keys.size == 1 + unless keys[0].is_a?(Puppet::Pops::Types::PAbstractType) + fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[0], {:base_type => 'Optional-Type', :actual => keys[0].class}) + end + result = Puppet::Pops::Types::POptionalType.new() + result.optional_type = keys[0] + result + else + fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_ARITY, @semantic, {:base_type => 'Optional-Type', :min => 1, :actual => keys.size}) + end + end + def access_PIntegerType(o, scope, keys) keys.flatten! unless keys.size.between?(1, 2) diff --git a/lib/puppet/pops/types/type_calculator.rb b/lib/puppet/pops/types/type_calculator.rb index 11b3ef6c9..3ace35fea 100644 --- a/lib/puppet/pops/types/type_calculator.rb +++ b/lib/puppet/pops/types/type_calculator.rb @@ -169,6 +169,7 @@ class Puppet::Pops::Types::TypeCalculator data_variant.addTypes(@data_hash.copy) data_variant.addTypes(@data_array.copy) data_variant.addTypes(Types::PLiteralType.new) + data_variant.addTypes(Types::PNilType.new) @data_variant_t = data_variant end @@ -218,10 +219,8 @@ class Puppet::Pops::Types::TypeCalculator # @api public # def assignable?(t, t2) - # nil is assignable to anything - if is_pnil?(t2) - return true - end + # nil is assignable to anything except to required types + return true if is_pnil?(t2) if t.is_a?(Class) t = type(t) @@ -328,11 +327,13 @@ class Puppet::Pops::Types::TypeCalculator end def instance_of(t, o) - return true if o.nil? +# return true if o.nil? && !t.is_a?(Types::PRequiredType) @@instance_of_visitor.visit_this_1(self, t, o) end def instance_of_Object(t, o) + # Undef is Undef and Object, but nothing else when checking instance? + return false if (o.nil? || o == :undef) && t.class != Types::PObjectType assignable?(t, infer(o)) end @@ -352,9 +353,18 @@ class Puppet::Pops::Types::TypeCalculator instance_of(@data_variant_t, o) end - def instance_of_PVariableType(t, o) - # calculate types without reducing, Array and Hash will have Variant type as parameters - assignable?(t, infer_set(o)) + def instance_of_PNilType(t, o) + return o.nil? || o == :undef + end + + def instance_of_POptionalType(t, o) + return true if (o.nil? || o == :undef) + instance_of(t.optional_type, o) + end + + def instance_of_PVariantType(t, o) + # instance of variant if o is instance? of any of variant's types + t.types.any? { |option_t| instance_of(option_t, o) } end # Answers 'is o an instance of type t' @@ -641,6 +651,12 @@ class Puppet::Pops::Types::TypeCalculator Types::PNilType.new() end + # Inference of :undef as PNilType, all other are Ruby[Symbol] + # @api private + def infer_Symbol(o) + o == :undef ? infer_NilClass(o) : infer_Object(o) + end + # @api private def infer_TrueClass(o) Types::PBooleanType.new() @@ -796,6 +812,15 @@ class Puppet::Pops::Types::TypeCalculator end end + def assignable_POptionalType(t, t2) + return true if t2.is_a(Types::PNilType) + if t2.is_a?(Types::POptionalType) + assignable?(t.optional_type, t2.optional_type) + else + assignable?(t.optional_type, t2) + end + end + def assignable_PEnumType(t, t2) return true if t == t2 || (t.values.empty? && (t2.is_a?(Types::PStringType) || t2.is_a?(Types::PEnumType))) if t2.is_a?(Types::PStringType) @@ -1066,6 +1091,10 @@ class Puppet::Pops::Types::TypeCalculator end end + def string_POptionalType(t) + "Optional[#{string(t.optional_type)}]" + end + # Catches all non enumerable types # @api private def enumerable_Object(o) diff --git a/spec/unit/pops/types/type_calculator_spec.rb b/spec/unit/pops/types/type_calculator_spec.rb index b6a7084ea..49fb9f069 100644 --- a/spec/unit/pops/types/type_calculator_spec.rb +++ b/spec/unit/pops/types/type_calculator_spec.rb @@ -39,6 +39,10 @@ describe 'The type calculator' do Puppet::Pops::Types::TypeFactory.array_of(t) end + def data_t() + Puppet::Pops::Types::TypeFactory.data() + end + def types Puppet::Pops::Types end @@ -115,6 +119,7 @@ describe 'The type calculator' do result << Puppet::Pops::Types::PDataType result << array_t(types::PDataType.new) result << types::TypeFactory.hash_of_data + result << Puppet::Pops::Types::PNilType result end @@ -163,6 +168,10 @@ describe 'The type calculator' do calculator.infer(nil).class.should == Puppet::Pops::Types::PNilType end + it ':undef translates to PNilType' do + calculator.infer(:undef).class.should == Puppet::Pops::Types::PNilType + end + it 'an instance of class Foo translates to PRubyType[Foo]' do class Foo end @@ -362,14 +371,14 @@ describe 'The type calculator' do calculator.string(common_t).should == "Pattern[/abc/, /xyz/]" end - it 'computes enum commonality to value set diff' do + it 'computes enum commonality to value set sum' do t1 = enum_t('a', 'b', 'c') t2 = enum_t('x', 'y', 'z') common_t = calculator.common_type(t1, t2) common_t.should == enum_t('a', 'b', 'c', 'x', 'y', 'z') end - it 'computed variant commonality to type union where added types are not assignable' do + it 'computed variant commonality to type union where added types are not sub-types' do a_t1 = integer_t() a_t2 = enum_t('b') v_a = variant_t(a_t1, a_t2) @@ -380,7 +389,7 @@ describe 'The type calculator' do Set.new(common_t.types).should == Set.new([a_t1, a_t2, b_t1]) end - it 'computed variant commonality to type union where added types are assignable' do + it 'computed variant commonality to type union where added types are sub-types' do a_t1 = integer_t() a_t2 = string_t() v_a = variant_t(a_t1, a_t2) @@ -721,46 +730,101 @@ describe 'The type calculator' do end context 'when testing if x is instance of type t' do + include_context "types_setup" + + it 'should consider undef to be instance of Object and NilType' do + calculator.instance?(Puppet::Pops::Types::PNilType.new(), nil).should == true + calculator.instance?(Puppet::Pops::Types::PObjectType.new(), nil).should == true + end + + it 'should not consider undef to be an instance of any other type than Object and NilType and Data' do + types_to_test = all_types - [ + Puppet::Pops::Types::PObjectType, + Puppet::Pops::Types::PNilType, + Puppet::Pops::Types::PDataType] + + types_to_test.each {|t| calculator.instance?(t.new, nil).should == false } + types_to_test.each {|t| calculator.instance?(t.new, :undef).should == false } + end + it 'should consider fixnum instanceof PIntegerType' do - calculator.instance?(Puppet::Pops::Types::PIntegerType.new(), 1) == true + calculator.instance?(Puppet::Pops::Types::PIntegerType.new(), 1).should == true end it 'should consider fixnum instanceof Fixnum' do - calculator.instance?(Fixnum, 1) == true + calculator.instance?(Fixnum, 1).should == true end it 'should consider integer in range' do range = int_range(0,10) - calculator.instance?(range, 1) == true - calculator.instance?(range, 10) == true - calculator.instance?(range, -1) == false - calculator.instance?(range, 11) == false + calculator.instance?(range, 1).should == true + calculator.instance?(range, 10).should == true + calculator.instance?(range, -1).should == false + calculator.instance?(range, 11).should == false end it 'should consider string matching enum as instanceof' do enum = enum_t('XS', 'S', 'M', 'L', 'XL', '0') - calculator.instance?(enum, 'XS') == true - calculator.instance?(enum, 'S') == true - calculator.instance?(enum, 'XXL') == false - calculator.instance?(enum, '') == false - calculator.instance?(enum, '0') == true - calculator.instance?(enum, 0) == false + calculator.instance?(enum, 'XS').should == true + calculator.instance?(enum, 'S').should == true + calculator.instance?(enum, 'XXL').should == false + calculator.instance?(enum, '').should == false + calculator.instance?(enum, '0').should == true + calculator.instance?(enum, 0).should == false end it 'should consider array[string] as instance of Array[Enum] when strings are instance of Enum' do enum = enum_t('XS', 'S', 'M', 'L', 'XL', '0') array = array_t(enum) - calculator.instance?(array, ['XS', 'S', 'XL']) == true - calculator.instance?(array, ['XS', 'S', 'XXL']) == false + calculator.instance?(array, ['XS', 'S', 'XL']).should == true + calculator.instance?(array, ['XS', 'S', 'XXL']).should == false end it 'should consider array[mixed] as instance of Variant[mixed] when mixed types are listed in Variant' do enum = enum_t('XS', 'S', 'M', 'L', 'XL') sizes = int_range(30, 50) - array = variant_t(enum, sizes) - calculator.instance?(array, ['XS', 'S', 30, 50]) == true - calculator.instance?(array, ['XS', 'S', 'XXL']) == false - calculator.instance?(array, ['XS', 'S', 29]) == false + array = array_t(variant_t(enum, sizes)) + calculator.instance?(array, ['XS', 'S', 30, 50]).should == true + calculator.instance?(array, ['XS', 'S', 'XXL']).should == false + calculator.instance?(array, ['XS', 'S', 29]).should == false + end + + context 'and t is Data' do + it 'undef should be considered instance of Data' do + calculator.instance?(data_t, :undef).should == true + end + + it 'other symbols should not br considered instance of Data' do + calculator.instance?(data_t, :love).should == false + end + + it 'an empty array should be considered instance of Data' do + calculator.instance?(data_t, []).should == true + end + + it 'an empty hash should be considered instance of Data' do + calculator.instance?(data_t, {}).should == true + end + + it 'a hash with nil/undef data should be considered instance of Data' do + calculator.instance?(data_t, {'a' => nil}).should == true + calculator.instance?(data_t, {'a' => :undef}).should == true + end + + it 'a hash with nil/undef key should not considered instance of Data' do + calculator.instance?(data_t, {nil => 10}).should == false + calculator.instance?(data_t, {:undef => 10}).should == false + end + + it 'an array with undef entries should be considered instance of Data' do + calculator.instance?(data_t, [:undef]).should == true + calculator.instance?(data_t, [nil]).should == true + end + + it 'an array with undef / data entries should be considered instance of Data' do + calculator.instance?(data_t, [1, :undef, 'a']).should == true + calculator.instance?(data_t, [1, nil, 'a']).should == true + end end end @@ -1072,6 +1136,15 @@ describe 'The type calculator' do element_type.types[2].class.should == Puppet::Pops::Types::PIntegerType element_type.types[3].class.should == Puppet::Pops::Types::PIntegerType end + + it "does not reduce by combining types when using infer_set and values are undef" do + element_type = calculator.infer(['a',nil]).element_type + element_type.class.should == Puppet::Pops::Types::PStringType + element_type = calculator.infer_set(['a',nil]).element_type + element_type.class.should == Puppet::Pops::Types::PVariantType + element_type.types[0].class.should == Puppet::Pops::Types::PStringType + element_type.types[1].class.should == Puppet::Pops::Types::PNilType + end end matcher :be_assignable_to do |type| From 85907209d14ab5d6a078c914ace1fca90452bb98 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Wed, 25 Dec 2013 01:05:17 +0100 Subject: [PATCH 294/800] (maint) Make injector accept undef/nil when doing type checking Since types are now restrictive, the injector must tolerate undef as instance (i.e. since it means 'not found'). --- lib/puppet/pops/binder/injector.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/puppet/pops/binder/injector.rb b/lib/puppet/pops/binder/injector.rb index 722e8abe5..4660fe658 100644 --- a/lib/puppet/pops/binder/injector.rb +++ b/lib/puppet/pops/binder/injector.rb @@ -406,6 +406,7 @@ module Private # @api private def lookup_type(scope, type, name='') val = lookup_key(scope, named_key(type, name)) + return nil if val.nil? unless key_factory.type_calculator.instance?(type, val) raise ArgumentError, "Type error: incompatible type, #{type_error_detail(type, val)}" end From 3d1ddf11efb1e7861111f6b322ec6ab8eeadee98 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 27 Dec 2013 16:30:20 +0100 Subject: [PATCH 295/800] (maint) Fix several issues with type system implementation The type system implementation and tests had several flaws. * Tests did not call expect/should and did not assert * Undef handled wrong wrt. instance_of * String allowed parameterization with string values (useless). Can now instead be parameterized with a size constraint * Collection, Array, and Hash can now be parameterized with size constraint. * Data type made lenient allowing undef in constructs --- .../binder/config/binder_config_checker.rb | 1 + lib/puppet/pops/evaluator/access_operator.rb | 98 ++++++++++-- lib/puppet/pops/issues.rb | 8 + lib/puppet/pops/types/type_calculator.rb | 148 +++++++++++++----- lib/puppet/pops/types/type_factory.rb | 8 + lib/puppet/pops/types/type_parser.rb | 55 ++++++- lib/puppet/pops/types/types.rb | 49 +++--- spec/unit/pops/evaluator/access_ops_spec.rb | 4 + .../pops/evaluator/evaluating_parser_spec.rb | 30 ++-- spec/unit/pops/types/type_calculator_spec.rb | 90 ++++++++++- spec/unit/pops/types/type_factory_spec.rb | 60 +++++++ spec/unit/pops/types/type_parser_spec.rb | 26 ++- 12 files changed, 480 insertions(+), 97 deletions(-) diff --git a/lib/puppet/pops/binder/config/binder_config_checker.rb b/lib/puppet/pops/binder/config/binder_config_checker.rb index 41deb3b6a..68d5c86ca 100644 --- a/lib/puppet/pops/binder/config/binder_config_checker.rb +++ b/lib/puppet/pops/binder/config/binder_config_checker.rb @@ -108,6 +108,7 @@ module Puppet::Pops::Binder::Config def check_category(category, config_file) type = @type_calculator.infer(category) unless @type_calculator.assignable?(@array_of_string_type, type) + require 'debugger'; debugger accept(Issues::CATEGORY_IS_NOT_ARRAY, config_file, :type => @type_calculator.string(type)) return end diff --git a/lib/puppet/pops/evaluator/access_operator.rb b/lib/puppet/pops/evaluator/access_operator.rb index cdb71e286..17799d9b9 100644 --- a/lib/puppet/pops/evaluator/access_operator.rb +++ b/lib/puppet/pops/evaluator/access_operator.rb @@ -168,8 +168,17 @@ class Puppet::Pops::Evaluator::AccessOperator def access_PStringType(o, scope, keys) keys.flatten! - assert_keys(keys, o, 1, INFINITY, String) - Puppet::Pops::Types::TypeFactory.string(*keys) + case keys.size + when 1 + size_t = collection_size_t(0, keys[0]) + when 2 + size_t = collection_size_t(0, keys[0], keys[1]) + else + fail(Puppet::Pops::Issues::BAD_STRING_SLICE_ARITY, @semantic, {:actual => keys.size}) + end + string_t = Puppet::Pops::Types::TypeFactory.string() + string_t.size_type = size_t + string_t end # Asserts type of each key and calls fail with BAD_TYPE_SPECIFICATION @@ -272,12 +281,13 @@ class Puppet::Pops::Evaluator::AccessOperator ranged_float end - # A Hash can create a new Hash type, one arg sets value type, two args sets key and value type in new type - # It is not possible to create a collection of Hash types. + # A Hash can create a new Hash type, one arg sets value type, two args sets key and value type in new type. + # With 3 or 4 arguments, these are used to create a size constraint. + # It is not possible to create a collection of Hash types directly. # def access_PHashType(o, scope, keys) keys.flatten! - keys.each_with_index do |k, index| + keys[0,2].each_with_index do |k, index| unless k.is_a?(Puppet::Pops::Types::PAbstractType) fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[index], {:base_type => 'Hash-Type', :actual => k.class}) end @@ -285,7 +295,7 @@ class Puppet::Pops::Evaluator::AccessOperator case keys.size when 1 result = Puppet::Pops::Types::PHashType.new() - result.key_type = Marshal.load(Marshal.dump(o.key_type)) + result.key_type = o.key_type.copy result.element_type = keys[0] result when 2 @@ -293,24 +303,82 @@ class Puppet::Pops::Evaluator::AccessOperator result.key_type = keys[0] result.element_type = keys[1] result + when 3 + result = Puppet::Pops::Types::PHashType.new() + result.key_type = keys[0] + result.element_type = keys[1] + size_t = collection_size_t(1, keys[2]) + result + when 4 + result = Puppet::Pops::Types::PHashType.new() + result.key_type = keys[0] + result.element_type = keys[1] + size_t = collection_size_t(1, keys[2], keys[3]) + result else - fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_ARITY, @semantic, {:base_type => 'Hash-Type', :min => 1, :max => 2, :actual => keys.size}) + fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_ARITY, @semantic, { + :base_type => 'Hash-Type', :min => 1, :max => 4, :actual => keys.size + }) end + result.size_type = size_t if size_t + result + end + + # CollectionType is parameterized with a range + def access_PCollectionType(o, scope, keys) + keys.flatten! + case keys.size + when 1 + size_t = collection_size_t(1, keys[0]) + when 2 + size_t = collection_size_t(1, keys[0], keys[1]) + else + fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_ARITY, @semantic, + {:base_type => 'Collection-Type', :min => 1, :max => 2, :actual => keys.size}) + end + result = Puppet::Pops::Types::PCollectionType.new() + result.size_type = size_t + result end # An Array can create a new Array type. It is not possible to create a collection of Array types. # def access_PArrayType(o, scope, keys) keys.flatten! - if keys.size == 1 - unless keys[0].is_a?(Puppet::Pops::Types::PAbstractType) - fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[0], {:base_type => 'Array-Type', :actual => keys[0].class}) - end - result = Puppet::Pops::Types::PArrayType.new() - result.element_type = keys[0] - result + case keys.size + when 1 + size_t = nil + when 2 + size_t = collection_size_t(1, keys[1]) + when 3 + size_t = collection_size_t(1, keys[1], keys[2]) else - fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_ARITY, @semantic, {:base_type => 'Array-Type', :min => 1, :actual => keys.size}) + fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_ARITY, @semantic, + {:base_type => 'Array-Type', :min => 1, :max => 3, :actual => keys.size}) + end + unless keys[0].is_a?(Puppet::Pops::Types::PAbstractType) + fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[0], {:base_type => 'Array-Type', :actual => keys[0].class}) + end + result = Puppet::Pops::Types::PArrayType.new() + result.element_type = keys[0] + result.size_type = size_t + result + end + + # Produces an PIntegerType (range) given one or two keys. + def collection_size_t(start_index, *keys) + if keys.size == 1 && keys[0].is_a?(Puppet::Pops::Types::PIntegerType) + keys[0].copy + else + keys.each_with_index do |x, index| + fail(Puppet::Pops::Issues::BAD_COLLECTION_SLICE_TYPE, @semantic.keys[start_index + index], + {:actual => x.class}) unless (x.is_a?(Integer) || x == :default) + end + ranged_integer = Puppet::Pops::Types::PIntegerType.new() + from, to = keys + ranged_integer.from = from == :default ? nil : from + ranged_integer.to = to == :default ? nil : to + ranged_integer end end diff --git a/lib/puppet/pops/issues.rb b/lib/puppet/pops/issues.rb index 9f901a6c1..7fe51bdb2 100644 --- a/lib/puppet/pops/issues.rb +++ b/lib/puppet/pops/issues.rb @@ -335,6 +335,10 @@ module Puppet::Pops::Issues "String supports [] with one or two arguments. Got #{actual}" end + BAD_STRING_SLICE_TYPE = issue :BAD_STRING_SLICE_TYPE, :actual do + "String-Type [] requires all arguments to be integers (or default). Got #{actual}" + end + BAD_ARRAY_SLICE_ARITY = issue :BAD_ARRAY_SLICE_ARITY, :actual do "Array supports [] with one or two arguments. Got #{actual}" end @@ -351,6 +355,10 @@ module Puppet::Pops::Issues "Integer-Type [] requires all arguments to be integers (or default). Got #{actual}" end + BAD_COLLECTION_SLICE_TYPE = issue :BAD_COLLECTION_SLICE_TYPE, :actual do + "A Type's size constraint arguments must be a single Integer type, or 1-2 integers (or default). Got #{label.a_an(actual)}" + end + BAD_FLOAT_SLICE_ARITY = issue :BAD_INTEGER_SLICE_ARITY, :actual do "Float-Type supports [] with one or two arguments (from, to). Got #{actual}" end diff --git a/lib/puppet/pops/types/type_calculator.rb b/lib/puppet/pops/types/type_calculator.rb index 3ace35fea..e314825fe 100644 --- a/lib/puppet/pops/types/type_calculator.rb +++ b/lib/puppet/pops/types/type_calculator.rb @@ -172,6 +172,11 @@ class Puppet::Pops::Types::TypeCalculator data_variant.addTypes(Types::PNilType.new) @data_variant_t = data_variant + collection_default_size = Types::PIntegerType.new() + collection_default_size.from = 0 + collection_default_size.to = nil # infinity + @collection_default_size_t = collection_default_size + end # Convenience method to get a data type for comparisons @@ -293,10 +298,17 @@ class Puppet::Pops::Types::TypeCalculator def generalize_PStringType(o) o.values = [] + o.size_type = nil [] end - def generalize_PFloatTye(o) + def generalize_PCollectionType(o) + # erase the size constraint from Array and Hash (if one exists, it is transformed to -Infinity - + Infinity, which is + # not desirable. + o.size_type = nil + end + + def generalize_PFloatType(o) o.to = nil o.from = nil end @@ -339,17 +351,24 @@ class Puppet::Pops::Types::TypeCalculator def instance_of_PArrayType(t, o) return false unless o.is_a?(Array) - o.all? {|element| instance_of(t.element_type, element) } + return false unless o.all? {|element| instance_of(t.element_type, element) } + size_t = t.size_type || @collection_default_size_t + size_t2 = size_as_type(o) + assignable?(size_t, size_t2) end def instance_of_PHashType(t, o) return false unless o.is_a?(Hash) key_t = t.key_type element_t = t.element_type - o.keys.all? {|key| instance_of(key_t, key) } && o.values.all? {|value| instance_of(element_t, value) } + return false unless o.keys.all? {|key| instance_of(key_t, key) } && o.values.all? {|value| instance_of(element_t, value) } + size_t = t.size_type || @collection_default_size_t + size_t2 = size_as_type(o) + assignable?(size_t, size_t2) end def instance_of_PDataType(t, o) + #require 'debugger'; debugger instance_of(@data_variant_t, o) end @@ -620,6 +639,7 @@ class Puppet::Pops::Types::TypeCalculator def infer_String(o) t = Types::PStringType.new() t.addValues(o) + t.size_type = size_as_type(o) t end @@ -680,11 +700,13 @@ class Puppet::Pops::Types::TypeCalculator # @api private def infer_Array(o) type = Types::PArrayType.new() - type.element_type = if o.empty? + type.element_type = + if o.empty? Types::PNilType.new() else infer_and_reduce_type(o) end + type.size_type = size_as_type(o) type end @@ -700,9 +722,18 @@ class Puppet::Pops::Types::TypeCalculator end type.key_type = ktype type.element_type = etype + type.size_type = size_as_type(o) type end + def size_as_type(collection) + size = collection.size + t = Types::PIntegerType.new() + t.from = size + t.to = size + t + end + # Common case for everything that intrinsically only has a single type def infer_set_Object(o) infer(o) @@ -717,6 +748,7 @@ class Puppet::Pops::Types::TypeCalculator t.types = o.map() {|x| infer_set(x) } t.types.size == 1 ? t.types[0] : t end + type.size_type = size_as_type(o) type end @@ -733,6 +765,7 @@ class Puppet::Pops::Types::TypeCalculator end type.key_type = unwrap_single_variant(ktype) type.element_type = unwrap_single_variant(vtype) + type.size_type = size_as_type(o) type end @@ -834,10 +867,37 @@ class Puppet::Pops::Types::TypeCalculator # @api private def assignable_PStringType(t, t2) if t.values.empty? - # A general string is assignable by any other string, or pattern restricted string - t2.is_a?(Types::PStringType) || t2.is_a?(Types::PPatternType) || t2.is_a?(Types::PEnumType) + # A general string is assignable by any other string or pattern restricted string + # if the string has a size constraint it does not match since there is no reasonable way + # to compute the min/max length a pattern will match. For enum, it is possible to test that + # each enumerator value is within range + size_t = t.size_type || @collection_default_size_t + case t2 + when Types::PStringType + # true if size compliant + size_t2 = t2.size_type || @collection_default_size_t + assignable?(size_t, size_t2) + + when Types::PPatternType + # true if size constraint is at least 0 to +Infinity (which is the same as the default) + assignable?(size_t, @collection_default_size_t) + + when Types::PEnumType + if t2.values + # true if all enum values are within range + min, max = t2.values.map(&:size).minmax + trange = from_to_ordered(size_t.from, size_t.to) + t2range = [min, max] + # If t2 min and max are within the range of t + trange[0] <= t2range[0] && trange[1] >= t2range[1] + else + # no string can match this enum anyway since it does not accept anything + false + end + end elsif t2.is_a?(Types::PStringType) # A specific string acts as a set of strings - must have exactly the same strings + # In this case, size does not matter since the definition is very precise anyway Set.new(t.values) == Set.new(t2.values) else # All others are false, since no other type describes the same set of specific strings @@ -881,7 +941,10 @@ class Puppet::Pops::Types::TypeCalculator # @api private def assignable_PCollectionType(t, t2) - t2.is_a?(Types::PCollectionType) + return false unless t2.is_a?(Types::PCollectionType) + size_t = t.size_type || @collection_default_size_t + size_t2 = t2.size_type || @collection_default_size_t + assignable?(size_t, size_t2) end # @api private @@ -894,20 +957,24 @@ class Puppet::Pops::Types::TypeCalculator # @api private def assignable_PArrayType(t, t2) return false unless t2.is_a?(Types::PArrayType) - assignable?(t.element_type, t2.element_type) + return false unless assignable?(t.element_type, t2.element_type) + assignable_PCollectionType(t, t2) end # Hash is assignable if t2 is a Hash and t2's key and element types are assignable # @api private def assignable_PHashType(t, t2) return false unless t2.is_a?(Types::PHashType) - assignable?(t.key_type, t2.key_type) && assignable?(t.element_type, t2.element_type) + return false unless assignable?(t.key_type, t2.key_type) && assignable?(t.element_type, t2.element_type) + assignable_PCollectionType(t, t2) end + # @api private def assignable_PCatalogEntryType(t1, t2) t2.is_a?(Types::PCatalogEntryType) end + # @api private def assignable_PHostClassType(t1, t2) return false unless t2.is_a?(Types::PHostClassType) # Class = Class[name}, Class[name] != Class @@ -916,6 +983,7 @@ class Puppet::Pops::Types::TypeCalculator return t1.class_name == t2.class_name end + # @api private def assignable_PResourceType(t1, t2) return false unless t2.is_a?(Types::PResourceType) return true if t1.type_name.nil? @@ -928,11 +996,6 @@ class Puppet::Pops::Types::TypeCalculator # @api private def assignable_PDataType(t, t2) t2.is_a?(Types::PDataType) || assignable?(@data_variant_t, t2) -# -# t2.is_a?(Types::PLiteralType) || -# assignable?(@data_array, t2) || -# assignable?(@data_hash, t2) || -# (t2.is_a?(Types::PVariantType) && !t2.types.empty? && t2.types.all? {|t| assignable?(data, t) } ) end # Assignable if t2's ruby class is same or subclass of t1's ruby class @@ -945,6 +1008,7 @@ class Puppet::Pops::Types::TypeCalculator !!(c2 <= c1) end + # @api private def debug_string_Object(t) string(t) end @@ -984,30 +1048,23 @@ class Puppet::Pops::Types::TypeCalculator # @api private def string_PIntegerType(t) - result = ["Integer"] - unless t.from.nil? && t.to.nil? - from = t.from.nil? ? 'default' : t.from - to = t.to.nil? ? 'default' : t.to - if from == to - "Integer[#{from}]" - else - "Integer[#{from}, #{to}]" - end + range = range_array_part(t) + unless range.empty? + "Integer[#{range.join(', ')}]" else "Integer" end end + def range_array_part(t) + return [] if t.nil? || (t.from.nil? && t.to.nil?) + [t.from.nil? ? 'default' : t.from , t.to.nil? ? 'default' : t.to ] + end + def string_PFloatType(t) - result = ["Float"] - unless t.from.nil? && t.to.nil? - from = t.from.nil? ? 'default' : t.from - to = t.to.nil? ? 'default' : t.to - if from == to - "Float[#{from}]" - else - "Float[#{from}, #{to}]" - end + range = range_array_part(t) + unless range.empty? + "Float[#{range.join(', ')}]" else "Float" end @@ -1021,13 +1078,19 @@ class Puppet::Pops::Types::TypeCalculator # @api private def string_PStringType(t) # skip values in regular output - see debug_string - return "String" + range = range_array_part(t.size_type) + unless range.empty? + "String[#{range.join(', ')}]" + else + "String" + end end # @api private def debug_string_PStringType(t) - return "String" # if t.values.empty? - "String[" << (t.values.map {|s| "'#{s}'" }).join(', ') << ']' + range = range_array_part(t.size_type) + range_part = range.empty? ? '' : '[' << range.join(' ,') << '], ' + "String[" << range_part << (t.values.map {|s| "'#{s}'" }).join(', ') << ']' end # @api private @@ -1049,19 +1112,28 @@ class Puppet::Pops::Types::TypeCalculator end # @api private - def string_PCollectionType(t) ; "Collection" ; end + def string_PCollectionType(t) + range = range_array_part(t.size_type) + unless range.empty? + "Collection[#{range.join(', ')}]" + else + "Collection" + end + end # @api private def string_PRubyType(t) ; "Ruby[#{string(t.ruby_class)}]" ; end # @api private def string_PArrayType(t) - "Array[#{string(t.element_type)}]" + parts = [string(t.element_type)] + range_array_part(t.size_type) + "Array[#{parts.join(', ')}]" end # @api private def string_PHashType(t) - "Hash[#{string(t.key_type)}, #{string(t.element_type)}]" + parts = [string(t.key_type), string(t.element_type)] + range_array_part(t.size_type) + "Hash[#{parts.join(', ')}]" end # @api private diff --git a/lib/puppet/pops/types/type_factory.rb b/lib/puppet/pops/types/type_factory.rb index 9b77d6bc5..25ebc1cee 100644 --- a/lib/puppet/pops/types/type_factory.rb +++ b/lib/puppet/pops/types/type_factory.rb @@ -276,4 +276,12 @@ module Puppet::Pops::Types::TypeFactory type end end + + # Sets the accepted size range of a collection if something other than the default 0 to Infinity + # is wanted. The semantics for from/to are the same as for #range + # + def self.constrain_size(collection_t, from, to) + collection_t.size_type = range(from, to) + collection_t + end end diff --git a/lib/puppet/pops/types/type_parser.rb b/lib/puppet/pops/types/type_parser.rb index e249dba07..647db3ea3 100644 --- a/lib/puppet/pops/types/type_parser.rb +++ b/lib/puppet/pops/types/type_parser.rb @@ -171,23 +171,60 @@ class Puppet::Pops::Types::TypeParser case parameterized_ast.left_expr.value when "array" - if parameters.size != 1 - raise_invalid_parameters_error("Array", 1, parameters.size) + case parameters.size + when 1 + when 2 + size_type = + if parameters[1].is_a?(Puppet::Pops::Types::PIntegerType) + parameters[1].copy + else + assert_range_parameter(parameters[1]) + TYPES.range(parameters[1], :default) + end + when 3 + assert_range_parameter(parameters[1]) + assert_range_parameter(parameters[2]) + size_type = TYPES.range(parameters[1], parameters[2]) + else + raise_invalid_parameters_error("Array", "1 to 3", parameters.size) end assert_type(parameters[0]) - TYPES.array_of(parameters[0]) + t = TYPES.array_of(parameters[0]) + t.size_type = size_type if size_type + t when "hash" - if parameters.size == 1 + result = case parameters.size + when 1 assert_type(parameters[0]) TYPES.hash_of(parameters[0]) - elsif parameters.size != 2 - raise_invalid_parameters_error("Hash", "1 or 2", parameters.size) - else + when 2 assert_type(parameters[0]) assert_type(parameters[1]) TYPES.hash_of(parameters[1], parameters[0]) + when 3 + size_type = + if parameters[2].is_a?(Puppet::Pops::Types::PIntegerType) + parameters[2].copy + else + assert_range_parameter(parameters[2]) + TYPES.range(parameters[2], :default) + end + assert_type(parameters[0]) + assert_type(parameters[1]) + TYPES.hash_of(parameters[1], parameters[0]) + when 4 + assert_range_parameter(parameters[2]) + assert_range_parameter(parameters[3]) + size_type = TYPES.range(parameters[2], parameters[3]) + assert_type(parameters[0]) + assert_type(parameters[1]) + TYPES.hash_of(parameters[1], parameters[0]) + else + raise_invalid_parameters_error("Hash", "1 to 4", parameters.size) end + result.size_type = size_type if size_type + result when "class" if parameters.size != 1 @@ -283,6 +320,10 @@ class Puppet::Pops::Types::TypeParser raise_invalid_type_specification_error unless t.is_a?(Puppet::Pops::Types::PObjectType) end + def assert_range_parameter(t) + raise_invalid_type_speification_error unless t.is_a?(Integer) || t == 'default' || t == :default + end + def raise_invalid_type_specification_error raise Puppet::ParseError, "The expression <#{@string}> is not a valid type specification." diff --git a/lib/puppet/pops/types/types.rb b/lib/puppet/pops/types/types.rb index 5763b9191..e6d848648 100644 --- a/lib/puppet/pops/types/types.rb +++ b/lib/puppet/pops/types/types.rb @@ -119,22 +119,6 @@ module Puppet::Pops::Types end end - # @api public - class PStringType < PLiteralType - has_many_attr 'values', String, :lowerBound => 0, :upperBound => -1, :unique => true - - module ClassModule - - def hash - [self.class, Set.new(self.values)].hash - end - - def ==(o) - self.class == o.class && Set.new(values) == Set.new(o.values) - end - end - end - # @api public class PNumericType < PLiteralType end @@ -192,6 +176,23 @@ module Puppet::Pops::Types end end + # @api public + class PStringType < PLiteralType + has_many_attr 'values', String, :lowerBound => 0, :upperBound => -1, :unique => true + contains_one_uni 'size_type', PIntegerType + + module ClassModule + + def hash + [self.class, self.size_type, Set.new(self.values)].hash + end + + def ==(o) + self.class == o.class && self.size_type == o.size_type && Set.new(values) == Set.new(o.values) + end + end + end + # @api public class PRegexpType < PLiteralType has_attr 'pattern', String, :lowerBound => 1 @@ -239,13 +240,14 @@ module Puppet::Pops::Types # @api public class PCollectionType < PObjectType contains_one_uni 'element_type', PAbstractType + contains_one_uni 'size_type', PIntegerType module ClassModule def hash - [self.class, element_type].hash + [self.class, element_type, size].hash end def ==(o) - self.class == o.class && element_type == o.element_type + self.class == o.class && element_type == o.element_type && size_type == o.size_type end end end @@ -254,11 +256,11 @@ module Puppet::Pops::Types class PArrayType < PCollectionType module ClassModule def hash - [self.class, self.element_type].hash + [self.class, self.element_type, self.size_type].hash end def ==(o) - self.class == o.class && self.element_type == o.element_type + self.class == o.class && self.element_type == o.element_type && self.size_type == o.size_type end end end @@ -268,11 +270,14 @@ module Puppet::Pops::Types contains_one_uni 'key_type', PAbstractType module ClassModule def hash - [self.class, key_type, self.element_type].hash + [self.class, key_type, self.element_type, self.size_type].hash end def ==(o) - self.class == o.class && key_type == o.key_type && self.element_type == o.element_type + self.class == o.class && + key_type == o.key_type && + self.element_type == o.element_type && + self.size_type == o.size_type end end end diff --git a/spec/unit/pops/evaluator/access_ops_spec.rb b/spec/unit/pops/evaluator/access_ops_spec.rb index 45472c8fc..62e7e0b7d 100644 --- a/spec/unit/pops/evaluator/access_ops_spec.rb +++ b/spec/unit/pops/evaluator/access_ops_spec.rb @@ -211,11 +211,15 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do expect { evaluate(expr)}.to raise_error(/Array-Type\[\] arguments must be types/) end + # Pattern Type + # it 'creates a PPatternType instance when applied to a Pattern' do regexp_expr = fqr('Pattern')['foo'] expect(evaluate(regexp_expr)).to eql(Puppet::Pops::Types::TypeFactory.pattern('foo')) end + # Regexp Type + # it 'creates a Regexp instance when applied to a Pattern' do regexp_expr = fqr('Regexp')['foo'] expect(evaluate(regexp_expr)).to eql(Puppet::Pops::Types::TypeFactory.regexp('foo')) diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index 4ca0b7e0b..a54f2e5a8 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -546,13 +546,24 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do # Type operations (full set tested by tests covering type calculator) { - "Array[Integer]" => types.array_of(types.integer), - "Hash[Integer,Integer]" => types.hash_of(types.integer, types.integer), - "Resource[File]" => types.resource('File'), - "Resource['File']" => types.resource(types.resource('File')), - "File[foo]" => types.resource('file', 'foo'), - "File[foo, bar]" => [types.resource('file', 'foo'), types.resource('file', 'bar')], + "Array[Integer]" => types.array_of(types.integer), + "Array[Integer,1]" => types.constrain_size(types.array_of(types.integer),1, :default), + "Array[Integer,1,2]" => types.constrain_size(types.array_of(types.integer),1, 2), + "Array[Integer,Integer[1,2]]" => types.constrain_size(types.array_of(types.integer),1, 2), + "Array[Integer,Integer[1]]" => types.constrain_size(types.array_of(types.integer),1, :default), + "Hash[Integer,Integer]" => types.hash_of(types.integer, types.integer), + "Hash[Integer,Integer,1]" => types.constrain_size(types.hash_of(types.integer, types.integer),1, :default), + "Hash[Integer,Integer,1,2]" => types.constrain_size(types.hash_of(types.integer, types.integer),1, 2), + "Hash[Integer,Integer,Integer[1,2]]" => types.constrain_size(types.hash_of(types.integer, types.integer),1, 2), + "Hash[Integer,Integer,Integer[1]]" => types.constrain_size(types.hash_of(types.integer, types.integer),1, :default), + "Resource[File]" => types.resource('File'), + "Resource['File']" => types.resource(types.resource('File')), + "File[foo]" => types.resource('file', 'foo'), + "File[foo, bar]" => [types.resource('file', 'foo'), types.resource('file', 'bar')], "Pattern[a, /b/, Pattern[c], Regexp[d]]" => types.pattern('a', 'b', 'c', 'd'), + "String[1,2]" => types.constrain_size(types.string,1, 2), + "String[Integer[1,2]]" => types.constrain_size(types.string,1, 2), + "String[Integer[1]]" => types.constrain_size(types.string,1, :default), }.each do |source, result| it "should parse and evaluate the expression '#{source}' to #{result}" do parser.evaluate_string(scope, source, __FILE__).should == result @@ -581,15 +592,16 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do "Array[0]" => 'Array-Type[] arguments must be types. Got Fixnum', "Hash[0]" => 'Hash-Type[] arguments must be types. Got Fixnum', "Hash[Integer, 0]" => 'Hash-Type[] arguments must be types. Got Fixnum', - "Array[Integer,String]" => 'Array-Type[] accepts 1 argument. Got 2', - "Hash[Integer,String, Hash]" => 'Hash-Type[] accepts 1 to 2 arguments. Got 3', + "Array[Integer,1,2,3]" => 'Array-Type[] accepts 1 to 3 arguments. Got 4', + "Array[Integer,String]" => "A Type's size constraint arguments must be a single Integer type, or 1-2 integers (or default). Got a String-Type", + "Hash[Integer,String, 1,2,3]" => 'Hash-Type[] accepts 1 to 4 arguments. Got 5', "'abc'[x]" => "The value 'x' cannot be converted to Numeric", "'abc'[1.0]" => "A String[] cannot use Float where Integer is expected", "'abc'[1,2,3]" => "String supports [] with one or two arguments. Got 3", "Resource[0]" => 'First argument to Resource[] must be a resource type or a String. Got Fixnum', "Resource[a, 0]" => 'Error creating type specialization of a Resource-Type, Cannot use Fixnum where String is expected', "File[0]" => 'Error creating type specialization of a File-Type, Cannot use Fixnum where String is expected', - "String[0]" => 'Error creating type specialization of a String-Type, Cannot use Fixnum where String is expected', + "String[a]" => "A Type's size constraint arguments must be a single Integer type, or 1-2 integers (or default). Got a String", "Pattern[0]" => 'Error creating type specialization of a Pattern-Type, Cannot use Fixnum where String or Regexp or Pattern-Type or Regexp-Type is expected', "Regexp[0]" => 'Error creating type specialization of a Regexp-Type, Cannot use Fixnum where String or Regexp is expected', "Regexp[a,b]" => 'A Regexp-Type[] accepts 1 argument. Got 2', diff --git a/spec/unit/pops/types/type_calculator_spec.rb b/spec/unit/pops/types/type_calculator_spec.rb index 49fb9f069..a65f5fc2c 100644 --- a/spec/unit/pops/types/type_calculator_spec.rb +++ b/spec/unit/pops/types/type_calculator_spec.rb @@ -39,10 +39,22 @@ describe 'The type calculator' do Puppet::Pops::Types::TypeFactory.array_of(t) end + def hash_t(k,v) + Puppet::Pops::Types::TypeFactory.hash_of(v, k) + end + def data_t() Puppet::Pops::Types::TypeFactory.data() end + def factory() + Puppet::Pops::Types::TypeFactory + end + + def collection_t() + Puppet::Pops::Types::TypeFactory.collection() + end + def types Puppet::Pops::Types end @@ -763,6 +775,46 @@ describe 'The type calculator' do calculator.instance?(range, 11).should == false end + it 'should consider string in length range' do + range = factory.constrain_size(string_t, 1,3) + calculator.instance?(range, 'a').should == true + calculator.instance?(range, 'abc').should == true + calculator.instance?(range, '').should == false + calculator.instance?(range, 'abcd').should == false + end + + it 'should consider array in length range' do + range = factory.constrain_size(array_t(integer_t), 1,3) + calculator.instance?(range, [1]).should == true + calculator.instance?(range, [1,2,3]).should == true + calculator.instance?(range, []).should == false + calculator.instance?(range, [1,2,3,4]).should == false + end + + it 'should consider hash in length range' do + range = factory.constrain_size(hash_t(integer_t, integer_t), 1,2) + calculator.instance?(range, {1=>1}).should == true + calculator.instance?(range, {1=>1, 2=>2}).should == true + calculator.instance?(range, {}).should == false + calculator.instance?(range, {1=>1, 2=>2, 3=>3}).should == false + end + + it 'should consider collection in length range for array ' do + range = factory.constrain_size(collection_t, 1,3) + calculator.instance?(range, [1]).should == true + calculator.instance?(range, [1,2,3]).should == true + calculator.instance?(range, []).should == false + calculator.instance?(range, [1,2,3,4]).should == false + end + + it 'should consider collection in length range for hash' do + range = factory.constrain_size(collection_t, 1,2) + calculator.instance?(range, {1=>1}).should == true + calculator.instance?(range, {1=>1, 2=>2}).should == true + calculator.instance?(range, {}).should == false + calculator.instance?(range, {1=>1, 2=>2, 3=>3}).should == false + end + it 'should consider string matching enum as instanceof' do enum = enum_t('XS', 'S', 'M', 'L', 'XL', '0') calculator.instance?(enum, 'XS').should == true @@ -902,7 +954,7 @@ describe 'The type calculator' do int = int_T.new() int.from = 1 int.to = 1 - calculator.string(int).should == 'Integer[1]' + calculator.string(int).should == 'Integer[1, 1]' int = int_T.new() int.from = 1 int.to = 2 @@ -939,12 +991,36 @@ describe 'The type calculator' do calculator.string(string_t('a', 'b', 'c')).should == 'String' end + it 'should yield \'String\' and from/to for PStringType' do + string_T = Puppet::Pops::Types::PStringType + calculator.string(factory.constrain_size(string_T.new(), 1,1)).should == 'String[1, 1]' + calculator.string(factory.constrain_size(string_T.new(), 1,2)).should == 'String[1, 2]' + calculator.string(factory.constrain_size(string_T.new(), :default, 2)).should == 'String[default, 2]' + calculator.string(factory.constrain_size(string_T.new(), 2, :default)).should == 'String[2, default]' + end + it 'should yield \'Array[Integer]\' for PArrayType[PIntegerType]' do t = Puppet::Pops::Types::PArrayType.new() t.element_type = Puppet::Pops::Types::PIntegerType.new() calculator.string(t).should == 'Array[Integer]' end + it 'should yield \'Collection\' and from/to for PCollectionType' do + col = collection_t() + calculator.string(factory.constrain_size(col.copy, 1,1)).should == 'Collection[1, 1]' + calculator.string(factory.constrain_size(col.copy, 1,2)).should == 'Collection[1, 2]' + calculator.string(factory.constrain_size(col.copy, :default, 2)).should == 'Collection[default, 2]' + calculator.string(factory.constrain_size(col.copy, 2, :default)).should == 'Collection[2, default]' + end + + it 'should yield \'Array\' and from/to for PArrayType' do + arr = array_t(string_t) + calculator.string(factory.constrain_size(arr.copy, 1,1)).should == 'Array[String, 1, 1]' + calculator.string(factory.constrain_size(arr.copy, 1,2)).should == 'Array[String, 1, 2]' + calculator.string(factory.constrain_size(arr.copy, :default, 2)).should == 'Array[String, default, 2]' + calculator.string(factory.constrain_size(arr.copy, 2, :default)).should == 'Array[String, 2, default]' + end + it 'should yield \'Hash[String, Integer]\' for PHashType[PStringType, PIntegerType]' do t = Puppet::Pops::Types::PHashType.new() t.key_type = Puppet::Pops::Types::PStringType.new() @@ -952,6 +1028,14 @@ describe 'The type calculator' do calculator.string(t).should == 'Hash[String, Integer]' end + it 'should yield \'Hash\' and from/to for PHashType' do + hsh = hash_t(string_t, string_t) + calculator.string(factory.constrain_size(hsh.copy, 1,1)).should == 'Hash[String, String, 1, 1]' + calculator.string(factory.constrain_size(hsh.copy, 1,2)).should == 'Hash[String, String, 1, 2]' + calculator.string(factory.constrain_size(hsh.copy, :default, 2)).should == 'Hash[String, String, default, 2]' + calculator.string(factory.constrain_size(hsh.copy, 2, :default)).should == 'Hash[String, String, 2, default]' + end + it "should yield 'Class' for a PHostClassType" do t = Puppet::Pops::Types::PHostClassType.new() calculator.string(t).should == 'Class' @@ -1055,8 +1139,8 @@ describe 'The type calculator' do it "computes the common type of PType's type parameter" do int_t = Puppet::Pops::Types::PIntegerType.new() string_t = Puppet::Pops::Types::PStringType.new() - calculator.string(calculator.infer([int_t])).should == "Array[Type[Integer]]" - calculator.string(calculator.infer([int_t, string_t])).should == "Array[Type[Literal]]" + calculator.string(calculator.infer([int_t])).should == "Array[Type[Integer], 1, 1]" + calculator.string(calculator.infer([int_t, string_t])).should == "Array[Type[Literal], 2, 2]" end it 'should infer PType as the type of ruby classes' do diff --git a/spec/unit/pops/types/type_factory_spec.rb b/spec/unit/pops/types/type_factory_spec.rb index 682a97f1b..5ace7e771 100644 --- a/spec/unit/pops/types/type_factory_spec.rb +++ b/spec/unit/pops/types/type_factory_spec.rb @@ -43,6 +43,50 @@ describe 'The type factory' do Puppet::Pops::Types::TypeFactory.data().class().should == Puppet::Pops::Types::PDataType end + it 'optional() returns POptionalType' do + Puppet::Pops::Types::TypeFactory.optional().class().should == Puppet::Pops::Types::POptionalType + end + + it 'collection() returns PCollectionType' do + Puppet::Pops::Types::TypeFactory.collection().class().should == Puppet::Pops::Types::PCollectionType + end + + it 'catalog_entry() returns PCatalogEntryType' do + Puppet::Pops::Types::TypeFactory.catalog_entry().class().should == Puppet::Pops::Types::PCatalogEntryType + end + + it 'undef() returns PNilType' do + Puppet::Pops::Types::TypeFactory.undef().class().should == Puppet::Pops::Types::PNilType + end + + it 'range(to, from) returns PIntegerType' do + t = Puppet::Pops::Types::TypeFactory.range(1,2) + t.class().should == Puppet::Pops::Types::PIntegerType + t.from.should == 1 + t.to.should == 2 + end + + it 'range(default, default) returns PIntegerType' do + t = Puppet::Pops::Types::TypeFactory.range(:default,:default) + t.class().should == Puppet::Pops::Types::PIntegerType + t.from.should == nil + t.to.should == nil + end + + it 'float_range(to, from) returns PFloatType' do + t = Puppet::Pops::Types::TypeFactory.float_range(1.0, 2.0) + t.class().should == Puppet::Pops::Types::PFloatType + t.from.should == 1.0 + t.to.should == 2.0 + end + + it 'float_range(default, default) returns PFloatType' do + t = Puppet::Pops::Types::TypeFactory.float_range(:default, :default) + t.class().should == Puppet::Pops::Types::PFloatType + t.from.should == nil + t.to.should == nil + end + it 'resource() creates a generic PResourceType' do pr = Puppet::Pops::Types::TypeFactory.resource() pr.class().should == Puppet::Pops::Types::PResourceType @@ -97,5 +141,21 @@ describe 'The type factory' do ht.class().should == Puppet::Pops::Types::PRubyType ht.ruby_class.should == 'Fixnum' end + + it 'a size constrained collection can be created from array' do + t = Puppet::Pops::Types::TypeFactory.array_of_data() + Puppet::Pops::Types::TypeFactory.constrain_size(t, 1,2).should == t + t.size_type.class.should == Puppet::Pops::Types::PIntegerType + t.size_type.from.should == 1 + t.size_type.to.should == 2 + end + + it 'a size constrained collection can be created from hash' do + t = Puppet::Pops::Types::TypeFactory.hash_of_data() + Puppet::Pops::Types::TypeFactory.constrain_size(t, 1,2).should == t + t.size_type.class.should == Puppet::Pops::Types::PIntegerType + t.size_type.from.should == 1 + t.size_type.to.should == 2 + end end end diff --git a/spec/unit/pops/types/type_parser_spec.rb b/spec/unit/pops/types/type_parser_spec.rb index 4751de056..3aba8d89e 100644 --- a/spec/unit/pops/types/type_parser_spec.rb +++ b/spec/unit/pops/types/type_parser_spec.rb @@ -75,9 +75,29 @@ describe Puppet::Pops::Types::TypeParser do expect(the_type_parsed_from(parameterized_hash)).to be_the_type(parameterized_hash) end - it "rejects an array spec with the wrong number of parameters" do - expect { parser.parse("Array[Integer, Integer]") }.to raise_the_parameter_error("Array", 1, 2) - expect { parser.parse("Hash[Integer, Integer, Integer]") }.to raise_the_parameter_error("Hash", "1 or 2", 3) + it "parses a size constrained collection using capped range" do + parameterized_array = types.array_of(types.integer) + types.constrain_size(parameterized_array, 1,2) + parameterized_hash = types.hash_of(types.integer, types.boolean) + types.constrain_size(parameterized_hash, 1,2) + + expect(the_type_parsed_from(parameterized_array)).to be_the_type(parameterized_array) + expect(the_type_parsed_from(parameterized_hash)).to be_the_type(parameterized_hash) + end + + it "parses a size constrained collection with open range" do + parameterized_array = types.array_of(types.integer) + types.constrain_size(parameterized_array, 1,:default) + parameterized_hash = types.hash_of(types.integer, types.boolean) + types.constrain_size(parameterized_hash, 1,:default) + + expect(the_type_parsed_from(parameterized_array)).to be_the_type(parameterized_array) + expect(the_type_parsed_from(parameterized_hash)).to be_the_type(parameterized_hash) + end + + it "rejects an collection spec with the wrong number of parameters" do + expect { parser.parse("Array[Integer, 1,2,3]") }.to raise_the_parameter_error("Array", "1 to 3", 4) + expect { parser.parse("Hash[Integer, Integer, 1,2,3]") }.to raise_the_parameter_error("Hash", "1 to 4", 5) end it "interprets anything that is not a built in type to be a resource type" do From c320f65802ee83d4c1976dec46ae89a207064a12 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 27 Dec 2013 09:42:05 -0800 Subject: [PATCH 296/800] (maint) Require json for logstash test Removal of the hiera2 bindings code exposed the fact that the test for logstash logging doesn't require JSON even though it uses it. This adds a line to require JSON so that it is available when the test runs. --- spec/unit/util/log/destinations_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/unit/util/log/destinations_spec.rb b/spec/unit/util/log/destinations_spec.rb index b34b973cf..a91236dba 100755 --- a/spec/unit/util/log/destinations_spec.rb +++ b/spec/unit/util/log/destinations_spec.rb @@ -1,5 +1,6 @@ #! /usr/bin/env ruby require 'spec_helper' +require 'json' require 'puppet/util/log' From 46f330073399cb4887acd739244399be6806c160 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 27 Dec 2013 10:33:57 -0800 Subject: [PATCH 297/800] (PUP-672) Add nested context capabilities A major problem in puppet is keeping track of contextual information. The Puppet::Indirector::Request attempts to do this, but it isn't able to pass that information between indirector requests. This adds a global context system, where once a value is set it cannot be changed, but a subcontext can be created. --- lib/puppet.rb | 1 + lib/puppet/context.rb | 60 +++++++++++++++++++++++++++++++ spec/unit/context_spec.rb | 74 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+) create mode 100644 lib/puppet/context.rb create mode 100644 spec/unit/context_spec.rb diff --git a/lib/puppet.rb b/lib/puppet.rb index aa47362f8..427290339 100644 --- a/lib/puppet.rb +++ b/lib/puppet.rb @@ -182,3 +182,4 @@ require 'puppet/data_binding' require 'puppet/util/storage' require 'puppet/status' require 'puppet/file_bucket/file' +require 'puppet/context' diff --git a/lib/puppet/context.rb b/lib/puppet/context.rb new file mode 100644 index 000000000..40a814086 --- /dev/null +++ b/lib/puppet/context.rb @@ -0,0 +1,60 @@ +module Puppet::Context + class ValueAlreadyBoundError < Puppet::Error; end + class UndefinedBindingError < Puppet::Error; end + + class Bindings + attr_reader :parent + + def initialize(parent) + @parent = parent + @table = {} + end + + def bind(name, value) + if @table.include?(name) + raise ValueAlreadyBoundError, name + else + @table[name] = value + end + end + + def lookup(name) + if @table.include?(name) + @table[name] + elsif @parent + @parent.lookup(name) + else + raise UndefinedBindingError, name + end + end + end + + @bindings = Bindings.new(nil) + + def self.push + @bindings = Bindings.new(@bindings) + end + + def self.pop + @bindings = @bindings.parent + end + + def self.bind(name, value) + @bindings.bind(name, value) + end + + def self.lookup(name) + @bindings.lookup(name) + end + + def self.override(bindings) + push + bindings.each do |name, value| + bind(name, value) + end + + yield + ensure + pop + end +end diff --git a/spec/unit/context_spec.rb b/spec/unit/context_spec.rb new file mode 100644 index 000000000..4074daaf6 --- /dev/null +++ b/spec/unit/context_spec.rb @@ -0,0 +1,74 @@ +require 'spec_helper' + +describe Puppet::Context do + before :each do + Puppet::Context.push + end + + after :each do + Puppet::Context.pop + end + + it "holds values for later lookup" do + Puppet::Context.bind("a", 1) + + expect(Puppet::Context.lookup("a")).to eq(1) + end + + it "does not allow a value to be re-set" do + Puppet::Context.bind("a", 1) + + expect { Puppet::Context.bind("a", 1) }.to raise_error(Puppet::Context::ValueAlreadyBoundError) + end + + it "fails to lookup a value that does not exist" do + expect { Puppet::Context.lookup("a") }.to raise_error(Puppet::Context::UndefinedBindingError) + end + + it "allows rebinding values in a nested context" do + Puppet::Context.bind("a", 1) + + inner = nil + Puppet::Context.override("a" => 2) do + inner = Puppet::Context.lookup("a") + end + + expect(inner).to eq(2) + end + + it "outer bindings are available in an overridden context" do + Puppet::Context.bind("a", 1) + + inner_a = nil + inner_b = nil + Puppet::Context.override("b" => 2) do + inner_a = Puppet::Context.lookup("a") + inner_b = Puppet::Context.lookup("b") + end + + expect(inner_a).to eq(1) + expect(inner_b).to eq(2) + end + + it "overridden bindings do not exist outside of the override" do + Puppet::Context.bind("a", 1) + + Puppet::Context.override("a" => 2) do + end + + expect(Puppet::Context.lookup("a")).to eq(1) + end + + it "overridden bindings do not exist outside of the override even when leaving via an error" do + Puppet::Context.bind("a", 1) + + begin + Puppet::Context.override("a" => 2) do + raise "this should still cause the bindings to leave" + end + rescue + end + + expect(Puppet::Context.lookup("a")).to eq(1) + end +end From 8841654187b0101316264a10273ce3af812fbbd6 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 27 Dec 2013 11:50:04 -0800 Subject: [PATCH 298/800] (maint) Log exception using log_exception The master application was not using Puppet.log_exception to log out any errors that might occur during --compile. This meant that there was no backtrace information available to diagnose the problem. --- lib/puppet/application/master.rb | 2 +- spec/unit/application/master_spec.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/puppet/application/master.rb b/lib/puppet/application/master.rb index b8f27994f..0c6a780a7 100644 --- a/lib/puppet/application/master.rb +++ b/lib/puppet/application/master.rb @@ -170,7 +170,7 @@ Copyright (c) 2012 Puppet Labs, LLC Licensed under the Apache 2.0 License puts PSON::pretty_generate(catalog.to_resource, :allow_nan => true, :max_nesting => false) rescue => detail - $stderr.puts detail + Puppet.log_exception(detail, "Failed to compile catalog for node #{options[:node]}: #{detail}") exit(30) end exit(0) diff --git a/spec/unit/application/master_spec.rb b/spec/unit/application/master_spec.rb index d7f26b74a..4d3167ce8 100755 --- a/spec/unit/application/master_spec.rb +++ b/spec/unit/application/master_spec.rb @@ -252,14 +252,14 @@ describe Puppet::Application::Master, :unless => Puppet.features.microsoft_windo it "should exit with error code 30 if no catalog can be found" do @master.options[:node] = "foo" Puppet::Resource::Catalog.indirection.expects(:find).returns nil - $stderr.expects(:puts) + Puppet.expects(:log_exception) expect { @master.compile }.to exit_with 30 end it "should exit with error code 30 if there's a failure" do @master.options[:node] = "foo" Puppet::Resource::Catalog.indirection.expects(:find).raises ArgumentError - $stderr.expects(:puts) + Puppet.expects(:log_exception) expect { @master.compile }.to exit_with 30 end end From 33839964794557886310a675b4b9393c6d6328bd Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 27 Dec 2013 12:00:03 -0800 Subject: [PATCH 299/800] (PUP-672) Add defaults for context lookups Before it wasn't possible to get a value from the context that might not have the requested binding. This adds the ability to provide a default in the case where there is no finding for the name. --- lib/puppet/context.rb | 10 ++++++---- spec/unit/context_spec.rb | 4 ++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/puppet/context.rb b/lib/puppet/context.rb index 40a814086..37e2de4b4 100644 --- a/lib/puppet/context.rb +++ b/lib/puppet/context.rb @@ -18,11 +18,13 @@ module Puppet::Context end end - def lookup(name) + def lookup(name, block) if @table.include?(name) @table[name] elsif @parent - @parent.lookup(name) + @parent.lookup(name, block) + elsif block + block.call else raise UndefinedBindingError, name end @@ -43,8 +45,8 @@ module Puppet::Context @bindings.bind(name, value) end - def self.lookup(name) - @bindings.lookup(name) + def self.lookup(name, &block) + @bindings.lookup(name, block) end def self.override(bindings) diff --git a/spec/unit/context_spec.rb b/spec/unit/context_spec.rb index 4074daaf6..d35abbf49 100644 --- a/spec/unit/context_spec.rb +++ b/spec/unit/context_spec.rb @@ -25,6 +25,10 @@ describe Puppet::Context do expect { Puppet::Context.lookup("a") }.to raise_error(Puppet::Context::UndefinedBindingError) end + it "calls a provided block for a default value when none is found" do + expect(Puppet::Context.lookup("a") { "default" }).to eq("default") + end + it "allows rebinding values in a nested context" do Puppet::Context.bind("a", 1) From a80015acf74b067760f9cbafc8cd23bc5b54306f Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 27 Dec 2013 12:03:55 -0800 Subject: [PATCH 300/800] (PUP-672) Track trusted information using the context This pulls apart the indirector request and the trusted information. The trusted information is stored in the Puppet::Context and overridden by the remote request handler to allow the injection of various elements of trusted information. The reason to separate it from the indirector request is because the indirector requests are hard to control, as they are contstructed from hashes of information passed around and manipulated. This should provide a more straightforward mechanism for managing this kind of "contextual" information. --- lib/puppet/indirector/catalog/compiler.rb | 29 +------------- lib/puppet/indirector/request.rb | 4 +- lib/puppet/indirector/trusted_information.rb | 40 ++++++++++++++++++++ lib/puppet/network/http/handler.rb | 5 ++- lib/puppet/test/test_helper.rb | 5 +++ spec/unit/network/http/webrick/rest_spec.rb | 2 +- 6 files changed, 54 insertions(+), 31 deletions(-) create mode 100644 lib/puppet/indirector/trusted_information.rb diff --git a/lib/puppet/indirector/catalog/compiler.rb b/lib/puppet/indirector/catalog/compiler.rb index b2a3c251a..d6223eba0 100644 --- a/lib/puppet/indirector/catalog/compiler.rb +++ b/lib/puppet/indirector/catalog/compiler.rb @@ -41,7 +41,7 @@ class Puppet::Resource::Catalog::Compiler < Puppet::Indirector::Code extract_facts_from_request(request) node = node_from_request(request) - node.trusted_data = trusted_hash_from_request(request) + node.trusted_data = Puppet::Context.lookup(:trusted_information) { Puppet::Indirector::TrustedInformation.local(node) }.to_h if catalog = compile(node) return catalog @@ -52,7 +52,6 @@ class Puppet::Resource::Catalog::Compiler < Puppet::Indirector::Code end end - # filter-out a catalog to remove exported resources def filter(catalog) return catalog.filter { |r| r.virtual? } if catalog.respond_to?(:filter) @@ -72,32 +71,6 @@ class Puppet::Resource::Catalog::Compiler < Puppet::Indirector::Code private - # Produces a deeply frozen hash with trusted information - # The key :authenticated is always present in the result with one of the values - # :remote, :local, false, where :remote is authenticated via cert, :local is trusted by virtue - # of running on the same machine (not a remove request), and false is an unauthenticated remot request. - # When the trusted hash value for :authenticated == false, there is no other values set in the hash. - # - def trusted_hash_from_request(request) - if request.remote? - if request.authenticated? - trust_authenticated = 'remote'.freeze - client_cert = request.node - else - trust_authenticated = false - client_cert = nil - end - else - trust_authenticated = 'local'.freeze - # Always trust local data by picking up the available parameters. - request_node = request.options[:use_node] - client_cert = request_node ? request_node.parameters['clientcert'] : nil - end - - # TODO nil or undef for client_cert missing? - trusted_hash = { 'authenticated' => trust_authenticated, 'certname' => client_cert }.freeze - end - # Add any extra data necessary to the node. def add_node_data(node) # Merge in our server-side facts, so they can be used during compilation. diff --git a/lib/puppet/indirector/request.rb b/lib/puppet/indirector/request.rb index 4eacee11b..ec9952df4 100644 --- a/lib/puppet/indirector/request.rb +++ b/lib/puppet/indirector/request.rb @@ -3,6 +3,7 @@ require 'uri' require 'puppet/indirector' require 'puppet/util/pson' require 'puppet/network/resolver' +require 'puppet/indirector/trusted_information' # This class encapsulates all of the information you need to make an # Indirection call, and as a result also handles REST calls. It's somewhat @@ -14,6 +15,8 @@ class Puppet::Indirector::Request attr_reader :indirection_name + # trusted_information is specifically left out because we can't serialize it + # and keep it "trusted" OPTION_ATTRIBUTES = [:ip, :node, :authenticated, :ignore_terminus, :ignore_cache, :instance, :environment] ::PSON.register_document_type('IndirectorRequest',self) @@ -142,7 +145,6 @@ class Puppet::Indirector::Request @indirection_name = name.to_sym end - def model raise ArgumentError, "Could not find indirection '#{indirection_name}'" unless i = indirection i.model diff --git a/lib/puppet/indirector/trusted_information.rb b/lib/puppet/indirector/trusted_information.rb new file mode 100644 index 000000000..f88a9e0be --- /dev/null +++ b/lib/puppet/indirector/trusted_information.rb @@ -0,0 +1,40 @@ +class Puppet::Indirector::TrustedInformation + # one of 'remote', 'local', or false, where 'remote' is authenticated via cert, + # 'local' is trusted by virtue of running on the same machine (not a remove + # request), and false is an unauthenticated remote request. + # + # @return [String, Boolean] + attr_reader :authenticated + + # The validated certificate name used for the request + # + # @return [String] + attr_reader :certname + + def initialize(authenticated, certname) + @authenticated = authenticated.freeze + @certname = certname.freeze + end + + def self.remote(authenticated, node_name) + if authenticated + new('remote', node_name) + else + new(false, nil) + end + end + + def self.local(node) + # Always trust local data by picking up the available parameters. + client_cert = node ? node.parameters['clientcert'] : nil + + new('local', client_cert) + end + + def to_h + { + 'authenticated'.freeze => authenticated, + 'certname'.freeze => certname + }.freeze + end +end diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb index 3b86195d0..1a9371ef6 100644 --- a/lib/puppet/network/http/handler.rb +++ b/lib/puppet/network/http/handler.rb @@ -101,7 +101,10 @@ module Puppet::Network::HTTP::Handler raise HTTPNotFoundError, "No handler for #{indirection.name}" end - send("do_#{method}", indirection, key, params, request, response) + trusted = Puppet::Indirector::TrustedInformation.remote(params[:authenticated], params[:node]) + Puppet::Context.override(:trusted_information => trusted) do + send("do_#{method}", indirection, key, params, request, response) + end end rescue HTTPError => e return do_http_control_exception(response, e) diff --git a/lib/puppet/test/test_helper.rb b/lib/puppet/test/test_helper.rb index f80e699e7..27dec691c 100644 --- a/lib/puppet/test/test_helper.rb +++ b/lib/puppet/test/test_helper.rb @@ -53,6 +53,7 @@ module Puppet::Test # Call this method once per test, prior to execution of each invididual test. # @return nil def self.before_each_test() + Puppet::Context.push # 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. @@ -91,6 +92,9 @@ module Puppet::Test Puppet.clear_deprecation_warnings Puppet::DataBinding::Hiera.instance_variable_set("@hiera", nil) + + Puppet::Context.bind(:trusted_information, + Puppet::Indirector::TrustedInformation.new('local', 'testing')) end # Call this method once per test, after execution of each individual test. @@ -137,6 +141,7 @@ module Puppet::Test $LOAD_PATH.clear $old_load_path.each {|x| $LOAD_PATH << x } + Puppet::Context.pop end diff --git a/spec/unit/network/http/webrick/rest_spec.rb b/spec/unit/network/http/webrick/rest_spec.rb index 05747bede..10ffe7a16 100755 --- a/spec/unit/network/http/webrick/rest_spec.rb +++ b/spec/unit/network/http/webrick/rest_spec.rb @@ -264,6 +264,6 @@ describe Puppet::Network::HTTP::WEBrickREST do @handler.params(@request)[:node].should == :resolved_node end - end + end end end From 39d630f57b78f2fc4d8905cd5cf015d58bf70efa Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Thu, 26 Dec 2013 18:50:08 -0800 Subject: [PATCH 301/800] (PUP-798) E4ParserAdapter watches files through Environment Instead of passing known_resource_types, if present, pass the given environment, and provide a delegate method to watch_file on Environment. Clears up a few layers of type checking and does not require the adapter to know anything about known_resource_types for the purpose of watching files, but a valid environment must be passed. --- lib/puppet/node/environment.rb | 7 +++++++ lib/puppet/parser/e4_parser_adapter.rb | 14 +++++++++++--- lib/puppet/parser/parser_factory.rb | 4 +--- spec/unit/node/environment_spec.rb | 18 +++++++++++++++++- 4 files changed, 36 insertions(+), 7 deletions(-) diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb index b3ea64ab7..301771e7e 100644 --- a/lib/puppet/node/environment.rb +++ b/lib/puppet/node/environment.rb @@ -393,6 +393,13 @@ class Puppet::Node::Environment deps end + # Set a periodic watcher on the file, so we can tell if it has changed. + # @param filename [File,String] File instance or filename + # @api private + def watch_file(file) + known_resource_types.watch_file(file.to_s) + end + # @return [String] The stringified value of the `name` instance variable # @api public def to_s diff --git a/lib/puppet/parser/e4_parser_adapter.rb b/lib/puppet/parser/e4_parser_adapter.rb index c0bfaf236..01822db13 100644 --- a/lib/puppet/parser/e4_parser_adapter.rb +++ b/lib/puppet/parser/e4_parser_adapter.rb @@ -6,9 +6,17 @@ module Puppet; module Parser; end; end; # class Puppet::Parser::E4ParserAdapter - # @param file_watcher [#watch_file] something that can watch a file + # Empty adapter fulfills watch_file contract without doing anything. + # @api private + class NullFileWatcher + def watch_file(file) + #nop + end + end + + # @param file_watcher [#watch_file] something that can watch a file def initialize(file_watcher = nil) - @file_watcher = file_watcher + @file_watcher = file_watcher || NullFileWatcher.new @file = '' @string = '' @use = :undefined @@ -19,7 +27,7 @@ class Puppet::Parser::E4ParserAdapter @file = file @use = :file # watch if possible, but only if the file is something worth watching - @file_watcher.watch_file(file) if @file_watcher.respond_to?(:watch_file) && !file.nil? && file != '' + @file_watcher.watch_file(file) if !file.nil? && file != '' end def parse(string = nil) diff --git a/lib/puppet/parser/parser_factory.rb b/lib/puppet/parser/parser_factory.rb index 38d9a205e..54bd9f7a4 100644 --- a/lib/puppet/parser/parser_factory.rb +++ b/lib/puppet/parser/parser_factory.rb @@ -11,9 +11,7 @@ module Puppet::Parser def self.parser(environment) case Puppet[:parser] when 'future' - # must check if the given environment is real or just a string, in which case watching is not supported - evaluating_parser(environment.respond_to?(:known_resource_types) ? environment.known_resource_types : nil) -# eparser(environment) + evaluating_parser(environment) else classic_parser(environment) end diff --git a/spec/unit/node/environment_spec.rb b/spec/unit/node/environment_spec.rb index d9ded4ef2..9fd149f18 100755 --- a/spec/unit/node/environment_spec.rb +++ b/spec/unit/node/environment_spec.rb @@ -47,6 +47,21 @@ describe Puppet::Node::Environment do Puppet::Node::Environment.new(one).should equal(one) end + describe "watching a file" do + let(:filename) { "filename" } + + it "accepts a File" do + file = tmpfile(filename) + env.known_resource_types.expects(:watch_file).with(file.to_s) + env.watch_file(file) + end + + it "accepts a String" do + env.known_resource_types.expects(:watch_file).with(filename) + env.watch_file(filename) + end + end + describe "when managing known resource types" do before do @collection = Puppet::Resource::TypeCollection.new(env) @@ -387,8 +402,9 @@ describe Puppet::Node::Environment do end describe "when performing initial import" do + let(:env) { Puppet::Node::Environment.new("test") } before do - @parser = Puppet::Parser::ParserFactory.parser("test") + @parser = Puppet::Parser::ParserFactory.parser(env) # @parser = Puppet::Parser::EParserAdapter.new(Puppet::Parser::Parser.new("test")) # TODO: FIX PARSER FACTORY Puppet::Parser::ParserFactory.stubs(:parser).returns @parser end From 9acfb98b23a2bba1c0ea07de276b112cd18b6046 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Thu, 26 Dec 2013 19:41:12 -0800 Subject: [PATCH 302/800] (maint) Uniform use of TypeCollectionHelper in Scope Scope includes TypeCollectionHelper which provides an accessor for known_resource_types. This patch just fixes a direct reference to environment.known_resource_types to instead use the delegate method the same as other known_resource_types calls in the Scope class. --- lib/puppet/parser/scope.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb index 9641b41fe..ecf5ca94d 100644 --- a/lib/puppet/parser/scope.rb +++ b/lib/puppet/parser/scope.rb @@ -716,7 +716,7 @@ class Puppet::Parser::Scope end def find_defined_resource_type(type) - environment.known_resource_types.find_definition(namespaces, type.to_s.downcase) + known_resource_types.find_definition(namespaces, type.to_s.downcase) end From a706fa30ae6be5dd5fcc3f539f31aa26cbccf42b Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 27 Dec 2013 15:40:44 -0800 Subject: [PATCH 303/800] (PUP-672) Provide certificate extensions as trusted data This extends the trusted information that is exposed to the system and manifests to include the certificate extensions that are part of the certificate provided by the agent. Only the custom extensions that are part of the puppet extension arc are included. --- lib/puppet/indirector/trusted_information.rb | 22 +++- lib/puppet/network/http/handler.rb | 5 +- lib/puppet/network/http/rack/rest.rb | 7 +- lib/puppet/network/http/webrick/rest.rb | 6 +- lib/puppet/test/test_helper.rb | 2 +- .../indirector/trusted_information_spec.rb | 112 ++++++++++++++++++ spec/unit/network/http/rack/rest_spec.rb | 28 +++-- 7 files changed, 157 insertions(+), 25 deletions(-) create mode 100644 spec/unit/indirector/trusted_information_spec.rb diff --git a/lib/puppet/indirector/trusted_information.rb b/lib/puppet/indirector/trusted_information.rb index f88a9e0be..87717bb12 100644 --- a/lib/puppet/indirector/trusted_information.rb +++ b/lib/puppet/indirector/trusted_information.rb @@ -11,16 +11,25 @@ class Puppet::Indirector::TrustedInformation # @return [String] attr_reader :certname - def initialize(authenticated, certname) + # Extra information that comes from the trusted certificate's extensions. + # + # @return [Hash{Object => Object}] + attr_reader :extensions + + def initialize(authenticated, certname, extensions) @authenticated = authenticated.freeze @certname = certname.freeze + @extensions = extensions.freeze end - def self.remote(authenticated, node_name) + def self.remote(authenticated, node_name, certificate) if authenticated - new('remote', node_name) + extensions = Hash[certificate.custom_extensions.collect do |ext| + [ext['oid'].freeze, ext['value'].freeze] + end] + new('remote', node_name, extensions) else - new(false, nil) + new(false, nil, {}) end end @@ -28,13 +37,14 @@ class Puppet::Indirector::TrustedInformation # Always trust local data by picking up the available parameters. client_cert = node ? node.parameters['clientcert'] : nil - new('local', client_cert) + new('local', client_cert, {}) end def to_h { 'authenticated'.freeze => authenticated, - 'certname'.freeze => certname + 'certname'.freeze => certname, + 'extensions'.freeze => extensions }.freeze end end diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb index 1a9371ef6..af9593795 100644 --- a/lib/puppet/network/http/handler.rb +++ b/lib/puppet/network/http/handler.rb @@ -90,9 +90,10 @@ module Puppet::Network::HTTP::Handler Puppet::Util::Profiler.profile("Processed request #{request_method} #{request_path}") do indirection_name, method, key, params = uri2indirection(request_method, request_path, request_params) + certificate = client_cert(request) check_authorization(indirection_name, method, key, params) - warn_if_near_expiration(client_cert(request)) + warn_if_near_expiration(certificate) indirection = Puppet::Indirector::Indirection.instance(indirection_name.to_sym) raise ArgumentError, "Could not find indirection '#{indirection_name}'" unless indirection @@ -101,7 +102,7 @@ module Puppet::Network::HTTP::Handler raise HTTPNotFoundError, "No handler for #{indirection.name}" end - trusted = Puppet::Indirector::TrustedInformation.remote(params[:authenticated], params[:node]) + trusted = Puppet::Indirector::TrustedInformation.remote(params[:authenticated], params[:node], certificate) Puppet::Context.override(:trusted_information => trusted) do send("do_#{method}", indirection, key, params, request, response) end diff --git a/lib/puppet/network/http/rack/rest.rb b/lib/puppet/network/http/rack/rest.rb index 83d61866b..c3cf40eb4 100644 --- a/lib/puppet/network/http/rack/rest.rb +++ b/lib/puppet/network/http/rack/rest.rb @@ -102,8 +102,11 @@ class Puppet::Network::HTTP::RackREST < Puppet::Network::HTTP::RackHttpHandler cert = request.env['SSL_CLIENT_CERT'] # NOTE: The SSL_CLIENT_CERT environment variable will be the empty string # when Puppet agent nodes have not yet obtained a signed certificate. - return nil if cert.nil? or cert.empty? - OpenSSL::X509::Certificate.new(cert) + if cert.nil? || cert.empty? + nil + else + Puppet::SSL::Certificate.from_instance(OpenSSL::X509::Certificate.new(cert)) + end end # Passenger freaks out if we finish handling the request without reading any diff --git a/lib/puppet/network/http/webrick/rest.rb b/lib/puppet/network/http/webrick/rest.rb index 0ed3db36c..d25c388de 100644 --- a/lib/puppet/network/http/webrick/rest.rb +++ b/lib/puppet/network/http/webrick/rest.rb @@ -60,7 +60,11 @@ class Puppet::Network::HTTP::WEBrickREST < WEBrick::HTTPServlet::AbstractServlet end def client_cert(request) - request.client_cert + if cert = request.client_cert + Puppet::SSL::Certificate.from_instance(cert) + else + nil + end end # Set the specified format as the content type of the response. diff --git a/lib/puppet/test/test_helper.rb b/lib/puppet/test/test_helper.rb index 27dec691c..32ce90cfb 100644 --- a/lib/puppet/test/test_helper.rb +++ b/lib/puppet/test/test_helper.rb @@ -94,7 +94,7 @@ module Puppet::Test Puppet::DataBinding::Hiera.instance_variable_set("@hiera", nil) Puppet::Context.bind(:trusted_information, - Puppet::Indirector::TrustedInformation.new('local', 'testing')) + Puppet::Indirector::TrustedInformation.new('local', 'testing', {})) end # Call this method once per test, after execution of each individual test. diff --git a/spec/unit/indirector/trusted_information_spec.rb b/spec/unit/indirector/trusted_information_spec.rb new file mode 100644 index 000000000..2ec74071f --- /dev/null +++ b/spec/unit/indirector/trusted_information_spec.rb @@ -0,0 +1,112 @@ +require 'spec_helper' + +describe Puppet::Indirector::TrustedInformation do + let(:key) do + key = Puppet::SSL::Key.new("myname") + key.generate + key + end + + let(:csr) do + csr = Puppet::SSL::CertificateRequest.new("csr") + csr.generate(key, :extension_requests => { + '1.3.6.1.4.1.15.1.2.1' => 'Ignored CSR extension', + + '1.3.6.1.4.1.34380.1.2.1' => 'CSR specific info', + '1.3.6.1.4.1.34380.1.2.2' => 'more CSR specific info', + }) + csr + end + + let(:cert) do + Puppet::SSL::Certificate.from_instance(Puppet::SSL::CertificateFactory.build('ca', csr, csr.content, 1)) + end + + context "when remote" do + it "has no cert information when it isn't authenticated" do + trusted = Puppet::Indirector::TrustedInformation.remote(false, 'ignored', nil) + + expect(trusted.authenticated).to eq(false) + expect(trusted.certname).to be_nil + expect(trusted.extensions).to eq({}) + end + + it "is remote and has certificate information when it is authenticated" do + trusted = Puppet::Indirector::TrustedInformation.remote(true, 'cert name', cert) + + expect(trusted.authenticated).to eq('remote') + expect(trusted.certname).to eq('cert name') + expect(trusted.extensions).to eq({ + '1.3.6.1.4.1.34380.1.2.1' => 'CSR specific info', + '1.3.6.1.4.1.34380.1.2.2' => 'more CSR specific info', + }) + end + end + + context "when local" do + it "is authenticated local with the nodes clientcert" do + node = Puppet::Node.new('testing', :parameters => { 'clientcert' => 'cert name' }) + + trusted = Puppet::Indirector::TrustedInformation.local(node) + + expect(trusted.authenticated).to eq('local') + expect(trusted.certname).to eq('cert name') + end + + it "is authenticated local with no clientcert when there is no node" do + trusted = Puppet::Indirector::TrustedInformation.local(nil) + + expect(trusted.authenticated).to eq('local') + expect(trusted.certname).to be_nil + end + end + + it "converts itself to a hash" do + trusted = Puppet::Indirector::TrustedInformation.remote(true, 'cert name', cert) + + expect(trusted.to_h).to eq({ + 'authenticated' => 'remote', + 'certname' => 'cert name', + 'extensions' => { + '1.3.6.1.4.1.34380.1.2.1' => 'CSR specific info', + '1.3.6.1.4.1.34380.1.2.2' => 'more CSR specific info', + } + }) + end + + it "freezes the hash" do + trusted = Puppet::Indirector::TrustedInformation.remote(true, 'cert name', cert) + + expect(trusted.to_h).to be_deeply_frozen + end + + matcher :be_deeply_frozen do + match do |actual| + unfrozen_items(actual).empty? + end + + failure_message_for_should do |actual| + "expected all items to be frozen but <#{unfrozen_items(actual).join(', ')}> was not" + end + + define_method :unfrozen_items do |actual| + unfrozen = [] + stack = [actual] + while item = stack.pop + if !item.frozen? + unfrozen.push(item) + end + + case item + when Hash + stack.concat(item.keys) + stack.concat(item.values) + when Array + stack.concat(item) + end + end + + unfrozen + end + end +end diff --git a/spec/unit/network/http/rack/rest_spec.rb b/spec/unit/network/http/rack/rest_spec.rb index e9527a460..82e49f153 100755 --- a/spec/unit/network/http/rack/rest_spec.rb +++ b/spec/unit/network/http/rack/rest_spec.rb @@ -31,6 +31,17 @@ describe "Puppet::Network::HTTP::RackREST", :if => Puppet.features.rack? do Rack::Request.new(env) end + let(:minimal_certificate) do + cert = OpenSSL::X509::Certificate.new + cert.version = 2 + cert.serial = 0 + cert.not_before = Time.now + cert.not_after = Time.now + 3600 + cert.public_key = OpenSSL::PKey::RSA.new(512) + cert.subject = OpenSSL::X509::Name.parse("/CN=testing") + cert + end + describe "#headers" do it "should return the headers (parsed from env with prefix 'HTTP_')" do req = mk_req('/', {'HTTP_Accept' => 'myaccept', @@ -67,24 +78,15 @@ describe "Puppet::Network::HTTP::RackREST", :if => Puppet.features.rack? do @handler.body(req).should == "mybody" end - it "should return the an OpenSSL::X509::Certificate instance as the client_cert" do - cert = stub 'cert' - req = mk_req('/foo/bar', 'SSL_CLIENT_CERT' => 'certificate in pem format') - OpenSSL::X509::Certificate.expects(:new).with('certificate in pem format').returns(cert) - @handler.client_cert(req).should == cert + it "should return the an Puppet::SSL::Certificate instance as the client_cert" do + req = mk_req('/foo/bar', 'SSL_CLIENT_CERT' => minimal_certificate.to_pem) + expect(@handler.client_cert(req).content.to_pem).to eq(minimal_certificate.to_pem) end it "returns nil when SSL_CLIENT_CERT is empty" do - cert = stub 'cert' req = mk_req('/foo/bar', 'SSL_CLIENT_CERT' => '') - OpenSSL::X509::Certificate.expects(:new).never - @handler.client_cert(req).should be_nil - end - it "(#16769) does not raise error 'header too long'" do - cert = stub 'cert' - req = mk_req('/foo/bar', 'SSL_CLIENT_CERT' => '') - lambda { @handler.client_cert(req) }.should_not raise_error + @handler.client_cert(req).should be_nil end it "should set the response's content-type header when setting the content type" do From 5f7fc89e7cc411f5e41f8692e3c2e3b922c6288f Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 27 Dec 2013 15:58:33 -0800 Subject: [PATCH 304/800] (PUP-672) Updated test to match trusted data structure During implementation of the trusted extensions the layout of the trusted hash changed a little bit. This updates the acceptance test to reflect that and also changes it to be much more straightforward to read for the expected structure. --- acceptance/tests/ssl/certificate_extensions.rb | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/acceptance/tests/ssl/certificate_extensions.rb b/acceptance/tests/ssl/certificate_extensions.rb index aabfb06a9..bb96d134f 100644 --- a/acceptance/tests/ssl/certificate_extensions.rb +++ b/acceptance/tests/ssl/certificate_extensions.rb @@ -57,10 +57,18 @@ test_name "certificate extensions available as trusted data" do :acceptable_exit_codes => [0, 2]) trusted_data = YAML.load(on(agent, "cat #{get_test_file_path(agent, 'trusted.yaml')}").stdout) - assert_equal('b5e63090-5167-11e3-8f96-0800200c9a66', trusted_data['pp_uuid']) - assert_equal('i-3fkva', trusted_data['pp_instance_id']) - assert_equal('db-server', trusted_data['1.3.6.1.4.1.34380.1.2.1']) - assert_equal('webops', trusted_data['1.3.6.1.4.1.34380.1.2.2']) + + assert_equal({ + 'authenticated' => 'remote', + 'certname' => fact_on(agent, 'fqdn'), + 'extensions' => { + 'pp_uuid' => 'b5e63090-5167-11e3-8f96-0800200c9a66', + 'pp_instance_id' => 'i-3fkva', + '1.3.6.1.4.1.34380.1.2.1' => 'db-server', + '1.3.6.1.4.1.34380.1.2.2' => 'webops' + } + }, + trusted_data) end end end From dac92a040a0977e98081a8bff33be7e475a5fa32 Mon Sep 17 00:00:00 2001 From: Felix Frank Date: Sat, 28 Dec 2013 02:01:28 +0100 Subject: [PATCH 305/800] (maint) cron: Make the munge method for the command property more readable The removal of leading and trailing spaces in cron commands was performed using a perlism instead of just using String#strip. Do that instead. --- lib/puppet/type/cron.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/puppet/type/cron.rb b/lib/puppet/type/cron.rb index 198d6c171..c6d6c3a36 100644 --- a/lib/puppet/type/cron.rb +++ b/lib/puppet/type/cron.rb @@ -234,9 +234,7 @@ Puppet::Type.newtype(:cron) do end def munge(value) - value.sub!(/^\s+/, '') - value.sub!(/\s+$/, '') - value + value.strip end end From 39a8807d6291c76319db479403b52d9486e13e70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20C=C3=B4rte-Real?= Date: Sat, 28 Dec 2013 02:24:35 +0000 Subject: [PATCH 306/800] (PUP-1051) Add rspec test to verify fix --- spec/unit/provider/package/gem_spec.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/spec/unit/provider/package/gem_spec.rb b/spec/unit/provider/package/gem_spec.rb index 02cb2cabd..7cedaedac 100755 --- a/spec/unit/provider/package/gem_spec.rb +++ b/spec/unit/provider/package/gem_spec.rb @@ -130,6 +130,18 @@ describe provider_class do ] end + it "should ignore platform specifications" do + provider_class.expects(:execute).with(%w{/my/gem list --local}).returns <<-HEREDOC.gsub(/ /, '') + systemu (1.2.0) + nokogiri (1.6.1 ruby java x86-mingw32 x86-mswin32-60, 1.4.4.1 x86-mswin32) + HEREDOC + + provider_class.instances.map {|p| p.properties}.should == [ + {:ensure => ["1.2.0"], :provider => :gem, :name => 'systemu'}, + {:ensure => ["1.6.1", "1.4.4.1"], :provider => :gem, :name => 'nokogiri'} + ] + end + it "should not fail when an unmatched line is returned" do provider_class.expects(:execute).with(%w{/my/gem list --local}). returns(File.read(my_fixture('line-with-1.8.5-warning'))) From fa4e64e90093704de80724926ad6a3ebdf5901d8 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 28 Dec 2013 17:33:01 +0100 Subject: [PATCH 307/800] (maint) Add support for creating the PType (metatype) in type factory This method was missing. --- lib/puppet/pops/types/type_factory.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/puppet/pops/types/type_factory.rb b/lib/puppet/pops/types/type_factory.rb index 25ebc1cee..6385959c9 100644 --- a/lib/puppet/pops/types/type_factory.rb +++ b/lib/puppet/pops/types/type_factory.rb @@ -239,6 +239,15 @@ module Puppet::Pops::Types::TypeFactory type end + # Produces a type for Type[T] + # @api public + # + def self.type_type(inst_type = nil) + type = Types::PType.new() + type.type = inst_type + type + end + # Produce a type corresponding to the class of given unless given is a String, Class or a PObjectType. # When a String is given this is taken as a classname. # From 860b0d09513b3555d0e6714ce2d2ce548f988a55 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 28 Dec 2013 17:36:00 +0100 Subject: [PATCH 308/800] (maint) Add support for Type[T] in access operator This makes it possible to parameterize a Type from the Puppet Language. e.g. support: Integer =~ Type[Integer] # true --- lib/puppet/pops/evaluator/access_operator.rb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/puppet/pops/evaluator/access_operator.rb b/lib/puppet/pops/evaluator/access_operator.rb index 17799d9b9..9f0043992 100644 --- a/lib/puppet/pops/evaluator/access_operator.rb +++ b/lib/puppet/pops/evaluator/access_operator.rb @@ -249,6 +249,20 @@ class Puppet::Pops::Evaluator::AccessOperator end end + def access_PType(o, scope, keys) + keys.flatten! + if keys.size == 1 + unless keys[0].is_a?(Puppet::Pops::Types::PAbstractType) + fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[0], {:base_type => 'Type-Type', :actual => keys[0].class}) + end + result = Puppet::Pops::Types::PType.new() + result.type = keys[0] + result + else + fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_ARITY, @semantic, {:base_type => 'Type-Type', :min => 1, :actual => keys.size}) + end + end + def access_PIntegerType(o, scope, keys) keys.flatten! unless keys.size.between?(1, 2) From a702bb8e7170153ac57d854f96ad8b9c6ce255a2 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 28 Dec 2013 17:40:45 +0100 Subject: [PATCH 309/800] (maint) Add test of Type[T] access operator. --- spec/unit/pops/evaluator/access_ops_spec.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/spec/unit/pops/evaluator/access_ops_spec.rb b/spec/unit/pops/evaluator/access_ops_spec.rb index 62e7e0b7d..35f261e88 100644 --- a/spec/unit/pops/evaluator/access_ops_spec.rb +++ b/spec/unit/pops/evaluator/access_ops_spec.rb @@ -295,6 +295,18 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do expect {evaluate(expr)}.to raise_error(/Resource not found: File\['x'\]/) end + # Type Type + # + it 'creates a Type instance when applied to a Type' do + type_expr = fqr('Type')[fqr('Integer')] + tf = Puppet::Pops::Types::TypeFactory + expect(evaluate(type_expr)).to eql(tf.type_type(tf.integer)) + + # arguments are flattened + type_expr = fqr('Type')[[fqr('Integer')]] + expect(evaluate(type_expr)).to eql(tf.type_type(tf.integer)) + end + end matcher :be_the_type do |type| From cda399347ab1b4a01d73bf4868f4391064ced53b Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 28 Dec 2013 17:46:47 +0100 Subject: [PATCH 310/800] (maint) Add support for Type[T] in TypeParser This makes it possible to go from String form to Type. --- lib/puppet/pops/types/type_parser.rb | 17 +++++++++++++---- spec/unit/pops/types/type_parser_spec.rb | 7 +++++-- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/lib/puppet/pops/types/type_parser.rb b/lib/puppet/pops/types/type_parser.rb index 647db3ea3..07682a26c 100644 --- a/lib/puppet/pops/types/type_parser.rb +++ b/lib/puppet/pops/types/type_parser.rb @@ -152,10 +152,12 @@ class Puppet::Pops::Types::TypeParser when "optional" TYPES.optional() - when "ruby", "type" - # should not be interpreted as Resource type - # TODO: these should not be errors + when "ruby" raise_unknown_type_error(name_ast) + + when "type" + TYPES.type_type() + else TYPES.resource(name_ast.value) end @@ -299,7 +301,14 @@ class Puppet::Pops::Types::TypeParser when "object", "collection", "data", "catalogentry", "boolean", "literal", "undef", "numeric", "pattern", "string" raise_unparameterized_type_error(parameterized_ast.left_expr) - when "ruby", "type" + when "type" + if parameters.size != 1 + raise_invalid_parameters_error("Type", 1, parameters.size) + end + assert_type(parameters[0]) + TYPES.type_type(parameters[0]) + + when "ruby" # TODO: Add Stage, Node (they are not Resource Type) # should not be interpreted as Resource type raise_unknown_type_error(parameterized_ast.left_expr) diff --git a/spec/unit/pops/types/type_parser_spec.rb b/spec/unit/pops/types/type_parser_spec.rb index 3aba8d89e..0f1624d52 100644 --- a/spec/unit/pops/types/type_parser_spec.rb +++ b/spec/unit/pops/types/type_parser_spec.rb @@ -30,9 +30,8 @@ describe Puppet::Pops::Types::TypeParser do end it "does not support types that do not make sense in the puppet language" do - # These will/may make sense later, but are not yet implemented and should not be interpreted as a PResourceType + # This will/may make sense later, but are not yet implemented and should not be interpreted as a PResourceType expect { parser.parse("Ruby") }.to raise_type_error_for("Ruby") - expect { parser.parse("Type") }.to raise_type_error_for("Type") end [ @@ -132,6 +131,10 @@ describe Puppet::Pops::Types::TypeParser do expect(parser.parse("Float[1.0,2.0]")).to be_the_type(types.float_range(1.0,2.0)) end + it 'parses a type type' do + expect(parser.parse("Type[Integer]")).to be_the_type(types.type_type(types.integer)) + end + matcher :be_the_type do |type| calc = Puppet::Pops::Types::TypeCalculator.new From eb0196db95c987b8d103bb9de25b2ecef30ea5ff Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 28 Dec 2013 18:15:14 +0100 Subject: [PATCH 311/800] (maint) Add support for Ruby['classname'] in access operator. There were also several problems since the existing behavior did an inference of the type parameter thus always arriving at Ruby[String]. From the Puppet Language, the parameter is always a String containing the class name. --- lib/puppet/pops/evaluator/access_operator.rb | 7 +++++++ lib/puppet/pops/types/type_calculator.rb | 10 ++++++++-- lib/puppet/pops/types/type_factory.rb | 9 +++++++++ lib/puppet/pops/types/type_parser.rb | 2 +- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/lib/puppet/pops/evaluator/access_operator.rb b/lib/puppet/pops/evaluator/access_operator.rb index 9f0043992..88d9dd1f4 100644 --- a/lib/puppet/pops/evaluator/access_operator.rb +++ b/lib/puppet/pops/evaluator/access_operator.rb @@ -263,6 +263,13 @@ class Puppet::Pops::Evaluator::AccessOperator end end + def access_PRubyType(o, scope, keys) + keys.flatten! + assert_keys(keys, o, 1, 1, String) + # create ruby type based on name of class, not inference of key's type + Puppet::Pops::Types::TypeFactory.ruby_type(keys[0]) + end + def access_PIntegerType(o, scope, keys) keys.flatten! unless keys.size.between?(1, 2) diff --git a/lib/puppet/pops/types/type_calculator.rb b/lib/puppet/pops/types/type_calculator.rb index e314825fe..75fb296b5 100644 --- a/lib/puppet/pops/types/type_calculator.rb +++ b/lib/puppet/pops/types/type_calculator.rb @@ -1002,6 +1002,8 @@ class Puppet::Pops::Types::TypeCalculator # @api private def assignable_PRubyType(t1, t2) return false unless t2.is_a?(Types::PRubyType) + return true if t1.ruby_class.nil? # t1 is wider + return false if t2.ruby_class.nil? # t1 not nil, so t2 can not be wider c1 = class_from_string(t1.ruby_class) c2 = class_from_string(t2.ruby_class) return false unless c1.is_a?(Class) && c2.is_a?(Class) @@ -1183,8 +1185,12 @@ class Puppet::Pops::Types::TypeCalculator private def class_from_string(str) - str.split('::').inject(Object) do |memo, name_segment| - memo.const_get(name_segment) + begin + str.split('::').inject(Object) do |memo, name_segment| + memo.const_get(name_segment) + end + rescue NameError + return nil end end diff --git a/lib/puppet/pops/types/type_factory.rb b/lib/puppet/pops/types/type_factory.rb index 6385959c9..ee702f6de 100644 --- a/lib/puppet/pops/types/type_factory.rb +++ b/lib/puppet/pops/types/type_factory.rb @@ -286,6 +286,15 @@ module Puppet::Pops::Types::TypeFactory end end + # Generic creator of a RubyType - allows creating the Ruby type with nil name, or String name. + # Also see ruby(o) which performs inference, or mapps a Ruby Class to its name. + # + def self.ruby_type(class_name = nil) + type = Types::PRubyType.new() + type.ruby_class = class_name + type + end + # Sets the accepted size range of a collection if something other than the default 0 to Infinity # is wanted. The semantics for from/to are the same as for #range # diff --git a/lib/puppet/pops/types/type_parser.rb b/lib/puppet/pops/types/type_parser.rb index 07682a26c..1e4c10359 100644 --- a/lib/puppet/pops/types/type_parser.rb +++ b/lib/puppet/pops/types/type_parser.rb @@ -153,7 +153,7 @@ class Puppet::Pops::Types::TypeParser TYPES.optional() when "ruby" - raise_unknown_type_error(name_ast) + TYPES.ruby_type() when "type" TYPES.type_type() From 335278db860cd86f3bc5ea05dc809f800f9fd370 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 28 Dec 2013 18:17:43 +0100 Subject: [PATCH 312/800] (maint) Add test for Ruby['classname'] --- spec/unit/pops/evaluator/access_ops_spec.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/spec/unit/pops/evaluator/access_ops_spec.rb b/spec/unit/pops/evaluator/access_ops_spec.rb index 35f261e88..1f19b929d 100644 --- a/spec/unit/pops/evaluator/access_ops_spec.rb +++ b/spec/unit/pops/evaluator/access_ops_spec.rb @@ -307,6 +307,18 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do expect(evaluate(type_expr)).to eql(tf.type_type(tf.integer)) end + # Ruby Type + # + it 'creates a Ruby Type instance when applied to a Ruby Type' do + type_expr = fqr('Ruby')['String'] + tf = Puppet::Pops::Types::TypeFactory + expect(evaluate(type_expr)).to eql(tf.ruby_type('String')) + + # arguments are flattened + type_expr = fqr('Ruby')[['String']] + expect(evaluate(type_expr)).to eql(tf.ruby_type('String')) + end + end matcher :be_the_type do |type| From 5614f21cd9b4873aa86ca7972a638e11c2b62cfd Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 28 Dec 2013 18:21:37 +0100 Subject: [PATCH 313/800] (maint) Add support for Ruby['classname'] in type parser This makes it possible to convert from Ruby type in string form to a RubyType. --- lib/puppet/pops/types/type_parser.rb | 5 ++--- spec/unit/pops/types/type_parser_spec.rb | 9 ++++----- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/puppet/pops/types/type_parser.rb b/lib/puppet/pops/types/type_parser.rb index 1e4c10359..6296b4a91 100644 --- a/lib/puppet/pops/types/type_parser.rb +++ b/lib/puppet/pops/types/type_parser.rb @@ -309,9 +309,8 @@ class Puppet::Pops::Types::TypeParser TYPES.type_type(parameters[0]) when "ruby" - # TODO: Add Stage, Node (they are not Resource Type) - # should not be interpreted as Resource type - raise_unknown_type_error(parameterized_ast.left_expr) + raise_invalid_parameters_error("Ruby", "1", parameters.size) unless parameters.size == 1 + TYPES.ruby_type(parameters[0]) else # It is a resource such a File['/tmp/foo'] diff --git a/spec/unit/pops/types/type_parser_spec.rb b/spec/unit/pops/types/type_parser_spec.rb index 0f1624d52..916ce47af 100644 --- a/spec/unit/pops/types/type_parser_spec.rb +++ b/spec/unit/pops/types/type_parser_spec.rb @@ -29,11 +29,6 @@ describe Puppet::Pops::Types::TypeParser do /The expression is not a valid type specification/) end - it "does not support types that do not make sense in the puppet language" do - # This will/may make sense later, but are not yet implemented and should not be interpreted as a PResourceType - expect { parser.parse("Ruby") }.to raise_type_error_for("Ruby") - end - [ 'Object', 'Collection', 'Data', 'CatalogEntry', 'Boolean', 'Literal', 'Undef', 'Numeric', ].each do |name| @@ -135,6 +130,10 @@ describe Puppet::Pops::Types::TypeParser do expect(parser.parse("Type[Integer]")).to be_the_type(types.type_type(types.integer)) end + it 'parses a ruby type' do + expect(parser.parse("Ruby['Integer']")).to be_the_type(types.ruby_type('Integer')) + end + matcher :be_the_type do |type| calc = Puppet::Pops::Types::TypeCalculator.new From f8cb0cea3516a8de9933cb5177297fdd6553c607 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 28 Dec 2013 18:38:36 +0100 Subject: [PATCH 314/800] (maint) Add support for Collection[] to type parser The type parser testd that Collection[from,to] resulted in error while this is supported as an access operation. Should have been the same in both places. This allows a Collection in string form to have type parameters for the size constraint. --- lib/puppet/pops/types/type_parser.rb | 20 ++++++++++++++++++++ spec/unit/pops/types/type_parser_spec.rb | 6 +++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/lib/puppet/pops/types/type_parser.rb b/lib/puppet/pops/types/type_parser.rb index 6296b4a91..6edb58365 100644 --- a/lib/puppet/pops/types/type_parser.rb +++ b/lib/puppet/pops/types/type_parser.rb @@ -228,6 +228,26 @@ class Puppet::Pops::Types::TypeParser result.size_type = size_type if size_type result + when "collection" + size_type = case parameters.size + when 1 + if parameters[0].is_a?(Puppet::Pops::Types::PIntegerType) + parameters[0].copy + else + assert_range_parameter(parameters[0]) + TYPES.range(parameters[0], :default) + end + when 2 + assert_range_parameter(parameters[0]) + assert_range_parameter(parameters[1]) + TYPES.range(parameters[0], parameters[1]) + else + raise_invalid_parameters_error("Collection", "1 to 2", parameters.size) + end + result = TYPES.collection + result.size_type = size_type + result + when "class" if parameters.size != 1 raise_invalid_parameters_error("Class", 1, parameters.size) diff --git a/spec/unit/pops/types/type_parser_spec.rb b/spec/unit/pops/types/type_parser_spec.rb index 916ce47af..54a3283ac 100644 --- a/spec/unit/pops/types/type_parser_spec.rb +++ b/spec/unit/pops/types/type_parser_spec.rb @@ -30,7 +30,7 @@ describe Puppet::Pops::Types::TypeParser do end [ - 'Object', 'Collection', 'Data', 'CatalogEntry', 'Boolean', 'Literal', 'Undef', 'Numeric', + 'Object', 'Data', 'CatalogEntry', 'Boolean', 'Literal', 'Undef', 'Numeric', ].each do |name| it "does not support parameterizing unparameterized type <#{name}" do expect { parser.parse("#{name}[Integer]") }.to raise_unparameterized_error_for(name) @@ -126,6 +126,10 @@ describe Puppet::Pops::Types::TypeParser do expect(parser.parse("Float[1.0,2.0]")).to be_the_type(types.float_range(1.0,2.0)) end + it 'parses a collection size range' do + expect(parser.parse("Collection[1,2]")).to be_the_type(types.constrain_size(types.collection,1,2)) + end + it 'parses a type type' do expect(parser.parse("Type[Integer]")).to be_the_type(types.type_type(types.integer)) end From 4250646b02da49919103a27651aadeeb4061b237 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 28 Dec 2013 18:48:44 +0100 Subject: [PATCH 315/800] (maint) Correct assignable? for Type without type parameter The expression Type[Integer] =~ Type should produce true but did not since it only considered the parameterized case. --- lib/puppet/pops/types/type_calculator.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/puppet/pops/types/type_calculator.rb b/lib/puppet/pops/types/type_calculator.rb index 75fb296b5..acc05a942 100644 --- a/lib/puppet/pops/types/type_calculator.rb +++ b/lib/puppet/pops/types/type_calculator.rb @@ -950,6 +950,8 @@ class Puppet::Pops::Types::TypeCalculator # @api private def assignable_PType(t, t2) return false unless t2.is_a?(Types::PType) + return true if t.type.nil? # wide enough to handle all types + return false if t2.type.nil? # wider than t assignable?(t.type, t2.type) end From a430cbcf566cd1ee65469395d89e0adc3f609d60 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 28 Dec 2013 19:00:00 +0100 Subject: [PATCH 316/800] (maint) Add test for Type[Type[x]] instance? operations There were no testing of meta type matching when there is meta type regression (the type of the type of the...) --- spec/unit/pops/types/type_calculator_spec.rb | 23 ++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/spec/unit/pops/types/type_calculator_spec.rb b/spec/unit/pops/types/type_calculator_spec.rb index a65f5fc2c..b50d5da3a 100644 --- a/spec/unit/pops/types/type_calculator_spec.rb +++ b/spec/unit/pops/types/type_calculator_spec.rb @@ -1154,6 +1154,29 @@ describe 'The type calculator' do it 'should infer PType as the type of PType (meta regression short-circuit)' do calculator.infer(Puppet::Pops::Types::PType.new()).is_a?(Puppet::Pops::Types::PType).should() == true end + + it 'computes instance? to be true if parameterized and type match' do + int_t = Puppet::Pops::Types::PIntegerType.new() + type_t = Puppet::Pops::Types::TypeFactory.type_type(int_t) + type_type_t = Puppet::Pops::Types::TypeFactory.type_type(type_t) + calculator.instance?(type_type_t, type_t).should == true + end + + it 'computes instance? to be false if parameterized and type do not match' do + int_t = Puppet::Pops::Types::PIntegerType.new() + string_t = Puppet::Pops::Types::PStringType.new() + type_t = Puppet::Pops::Types::TypeFactory.type_type(int_t) + type_t2 = Puppet::Pops::Types::TypeFactory.type_type(string_t) + type_type_t = Puppet::Pops::Types::TypeFactory.type_type(type_t) + # i.e. Type[Integer] =~ Type[Type[Integer]] # false + calculator.instance?(type_type_t, type_t2).should == false + end + + it 'computes instance? to be true if unparameterized and matched against a type[?]' do + int_t = Puppet::Pops::Types::PIntegerType.new() + type_t = Puppet::Pops::Types::TypeFactory.type_type(int_t) + calculator.instance?(Puppet::Pops::Types::PType.new, type_t).should == true + end end context "when asking for an enumerable " do From 236b87d35564d329921a6feaca54e25077a71d16 Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Sun, 29 Dec 2013 12:09:21 -0800 Subject: [PATCH 317/800] (maint) Add temp workaround for travis Rubygems broke travis builds on ruby 1.8.7: https://github.com/travis-ci/travis-ci/issues/1793 https://github.com/rubygems/rubygems/pull/763 This patch is a temporary workaround until rubygems is updated. --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 09f7db6e5..1c0a4c579 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,6 @@ +before_install: + - gem update --system 2.1.11 + - gem --version language: ruby bundler_args: --without development script: "bundle exec rake spec" From 37aabc49faf0de5490d1d7d23f34c298f044384f Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Mon, 30 Dec 2013 14:52:51 -0800 Subject: [PATCH 318/800] (maint) No longer install json gem for future parser acceptance runs Source based acceptance test runs with --parser future were installing json gem do to binder requirements, but as much of the binder has been removed, json is no longer needed. --- acceptance/setup/common/SetParser.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/acceptance/setup/common/SetParser.rb b/acceptance/setup/common/SetParser.rb index d49ebf5e9..e8c76e69f 100644 --- a/acceptance/setup/common/SetParser.rb +++ b/acceptance/setup/common/SetParser.rb @@ -17,8 +17,6 @@ test_name "add parser=#{ENV['PARSER']} to all puppet.conf (only if $PARSER is se # We are installing from source rather than packages and need the following: win_cmd_prefix = 'cmd /c ' if host['platform'] =~ /windows/ on(host, "#{win_cmd_prefix}gem install rgen") - # deprecated - remove when binder is removed from pops - on(host, "#{win_cmd_prefix}gem install json") end end end From f7e81b3a241a3b4fcc509785674a42abadfc659c Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Mon, 30 Dec 2013 15:00:44 -0800 Subject: [PATCH 319/800] (maint) Acceptance test selector tests for a string rather than a type Bare upper case words are types in the future parser, so this test fails because $operatingsystem returns a string which gets compared to the Resource[Fedora] type. Explicity quoting so it's clear what the intention is in the current parser and so that it works properly with the future parser. --- .../enc_provides_node_when_storeconfigs_enabled.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance/tests/store_configs/enc_provides_node_when_storeconfigs_enabled.rb b/acceptance/tests/store_configs/enc_provides_node_when_storeconfigs_enabled.rb index 7e4ba3f42..daf283f51 100644 --- a/acceptance/tests/store_configs/enc_provides_node_when_storeconfigs_enabled.rb +++ b/acceptance/tests/store_configs/enc_provides_node_when_storeconfigs_enabled.rb @@ -55,7 +55,7 @@ if $osfamily == "Debian" { } } elsif $osfamily == "RedHat" { $sqlite_gem_pkg_name = $operatingsystem ? { - Fedora => "rubygem-sqlite3", + "Fedora" => "rubygem-sqlite3", default => "rubygem-sqlite3-ruby" } From be0cebf508cda6fb105aa73263bf7445e9d6f931 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 31 Dec 2013 00:41:26 +0100 Subject: [PATCH 320/800] (maint) Fix inconsistency when case option is a Type. The way the implementation worked made it impossible to differentiate between an instance and its type. --- lib/puppet/pops/evaluator/evaluator_impl.rb | 2 +- spec/unit/pops/evaluator/evaluating_parser_spec.rb | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index 2590ec29d..b493f51fa 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -951,7 +951,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl matched = right.match(left) set_match_data(matched, o, scope) # creates or clears ephemeral !!matched # convert to boolean - elsif right.is_a?(Puppet::Pops::Types::PAbstractType) && !left.is_a?(Puppet::Pops::Types::PAbstractType) + elsif right.is_a?(Puppet::Pops::Types::PAbstractType) # right is a type and left is not - check if left is an instance of the given type # (The reverse is not terribly meaningful - computing which of the case options that first produces # an instance of a given type). diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index a54f2e5a8..de5119a26 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -444,6 +444,13 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do Array[String] : { no } Array[Integer]: { yes } }" => 'yes', + "case 1 { + Integer : { yes } + Type[Integer] : { no } }" => 'yes', + "case Integer { + Integer : { no } + Type[Integer] : { yes } }" => 'yes', + }.each do |source, result| it "should parse and evaluate the expression '#{source}' to #{result}" do parser.evaluate_string(scope, source, __FILE__).should == result From 959e0cc8559afb879a10d62425788e729b5b2503 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Mon, 30 Dec 2013 16:12:35 -0800 Subject: [PATCH 321/800] (main) Remove symlinks from acceptance harness Git's cross platform handling of symlinks is problematic on windows, and the symlinks are also causing problems in third party software like Eclipse. This commit removes them, and instead is explicit about which pre-suite steps are executed, in what order, in the config/{git,packages}/options.rb files. --- acceptance/config/git/options.rb | 13 ++++++++++++- acceptance/config/packages/options.rb | 7 ++++++- .../025_StopFirewall.rb} | 0 .../040_ValidateSignCert.rb} | 0 .../{SetParser.rb => pre-suite/100_SetParser.rb} | 0 .../pre-suite/{00_EnvSetup.rb => 000_EnvSetup.rb} | 0 .../pre-suite/{01_TestSetup.rb => 010_TestSetup.rb} | 0 ...petUserAndGroup.rb => 020_PuppetUserAndGroup.rb} | 0 ...petMasterSanity.rb => 030_PuppetMasterSanity.rb} | 0 acceptance/setup/git/pre-suite/035_StopFirewall.rb | 1 - .../setup/git/pre-suite/04_ValidateSignCert.rb | 1 - .../{05_HieraSetup.rb => 050_HieraSetup.rb} | 0 .../{06_InstallModules.rb => 060_InstallModules.rb} | 0 .../{07_InstallCACerts.rb => 070_InstalCACerts.rb} | 0 acceptance/setup/git/pre-suite/10_SetParser.rb | 1 - .../pre-suite/{01_Install.rb => 010_Install.rb} | 0 .../setup/packages/pre-suite/02_StopFirewall.rb | 1 - .../setup/packages/pre-suite/04_ValidateSignCert.rb | 1 - acceptance/setup/packages/pre-suite/10_SetParser.rb | 1 - .../pe/pre-suite/{00_install.rb => 000_Install.rb} | 0 ...urgeAndReinstall.rb => 000_PurgeAndReinstall.rb} | 0 .../{01_RsyncSource.rb => 010_RsyncSource.rb} | 0 22 files changed, 18 insertions(+), 8 deletions(-) rename acceptance/setup/common/{StopFirewall.rb => pre-suite/025_StopFirewall.rb} (100%) rename acceptance/setup/common/{ValidateSignCert.rb => pre-suite/040_ValidateSignCert.rb} (100%) rename acceptance/setup/common/{SetParser.rb => pre-suite/100_SetParser.rb} (100%) rename acceptance/setup/git/pre-suite/{00_EnvSetup.rb => 000_EnvSetup.rb} (100%) rename acceptance/setup/git/pre-suite/{01_TestSetup.rb => 010_TestSetup.rb} (100%) rename acceptance/setup/git/pre-suite/{02_PuppetUserAndGroup.rb => 020_PuppetUserAndGroup.rb} (100%) rename acceptance/setup/git/pre-suite/{03_PuppetMasterSanity.rb => 030_PuppetMasterSanity.rb} (100%) delete mode 120000 acceptance/setup/git/pre-suite/035_StopFirewall.rb delete mode 120000 acceptance/setup/git/pre-suite/04_ValidateSignCert.rb rename acceptance/setup/git/pre-suite/{05_HieraSetup.rb => 050_HieraSetup.rb} (100%) rename acceptance/setup/git/pre-suite/{06_InstallModules.rb => 060_InstallModules.rb} (100%) rename acceptance/setup/git/pre-suite/{07_InstallCACerts.rb => 070_InstalCACerts.rb} (100%) delete mode 120000 acceptance/setup/git/pre-suite/10_SetParser.rb rename acceptance/setup/packages/pre-suite/{01_Install.rb => 010_Install.rb} (100%) delete mode 120000 acceptance/setup/packages/pre-suite/02_StopFirewall.rb delete mode 120000 acceptance/setup/packages/pre-suite/04_ValidateSignCert.rb delete mode 120000 acceptance/setup/packages/pre-suite/10_SetParser.rb rename acceptance/setup/pe/pre-suite/{00_install.rb => 000_Install.rb} (100%) rename acceptance/setup/rsync/pre-suite/{00_PurgeAndReinstall.rb => 000_PurgeAndReinstall.rb} (100%) rename acceptance/setup/rsync/pre-suite/{01_RsyncSource.rb => 010_RsyncSource.rb} (100%) diff --git a/acceptance/config/git/options.rb b/acceptance/config/git/options.rb index 2268fa0c9..c20149d3f 100644 --- a/acceptance/config/git/options.rb +++ b/acceptance/config/git/options.rb @@ -3,5 +3,16 @@ 'git://github.com/puppetlabs/facter.git#stable', 'git://github.com/puppetlabs/hiera.git#stable', ], - :pre_suite => ['setup/git/pre-suite'], + :pre_suite => [ + 'setup/git/pre-suite/000_EnvSetup.rb', + 'setup/git/pre-suite/010_TestSetup.rb', + 'setup/git/pre-suite/020_PuppetUserAndGroup.rb', + 'setup/common/pre-suite/025_StopFirewall.rb', + 'setup/git/pre-suite/030_PuppetMasterSanity.rb', + 'setup/common/pre-suite/040_ValidateSignCert.rb', + 'setup/git/pre-suite/050_HieraSetup.rb', + 'setup/git/pre-suite/060_InstallModules.rb', + 'setup/git/pre-suite/070_InstalCACerts.rb', + 'setup/common/pre-suite/100_SetParser.rb', + ], } diff --git a/acceptance/config/packages/options.rb b/acceptance/config/packages/options.rb index bc2b8189f..4adaab5de 100644 --- a/acceptance/config/packages/options.rb +++ b/acceptance/config/packages/options.rb @@ -1,3 +1,8 @@ { - :pre_suite => ['setup/packages/pre-suite'], + :pre_suite => [ + 'setup/packages/pre-suite/010_Install.rb', + 'setup/common/pre-suite/025_StopFirewall.rb', + 'setup/common/pre-suite/040_ValidateSignCert.rb', + 'setup/common/pre-suite/100_SetParser.rb', + ], } diff --git a/acceptance/setup/common/StopFirewall.rb b/acceptance/setup/common/pre-suite/025_StopFirewall.rb similarity index 100% rename from acceptance/setup/common/StopFirewall.rb rename to acceptance/setup/common/pre-suite/025_StopFirewall.rb diff --git a/acceptance/setup/common/ValidateSignCert.rb b/acceptance/setup/common/pre-suite/040_ValidateSignCert.rb similarity index 100% rename from acceptance/setup/common/ValidateSignCert.rb rename to acceptance/setup/common/pre-suite/040_ValidateSignCert.rb diff --git a/acceptance/setup/common/SetParser.rb b/acceptance/setup/common/pre-suite/100_SetParser.rb similarity index 100% rename from acceptance/setup/common/SetParser.rb rename to acceptance/setup/common/pre-suite/100_SetParser.rb diff --git a/acceptance/setup/git/pre-suite/00_EnvSetup.rb b/acceptance/setup/git/pre-suite/000_EnvSetup.rb similarity index 100% rename from acceptance/setup/git/pre-suite/00_EnvSetup.rb rename to acceptance/setup/git/pre-suite/000_EnvSetup.rb diff --git a/acceptance/setup/git/pre-suite/01_TestSetup.rb b/acceptance/setup/git/pre-suite/010_TestSetup.rb similarity index 100% rename from acceptance/setup/git/pre-suite/01_TestSetup.rb rename to acceptance/setup/git/pre-suite/010_TestSetup.rb diff --git a/acceptance/setup/git/pre-suite/02_PuppetUserAndGroup.rb b/acceptance/setup/git/pre-suite/020_PuppetUserAndGroup.rb similarity index 100% rename from acceptance/setup/git/pre-suite/02_PuppetUserAndGroup.rb rename to acceptance/setup/git/pre-suite/020_PuppetUserAndGroup.rb diff --git a/acceptance/setup/git/pre-suite/03_PuppetMasterSanity.rb b/acceptance/setup/git/pre-suite/030_PuppetMasterSanity.rb similarity index 100% rename from acceptance/setup/git/pre-suite/03_PuppetMasterSanity.rb rename to acceptance/setup/git/pre-suite/030_PuppetMasterSanity.rb diff --git a/acceptance/setup/git/pre-suite/035_StopFirewall.rb b/acceptance/setup/git/pre-suite/035_StopFirewall.rb deleted file mode 120000 index 8137a325a..000000000 --- a/acceptance/setup/git/pre-suite/035_StopFirewall.rb +++ /dev/null @@ -1 +0,0 @@ -../../common/StopFirewall.rb \ No newline at end of file diff --git a/acceptance/setup/git/pre-suite/04_ValidateSignCert.rb b/acceptance/setup/git/pre-suite/04_ValidateSignCert.rb deleted file mode 120000 index 5f5a2f09e..000000000 --- a/acceptance/setup/git/pre-suite/04_ValidateSignCert.rb +++ /dev/null @@ -1 +0,0 @@ -../../common/ValidateSignCert.rb \ No newline at end of file diff --git a/acceptance/setup/git/pre-suite/05_HieraSetup.rb b/acceptance/setup/git/pre-suite/050_HieraSetup.rb similarity index 100% rename from acceptance/setup/git/pre-suite/05_HieraSetup.rb rename to acceptance/setup/git/pre-suite/050_HieraSetup.rb diff --git a/acceptance/setup/git/pre-suite/06_InstallModules.rb b/acceptance/setup/git/pre-suite/060_InstallModules.rb similarity index 100% rename from acceptance/setup/git/pre-suite/06_InstallModules.rb rename to acceptance/setup/git/pre-suite/060_InstallModules.rb diff --git a/acceptance/setup/git/pre-suite/07_InstallCACerts.rb b/acceptance/setup/git/pre-suite/070_InstalCACerts.rb similarity index 100% rename from acceptance/setup/git/pre-suite/07_InstallCACerts.rb rename to acceptance/setup/git/pre-suite/070_InstalCACerts.rb diff --git a/acceptance/setup/git/pre-suite/10_SetParser.rb b/acceptance/setup/git/pre-suite/10_SetParser.rb deleted file mode 120000 index f3048323f..000000000 --- a/acceptance/setup/git/pre-suite/10_SetParser.rb +++ /dev/null @@ -1 +0,0 @@ -../../common/SetParser.rb \ No newline at end of file diff --git a/acceptance/setup/packages/pre-suite/01_Install.rb b/acceptance/setup/packages/pre-suite/010_Install.rb similarity index 100% rename from acceptance/setup/packages/pre-suite/01_Install.rb rename to acceptance/setup/packages/pre-suite/010_Install.rb diff --git a/acceptance/setup/packages/pre-suite/02_StopFirewall.rb b/acceptance/setup/packages/pre-suite/02_StopFirewall.rb deleted file mode 120000 index 8137a325a..000000000 --- a/acceptance/setup/packages/pre-suite/02_StopFirewall.rb +++ /dev/null @@ -1 +0,0 @@ -../../common/StopFirewall.rb \ No newline at end of file diff --git a/acceptance/setup/packages/pre-suite/04_ValidateSignCert.rb b/acceptance/setup/packages/pre-suite/04_ValidateSignCert.rb deleted file mode 120000 index 5f5a2f09e..000000000 --- a/acceptance/setup/packages/pre-suite/04_ValidateSignCert.rb +++ /dev/null @@ -1 +0,0 @@ -../../common/ValidateSignCert.rb \ No newline at end of file diff --git a/acceptance/setup/packages/pre-suite/10_SetParser.rb b/acceptance/setup/packages/pre-suite/10_SetParser.rb deleted file mode 120000 index f3048323f..000000000 --- a/acceptance/setup/packages/pre-suite/10_SetParser.rb +++ /dev/null @@ -1 +0,0 @@ -../../common/SetParser.rb \ No newline at end of file diff --git a/acceptance/setup/pe/pre-suite/00_install.rb b/acceptance/setup/pe/pre-suite/000_Install.rb similarity index 100% rename from acceptance/setup/pe/pre-suite/00_install.rb rename to acceptance/setup/pe/pre-suite/000_Install.rb diff --git a/acceptance/setup/rsync/pre-suite/00_PurgeAndReinstall.rb b/acceptance/setup/rsync/pre-suite/000_PurgeAndReinstall.rb similarity index 100% rename from acceptance/setup/rsync/pre-suite/00_PurgeAndReinstall.rb rename to acceptance/setup/rsync/pre-suite/000_PurgeAndReinstall.rb diff --git a/acceptance/setup/rsync/pre-suite/01_RsyncSource.rb b/acceptance/setup/rsync/pre-suite/010_RsyncSource.rb similarity index 100% rename from acceptance/setup/rsync/pre-suite/01_RsyncSource.rb rename to acceptance/setup/rsync/pre-suite/010_RsyncSource.rb From 93b43a01b5733ed1113b2139c2146da8c2265100 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 28 Dec 2013 17:15:52 +0100 Subject: [PATCH 322/800] (maint) Remove support for categories in the binder This removes the ability to define categories and use predicated bindings. Instead, all contributors should evaluate predicates and only contribute applicable bindings. This reduces the complexity of the binder. The support for predicated bindings was only there to support the now removed experimental "hiera2-data-in-modules" --- lib/puppet/parser/compiler.rb | 2 - lib/puppet/pops/binder/binder.rb | 137 +++--------------- lib/puppet/pops/binder/binder_issues.rb | 20 --- lib/puppet/pops/binder/bindings_checker.rb | 20 --- lib/puppet/pops/binder/bindings_composer.rb | 59 +------- lib/puppet/pops/binder/bindings_factory.rb | 53 +------ .../pops/binder/bindings_label_provider.rb | 3 - lib/puppet/pops/binder/bindings_model.rb | 32 +--- .../pops/binder/bindings_model_dumper.rb | 20 +-- .../pops/binder/config/binder_config.rb | 27 +--- .../binder/config/binder_config_checker.rb | 37 +---- lib/puppet/pops/binder/config/issues.rb | 20 --- lib/puppet/pops/binder/injector_entry.rb | 4 +- .../binder/scheme_handler/symbolic_scheme.rb | 3 +- lib/puppet/pops/binder/system_bindings.rb | 20 +-- .../hiera1config/binder_config.yaml | 12 -- .../bindings_composer/hiera1config/hiera.yaml | 8 - .../good/lib/puppet/bindings/good/default.rb | 6 - .../bindings_composer/ok/binder_config.yaml | 6 - .../ok/lib/puppet/bindings/confdirtest.rb | 8 +- .../lib/puppet/bindings/awesome2/default.rb | 8 +- .../puppetx/awesome2/echo_scheme_handler.rb | 2 +- .../good/lib/puppet/bindings/good/default.rb | 4 - spec/unit/parser/functions/lookup_spec.rb | 1 - spec/unit/pops/binder/binder_spec.rb | 22 +-- .../unit/pops/binder/bindings_checker_spec.rb | 53 ++----- .../pops/binder/bindings_composer_spec.rb | 30 ---- .../pops/binder/config/binder_config_spec.rb | 13 -- spec/unit/pops/binder/injector_spec.rb | 77 ++-------- 29 files changed, 70 insertions(+), 637 deletions(-) delete mode 100644 spec/fixtures/unit/pops/binder/bindings_composer/hiera1config/binder_config.yaml delete mode 100644 spec/fixtures/unit/pops/binder/bindings_composer/hiera1config/hiera.yaml delete mode 100644 spec/fixtures/unit/pops/binder/bindings_composer/hiera1config/modules/good/lib/puppet/bindings/good/default.rb diff --git a/lib/puppet/parser/compiler.rb b/lib/puppet/parser/compiler.rb index 2ea34c1de..01d30fcde 100644 --- a/lib/puppet/parser/compiler.rb +++ b/lib/puppet/parser/compiler.rb @@ -249,7 +249,6 @@ class Puppet::Parser::Compiler boot_contribution = Puppet::Pops::Binder::SystemBindings.injector_boot_contribution(env_boot_bindings) final_contribution = Puppet::Pops::Binder::SystemBindings.final_contribution binder = Puppet::Pops::Binder::Binder.new() - binder.define_categories(boot_contribution.effective_categories) binder.define_layers(Puppet::Pops::Binder::BindingsFactory.layered_bindings(final_contribution, boot_contribution)) @boot_injector = Puppet::Pops::Binder::Injector.new(binder) end @@ -545,7 +544,6 @@ class Puppet::Parser::Compiler composer = Puppet::Pops::Binder::BindingsComposer.new() layered_bindings = composer.compose(topscope) binder = Puppet::Pops::Binder::Binder.new() - binder.define_categories(composer.effective_categories(topscope)) binder.define_layers(layered_bindings) @injector = Puppet::Pops::Binder::Injector.new(binder) end diff --git a/lib/puppet/pops/binder/binder.rb b/lib/puppet/pops/binder/binder.rb index a21098ebc..608c041fb 100644 --- a/lib/puppet/pops/binder/binder.rb +++ b/lib/puppet/pops/binder/binder.rb @@ -1,23 +1,13 @@ # The Binder is responsible for processing layered bindings that can be used to setup an Injector. # -# An instance should be created, and calls should then be made to {#define_categories} to define the available categories, and -# their precedence. This should be followed by a call to {#define_layers} which will match the layered bindings against the -# effective categories (filtering out everything that does not apply, handle overrides, abstract entries etc.). +# An instance should be created and a call to {#define_layers} should be made which will process the layered bindings +# (handle overrides, abstract entries etc.). # The constructed hash with `key => InjectorEntry` mappings is obtained as {#injector_entries}, and is used to initialize an # {Puppet::Pops::Binder::Injector Injector}. # # @api public # class Puppet::Pops::Binder::Binder - # This limits the number of available categorizations, including "common". - # @api private - PRECEDENCE_MAX = 1000 - - # @api private - attr_reader :category_precedences - - # @api private - attr_reader :category_values # @api private attr_reader :injector_entries @@ -32,8 +22,6 @@ class Puppet::Pops::Binder::Binder # @api public def initialize - @category_precedences = {} - @category_values = {} @key_factory = Puppet::Pops::Binder::KeyFactory.new() # Resulting hash of all key -> binding @@ -51,57 +39,13 @@ class Puppet::Pops::Binder::Binder configured() end - # Defines the effective categories in precedence order (highest precedence first). - # The 'common' (lowest precedence) category should not be included in the list. - # A sanity check is made that there are no more than 1000 categorizations (which is pretty wild). - # - # The term 'effective categories' refers to the evaluated list of tuples (categorization, category-value) represented with - # an instance of Puppet::Pops::Binder::Bindings::EffectiveCategories. - # - # @param effective_categories [Puppet::Pops::Binder::Bindings::EffectiveCategories] effective categories (i.e. with evaluated values) - # @raise ArgumentError if this binder is already configured - # @raise ArgumentError if the argument is not an EffectiveCategories - # @raise ArgumentError if there is an attempt to redefine a category (non unique, or 'common'). - # @return [Puppet::Pops::Binder::Binder] self - # @api public - # - def define_categories(effective_categories) - raise ArgumentError, "This categories are already defined. Cannot redefine." unless @category_precedences.empty? - - # Note: a model instance is used since a Hash does not have a defined order in all Rubies. - unless effective_categories.is_a?(Puppet::Pops::Binder::Bindings::EffectiveCategories) - raise ArgumentError, "Expected Puppet::Pops::Binder::Bindings::EffectiveCategories, but got a: #{effective_categories.class}" - end - categories = effective_categories.categories - raise ArgumentError, "Category limit (#{PRECEDENCE_MAX}) exceeded" unless categories.size <= PRECEDENCE_MAX - - # Automatically add the 'common' category with lowest precedence - @category_precedences['common'] = 0 - - # if categories contains "common", it should be last - simply drop it if present - if last = categories[-1] - if last.categorization == 'common' - categories.delete_at(-1) - end - end - # Process the given categories (highest precedence is first in the list) - categories.each_with_index do |c, index| - cname = c.categorization - raise ArgumentError, "Attempt to redefine categorization: #{cname}" if @category_precedences[cname] - @category_precedences[cname] = PRECEDENCE_MAX - index - @category_values[cname] = c.value - end - self - end - # Binds layers from highest to lowest as defined by the given LayeredBindings. # @note - # Categories must be set with #define_categories before calling this method. The model should have been + # The model should have been # validated to get better error messages if the model is invalid. This implementation expects the model # to be valid, and any errors raised will be more technical runtime errors. # # @param layered_bindings [Puppet::Pops::Binder::Bindings::LayeredBindings] the named and ordered layers - # @raise ArgumentError if categories have not been defined # @raise ArgumentError if this binder is already configured # @raise ArgumentError if bindings with unresolved 'override' surfaces as an effective binding # @raise ArgumentError if the given argument has the wrong type, or if model is invalid in some way @@ -111,7 +55,6 @@ class Puppet::Pops::Binder::Binder def define_layers(layered_bindings) raise ArgumentError, "This binder is already configured. Cannot redefine its content." if configured?() - raise ArgumentError, "Categories must be defined first" if @category_precedences.empty? LayerProcessor.new(self, key_factory).bind(layered_bindings) injector_entries.each do |k,v| unless key_factory.is_contributions_key?(k) || v.is_resolved?() @@ -163,8 +106,6 @@ class Puppet::Pops::Binder::Binder # @api private # class LayerProcessor - attr :effective_prec - attr :prec_stack attr :bindings attr :binder attr :key_factory @@ -173,8 +114,6 @@ class Puppet::Pops::Binder::Binder def initialize(binder, key_factory) @binder = binder @key_factory = key_factory - @prec_stack = [] - @effective_prec = nil @bindings = [] @contributions = [] @@bind_visitor ||= Puppet::Pops::Visitor.new(nil,"bind",0,0) @@ -184,14 +123,14 @@ class Puppet::Pops::Binder::Binder # @api private # def add(b) - bindings << Puppet::Pops::Binder::InjectorEntry.new(effective_prec, b) + bindings << Puppet::Pops::Binder::InjectorEntry.new(b) end # Add a multibind contribution # @api private # def add_contribution(b) - contributions << Puppet::Pops::Binder::InjectorEntry.new(effective_prec, b) + contributions << Puppet::Pops::Binder::InjectorEntry.new(b) end # Bind given abstract binding @@ -204,12 +143,17 @@ class Puppet::Pops::Binder::Binder # @return [Puppet::Pops::Binder::InjectorEntry] the entry with the highest (category) precedence # @api private def highest(b1, b2) - case b1.precedence <=> b2.precedence - when 1 - b1 - when -1 - b2 - when 0 + if b1.is_abstract? != b2.is_abstract? + # if one is abstract and the other is not, the non abstract wins + b1.is_abstract? ? b2 : b1 + else +# case b1.precedence <=> b2.precedence +# when 1 +# b1 +# when -1 +# b2 +# when 0 + raise_conflicting_binding(b1, b2) end end @@ -276,33 +220,6 @@ class Puppet::Pops::Binder::Binder ! binding.multibind_id.nil? end - # @api private - def push_precedences(precedences) - prec_stack.push(precedences) - @effective_prec = nil # clear cache - end - - # @api private - def pop_precedences() - prec_stack.pop() - @effective_prec = nil # clear cache - end - - # Returns the effective precedence as an array with highest precedence first. - # Internally the precedence is an array with the highest precedence first. - # - # @api private - # - def effective_prec() - unless @effective_prec - @effective_prec = prec_stack.flatten.uniq.sort.reverse - if @effective_prec.size == 0 - @effective_prec = [ 0 ] # i.e. "common" - end - end - @effective_prec - end - # @api private def bind_Binding(o) if is_contribution?(o) @@ -323,27 +240,6 @@ class Puppet::Pops::Binder::Binder o.bindings.each {|b| bind(b) } end - # Process CategorizedBindings by calculating precedence, and then if satisfying the predicates, process the contained - # bindings. - # @api private - # - def bind_CategorizedBindings(o) - precedences = o.predicates.collect do |p| - prec = binder.category_precedences[p.categorization] - - # Skip bindings if the categorization is not present, or - # if the category value is not the effective value for the categorization - # Ignore the value for the common category (it is not possible to state common 'false' etc.) - # - return unless prec - return unless binder.category_values[p.categorization] == p.value.downcase || p.categorization == 'common' - prec - end - push_precedences(precedences) - o.bindings.each {|b| bind(b) } - pop_precedences() - end - # Process layered bindings from highest to lowest layer # @api private # @@ -388,6 +284,7 @@ class Puppet::Pops::Binder::Binder # if already found in this layer, one wins (and resolves override), or it is an error existing = this_layer[bkey] + # TODO: highest is not really needed - it is an error at all times when there are no categories winner = existing ? highest(existing, b) : b this_layer[bkey] = winner if existing diff --git a/lib/puppet/pops/binder/binder_issues.rb b/lib/puppet/pops/binder/binder_issues.rb index 1258b1e78..a23e7735a 100644 --- a/lib/puppet/pops/binder/binder_issues.rb +++ b/lib/puppet/pops/binder/binder_issues.rb @@ -104,16 +104,6 @@ module Puppet::Pops::Binder::BinderIssues "#{label.a_an_uc(semantic)} has zero predicates" end - # @api public - MISSING_CATEGORIZATION = issue :MISSING_CATEGORIZATION do - "#{label.a_an_uc(semantic)} has a category without categorization" - end - - # @api public - MISSING_CATEGORY_VALUE = issue :MISSING_CATEGORY_VALUE do - "#{label.a_an_uc(semantic)} has a category without value" - end - # @api public MISSING_LAYERS = issue :MISSING_LAYERS do "#{label.a_an_uc(semantic)} has zero layers" @@ -129,14 +119,4 @@ module Puppet::Pops::Binder::BinderIssues "#{label.a_an_uc(semantic)} has zero bindings in #{label.label(layer)}" end - # @api public - PRECEDENCE_MISMATCH_IN_CONTRIBUTION = issue :PRECEDENCE_MISMATCH_IN_CONTRIBUTION, :categorization do - "Precedence mismatch: binding contribution '#{semantic.name}', category: '#{categorization}' is not in correct order" - end - - # @api public - MISSING_CATEGORY_PRECEDENCE = issue :MISSING_CATEGORY_PRECEDENCE, :categorization do - "Missing category precedence: binding contribution '#{semantic.name}', category: '#{categorization}' not found in overall config" - end - end \ No newline at end of file diff --git a/lib/puppet/pops/binder/bindings_checker.rb b/lib/puppet/pops/binder/bindings_checker.rb index dad4bd7c2..6e436db72 100644 --- a/lib/puppet/pops/binder/bindings_checker.rb +++ b/lib/puppet/pops/binder/bindings_checker.rb @@ -71,30 +71,10 @@ class Puppet::Pops::Binder::BindingsChecker check_Bindings(b) end - # Check that the category has a categorization and a value - # @api private - def check_Category(c) - acceptor.accept(Issues::MISSING_CATEGORIZATION, binding_parent(c)) unless has_chars?(c.categorization) - acceptor.accept(Issues::MISSING_CATEGORY_VALUE, binding_parent(c)) unless has_chars?(c.value) - end - - # Check that the binding contains at least one predicate and that all predicates are categorized and has a value - # @api private - def check_CategorizedBindings(b) - acceptor.accept(Issues::MISSING_PREDICATES, b) unless has_entries?(b.predicates) - check_Bindings(b) - end - - # @api private - def check_EffectiveCategories(ec) - end - # Check layer has a name # @api private def check_NamedLayer(l) acceptor.accept(Issues::MISSING_LAYER_NAME, binding_parent(l)) unless has_chars?(l.name) -# It is ok to have an empty layer -# acceptor.accept(Issues::MISSING_BINDINGS_IN_LAYER, binding_parent(l), { :layer => l.name }) unless has_entries?(l.bindings) end # Checks that the binding has layers and that each layer has a name and at least one binding diff --git a/lib/puppet/pops/binder/bindings_composer.rb b/lib/puppet/pops/binder/bindings_composer.rb index ee25816e5..86be89ef0 100644 --- a/lib/puppet/pops/binder/bindings_composer.rb +++ b/lib/puppet/pops/binder/bindings_composer.rb @@ -68,7 +68,7 @@ class Puppet::Pops::Binder::BindingsComposer scheme_extensions.each_pair do |scheme, class_name| # turn each scheme => class_name into a binding (contribute to the buildings-schemes multibind). # do this in category 'extensions' to allow them to override the 'default' - when_in_category('extension', 'true').bind do + bind do name(scheme) instance_of(::Puppetx::BINDINGS_SCHEMES_TYPE) in_multibind(::Puppetx::BINDINGS_SCHEMES) @@ -95,7 +95,7 @@ class Puppet::Pops::Binder::BindingsComposer factory = Puppet::Pops::Binder::BindingsFactory contributions = [] configured_layers = @config.layering_config.collect do | layer_config | - # get contributions with effective categories + # get contributions contribs = configure_layer(layer_config, scope, diagnostics) # collect the contributions separately for later checking of category precedence contributions.concat(contribs) @@ -103,12 +103,6 @@ class Puppet::Pops::Binder::BindingsComposer factory.named_layer(layer_config['name'], *contribs.collect {|c| c.bindings }.flatten) end - # must check all contributions are based on compatible category precedence - # (Note that contributions no longer contains the bindings as a side effect of setting them in the collected - # layer. The effective categories and the name remains in the contributed model; this is enough for checking - # and error reporting). - check_contribution_precedence(contributions) - # Add the two system layers; the final - highest ("can not be overridden" layer), and the lowest # Everything here can be overridden 'default' layer. # @@ -119,57 +113,8 @@ class Puppet::Pops::Binder::BindingsComposer factory.layered_bindings(*configured_layers) end - # Evaluates configured categorization and returns the result. - # The result is not cached. - # @api public - # - def effective_categories(scope) - unevaluated_categories = @config.categorization - parser = Puppet::Pops::Parser::EvaluatingParser.new() - file_source = @config.config_file or "defaults in: #{__FILE__}" - evaluated_categories = unevaluated_categories.collect do |category_tuple| - evaluated_categories = [ category_tuple[0], parser.evaluate_string( scope, parser.quote( category_tuple[1] ), file_source ) ] - if evaluated_categories[1].is_a?(String) - # category values are always in lower case - evaluated_categories[1] = evaluated_categories[1].downcase - else - raise ArgumentError, "Categorization value must be a string, category #{evaluated_categories[0]} evaluation resulted in a: '#{result[1].class}'" - end - evaluated_categories - end - Puppet::Pops::Binder::BindingsFactory::categories(evaluated_categories) - end - private - # Checks that contribution's effective categorization is in the same relative order as in the overall - # categorization precedence. - # - def check_contribution_precedence(contributions) - cat_prec = { } - @config.categorization.each_with_index {|c, i| cat_prec[ c[0] ] = i } - contributions.each() do |contrib| - # Contributions that do not specify their opinion about categorization silently accepts the precedence - # set in the root configuration - and may thus produce an unexpected result - # - next unless ec = contrib.effective_categories - next unless categories = ec.categories - prev_prec = -1 - categories.each do |c| - prec = cat_prec[c.categorization] - issues = Puppet::Pops::Binder::BinderIssues - unless prec - diagnostics.accept(issues::MISSING_CATEGORY_PRECEDENCE, c, :categorization => c.categorization) - next - end - unless prec > prev_prec - diagnostics.accept(issues::PRECEDENCE_MISMATCH_IN_CONTRIBUTION, c, :categorization => c.categorization) - end - prev_prec = prec - end - end - end - def configure_layer(layer_description, scope, diagnostics) name = layer_description['name'] diff --git a/lib/puppet/pops/binder/bindings_factory.rb b/lib/puppet/pops/binder/bindings_factory.rb index 7c8f02ac7..6c4492588 100644 --- a/lib/puppet/pops/binder/bindings_factory.rb +++ b/lib/puppet/pops/binder/bindings_factory.rb @@ -154,39 +154,6 @@ module Puppet::Pops::Binder::BindingsFactory builder.instance_eval(&block) if block_given? builder end - - # Adds a categorized bindings to this container. Returns a BindingsContainerBuilder to allow adding - # bindings in the newly created container. An optional block may be given which is evaluated using `instance_eval`. - # @param categorization [String] the name of the categorization e.g. 'node' - # @param category_value [String] the value in that category e.g. 'kermit.example.com' - # @return [BindingsContainerBuilder] the builder for the created categorized bindings container - # @api public - # - def when_in_category(categorization, category_value, &block) - when_in_categories({categorization => category_value}, &block) - end - - # Adds a categorized bindings to this container. Returns a BindingsContainerBuilder to allow adding - # bindings in the newly created container. - # The result is that a processed request must match all the given categorizations - # with the given values. An optional block may be given which is evaluated using `instance_eval`. - # @param categories_hash Hash[String, String] a hash with categorization and categorization value entries - # @return [BindingsContainerBuilder] the builder for the created categorized bindings container - # @api public - # - def when_in_categories(categories_hash, &block) - binding = Puppet::Pops::Binder::Bindings::CategorizedBindings.new() - categories_hash.each do |k,v| - pred = Puppet::Pops::Binder::Bindings::Category.new() - pred.categorization = k - pred.value = v - binding.addPredicates(pred) - end - model.addBindings(binding) - builder = BindingsContainerBuilder.new(binding) - builder.instance_eval(&block) if block_given? - builder - end end # Builds a Binding via convenience methods. @@ -619,15 +586,12 @@ module Puppet::Pops::Binder::BindingsFactory # @param name [String] the name of the contributed bindings (for human use in messages/logs only) # @param named_bindings [Puppet::Pops::Binder::Bindings::NamedBindings, Array] the # named bindings to include - # @param effective_categories [Puppet::Pops::Binder::Bindings::EffectiveCategories] the contributors opinion about categorization - # this is used to ensure consistent use of categories. # - def self.contributed_bindings(name, named_bindings, effective_categories) + def self.contributed_bindings(name, named_bindings) cb = Puppet::Pops::Binder::Bindings::ContributedBindings.new() cb.name = name named_bindings = [named_bindings] unless named_bindings.is_a?(Array) named_bindings.each {|b| cb.addBindings(b) } - cb.effective_categories = effective_categories cb end @@ -778,21 +742,6 @@ module Puppet::Pops::Binder::BindingsFactory p end - # Creates an EffectiveCategories from a list of tuples `[categorization category ...]`, or `[[categorization category] ...]` - # This method is used by backends to create a model of the effective categories. - # @api public - # - def self.categories(tuple_array) - result = Puppet::Pops::Binder::Bindings::EffectiveCategories.new() - tuple_array.flatten.each_slice(2) do |c| - cat = Puppet::Pops::Binder::Bindings::Category.new() - cat.categorization = c[0] - cat.value = c[1] - result.addCategories(cat) - end - result - end - # Creates a NamedLayer. This is used by the bindings system to create a model of the layers. # # @api public diff --git a/lib/puppet/pops/binder/bindings_label_provider.rb b/lib/puppet/pops/binder/bindings_label_provider.rb index f7555468a..ac10f7677 100644 --- a/lib/puppet/pops/binder/bindings_label_provider.rb +++ b/lib/puppet/pops/binder/bindings_label_provider.rb @@ -28,11 +28,8 @@ class Puppet::Pops::Binder::BindingsLabelProvider < Puppet::Pops::LabelProvider def label_HashMultibindProducerDescriptor o ; "Hash Multibind Producer" end def label_Bindings o ; "Bindings" end def label_NamedBindings o ; "Named Bindings" end - def label_Category o ; "Category '#{o.categorization}/#{o.value}'" end - def label_CategorizedBindings o ; "Categorized Bindings" end def label_LayeredBindings o ; "Layered Bindings" end def label_NamedLayer o ; "Layer '#{o.name}'" end - def label_EffectiveCategories o ; "Effective Categories" end def label_ContributedBindings o ; "Contributed Bindings" end def label_NamedArgument o ; "Named Argument" end diff --git a/lib/puppet/pops/binder/bindings_model.rb b/lib/puppet/pops/binder/bindings_model.rb index aa01e1366..3ca3b6d51 100644 --- a/lib/puppet/pops/binder/bindings_model.rb +++ b/lib/puppet/pops/binder/bindings_model.rb @@ -163,23 +163,6 @@ module Puppet::Pops::Binder::Bindings has_attr 'name', String end - # A category predicate (the request has to be in this category). - # @api public - # - class Category < Puppet::Pops::Model::PopsObject - has_attr 'categorization', String, :lowerBound => 1 - has_attr 'value', String, :lowerBound => 1 - end - - # A container of Binding instances that are in effect when the - # predicates (min one) evaluates to true. Multiple predicates are handles as an 'and'. - # Note that 'or' semantics are handled by repeating the same rules. - # @api public - # - class CategorizedBindings < Bindings - contains_many_uni 'predicates', Category, :lowerBound => 1 - end - # A named layer of bindings having the same priority. # @api public class NamedLayer < Puppet::Pops::Model::PopsObject @@ -194,22 +177,13 @@ module Puppet::Pops::Binder::Bindings contains_many_uni 'layers', NamedLayer end - # A list of categories consisting of categroization name and category value (i.e. the *state of the request*) - # @api public - # - class EffectiveCategories < Puppet::Pops::Model::PopsObject - # The order is from highest precedence to lowest - contains_many_uni 'categories', Category - end - # ContributedBindings is a named container of one or more NamedBindings. - # The intent is that a bindings producer returns a ContributedBindings which in addition to the bindings - # may optionally contain provider's opinion about the precedence of categories, and their category values. - # This enables merging of bindings, and validation of consistency. + # The intent is that a bindings producer returns a ContributedBindings that identifies the contributor + # as opposed to the names of the different set of bindings. The ContributorBindings name is typically + # a technical name that indicates their source (a service). # # @api public # class ContributedBindings < NamedLayer - contains_one_uni 'effective_categories', EffectiveCategories end end diff --git a/lib/puppet/pops/binder/bindings_model_dumper.rb b/lib/puppet/pops/binder/bindings_model_dumper.rb index de62e525f..f47436ea9 100644 --- a/lib/puppet/pops/binder/bindings_model_dumper.rb +++ b/lib/puppet/pops/binder/bindings_model_dumper.rb @@ -171,20 +171,6 @@ class Puppet::Pops::Binder::BindingsModelDumper < Puppet::Pops::Model::TreeDumpe result end - def dump_Category o - ['category', o.categorization, do_dump(o.value)] - end - - def dump_CategorizedBindings o - result = ['when', do_dump(o.predicates), :indent] - o.bindings.each do |b| - result << :break - result << do_dump(b) - end - result << :dedent - result - end - def dump_LayeredBindings o result = ['layers', :indent] o.layers.each do |layer| @@ -195,11 +181,7 @@ class Puppet::Pops::Binder::BindingsModelDumper < Puppet::Pops::Model::TreeDumpe result end - def dump_EffectiveCategories o - ['categories', do_dump(o.categories)] - end - def dump_ContributedBindings o - ['contributed', o.name, do_dump(o.effective_categories), do_dump(o.bindings)] + ['contributed', o.name, do_dump(o.bindings)] end end diff --git a/lib/puppet/pops/binder/config/binder_config.rb b/lib/puppet/pops/binder/config/binder_config.rb index c37efc1ee..c24a50571 100644 --- a/lib/puppet/pops/binder/config/binder_config.rb +++ b/lib/puppet/pops/binder/config/binder_config.rb @@ -6,21 +6,14 @@ module Puppet::Pops::Binder::Config # class BinderConfig - # The bindings hierarchy is an array of categorizations where the - # array for each category has exactly three elements - the categorization name, - # category value, and the path that is later used by the backend to read - # the bindings for that category + # The layering configuration is an array of layers from most to least significant. + # Each layer is represented by a Hash containing :name and :include and optionally :exclude # # @return [Array, Hash>] # @api public # attr_reader :layering_config - # @return [Array] Array of Category tuples where Strings are not evaluated. - # @api public - # - attr_reader :categorization - # @return ] ({}) optional mapping of bindings-scheme to handler class name attr_reader :scheme_extensions @@ -33,18 +26,11 @@ module Puppet::Pops::Binder::Config { 'name' => 'modules', 'include' => [ 'module:/*::default'] }, ] - DEFAULT_CATEGORIES = [ - ['node', "${fqdn}"], - ['osfamily', "${osfamily}"], - ['environment', "${environment}"], - ['common', "true"] - ] - DEFAULT_SCHEME_EXTENSIONS = {} def default_config() # This is hardcoded now, but may be a user supplied default configuration later - {'version' => 1, 'layers' => default_layers, 'categories' => default_categories} + {'version' => 1, 'layers' => default_layers } end def confdir() @@ -99,11 +85,9 @@ module Puppet::Pops::Binder::Config unless diagnostics.errors? @layering_config = data['layers'] or default_layers - @categorization = data['categories'] or default_categories @scheme_extensions = (data['extensions'] and data['extensions']['scheme_handlers'] or default_scheme_extensions) else @layering_config = [] - @categorization = {} @scheme_extensions = {} end end @@ -115,11 +99,6 @@ module Puppet::Pops::Binder::Config DEFAULT_LAYERS end - # @api private - def default_categories - DEFAULT_CATEGORIES - end - # @api private def default_scheme_extensions DEFAULT_SCHEME_EXTENSIONS diff --git a/lib/puppet/pops/binder/config/binder_config_checker.rb b/lib/puppet/pops/binder/config/binder_config_checker.rb index 68d5c86ca..a65623dca 100644 --- a/lib/puppet/pops/binder/config/binder_config_checker.rb +++ b/lib/puppet/pops/binder/config/binder_config_checker.rb @@ -42,12 +42,6 @@ module Puppet::Pops::Binder::Config accept(Issues::CONFIG_LAYERS_MISSING, config_file) end - if categories = (data['categories'] || data[:categories]) - check_categories(categories, config_file) - else - accept(Issues::CONFIG_CATEGORIES_MISSING, config_file) - end - if version = (data['version'] or data[:version]) accept(Issues::CONFIG_WRONG_VERSION, config_file, {:expected => 1, :actual => version}) unless version == 1 else @@ -97,30 +91,6 @@ module Puppet::Pops::Binder::Config end end - def check_categories(categories, config_file) - unless categories.is_a?(Array) - accept(Issues::CATEGORIES_IS_NOT_ARRAY, config_file, :klass => categories.class) - else - categories.each { |entry| check_category(entry, config_file) } - end - end - - def check_category(category, config_file) - type = @type_calculator.infer(category) - unless @type_calculator.assignable?(@array_of_string_type, type) - require 'debugger'; debugger - accept(Issues::CATEGORY_IS_NOT_ARRAY, config_file, :type => @type_calculator.string(type)) - return - end - unless category.size == 2 - accept(Issues::CATEGORY_NOT_TWO_STRINGS, config_file, :count => category.size) - return - end - unless category[0] =~ /[a-z][a-zA-Z0-9_]*/ - accept(Issues::INVALID_CATEGORY_NAME, config_file, :name => category[0]) - end - end - # references to bindings is a single String URI, or an array of String URI # @param kind [String] 'include' or 'exclude' (used in issue messages) # @param value [String, Array] one or more String URI binding references @@ -132,12 +102,7 @@ module Puppet::Pops::Binder::Config value.each {|ref| check_reference(ref, kind, config_file) } end - # A reference is a URI in string form having one of the schemes: - # - module-hiera - # - confdir-hiera - # - enc - # - # and with a path (at least '/') + # A reference is a URI in string form having a scheme and a path (at least '/') # def check_reference(value, kind, config_file) begin diff --git a/lib/puppet/pops/binder/config/issues.rb b/lib/puppet/pops/binder/config/issues.rb index 221184eda..11e37fb32 100644 --- a/lib/puppet/pops/binder/config/issues.rb +++ b/lib/puppet/pops/binder/config/issues.rb @@ -24,30 +24,10 @@ module Puppet::Pops::Binder::Config::Issues "The configuration file '#{semantic}' has no 'version' entry in the top level hash" end - CONFIG_CATEGORIES_MISSING = issue :CONFIG_CATEGORIES_MISSING do - "The configuration file '#{semantic}' has no 'categories' entry in the top level hash" - end - LAYERS_IS_NOT_ARRAY = issue :LAYERS_IS_NOT_ARRAY, :klass do "The configuration file '#{semantic}' should contain a 'layers' key with an Array value, got: #{klass.name}" end - CATEGORIES_IS_NOT_ARRAY = issue :CATEGORIES_IS_NOT_ARRAY, :klass do - "The configuration file '#{semantic}' should contain a 'categories' key with an Array value, got: #{klass.name}" - end - - CATEGORY_IS_NOT_ARRAY = issue :CATEGORY_IS_NOT_ARRAY, :klass do - "The configuration file '#{semantic}' each entry in 'categories' should be an Array(String, String), got: #{klass.name}" - end - - CATEGORY_NOT_TWO_STRINGS = issue :CATEGORY_NOT_TWO_STRINGS, :count do - "The configuration file '#{semantic}' each entry in 'categories' should be an array with 2 strings, got: #{count}" - end - - INVALID_CATEGORY_NAME = issue :INVALID_CATEGORY_NAME, :name do - "The configuration file '#{semantic}' contains the invalid category: '#{name}', must match /[a-z][a-zA-Z0-9_]*/" - end - LAYER_IS_NOT_HASH = issue :LAYER_IS_NOT_HASH, :klass do "The configuration file '#{semantic}' should contain one hash per layer, got #{klass.name} instead of Hash" end diff --git a/lib/puppet/pops/binder/injector_entry.rb b/lib/puppet/pops/binder/injector_entry.rb index f61773706..7e89bb7c6 100644 --- a/lib/puppet/pops/binder/injector_entry.rb +++ b/lib/puppet/pops/binder/injector_entry.rb @@ -3,7 +3,7 @@ # @api public # class Puppet::Pops::Binder::InjectorEntry - # @return [Object] An opaque object representing the precedence + # @return [Object] An opaque (comparable) object representing the precedence # @api public attr_reader :precedence @@ -18,7 +18,7 @@ class Puppet::Pops::Binder::InjectorEntry attr_accessor :cached_producer # @api private - def initialize(precedence, binding) + def initialize(binding, precedence = 0) @precedence = precedence.freeze @binding = binding @cached_producer = nil diff --git a/lib/puppet/pops/binder/scheme_handler/symbolic_scheme.rb b/lib/puppet/pops/binder/scheme_handler/symbolic_scheme.rb index af8c9c786..0cfb0c486 100644 --- a/lib/puppet/pops/binder/scheme_handler/symbolic_scheme.rb +++ b/lib/puppet/pops/binder/scheme_handler/symbolic_scheme.rb @@ -21,8 +21,7 @@ class Puppet::Pops::Binder::SchemeHandler::SymbolicScheme < Puppetx::Puppet::Bin raise ArgumentError, "Cannot load bindings '#{uri}' - no bindings found." unless bindings # Must clone as the the rest mutates the model cloned_bindings = Marshal.load(Marshal.dump(bindings)) - # Give no effective categories (i.e. ok with whatever categories there is) - Puppet::Pops::Binder::BindingsFactory.contributed_bindings(fqn, cloned_bindings, nil) + Puppet::Pops::Binder::BindingsFactory.contributed_bindings(fqn, cloned_bindings) end # @api private diff --git a/lib/puppet/pops/binder/system_bindings.rb b/lib/puppet/pops/binder/system_bindings.rb index 557074825..33bbd71f9 100644 --- a/lib/puppet/pops/binder/system_bindings.rb +++ b/lib/puppet/pops/binder/system_bindings.rb @@ -20,18 +20,12 @@ class Puppet::Pops::Binder::SystemBindings @injector_boot_bindings end -# def self.env_boot_bindings() -# Puppet::Bindings[Puppet::Pops::Binder::SystemBindings::ENVIRONMENT_BOOT_BINDINGS_NAME] -# end - def self.final_contribution - effective_categories = Factory.categories([['common', 'true']]) - Factory.contributed_bindings("puppet-final", [deep_clone(@extension_bindings.model)], effective_categories) + Factory.contributed_bindings("puppet-final", [deep_clone(@extension_bindings.model)]) end def self.default_contribution - effective_categories = Factory.categories([['common', 'true']]) - Factory.contributed_bindings("puppet-default", [deep_clone(@default_bindings.model)], effective_categories) + Factory.contributed_bindings("puppet-default", [deep_clone(@default_bindings.model)]) end def self.injector_boot_contribution(env_boot_bindings) @@ -46,14 +40,8 @@ class Puppet::Pops::Binder::SystemBindings # bindings << env_boot_bindings unless env_boot_bindings.nil? - # Use an 'extension' category for extension bindings to allow them to override the default - # bindings since they are placed in the same layer (this is done to avoid having a separate layer). - # The purpose for allowing overrides is that someone may want to replace say 'yaml' with a different version, - # (say one that uses a YAML implementation that actually works ok in ruby 1.8.7 ;-)), an encrypted parser, etc. - effective_categories = Factory.categories([['extension', 'true'],['common', 'true']]) - - # return the composition and the cateogires. - Factory.contributed_bindings("puppet-injector-boot", bindings, effective_categories) + # return the composition + Factory.contributed_bindings("puppet-injector-boot", bindings) end def self.factory() diff --git a/spec/fixtures/unit/pops/binder/bindings_composer/hiera1config/binder_config.yaml b/spec/fixtures/unit/pops/binder/bindings_composer/hiera1config/binder_config.yaml deleted file mode 100644 index 13ef73157..000000000 --- a/spec/fixtures/unit/pops/binder/bindings_composer/hiera1config/binder_config.yaml +++ /dev/null @@ -1,12 +0,0 @@ ---- -version: 1 -layers: - [{name: site, include: 'confdir:/default?optional'}, - {name: modules, include: ['module:/*::default'] } - ] -categories: - [['node', '$fqdn'], - ['environment', '${environment}'], - ['osfamily', '${osfamily}'], - ['common', 'true'] - ] diff --git a/spec/fixtures/unit/pops/binder/bindings_composer/hiera1config/hiera.yaml b/spec/fixtures/unit/pops/binder/bindings_composer/hiera1config/hiera.yaml deleted file mode 100644 index 717eacc4b..000000000 --- a/spec/fixtures/unit/pops/binder/bindings_composer/hiera1config/hiera.yaml +++ /dev/null @@ -1,8 +0,0 @@ ---- -hierarchy: - - '%fqdn' - - 'common' - -backends: - - yaml - - json diff --git a/spec/fixtures/unit/pops/binder/bindings_composer/hiera1config/modules/good/lib/puppet/bindings/good/default.rb b/spec/fixtures/unit/pops/binder/bindings_composer/hiera1config/modules/good/lib/puppet/bindings/good/default.rb deleted file mode 100644 index 4bcd500b0..000000000 --- a/spec/fixtures/unit/pops/binder/bindings_composer/hiera1config/modules/good/lib/puppet/bindings/good/default.rb +++ /dev/null @@ -1,6 +0,0 @@ -Puppet::Bindings.newbindings('good::default') do |scope| - bind { - name 'the_meaning_of_life' - to 3000 - } -end \ No newline at end of file diff --git a/spec/fixtures/unit/pops/binder/bindings_composer/ok/binder_config.yaml b/spec/fixtures/unit/pops/binder/bindings_composer/ok/binder_config.yaml index 1a1de27f6..f7a6f8c4d 100644 --- a/spec/fixtures/unit/pops/binder/bindings_composer/ok/binder_config.yaml +++ b/spec/fixtures/unit/pops/binder/bindings_composer/ok/binder_config.yaml @@ -5,12 +5,6 @@ layers: {name: test, include: 'echo:/quick/brown/fox'}, {name: modules, include: ['module:/*::default'], exclude: 'module:/bad::default/' } ] -categories: - [['node', '$fqdn'], - ['environment', '${environment}'], - ['osfamily', '${osfamily}'], - ['common', 'true'] - ] extensions: scheme_handlers: echo: 'Puppetx::Awesome2::EchoSchemeHandler' diff --git a/spec/fixtures/unit/pops/binder/bindings_composer/ok/lib/puppet/bindings/confdirtest.rb b/spec/fixtures/unit/pops/binder/bindings_composer/ok/lib/puppet/bindings/confdirtest.rb index 6973c4775..096d1d938 100644 --- a/spec/fixtures/unit/pops/binder/bindings_composer/ok/lib/puppet/bindings/confdirtest.rb +++ b/spec/fixtures/unit/pops/binder/bindings_composer/ok/lib/puppet/bindings/confdirtest.rb @@ -3,10 +3,8 @@ Puppet::Bindings.newbindings('confdirtest') do |scope| name 'has_funny_hat' to 'the pope' } - when_in_category('node', 'localhost') { - bind { - name 'the_meaning_of_life' - to 42 - } + bind { + name 'the_meaning_of_life' + to 42 } end \ No newline at end of file diff --git a/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome2/lib/puppet/bindings/awesome2/default.rb b/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome2/lib/puppet/bindings/awesome2/default.rb index 5c671c82c..eb686a8da 100644 --- a/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome2/lib/puppet/bindings/awesome2/default.rb +++ b/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome2/lib/puppet/bindings/awesome2/default.rb @@ -13,10 +13,8 @@ Puppet::Bindings.newbindings('awesome2::default') do |scope| name 'has_funny_hat' to 'kkk' } - when_in_category('node', 'localhost') { - bind { - name 'good_x' - to 'golden' - } + bind { + name 'good_x' + to 'golden' } end \ No newline at end of file diff --git a/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome2/lib/puppetx/awesome2/echo_scheme_handler.rb b/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome2/lib/puppetx/awesome2/echo_scheme_handler.rb index 4fb29ee38..8d51ab7ad 100644 --- a/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome2/lib/puppetx/awesome2/echo_scheme_handler.rb +++ b/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/awesome2/lib/puppetx/awesome2/echo_scheme_handler.rb @@ -11,7 +11,7 @@ module Puppetx factory = ::Puppet::Pops::Binder::BindingsFactory bindings = factory.named_bindings("echo") bindings.bind.name(uri.path.gsub(/\//, '::')).to("echo: #{uri.path.gsub(/\//, ' ').strip!}") - result = factory.contributed_bindings("echo", bindings.model, nil) + result = factory.contributed_bindings("echo", bindings.model) ### , nil) end end end diff --git a/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/good/lib/puppet/bindings/good/default.rb b/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/good/lib/puppet/bindings/good/default.rb index 5f7139a53..76b6cc410 100644 --- a/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/good/lib/puppet/bindings/good/default.rb +++ b/spec/fixtures/unit/pops/binder/bindings_composer/ok/modules/good/lib/puppet/bindings/good/default.rb @@ -1,8 +1,4 @@ Puppet::Bindings.newbindings('good::default') do |scope| - bind { - name 'good_x' - to 'decent' - } bind { name 'the_meaning_of_life' to 300 diff --git a/spec/unit/parser/functions/lookup_spec.rb b/spec/unit/parser/functions/lookup_spec.rb index 18afcae69..8d4440a32 100644 --- a/spec/unit/parser/functions/lookup_spec.rb +++ b/spec/unit/parser/functions/lookup_spec.rb @@ -134,7 +134,6 @@ describe "lookup function" do def bound(local_bindings) binder = Puppet::Pops::Binder::Binder.new - binder.define_categories(Puppet::Pops::Binder::BindingsFactory.categories([])) binder.define_layers(Puppet::Pops::Binder::BindingsFactory.layered_bindings(Puppet::Pops::Binder::BindingsFactory.named_layer('test layer', local_bindings.model))) binder end diff --git a/spec/unit/pops/binder/binder_spec.rb b/spec/unit/pops/binder/binder_spec.rb index dfcf633e1..9450f04c3 100644 --- a/spec/unit/pops/binder/binder_spec.rb +++ b/spec/unit/pops/binder/binder_spec.rb @@ -22,39 +22,19 @@ end describe 'Binder' do include BinderSpecModule - context 'when defining categories' do - it 'redefinition is not allowed' do - expect do - b = binder() - b.define_categories(factory.categories([])) - b.define_categories(factory.categories([])) - end.to raise_error(/Cannot redefine/) - end - end - context 'when defining layers' do - it 'they must be defined after categories' do - expect do - binder().define_layers(factory.layered_bindings(test_layer_with_empty_bindings)) - end.to raise_error(/Categories must be defined first/) - end it 'redefinition is not allowed' do expect do b = binder() - b.define_categories(factory.categories([])) b.define_layers(factory.layered_bindings(test_layer_with_empty_bindings)) b.define_layers(factory.layered_bindings(test_layer_with_empty_bindings)) end.to raise_error(/Cannot redefine its content/) end - end - context 'when defining categories and layers' do - it 'a binder should report being configured when both categories and layers have been defined' do + it 'a binder should report being configured when layers have been defined' do b = binder() b.configured?().should == false - b.define_categories(factory.categories([])) - b.configured?().should == false b.define_layers(factory.layered_bindings(test_layer_with_empty_bindings)) b.configured?().should == true end diff --git a/spec/unit/pops/binder/bindings_checker_spec.rb b/spec/unit/pops/binder/bindings_checker_spec.rb index dc3934c6c..d17d8b422 100644 --- a/spec/unit/pops/binder/bindings_checker_spec.rb +++ b/spec/unit/pops/binder/bindings_checker_spec.rb @@ -38,19 +38,19 @@ describe 'The bindings checker' do b end - def category(name, value) - b = Bindings::Category.new() - b.categorization = name - b.value = value - b - end - - def categorized_bindings(bindings, *predicates) - b = Bindings::CategorizedBindings.new() - b.bindings = bindings - b.predicates = predicates - b - end +# def category(name, value) +# b = Bindings::Category.new() +# b.categorization = name +# b.value = value +# b +# end +# +# def categorized_bindings(bindings, *predicates) +# b = Bindings::CategorizedBindings.new() +# b.bindings = bindings +# b.predicates = predicates +# b +# end def layer(name, *bindings) l = Bindings::NamedLayer.new() @@ -145,33 +145,6 @@ describe 'The bindings checker' do end end - context 'when checking categorized bindings' do - it 'should accept non-zero predicates' do - validate(categorized_bindings([ok_binding], category('foo', 'bar'))) - acceptor.errors_or_warnings?.should() == false - end - - it 'should not accept zero predicates' do - validate(categorized_bindings([ok_binding])) - acceptor.should have_issue(Issues::MISSING_PREDICATES) - end - - it 'should not accept predicates that has no categorization' do - validate(categorized_bindings([ok_binding], category(nil, 'bar'))) - acceptor.should have_issue(Issues::MISSING_CATEGORIZATION) - end - - it 'should not accept predicates that has no value' do - validate(categorized_bindings([ok_binding], category('foo', nil))) - acceptor.should have_issue(Issues::MISSING_CATEGORY_VALUE) - end - - it 'should do generic bindings check' do - validate(categorized_bindings([], category('foo', 'bar'))) - acceptor.should have_issue(Issues::MISSING_BINDINGS) - end - end - context 'when checking layered bindings' do it 'should not accept zero layers' do validate(layered_bindings()) diff --git a/spec/unit/pops/binder/bindings_composer_spec.rb b/spec/unit/pops/binder/bindings_composer_spec.rb index 52451593b..073b7e3d3 100644 --- a/spec/unit/pops/binder/bindings_composer_spec.rb +++ b/spec/unit/pops/binder/bindings_composer_spec.rb @@ -47,8 +47,6 @@ describe 'BinderComposer' do layered_bindings = composer.compose(scope) # puts Puppet::Pops::Binder::BindingsModelDumper.new().dump(layered_bindings) binder = Puppet::Pops::Binder::Binder.new() - # TODO: this is cheating, the categories should come from the composer/config - binder.define_categories(factory.categories([['node', 'localhost'], ['environment', 'production']])) binder.define_layers(layered_bindings) injector = Puppet::Pops::Binder::Injector.new(binder) expect(injector.lookup(scope, 'awesome_x')).to be == 'golden' @@ -62,34 +60,6 @@ describe 'BinderComposer' do end end - context "when loading a configuration with hiera1 hiera.yaml" do - let(:config_directory) { config_dir('hiera1config') } - - it 'should load without errors by skipping the hiera.yaml' do - Puppet.settings[:confdir] = config_directory - Puppet.settings[:libdir] = File.join(config_directory, 'lib') - Puppet.settings[:modulepath] = File.join(config_directory, 'modules') - # this ensure the binder is active at the right time - # (issues with getting a /dev/null path for "confdir" / "libdir") - raise "Binder not active" unless scope.compiler.is_binder_active? - - diagnostics = diag - composer = Puppet::Pops::Binder::BindingsComposer.new() - the_scope = scope - the_scope['fqdn'] = 'localhost' - the_scope['environment'] = 'production' - layered_bindings = composer.compose(scope) - # puts Puppet::Pops::Binder::BindingsModelDumper.new().dump(layered_bindings) - binder = Puppet::Pops::Binder::Binder.new() - # TODO: this is cheating, the categories should come from the composer/config - binder.define_categories(factory.categories([['node', 'localhost'], ['environment', 'production']])) - binder.define_layers(layered_bindings) - injector = Puppet::Pops::Binder::Injector.new(binder) - - expect(injector.lookup(scope, 'the_meaning_of_life')).to be == 3000 - end - end - # TODO: test error conditions (see BinderConfigChecker for what to test) end \ No newline at end of file diff --git a/spec/unit/pops/binder/config/binder_config_spec.rb b/spec/unit/pops/binder/config/binder_config_spec.rb index 5fd2681d8..67fe0bb6e 100644 --- a/spec/unit/pops/binder/config/binder_config_spec.rb +++ b/spec/unit/pops/binder/config/binder_config_spec.rb @@ -17,13 +17,6 @@ describe 'BinderConfig' do expect(config.layering_config[0]['include']).to be == ['confdir:/default?optional'] expect(config.layering_config[1]['name']).to be == 'modules' expect(config.layering_config[1]['include']).to be == ['module:/*::default'] - - expect(config.categorization.is_a?(Array)).to be == true - expect(config.categorization.size).to be == 4 - expect(config.categorization[0][0]).to be == 'node' - expect(config.categorization[1][0]).to be == 'osfamily' - expect(config.categorization[2][0]).to be == 'environment' - expect(config.categorization[3][0]).to be == 'common' end it 'should load binder_config.yaml if it exists in confdir)' do @@ -35,12 +28,6 @@ describe 'BinderConfig' do expect(config.layering_config[1]['name']).to be == 'modules' expect(config.layering_config[1]['include']).to be == 'module:/*::test/' expect(config.layering_config[1]['exclude']).to be == 'module:/bad::test/' - - expect(config.categorization.is_a?(Array)).to be == true - expect(config.categorization.size).to be == 3 - expect(config.categorization[0][0]).to be == 'node' - expect(config.categorization[1][0]).to be == 'environment' - expect(config.categorization[2][0]).to be == 'common' end # TODO: test error conditions (see BinderConfigChecker for what to test) diff --git a/spec/unit/pops/binder/injector_spec.rb b/spec/unit/pops/binder/injector_spec.rb index 966561ede..e224e1bcd 100644 --- a/spec/unit/pops/binder/injector_spec.rb +++ b/spec/unit/pops/binder/injector_spec.rb @@ -30,11 +30,10 @@ module InjectorSpecModule Puppet::Pops::Types::TypeFactory end - # Returns a binder with the effective categories highest/test, node/kermit, environment/dev (and implicit 'common') + # Returns a binder # - def binder_with_categories + def configured_binder b = Puppet::Pops::Binder::Binder.new() - b.define_categories(factory.categories(['highest', 'test', 'node', 'kermit', 'environment','dev'])) b end @@ -106,36 +105,25 @@ describe 'Injector' do let(:binder) { Puppet::Pops::Binder::Binder.new()} - let(:cbinder) do - b = Puppet::Pops::Binder::Binder.new() - b.define_categories(factory.categories([])) - b - end - let(:lbinder) do - cbinder.define_layers(layered_bindings) + binder.define_layers(layered_bindings) end let(:layered_bindings) { factory.layered_bindings(test_layer_with_bindings(bindings.model)) } - #let(:xinjector) { Puppet::Pops::Binder::Injector.new(lbinder) } context 'When created' do it 'should raise an error when given binder is not configured at all' do expect { Puppet::Pops::Binder::Injector.new(binder()) }.to raise_error(/Given Binder is not configured/) end - it 'should raise an error if binder has categories, but is not completely configured' do - expect { Puppet::Pops::Binder::Injector.new(cbinder) }.to raise_error(/Given Binder is not configured/) - end - it 'should not raise an error if binder is configured' do lbinder.configured?().should == true # of something is very wrong expect { injector(lbinder) }.to_not raise_error end it 'should create an empty injector given an empty binder' do - expect { cbinder.define_layers(layered_bindings) }.to_not raise_exception + expect { binder.define_layers(layered_bindings) }.to_not raise_exception end it "should be possible to reference the TypeCalculator" do @@ -214,48 +202,12 @@ describe 'Injector' do end end - context 'and conditionals are in use' do - let(:binder) { binder_with_categories()} - let(:lbinder) { binder.define_layers(layered_bindings) } - - it "should be possible to shadow a bound value in a higher precedented category" do - bindings.bind().name('a_string').to('42') - bindings.when_in_category('environment', 'dev').bind().name('a_string').to('43') - bindings.when_in_category('node', 'kermit').bind().name('a_string').to('being green') - injector(lbinder).lookup(scope,'a_string').should == 'being green' - end - - it "shadowing should not happen when not in a category" do - bindings.bind().name('a_string').to('42') - bindings.when_in_category('environment', 'dev').bind().name('a_string').to('43') - bindings.when_in_category('node', 'piggy').bind().name('a_string').to('being green') - injector(lbinder).lookup(scope,'a_string').should == '43' - end - - it "multiple predicates makes binding more specific" do - bindings.bind().name('a_string').to('42') - bindings.when_in_category('environment', 'dev').bind().name('a_string').to('43') - bindings.when_in_category('node', 'kermit').bind().name('a_string').to('being green') - bindings.when_in_categories({'node'=>'kermit', 'environment'=>'dev'}).bind().name('a_string').to('being dev green') - injector(lbinder).lookup(scope,'a_string').should == 'being dev green' - end - - it "multiple predicates makes binding more specific, but not more specific than higher precedence" do - bindings.bind().name('a_string').to('42') - bindings.when_in_category('environment', 'dev').bind().name('a_string').to('43') - bindings.when_in_category('node', 'kermit').bind().name('a_string').to('being green') - bindings.when_in_categories({'node'=>'kermit', 'environment'=>'dev'}).bind().name('a_string').to('being dev green') - bindings.when_in_category('highest', 'test').bind().name('a_string').to('bazinga') - injector(lbinder).lookup(scope,'a_string').should == 'bazinga' - end - end - context "and multiple layers are in use" do - let(:binder) { binder_with_categories()} + let(:binder) { configured_binder()} it "a higher layer shadows anything in a lower layer" do bindings1 = factory.named_bindings('test1') - bindings1.when_in_category("highest", "test").bind().name('a_string').to('bad stuff') + bindings1.bind().name('a_string').to('bad stuff') lower_layer = factory.named_layer('lower-layer', bindings1.model) bindings2 = factory.named_bindings('test2') @@ -269,7 +221,7 @@ describe 'Injector' do end context "and dealing with Data types" do - let(:binder) { binder_with_categories()} + let(:binder) { configured_binder()} let(:lbinder) { binder.define_layers(layered_bindings) } it "should treat all data as same type w.r.t. key" do @@ -600,7 +552,6 @@ describe 'Injector' do mb.producer_options(:uniq => :true) - binder.define_categories(factory.categories([])) binder.define_layers(factory.layered_bindings(test_layer_with_bindings(bindings.model))) injector = injector(binder) expect { injector.lookup(scope, 'broken_family0')}.to raise_error(/:conflict_resolution => :append/) @@ -614,31 +565,31 @@ describe 'Injector' do bindings.multibind(multibind_id).type(hash_of_duck).name('donalds_nephews') - mb1 = bindings.when_in_category("highest", "test").bind.in_multibind(multibind_id) + mb1 = bindings.bind.in_multibind(multibind_id) + pending 'priority based on layers not added, and priority on category removed' mb1.type(duck_type).name('nephew').to(InjectorSpecModule::NamedDuck, 'Huey') mb2 = bindings.bind.in_multibind(multibind_id) mb2.type(duck_type).name('nephew').to(InjectorSpecModule::NamedDuck, 'Dewey') - binder.define_categories(factory.categories(['highest', 'test'])) binder.define_layers(layered_bindings) injector(binder).lookup(scope, hash_of_duck, "donalds_nephews")['nephew'].name.should == 'Huey' end it "a higher priority contribution wins when resolution is :merge" do + # THIS TEST MAY DEPEND ON HASH ORDER SINCE PRIORITY BASED ON CATEGORY IS REMOVED hash_of_data = type_factory.hash_of_data() multibind_id = "hashed_ducks" bindings.multibind(multibind_id).type(hash_of_data).name('donalds_nephews').producer_options(:conflict_resolution => :merge) - mb1 = bindings.when_in_category("highest", "test").bind.in_multibind(multibind_id) + mb1 = bindings.bind.in_multibind(multibind_id) mb1.name('nephew').to({'name' => 'Huey', 'is' => 'winner'}) mb2 = bindings.bind.in_multibind(multibind_id) mb2.name('nephew').to({'name' => 'Dewey', 'is' => 'looser', 'has' => 'cap'}) - binder.define_categories(factory.categories(['highest', 'test'])) binder.define_layers(layered_bindings) the_ducks = injector(binder).lookup(scope, "donalds_nephews"); @@ -762,7 +713,7 @@ describe 'Injector' do end end context "When there are problems with configuration" do - let(:binder) { binder_with_categories()} + let(:binder) { configured_binder()} let(:lbinder) { binder.define_layers(layered_bindings) } it "reports error for surfacing abstract bindings" do @@ -772,8 +723,8 @@ describe 'Injector' do it "does not report error for abstract binding that is ovrridden" do bindings.bind.abstract.name('an_int') - bindings.when_in_category('highest', 'test').bind.override.name('an_int').to(142) - expect{injector(lbinder).lookup(scope, 'an_int') }.to_not raise_error + bindings.bind.override.name('an_int').to(142) + expect{ injector(lbinder).lookup(scope, 'an_int') }.to_not raise_error end it "reports error for overriding binding that does not override" do From 44ac0e3ec456a131a1f4ed65672be5ffa638590e Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 30 Dec 2013 15:19:21 +0100 Subject: [PATCH 323/800] (yardoc) Fix yardoc text for Injector and BindingsFactory --- lib/puppet/pops/binder/bindings_factory.rb | 1 - lib/puppet/pops/binder/injector.rb | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/puppet/pops/binder/bindings_factory.rb b/lib/puppet/pops/binder/bindings_factory.rb index 6c4492588..ca54b5b58 100644 --- a/lib/puppet/pops/binder/bindings_factory.rb +++ b/lib/puppet/pops/binder/bindings_factory.rb @@ -78,7 +78,6 @@ # @example Create a NamedBinding with content # result = Puppet::Pops::Binder::BindingsFactory.named_bindings("mymodule::mybindings") do # bind.name("foo").to(42) -# when_in_category("node", "kermit.example.com").bind.name("foo").to(43) # bind.string.name("site url").to("http://www.example.com") # end # result.model() diff --git a/lib/puppet/pops/binder/injector.rb b/lib/puppet/pops/binder/injector.rb index 4660fe658..c46a8c2ea 100644 --- a/lib/puppet/pops/binder/injector.rb +++ b/lib/puppet/pops/binder/injector.rb @@ -70,7 +70,7 @@ # # Access to key factory and type calculator # ----------------------------------------- -# It is important to use the same key factory, and type calculator as the binder. It is therefor possible to obtaint +# It is important to use the same key factory, and type calculator as the binder. It is therefor possible to obtain # these with the methods {#key_factory}, and {#type_calculator}. # # Special support for producers @@ -346,7 +346,8 @@ module Private attr_reader :type_calculator def initialize(configured_binder) - raise ArgumentError, "Given Binder is not configured" unless configured_binder && configured_binder.configured?() + # TODO: Different error message + raise ArgumentError, "Given Binder is not configured" unless configured_binder #&& configured_binder.configured?() @entries = configured_binder.injector_entries() # It is essential that the injector uses the same key factory as the binder since keys must be From b94a4aa71f1edcb5cf947b595e15e4d793f3d9a1 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 30 Dec 2013 15:23:49 +0100 Subject: [PATCH 324/800] (maint) Refactor Binder to not use two step creation. Earlier it was required to first create an instance of Binder, and then ask it to configure categories, and then layers. Now that categories are removed, the creation can be made in one step and the layered bindings are now passed to the constructor. This removes the need to have methods to query if the binder is configured. This also adds a first step in supporting creation of a stacked injector (the binder can not accept a parent binder - this is w.i.p). --- lib/puppet/parser/compiler.rb | 3 +- lib/puppet/pops/binder/binder.rb | 37 ++++++++----------- spec/unit/pops/binder/binder_spec.rb | 29 ++++++++------- .../pops/binder/bindings_composer_spec.rb | 3 +- spec/unit/pops/binder/injector_spec.rb | 31 +++++----------- 5 files changed, 42 insertions(+), 61 deletions(-) diff --git a/lib/puppet/parser/compiler.rb b/lib/puppet/parser/compiler.rb index 01d30fcde..402531854 100644 --- a/lib/puppet/parser/compiler.rb +++ b/lib/puppet/parser/compiler.rb @@ -248,8 +248,7 @@ class Puppet::Parser::Compiler assert_binder_active() boot_contribution = Puppet::Pops::Binder::SystemBindings.injector_boot_contribution(env_boot_bindings) final_contribution = Puppet::Pops::Binder::SystemBindings.final_contribution - binder = Puppet::Pops::Binder::Binder.new() - binder.define_layers(Puppet::Pops::Binder::BindingsFactory.layered_bindings(final_contribution, boot_contribution)) + binder = Puppet::Pops::Binder::Binder.new(Puppet::Pops::Binder::BindingsFactory.layered_bindings(final_contribution, boot_contribution)) @boot_injector = Puppet::Pops::Binder::Injector.new(binder) end diff --git a/lib/puppet/pops/binder/binder.rb b/lib/puppet/pops/binder/binder.rb index 608c041fb..2c3e49217 100644 --- a/lib/puppet/pops/binder/binder.rb +++ b/lib/puppet/pops/binder/binder.rb @@ -15,28 +15,26 @@ class Puppet::Pops::Binder::Binder # @api private attr_reader :key_factory - # Whether the binder is fully configured or not - # @api public - # - attr_reader :configured + # A parent Binder or nil + # @api private + attr_reader :parent + + # The next anonymous key to use + # @api private + attr_reader :anonymous_key # @api public - def initialize + def initialize(layered_bindings, parent_binder=nil) + @parent = parent_binder + @key_factory = Puppet::Pops::Binder::KeyFactory.new() # Resulting hash of all key -> binding @injector_entries = {} - # Not configured until the fat lady sings - @configured = false - - @next_anonymous_key = 0 - end - - # Answers the question 'is this binder configured?' to the point it can be used to instantiate an Injector - # @api public - def configured?() - configured() + # First anonymous key is the parent's next (non incremented key) + @anonymous_key = 0 + (@parent.nil? ? 0 : @parent.anonymous_key) + define_layers(layered_bindings) end # Binds layers from highest to lowest as defined by the given LayeredBindings. @@ -53,7 +51,6 @@ class Puppet::Pops::Binder::Binder # @api public # def define_layers(layered_bindings) - raise ArgumentError, "This binder is already configured. Cannot redefine its content." if configured?() LayerProcessor.new(self, key_factory).bind(layered_bindings) injector_entries.each do |k,v| @@ -61,15 +58,13 @@ class Puppet::Pops::Binder::Binder raise ArgumentError, "Binding with unresolved 'override' detected: #{self.class.format_binding(v.binding)}}" end end - # and the fat lady has sung - @configured = true - self end + private :define_layers # @api private def next_anonymous_key - tmp = @next_anonymous_key - @next_anonymous_key += 1 + tmp = @anonymous_key + @anonymous_key += 1 tmp end diff --git a/spec/unit/pops/binder/binder_spec.rb b/spec/unit/pops/binder/binder_spec.rb index 9450f04c3..fec4496ff 100644 --- a/spec/unit/pops/binder/binder_spec.rb +++ b/spec/unit/pops/binder/binder_spec.rb @@ -22,21 +22,22 @@ end describe 'Binder' do include BinderSpecModule + # TODO: Test binder + parent binder context 'when defining layers' do - it 'redefinition is not allowed' do - expect do - b = binder() - b.define_layers(factory.layered_bindings(test_layer_with_empty_bindings)) - b.define_layers(factory.layered_bindings(test_layer_with_empty_bindings)) - end.to raise_error(/Cannot redefine its content/) - end - - it 'a binder should report being configured when layers have been defined' do - b = binder() - b.configured?().should == false - b.define_layers(factory.layered_bindings(test_layer_with_empty_bindings)) - b.configured?().should == true - end +# it 'redefinition is not allowed' do +# expect do +# b = binder() +# b.define_layers(factory.layered_bindings(test_layer_with_empty_bindings)) +# b.define_layers(factory.layered_bindings(test_layer_with_empty_bindings)) +# end.to raise_error(/Cannot redefine its content/) +# end +# +# it 'a binder should report being configured when layers have been defined' do +# b = binder() +# b.configured?().should == false +# b.define_layers(factory.layered_bindings(test_layer_with_empty_bindings)) +# b.configured?().should == true +# end end end \ No newline at end of file diff --git a/spec/unit/pops/binder/bindings_composer_spec.rb b/spec/unit/pops/binder/bindings_composer_spec.rb index 073b7e3d3..93bc44722 100644 --- a/spec/unit/pops/binder/bindings_composer_spec.rb +++ b/spec/unit/pops/binder/bindings_composer_spec.rb @@ -46,8 +46,7 @@ describe 'BinderComposer' do the_scope['environment'] = 'production' layered_bindings = composer.compose(scope) # puts Puppet::Pops::Binder::BindingsModelDumper.new().dump(layered_bindings) - binder = Puppet::Pops::Binder::Binder.new() - binder.define_layers(layered_bindings) + binder = Puppet::Pops::Binder::Binder.new(layered_bindings) injector = Puppet::Pops::Binder::Injector.new(binder) expect(injector.lookup(scope, 'awesome_x')).to be == 'golden' expect(injector.lookup(scope, 'good_x')).to be == 'golden' diff --git a/spec/unit/pops/binder/injector_spec.rb b/spec/unit/pops/binder/injector_spec.rb index e224e1bcd..cf9114605 100644 --- a/spec/unit/pops/binder/injector_spec.rb +++ b/spec/unit/pops/binder/injector_spec.rb @@ -103,27 +103,23 @@ describe 'Injector' do let(:scope) { null_scope()} let(:duck_type) { type_factory.ruby(InjectorSpecModule::TestDuck) } - let(:binder) { Puppet::Pops::Binder::Binder.new()} + let(:binder) { Puppet::Pops::Binder::Binder } let(:lbinder) do - binder.define_layers(layered_bindings) + binder.new(layered_bindings) end let(:layered_bindings) { factory.layered_bindings(test_layer_with_bindings(bindings.model)) } context 'When created' do - it 'should raise an error when given binder is not configured at all' do - expect { Puppet::Pops::Binder::Injector.new(binder()) }.to raise_error(/Given Binder is not configured/) - end - it 'should not raise an error if binder is configured' do - lbinder.configured?().should == true # of something is very wrong +# lbinder.configured?().should == true # of something is very wrong expect { injector(lbinder) }.to_not raise_error end it 'should create an empty injector given an empty binder' do - expect { binder.define_layers(layered_bindings) }.to_not raise_exception + expect { binder.new(layered_bindings) }.to_not raise_exception end it "should be possible to reference the TypeCalculator" do @@ -203,8 +199,6 @@ describe 'Injector' do end context "and multiple layers are in use" do - let(:binder) { configured_binder()} - it "a higher layer shadows anything in a lower layer" do bindings1 = factory.named_bindings('test1') bindings1.bind().name('a_string').to('bad stuff') @@ -214,15 +208,13 @@ describe 'Injector' do bindings2.bind().name('a_string').to('good stuff') higher_layer = factory.named_layer('higher-layer', bindings2.model) - binder.define_layers(factory.layered_bindings(higher_layer, lower_layer)) - injector = injector(binder) + injector = injector(binder.new(factory.layered_bindings(higher_layer, lower_layer))) injector.lookup(scope,'a_string').should == 'good stuff' end end context "and dealing with Data types" do - let(:binder) { configured_binder()} - let(:lbinder) { binder.define_layers(layered_bindings) } + let(:lbinder) { binder.new(layered_bindings) } it "should treat all data as same type w.r.t. key" do bindings.bind().name('a_string').to('42') @@ -551,9 +543,7 @@ describe 'Injector' do mb = bindings.multibind(ids[2]).type(hash_of_integer).name('broken_family2') mb.producer_options(:uniq => :true) - - binder.define_layers(factory.layered_bindings(test_layer_with_bindings(bindings.model))) - injector = injector(binder) + injector = injector(binder.new(factory.layered_bindings(test_layer_with_bindings(bindings.model)))) expect { injector.lookup(scope, 'broken_family0')}.to raise_error(/:conflict_resolution => :append/) expect { injector.lookup(scope, 'broken_family1')}.to raise_error(/:flatten/) expect { injector.lookup(scope, 'broken_family2')}.to raise_error(/:uniq/) @@ -590,9 +580,7 @@ describe 'Injector' do mb2 = bindings.bind.in_multibind(multibind_id) mb2.name('nephew').to({'name' => 'Dewey', 'is' => 'looser', 'has' => 'cap'}) - binder.define_layers(layered_bindings) - - the_ducks = injector(binder).lookup(scope, "donalds_nephews"); + the_ducks = injector(binder.new(layered_bindings)).lookup(scope, "donalds_nephews"); the_ducks['nephew']['name'].should == 'Huey' the_ducks['nephew']['is'].should == 'winner' the_ducks['nephew']['has'].should == 'cap' @@ -713,8 +701,7 @@ describe 'Injector' do end end context "When there are problems with configuration" do - let(:binder) { configured_binder()} - let(:lbinder) { binder.define_layers(layered_bindings) } + let(:lbinder) { binder.new(layered_bindings) } it "reports error for surfacing abstract bindings" do bindings.bind.abstract.name('an_int') From 5c48b2fd1210c5ac84dee4cee35c4b49abc6cda6 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 30 Dec 2013 15:26:06 +0100 Subject: [PATCH 325/800] (maint) Cleanup spec tests after refactor of binder one step create --- spec/unit/pops/binder/bindings_checker_spec.rb | 14 -------------- spec/unit/pops/binder/injector_spec.rb | 1 - 2 files changed, 15 deletions(-) diff --git a/spec/unit/pops/binder/bindings_checker_spec.rb b/spec/unit/pops/binder/bindings_checker_spec.rb index d17d8b422..9d8630de2 100644 --- a/spec/unit/pops/binder/bindings_checker_spec.rb +++ b/spec/unit/pops/binder/bindings_checker_spec.rb @@ -38,20 +38,6 @@ describe 'The bindings checker' do b end -# def category(name, value) -# b = Bindings::Category.new() -# b.categorization = name -# b.value = value -# b -# end -# -# def categorized_bindings(bindings, *predicates) -# b = Bindings::CategorizedBindings.new() -# b.bindings = bindings -# b.predicates = predicates -# b -# end - def layer(name, *bindings) l = Bindings::NamedLayer.new() l.name = name diff --git a/spec/unit/pops/binder/injector_spec.rb b/spec/unit/pops/binder/injector_spec.rb index cf9114605..1fbf35011 100644 --- a/spec/unit/pops/binder/injector_spec.rb +++ b/spec/unit/pops/binder/injector_spec.rb @@ -114,7 +114,6 @@ describe 'Injector' do context 'When created' do it 'should not raise an error if binder is configured' do -# lbinder.configured?().should == true # of something is very wrong expect { injector(lbinder) }.to_not raise_error end From 091730d5249ce02bfc686941a46a818b07190f48 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 30 Dec 2013 22:54:52 +0100 Subject: [PATCH 326/800] (maint) Add support for final binding A final binding may not be overridden in a higher layer. --- lib/puppet/pops/binder/binder.rb | 80 +++++++++++++++------- lib/puppet/pops/binder/bindings_factory.rb | 7 ++ lib/puppet/pops/binder/bindings_model.rb | 1 + lib/puppet/pops/binder/injector_entry.rb | 4 ++ spec/unit/pops/binder/injector_spec.rb | 13 ++++ 5 files changed, 82 insertions(+), 23 deletions(-) diff --git a/lib/puppet/pops/binder/binder.rb b/lib/puppet/pops/binder/binder.rb index 2c3e49217..d5ff312e5 100644 --- a/lib/puppet/pops/binder/binder.rb +++ b/lib/puppet/pops/binder/binder.rb @@ -23,6 +23,10 @@ class Puppet::Pops::Binder::Binder # @api private attr_reader :anonymous_key + # This binder's precedence + # @api private + attr_reader :binder_precedence + # @api public def initialize(layered_bindings, parent_binder=nil) @parent = parent_binder @@ -32,8 +36,15 @@ class Puppet::Pops::Binder::Binder # Resulting hash of all key -> binding @injector_entries = {} - # First anonymous key is the parent's next (non incremented key) - @anonymous_key = 0 + (@parent.nil? ? 0 : @parent.anonymous_key) + if @parent.nil? + @anonymous_key = 0 + @binder_precedence = 0 + else + # First anonymous key is the parent's next (non incremented key). (The parent can not change, it is + # the final, free key). + @anonymous_key = @parent.anonymous_key + @binder_precedence = @parent.binder_precedence + 1 + end define_layers(layered_bindings) end @@ -68,6 +79,17 @@ class Puppet::Pops::Binder::Binder tmp end + def lookup_in_parent(key) + @parent.nil? ? nil : @parent.lookup(key) + end + + def lookup(key) + if x = injector_entries[key] + return x + end + @parent ? @parent.lookup(key) : nil + end + # @api private def self.format_binding(b) type_name = Puppet::Pops::Types::TypeCalculator.new().string(b.type) @@ -105,9 +127,11 @@ class Puppet::Pops::Binder::Binder attr :binder attr :key_factory attr :contributions + attr :binder_precedence def initialize(binder, key_factory) @binder = binder + @binder_precedence = binder.binder_precedence @key_factory = key_factory @bindings = [] @contributions = [] @@ -118,14 +142,14 @@ class Puppet::Pops::Binder::Binder # @api private # def add(b) - bindings << Puppet::Pops::Binder::InjectorEntry.new(b) + bindings << Puppet::Pops::Binder::InjectorEntry.new(b, binder_precedence) end # Add a multibind contribution # @api private # def add_contribution(b) - contributions << Puppet::Pops::Binder::InjectorEntry.new(b) + contributions << Puppet::Pops::Binder::InjectorEntry.new(b, binder_precedence) end # Bind given abstract binding @@ -135,40 +159,41 @@ class Puppet::Pops::Binder::Binder @@bind_visitor.visit_this(self, binding) end - # @return [Puppet::Pops::Binder::InjectorEntry] the entry with the highest (category) precedence + # @return [Puppet::Pops::Binder::InjectorEntry] the entry with the highest precedence # @api private def highest(b1, b2) if b1.is_abstract? != b2.is_abstract? # if one is abstract and the other is not, the non abstract wins b1.is_abstract? ? b2 : b1 else -# case b1.precedence <=> b2.precedence -# when 1 -# b1 -# when -1 -# b2 -# when 0 - - raise_conflicting_binding(b1, b2) + case b1.precedence <=> b2.precedence + when 1 + b1 + when -1 + b2 + when 0 + raise_conflicting_binding(b1, b2) + end end end # Raises a conflicting bindings error given two InjectorEntry's with same precedence in the same layer # (if they are in different layers, something is seriously wrong) def raise_conflicting_binding(b1, b2) - b1_layer_name, b1_bindings_name = Puppet::Pops::Binder::Binder.get_named_binding_layer_and_name(b1.binding) - b2_layer_name, b2_bindings_name = Puppet::Pops::Binder::Binder.get_named_binding_layer_and_name(b2.binding) + b1_layer_name, b1_bindings_name = binder.class.get_named_binding_layer_and_name(b1.binding) + b2_layer_name, b2_bindings_name = binder.class.get_named_binding_layer_and_name(b2.binding) - # The resolution is per layer, and if they differ something is serious wrong as a higher layer - # overrides a lower; so no such conflict should be possible: + finality_msg = (b1.is_final? || b2.is_final?) ? ". Override of final binding not allowed" : '' + + # TODO: Use of layer_name is not very good, it is not guaranteed to be unique unless b1_layer_name == b2_layer_name raise ArgumentError, [ - 'Internal Error: Conflicting binding for', + 'Conflicting binding for', "'#{b1.binding.name}'", 'being resolved across layers', "'#{b1_layer_name}' and", "'#{b2_layer_name}'" - ].join(' ') + ].join(' ')+finality_msg end # Conflicting bindings made from the same source @@ -180,7 +205,7 @@ class Puppet::Pops::Binder::Binder "'#{b1_layer_name}', ", 'both from:', "'#{b1_bindings_name}'" - ].join(' ') + ].join(' ')+finality_msg end # Conflicting bindings from different sources @@ -192,7 +217,7 @@ class Puppet::Pops::Binder::Binder 'from:', "'#{b1_bindings_name}', and", "'#{b2_bindings_name}'" - ].join(' ') + ].join(' ')+finality_msg end @@ -271,15 +296,24 @@ class Puppet::Pops::Binder::Binder bindings.each do |b| bkey = key(b.binding) - # ignore if a higher layer defined it, but ensure override gets resolved + # ignore if a higher layer defined it (unless the lower is final), but ensure override gets resolved + # (override is not resolved across binders) if x = binder.injector_entries[bkey] + if b.is_final? + raise_conflicting_binding(x, b) + end x.mark_override_resolved() next end + # If a lower (parent) binder exposes a final binding it may not be overridden + # + if (x = binder.lookup_in_parent(bkey)) && x.is_final? + raise_conflicting_binding(x, b) + end + # if already found in this layer, one wins (and resolves override), or it is an error existing = this_layer[bkey] - # TODO: highest is not really needed - it is an error at all times when there are no categories winner = existing ? highest(existing, b) : b this_layer[bkey] = winner if existing diff --git a/lib/puppet/pops/binder/bindings_factory.rb b/lib/puppet/pops/binder/bindings_factory.rb index ca54b5b58..d00442b90 100644 --- a/lib/puppet/pops/binder/bindings_factory.rb +++ b/lib/puppet/pops/binder/bindings_factory.rb @@ -194,6 +194,13 @@ module Puppet::Pops::Binder::BindingsFactory self end + # Sets the binding to be final (it may not be overridden) + # @api public + def final + model.final = true + self + end + # Makes the binding a multibind contribution to the given multibind id # @param id [String] the multibind id to contribute this binding to # @api public diff --git a/lib/puppet/pops/binder/bindings_model.rb b/lib/puppet/pops/binder/bindings_model.rb index 3ca3b6d51..c909c66fe 100644 --- a/lib/puppet/pops/binder/bindings_model.rb +++ b/lib/puppet/pops/binder/bindings_model.rb @@ -133,6 +133,7 @@ module Puppet::Pops::Binder::Bindings has_attr 'name', String has_attr 'override', Boolean has_attr 'abstract', Boolean + has_attr 'final', Boolean # If set is a contribution in a multibind has_attr 'multibind_id', String, :lowerBound => 0 # Invariant: Only multibinds may have lowerBound 0, all regular Binding must have a producer. diff --git a/lib/puppet/pops/binder/injector_entry.rb b/lib/puppet/pops/binder/injector_entry.rb index 7e89bb7c6..629bc5e74 100644 --- a/lib/puppet/pops/binder/injector_entry.rb +++ b/lib/puppet/pops/binder/injector_entry.rb @@ -42,6 +42,10 @@ class Puppet::Pops::Binder::InjectorEntry binding.abstract end + def is_final? + binding.final + end + # Compares against another InjectorEntry by comparing precedence. # @param injector_entry [InjectorEntry] entry to compare against. # @return [Integer] 1, if this entry has higher precedence, 0 if equal, and -1 if given entry has higher precedence. diff --git a/spec/unit/pops/binder/injector_spec.rb b/spec/unit/pops/binder/injector_spec.rb index 1fbf35011..58394b468 100644 --- a/spec/unit/pops/binder/injector_spec.rb +++ b/spec/unit/pops/binder/injector_spec.rb @@ -210,6 +210,19 @@ describe 'Injector' do injector = injector(binder.new(factory.layered_bindings(higher_layer, lower_layer))) injector.lookup(scope,'a_string').should == 'good stuff' end + + it "a higher layer may not shadow a lower layer binding that is final" do + bindings1 = factory.named_bindings('test1') + bindings1.bind().final.name('a_string').to('required stuff') + lower_layer = factory.named_layer('lower-layer', bindings1.model) + + bindings2 = factory.named_bindings('test2') + bindings2.bind().name('a_string').to('contraband') + higher_layer = factory.named_layer('higher-layer', bindings2.model) + expect { + injector = injector(binder.new(factory.layered_bindings(higher_layer, lower_layer))) + }.to raise_error(/Override of final binding not allowed/) + end end context "and dealing with Data types" do From 260eaf1afb0c52e679d6fef87bf1696c774b9025 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Tue, 31 Dec 2013 15:07:42 -0800 Subject: [PATCH 327/800] (maint) Simply confine test to any platform with Ruby shadow Original confine was prone to errors in platform pattern, which we saw when testing fedora18 whose platform tag no longer contains 'fc-' in our current acceptance/config/nodes. This would fall through to the false case, which meant the host was /not/ rejected. And then fedora18 from a source checkout would run the test and fail, because Ruby shadow lib is not installed by default. This only turned up because the future parser acceptance job was running from source checkouts (because one of the nodes it was testing is Windows...) At any rate, it proved confusing. This change just simplifies the confine to reject a host if the Ruby shadow library is not present. Because without that, we can't manipulate passwords, and have nothing to test. --- ...ssword-disclosure-when-changing-a-users-password.rb | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/acceptance/tests/ticket_6857_password-disclosure-when-changing-a-users-password.rb b/acceptance/tests/ticket_6857_password-disclosure-when-changing-a-users-password.rb index 97577c9d0..7752c3ecf 100644 --- a/acceptance/tests/ticket_6857_password-disclosure-when-changing-a-users-password.rb +++ b/acceptance/tests/ticket_6857_password-disclosure-when-changing-a-users-password.rb @@ -1,14 +1,10 @@ test_name "#6857: redact password hashes when applying in noop mode" hosts_to_test = agents.reject do |agent| - if agent['platform'].match /(?:ubuntu|centos|debian|el-|fc-)/ - result = on(agent, %Q{#{agent['puppetbindir']}/ruby -e 'require "shadow" or raise'}, :silent => true) - result.exit_code != 0 - else - false - end + result = on(agent, %Q{#{agent['puppetbindir']}/ruby -e 'require "shadow" or raise'}, :silent => true) + result.exit_code != 0 end -skip_test "No suitable hosts found" if hosts_to_test.empty? +skip_test "No suitable hosts found. Without the Ruby shadow library, passwords cannot be set." if hosts_to_test.empty? adduser_manifest = < Date: Thu, 2 Jan 2014 01:43:45 +0100 Subject: [PATCH 328/800] (maint) Add handling of layered multibind This adds correct handling of multibind contributions when injectors are stacked. Higher layer contributions cannot be allowed to leave behind a cached multibind containing entries from an overriding injector when it goes out of scope. --- lib/puppet/pops/binder/binder.rb | 50 +++++++++++++++++++++++++-- lib/puppet/pops/binder/key_factory.rb | 6 ++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/lib/puppet/pops/binder/binder.rb b/lib/puppet/pops/binder/binder.rb index d5ff312e5..54620db46 100644 --- a/lib/puppet/pops/binder/binder.rb +++ b/lib/puppet/pops/binder/binder.rb @@ -12,6 +12,9 @@ class Puppet::Pops::Binder::Binder # @api private attr_reader :injector_entries + # @api private + attr :id_index + # @api private attr_reader :key_factory @@ -30,6 +33,7 @@ class Puppet::Pops::Binder::Binder # @api public def initialize(layered_bindings, parent_binder=nil) @parent = parent_binder + @id_index = Hash.new() { |k, v| [] } @key_factory = Puppet::Pops::Binder::KeyFactory.new() @@ -64,11 +68,34 @@ class Puppet::Pops::Binder::Binder def define_layers(layered_bindings) LayerProcessor.new(self, key_factory).bind(layered_bindings) - injector_entries.each do |k,v| - unless key_factory.is_contributions_key?(k) || v.is_resolved?() + contribution_keys = [] + # make one pass over entries to collect contributions, and check overrides + injector_entries.each do |k,v| + if key_factory.is_contributions_key?(k) + contribution_keys << [k,v] + elsif !v.is_resolved?() raise ArgumentError, "Binding with unresolved 'override' detected: #{self.class.format_binding(v.binding)}}" + else + # if binding has an id, add it to the index + add_id_to_index(v.binding) end end + + # If a lower level binder has contributions for a key also contributed to in this binder + # they must included in the higher shadowing contribution. + # If a contribution is made to an id that is defined in a parent + # contribute to an id that is defined in a lower binder, it must be promoted to this binder (copied) or + # there is risk of making the lower level injector dirty. + # + contribution_keys.each do |kv| + parent_contribution = lookup_in_parent(kv[0]) + next unless parent_contribution + injector_entries[kv[0]] = kv[1] + parent_contributions + + # key the multibind_id from the contribution key + multibind_id = key_factory.multibind_contribution_key_to_id(kv[0]) + promote_matching_bindings(self, @parent, multibind_id) + end end private :define_layers @@ -79,6 +106,25 @@ class Puppet::Pops::Binder::Binder tmp end + def add_id_to_index(binding) + return unless binding.is_a?(Puppet::Pops::Binder::Bindings::Multibinding) && !(id = binding.id).nil? + @id_index[id] = @id_index[id] << binding + end + + def promote_matching_bindings(to_binder, from_binder, multibind_id) + return if from_binder.nil? + from_binder.id_index[ multibind_id ].each do |binding| + key = key_factory.binding_key(binding) + entry = lookup(key) + unless entry.precedence == @binder_precedence + # it is from a lower layer it must be promoted + injector_entries[ key ] = Puppet::Pops::Binder::InjectorEntry.new(binding, binder_precedence) + end + end + # recursive "up the parent chain" to promote all + promote_matching_bindings(to_binder, from_binder.parent, multibind_id) + end + def lookup_in_parent(key) @parent.nil? ? nil : @parent.lookup(key) end diff --git a/lib/puppet/pops/binder/key_factory.rb b/lib/puppet/pops/binder/key_factory.rb index e5d890c63..5abff5fa1 100644 --- a/lib/puppet/pops/binder/key_factory.rb +++ b/lib/puppet/pops/binder/key_factory.rb @@ -35,6 +35,12 @@ class Puppet::Pops::Binder::KeyFactory "mc_#{multibind_id}" end + # @api public + def multibind_contribution_key_to_id(contributions_key) + # removes the leading "mc_" from the key to get the multibind_id + contributions_key[3..-1] + end + # @api public def is_named?(key) key.is_a?(Array) && key[1] && !key[1].empty? From de12ade7651bfb8c9acbb45d3300e1d95e850725 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 2 Jan 2014 15:25:45 +0100 Subject: [PATCH 329/800] (maint) Add convenient create/override ability to Injector Setting up the initial injector, as well as creating an override required quite a bit of gymnastics. This is now made much simpler. The Injector now has class methods create, create_from_model, and create_from_hash. The instance has override, override_with_model, override_with_hash. --- lib/puppet/parser/compiler.rb | 13 ++-- lib/puppet/pops/binder/injector.rb | 85 +++++++++++++++++++++-- spec/unit/parser/functions/lookup_spec.rb | 5 +- spec/unit/pops/binder/injector_spec.rb | 45 ++++++++++++ 4 files changed, 134 insertions(+), 14 deletions(-) diff --git a/lib/puppet/parser/compiler.rb b/lib/puppet/parser/compiler.rb index 402531854..700d27b70 100644 --- a/lib/puppet/parser/compiler.rb +++ b/lib/puppet/parser/compiler.rb @@ -246,10 +246,11 @@ class Puppet::Parser::Compiler # def create_boot_injector(env_boot_bindings) assert_binder_active() - boot_contribution = Puppet::Pops::Binder::SystemBindings.injector_boot_contribution(env_boot_bindings) - final_contribution = Puppet::Pops::Binder::SystemBindings.final_contribution - binder = Puppet::Pops::Binder::Binder.new(Puppet::Pops::Binder::BindingsFactory.layered_bindings(final_contribution, boot_contribution)) - @boot_injector = Puppet::Pops::Binder::Injector.new(binder) + pb = Puppet::Pops::Binder + boot_contribution = pb::SystemBindings.injector_boot_contribution(env_boot_bindings) + final_contribution = pb::SystemBindings.final_contribution + binder = pb::Binder.new(pb::BindingsFactory.layered_bindings(final_contribution, boot_contribution)) + @boot_injector = pb::Injector.new(binder) end # Answers if Puppet Binder should be active or not, and if it should and is not active, then it is activated. @@ -542,9 +543,7 @@ class Puppet::Parser::Compiler assert_binder_active() composer = Puppet::Pops::Binder::BindingsComposer.new() layered_bindings = composer.compose(topscope) - binder = Puppet::Pops::Binder::Binder.new() - binder.define_layers(layered_bindings) - @injector = Puppet::Pops::Binder::Injector.new(binder) + @injector = Puppet::Pops::Binder::Injector.new(Puppet::Pops::Binder::Binder.new(layered_bindings)) end def assert_binder_active diff --git a/lib/puppet/pops/binder/injector.rb b/lib/puppet/pops/binder/injector.rb index c46a8c2ea..8087551a4 100644 --- a/lib/puppet/pops/binder/injector.rb +++ b/lib/puppet/pops/binder/injector.rb @@ -86,6 +86,73 @@ class Puppet::Pops::Binder::Injector Producers = Puppet::Pops::Binder::Producers + def self.create_from_model(layered_bindings_model) + self.new(Puppet::Pops::Binder::Binder.new(layered_bindings_model)) + end + + def self.create_from_hash(name, key_value_hash) + factory = Puppet::Pops::Binder::BindingsFactory + named_bindings = factory.named_bindings(name) { key_value_hash.each {|k,v| bind.name(k).to(v) }} + layered_bindings = factory.layered_bindings(factory.named_layer(name+'-layer',named_bindings.model)) + self.new(Puppet::Pops::Binder::Binder.new(layered_bindings)) + end + + # Creates an injector with a single bindings layer created with the given name, and the bindings + # produced by the given block. The block is evaluated with self bound to a BindingsContainerBuilder. + # + # @example + # Injector.create('mysettings') do + # bind('name').to(42) + # end + # + # @api public + # + def self.create(name, &block) + factory = Puppet::Pops::Binder::BindingsFactory + layered_bindings = factory.layered_bindings(factory.named_layer(name+'-layer',factory.named_bindings(name, &block).model)) + self.new(Puppet::Pops::Binder::Binder.new(layered_bindings)) + end + + # Creates an overriding injector with a single bindings layer + # created with the given name, and the bindings produced by the given block. + # The block is evaluated with self bound to a BindingsContainerBuilder. + # + # @example + # an_injector.override('myoverrides') do + # bind('name').to(43) + # end + # + # @api public + # + def override(name, &block) + factory = Puppet::Pops::Binder::BindingsFactory + layered_bindings = factory.layered_bindings(factory.named_layer(name+'-layer',factory.named_bindings(name, &block).model)) + self.class.new(Puppet::Pops::Binder::Binder.new(layered_bindings, @impl.binder)) + end + + # Creates an overriding injector with bindings from a bindings model (a LayeredBindings) which + # may consists of multiple layers of bindings. + # + # @api public + # + def override_with_model(layered_bindings) + unless layered_bindings.is_a?(Puppet::Pops::Binder::Bindings::LayeredBindings) + raise ArgumentError, "Expected a LayeredBindings model, got '#{bindings_model.class}'" + end + self.class.new(Puppet::Pops::Binder::Binder.new(layered_bindings, @impl.binder)) + end + + # Creates an overriding injector with a single bindings layer + # created with the given name, and the bindings given in the key_value_hash + # @api public + # + def override_with_hash(name, key_value_hash) + factory = Puppet::Pops::Binder::BindingsFactory + named_bindings = factory.named_bindings(name) { key_value_hash.each {|k,v| bind.name(k).to(v) }} + layered_bindings = factory.layered_bindings(factory.named_layer(name+'-layer',named_bindings.model)) + self.class.new(Puppet::Pops::Binder::Binder.new(layered_bindings, @impl.binder)) + end + # An Injector is initialized with a configured {Puppet::Pops::Binder::Binder Binder}. # # @param configured_binder [Puppet::Pops::Binder::Binder,nil] The configured binder containing effective bindings. A given value @@ -94,11 +161,11 @@ class Puppet::Pops::Binder::Injector # # @api public # - def initialize(configured_binder) + def initialize(configured_binder, parent_injector = nil) if configured_binder.nil? @impl = Private::NullInjectorImpl.new() else - @impl = Private::InjectorImpl.new(configured_binder) + @impl = Private::InjectorImpl.new(configured_binder, parent_injector) end end @@ -305,7 +372,11 @@ module Private else val end + end + # @api private + def binder + nil end # @api private @@ -345,7 +416,12 @@ module Private attr_reader :type_calculator - def initialize(configured_binder) + attr_reader :binder + + def initialize(configured_binder, parent_injector = nil) + @binder = configured_binder + @parent = parent_injector + # TODO: Different error message raise ArgumentError, "Given Binder is not configured" unless configured_binder #&& configured_binder.configured?() @entries = configured_binder.injector_entries() @@ -429,7 +505,8 @@ module Private @recursion_lock.push(key) case entry = get_entry(key) when NilClass - nil + @parent ? @parent.lookup_key(scope, key) : nil + when Puppet::Pops::Binder::InjectorEntry val = produce(scope, entry) return nil if val.nil? diff --git a/spec/unit/parser/functions/lookup_spec.rb b/spec/unit/parser/functions/lookup_spec.rb index 8d4440a32..05484adfa 100644 --- a/spec/unit/parser/functions/lookup_spec.rb +++ b/spec/unit/parser/functions/lookup_spec.rb @@ -133,9 +133,8 @@ describe "lookup function" do end def bound(local_bindings) - binder = Puppet::Pops::Binder::Binder.new - binder.define_layers(Puppet::Pops::Binder::BindingsFactory.layered_bindings(Puppet::Pops::Binder::BindingsFactory.named_layer('test layer', local_bindings.model))) - binder + layered_bindings = Puppet::Pops::Binder::BindingsFactory.layered_bindings(Puppet::Pops::Binder::BindingsFactory.named_layer('test layer', local_bindings.model)) + Puppet::Pops::Binder::Binder.new(layered_bindings) end def ast_lambda(puppet_source) diff --git a/spec/unit/pops/binder/injector_spec.rb b/spec/unit/pops/binder/injector_spec.rb index 58394b468..1c2c66a49 100644 --- a/spec/unit/pops/binder/injector_spec.rb +++ b/spec/unit/pops/binder/injector_spec.rb @@ -128,6 +128,50 @@ describe 'Injector' do it "should be possible to reference the KeyFactory" do injector(lbinder).key_factory.is_a?(Puppet::Pops::Binder::KeyFactory).should == true end + + it "can be created using a model" do + bindings.bind.name('a_string').to('42') + injector = Puppet::Pops::Binder::Injector.create_from_model(layered_bindings) + injector.lookup(scope, 'a_string').should == '42' + end + + it 'can be created using a block' do + injector = Puppet::Pops::Binder::Injector.create('test') do + bind.name('a_string').to('42') + end + injector.lookup(scope, 'a_string').should == '42' + end + + it 'can be created using a hash' do + injector = Puppet::Pops::Binder::Injector.create_from_hash('test', 'a_string' => '42') + injector.lookup(scope, 'a_string').should == '42' + end + + it 'can be created using an overriding injector with block' do + injector = Puppet::Pops::Binder::Injector.create('test') do + bind.name('a_string').to('42') + end + injector2 = injector.override('override') do + bind.name('a_string').to('43') + end + injector.lookup(scope, 'a_string').should == '42' + injector2.lookup(scope, 'a_string').should == '43' + end + + it 'can be created using an overriding injector with hash' do + injector = Puppet::Pops::Binder::Injector.create_from_hash('test', 'a_string' => '42') + injector2 = injector.override_with_hash('override', 'a_string' => '43') + injector.lookup(scope, 'a_string').should == '42' + injector2.lookup(scope, 'a_string').should == '43' + end + + it "can be created using an overriding injector with a model" do + injector = Puppet::Pops::Binder::Injector.create_from_hash('test', 'a_string' => '42') + bindings.bind.name('a_string').to('43') + injector2 = injector.override_with_model(layered_bindings) + injector.lookup(scope, 'a_string').should == '42' + injector2.lookup(scope, 'a_string').should == '43' + end end context "When looking up objects" do @@ -712,6 +756,7 @@ describe 'Injector' do end end end + context "When there are problems with configuration" do let(:lbinder) { binder.new(layered_bindings) } From 6fcceceaf48e3c1329052ce017cb3afd10b14f1d Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 20 Dec 2013 22:11:54 +0100 Subject: [PATCH 330/800] (PUP-1176) Add evaluator feature switch for future/current evaluator This adds an --evaluator future/current setting to control if the future evaluator should be used or not when using the future parser. It is set to "on" (i.e. "future") by default. Setting it to "current" turns off the future evaluator. This commit only contains the setting itself. --- lib/puppet/defaults.rb | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index c07334080..e1b0c9bf3 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -1765,6 +1765,33 @@ EOT Available Since Puppet 3.2. EOT }, + :evaluator => { + :default => "future", + :desc => <<-'EOT' + Selects the evaluator to use for evaluation of puppet manifests parsed + with the option "parser = future". Available choices are `current`, + and `future` (the default). + + The `future` evaluator means that the new evaluator (experimental) will + be used, whereas 'current' means that the "parser future" option will + transform the parsed result to Puppet 3x and use the regular + evaluator. + + The `future` evaluator is a "time travel to the future" allowing early + exposure to new language features. What these fatures are will vary from + release to release and they may be invididually configurable. + + The default for this parameter is 'future', which means that the flag is + there to turn off the future evaluator for A / B testing. + + The experimental features "parser future, evaluator future" are expected + to become what is released as the standard / current in Puppet 4.x. + + The evaluator option has no effect unless parser is set to 'future'. + + Available Since Puppet 3.5. + EOT + }, :max_errors => { :default => 10, :desc => <<-'EOT' From 0b401fc592446eac1fff5d5ab60bd01dcbe38a66 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 2 Jan 2014 16:08:08 +0100 Subject: [PATCH 331/800] (PUP-1176) Add feature switch for evaluator to ParserFactory This adds the --evaluator future/current switch to the ParserFactory. It is only in effect when using --parser future. By default the future evaluator is used, but the older transforming evaluator can be used by also stating --evaluator current. --- lib/puppet/defaults.rb | 5 +++++ lib/puppet/parser/e_parser_adapter.rb | 1 - lib/puppet/parser/parser_factory.rb | 6 +++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index e1b0c9bf3..46fa7b9ee 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -1767,6 +1767,11 @@ EOT }, :evaluator => { :default => "future", + :hook => proc do |value| + if !['future', 'current'].include?(value) + raise "evaluator can only be set to 'future' or 'current', got '#{value}'" + end + end, :desc => <<-'EOT' Selects the evaluator to use for evaluation of puppet manifests parsed with the option "parser = future". Available choices are `current`, diff --git a/lib/puppet/parser/e_parser_adapter.rb b/lib/puppet/parser/e_parser_adapter.rb index beec14752..fe0e28b55 100644 --- a/lib/puppet/parser/e_parser_adapter.rb +++ b/lib/puppet/parser/e_parser_adapter.rb @@ -36,7 +36,6 @@ class Puppet::Parser::EParserAdapter # Validate validate(parse_result) - # Transform the result, but only if not nil parse_result = Puppet::Pops::Model::AstTransformer.new(source_file, @classic_parser).transform(parse_result) if parse_result if parse_result && !parse_result.is_a?(Puppet::Parser::AST::BlockExpression) diff --git a/lib/puppet/parser/parser_factory.rb b/lib/puppet/parser/parser_factory.rb index 54bd9f7a4..d4239afe6 100644 --- a/lib/puppet/parser/parser_factory.rb +++ b/lib/puppet/parser/parser_factory.rb @@ -11,7 +11,11 @@ module Puppet::Parser def self.parser(environment) case Puppet[:parser] when 'future' - evaluating_parser(environment) + if Puppet[:evaluator] == 'future' + evaluating_parser(environment) + else + eparser(environment) + end else classic_parser(environment) end From 33b3920d74ed52556d1dee0640d6f8a80824b49e Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 2 Jan 2014 16:39:15 +0100 Subject: [PATCH 332/800] (maint) Fix typo in documentation for --parser and --evaluator A fature is not spelled that way. --- lib/puppet/defaults.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index 46fa7b9ee..22dcad798 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -1759,7 +1759,7 @@ EOT be used. The `future` parser is a "time travel to the future" allowing early - exposure to new language features. What these fatures are will vary from + exposure to new language features. What these features are will vary from release to release and they may be invididually configurable. Available Since Puppet 3.2. @@ -1783,7 +1783,7 @@ EOT evaluator. The `future` evaluator is a "time travel to the future" allowing early - exposure to new language features. What these fatures are will vary from + exposure to new language features. What these features are will vary from release to release and they may be invididually configurable. The default for this parameter is 'future', which means that the flag is From 7a1a025b380bad1fa6e9ad64b4909aa989f196dc Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Thu, 2 Jan 2014 11:56:15 -0800 Subject: [PATCH 333/800] (PUP-672) Puppet::Context is immutable. Previous implementation exposed a bind method allowing you to add bindings to the current context. This method has been removed. Now every context is generated only once, either from an initial push or override, both of which require a hash of bindings which are merged into the parent's binding state. --- lib/puppet/context.rb | 55 ++++++++------- lib/puppet/test/test_helper.rb | 4 +- spec/unit/context_spec.rb | 124 +++++++++++++++++---------------- 3 files changed, 95 insertions(+), 88 deletions(-) diff --git a/lib/puppet/context.rb b/lib/puppet/context.rb index 37e2de4b4..11e82d72c 100644 --- a/lib/puppet/context.rb +++ b/lib/puppet/context.rb @@ -1,59 +1,60 @@ module Puppet::Context - class ValueAlreadyBoundError < Puppet::Error; end class UndefinedBindingError < Puppet::Error; end + # @api private class Bindings attr_reader :parent - def initialize(parent) + def initialize(parent, overrides = {}) + overrides ||= {} @parent = parent - @table = {} + @table = parent ? parent.table.merge(overrides) : overrides end - def bind(name, value) - if @table.include?(name) - raise ValueAlreadyBoundError, name - else - @table[name] = value - end - end - - def lookup(name, block) + def lookup(name, default_proc) if @table.include?(name) @table[name] - elsif @parent - @parent.lookup(name, block) - elsif block - block.call + elsif default_proc + default_proc.call else raise UndefinedBindingError, name end end + + def root? + @parent.nil? + end + + protected + + attr_reader :table end @bindings = Bindings.new(nil) - def self.push - @bindings = Bindings.new(@bindings) + # @param overrides [Hash] A hash of bindings to be merged with the parent context. + # @api private + def self.push(overrides) + @bindings = Bindings.new(@bindings, overrides) end + # @api private def self.pop - @bindings = @bindings.parent - end - - def self.bind(name, value) - @bindings.bind(name, value) + @bindings = @bindings.parent if !@bindings.root? + @bindings end + # Lookup a binding by name or return a default value provided by a passed block (if given). + # @api public def self.lookup(name, &block) @bindings.lookup(name, block) end + # @param bindings [Hash] A hash of bindings to be merged with the parent context. + # @yield [] A block executed in the context of the temporarily pushed bindings. + # @api public def self.override(bindings) - push - bindings.each do |name, value| - bind(name, value) - end + push(bindings) yield ensure diff --git a/lib/puppet/test/test_helper.rb b/lib/puppet/test/test_helper.rb index 32ce90cfb..c7446c79a 100644 --- a/lib/puppet/test/test_helper.rb +++ b/lib/puppet/test/test_helper.rb @@ -53,7 +53,7 @@ module Puppet::Test # Call this method once per test, prior to execution of each invididual test. # @return nil def self.before_each_test() - Puppet::Context.push + # 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. @@ -93,7 +93,7 @@ module Puppet::Test Puppet::DataBinding::Hiera.instance_variable_set("@hiera", nil) - Puppet::Context.bind(:trusted_information, + Puppet::Context.push(:trusted_information => Puppet::Indirector::TrustedInformation.new('local', 'testing', {})) end diff --git a/spec/unit/context_spec.rb b/spec/unit/context_spec.rb index d35abbf49..7ba5a3ec9 100644 --- a/spec/unit/context_spec.rb +++ b/spec/unit/context_spec.rb @@ -1,78 +1,84 @@ require 'spec_helper' describe Puppet::Context do - before :each do - Puppet::Context.push - end - after :each do - Puppet::Context.pop - end - - it "holds values for later lookup" do - Puppet::Context.bind("a", 1) - - expect(Puppet::Context.lookup("a")).to eq(1) - end - - it "does not allow a value to be re-set" do - Puppet::Context.bind("a", 1) - - expect { Puppet::Context.bind("a", 1) }.to raise_error(Puppet::Context::ValueAlreadyBoundError) - end - - it "fails to lookup a value that does not exist" do - expect { Puppet::Context.lookup("a") }.to raise_error(Puppet::Context::UndefinedBindingError) - end - - it "calls a provided block for a default value when none is found" do - expect(Puppet::Context.lookup("a") { "default" }).to eq("default") - end - - it "allows rebinding values in a nested context" do - Puppet::Context.bind("a", 1) - - inner = nil - Puppet::Context.override("a" => 2) do - inner = Puppet::Context.lookup("a") + context "with the implicit test_helper.rb pushed context" do + it "fails to lookup a value that does not exist" do + expect { Puppet::Context.lookup("a") }.to raise_error(Puppet::Context::UndefinedBindingError) end - expect(inner).to eq(2) - end - - it "outer bindings are available in an overridden context" do - Puppet::Context.bind("a", 1) - - inner_a = nil - inner_b = nil - Puppet::Context.override("b" => 2) do - inner_a = Puppet::Context.lookup("a") - inner_b = Puppet::Context.lookup("b") + it "calls a provided block for a default value when none is found" do + expect(Puppet::Context.lookup("a") { "default" }).to eq("default") end - expect(inner_a).to eq(1) - expect(inner_b).to eq(2) - end - - it "overridden bindings do not exist outside of the override" do - Puppet::Context.bind("a", 1) - - Puppet::Context.override("a" => 2) do + it "behaves as if pushed a {} if you push nil" do + Puppet::Context.push(nil) + expect(Puppet::Context.lookup(:trusted_information)).to_not be_nil + Puppet::Context.pop end - expect(Puppet::Context.lookup("a")).to eq(1) + it "cannot pop off the top of the stack" do + root = Puppet::Context.pop + expect(root).to be_root + expect(Puppet::Context.pop).to be_equal(root) + end + + it "protects the bindings table from casual access" do + expect { Puppet::Context.push({}).table }.to raise_error(NoMethodError, /protected/) + Puppet::Context.pop + end end - it "overridden bindings do not exist outside of the override even when leaving via an error" do - Puppet::Context.bind("a", 1) + describe "with additional context" do + before :each do + Puppet::Context.push("a" => 1) + end - begin + after :each do + Puppet::Context.pop + end + + it "holds values for later lookup" do + expect(Puppet::Context.lookup("a")).to eq(1) + end + + it "allows rebinding values in a nested context" do + inner = nil Puppet::Context.override("a" => 2) do - raise "this should still cause the bindings to leave" + inner = Puppet::Context.lookup("a") end - rescue + + expect(inner).to eq(2) end - expect(Puppet::Context.lookup("a")).to eq(1) + it "outer bindings are available in an overridden context" do + inner_a = nil + inner_b = nil + Puppet::Context.override("b" => 2) do + inner_a = Puppet::Context.lookup("a") + inner_b = Puppet::Context.lookup("b") + end + + expect(inner_a).to eq(1) + expect(inner_b).to eq(2) + end + + it "overridden bindings do not exist outside of the override" do + Puppet::Context.override("a" => 2) do + end + + expect(Puppet::Context.lookup("a")).to eq(1) + end + + it "overridden bindings do not exist outside of the override even when leaving via an error" do + begin + Puppet::Context.override("a" => 2) do + raise "this should still cause the bindings to leave" + end + rescue + end + + expect(Puppet::Context.lookup("a")).to eq(1) + end end end From d288d689514c9355ad5dff4800973b1eb8c5a4ef Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Thu, 2 Jan 2014 12:24:04 -0800 Subject: [PATCH 334/800] (PUP-672) Move TrustedInformation into Puppet::Context This is just a refactor putting Puppet::Indirector::TrustedInformation into the Puppet::Context module instead. --- lib/puppet/context.rb | 2 ++ .../trusted_information.rb | 5 +++-- lib/puppet/indirector/catalog/compiler.rb | 2 +- lib/puppet/indirector/request.rb | 1 - lib/puppet/network/http/handler.rb | 2 +- lib/puppet/test/test_helper.rb | 2 +- .../trusted_information_spec.rb | 16 +++++++++------- 7 files changed, 17 insertions(+), 13 deletions(-) rename lib/puppet/{indirector => context}/trusted_information.rb (95%) rename spec/unit/{indirector => context}/trusted_information_spec.rb (81%) diff --git a/lib/puppet/context.rb b/lib/puppet/context.rb index 11e82d72c..7d4195127 100644 --- a/lib/puppet/context.rb +++ b/lib/puppet/context.rb @@ -1,4 +1,6 @@ module Puppet::Context + require 'puppet/context/trusted_information' + class UndefinedBindingError < Puppet::Error; end # @api private diff --git a/lib/puppet/indirector/trusted_information.rb b/lib/puppet/context/trusted_information.rb similarity index 95% rename from lib/puppet/indirector/trusted_information.rb rename to lib/puppet/context/trusted_information.rb index 87717bb12..57024f4a9 100644 --- a/lib/puppet/indirector/trusted_information.rb +++ b/lib/puppet/context/trusted_information.rb @@ -1,6 +1,7 @@ -class Puppet::Indirector::TrustedInformation +# @api private +class Puppet::Context::TrustedInformation # one of 'remote', 'local', or false, where 'remote' is authenticated via cert, - # 'local' is trusted by virtue of running on the same machine (not a remove + # 'local' is trusted by virtue of running on the same machine (not a remote # request), and false is an unauthenticated remote request. # # @return [String, Boolean] diff --git a/lib/puppet/indirector/catalog/compiler.rb b/lib/puppet/indirector/catalog/compiler.rb index d6223eba0..e3af33e7c 100644 --- a/lib/puppet/indirector/catalog/compiler.rb +++ b/lib/puppet/indirector/catalog/compiler.rb @@ -41,7 +41,7 @@ class Puppet::Resource::Catalog::Compiler < Puppet::Indirector::Code extract_facts_from_request(request) node = node_from_request(request) - node.trusted_data = Puppet::Context.lookup(:trusted_information) { Puppet::Indirector::TrustedInformation.local(node) }.to_h + node.trusted_data = Puppet::Context.lookup(:trusted_information) { Puppet::Context::TrustedInformation.local(node) }.to_h if catalog = compile(node) return catalog diff --git a/lib/puppet/indirector/request.rb b/lib/puppet/indirector/request.rb index ec9952df4..07ff46a1a 100644 --- a/lib/puppet/indirector/request.rb +++ b/lib/puppet/indirector/request.rb @@ -3,7 +3,6 @@ require 'uri' require 'puppet/indirector' require 'puppet/util/pson' require 'puppet/network/resolver' -require 'puppet/indirector/trusted_information' # This class encapsulates all of the information you need to make an # Indirection call, and as a result also handles REST calls. It's somewhat diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb index af9593795..63a470e6c 100644 --- a/lib/puppet/network/http/handler.rb +++ b/lib/puppet/network/http/handler.rb @@ -102,7 +102,7 @@ module Puppet::Network::HTTP::Handler raise HTTPNotFoundError, "No handler for #{indirection.name}" end - trusted = Puppet::Indirector::TrustedInformation.remote(params[:authenticated], params[:node], certificate) + trusted = Puppet::Context::TrustedInformation.remote(params[:authenticated], params[:node], certificate) Puppet::Context.override(:trusted_information => trusted) do send("do_#{method}", indirection, key, params, request, response) end diff --git a/lib/puppet/test/test_helper.rb b/lib/puppet/test/test_helper.rb index c7446c79a..d3970b82d 100644 --- a/lib/puppet/test/test_helper.rb +++ b/lib/puppet/test/test_helper.rb @@ -94,7 +94,7 @@ module Puppet::Test Puppet::DataBinding::Hiera.instance_variable_set("@hiera", nil) Puppet::Context.push(:trusted_information => - Puppet::Indirector::TrustedInformation.new('local', 'testing', {})) + Puppet::Context::TrustedInformation.new('local', 'testing', {})) end # Call this method once per test, after execution of each individual test. diff --git a/spec/unit/indirector/trusted_information_spec.rb b/spec/unit/context/trusted_information_spec.rb similarity index 81% rename from spec/unit/indirector/trusted_information_spec.rb rename to spec/unit/context/trusted_information_spec.rb index 2ec74071f..2b9ecda0c 100644 --- a/spec/unit/indirector/trusted_information_spec.rb +++ b/spec/unit/context/trusted_information_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Puppet::Indirector::TrustedInformation do +describe Puppet::Context::TrustedInformation do let(:key) do key = Puppet::SSL::Key.new("myname") key.generate @@ -24,7 +24,7 @@ describe Puppet::Indirector::TrustedInformation do context "when remote" do it "has no cert information when it isn't authenticated" do - trusted = Puppet::Indirector::TrustedInformation.remote(false, 'ignored', nil) + trusted = Puppet::Context::TrustedInformation.remote(false, 'ignored', nil) expect(trusted.authenticated).to eq(false) expect(trusted.certname).to be_nil @@ -32,7 +32,7 @@ describe Puppet::Indirector::TrustedInformation do end it "is remote and has certificate information when it is authenticated" do - trusted = Puppet::Indirector::TrustedInformation.remote(true, 'cert name', cert) + trusted = Puppet::Context::TrustedInformation.remote(true, 'cert name', cert) expect(trusted.authenticated).to eq('remote') expect(trusted.certname).to eq('cert name') @@ -47,22 +47,24 @@ describe Puppet::Indirector::TrustedInformation do it "is authenticated local with the nodes clientcert" do node = Puppet::Node.new('testing', :parameters => { 'clientcert' => 'cert name' }) - trusted = Puppet::Indirector::TrustedInformation.local(node) + trusted = Puppet::Context::TrustedInformation.local(node) expect(trusted.authenticated).to eq('local') expect(trusted.certname).to eq('cert name') + expect(trusted.extensions).to eq({}) end it "is authenticated local with no clientcert when there is no node" do - trusted = Puppet::Indirector::TrustedInformation.local(nil) + trusted = Puppet::Context::TrustedInformation.local(nil) expect(trusted.authenticated).to eq('local') expect(trusted.certname).to be_nil + expect(trusted.extensions).to eq({}) end end it "converts itself to a hash" do - trusted = Puppet::Indirector::TrustedInformation.remote(true, 'cert name', cert) + trusted = Puppet::Context::TrustedInformation.remote(true, 'cert name', cert) expect(trusted.to_h).to eq({ 'authenticated' => 'remote', @@ -75,7 +77,7 @@ describe Puppet::Indirector::TrustedInformation do end it "freezes the hash" do - trusted = Puppet::Indirector::TrustedInformation.remote(true, 'cert name', cert) + trusted = Puppet::Context::TrustedInformation.remote(true, 'cert name', cert) expect(trusted.to_h).to be_deeply_frozen end From 59b13da45717ca55321cc70a68125d549421c516 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Thu, 2 Jan 2014 14:15:17 -0800 Subject: [PATCH 335/800] (PUP-672) Fail fast if pop root of Context stack Rather than silently accept a root pop and return the root binding, fail fast with a StackUnderflow exception if attempt to pop the root of Puppet::Context. --- lib/puppet/context.rb | 5 +++-- spec/unit/context_spec.rb | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/puppet/context.rb b/lib/puppet/context.rb index 7d4195127..c6e871880 100644 --- a/lib/puppet/context.rb +++ b/lib/puppet/context.rb @@ -2,6 +2,7 @@ module Puppet::Context require 'puppet/context/trusted_information' class UndefinedBindingError < Puppet::Error; end + class StackUnderflow < Puppet::Error; end # @api private class Bindings @@ -42,8 +43,8 @@ module Puppet::Context # @api private def self.pop - @bindings = @bindings.parent if !@bindings.root? - @bindings + raise(StackUnderflow, "Attempted to pop, but lready at root of the context stack.") if @bindings.root? + @bindings = @bindings.parent end # Lookup a binding by name or return a default value provided by a passed block (if given). diff --git a/spec/unit/context_spec.rb b/spec/unit/context_spec.rb index 7ba5a3ec9..d7e3bd860 100644 --- a/spec/unit/context_spec.rb +++ b/spec/unit/context_spec.rb @@ -17,10 +17,12 @@ describe Puppet::Context do Puppet::Context.pop end - it "cannot pop off the top of the stack" do + it "fails if you try to pop off the top of the stack" do root = Puppet::Context.pop expect(root).to be_root - expect(Puppet::Context.pop).to be_equal(root) + expect { Puppet::Context.pop }.to raise_error(Puppet::Context::StackUnderflow) + # TestHelper expects to have something to pop in its after_each_test() + Puppet::Context.push({}) end it "protects the bindings table from casual access" do From 513f5fff4b3a28272bde74cd528a17958fbd9397 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Thu, 2 Jan 2014 15:48:41 -0800 Subject: [PATCH 336/800] Revert "Merge pull request #2207 from jpartlow/maint/master/simplify-confine-for-6857-test" This reverts commit 6ad16a47f878df3eb3a31c063bff3e0f5c63745b, reversing changes made to 2f1bee412b41cd2b244da39883f3a08a37aef1a3. --- ...ssword-disclosure-when-changing-a-users-password.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/acceptance/tests/ticket_6857_password-disclosure-when-changing-a-users-password.rb b/acceptance/tests/ticket_6857_password-disclosure-when-changing-a-users-password.rb index 7752c3ecf..97577c9d0 100644 --- a/acceptance/tests/ticket_6857_password-disclosure-when-changing-a-users-password.rb +++ b/acceptance/tests/ticket_6857_password-disclosure-when-changing-a-users-password.rb @@ -1,10 +1,14 @@ test_name "#6857: redact password hashes when applying in noop mode" hosts_to_test = agents.reject do |agent| - result = on(agent, %Q{#{agent['puppetbindir']}/ruby -e 'require "shadow" or raise'}, :silent => true) - result.exit_code != 0 + if agent['platform'].match /(?:ubuntu|centos|debian|el-|fc-)/ + result = on(agent, %Q{#{agent['puppetbindir']}/ruby -e 'require "shadow" or raise'}, :silent => true) + result.exit_code != 0 + else + false + end end -skip_test "No suitable hosts found. Without the Ruby shadow library, passwords cannot be set." if hosts_to_test.empty? +skip_test "No suitable hosts found" if hosts_to_test.empty? adduser_manifest = < Date: Thu, 2 Jan 2014 17:00:20 -0800 Subject: [PATCH 337/800] (PUP-724,#21922) Autoload compares only integer secs Although the ultimate reasons have not been tracked down, there have been occasions where the Puppet::Util::Autoload.changed? method will erroneously report that a file has changed based on an mtime having seemed to change nsecs, despite no evidence of this being seen in the file system. The previous implementation was comparing mtime (Ruby Time instances) and the nsecs seem to be volatile under unknown conditions. When it does break, this was manifesting as a failure to autoload puppet/util/instrumentation/listeners/log.rb (the instrumentation code is relying on Puppet::Util::Autoload for initial loading but does not expect its listeners to be reloaded, and they are not written in such a way as to be reloadable...) (see PUP-724 and its associated tickets for details). The work around is to instead just compare integer secs. --- lib/puppet/util/autoload.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/util/autoload.rb b/lib/puppet/util/autoload.rb index 82e0560e8..5c3d3dea2 100644 --- a/lib/puppet/util/autoload.rb +++ b/lib/puppet/util/autoload.rb @@ -45,7 +45,7 @@ class Puppet::Util::Autoload file, old_mtime = loaded[name] return true unless file == get_file(name) begin - old_mtime != File.mtime(file) + old_mtime.to_i != File.mtime(file).to_i rescue Errno::ENOENT true end From 781e57903f5be5a7a3d8b50736919b39436ae6ab Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Fri, 3 Jan 2014 09:58:54 -0800 Subject: [PATCH 338/800] (maint) Update fedora platform designation for ticket_6857 test Our config/nodes platform strings have changed and now read 'fedora' rather than 'fc-' so ticket_6857_password-disclosure-when-changing... needed to be updated to perform the ruby-libshadow test on fedora again. Also providing the libshadow test output so we can see a little better what's going on the next time this needs debugging. --- ...57_password-disclosure-when-changing-a-users-password.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/acceptance/tests/ticket_6857_password-disclosure-when-changing-a-users-password.rb b/acceptance/tests/ticket_6857_password-disclosure-when-changing-a-users-password.rb index 97577c9d0..d93348d70 100644 --- a/acceptance/tests/ticket_6857_password-disclosure-when-changing-a-users-password.rb +++ b/acceptance/tests/ticket_6857_password-disclosure-when-changing-a-users-password.rb @@ -1,10 +1,12 @@ test_name "#6857: redact password hashes when applying in noop mode" hosts_to_test = agents.reject do |agent| - if agent['platform'].match /(?:ubuntu|centos|debian|el-|fc-)/ - result = on(agent, %Q{#{agent['puppetbindir']}/ruby -e 'require "shadow" or raise'}, :silent => true) + if agent['platform'].match /(?:ubuntu|centos|debian|el-|fedora)/ + result = on(agent, %Q{#{agent['puppetbindir']}/ruby -e 'require "shadow" or raise'}, :acceptable_exit_codes => [0,1]) result.exit_code != 0 else + # Non-linux platforms do not rely on ruby-libshadow for password management + # and so we don't reject them from testing false end end From b1dfbd2da114958b75cd156f01584f9a85154bc4 Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Fri, 3 Jan 2014 11:07:26 -0800 Subject: [PATCH 339/800] (maint) Add a diagnostic check that lmhosts is running --- spec/integration/util/windows/security_spec.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/spec/integration/util/windows/security_spec.rb b/spec/integration/util/windows/security_spec.rb index f839ec230..bf52bc8d9 100755 --- a/spec/integration/util/windows/security_spec.rb +++ b/spec/integration/util/windows/security_spec.rb @@ -25,6 +25,13 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win :none => Win32::Security::SID::Nobody, :everyone => Win32::Security::SID::Everyone } + # The TCP/IP NetBIOS Helper service (aka 'lmhosts') has ended up + # disabled on some VMs for reasons we couldn't track down. This + # condition causes tests which rely on resolving UNC style paths + # (like \\localhost) to fail with unhelpful error messages. + # Put a check for this upfront to aid debug should this strike again. + service = Puppet::Type.type(:service).new(:name => 'lmhosts') + service.provider.status.should == :running end let (:sids) { @sids } From 34c46977857ab06e9dc1b11045300264b96e5775 Mon Sep 17 00:00:00 2001 From: Stephen Gelman Date: Sun, 17 Nov 2013 18:49:13 -0600 Subject: [PATCH 340/800] (#7173) Add HTTP basic auth support to HTTP reporting in Puppet. Does the following: * Add logic in Puppet::Reports::Http to properly handle HTTP basic auth using the form http://username:password@host/path * Add a unit test to make sure HTTP basic auth is done if it is given in the url --- lib/puppet/reports/http.rb | 5 +++++ spec/unit/reports/http_spec.rb | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/lib/puppet/reports/http.rb b/lib/puppet/reports/http.rb index 475400797..04808ab2e 100644 --- a/lib/puppet/reports/http.rb +++ b/lib/puppet/reports/http.rb @@ -1,3 +1,4 @@ +require 'base64' require 'puppet' require 'puppet/network/http_pool' require 'uri' @@ -15,6 +16,10 @@ Puppet::Reports.register_report(:http) do url = URI.parse(Puppet[:reporturl]) body = self.to_yaml headers = { "Content-Type" => "application/x-yaml" } + if url.user && url.password + user_pass_encoded = Base64.encode64("#{url.user}:#{url.password}").strip + headers["Authorization"] = "Basic #{user_pass_encoded}" + end use_ssl = url.scheme == 'https' conn = Puppet::Network::HttpPool.http_instance(url.host, url.port, use_ssl) response = conn.post(url.path, body, headers) diff --git a/spec/unit/reports/http_spec.rb b/spec/unit/reports/http_spec.rb index 1a99761d8..3057c3c85 100755 --- a/spec/unit/reports/http_spec.rb +++ b/spec/unit/reports/http_spec.rb @@ -51,6 +51,15 @@ describe processor do subject.process end + it "should use the username and password specified by the 'reporturl' setting" do + Puppet[:reporturl] = "https://user:pass@myhost.mydomain:1234/report/upload" + http.expects(:post).with {|path,data,headers| + headers['Authorization'].should == "Basic dXNlcjpwYXNz" + }.returns(httpok) + + subject.process + end + it "should give the body as the report as YAML" do connection.expects(:post).with(anything, subject.to_yaml, anything).returns(httpok) From 747076f00ce7c6b3b86acf2823be9c6536a821d4 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 3 Jan 2014 12:47:10 -0800 Subject: [PATCH 341/800] (PUP-1068) Add basic auth to HTTP Connection Sometimes a request needs to perform authentication. We can support this with basic auth readily enough. This commit moves the basic auth support from the report processor and makes it a capability of the network connection code. As part of this it needed to move away from using the #get, #post, etc calls on the connection object and instead construct the Net::HTTP request objects. This might present a slight change in behavior, but not one that users will normally notice. The most noticable change is that the #request method can no longer be used for calling arbitrary methods on the underlying connection, instead it can only be used to call to the appropriate request method (get, head, delete, post, put) In order to make this work more clearly, this also adds actual parameters to the connection class's #get, #post, etc methods. The defaults are taken from the Net::HTTP defaults, which is what would have been used before. --- lib/puppet/network/http/connection.rb | 124 ++++++++++++++++------ lib/puppet/network/http_pool.rb | 6 +- lib/puppet/reports/http.rb | 11 +- spec/unit/network/http/connection_spec.rb | 44 +++++++- spec/unit/reports/http_spec.rb | 14 +-- 5 files changed, 146 insertions(+), 53 deletions(-) diff --git a/lib/puppet/network/http/connection.rb b/lib/puppet/network/http/connection.rb index adb166439..3b1780e3c 100644 --- a/lib/puppet/network/http/connection.rb +++ b/lib/puppet/network/http/connection.rb @@ -19,25 +19,33 @@ module Puppet::Network::HTTP # certificate configuration for their authentication, and # * Provides some useful error handling for any SSL errors that occur # during a request. + # @api public class Connection include Puppet::Network::Authentication OPTION_DEFAULTS = { :use_ssl => true, :verify => nil, - :redirect_limit => 10 + :redirect_limit => 10, } # Creates a new HTTP client connection to `host`:`port`. # @param host [String] the host to which this client will connect to # @param port [Fixnum] the port to which this client will connect to - # @param options [Hash] options influencing the properties of the created connection, - # the following options are recognized: - # :use_ssl [Boolean] true to connect with SSL, false otherwise, defaults to true - # :verify [#setup_connection] An object that will configure any verification to do on the connection - # :redirect_limit [Fixnum] the number of allowed redirections, defaults to 10 - # passing any other option in the options hash results in a Puppet::Error exception - # @note the HTTP connection itself happens lazily only when {#request}, or one of the {#get}, {#post}, {#delete}, {#head} or {#put} is called + # @param options [Hash] options influencing the properties of the created + # connection, + # @option options [Boolean] :use_ssl true to connect with SSL, false + # otherwise, defaults to true + # @option options [#setup_connection] :verify An object that will configure + # any verification to do on the connection + # @option options [Fixnum] :redirect_limit the number of allowed + # redirections, defaults to 10 passing any other option in the options + # hash results in a Puppet::Error exception + # + # @note the HTTP connection itself happens lazily only when {#request}, or + # one of the {#get}, {#post}, {#delete}, {#head} or {#put} is called + # @note The correct way to obtain a connection is to use one of the factory + # methods on {Puppet::Network::HttpPool} # @api private def initialize(host, port, options = {}) @host = host @@ -52,41 +60,59 @@ module Puppet::Network::HTTP @redirect_limit = options[:redirect_limit] end - def get(*args) - request(:get, *args) + # @!macro [new] common_options + # @param options [Hash] options influencing the request made + # @option options [Hash{Symbol => String}] :basic_auth The basic auth + # :username and :password to use for the request + + # @param path [String] + # @param headers [Hash{String => String}] + # @!macro common_options + # @api public + def get(path, headers = {}, options = {}) + request_with_redirects(Net::HTTP::Get.new(path, headers), options) end - def post(*args) - request(:post, *args) + # @param path [String] + # @param data [String] + # @param headers [Hash{String => String}] + # @!macro common_options + # @api public + def post(path, data, headers = nil, options = {}) + request = Net::HTTP::Post.new(path, headers) + request.body = data + request_with_redirects(request, options) end - def head(*args) - request(:head, *args) + # @param path [String] + # @param headers [Hash{String => String}] + # @!macro common_options + # @api public + def head(path, headers = {}, options = {}) + request_with_redirects(Net::HTTP::Head.new(path, headers), options) end - def delete(*args) - request(:delete, *args) + # @param path [String] + # @param headers [Hash{String => String}] + # @!macro common_options + # @api public + def delete(path, headers = {'Depth' => 'Infinity'}, options = {}) + request_with_redirects(Net::HTTP::Delete.new(path, headers), options) end - def put(*args) - request(:put, *args) + # @param path [String] + # @param data [String] + # @param headers [Hash{String => String}] + # @!macro common_options + # @api public + def put(path, data, headers = nil, options = {}) + request = Net::HTTP::Put.new(path, headers) + request.body = data + request_with_redirects(request, options) end def request(method, *args) - current_args = args.dup - @redirect_limit.times do |redirection| - response = execute_request(method, *args) - return response unless [301, 302, 307].include?(response.code.to_i) - - # handle the redirection - location = URI.parse(response['location']) - @connection = initialize_connection(location.host, location.port, location.scheme == 'https') - - # update to the current request path - current_args = [location.path] + current_args.drop(1) - # and try again... - end - raise RedirectionLimitExceededException, "Too many HTTP redirections for #{@host}:#{@port}" + self.send(method, *args) end # TODO: These are proxies for the Net::HTTP#request_* methods, which are @@ -123,12 +149,42 @@ module Puppet::Network::HTTP private + def request_with_redirects(request, options) + current_request = request + @redirect_limit.times do |redirection| + apply_options_to(current_request, options) + + response = execute_request(current_request) + return response unless [301, 302, 307].include?(response.code.to_i) + + # handle the redirection + location = URI.parse(response['location']) + @connection = initialize_connection(location.host, location.port, location.scheme == 'https') + + # update to the current request path + current_request = current_request.class.new(location.path) + current_request.body = request.body + request.each do |header, value| + current_request[header] = value + end + + # and try again... + end + raise RedirectionLimitExceededException, "Too many HTTP redirections for #{@host}:#{@port}" + end + + def apply_options_to(request, options) + if options[:basic_auth] + request.basic_auth(options[:basic_auth][:user], options[:basic_auth][:password]) + end + end + def connection @connection || initialize_connection(@host, @port, @use_ssl) end - def execute_request(method, *args) - response = connection.send(method, *args) + def execute_request(request) + response = connection.request(request) # Check the peer certs and warn if they're nearing expiration. warn_if_near_expiration(*@verify.peer_certs) diff --git a/lib/puppet/network/http_pool.rb b/lib/puppet/network/http_pool.rb index 97094c9ad..67ef7faef 100644 --- a/lib/puppet/network/http_pool.rb +++ b/lib/puppet/network/http_pool.rb @@ -3,10 +3,10 @@ require 'puppet/network/http/connection' module Puppet::Network; end # This module contains the factory methods that should be used for getting a -# Puppet::Network::HTTP::Connection instance. +# {Puppet::Network::HTTP::Connection} instance. # -# The name "HttpPool" is a misnomer, and a leftover of history, but we would -# like to make this cache connections in the future. +# @note The name "HttpPool" is a misnomer, and a leftover of history, but we would +# like to make this cache connections in the future. # # @api public # diff --git a/lib/puppet/reports/http.rb b/lib/puppet/reports/http.rb index 04808ab2e..403e81ad7 100644 --- a/lib/puppet/reports/http.rb +++ b/lib/puppet/reports/http.rb @@ -1,4 +1,3 @@ -require 'base64' require 'puppet' require 'puppet/network/http_pool' require 'uri' @@ -14,15 +13,17 @@ Puppet::Reports.register_report(:http) do def process url = URI.parse(Puppet[:reporturl]) - body = self.to_yaml headers = { "Content-Type" => "application/x-yaml" } + options = {} if url.user && url.password - user_pass_encoded = Base64.encode64("#{url.user}:#{url.password}").strip - headers["Authorization"] = "Basic #{user_pass_encoded}" + options[:basic_auth] = { + :user => url.user, + :password => url.password + } end use_ssl = url.scheme == 'https' conn = Puppet::Network::HttpPool.http_instance(url.host, url.port, use_ssl) - response = conn.post(url.path, body, headers) + response = conn.post(url.path, self.to_yaml, headers, options) unless response.kind_of?(Net::HTTPSuccess) Puppet.err "Unable to submit report to #{Puppet[:reporturl].to_s} [#{response.code}] #{response.msg}" end diff --git a/spec/unit/network/http/connection_spec.rb b/spec/unit/network/http/connection_spec.rb index 467705f01..a5e6f64ae 100644 --- a/spec/unit/network/http/connection_spec.rb +++ b/spec/unit/network/http/connection_spec.rb @@ -8,6 +8,7 @@ describe Puppet::Network::HTTP::Connection do let (:host) { "me" } let (:port) { 54321 } subject { Puppet::Network::HTTP::Connection.new(host, port, :verify => Puppet::SSL::Validator.no_validator) } + let (:httpok) { Net::HTTPOK.new('1.1', 200, '') } context "when providing HTTP connections" do after do @@ -66,7 +67,6 @@ describe Puppet::Network::HTTP::Connection do let (:host) { "my_server" } let (:port) { 8140 } let (:subject) { Puppet::Network::HTTP::Connection.new(host, port, :use_ssl => false, :verify => Puppet::SSL::Validator.no_validator) } - let (:httpok) { Net::HTTPOK.new('1.1', 200, '') } before :each do httpok.stubs(:body).returns "" @@ -101,7 +101,6 @@ describe Puppet::Network::HTTP::Connection do let (:host) { "my_server" } let (:port) { 8140 } - let (:httpok) { Net::HTTPOK.new('1.1', 200, '') } it "should provide a useful error message when one is available and certificate validation fails", :unless => Puppet.features.microsoft_windows? do connection = Puppet::Network::HTTP::Connection.new( @@ -151,7 +150,7 @@ describe Puppet::Network::HTTP::Connection do host, port, :verify => NoProblemsValidator.new(cert)) - Net::HTTP.any_instance.stubs(:get).returns(httpok) + Net::HTTP.any_instance.stubs(:request).returns(httpok) connection.expects(:warn_if_near_expiration).with(cert) @@ -166,7 +165,7 @@ describe Puppet::Network::HTTP::Connection do end def setup_connection(connection) - connection.stubs(:get).with do + connection.stubs(:request).with do true end.raises(OpenSSL::SSL::SSLError.new(@fails_with)) end @@ -204,7 +203,6 @@ describe Puppet::Network::HTTP::Connection do let (:other_path) { "other-path" } let (:subject) { Puppet::Network::HTTP::Connection.new("my_server", 8140, :use_ssl => false, :verify => Puppet::SSL::Validator.no_validator) } let (:httpredirection) { Net::HTTPFound.new('1.1', 302, 'Moved Temporarily') } - let (:httpok) { Net::HTTPOK.new('1.1', 200, '') } before :each do httpredirection['location'] = "http://#{other_host}:#{other_port}/#{other_path}" @@ -234,4 +232,40 @@ describe Puppet::Network::HTTP::Connection do }.to raise_error(Puppet::Network::HTTP::RedirectionLimitExceededException) end end + + it "allows setting basic auth on get requests" do + expect_request_with_basic_auth + + subject.get('/path', nil, :basic_auth => { :user => 'user', :password => 'password' }) + end + + it "allows setting basic auth on post requests" do + expect_request_with_basic_auth + + subject.post('/path', 'data', nil, :basic_auth => { :user => 'user', :password => 'password' }) + end + + it "allows setting basic auth on head requests" do + expect_request_with_basic_auth + + subject.head('/path', nil, :basic_auth => { :user => 'user', :password => 'password' }) + end + + it "allows setting basic auth on delete requests" do + expect_request_with_basic_auth + + subject.delete('/path', nil, :basic_auth => { :user => 'user', :password => 'password' }) + end + + it "allows setting basic auth on put requests" do + expect_request_with_basic_auth + + subject.put('/path', 'data', nil, :basic_auth => { :user => 'user', :password => 'password' }) + end + + def expect_request_with_basic_auth + Net::HTTP.any_instance.expects(:request).with do |request| + expect(request['authorization']).to match(/^Basic/) + end.returns(httpok) + end end diff --git a/spec/unit/reports/http_spec.rb b/spec/unit/reports/http_spec.rb index 3057c3c85..6efb72751 100755 --- a/spec/unit/reports/http_spec.rb +++ b/spec/unit/reports/http_spec.rb @@ -46,28 +46,30 @@ describe processor do it "should use the path specified by the 'reporturl' setting" do report_path = URI.parse(Puppet[:reporturl]).path - connection.expects(:post).with(report_path, anything, anything).returns(httpok) + connection.expects(:post).with(report_path, anything, anything, {}).returns(httpok) subject.process end it "should use the username and password specified by the 'reporturl' setting" do Puppet[:reporturl] = "https://user:pass@myhost.mydomain:1234/report/upload" - http.expects(:post).with {|path,data,headers| - headers['Authorization'].should == "Basic dXNlcjpwYXNz" - }.returns(httpok) + + connection.expects(:post).with(anything, anything, anything, :basic_auth => { + :user => 'user', + :password => 'pass' + }).returns(httpok) subject.process end it "should give the body as the report as YAML" do - connection.expects(:post).with(anything, subject.to_yaml, anything).returns(httpok) + connection.expects(:post).with(anything, subject.to_yaml, anything, {}).returns(httpok) subject.process end it "should set content-type to 'application/x-yaml'" do - connection.expects(:post).with(anything, anything, has_entry("Content-Type" => "application/x-yaml")).returns(httpok) + connection.expects(:post).with(anything, anything, has_entry("Content-Type" => "application/x-yaml"), {}).returns(httpok) subject.process end From c2446db0605038fa390a46f09c882e3e8a47a42c Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 4 Jan 2014 01:52:21 +0100 Subject: [PATCH 342/800] (PUP-1144) Make future parser accept $_private This removes validation of QualifiedName (they must be validated differently depending on where they appear). Variables were bit by this too restrictive validation rule. --- lib/puppet/pops/validation/checker3_1.rb | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/puppet/pops/validation/checker3_1.rb b/lib/puppet/pops/validation/checker3_1.rb index d752db8b7..77f643fb1 100644 --- a/lib/puppet/pops/validation/checker3_1.rb +++ b/lib/puppet/pops/validation/checker3_1.rb @@ -261,15 +261,12 @@ class Puppet::Pops::Validation::Checker3_1 top(o.eContainer, o) end - # Asserts that value is a valid QualifiedName. No additional checking is made, objects that use - # a QualifiedName as a name should check the validity - this since a QualifiedName is used as a BARE WORD - # and then additional chars may be valid (like a hyphen). + # No checking takes place - all expressions using a QualifiedName need to check. This because the + # rules are slightly different depending on the container (A variable allows a numeric start, but not + # other names). This means that (if the lexer/parser so chooses) a QualifiedName + # can be anything when it represents a Bare Word and evaluates to a String. # def check_QualifiedName(o) - # Is this a valid qualified name? - if o.value !~ Puppet::Pops::Patterns::NAME - acceptor.accept(Issues::ILLEGAL_NAME, o, {:name=>o.value}) - end end # Checks that the value is a valid UpperCaseWord (a CLASSREF), and optionally if it contains a hypen. From 1304a33845697e6b7cc78cc4d34cc9e30204f877 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 4 Jan 2014 02:46:22 +0100 Subject: [PATCH 343/800] (PUP-1144) Fix up failing tests (pending new/better validation) Two tests that expected validation to fail did not get triggered because the too strict validation was removed. It needs to be replaced with a different better validation. Such validation should be added in 3.5 (too much work for 3.4) - instead, just accepting potentially faulty names (they still have to lex correctly). --- spec/unit/pops/validator/validator_spec.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/unit/pops/validator/validator_spec.rb b/spec/unit/pops/validator/validator_spec.rb index 16052d2cc..654e87f06 100644 --- a/spec/unit/pops/validator/validator_spec.rb +++ b/spec/unit/pops/validator/validator_spec.rb @@ -19,11 +19,13 @@ describe "validating 3x" do end it 'should raise error for illegal names' do + pending "validation was too strict, now too relaxed - validation missing" expect(validate(fqn('Aaa'))).to have_issue(Puppet::Pops::Issues::ILLEGAL_NAME) expect(validate(fqn('AAA'))).to have_issue(Puppet::Pops::Issues::ILLEGAL_NAME) end it 'should raise error for illegal variable names' do + pending "validation was too strict, now too relaxed - validation missing" expect(validate(fqn('Aaa').var())).to have_issue(Puppet::Pops::Issues::ILLEGAL_NAME) expect(validate(fqn('AAA').var())).to have_issue(Puppet::Pops::Issues::ILLEGAL_NAME) end @@ -32,4 +34,4 @@ describe "validating 3x" do expect(validate(fqn('aaa').minus_set(2))).to have_issue(Puppet::Pops::Issues::UNSUPPORTED_OPERATOR) end -end \ No newline at end of file +end From ae9d4f9336bb775e42d402be5c05fcd3be1d76bd Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 5 Jan 2014 23:46:04 +0100 Subject: [PATCH 344/800] (PUP-1144) Make variable name checking stricter The fix for PUP-1144 relaxed the rules for variable checking. This reintroduces checking but done right. It is now possible to start the *first* name segment with an underscore. Subsequent segments must start with a lower case letter. Legal: * $_a * $::_a * $aA * $aA::aA Illegal: * $a::_a * $A * $a::A --- lib/puppet/pops/issues.rb | 4 +-- lib/puppet/pops/patterns.rb | 2 +- lib/puppet/pops/validation/checker4_0.rb | 6 +++-- spec/unit/pops/validator/validator_spec.rb | 31 ++++++++++++++++++++++ 4 files changed, 38 insertions(+), 5 deletions(-) diff --git a/lib/puppet/pops/issues.rb b/lib/puppet/pops/issues.rb index 7fe51bdb2..74cb5525f 100644 --- a/lib/puppet/pops/issues.rb +++ b/lib/puppet/pops/issues.rb @@ -240,11 +240,11 @@ module Puppet::Pops::Issues end ILLEGAL_NAME = hard_issue :ILLEGAL_NAME, :name do - "Illegal name. The given name #{name} does not conform to the naming rule /^((::)?[a-z]\w*)(::[a-z]\w*)*$/" + "Illegal name. The given name #{name} does not conform to the naming rule /^((::)?[a-z_]\w*)(::[a-z]\w*)*$/" end ILLEGAL_VAR_NAME = hard_issue :ILLEGAL_VAR_NAME, :name do - "Illegal variable name, The given name '#{name}' does not conform to the naming rule /^(::)?(\w+::)*\w+$/" + "Illegal variable name, The given name '#{name}' does not conform to the naming rule /^((::)?[a-z_]\w*)(::[a-z]\w*)*$/" end ILLEGAL_NUMERIC_VAR_NAME = hard_issue :ILLEGAL_NUMERIC_VAR_NAME, :name do diff --git a/lib/puppet/pops/patterns.rb b/lib/puppet/pops/patterns.rb index fd8764fa0..61246264f 100644 --- a/lib/puppet/pops/patterns.rb +++ b/lib/puppet/pops/patterns.rb @@ -35,7 +35,7 @@ module Puppet::Pops::Patterns DOLLAR_VAR = %r{\$(::)?(\w+::)*\w+} # VAR_NAME matches the name part of a variable (The $ character is not included) - VAR_NAME = %r{(::)?(\w+::)*\w+} + VAR_NAME = %r{\A((::)?[a-z_]\w*)(::[a-z]\w*)*\z} # A Numeric var name must be the decimal number 0, or a decimal number not starting with 0 NUMERIC_VAR_NAME = %r{\A(?:0|(?:[1-9][0-9]*))\z} diff --git a/lib/puppet/pops/validation/checker4_0.rb b/lib/puppet/pops/validation/checker4_0.rb index 705989331..e162f42da 100644 --- a/lib/puppet/pops/validation/checker4_0.rb +++ b/lib/puppet/pops/validation/checker4_0.rb @@ -334,7 +334,7 @@ class Puppet::Pops::Validation::Checker4_0 # Checks that variable is either strictly 0, or a non 0 starting decimal number, or a valid VAR_NAME def check_VariableExpression(o) # The expression must be a qualified name - if !o.expr.is_a? Model::QualifiedName + if !o.expr.is_a?(Model::QualifiedName) acceptor.accept(Issues::ILLEGAL_EXPRESSION, o, :feature => 'name', :container => o) else # name must be either a decimal value, or a valid NAME @@ -344,7 +344,9 @@ class Puppet::Pops::Validation::Checker4_0 acceptor.accept(Issues::ILLEGAL_NUMERIC_VAR_NAME, o, :name => name) end else - check(o.expr) + unless name =~ Puppet::Pops::Patterns::VAR_NAME + acceptor.accept(Issues::ILLEGAL_VAR_NAME, o, :name => name) + end end end end diff --git a/spec/unit/pops/validator/validator_spec.rb b/spec/unit/pops/validator/validator_spec.rb index 654e87f06..1d865de5f 100644 --- a/spec/unit/pops/validator/validator_spec.rb +++ b/spec/unit/pops/validator/validator_spec.rb @@ -35,3 +35,34 @@ describe "validating 3x" do end end + +describe "validating 4x" do + include ParserRspecHelper + include PuppetSpec::Pops + + let(:acceptor) { Puppet::Pops::Validation::Acceptor.new() } + let(:validator) { Puppet::Pops::Validation::ValidatorFactory_4_0.new().validator(acceptor) } + + def validate(model) + validator.validate(model) + acceptor + end + + it 'should raise error for illegal names' do + pending "validation was too strict, now too relaxed - validation missing" + expect(validate(fqn('Aaa'))).to have_issue(Puppet::Pops::Issues::ILLEGAL_NAME) + expect(validate(fqn('AAA'))).to have_issue(Puppet::Pops::Issues::ILLEGAL_NAME) + end + + it 'should raise error for illegal variable names' do + expect(validate(fqn('Aaa').var())).to have_issue(Puppet::Pops::Issues::ILLEGAL_VAR_NAME) + expect(validate(fqn('AAA').var())).to have_issue(Puppet::Pops::Issues::ILLEGAL_VAR_NAME) + expect(validate(fqn('aaa::_aaa').var())).to have_issue(Puppet::Pops::Issues::ILLEGAL_VAR_NAME) + end + + it 'should not raise error for variable name with underscore first in first name segment' do + expect(validate(fqn('_aa').var())).to_not have_issue(Puppet::Pops::Issues::ILLEGAL_VAR_NAME) + expect(validate(fqn('::_aa').var())).to_not have_issue(Puppet::Pops::Issues::ILLEGAL_VAR_NAME) + end + +end From ca3adc4df890f8ec142e785c2a2955ead33dc324 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 6 Jan 2014 00:14:44 +0100 Subject: [PATCH 345/800] (PUP-800) Add offset and length to Locatable base class in model This adds offset and locatable as attributes in the pops model and makes all objects in a Program model have this trait (except the top level Program which is always from 0 to the entire length of the source). Further, the Program may now contain the line index, and may contain the source text. --- lib/puppet/pops/model/model.rb | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/lib/puppet/pops/model/model.rb b/lib/puppet/pops/model/model.rb index 5f7509fd8..4cb2888b1 100644 --- a/lib/puppet/pops/model/model.rb +++ b/lib/puppet/pops/model/model.rb @@ -33,8 +33,18 @@ module Puppet::Pops::Model abstract end + # A locateable object has an offset measured in characters from the start of a source text (starting + # from 0), and a length measured in number of characters. + # The offset and length are optional if the source of the model is not from parsed text. + # + class Locatable < PopsObject + abstract + has_attr 'offset', Integer + has_attr 'length', Integer + end + # @abstract base class for expressions - class Expression < PopsObject + class Expression < Locatable abstract end @@ -145,7 +155,7 @@ module Puppet::Pops::Model # A Keyed entry has a key and a value expression. It it typically used as an entry in a Hash. # - class KeyedEntry < PopsObject + class KeyedEntry < Locatable contains_one_uni 'key', Expression, :lowerBound => 1 contains_one_uni 'value', Expression, :lowerBound => 1 end @@ -199,7 +209,7 @@ module Puppet::Pops::Model # An attribute operation sets or appends a value to a named attribute. # - class AttributeOperation < PopsObject + class AttributeOperation < Locatable has_attr 'attribute_name', String, :lowerBound => 1 has_attr 'operator', RGen::MetamodelBuilder::DataTypes::Enum.new([:'=>', :'+>', ]), :lowerBound => 1 contains_one_uni 'value_expr', Expression, :lowerBound => 1 @@ -214,7 +224,7 @@ module Puppet::Pops::Model contains_many_uni 'operations', AttributeOperation end - class Parameter < PopsObject + class Parameter < Locatable has_attr 'name', String, :lowerBound => 1 contains_one_uni 'value', Expression end @@ -410,7 +420,7 @@ module Puppet::Pops::Model # A resource body describes one resource instance # - class ResourceBody < PopsObject + class ResourceBody < Locatable contains_one_uni 'title', Expression contains_many_uni 'operations', AttributeOperation end @@ -463,7 +473,7 @@ module Puppet::Pops::Model # A selector entry describes a map from matching_expr to value_expr. # - class SelectorEntry < PopsObject + class SelectorEntry < Locatable contains_one_uni 'matching_expr', Expression, :lowerBound => 1 contains_one_uni 'value_expr', Expression, :lowerBound => 1 end @@ -482,5 +492,7 @@ module Puppet::Pops::Model class Program < PopsObject contains_one_uni 'body', Expression has_many 'definitions', Definition + has_attr 'source_text', String + has_many_attr 'line_offsets', Integer end end From fecccb3575dfeeaac97527d29e9ff22a8a7a21f8 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 6 Jan 2014 01:11:59 +0100 Subject: [PATCH 346/800] (maint) Remove stale commented code --- lib/puppet/pops/model/factory.rb | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/lib/puppet/pops/model/factory.rb b/lib/puppet/pops/model/factory.rb index 716c031f2..08ba69991 100644 --- a/lib/puppet/pops/model/factory.rb +++ b/lib/puppet/pops/model/factory.rb @@ -297,10 +297,12 @@ class Puppet::Pops::Model::Factory o end - def build_Program(o, body, definitions) + def build_Program(o, body, definitions, source_text, line_index) o.body = to_ops(body) # non containment definitions.each { |d| o.addDefinitions(d) } + o.source_text = source_text + o.line_offsets = line_index o end @@ -450,14 +452,6 @@ class Puppet::Pops::Model::Factory else a.locatable = Puppet::Pops::Parser::Locatable::Lazy.new(start_locatable) end - -# a.line = start_pos.line -# a.offset = start_pos.offset -# a.pos = start_pos.pos -# a.length = start_pos.length -# if(end_pos.offset && end_pos.length) -# a.length = end_pos.offset + end_pos.length - start_pos.offset -# end end self end @@ -653,8 +647,8 @@ class Puppet::Pops::Model::Factory new(Model::ResourceBody, resource_title, attribute_operations) end - def self.PROGRAM(body, definitions) - new(Model::Program, body, definitions) + def self.PROGRAM(body, definitions, source_text, line_index) + new(Model::Program, body, definitions, source_text, line_index) end # Builds a BlockExpression if args size > 1, else the single expression/value in args From f49b9ce0cdd3c94202dd3676713313d0241a9335 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 6 Jan 2014 15:37:33 +0100 Subject: [PATCH 347/800] (maint) Fix up type parser's handling of String String parameterization not fully supported (in string form), and previously non parameterized types were listed twice. --- lib/puppet/pops/types/type_parser.rb | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/lib/puppet/pops/types/type_parser.rb b/lib/puppet/pops/types/type_parser.rb index 6edb58365..b4a826c10 100644 --- a/lib/puppet/pops/types/type_parser.rb +++ b/lib/puppet/pops/types/type_parser.rb @@ -60,6 +60,11 @@ class Puppet::Pops::Types::TypeParser raise_invalid_type_specification_error end + # @api private + def interpret_Program(o) + interpret(o.body) + end + # @api private def interpret_QualifiedName(o) o.value @@ -311,6 +316,26 @@ class Puppet::Pops::Types::TypeParser TYPES.float_range(parameters[0] == :default ? nil : parameters[0], parameters[1] == :default ? nil : parameters[1]) end + when "string" + size_type = case parameters.size + when 1 + if parameters[0].is_a?(Puppet::Pops::Types::PIntegerType) + parameters[0].copy + else + assert_range_parameter(parameters[0]) + TYPES.range(parameters[0], :default) + end + when 2 + assert_range_parameter(parameters[0]) + assert_range_parameter(parameters[1]) + TYPES.range(parameters[0], parameters[1]) + else + raise_invalid_parameters_error("String", "1 to 2", parameters.size) + end + result = TYPES.string + result.size_type = size_type + result + when "optional" if parameters.size != 1 raise_invalid_parameters_error("Optional", 1, parameters.size) @@ -318,7 +343,7 @@ class Puppet::Pops::Types::TypeParser assert_type(parameters[0]) TYPES.optional(parameters[0]) - when "object", "collection", "data", "catalogentry", "boolean", "literal", "undef", "numeric", "pattern", "string" + when "object", "data", "catalogentry", "boolean", "literal", "undef", "numeric" raise_unparameterized_type_error(parameterized_ast.left_expr) when "type" From fa2f6cfe1dab4d991cb42c8f87336a0462671aa6 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Fri, 3 Jan 2014 14:51:11 -0800 Subject: [PATCH 348/800] (PUP-672) TrustedInformation handles a nil certificate. Acceptance caught an issue with an external ca not passing the certificate info in the request (the acceptance/tests/external_ca_support/apache_external_root_ca.rb does this because its SSLOptions does not have +ExportCertData in acceptance/tests/external_ca_support/fixtures/httpd.conf). So we are modifying the TrustedInformation class to handle a nil certificate without failure, but issue an info level warning in the logs. This also modifies the acceptance test to ensure a teardown, as failure to reach the clean up code is breaking following tests when the httpd server gets left up as master. --- .../apache_external_root_ca.rb | 38 ++++++++++--------- lib/puppet/context/trusted_information.rb | 11 ++++-- spec/unit/context/trusted_information_spec.rb | 10 +++++ 3 files changed, 38 insertions(+), 21 deletions(-) diff --git a/acceptance/tests/external_ca_support/apache_external_root_ca.rb b/acceptance/tests/external_ca_support/apache_external_root_ca.rb index 7d8879f19..4019b56d5 100644 --- a/acceptance/tests/external_ca_support/apache_external_root_ca.rb +++ b/acceptance/tests/external_ca_support/apache_external_root_ca.rb @@ -35,6 +35,26 @@ fixture_dir = File.expand_path('../fixtures', __FILE__) testdir = master.tmpdir('apache_external_root_ca') fixtures = PuppetX::Acceptance::ExternalCertFixtures.new(fixture_dir, testdir) +# We need this variable in scope. +disable_and_reenable_selinux = nil + +# Register our cleanup steps early in a teardown so that they will happen even +# if execution aborts part way. +teardown do + step "Cleanup Apache (httpd) and /etc/hosts" + # Restore /etc/hosts + on master, "cp -p '#{testdir}/hosts' /etc/hosts" + # stop the service before moving files around + on master, "/etc/init.d/httpd stop" + on master, "mv --force /etc/httpd/conf/httpd.conf{,.external_ca_test}" + on master, "mv --force /etc/httpd/conf/httpd.conf{.orig,}" + + if disable_and_reenable_selinux + step "Restore the original state of SELinux" + on master, "setenforce 1" + end +end + # Read all of the CA certificates. # Copy all of the x.509 fixture data over to the master. @@ -78,7 +98,6 @@ create_remote_file master, "#{testdir}/etc/master/config.ru", fixtures.config_ru step "Set filesystem permissions and ownership for the master" # These permissions are required for Passenger to start Puppet as puppet -on master, "chown puppet:puppet #{testdir}/etc/master/config.ru" on master, "chown -R puppet:puppet #{testdir}/etc/master" # These permissions are just for testing, end users should protect their @@ -86,7 +105,6 @@ on master, "chown -R puppet:puppet #{testdir}/etc/master" on master, "chmod -R a+rX #{testdir}" agent_cmd_prefix = "--confdir #{testdir}/etc/agent --vardir #{testdir}/etc/agent/var" -master_cmd_prefix = "--confdir #{testdir}/etc/master --vardir #{testdir}/etc/master/var" step "Configure EPEL" epel_release_path = "http://mirror.us.leaseweb.net/epel/6/i386/epel-release-6-8.noarch.rpm" @@ -104,8 +122,6 @@ on master, "cat #{testdir}/etc/httpd.conf > /etc/httpd/conf/httpd.conf" step "Make SELinux and Apache play nicely together..." -# We need this variable in scope. -disable_and_reenable_selinux = 'UNKNOWN' on master, "sestatus" do if stdout.match(/Current mode:.*enforcing/) disable_and_reenable_selinux = true @@ -161,7 +177,6 @@ on master, puppet_agent("#{agent_cmd_prefix} --test"), :acceptable_exit_codes => assert exit_code == 0 end - step "Agent refuses to connect to revoked master" on master, "cp #{testdir}/etc/agent/puppet.conf{,.no_crl}" on master, "cp #{testdir}/etc/agent/puppet.conf{.crl,}" @@ -172,17 +187,4 @@ on master, puppet_agent("#{agent_cmd_prefix} #{revoke_opts} --test"), :acceptabl assert exit_code == 1 end -step "Cleanup Apache (httpd) and /etc/hosts" -# Restore /etc/hosts -on master, "cp -p '#{testdir}/hosts' /etc/hosts" -# stop the service before moving files around -on master, "/etc/init.d/httpd stop" -on master, "mv --force /etc/httpd/conf/httpd.conf{,.external_ca_test}" -on master, "mv --force /etc/httpd/conf/httpd.conf{.orig,}" - -if disable_and_reenable_selinux - step "Restore the original state of SELinux" - on master, "setenforce 1" -end - step "Finished testing External Certificates" diff --git a/lib/puppet/context/trusted_information.rb b/lib/puppet/context/trusted_information.rb index 57024f4a9..35d3a7716 100644 --- a/lib/puppet/context/trusted_information.rb +++ b/lib/puppet/context/trusted_information.rb @@ -25,9 +25,14 @@ class Puppet::Context::TrustedInformation def self.remote(authenticated, node_name, certificate) if authenticated - extensions = Hash[certificate.custom_extensions.collect do |ext| - [ext['oid'].freeze, ext['value'].freeze] - end] + extensions = {} + if certificate.nil? + Puppet.info('TrustedInformation expected a certificate, but none was given.') + else + extensions = Hash[certificate.custom_extensions.collect do |ext| + [ext['oid'].freeze, ext['value'].freeze] + end] + end new('remote', node_name, extensions) else new(false, nil, {}) diff --git a/spec/unit/context/trusted_information_spec.rb b/spec/unit/context/trusted_information_spec.rb index 2b9ecda0c..c93d7f20e 100644 --- a/spec/unit/context/trusted_information_spec.rb +++ b/spec/unit/context/trusted_information_spec.rb @@ -41,6 +41,16 @@ describe Puppet::Context::TrustedInformation do '1.3.6.1.4.1.34380.1.2.2' => 'more CSR specific info', }) end + + it "is remote but lacks certificate information when it is authenticated" do + Puppet.expects(:info).once.with("TrustedInformation expected a certificate, but none was given.") + + trusted = Puppet::Context::TrustedInformation.remote(true, 'cert name', nil) + + expect(trusted.authenticated).to eq('remote') + expect(trusted.certname).to eq('cert name') + expect(trusted.extensions).to eq({}) + end end context "when local" do From 938b4e7d495f5f15557110040458171e832cecf2 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 3 Jan 2014 01:39:51 +0100 Subject: [PATCH 349/800] (PUP-716) Change Puppet::FileSystem::File to module methods This changes the API in a new implementation in a file called file_.rb The intent is that it should replace the implemntation in File, or perhaps directly in a Puppet::FileSystem class. --- lib/hiera_puppet.rb | 4 +- lib/puppet/application.rb | 2 +- lib/puppet/application/agent.rb | 2 +- lib/puppet/application/apply.rb | 4 +- lib/puppet/application/filebucket.rb | 2 +- lib/puppet/configurer.rb | 2 +- lib/puppet/confine/exists.rb | 2 +- lib/puppet/face/config.rb | 6 +- lib/puppet/face/file/store.rb | 2 +- lib/puppet/face/parser.rb | 2 +- lib/puppet/feature/libuser.rb | 2 +- lib/puppet/feature/rails.rb | 4 +- lib/puppet/file_bucket/dipper.rb | 16 +- lib/puppet/file_serving/base.rb | 2 +- lib/puppet/file_serving/configuration.rb | 2 +- .../file_serving/configuration/parser.rb | 2 +- lib/puppet/file_serving/content.rb | 2 +- lib/puppet/file_serving/fileset.rb | 4 +- lib/puppet/file_serving/metadata.rb | 4 +- lib/puppet/file_serving/mount/file.rb | 2 +- lib/puppet/file_system.rb | 292 +++++++++++++++++- lib/puppet/file_system/file.rb | 2 +- lib/puppet/file_system/file18.rb | 6 +- lib/puppet/file_system/file19.rb | 6 +- lib/puppet/file_system/file19windows.rb | 36 +-- lib/puppet/file_system/file_2.rb | 279 +++++++++++++++++ lib/puppet/file_system/file_impl.rb | 119 +++++++ lib/puppet/indirector/data_binding/hiera.rb | 2 +- lib/puppet/indirector/direct_file_server.rb | 4 +- .../indirector/file_bucket_file/file.rb | 42 ++- lib/puppet/indirector/json.rb | 2 +- lib/puppet/indirector/key/file.rb | 9 +- lib/puppet/indirector/ssl_file.rb | 10 +- lib/puppet/indirector/yaml.rb | 6 +- lib/puppet/module.rb | 8 +- .../module_tool/applications/installer.rb | 2 +- lib/puppet/module_tool/checksums.rb | 2 +- lib/puppet/network/authentication.rb | 2 +- lib/puppet/parser/files.rb | 2 +- lib/puppet/parser/functions/extlookup.rb | 4 +- lib/puppet/parser/functions/file.rb | 2 +- lib/puppet/parser/lexer.rb | 2 +- lib/puppet/parser/parser_support.rb | 2 +- lib/puppet/pops/binder/bindings_loader.rb | 2 +- .../pops/binder/config/binder_config.rb | 6 +- lib/puppet/pops/parser/lexer.rb | 2 +- lib/puppet/pops/parser/parser_support.rb | 2 +- lib/puppet/provider/augeas/augeas.rb | 2 +- lib/puppet/provider/exec/posix.rb | 2 +- lib/puppet/provider/exec/windows.rb | 2 +- lib/puppet/provider/file/windows.rb | 7 +- .../macauthorization/macauthorization.rb | 2 +- .../provider/nameservice/directoryservice.rb | 6 +- lib/puppet/provider/package/appdmg.rb | 2 +- lib/puppet/provider/package/apple.rb | 2 +- lib/puppet/provider/package/apt.rb | 2 +- lib/puppet/provider/package/blastwave.rb | 2 +- lib/puppet/provider/package/fink.rb | 2 +- lib/puppet/provider/package/openbsd.rb | 2 +- lib/puppet/provider/package/pacman.rb | 2 +- lib/puppet/provider/package/pkgdmg.rb | 2 +- lib/puppet/provider/package/pkgutil.rb | 2 +- .../provider/package/windows/package.rb | 2 +- lib/puppet/provider/service/bsd.rb | 6 +- lib/puppet/provider/service/daemontools.rb | 14 +- lib/puppet/provider/service/freebsd.rb | 4 +- lib/puppet/provider/service/init.rb | 8 +- lib/puppet/provider/service/runit.rb | 4 +- lib/puppet/provider/service/upstart.rb | 6 +- .../provider/ssh_authorized_key/parsed.rb | 4 +- lib/puppet/provider/user/directoryservice.rb | 2 +- lib/puppet/provider/zone/solaris.rb | 2 +- lib/puppet/rails/benchmark.rb | 2 +- lib/puppet/reports/rrdgraph.rb | 2 +- lib/puppet/reports/store.rb | 6 +- lib/puppet/reports/tagmail.rb | 2 +- lib/puppet/settings.rb | 4 +- lib/puppet/settings/directory_setting.rb | 3 +- lib/puppet/settings/file_setting.rb | 9 +- lib/puppet/ssl/certificate_authority.rb | 10 +- .../ssl/certificate_request_attributes.rb | 2 +- lib/puppet/ssl/inventory.rb | 2 +- lib/puppet/ssl/key.rb | 2 +- lib/puppet/ssl/validator/default_validator.rb | 2 +- lib/puppet/type/exec.rb | 2 +- lib/puppet/type/file.rb | 4 +- lib/puppet/type/file/ensure.rb | 6 +- lib/puppet/type/file/source.rb | 2 +- lib/puppet/type/file/target.rb | 10 +- lib/puppet/type/k5login.rb | 8 +- lib/puppet/type/tidy.rb | 2 +- lib/puppet/type/yumrepo.rb | 2 +- lib/puppet/util.rb | 25 +- lib/puppet/util/autoload.rb | 2 +- lib/puppet/util/backups.rb | 8 +- lib/puppet/util/checksums.rb | 4 +- lib/puppet/util/execution.rb | 2 +- lib/puppet/util/filetype.rb | 6 +- lib/puppet/util/lockfile.rb | 4 +- lib/puppet/util/log/destinations.rb | 2 +- lib/puppet/util/metric.rb | 2 +- lib/puppet/util/network_device/config.rb | 2 +- lib/puppet/util/plugins.rb | 2 +- .../util/rdoc/generators/puppet_generator.rb | 2 +- lib/puppet/util/reference.rb | 2 +- lib/puppet/util/resource_template.rb | 2 +- lib/puppet/util/selinux.rb | 2 +- lib/puppet/util/storage.rb | 4 +- lib/puppet/util/watched_file.rb | 2 +- lib/puppet/util/watcher.rb | 2 +- .../lib/puppet/provider/a2mod/debian.rb | 2 +- spec/integration/application/apply_spec.rb | 2 +- spec/integration/application/doc_spec.rb | 2 +- spec/integration/configurer_spec.rb | 2 +- .../indirector/direct_file_server_spec.rb | 2 +- .../file_content/file_server_spec.rb | 4 +- spec/integration/node/facts_spec.rb | 2 +- spec/integration/node_spec.rb | 2 +- spec/integration/resource/catalog_spec.rb | 2 +- spec/integration/ssl/autosign_spec.rb | 2 +- .../ssl/certificate_revocation_list_spec.rb | 2 +- spec/integration/ssl/host_spec.rb | 2 +- spec/integration/transaction_spec.rb | 26 +- spec/integration/type/exec_spec.rb | 4 +- spec/integration/type/file_spec.rb | 78 ++--- spec/integration/type/tidy_spec.rb | 4 +- spec/integration/util/rdoc/parser_spec.rb | 2 +- spec/integration/util/settings_spec.rb | 2 +- .../integration/util/windows/security_spec.rb | 4 +- .../shared_behaviours/file_server_terminus.rb | 4 +- spec/unit/application/agent_spec.rb | 4 +- spec/unit/application/filebucket_spec.rb | 2 +- spec/unit/configurer/downloader_spec.rb | 2 +- spec/unit/confine/exists_spec.rb | 10 +- spec/unit/file_bucket/dipper_spec.rb | 4 +- spec/unit/file_serving/base_spec.rb | 21 +- spec/unit/file_serving/configuration_spec.rb | 14 +- spec/unit/file_serving/content_spec.rb | 7 +- spec/unit/file_serving/mount/file_spec.rb | 18 +- 139 files changed, 1049 insertions(+), 352 deletions(-) create mode 100644 lib/puppet/file_system/file_2.rb create mode 100644 lib/puppet/file_system/file_impl.rb diff --git a/lib/hiera_puppet.rb b/lib/hiera_puppet.rb index d90b82089..fe4fecd90 100644 --- a/lib/hiera_puppet.rb +++ b/lib/hiera_puppet.rb @@ -71,12 +71,12 @@ module HieraPuppet if Puppet.settings[:hiera_config].is_a?(String) expanded_config_file = File.expand_path(Puppet.settings[:hiera_config]) - if Puppet::FileSystem::File.exist?(expanded_config_file) + if Puppet::FileSystem.exist?(expanded_config_file) config_file = expanded_config_file end elsif Puppet.settings[:confdir].is_a?(String) expanded_config_file = File.expand_path(File.join(Puppet.settings[:confdir], '/hiera.yaml')) - if Puppet::FileSystem::File.exist?(expanded_config_file) + if Puppet::FileSystem.exist?(expanded_config_file) config_file = expanded_config_file end end diff --git a/lib/puppet/application.rb b/lib/puppet/application.rb index acef32134..0cb3c9a3f 100644 --- a/lib/puppet/application.rb +++ b/lib/puppet/application.rb @@ -405,7 +405,7 @@ class Application def configure_indirector_routes route_file = Puppet[:route_file] - if Puppet::FileSystem::File.exist?(route_file) + if Puppet::FileSystem.exist?(route_file) routes = YAML.load_file(route_file) application_routes = routes[name.to_s] Puppet::Indirector.configure_routes(application_routes) if application_routes diff --git a/lib/puppet/application/agent.rb b/lib/puppet/application/agent.rb index d86f3f47f..b84003e92 100644 --- a/lib/puppet/application/agent.rb +++ b/lib/puppet/application/agent.rb @@ -437,7 +437,7 @@ Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License def setup_listen(daemon) Puppet.warning "Puppet --listen / kick is deprecated. See http://links.puppetlabs.com/puppet-kick-deprecation" - unless Puppet::FileSystem::File.exist?(Puppet[:rest_authconfig]) + unless Puppet::FileSystem::exist?(Puppet[:rest_authconfig]) Puppet.err "Will not start without authorization file #{Puppet[:rest_authconfig]}" exit(14) end diff --git a/lib/puppet/application/apply.rb b/lib/puppet/application/apply.rb index 2eb4415b3..e8154b6ca 100644 --- a/lib/puppet/application/apply.rb +++ b/lib/puppet/application/apply.rb @@ -163,7 +163,7 @@ Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License Puppet[:code] = options[:code] || STDIN.read else manifest = command_line.args.shift - raise "Could not find file #{manifest}" unless Puppet::FileSystem::File.exist?(manifest) + raise "Could not find file #{manifest}" unless Puppet::FileSystem.exist?(manifest) Puppet.warning("Only one file can be applied per run. Skipping #{command_line.args.join(', ')}") if command_line.args.size > 0 Puppet[:manifest] = manifest end @@ -189,7 +189,7 @@ Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License # Allow users to load the classes that puppet agent creates. if options[:loadclasses] file = Puppet[:classfile] - if Puppet::FileSystem::File.exist?(file) + if Puppet::FileSystem.exist?(file) unless FileTest.readable?(file) $stderr.puts "#{file} is not readable" exit(63) diff --git a/lib/puppet/application/filebucket.rb b/lib/puppet/application/filebucket.rb index 509885602..3a2e10b86 100644 --- a/lib/puppet/application/filebucket.rb +++ b/lib/puppet/application/filebucket.rb @@ -129,7 +129,7 @@ Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License raise "You must specify a file to back up" unless args.length > 0 args.each do |file| - unless Puppet::FileSystem::File.exist?(file) + unless Puppet::FileSystem.exist?(file) $stderr.puts "#{file}: no such file" next end diff --git a/lib/puppet/configurer.rb b/lib/puppet/configurer.rb index 0c000c9ea..8bf701633 100644 --- a/lib/puppet/configurer.rb +++ b/lib/puppet/configurer.rb @@ -44,7 +44,7 @@ class Puppet::Configurer rescue => detail Puppet.log_exception(detail, "Removing corrupt state file #{Puppet[:statefile]}: #{detail}") begin - Puppet::FileSystem::File.unlink(Puppet[:statefile]) + Puppet::FileSystem.unlink(Puppet[:statefile]) retry rescue => detail raise Puppet::Error.new("Cannot remove #{Puppet[:statefile]}: #{detail}") diff --git a/lib/puppet/confine/exists.rb b/lib/puppet/confine/exists.rb index 95a315514..6e29022fb 100644 --- a/lib/puppet/confine/exists.rb +++ b/lib/puppet/confine/exists.rb @@ -6,7 +6,7 @@ class Puppet::Confine::Exists < Puppet::Confine end def pass?(value) - value && (for_binary? ? which(value) : Puppet::FileSystem::File.exist?(value)) + value && (for_binary? ? which(value) : Puppet::FileSystem.exist?(value)) end def message(value) diff --git a/lib/puppet/face/config.rb b/lib/puppet/face/config.rb index 7d18e8a52..28af1d9e7 100644 --- a/lib/puppet/face/config.rb +++ b/lib/puppet/face/config.rb @@ -76,9 +76,9 @@ Puppet::Face.define(:config, '0.0.1') do EOT when_invoked do |name, value, options| - file = Puppet::FileSystem::File.new(Puppet.settings.which_configuration_file) - file.touch - file.open(nil, 'r+') do |file| + path = Puppet::FileSystem.pathname(Puppet.settings.which_configuration_file) + Puppet::FileSystem.touch(path) + Puppet::FileSystem..open(path, nil, 'r+') do |file| Puppet::Settings::IniFile.update(file) do |config| config.set(options[:section], name, value) end diff --git a/lib/puppet/face/file/store.rb b/lib/puppet/face/file/store.rb index 76ebbc15b..dc43fee04 100644 --- a/lib/puppet/face/file/store.rb +++ b/lib/puppet/face/file/store.rb @@ -11,7 +11,7 @@ Puppet::Face.define(:file, '0.0.1') do EOT when_invoked do |path, options| - file = Puppet::FileBucket::File.new(Puppet::FileSystem::File.new(path).binread) + file = Puppet::FileBucket::File.new(Puppet::FileSystem.binread(path)) Puppet::FileBucket::File.indirection.terminus_class = :file Puppet::FileBucket::File.indirection.save file diff --git a/lib/puppet/face/parser.rb b/lib/puppet/face/parser.rb index e42919091..4ba044f68 100644 --- a/lib/puppet/face/parser.rb +++ b/lib/puppet/face/parser.rb @@ -43,7 +43,7 @@ Puppet::Face.define(:parser, '0.0.1') do end missing_files = [] files.each do |file| - missing_files << file if ! Puppet::FileSystem::File.exist?(file) + missing_files << file if ! Puppet::FileSystem.exist?(file) Puppet[:manifest] = file validate_manifest end diff --git a/lib/puppet/feature/libuser.rb b/lib/puppet/feature/libuser.rb index 29a745de4..5d8f8685d 100644 --- a/lib/puppet/feature/libuser.rb +++ b/lib/puppet/feature/libuser.rb @@ -4,5 +4,5 @@ require 'puppet/util/libuser' Puppet.features.add(:libuser) { File.executable?("/usr/sbin/lgroupadd") and File.executable?("/usr/sbin/luseradd") and - Puppet::FileSystem::File.exist?(Puppet::Util::Libuser.getconf) + Puppet::FileSystem.exist?(Puppet::Util::Libuser.getconf) } diff --git a/lib/puppet/feature/rails.rb b/lib/puppet/feature/rails.rb index c1537effb..b76b7c187 100644 --- a/lib/puppet/feature/rails.rb +++ b/lib/puppet/feature/rails.rb @@ -24,11 +24,11 @@ Puppet.features.add(:rails) do require 'active_record' require 'active_record/version' rescue LoadError - if Puppet::FileSystem::File.exist?("/usr/share/rails") + if Puppet::FileSystem.exist?("/usr/share/rails") count = 0 Dir.entries("/usr/share/rails").each do |dir| libdir = File.join("/usr/share/rails", dir, "lib") - if Puppet::FileSystem::File.exist?(libdir) and ! $LOAD_PATH.include?(libdir) + if Puppet::FileSystem.exist?(libdir) and ! $LOAD_PATH.include?(libdir) count += 1 $LOAD_PATH << libdir end diff --git a/lib/puppet/file_bucket/dipper.rb b/lib/puppet/file_bucket/dipper.rb index 5d4d9382a..f58ea7761 100644 --- a/lib/puppet/file_bucket/dipper.rb +++ b/lib/puppet/file_bucket/dipper.rb @@ -31,9 +31,9 @@ class Puppet::FileBucket::Dipper # Back up a file to our bucket def backup(file) - file_handle = Puppet::FileSystem::File.new(file) - raise(ArgumentError, "File #{file} does not exist") unless file_handle.exist? - contents = file_handle.binread + file_handle = Puppet::FileSystem.pathname(file) + raise(ArgumentError, "File #{file} does not exist") unless Puppet::FileSystem.exist?(file_handle) + contents = Puppet::FileSystem.binread(file_handle) begin file_bucket_file = Puppet::FileBucket::File.new(contents, :bucket_path => @local_path) files_original_path = absolutize_path(file) @@ -66,9 +66,9 @@ class Puppet::FileBucket::Dipper # Restore the file def restore(file,sum) restore = true - file_handle = Puppet::FileSystem::File.new(file) - if file_handle.exist? - cursum = Digest::MD5.hexdigest(file_handle.binread) + file_handle = Puppet::FileSystem.pathname(file) + if Puppet::FileSystem.exist?(file_handle) + cursum = Digest::MD5.hexdigest(Puppet::FileSystem.binread(file_handle)) # if the checksum has changed... # this might be extra effort @@ -81,8 +81,8 @@ class Puppet::FileBucket::Dipper if newcontents = getfile(sum) newsum = Digest::MD5.hexdigest(newcontents) changed = nil - if file_handle.exist? and ! file_handle.writable? - changed = Puppet::FileSystem::File.new(file).stat.mode + if Puppet::FileSystem.exist?(file_handle) and ! Puppet::FileSystem.writable?(file_handle) + changed = Puppet::FileSystem.stat(file_handle).mode ::File.chmod(changed | 0200, file) end ::File.open(file, ::File::WRONLY|::File::TRUNC|::File::CREAT) { |of| diff --git a/lib/puppet/file_serving/base.rb b/lib/puppet/file_serving/base.rb index f04a7a453..c2e2c1eff 100644 --- a/lib/puppet/file_serving/base.rb +++ b/lib/puppet/file_serving/base.rb @@ -68,7 +68,7 @@ class Puppet::FileServing::Base # Stat our file, using the appropriate link-sensitive method. def stat @stat_method ||= self.links == :manage ? :lstat : :stat - Puppet::FileSystem::File.new(full_path).send(@stat_method) + Puppet::FileSystem.send(@stat_method, full_path) end def to_data_hash diff --git a/lib/puppet/file_serving/configuration.rb b/lib/puppet/file_serving/configuration.rb index c386a7e0a..4ac897376 100644 --- a/lib/puppet/file_serving/configuration.rb +++ b/lib/puppet/file_serving/configuration.rb @@ -88,7 +88,7 @@ class Puppet::FileServing::Configuration def readconfig(check = true) config = Puppet[:fileserverconfig] - return unless Puppet::FileSystem::File.exist?(config) + return unless Puppet::FileSystem.exist?(config) @parser ||= Puppet::FileServing::Configuration::Parser.new(config) diff --git a/lib/puppet/file_serving/configuration/parser.rb b/lib/puppet/file_serving/configuration/parser.rb index 4195b8828..ccb6b3568 100644 --- a/lib/puppet/file_serving/configuration/parser.rb +++ b/lib/puppet/file_serving/configuration/parser.rb @@ -7,7 +7,7 @@ class Puppet::FileServing::Configuration::Parser # Parse our configuration file. def parse - raise("File server configuration #{@file} does not exist") unless Puppet::FileSystem::File.exist?(@file) + raise("File server configuration #{@file} does not exist") unless Puppet::FileSystem.exist?(@file) raise("Cannot read file server configuration #{@file}") unless FileTest.readable?(@file) @mounts = {} diff --git a/lib/puppet/file_serving/content.rb b/lib/puppet/file_serving/content.rb index fc5a70ea5..189e899ca 100644 --- a/lib/puppet/file_serving/content.rb +++ b/lib/puppet/file_serving/content.rb @@ -34,7 +34,7 @@ class Puppet::FileServing::Content < Puppet::FileServing::Base # This stat can raise an exception, too. raise(ArgumentError, "Cannot read the contents of links unless following links") if stat.ftype == "symlink" - @content = Puppet::FileSystem::File.new(full_path).binread + @content = Puppet::FileSystem.binread(full_path) end @content end diff --git a/lib/puppet/file_serving/fileset.rb b/lib/puppet/file_serving/fileset.rb index 20957831f..89ed86985 100644 --- a/lib/puppet/file_serving/fileset.rb +++ b/lib/puppet/file_serving/fileset.rb @@ -133,7 +133,7 @@ class Puppet::FileServing::Fileset end def directory? - Puppet::FileSystem::File.new(path).send(stat_method).directory? + Puppet::FileSystem.send(stat_method, path).directory? rescue Errno::ENOENT, Errno::EACCES false end @@ -159,7 +159,7 @@ class Puppet::FileServing::Fileset end def valid?(path) - Puppet::FileSystem::File.new(path).send(@stat_method) + Puppet::FileSystem.send(@stat_method, path) true rescue Errno::ENOENT, Errno::EACCES false diff --git a/lib/puppet/file_serving/metadata.rb b/lib/puppet/file_serving/metadata.rb index 2073c53ca..a47f0fcc9 100644 --- a/lib/puppet/file_serving/metadata.rb +++ b/lib/puppet/file_serving/metadata.rb @@ -65,7 +65,7 @@ class Puppet::FileServing::Metadata < Puppet::FileServing::Base end # Retrieve the attributes for this file, relative to a base directory. - # Note that Puppet::FileSystem::File.new(path).stat raises Errno::ENOENT + # Note that Puppet::FileSystemstat(path) raises Errno::ENOENT # if the file is absent and this method does not catch that exception. def collect real_path = full_path @@ -85,7 +85,7 @@ class Puppet::FileServing::Metadata < Puppet::FileServing::Base @checksum_type = "ctime" @checksum = ("{#{@checksum_type}}") + send("#{@checksum_type}_file", path).to_s when "link" - @destination = Puppet::FileSystem::File.new(real_path).readlink + @destination = Puppet::FileSystem.readlink(real_path) @checksum = ("{#{@checksum_type}}") + send("#{@checksum_type}_file", real_path).to_s rescue nil else raise ArgumentError, "Cannot manage files of type #{stat.ftype}" diff --git a/lib/puppet/file_serving/mount/file.rb b/lib/puppet/file_serving/mount/file.rb index 535a90bde..e72a7ba63 100644 --- a/lib/puppet/file_serving/mount/file.rb +++ b/lib/puppet/file_serving/mount/file.rb @@ -22,7 +22,7 @@ class Puppet::FileServing::Mount::File < Puppet::FileServing::Mount file = ::File.join(full_path, relative_path) - if !(Puppet::FileSystem::File.exist?(file) or Puppet::FileSystem::File.new(file).symlink?) + if !(Puppet::FileSystem::File.exist?(file) or Puppet::FileSystem.symlink?(file)) Puppet.info("File does not exist or is not accessible: #{file}") return nil end diff --git a/lib/puppet/file_system.rb b/lib/puppet/file_system.rb index 78e0f71db..9f253a242 100644 --- a/lib/puppet/file_system.rb +++ b/lib/puppet/file_system.rb @@ -1,6 +1,296 @@ module Puppet::FileSystem require 'puppet/file_system/path_pattern' - require 'puppet/file_system/file' + require 'puppet/file_system/file_impl' require 'puppet/file_system/memory_file' require 'puppet/file_system/tempfile' + + + # create instance of the file system implementation to use for the current platform + @impl = if RUBY_VERSION =~ /^1\.8/ + require 'puppet/file_system/file18' + Puppet::FileSystem::File18 + elsif Puppet::Util::Platform.windows? + require 'puppet/file_system/file19windows' + Puppet::FileSystem::File19Windows + else + require 'puppet/file_system/file19' + Puppet::FileSystem::File19 + end.new() + + # Overrides the automatic file system implementation selection that is based on the current platform + # Should only be used for testing. + # @return [Object] the previous file system implementation (to allow it to be restored) + # + # @api private + # + def self.set_file_system_implementation(impl) + tmp = @impl + @impl = impl + tmp + end + + # Opens the given path with given mode, and options and optionally yields it to the given block. + # + # @api public + # + def self.open(path, mode, options, &block) + @impl.open(assert_path(path), options, mode, &block) + end + + # @return [Pathname] The directory of this file + # + # @api public + # + def self.dir(path) + @impl.dir(assert_path(path)) + end + + # @return [Boolean] Does the directory of the given path exist? + def self.dir_exist?(path) + @impl.exist?(@impl.dir(assert_path(path))) + end + + # Creates all directories down to (inclusive) the dir of the given path + def self.dir_mkpath(path) + @impl.mkpath(@impl.dir(assert_path(path))) + end + + # @return [String] the name of the file + # + # @api public + # + def self.basename(path) + @impl.basename(assert_path(path.basename)) + end + + # @return [Integer] the size of the file + # + # @api public + # + def self.basename(path) + @impl.size(assert_path(path.basename)) + end + + # Allows exclusive updates to a file to be made by excluding concurrent + # access using flock. This means that if the file is on a filesystem that + # does not support flock, this method will provide no protection. + # + # While polling to aquire the lock the process will wait ever increasing + # amounts of time in order to prevent multiple processes from wasting + # resources. + # + # @param path [Pathname] the path to the file to operate on + # @param mode [Integer] The mode to apply to the file if it is created + # @param options [Integer] Extra file operation mode information to use + # (defaults to read-only mode) + # @param timeout [Integer] Number of seconds to wait for the lock (defaults to 300) + # @yield The file handle, in read-write mode + # @return [Void] + # @raise [Timeout::Error] If the timeout is exceeded while waiting to acquire the lock + # + # @api public + # + def self.exclusive_open(path, mode, options = 'r', timeout = 300, &block) + @impl.exclusive_open(assert_path(path), mode, options, timeout, &block) + end + + # Processes each line of the file by yielding it to the given block + # + # @api public + # + def self.each_line(path, &block) + @impl.each_line(assert_path(path), &block) + end + + # @return [String] The contents of the file + # + # @api public + # + def self.read(path) + path.read + end + + # @return [String] The binary contents of the file + # + # @api public + # + def binread(path) + @impl.binread(assert_path(path)) + end + + # Determines if a file exists by verifying that the file can be stat'd. + # Will follow symlinks and verify that the actual target path exists. + # + # @return [Boolean] true if the named file exists. + # + # @api public + # + def self.exist?(path) + @impl.exist?(assert_path(path)) + end + + # Determines if a file is executable. + # + # @todo Should this take into account extensions on the windows platform? + # + # @return [Boolean] true if this file can be executed + # + # @api public + # + def self.executable?(path) + @impl.executable?(assert_path(path)) + end + + # @return [Boolean] Whether the file is writable by the current process + # + # @api public + # + def self.writable?(path) + @impl.writeable?(assert_path(path)) + end + + # Touches the file. On most systems this updates the mtime of the file. + # + # @api public + # + def self.touch(path) + @impl.touch(assert_path(path)) + end + + # Creates directories for all parts of the given path. + # + # @api public + # + def self.mkpath(path) + @impl.mkpath(assert_path(path)) + end + + # Creates a symbolic link dest which points to the current file. + # If dest already exists: + # + # * and is a file, will raise Errno::EEXIST + # * and is a directory, will return 0 but perform no action + # * and is a symlink referencing a file, will raise Errno::EEXIST + # * and is a symlink referencing a directory, will return 0 but perform no action + # + # With the :force option set to true, when dest already exists: + # + # * and is a file, will replace the existing file with a symlink (DANGEROUS) + # * and is a directory, will return 0 but perform no action + # * and is a symlink referencing a file, will modify the existing symlink + # * and is a symlink referencing a directory, will return 0 but perform no action + # + # @param dest [String] The path to create the new symlink at + # @param [Hash] options the options to create the symlink with + # @option options [Boolean] :force overwrite dest + # @option options [Boolean] :noop do not perform the operation + # @option options [Boolean] :verbose verbose output + # + # @raise [Errno::EEXIST] dest already exists as a file and, :force is not set + # + # @return [Integer] 0 + # + # @api public + # + def self.symlink(path, dest, options = {}) + @impl.symlink(assert_path(path), dest, options) + end + + # @return [Boolean] true if the file is a symbolic link. + # + # @api public + # + def self.symlink?(path) + @impl.symlink?(assert_path(path)) + end + + # @return [String] the name of the file referenced by the given link. + # + # @api public + # + def self.readlink(path) + @impl.readlink(assert_path(path)) + end + + # Deletes the given paths, returning the number of names passed as arguments. + # See also Dir::rmdir. + # + # @raise an exception on any error. + # + # @return [Integer] the number of paths passed as arguments + # + # @api public + # + def self.unlink(*paths) + paths.each {|p| assert_path(p) } + @impl.unlink(*paths) + end + + # @return [File::Stat] object for the named file. + # + # @api public + # + def stat(path) + @impl.stat(assert_path(path)) + end + + # @return [Integer] the size of the file + # + # @api public + # + def size(path) + @impl.size(assert_path(path)) + end + + # @return [File::Stat] Same as stat, but does not follow the last symbolic + # link. Instead, reports on the link itself. + # + # @api public + # + def lstat(path) + @impl.lstat(assert_path(path)) + end + + # Compares the contents of this file against the contents of a stream. + # + # @param stream [IO] The stream to compare the contents against + # @return [Boolean] Whether the contents were the same + # + # @api public + # + def compare_stream(path, stream) + @impl.compare_stream(assert_path(path), stream) + end + + # Produces an opaque pathname "handle" object representing the given path. + # Different implementations of the underlying file system may use different runtime + # objects. The produced "handle" should be used in all other operations + # that take a "path". No operation should be directly invoked on the returned opaque object + # + # @return [Object] An opaque path handle on which no operations should be directly performed + # + # @api public + # + def self.pathname(path) + @impl.pathname(path) + end + + # Asserts that the given path is of the expected type produced by #pathname + # + # @raise [ArgumentError] when path is not of the expected type + # + # @api public + # + def self.assert_path(path) + @impl.assert_path(path) + end + + # Produces a string representation of the opaque path handle. + # + # @return [String] a string representation of the path + # + def self.path_string(path) + @impl.path_string(path) + end + end diff --git a/lib/puppet/file_system/file.rb b/lib/puppet/file_system/file.rb index 57dd5341d..f9a3ad766 100644 --- a/lib/puppet/file_system/file.rb +++ b/lib/puppet/file_system/file.rb @@ -4,7 +4,7 @@ # while. # # @api private -class Puppet::FileSystem::File +class Puppet::FileSystem::FileXXX attr_reader :path IMPL = if RUBY_VERSION =~ /^1\.8/ diff --git a/lib/puppet/file_system/file18.rb b/lib/puppet/file_system/file18.rb index 99c2d5e06..aa4889c79 100644 --- a/lib/puppet/file_system/file18.rb +++ b/lib/puppet/file_system/file18.rb @@ -1,5 +1,5 @@ -class Puppet::FileSystem::File18 < Puppet::FileSystem::File - def binread - ::File.open(@path, 'rb') { |f| f.read } +class Puppet::FileSystem::File18 < Puppet::FileSystem::FileImpl + def binread(path) + ::File.open(path, 'rb') { |f| f.read } end end diff --git a/lib/puppet/file_system/file19.rb b/lib/puppet/file_system/file19.rb index ca011613e..fce9a6a82 100644 --- a/lib/puppet/file_system/file19.rb +++ b/lib/puppet/file_system/file19.rb @@ -1,5 +1,5 @@ -class Puppet::FileSystem::File19 < Puppet::FileSystem::File - def binread - @path.binread +class Puppet::FileSystem::File19 < Puppet::FileSystem::FileImpl + def binread(path) + path.binread end end diff --git a/lib/puppet/file_system/file19windows.rb b/lib/puppet/file_system/file19windows.rb index 87f9915fe..d9264f100 100644 --- a/lib/puppet/file_system/file19windows.rb +++ b/lib/puppet/file_system/file19windows.rb @@ -3,7 +3,7 @@ require 'puppet/util/windows' class Puppet::FileSystem::File19Windows < Puppet::FileSystem::File19 - def self.exist?(path) + def exist?(path) if ! Puppet.features.manages_symlinks? return ::File.exist?(path) end @@ -21,11 +21,7 @@ class Puppet::FileSystem::File19Windows < Puppet::FileSystem::File19 end end - def exist? - self.class.exist?(@path) - end - - def symlink(dest, options = {}) + def symlink(path, dest, options = {}) raise_if_symlinks_unsupported dest_exists = self.class.exist?(dest) # returns false on dangling symlink @@ -41,23 +37,23 @@ class Puppet::FileSystem::File19Windows < Puppet::FileSystem::File19 if options[:noop] != true ::File.delete(dest) if dest_exists # can only be file - Puppet::Util::Windows::File.symlink(@path, dest) + Puppet::Util::Windows::File.symlink(path, dest) end 0 end - def symlink? + def symlink?(path) return false if ! Puppet.features.manages_symlinks? - Puppet::Util::Windows::File.symlink?(@path) + Puppet::Util::Windows::File.symlink?(path) end - def readlink + def readlink(path) raise_if_symlinks_unsupported - Puppet::Util::Windows::File.readlink(@path) + Puppet::Util::Windows::File.readlink(path) end - def self.unlink(*file_names) + def unlink(*file_names) if ! Puppet.features.manages_symlinks? return ::File.unlink(*file_names) end @@ -81,25 +77,22 @@ class Puppet::FileSystem::File19Windows < Puppet::FileSystem::File19 file_names.length end - def unlink - self.class.unlink(@path) - end - - def stat + def stat(path) if ! Puppet.features.manages_symlinks? return super end - Puppet::Util::Windows::File.stat(@path) + Puppet::Util::Windows::File.stat(path) end - def lstat + def lstat(path) if ! Puppet.features.manages_symlinks? - return Puppet::Util::Windows::File.stat(@path) + return Puppet::Util::Windows::File.stat(path) end - Puppet::Util::Windows::File.lstat(@path) + Puppet::Util::Windows::File.lstat(path) end private + def raise_if_symlinks_unsupported if ! Puppet.features.manages_symlinks? msg = "This version of Windows does not support symlinks. Windows Vista / 2008 or higher is required." @@ -110,4 +103,5 @@ class Puppet::FileSystem::File19Windows < Puppet::FileSystem::File19 Puppet.warning "The current user does not have the necessary permission to manage symlinks." end end + end diff --git a/lib/puppet/file_system/file_2.rb b/lib/puppet/file_system/file_2.rb new file mode 100644 index 000000000..e674cc82f --- /dev/null +++ b/lib/puppet/file_system/file_2.rb @@ -0,0 +1,279 @@ +# An abstraction over the ruby file system operations for a single file. +# +# For the time being this is being kept private so that we can evolve it for a +# while. +# +# @api private +class Puppet::FileSystem::File + + # create instance of the file system implementation to use for the current platform + @impl = if RUBY_VERSION =~ /^1\.8/ + require 'puppet/file_system/file18' + Puppet::FileSystem::File18 + elsif Puppet::Util::Platform.windows? + require 'puppet/file_system/file19windows' + Puppet::FileSystem::File19Windows + else + require 'puppet/file_system/file19' + Puppet::FileSystem::File19 + end.new() + + # Overrides the automatic file system implementation selection that is based on the current platform + # Should only be used for testing. + # @return [Object] the previous file system implementation (to allow it to be restored) + # + # @api private + # + def self.set_file_system_implementation(impl) + tmp = @impl + @impl = impl + tmp + end + + # Opens the given path with given mode, and options and optionally yields it to the given block. + # + # @api public + # + def self.open(path, mode, options, &block) + @impl.open(assert_path(path), options, mode, &block) + end + + # @return [Pathname] The directory of this file + # + # @api public + # + def dir(path) + @impl.dir(assert_path(path)) + end + + # @return [String] the name of the file + # + # @api public + # + def self.basename(path) + @impl.basename(assert_path(path.basename)) + end + + # @return [Integer] the size of the file + # + # @api public + # + def self.basename(path) + @impl.size(assert_path(path.basename)) + end + + # Allows exclusive updates to a file to be made by excluding concurrent + # access using flock. This means that if the file is on a filesystem that + # does not support flock, this method will provide no protection. + # + # While polling to aquire the lock the process will wait ever increasing + # amounts of time in order to prevent multiple processes from wasting + # resources. + # + # @param path [Pathname] the path to the file to operate on + # @param mode [Integer] The mode to apply to the file if it is created + # @param options [Integer] Extra file operation mode information to use + # (defaults to read-only mode) + # @param timeout [Integer] Number of seconds to wait for the lock (defaults to 300) + # @yield The file handle, in read-write mode + # @return [Void] + # @raise [Timeout::Error] If the timeout is exceeded while waiting to acquire the lock + # + # @api public + # + def self.exclusive_open(path, mode, options = 'r', timeout = 300, &block) + @impl.exclusive_open(assert_path(path), mode, options, timeout, &block) + end + + # Processes each line of the file by yielding it to the given block + # + # @api public + # + def self.each_line(path, &block) + @impl.each_line(assert_path(path), &block) + end + + # @return [String] The contents of the file + # + # @api public + # + def self.read(path) + path.read + end + + # @return [String] The binary contents of the file + # + # @api public + # + def binread(path) + @impl.binread(assert_path(path)) + end + + # Determines if a file exists by verifying that the file can be stat'd. + # Will follow symlinks and verify that the actual target path exists. + # + # @return [Boolean] true if the named file exists. + # + # @api public + # + def self.exist?(path) + @impl.exist?(assert_path(path)) + end + + # Determines if a file is executable. + # + # @todo Should this take into account extensions on the windows platform? + # + # @return [Boolean] true if this file can be executed + # + # @api public + # + def self.executable?(path) + @impl.executable?(assert_path(path)) + end + + # @return [Boolean] Whether the file is writable by the current process + # + # @api public + # + def self.writable?(path) + @impl.writeable?(assert_path(path)) + end + + # Touches the file. On most systems this updates the mtime of the file. + # + # @api public + # + def self.touch(path) + @impl.touch(assert_path(path)) + end + + # Creates directories for all parts of the given path. + # + # @api public + # + def self.mkpath(path) + @impl.mkpath(assert_path(path)) + end + + # Creates a symbolic link dest which points to the current file. + # If dest already exists: + # + # * and is a file, will raise Errno::EEXIST + # * and is a directory, will return 0 but perform no action + # * and is a symlink referencing a file, will raise Errno::EEXIST + # * and is a symlink referencing a directory, will return 0 but perform no action + # + # With the :force option set to true, when dest already exists: + # + # * and is a file, will replace the existing file with a symlink (DANGEROUS) + # * and is a directory, will return 0 but perform no action + # * and is a symlink referencing a file, will modify the existing symlink + # * and is a symlink referencing a directory, will return 0 but perform no action + # + # @param dest [String] The path to create the new symlink at + # @param [Hash] options the options to create the symlink with + # @option options [Boolean] :force overwrite dest + # @option options [Boolean] :noop do not perform the operation + # @option options [Boolean] :verbose verbose output + # + # @raise [Errno::EEXIST] dest already exists as a file and, :force is not set + # + # @return [Integer] 0 + # + # @api public + # + def self.symlink(path, dest, options = {}) + @impl.symlink(assert_path(path), dest, options) + end + + # @return [Boolean] true if the file is a symbolic link. + # + # @api public + # + def self.symlink?(path) + @impl.symlink?(assert_path(path)) + end + + # @return [String] the name of the file referenced by the given link. + # + # @api public + # + def self.readlink(path) + @impl.readlink(assert_path(path)) + end + + # Deletes the given paths, returning the number of names passed as arguments. + # See also Dir::rmdir. + # + # @raise an exception on any error. + # + # @return [Integer] the number of paths passed as arguments + # + # @api public + # + def self.unlink(*paths) + paths.each {|p| assert_path(p) } + @impl.unlink(*paths) + end + + # @return [File::Stat] object for the named file. + # + # @api public + # + def stat(path) + @impl.stat(assert_path(path)) + end + + # @return [File::Stat] Same as stat, but does not follow the last symbolic + # link. Instead, reports on the link itself. + # + # @api public + # + def lstat(path) + @impl.lstat(assert_path(path)) + end + + # Compares the contents of this file against the contents of a stream. + # + # @param stream [IO] The stream to compare the contents against + # @return [Boolean] Whether the contents were the same + # + # @api public + # + def compare_stream(path, stream) + @impl.compare_stream(assert_path(path), stream) + end + + # Produces an opaque pathname "handle" object representing the given path. + # Different implementations of the underlying file system may use different runtime + # objects. The produced "handle" should be used in all other operations + # that take a "path". No operation should be directly invoked on the returned opaque object + # + # @return [Object] An opaque path handle on which no operations should be directly performed + # + # @api public + # + def self.pathname(path) + @impl.pathname(path) + end + + # Asserts that the given path is of the expected type produced by #pathname + # + # @raise [ArgumentError] when path is not of the expected type + # + # @api public + # + def self.assert_path(path) + @impl.assert_path(path) + end + + # Produces a string representation of the opaque path handle. + # + # @return [String] a string representation of the path + # + def self.path_string(path) + @impl.path_string(path) + end + +end diff --git a/lib/puppet/file_system/file_impl.rb b/lib/puppet/file_system/file_impl.rb new file mode 100644 index 000000000..19bb04e60 --- /dev/null +++ b/lib/puppet/file_system/file_impl.rb @@ -0,0 +1,119 @@ +# Abstract implementation of the Puppet::FileSystem +# +class Puppet::FileSystem::FileImpl + + def pathname(path) + path.is_a?(Pathname) ? path : Pathname.new(path) + end + + def assert_path(path) + return path if path.is_a?(Pathname) + raise ArgumentError, "FileSystem implementation expected Pathname, got: '#{path.class}" unless path.is_a?(String) + Pathname.new(path) + end + + def path_string(path) + path.to_s + end + + def open(path, mode, options, &block) + ::File.open(path, options, mode, &block) + end + + def dir(path) + path.dirname + end + + def basename(path) + path.basename.to_s + end + + def size(path) + path.size + end + + def exclusive_open(path, mode, options = 'r', timeout = 300, &block) + wait = 0.001 + (Kernel.rand / 1000) + written = false + while !written + ::File.open(path, options, mode) do |rf| + if rf.flock(::File::LOCK_EX|::File::LOCK_NB) + yield rf + written = true + else + sleep wait + timeout -= wait + wait *= 2 + if timeout < 0 + raise Timeout::Error, "Timeout waiting for exclusive lock on #{@path}" + end + end + end + end + end + + def each_line(path, &block) + ::File.open(path) do |f| + f.each_line do |line| + yield line + end + end + end + + def read(path) + path.read + end + + def binread(path) + raise NotImplementedError + end + + def exist?(path) + File.exist?(path) + end + + def executable?(path) + ::File.executable?(path) + end + + def writable?(path) + path.writable? + end + + def touch(path) + ::FileUtils.touch(path) + end + + def mkpath(path) + path.mkpath + end + + def symlink(path, dest, options = {}) + FileUtils.symlink(path, dest, options) + end + + def symlink?(path) + File.symlink?(path) + end + + def readlink(path) + File.readlink(path) + end + + def unlink(*paths) + File.unlink(*paths) + end + + def stat(path) + File.stat(path) + end + + def lstat(path) + File.lstat(path) + end + + def compare_stream(path, stream) + open(path, 0, 'rb') { |this| FileUtils.compare_stream(this, stream) } + end + +end diff --git a/lib/puppet/indirector/data_binding/hiera.rb b/lib/puppet/indirector/data_binding/hiera.rb index 9ff640b2e..7fbc63782 100644 --- a/lib/puppet/indirector/data_binding/hiera.rb +++ b/lib/puppet/indirector/data_binding/hiera.rb @@ -29,7 +29,7 @@ class Puppet::DataBinding::Hiera < Puppet::Indirector::Code hiera_config = Puppet.settings[:hiera_config] config = {} - if Puppet::FileSystem::File.exist?(hiera_config) + if Puppet::FileSystem.exist?(hiera_config) config = Hiera::Config.load(hiera_config) else Puppet.warning "Config file #{hiera_config} not found, using Hiera defaults" diff --git a/lib/puppet/indirector/direct_file_server.rb b/lib/puppet/indirector/direct_file_server.rb index dba4d60bd..a043c425e 100644 --- a/lib/puppet/indirector/direct_file_server.rb +++ b/lib/puppet/indirector/direct_file_server.rb @@ -6,14 +6,14 @@ class Puppet::Indirector::DirectFileServer < Puppet::Indirector::Terminus include Puppet::FileServing::TerminusHelper def find(request) - return nil unless Puppet::FileSystem::File.exist?(request.key) + return nil unless Puppet::FileSystem.exist?(request.key) instance = model.new(request.key) instance.links = request.options[:links] if request.options[:links] instance end def search(request) - return nil unless Puppet::FileSystem::File.exist?(request.key) + return nil unless Puppet::FileSystem.exist?(request.key) path2instances(request, request.key) end end diff --git a/lib/puppet/indirector/file_bucket_file/file.rb b/lib/puppet/indirector/file_bucket_file/file.rb index 59be12451..858cc30df 100644 --- a/lib/puppet/indirector/file_bucket_file/file.rb +++ b/lib/puppet/indirector/file_bucket_file/file.rb @@ -14,14 +14,14 @@ module Puppet::FileBucketFile contents_file = path_for(request.options[:bucket_path], checksum, 'contents') paths_file = path_for(request.options[:bucket_path], checksum, 'paths') - if contents_file.exist? && matches(paths_file, files_original_path) + if Puppet::FileSystem.exist?(contents_file) && matches(paths_file, files_original_path) if request.options[:diff_with] other_contents_file = path_for(request.options[:bucket_path], request.options[:diff_with], 'contents') - raise "could not find diff_with #{request.options[:diff_with]}" unless other_contents_file.exist? - return `diff #{contents_file.path.to_s.inspect} #{other_contents_file.path.to_s.inspect}` + raise "could not find diff_with #{request.options[:diff_with]}" unless Puppet::FileSystem.exist?(other_contents_file) + return `diff #{Puppet::FileSystem.path_string(contents_file).inspect} #{Puppet::FileSystem.path_string(other_contents_file).inspect}` else Puppet.info "FileBucket read #{checksum}" - model.new(contents_file.binread) + model.new(Puppet::FileSystem.binread(contents_file)) end else nil @@ -33,7 +33,7 @@ module Puppet::FileBucketFile contents_file = path_for(request.options[:bucket_path], checksum, 'contents') paths_file = path_for(request.options[:bucket_path], checksum, 'paths') - contents_file.exist? && matches(paths_file, files_original_path) + Puppet::FileSystem.exist?(contents_file) && matches(paths_file, files_original_path) end def save(request) @@ -54,8 +54,11 @@ module Puppet::FileBucketFile private + # @param paths_file [Object] Opaque file path + # @param files_original_path [String] + # def matches(paths_file, files_original_path) - paths_file.open(0640, 'a+') do |f| + Puppet::FileSystem.open(paths_file, 0640, 'a+') do |f| path_match(f, files_original_path) end end @@ -69,19 +72,22 @@ module Puppet::FileBucketFile return false end + # @param contents_file [Object] Opaque file path + # @param paths_file [Object] Opaque file path + # def save_to_disk(bucket_file, files_original_path, contents_file, paths_file) Puppet::Util.withumask(0007) do - unless paths_file.dir.exist? - paths_file.dir.mkpath + unless Puppet::FileSystem.dir_exist?(paths_file) + Puppet::FileSystem.dir_mkpath(paths_file) end - paths_file.exclusive_open(0640, 'a+') do |f| - if contents_file.exist? + Puppet::FileSystem.exclusive_open(paths_file, 0640, 'a+') do |f| + if Puppet::FileSystem.exist?(contents_file) verify_identical_file!(contents_file, bucket_file) - contents_file.touch + Puppet::FileSystem.touch(contents_file) else - contents_file.open(0440, 'wb') do |of| - of.write(bucket_file.contents) + Puppet::FileSystem.open(contents_file, 0440, 'wb') do |of| + of.write(Puppet::FileSystem.contents(bucket_file)) end end @@ -103,18 +109,22 @@ module Puppet::FileBucketFile [checksum, path] end + # @return [Object] Opaque path as constructed by the Puppet::FileSystem + # def path_for(bucket_path, digest, subfile = nil) bucket_path ||= Puppet[:bucketdir] dir = ::File.join(digest[0..7].split("")) basedir = ::File.join(bucket_path, dir, digest) - Puppet::FileSystem::File.new(subfile ? ::File.join(basedir, subfile) : basedir) + Puppet::FileSystem.pathname(subfile ? ::File.join(basedir, subfile) : basedir) end + # @param contents_file [Object] Opaque file path + # @param bucket_file [IO] def verify_identical_file!(contents_file, bucket_file) - if bucket_file.contents.size == contents_file.size - if contents_file.compare_stream(bucket_file.stream) + if bucket_file.contents.size == Puppet::FileSystem.size(contents_file) + if Puppet::FileSystem.compare_stream(contents_file, bucket_file.stream) Puppet.info "FileBucket got a duplicate file #{bucket_file.checksum}" return end diff --git a/lib/puppet/indirector/json.rb b/lib/puppet/indirector/json.rb index 515ad33b6..8c8f26e40 100644 --- a/lib/puppet/indirector/json.rb +++ b/lib/puppet/indirector/json.rb @@ -21,7 +21,7 @@ class Puppet::Indirector::JSON < Puppet::Indirector::Terminus end def destroy(request) - Puppet::FileSystem::File.unlink(path(request.key)) + Puppet::FileSystem.unlink(path(request.key)) rescue => detail unless detail.is_a? Errno::ENOENT raise Puppet::Error, "Could not destroy #{self.name} #{request.key}: #{detail}" diff --git a/lib/puppet/indirector/key/file.rb b/lib/puppet/indirector/key/file.rb index 40f8a331d..a1886514d 100644 --- a/lib/puppet/indirector/key/file.rb +++ b/lib/puppet/indirector/key/file.rb @@ -24,10 +24,11 @@ class Puppet::SSL::Key::File < Puppet::Indirector::SslFile def destroy(request) super - return unless Puppet::FileSystem::File.exist?(public_key_path(request.key)) + key_path = Puppet::FileSystem.pathname(public_key_path(request.key)) + return unless Puppet::FileSystem.exist?(key_path) begin - Puppet::FileSystem::File.unlink(public_key_path(request.key)) + Puppet::FileSystem.unlink(key_path) rescue => detail raise Puppet::Error, "Could not remove #{request.key} public key: #{detail}" end @@ -38,7 +39,9 @@ class Puppet::SSL::Key::File < Puppet::Indirector::SslFile super begin - Puppet.settings.setting(:publickeydir).open_file(public_key_path(request.key), 'w') { |f| f.print request.instance.content.public_key.to_pem } + Puppet.settings.setting(:publickeydir).open_file(public_key_path(request.key), 'w') do |f| + f.print request.instance.content.public_key.to_pem + end rescue => detail raise Puppet::Error, "Could not write #{request.key}: #{detail}" end diff --git a/lib/puppet/indirector/ssl_file.rb b/lib/puppet/indirector/ssl_file.rb index a4ca4bd77..8b823eb9c 100644 --- a/lib/puppet/indirector/ssl_file.rb +++ b/lib/puppet/indirector/ssl_file.rb @@ -69,12 +69,12 @@ class Puppet::Indirector::SslFile < Puppet::Indirector::Terminus # Remove our file. def destroy(request) - path = path(request.key) - return false unless Puppet::FileSystem::File.exist?(path) + path = Puppet::FileSystem.pathname(path(request.key)) + return false unless Puppet::FileSystem.exist?(path) Puppet.notice "Removing file #{model} #{request.key} at '#{path}'" begin - Puppet::FileSystem::File.unlink(path) + Puppet::FileSystem.unlink(path) rescue => detail raise Puppet::Error, "Could not remove #{request.key}: #{detail}" end @@ -135,10 +135,10 @@ class Puppet::Indirector::SslFile < Puppet::Indirector::Terminus # which we'll be EOL'ing at some point. This method was added at 20080702 # and should be removed at some point. def rename_files_with_uppercase(file) - return file if Puppet::FileSystem::File.exist?(file) + return file if Puppet::FileSystem.exist?(file) dir, short = File.split(file) - return nil unless Puppet::FileSystem::File.exist?(dir) + return nil unless Puppet::FileSystem.exist?(dir) raise ArgumentError, "Tried to fix SSL files to a file containing uppercase" unless short.downcase == short real_file = Dir.entries(dir).reject { |f| f =~ /^\./ }.find do |other| diff --git a/lib/puppet/indirector/yaml.rb b/lib/puppet/indirector/yaml.rb index 9c4e6f102..0f2816f01 100644 --- a/lib/puppet/indirector/yaml.rb +++ b/lib/puppet/indirector/yaml.rb @@ -6,7 +6,7 @@ class Puppet::Indirector::Yaml < Puppet::Indirector::Terminus # Read a given name's file in and convert it from YAML. def find(request) file = path(request.key) - return nil unless Puppet::FileSystem::File.exist?(file) + return nil unless Puppet::FileSystem.exist?(file) begin return Puppet::Util::Yaml.load_file(file) @@ -24,7 +24,7 @@ class Puppet::Indirector::Yaml < Puppet::Indirector::Terminus basedir = File.dirname(file) # This is quite likely a bad idea, since we're not managing ownership or modes. - Dir.mkdir(basedir) unless Puppet::FileSystem::File.exist?(basedir) + Dir.mkdir(basedir) unless Puppet::FileSystem.exist?(basedir) begin Puppet::Util::Yaml.dump(request.instance, file) @@ -46,7 +46,7 @@ class Puppet::Indirector::Yaml < Puppet::Indirector::Terminus def destroy(request) file_path = path(request.key) - Puppet::FileSystem::File.unlink(file_path) if Puppet::FileSystem::File.exist?(file_path) + Puppet::FileSystem.unlink(file_path) if Puppet::FileSystem.exist?(file_path) end def search(request) diff --git a/lib/puppet/module.rb b/lib/puppet/module.rb index 39274c044..c69de2226 100644 --- a/lib/puppet/module.rb +++ b/lib/puppet/module.rb @@ -54,7 +54,7 @@ class Puppet::Module def has_metadata? return false unless metadata_file - return false unless Puppet::FileSystem::File.exist?(metadata_file) + return false unless Puppet::FileSystem.exist?(metadata_file) begin metadata = PSON.parse(File.read(metadata_file)) @@ -71,7 +71,7 @@ class Puppet::Module # we have files of a given type. define_method(type +'?') do type_subpath = subpath(location) - unless Puppet::FileSystem::File.exist?(type_subpath) + unless Puppet::FileSystem.exist?(type_subpath) Puppet.debug("No #{type} found in subpath '#{type_subpath}' " + "(file / directory does not exist)") return false @@ -94,7 +94,7 @@ class Puppet::Module full_path = subpath(location) end - return nil unless Puppet::FileSystem::File.exist?(full_path) + return nil unless Puppet::FileSystem.exist?(full_path) return full_path end @@ -153,7 +153,7 @@ class Puppet::Module end def all_manifests - return [] unless Puppet::FileSystem::File.exist?(manifests) + return [] unless Puppet::FileSystem.exist?(manifests) Dir.glob(File.join(manifests, '**', '*.{rb,pp}')) end diff --git a/lib/puppet/module_tool/applications/installer.rb b/lib/puppet/module_tool/applications/installer.rb index 772d7a831..3f7db78b2 100644 --- a/lib/puppet/module_tool/applications/installer.rb +++ b/lib/puppet/module_tool/applications/installer.rb @@ -32,7 +32,7 @@ module Puppet::ModuleTool if is_module_package?(@name) @source = :filesystem @filename = File.expand_path(@name) - raise MissingPackageError, :requested_package => @filename unless Puppet::FileSystem::File.exist?(@filename) + raise MissingPackageError, :requested_package => @filename unless Puppet::FileSystem.exist?(@filename) parsed = parse_filename(@filename) @module_name = parsed[:module_name] diff --git a/lib/puppet/module_tool/checksums.rb b/lib/puppet/module_tool/checksums.rb index 044357a8f..4731a734e 100644 --- a/lib/puppet/module_tool/checksums.rb +++ b/lib/puppet/module_tool/checksums.rb @@ -16,7 +16,7 @@ module Puppet::ModuleTool # Return checksum for the +Pathname+. def checksum(pathname) - return Digest::MD5.hexdigest(Puppet::FileSystem::File.new(pathname).binread) + return Digest::MD5.hexdigest(Puppet::FileSystem.binread(pathname)) end # Return checksums for object's +Pathname+, generate if it's needed. diff --git a/lib/puppet/network/authentication.rb b/lib/puppet/network/authentication.rb index 2517fd97e..c7f9ace87 100644 --- a/lib/puppet/network/authentication.rb +++ b/lib/puppet/network/authentication.rb @@ -17,7 +17,7 @@ module Puppet::Network::Authentication # master or the agent. Don't load the certificate if the CA cert is not # present: infinite recursion will occur as another authenticated request # will be spawned to download the CA cert. - if Puppet::FileSystem::File.exist?(Puppet[:hostcert]) && Puppet::FileSystem::File.exist?(Puppet[:localcacert]) + if [Puppet[:hostcert], Puppet[:localcacert]].all? {|path| Puppet::FileSystem.exist?(path) } certs << Puppet::SSL::Host.localhost.certificate end diff --git a/lib/puppet/parser/files.rb b/lib/puppet/parser/files.rb index 49f36019f..809968547 100644 --- a/lib/puppet/parser/files.rb +++ b/lib/puppet/parser/files.rb @@ -41,7 +41,7 @@ module Puppet; module Parser; module Files template_paths.collect { |path| File::join(path, template) }.each do |f| - return f if Puppet::FileSystem::File.exist?(f) + return f if Puppet::FileSystem.exist?(f) end end diff --git a/lib/puppet/parser/functions/extlookup.rb b/lib/puppet/parser/functions/extlookup.rb index 293a9ea62..359c9b452 100644 --- a/lib/puppet/parser/functions/extlookup.rb +++ b/lib/puppet/parser/functions/extlookup.rb @@ -100,7 +100,7 @@ This is for back compatibility to interpolate variables with %. % interpolation # if we got a custom data file, put it first in the array of search files if datafile != "" - datafiles << extlookup_datadir + "/#{datafile}.csv" if Puppet::FileSystem::File.exist?(extlookup_datadir + "/#{datafile}.csv") + datafiles << extlookup_datadir + "/#{datafile}.csv" if Puppet::FileSystem.exist?(extlookup_datadir + "/#{datafile}.csv") end extlookup_precedence.each do |d| @@ -111,7 +111,7 @@ This is for back compatibility to interpolate variables with %. % interpolation datafiles.each do |file| if desired.nil? - if Puppet::FileSystem::File.exist?(file) + if Puppet::FileSystem.exist?(file) result = CSV.read(file).find_all do |r| r[0] == key end diff --git a/lib/puppet/parser/functions/file.rb b/lib/puppet/parser/functions/file.rb index 89d78f8ba..17401fc8b 100644 --- a/lib/puppet/parser/functions/file.rb +++ b/lib/puppet/parser/functions/file.rb @@ -10,7 +10,7 @@ Puppet::Parser::Functions::newfunction( unless Puppet::Util.absolute_path?(file) raise Puppet::ParseError, "Files must be fully qualified" end - if Puppet::FileSystem::File.exist?(file) + if Puppet::FileSystem.exist?(file) ret = File.read(file) break end diff --git a/lib/puppet/parser/lexer.rb b/lib/puppet/parser/lexer.rb index 65cbef92e..bb0e6f6f2 100644 --- a/lib/puppet/parser/lexer.rb +++ b/lib/puppet/parser/lexer.rb @@ -347,7 +347,7 @@ class Puppet::Parser::Lexer def file=(file) @file = file @line = 1 - contents = Puppet::FileSystem::File.exist?(file) ? File.read(file) : "" + contents = Puppet::FileSystem.exist?(file) ? File.read(file) : "" @scanner = StringScanner.new(contents) end diff --git a/lib/puppet/parser/parser_support.rb b/lib/puppet/parser/parser_support.rb index fb4284403..dedae1bab 100644 --- a/lib/puppet/parser/parser_support.rb +++ b/lib/puppet/parser/parser_support.rb @@ -82,7 +82,7 @@ class Puppet::Parser::Parser def_delegators :@lexer, :file, :string= def file=(file) - unless Puppet::FileSystem::File.exist?(file) + unless Puppet::FileSystem.exist?(file) unless file =~ /\.pp$/ file = file + ".pp" end diff --git a/lib/puppet/pops/binder/bindings_loader.rb b/lib/puppet/pops/binder/bindings_loader.rb index 4e37c7e87..ca9043f2c 100644 --- a/lib/puppet/pops/binder/bindings_loader.rb +++ b/lib/puppet/pops/binder/bindings_loader.rb @@ -36,7 +36,7 @@ class Puppet::Pops::Binder::BindingsLoader def self.loadable?(basedir, name) # note, "lib" is added by the autoloader # - paths_for_name(name).find {|p| Puppet::FileSystem::File.exist?(File.join(basedir, "lib/puppet/bindings", p)+'.rb') } + paths_for_name(name).find {|p| Puppet::FileSystem.exist?(File.join(basedir, "lib/puppet/bindings", p)+'.rb') } end private diff --git a/lib/puppet/pops/binder/config/binder_config.rb b/lib/puppet/pops/binder/config/binder_config.rb index c24a50571..3e83e4afb 100644 --- a/lib/puppet/pops/binder/config/binder_config.rb +++ b/lib/puppet/pops/binder/config/binder_config.rb @@ -54,20 +54,20 @@ module Puppet::Pops::Binder::Config rootdir = confdir if rootdir.is_a?(String) expanded_config_file = File.expand_path(File.join(rootdir, '/binder_config.yaml')) - if Puppet::FileSystem::File.exist?(expanded_config_file) + if Puppet::FileSystem.exist?(expanded_config_file) @config_file = expanded_config_file end else raise ArgumentError, "No Puppet settings 'confdir', or it is not a String" end when String - unless Puppet::FileSystem::File.exist?(@config_file) + unless Puppet::FileSystem.exist?(@config_file) raise ArgumentError, "Cannot find the given binder configuration file '#{@config_file}'" end else raise ArgumentError, "The setting binder_config is expected to be a String, got: #{@config_file.class.name}." end - unless @config_file.is_a?(String) && Puppet::FileSystem::File.exist?(@config_file) + unless @config_file.is_a?(String) && Puppet::FileSystem.exist?(@config_file) @config_file = nil # use defaults end diff --git a/lib/puppet/pops/parser/lexer.rb b/lib/puppet/pops/parser/lexer.rb index 39e3c4099..65cc9d969 100644 --- a/lib/puppet/pops/parser/lexer.rb +++ b/lib/puppet/pops/parser/lexer.rb @@ -398,7 +398,7 @@ class Puppet::Pops::Parser::Lexer def file=(file) @file = file - contents = Puppet::FileSystem::File.exist?(file) ? File.read(file) : "" + contents = Puppet::FileSystem.exist?(file) ? File.read(file) : "" @scanner = StringScanner.new(contents.freeze) @locator = Puppet::Pops::Parser::Locator.locator(contents, file) end diff --git a/lib/puppet/pops/parser/parser_support.rb b/lib/puppet/pops/parser/parser_support.rb index ee57ba130..646fff2ae 100644 --- a/lib/puppet/pops/parser/parser_support.rb +++ b/lib/puppet/pops/parser/parser_support.rb @@ -64,7 +64,7 @@ class Puppet::Pops::Parser::Parser # Parses a file expected to contain pp DSL logic. def parse_file(file) - unless Puppet::FileSystem::File.exist?(file) + unless Puppet::FileSystem.exist?(file) unless file =~ /\.pp$/ file = file + ".pp" end diff --git a/lib/puppet/provider/augeas/augeas.rb b/lib/puppet/provider/augeas/augeas.rb index 5a6ca6875..47c0acb1a 100644 --- a/lib/puppet/provider/augeas/augeas.rb +++ b/lib/puppet/provider/augeas/augeas.rb @@ -308,7 +308,7 @@ Puppet::Type.type(:augeas).provide(:augeas) do load_path.flatten! end - if Puppet::FileSystem::File.exist?("#{Puppet[:libdir]}/augeas/lenses") + if Puppet::FileSystem.exist?("#{Puppet[:libdir]}/augeas/lenses") load_path << "#{Puppet[:libdir]}/augeas/lenses" end diff --git a/lib/puppet/provider/exec/posix.rb b/lib/puppet/provider/exec/posix.rb index c552f9a03..1b0ae56a5 100644 --- a/lib/puppet/provider/exec/posix.rb +++ b/lib/puppet/provider/exec/posix.rb @@ -17,7 +17,7 @@ Puppet::Type.type(:exec).provide :posix, :parent => Puppet::Provider::Exec do exe = extractexe(command) if File.expand_path(exe) == exe - if !Puppet::FileSystem::File.exist?(exe) + if !Puppet::FileSystem.exist?(exe) raise ArgumentError, "Could not find command '#{exe}'" elsif !File.file?(exe) raise ArgumentError, "'#{exe}' is a #{File.ftype(exe)}, not a file" diff --git a/lib/puppet/provider/exec/windows.rb b/lib/puppet/provider/exec/windows.rb index 1c727ef0b..4c792af6d 100644 --- a/lib/puppet/provider/exec/windows.rb +++ b/lib/puppet/provider/exec/windows.rb @@ -36,7 +36,7 @@ Puppet::Type.type(:exec).provide :windows, :parent => Puppet::Provider::Exec do exe = extractexe(command) if absolute_path?(exe) - if !Puppet::FileSystem::File.exist?(exe) + if !Puppet::FileSystem.exist?(exe) raise ArgumentError, "Could not find command '#{exe}'" elsif !File.file?(exe) raise ArgumentError, "'#{exe}' is a #{File.ftype(exe)}, not a file" diff --git a/lib/puppet/provider/file/windows.rb b/lib/puppet/provider/file/windows.rb index e67791748..f77f67be4 100644 --- a/lib/puppet/provider/file/windows.rb +++ b/lib/puppet/provider/file/windows.rb @@ -90,15 +90,16 @@ Puppet::Type.type(:file).provide :windows do attr_reader :file private def file - @file ||= Puppet::FileSystem::File.new(resource[:path]) + @file ||= Puppet::FileSystem.pathname(resource[:path]) end def resolved_path + path = file() # under POSIX, :manage means use lchown - i.e. operate on the link - return file.path.to_s if resource[:links] == :manage + return path.to_s if resource[:links] == :manage # otherwise, use chown -- that will resolve the link IFF it is a link # otherwise it will operate on the path - file.symlink? ? file.readlink : file.path.to_s + Puppet::FileSystem.symlink?(path) ? Puppet::FileSystem.readlink(path) : path.to_s end end diff --git a/lib/puppet/provider/macauthorization/macauthorization.rb b/lib/puppet/provider/macauthorization/macauthorization.rb index e410d6f43..64eaf0574 100644 --- a/lib/puppet/provider/macauthorization/macauthorization.rb +++ b/lib/puppet/provider/macauthorization/macauthorization.rb @@ -17,7 +17,7 @@ Puppet::Type.type(:macauthorization).provide :macauthorization, :parent => Puppe # This should be confined based on macosx_productversion # but puppet resource doesn't make the facts available and # that interface is heavily used with this provider. - if Puppet::FileSystem::File.exist?("/usr/bin/sw_vers") + if Puppet::FileSystem.exist?("/usr/bin/sw_vers") product_version = sw_vers "-productVersion" confine :true => unless /^10\.[0-4]/.match(product_version) diff --git a/lib/puppet/provider/nameservice/directoryservice.rb b/lib/puppet/provider/nameservice/directoryservice.rb index e8fe9016f..0dd4113a0 100644 --- a/lib/puppet/provider/nameservice/directoryservice.rb +++ b/lib/puppet/provider/nameservice/directoryservice.rb @@ -251,7 +251,7 @@ class Puppet::Provider::NameService::DirectoryService < Puppet::Provider::NameSe Please check your password and try again.") end - if Puppet::FileSystem::File.exist?("#{users_plist_dir}/#{resource_name}.plist") + if Puppet::FileSystem.exist?("#{users_plist_dir}/#{resource_name}.plist") # If a plist already exists in /var/db/dslocal/nodes/Default/users, then # we will need to extract the binary plist from the 'ShadowHashData' # key, log the new password into the resultant plist's 'SALTED-SHA512' @@ -296,7 +296,7 @@ class Puppet::Provider::NameService::DirectoryService < Puppet::Provider::NameSe if (Puppet::Util::Package.versioncmp(get_macosx_version_major, '10.7') == -1) password_hash = nil password_hash_file = "#{password_hash_dir}/#{guid}" - if Puppet::FileSystem::File.exist?(password_hash_file) and File.file?(password_hash_file) + if Puppet::FileSystem.exist?(password_hash_file) and File.file?(password_hash_file) fail("Could not read password hash file at #{password_hash_file}") if not File.readable?(password_hash_file) f = File.new(password_hash_file) password_hash = f.read @@ -304,7 +304,7 @@ class Puppet::Provider::NameService::DirectoryService < Puppet::Provider::NameSe end password_hash else - if Puppet::FileSystem::File.exist?("#{users_plist_dir}/#{username}.plist") + if Puppet::FileSystem.exist?("#{users_plist_dir}/#{username}.plist") # If a plist exists in /var/db/dslocal/nodes/Default/users, we will # extract the binary plist from the 'ShadowHashData' key, decode the # salted-SHA512 password hash, and then return it. diff --git a/lib/puppet/provider/package/appdmg.rb b/lib/puppet/provider/package/appdmg.rb index aaf5a7be2..1f8aee14f 100644 --- a/lib/puppet/provider/package/appdmg.rb +++ b/lib/puppet/provider/package/appdmg.rb @@ -93,7 +93,7 @@ Puppet::Type.type(:package).provide(:appdmg, :parent => Puppet::Provider::Packag end def query - Puppet::FileSystem::File.exist?("/var/db/.puppet_appdmg_installed_#{@resource[:name]}") ? {:name => @resource[:name], :ensure => :present} : nil + Puppet::FileSystem.exist?("/var/db/.puppet_appdmg_installed_#{@resource[:name]}") ? {:name => @resource[:name], :ensure => :present} : nil end def install diff --git a/lib/puppet/provider/package/apple.rb b/lib/puppet/provider/package/apple.rb index a603d0783..1d44c3e61 100644 --- a/lib/puppet/provider/package/apple.rb +++ b/lib/puppet/provider/package/apple.rb @@ -33,7 +33,7 @@ Puppet::Type.type(:package).provide :apple, :parent => Puppet::Provider::Package end def query - Puppet::FileSystem::File.exist?("/Library/Receipts/#{@resource[:name]}.pkg") ? {:name => @resource[:name], :ensure => :present} : nil + Puppet::FileSystem.exist?("/Library/Receipts/#{@resource[:name]}.pkg") ? {:name => @resource[:name], :ensure => :present} : nil end def install diff --git a/lib/puppet/provider/package/apt.rb b/lib/puppet/provider/package/apt.rb index df5cd725a..d959d5007 100644 --- a/lib/puppet/provider/package/apt.rb +++ b/lib/puppet/provider/package/apt.rb @@ -84,7 +84,7 @@ Puppet::Type.type(:package).provide :apt, :parent => :dpkg, :source => :dpkg do # preseeds answers to dpkg-set-selection from the "responsefile" # def run_preseed - if response = @resource[:responsefile] and Puppet::FileSystem::File.exist?(response) + if response = @resource[:responsefile] and Puppet::FileSystem.exist?(response) self.info("Preseeding #{response} to debconf-set-selections") preseed response diff --git a/lib/puppet/provider/package/blastwave.rb b/lib/puppet/provider/package/blastwave.rb index fc0698e1a..27ce4d3b4 100644 --- a/lib/puppet/provider/package/blastwave.rb +++ b/lib/puppet/provider/package/blastwave.rb @@ -18,7 +18,7 @@ Puppet::Type.type(:package).provide :blastwave, :parent => :sun, :source => :sun "The pkg-get command is missing; blastwave packaging unavailable" end - unless Puppet::FileSystem::File.exist?("/var/pkg-get/admin") + unless Puppet::FileSystem.exist?("/var/pkg-get/admin") Puppet.notice "It is highly recommended you create '/var/pkg-get/admin'." Puppet.notice "See /var/pkg-get/admin-fullauto" end diff --git a/lib/puppet/provider/package/fink.rb b/lib/puppet/provider/package/fink.rb index cbf141462..6b495171b 100644 --- a/lib/puppet/provider/package/fink.rb +++ b/lib/puppet/provider/package/fink.rb @@ -56,7 +56,7 @@ Puppet::Type.type(:package).provide :fink, :parent => :dpkg, :source => :dpkg do # preseeds answers to dpkg-set-selection from the "responsefile" # def run_preseed - if response = @resource[:responsefile] and Puppet::FileSystem::File.exist?(response) + if response = @resource[:responsefile] and Puppet::FileSystem.exist?(response) self.info("Preseeding #{response} to debconf-set-selections") preseed response diff --git a/lib/puppet/provider/package/openbsd.rb b/lib/puppet/provider/package/openbsd.rb index d51bc8f04..ad64714d8 100644 --- a/lib/puppet/provider/package/openbsd.rb +++ b/lib/puppet/provider/package/openbsd.rb @@ -54,7 +54,7 @@ Puppet::Type.type(:package).provide :openbsd, :parent => Puppet::Provider::Packa def parse_pkgconf unless @resource[:source] - if Puppet::FileSystem::File.exist?("/etc/pkg.conf") + if Puppet::FileSystem.exist?("/etc/pkg.conf") File.open("/etc/pkg.conf", "rb").readlines.each do |line| if matchdata = line.match(/^installpath\s*=\s*(.+)\s*$/i) @resource[:source] = matchdata[1] diff --git a/lib/puppet/provider/package/pacman.rb b/lib/puppet/provider/package/pacman.rb index f811aa5a8..3bc87635d 100644 --- a/lib/puppet/provider/package/pacman.rb +++ b/lib/puppet/provider/package/pacman.rb @@ -14,7 +14,7 @@ Puppet::Type.type(:package).provide :pacman, :parent => Puppet::Provider::Packag # If yaourt is installed, we can make use of it def yaourt? - return Puppet::FileSystem::File.exist? '/usr/bin/yaourt' + return Puppet::FileSystem.exist?('/usr/bin/yaourt') end # Install a package using 'pacman', or 'yaourt' if available. diff --git a/lib/puppet/provider/package/pkgdmg.rb b/lib/puppet/provider/package/pkgdmg.rb index f14b53158..fea9eb4a6 100644 --- a/lib/puppet/provider/package/pkgdmg.rb +++ b/lib/puppet/provider/package/pkgdmg.rb @@ -113,7 +113,7 @@ Puppet::Type.type(:package).provide :pkgdmg, :parent => Puppet::Provider::Packag end def query - if Puppet::FileSystem::File.exist?("/var/db/.puppet_pkgdmg_installed_#{@resource[:name]}") + if Puppet::FileSystem.exist?("/var/db/.puppet_pkgdmg_installed_#{@resource[:name]}") Puppet.debug "/var/db/.puppet_pkgdmg_installed_#{@resource[:name]} found" return {:name => @resource[:name], :ensure => :present} else diff --git a/lib/puppet/provider/package/pkgutil.rb b/lib/puppet/provider/package/pkgutil.rb index c114fa949..71aa29177 100644 --- a/lib/puppet/provider/package/pkgutil.rb +++ b/lib/puppet/provider/package/pkgutil.rb @@ -14,7 +14,7 @@ Puppet::Type.type(:package).provide :pkgutil, :parent => :sun, :source => :sun d end def self.healthcheck() - unless Puppet::FileSystem::File.exist?("/var/opt/csw/pkgutil/admin") + unless Puppet::FileSystem.exist?("/var/opt/csw/pkgutil/admin") Puppet.notice "It is highly recommended you create '/var/opt/csw/pkgutil/admin'." Puppet.notice "See /var/opt/csw/pkgutil" end diff --git a/lib/puppet/provider/package/windows/package.rb b/lib/puppet/provider/package/windows/package.rb index e26c6773c..07fb64d25 100644 --- a/lib/puppet/provider/package/windows/package.rb +++ b/lib/puppet/provider/package/windows/package.rb @@ -58,7 +58,7 @@ class Puppet::Provider::Package::Windows # REMIND: what about msp, etc MsiPackage when /\.exe"?\Z/i - fail("The source does not exist: '#{resource[:source]}'") unless Puppet::FileSystem::File.exist?(resource[:source]) + fail("The source does not exist: '#{resource[:source]}'") unless Puppet::FileSystem.exist?(resource[:source]) ExePackage else fail("Don't know how to install '#{resource[:source]}'") diff --git a/lib/puppet/provider/service/bsd.rb b/lib/puppet/provider/service/bsd.rb index a19608a51..d779aad12 100644 --- a/lib/puppet/provider/service/bsd.rb +++ b/lib/puppet/provider/service/bsd.rb @@ -20,13 +20,13 @@ Puppet::Type.type(:service).provide :bsd, :parent => :init do # remove service file from rc.conf.d to disable it def disable rcfile = File.join(rcconf_dir, @model[:name]) - File.delete(rcfile) if Puppet::FileSystem::File.exist?(rcfile) + File.delete(rcfile) if Puppet::FileSystem.exist?(rcfile) end # if the service file exists in rc.conf.d then it's already enabled def enabled? rcfile = File.join(rcconf_dir, @model[:name]) - return :true if Puppet::FileSystem::File.exist?(rcfile) + return :true if Puppet::FileSystem.exist?(rcfile) :false end @@ -34,7 +34,7 @@ Puppet::Type.type(:service).provide :bsd, :parent => :init do # enable service by creating a service file under rc.conf.d with the # proper contents def enable - Dir.mkdir(rcconf_dir) if not Puppet::FileSystem::File.exist?(rcconf_dir) + Dir.mkdir(rcconf_dir) if not Puppet::FileSystem.exist?(rcconf_dir) rcfile = File.join(rcconf_dir, @model[:name]) open(rcfile, 'w') { |f| f << "%s_enable=\"YES\"\n" % @model[:name] } end diff --git a/lib/puppet/provider/service/daemontools.rb b/lib/puppet/provider/service/daemontools.rb index 32c0e0ff7..b6766560e 100644 --- a/lib/puppet/provider/service/daemontools.rb +++ b/lib/puppet/provider/service/daemontools.rb @@ -48,7 +48,7 @@ Puppet::Type.type(:service).provide :daemontools, :parent => :base do def defpath(dummy_argument=:work_arround_for_ruby_GC_bug) unless @defpath ["/var/lib/service", "/etc"].each do |path| - if Puppet::FileSystem::File.exist?(path) + if Puppet::FileSystem.exist?(path) @defpath = path break end @@ -89,7 +89,7 @@ Puppet::Type.type(:service).provide :daemontools, :parent => :base do def servicedir unless @servicedir ["/service", "/etc/service","/var/lib/svscan"].each do |path| - if Puppet::FileSystem::File.exist?(path) + if Puppet::FileSystem.exist?(path) @servicedir = path break end @@ -142,7 +142,7 @@ Puppet::Type.type(:service).provide :daemontools, :parent => :base do return :true else # the service is enabled if it is linked - return Puppet::FileSystem::File.new(self.service).symlink? ? :true : :false + return Puppet::FileSystem.symlink?(self.service) ? :true : :false end end @@ -152,9 +152,9 @@ Puppet::Type.type(:service).provide :daemontools, :parent => :base do self.setupservice end if self.daemon - if ! Puppet::FileSystem::File.new(self.service).symlink? + if ! Puppet::FileSystem.symlink?(self.service) Puppet.notice "Enabling #{self.service}: linking #{self.daemon} -> #{self.service}" - Puppet::FileSystem::File.new(self.daemon).symlink(self.service) + Puppet::FileSystem.symlink(self.daemon, self.service) end end rescue Puppet::ExecutionFailure @@ -168,9 +168,9 @@ Puppet::Type.type(:service).provide :daemontools, :parent => :base do self.setupservice end if self.daemon - if Puppet::FileSystem::File.new(self.service).symlink? + if Puppet::FileSystem.symlink?(self.service) Puppet.notice "Disabling #{self.service}: removing link #{self.daemon} -> #{self.service}" - Puppet::FileSystem::File.unlink(self.service) + Puppet::FileSystem.unlink(self.service) end end rescue Puppet::ExecutionFailure diff --git a/lib/puppet/provider/service/freebsd.rb b/lib/puppet/provider/service/freebsd.rb index 36de850c5..36386cb24 100644 --- a/lib/puppet/provider/service/freebsd.rb +++ b/lib/puppet/provider/service/freebsd.rb @@ -87,14 +87,14 @@ Puppet::Type.type(:service).provide :freebsd, :parent => :init do def rc_add(service, rcvar, yesno) append = "\# Added by Puppet\n#{rcvar}_enable=\"#{yesno}\"\n" # First, try the one-file-per-service style - if Puppet::FileSystem::File.exist?(rcconf_dir) + if Puppet::FileSystem.exist?(rcconf_dir) File.open(rcconf_dir + "/#{service}", File::WRONLY | File::APPEND | File::CREAT, 0644) { |f| f << append self.debug("Appended to #{f.path}") } else # Else, check the local rc file first, but don't create it - if Puppet::FileSystem::File.exist?(rcconf_local) + if Puppet::FileSystem.exist?(rcconf_local) File.open(rcconf_local, File::WRONLY | File::APPEND) { |f| f << append self.debug("Appended to #{f.path}") diff --git a/lib/puppet/provider/service/init.rb b/lib/puppet/provider/service/init.rb index d46cc8772..b38c98034 100644 --- a/lib/puppet/provider/service/init.rb +++ b/lib/puppet/provider/service/init.rb @@ -112,7 +112,7 @@ Puppet::Type.type(:service).provide :init, :parent => :base do def search(name) paths.each do |path| fqname = File.join(path,name) - if Puppet::FileSystem::File.exist? fqname + if Puppet::FileSystem.exist? fqname return fqname else self.debug("Could not find #{name} in #{path}") @@ -121,7 +121,7 @@ Puppet::Type.type(:service).provide :init, :parent => :base do paths.each do |path| fqname_sh = File.join(path,"#{name}.sh") - if Puppet::FileSystem::File.exist? fqname_sh + if Puppet::FileSystem.exist? fqname_sh return fqname_sh else self.debug("Could not find #{name}.sh in #{path}") @@ -154,8 +154,8 @@ Puppet::Type.type(:service).provide :init, :parent => :base do private def self.is_init?(script = initscript) - file = Puppet::FileSystem::File.new(script) - !file.symlink? || file.readlink != "/lib/init/upstart-job" + file = Puppet::FileSystem.pathname(script) + !Puppet::FileSystem.symlink?(file) || Puppet::FileSystem.readlink(file) != "/lib/init/upstart-job" end end diff --git a/lib/puppet/provider/service/runit.rb b/lib/puppet/provider/service/runit.rb index 8d835d2db..57df0a78b 100644 --- a/lib/puppet/provider/service/runit.rb +++ b/lib/puppet/provider/service/runit.rb @@ -57,7 +57,7 @@ Puppet::Type.type(:service).provide :runit, :parent => :daemontools do def servicedir unless @servicedir ["/service", "/etc/service","/var/service"].each do |path| - if Puppet::FileSystem::File.exist?(path) + if Puppet::FileSystem.exist?(path) @servicedir = path break end @@ -105,7 +105,7 @@ Puppet::Type.type(:service).provide :runit, :parent => :daemontools do # before a disable def disable # unlink the daemon symlink to disable it - Puppet::FileSystem::File.unlink(self.service) if Puppet::FileSystem::File.new(self.service).symlink? + Puppet::FileSystem.unlink(self.service) if Puppet::FileSystem.symlink?(self.service) end end diff --git a/lib/puppet/provider/service/upstart.rb b/lib/puppet/provider/service/upstart.rb index c7fd58c4e..192942284 100644 --- a/lib/puppet/provider/service/upstart.rb +++ b/lib/puppet/provider/service/upstart.rb @@ -71,7 +71,7 @@ Puppet::Type.type(:service).provide :upstart, :parent => :debian do paths.each do |path| service_name = name.match(/^(\S+)/)[1] fqname = File.join(path, service_name + suffix) - if Puppet::FileSystem::File.exist?(fqname) + if Puppet::FileSystem.exist?(fqname) return fqname end @@ -148,7 +148,7 @@ Puppet::Type.type(:service).provide :upstart, :parent => :debian do private def is_upstart?(script = initscript) - Puppet::FileSystem::File.exist?(script) && script.match(/\/etc\/init\/\S+\.conf/) + Puppet::FileSystem.exist?(script) && script.match(/\/etc\/init\/\S+\.conf/) end def version_is_pre_0_6_7 @@ -256,7 +256,7 @@ private end def read_override_file - if Puppet::FileSystem::File.exist?(overscript) + if Puppet::FileSystem.exist?(overscript) read_script_from(overscript) else "" diff --git a/lib/puppet/provider/ssh_authorized_key/parsed.rb b/lib/puppet/provider/ssh_authorized_key/parsed.rb index cb08b593f..f60a13173 100644 --- a/lib/puppet/provider/ssh_authorized_key/parsed.rb +++ b/lib/puppet/provider/ssh_authorized_key/parsed.rb @@ -41,7 +41,7 @@ Puppet::Type.type(:ssh_authorized_key).provide( end def user - uid = Puppet::FileSystem::File.new(target).stat.uid + uid = Puppet::FileSystem.stat(target).uid Etc.getpwuid(uid).name end @@ -55,7 +55,7 @@ Puppet::Type.type(:ssh_authorized_key).provide( self.class.backup_target(target) Puppet::Util::SUIDManager.asuser(@resource.should(:user)) do - unless Puppet::FileSystem::File.exist?(dir = File.dirname(target)) + unless Puppet::FileSystem.exist?(dir = File.dirname(target)) Puppet.debug "Creating #{dir}" Dir.mkdir(dir, dir_perm) end diff --git a/lib/puppet/provider/user/directoryservice.rb b/lib/puppet/provider/user/directoryservice.rb index 6a59c4bee..8cf2ee5d6 100644 --- a/lib/puppet/provider/user/directoryservice.rb +++ b/lib/puppet/provider/user/directoryservice.rb @@ -238,7 +238,7 @@ Puppet::Type.type(:user).provide :directoryservice do def self.get_sha1(guid) password_hash = nil password_hash_file = "#{password_hash_dir}/#{guid}" - if Puppet::FileSystem::File.exist?(password_hash_file) and File.file?(password_hash_file) + if Puppet::FileSystem.exist?(password_hash_file) and File.file?(password_hash_file) raise Puppet::Error, "Could not read password hash file at #{password_hash_file}" if not File.readable?(password_hash_file) f = File.new(password_hash_file) password_hash = f.read diff --git a/lib/puppet/provider/zone/solaris.rb b/lib/puppet/provider/zone/solaris.rb index 2cd0e99bb..935495252 100644 --- a/lib/puppet/provider/zone/solaris.rb +++ b/lib/puppet/provider/zone/solaris.rb @@ -261,7 +261,7 @@ Puppet::Type.type(:zone).provide(:solaris) do # which makes zoneadmd mount the zone root zoneadm :ready unless File.directory?(zoneetc) - unless Puppet::FileSystem::File.exist?(sysidcfg) + unless Puppet::FileSystem.exist?(sysidcfg) begin File.open(sysidcfg, "w", 0600) do |f| f.puts cfg diff --git a/lib/puppet/rails/benchmark.rb b/lib/puppet/rails/benchmark.rb index 8d88280c3..cf1afd8d3 100644 --- a/lib/puppet/rails/benchmark.rb +++ b/lib/puppet/rails/benchmark.rb @@ -52,7 +52,7 @@ module Puppet::Rails::Benchmark file = "/tmp/time_debugging.yaml" - if Puppet::FileSystem::File.exist?(file) + if Puppet::FileSystem.exist?(file) data = YAML.load_file(file) else data = {} diff --git a/lib/puppet/reports/rrdgraph.rb b/lib/puppet/reports/rrdgraph.rb index e55d653c4..6238ddda9 100644 --- a/lib/puppet/reports/rrdgraph.rb +++ b/lib/puppet/reports/rrdgraph.rb @@ -114,7 +114,7 @@ Puppet::Reports.register_report(:rrdgraph) do metric.graph end - mkhtml unless Puppet::FileSystem::File.exist?(File.join(hostdir, "index.html")) + mkhtml unless Puppet::FileSystem.exist?(File.join(hostdir, "index.html")) end # Unfortunately, RRD does not deal well with changing lists of values, diff --git a/lib/puppet/reports/store.rb b/lib/puppet/reports/store.rb index 27d649355..5b82f6d0f 100644 --- a/lib/puppet/reports/store.rb +++ b/lib/puppet/reports/store.rb @@ -17,7 +17,7 @@ Puppet::Reports.register_report(:store) do dir = File.join(Puppet[:reportdir], host) - if ! Puppet::FileSystem::File.exist?(dir) + if ! Puppet::FileSystem.exist?(dir) FileUtils.mkdir_p(dir) FileUtils.chmod_R(0750, dir) end @@ -54,11 +54,11 @@ Puppet::Reports.register_report(:store) do dir = File.join(Puppet[:reportdir], host) - if Puppet::FileSystem::File.exist?(dir) + if Puppet::FileSystem.exist?(dir) Dir.entries(dir).each do |file| next if ['.','..'].include?(file) file = File.join(dir, file) - Puppet::FileSystem::File.unlink(file) if File.file?(file) + Puppet::FileSystem.unlink(file) if File.file?(file) end Dir.rmdir(dir) end diff --git a/lib/puppet/reports/tagmail.rb b/lib/puppet/reports/tagmail.rb index a07f98986..757812509 100644 --- a/lib/puppet/reports/tagmail.rb +++ b/lib/puppet/reports/tagmail.rb @@ -108,7 +108,7 @@ Puppet::Reports.register_report(:tagmail) do # Process the report. This just calls the other associated messages. def process - unless Puppet::FileSystem::File.exist?(Puppet[:tagmap]) + unless Puppet::FileSystem.exist?(Puppet[:tagmap]) Puppet.notice "Cannot send tagmail report; no tagmap file #{Puppet[:tagmap]}" return end diff --git a/lib/puppet/settings.rb b/lib/puppet/settings.rb index 822867f0b..36347b756 100644 --- a/lib/puppet/settings.rb +++ b/lib/puppet/settings.rb @@ -542,7 +542,7 @@ class Puppet::Settings # Parse the configuration file. Just provides thread safety. def parse_config_files file = which_configuration_file - if Puppet::FileSystem::File.exist?(file) + if Puppet::FileSystem.exist?(file) begin text = read_file(file) rescue => detail @@ -680,7 +680,7 @@ class Puppet::Settings return @files if @files @files = [] [main_config_file, user_config_file].each do |path| - if Puppet::FileSystem::File.exist?(path) + if Puppet::FileSystem.exist?(path) @files << Puppet::Util::WatchedFile.new(path) end end diff --git a/lib/puppet/settings/directory_setting.rb b/lib/puppet/settings/directory_setting.rb index 9f67f96ab..ea795e39c 100644 --- a/lib/puppet/settings/directory_setting.rb +++ b/lib/puppet/settings/directory_setting.rb @@ -5,9 +5,8 @@ class Puppet::Settings::DirectorySetting < Puppet::Settings::FileSetting # @api private def open_file(filename, option = 'r', &block) - file = Puppet::FileSystem::File.new(filename) controlled_access do |mode| - file.open(mode, option, &block) + Puppet::FileSystem.open(filename, mode, option, &block) end end end diff --git a/lib/puppet/settings/file_setting.rb b/lib/puppet/settings/file_setting.rb index 7ebb03f48..14822bef0 100644 --- a/lib/puppet/settings/file_setting.rb +++ b/lib/puppet/settings/file_setting.rb @@ -134,7 +134,7 @@ class Puppet::Settings::FileSetting < Puppet::Settings::StringSetting # Make sure the paths are fully qualified. path = File.expand_path(path) - return nil unless type == :directory or create_files? or Puppet::FileSystem::File.exist?(path) + return nil unless type == :directory or create_files? or Puppet::FileSystem.exist?(path) return nil if path =~ /^\/dev/ or path =~ /^[A-Z]:\/dev/i resource = Puppet::Resource.new(:file, path) @@ -187,7 +187,7 @@ class Puppet::Settings::FileSetting < Puppet::Settings::StringSetting # @api private def exclusive_open(option = 'r', &block) controlled_access do |mode| - file.exclusive_open(mode, option, &block) + Puppet::FileSystem.exclusive_open(file(), mode, option, &block) end end @@ -198,9 +198,10 @@ class Puppet::Settings::FileSetting < Puppet::Settings::StringSetting end end -private + private + def file - Puppet::FileSystem::File.new(value) + Puppet::FileSystem.pathname(value) end def unknown_value(parameter, value) diff --git a/lib/puppet/ssl/certificate_authority.rb b/lib/puppet/ssl/certificate_authority.rb index 3ff4d5eb5..2738c2262 100644 --- a/lib/puppet/ssl/certificate_authority.rb +++ b/lib/puppet/ssl/certificate_authority.rb @@ -85,10 +85,10 @@ class Puppet::SSL::CertificateAuthority when 'true', true AutosignAlways.new else - file = Puppet::FileSystem::File.new(auto) - if file.executable? + file = Puppet::FileSystem.pathname(auto) + if Puppet::FileSystem.executable?(file) Puppet::SSL::CertificateAuthority::AutosignCommand.new(auto) - elsif file.exist? + elsif Puppet::FileSystem.exist?(file) AutosignConfig.new(file) else AutosignNever.new @@ -227,7 +227,7 @@ class Puppet::SSL::CertificateAuthority # Does the password file exist? def password? - Puppet::FileSystem::File.exist? Puppet[:capass] + Puppet::FileSystem.exist?(Puppet[:capass]) end # Print a given host's certificate as text. @@ -492,7 +492,7 @@ class Puppet::SSL::CertificateAuthority def autosign_store auth = Puppet::Network::AuthStore.new - @config.each_line do |line| + Puppet::FileSystem.each_line(@config) do |line| next if line =~ /^\s*#/ next if line =~ /^\s*$/ auth.allow(line.chomp) diff --git a/lib/puppet/ssl/certificate_request_attributes.rb b/lib/puppet/ssl/certificate_request_attributes.rb index e65b01443..9a2a4673a 100644 --- a/lib/puppet/ssl/certificate_request_attributes.rb +++ b/lib/puppet/ssl/certificate_request_attributes.rb @@ -20,7 +20,7 @@ class Puppet::SSL::CertificateRequestAttributes # @raise [Puppet::Error] if there are unexpected attribute keys def load Puppet.info("csr_attributes file loading from #{path}") - if Puppet::FileSystem::File.exist?(path) + if Puppet::FileSystem.exist?(path) hash = Puppet::Util::Yaml.load_file(path, {}) if ! hash.is_a?(Hash) raise Puppet::Error, "invalid CSR attributes, expected instance of Hash, received instance of #{hash.class}" diff --git a/lib/puppet/ssl/inventory.rb b/lib/puppet/ssl/inventory.rb index 3aae02a65..e3ad3121f 100644 --- a/lib/puppet/ssl/inventory.rb +++ b/lib/puppet/ssl/inventory.rb @@ -37,7 +37,7 @@ class Puppet::SSL::Inventory # Find the serial number for a given certificate. def serial(name) - return nil unless Puppet::FileSystem::File.exist?(@path) + return nil unless Puppet::FileSystem.exist?(@path) File.readlines(@path).each do |line| next unless line =~ /^(\S+).+\/CN=#{name}$/ diff --git a/lib/puppet/ssl/key.rb b/lib/puppet/ssl/key.rb index b64fde544..f8440b0a8 100644 --- a/lib/puppet/ssl/key.rb +++ b/lib/puppet/ssl/key.rb @@ -36,7 +36,7 @@ DOC end def password - return nil unless password_file and Puppet::FileSystem::File.exist?(password_file) + return nil unless password_file and Puppet::FileSystem.exist?(password_file) ::File.read(password_file) end diff --git a/lib/puppet/ssl/validator/default_validator.rb b/lib/puppet/ssl/validator/default_validator.rb index 1f238f4c4..3cb8a0f02 100644 --- a/lib/puppet/ssl/validator/default_validator.rb +++ b/lib/puppet/ssl/validator/default_validator.rb @@ -148,6 +148,6 @@ class Puppet::SSL::Validator::DefaultValidator #< class Puppet::SSL::Validator # @api private # def ssl_certificates_are_present? - Puppet::FileSystem::File.exist?(Puppet[:hostcert]) && Puppet::FileSystem::File.exist?(@ssl_configuration.ca_auth_file) + Puppet::FileSystem.exist?(Puppet[:hostcert]) && Puppet::FileSystem.exist?(@ssl_configuration.ca_auth_file) end end diff --git a/lib/puppet/type/exec.rb b/lib/puppet/type/exec.rb index d3e83e948..f86a3b904 100644 --- a/lib/puppet/type/exec.rb +++ b/lib/puppet/type/exec.rb @@ -353,7 +353,7 @@ module Puppet # If the file exists, return false (i.e., don't run the command), # else return true def check(value) - ! Puppet::FileSystem::File.exist?(value) + ! Puppet::FileSystem.exist?(value) end end diff --git a/lib/puppet/type/file.rb b/lib/puppet/type/file.rb index eee50e284..3ffa5dc79 100644 --- a/lib/puppet/type/file.rb +++ b/lib/puppet/type/file.rb @@ -691,7 +691,7 @@ Puppet::Type.newtype(:file) do end @stat = begin - Puppet::FileSystem::File.new(self[:path]).send(method) + Puppet::FileSystem.send(method, self[:path]) rescue Errno::ENOENT => error nil rescue Errno::ENOTDIR => error @@ -773,7 +773,7 @@ Puppet::Type.newtype(:file) do # @api private def remove_file(current_type, wanted_type) debug "Removing existing #{current_type} for replacement with #{wanted_type}" - Puppet::FileSystem::File.unlink(self[:path]) + Puppet::FileSystem.unlink(self[:path]) stat_needed true end diff --git a/lib/puppet/type/file/ensure.rb b/lib/puppet/type/file/ensure.rb index d75c8f6ac..77ecc57db 100644 --- a/lib/puppet/type/file/ensure.rb +++ b/lib/puppet/type/file/ensure.rb @@ -54,7 +54,7 @@ module Puppet nodefault newvalue(:absent) do - Puppet::FileSystem::File.unlink(@resource[:path]) + Puppet::FileSystem.unlink(@resource[:path]) end aliasvalue(:false, :absent) @@ -79,7 +79,7 @@ module Puppet newvalue(:directory, :event => :directory_created) do mode = @resource.should(:mode) parent = File.dirname(@resource[:path]) - unless Puppet::FileSystem::File.exist? parent + unless Puppet::FileSystem.exist? parent raise Puppet::Error, "Cannot create #{@resource[:path]}; parent directory #{parent} does not exist" end @@ -139,7 +139,7 @@ module Puppet def check basedir = File.dirname(@resource[:path]) - if ! Puppet::FileSystem::File.exist?(basedir) + if ! Puppet::FileSystem.exist?(basedir) raise Puppet::Error, "Can not create #{@resource.title}; parent directory does not exist" elsif ! FileTest.directory?(basedir) diff --git a/lib/puppet/type/file/source.rb b/lib/puppet/type/file/source.rb index 7f88e692a..180ec0172 100644 --- a/lib/puppet/type/file/source.rb +++ b/lib/puppet/type/file/source.rb @@ -141,7 +141,7 @@ module Puppet when :ignore next when :use_when_creating - next if Puppet::FileSystem::File.exist?(resource[:path]) + next if Puppet::FileSystem.exist?(resource[:path]) end copy_source_value(metadata_method) diff --git a/lib/puppet/type/file/target.rb b/lib/puppet/type/file/target.rb index 08a2a97df..12b21d185 100644 --- a/lib/puppet/type/file/target.rb +++ b/lib/puppet/type/file/target.rb @@ -40,17 +40,17 @@ module Puppet # it doesn't determine what's removed. @resource.remove_existing(target) - raise Puppet::Error, "Could not remove existing file" if Puppet::FileSystem::File.exist?(@resource[:path]) + raise Puppet::Error, "Could not remove existing file" if Puppet::FileSystem.exist?(@resource[:path]) Dir.chdir(File.dirname(@resource[:path])) do Puppet::Util::SUIDManager.asuser(@resource.asuser) do mode = @resource.should(:mode) if mode Puppet::Util.withumask(000) do - Puppet::FileSystem::File.new(target).symlink(@resource[:path]) + Puppet::FileSystem.symlink(target, @resource[:path]) end else - Puppet::FileSystem::File.new(target).symlink(@resource[:path]) + Puppet::FileSystem.symlink(target, @resource[:path]) end end @@ -63,7 +63,7 @@ module Puppet def insync?(currentvalue) if [:nochange, :notlink].include?(self.should) or @resource.recurse? return true - elsif ! @resource.replace? and Puppet::FileSystem::File.exist?(@resource[:path]) + elsif ! @resource.replace? and Puppet::FileSystem.exist?(@resource[:path]) return true else return super(currentvalue) @@ -74,7 +74,7 @@ module Puppet def retrieve if stat = @resource.stat if stat.ftype == "link" - return Puppet::FileSystem::File.new(@resource[:path]).readlink + return Puppet::FileSystem.readlink(@resource[:path]) else return :notlink end diff --git a/lib/puppet/type/k5login.rb b/lib/puppet/type/k5login.rb index a87b3e7d8..554d7c204 100644 --- a/lib/puppet/type/k5login.rb +++ b/lib/puppet/type/k5login.rb @@ -37,7 +37,7 @@ Puppet::Type.newtype(:k5login) do # Does this file exist? def exists? - Puppet::FileSystem::File.exist?(@resource[:name]) + Puppet::FileSystem.exist?(@resource[:name]) end # create the file @@ -51,12 +51,12 @@ Puppet::Type.newtype(:k5login) do # remove the file def destroy - Puppet::FileSystem::File.unlink(@resource[:name]) + Puppet::FileSystem.unlink(@resource[:name]) end # Return the principals def principals(dummy_argument=:work_arround_for_ruby_GC_bug) - if Puppet::FileSystem::File.exist?(@resource[:name]) + if Puppet::FileSystem.exist?(@resource[:name]) File.readlines(@resource[:name]).collect { |line| line.chomp } else :absent @@ -70,7 +70,7 @@ Puppet::Type.newtype(:k5login) do # Return the mode as an octal string, not as an integer def mode - "%o" % (Puppet::FileSystem::File.new(@resource[:name]).stat.mode & 007777) + "%o" % (Puppet::FileSystem.stat(@resource[:name]).mode & 007777) end # Set the file mode, converting from a string to an integer. diff --git a/lib/puppet/type/tidy.rb b/lib/puppet/type/tidy.rb index 83ac3322d..aecb1e609 100644 --- a/lib/puppet/type/tidy.rb +++ b/lib/puppet/type/tidy.rb @@ -312,7 +312,7 @@ Puppet::Type.newtype(:tidy) do def stat(path) begin - Puppet::FileSystem::File.new(path).lstat + Puppet::FileSystem.lstat(path) rescue Errno::ENOENT => error info "File does not exist" return nil diff --git a/lib/puppet/type/yumrepo.rb b/lib/puppet/type/yumrepo.rb index f71736045..8bf1b44fc 100644 --- a/lib/puppet/type/yumrepo.rb +++ b/lib/puppet/type/yumrepo.rb @@ -170,7 +170,7 @@ module Puppet unless Puppet[:noop] target_mode = 0644 # FIXME: should be configurable inifile.each_file do |file| - current_mode = Puppet::FileSystem::File.new(file).stat.mode & 0777 + current_mode = Puppet::FileSystem.stat(file).mode & 0777 unless current_mode == target_mode Puppet::info "changing mode of #{file} from %03o to %03o" % [current_mode, target_mode] ::File.chmod(target_mode, file) diff --git a/lib/puppet/util.rb b/lib/puppet/util.rb index 0249c9c0d..4ecdd60c0 100644 --- a/lib/puppet/util.rb +++ b/lib/puppet/util.rb @@ -396,8 +396,8 @@ module Util end end - file = Puppet::FileSystem::File.new(file) - tempfile = Tempfile.new(file.basename, file.dir.to_s) + file = Puppet::FileSystem.pathname(file) + tempfile = Tempfile.new(file.basename, Puppet::FileSystem.path_string(Puppet::FileSystem.dir(file))) # Set properties of the temporary file before we write the content, because # Tempfile doesn't promise to be safe from reading by other people, just @@ -409,12 +409,13 @@ module Util # secure" tempfile permissions instead. Magic happens later. if !Puppet.features.microsoft_windows? # Grab the current file mode, and fall back to the defaults. - if file.exist? - stat = file.path.lstat + effective_mode = + if Puppet::FileSystem.exist?(file) + stat = Puppet::FileSystem.lstat(file) tempfile.chown(stat.uid, stat.gid) - effective_mode = stat.mode + stat.mode else - effective_mode = mode + mode end if effective_mode @@ -445,25 +446,25 @@ module Util if Puppet.features.microsoft_windows? # Windows ReplaceFile needs a file to exist, so touch handles this - if !file.exist? - file.touch + if !Puppet::FileSystem.exist?(file) + Puppet::FileSystem.touch(file) if mode - Puppet::Util::Windows::Security.set_mode(mode, file.path.to_s) + Puppet::Util::Windows::Security.set_mode(mode, Puppet::FileSystem.path_string(file)) end end # Yes, the arguments are reversed compared to the rename in the rest # of the world. - Puppet::Util::Windows::File.replace_file(file.path, tempfile.path) + Puppet::Util::Windows::File.replace_file(file, tempfile.path) else - File.rename(tempfile.path, file.path.to_s) + File.rename(tempfile.path, Puppet::FileSystem.path_string(file)) end # Ideally, we would now fsync the directory as well, but Ruby doesn't # have support for that, and it doesn't matter /that/ much... # Return something true, and possibly useful. - file.path + file end module_function :replace_file diff --git a/lib/puppet/util/autoload.rb b/lib/puppet/util/autoload.rb index 5c3d3dea2..ddd36ded8 100644 --- a/lib/puppet/util/autoload.rb +++ b/lib/puppet/util/autoload.rb @@ -85,7 +85,7 @@ class Puppet::Util::Autoload # returns nil if no file is found def get_file(name, env=nil) name = name + '.rb' unless name =~ /\.rb$/ - path = search_directories(env).find { |dir| Puppet::FileSystem::File.exist?(File.join(dir, name)) } + path = search_directories(env).find { |dir| Puppet::FileSystem.exist?(File.join(dir, name)) } path and File.join(path, name) end diff --git a/lib/puppet/util/backups.rb b/lib/puppet/util/backups.rb index 8ae14c190..24161ff15 100644 --- a/lib/puppet/util/backups.rb +++ b/lib/puppet/util/backups.rb @@ -10,7 +10,7 @@ module Puppet::Util::Backups # let the path be specified file ||= self[:path] - return true unless Puppet::FileSystem::File.exist?(file) + return true unless Puppet::FileSystem.exist?(file) return(self.bucket ? perform_backup_with_bucket(file) : perform_backup_with_backuplocal(file, self[:backup])) end @@ -19,7 +19,7 @@ module Puppet::Util::Backups def perform_backup_with_bucket(fileobj) file = (fileobj.class == String) ? fileobj : fileobj.name - case Puppet::FileSystem::File.new(file).lstat.ftype + case Puppet::FileSystem.lstat(file).ftype when "directory" # we don't need to backup directories when recurse is on return true if self[:recurse] @@ -58,7 +58,7 @@ module Puppet::Util::Backups end begin - stat = Puppet::FileSystem::File.new(newfile).send(method) + stat = Puppet::FileSystem.send(method, newfile) rescue Errno::ENOENT return end @@ -70,7 +70,7 @@ module Puppet::Util::Backups info "Removing old backup of type #{stat.ftype}" begin - Puppet::FileSystem::File.unlink(newfile) + Puppet::FileSystem.unlink(newfile) rescue => detail message = "Could not remove old backup: #{detail}" self.log_exception(detail, message) diff --git a/lib/puppet/util/checksums.rb b/lib/puppet/util/checksums.rb index 1f28fe8a7..76772802f 100644 --- a/lib/puppet/util/checksums.rb +++ b/lib/puppet/util/checksums.rb @@ -56,7 +56,7 @@ module Puppet::Util::Checksums # Return the :mtime timestamp of a file. def mtime_file(filename) - Puppet::FileSystem::File.new(filename).stat.send(:mtime) + Puppet::FileSystem.stat(filename).send(:mtime) end # by definition this doesn't exist @@ -102,7 +102,7 @@ module Puppet::Util::Checksums # Return the :ctime of a file. def ctime_file(filename) - Puppet::FileSystem::File.new(filename).stat.send(:ctime) + Puppet::FileSystem.stat(filename).send(:ctime) end alias :ctime_stream :mtime_stream diff --git a/lib/puppet/util/execution.rb b/lib/puppet/util/execution.rb index e931816f6..0e8350cbb 100644 --- a/lib/puppet/util/execution.rb +++ b/lib/puppet/util/execution.rb @@ -286,7 +286,7 @@ module Puppet::Util::Execution # about a race condition because all of the places that we call this from are preceded by a call to "waitpid2", # meaning that the processes responsible for writing the file have completed before we get here.) 2.times do |try| - if Puppet::FileSystem::File.exist?(stdout.path) + if Puppet::FileSystem.exist?(stdout.path) stdout.open begin return stdout.read diff --git a/lib/puppet/util/filetype.rb b/lib/puppet/util/filetype.rb index 7f43f8c10..28900420a 100644 --- a/lib/puppet/util/filetype.rb +++ b/lib/puppet/util/filetype.rb @@ -98,12 +98,12 @@ class Puppet::Util::FileType newfiletype(:flat) do # Back the file up before replacing it. def backup - bucket.backup(@path) if Puppet::FileSystem::File.exist?(@path) + bucket.backup(@path) if Puppet::FileSystem.exist?(@path) end # Read the file. def read - if Puppet::FileSystem::File.exist?(@path) + if Puppet::FileSystem.exist?(@path) File.read(@path) else return nil @@ -112,7 +112,7 @@ class Puppet::Util::FileType # Remove the file. def remove - Puppet::FileSystem::File.unlink(@path) if Puppet::FileSystem::File.exist?(@path) + Puppet::FileSystem.unlink(@path) if Puppet::FileSystem.exist?(@path) end # Overwrite the file. diff --git a/lib/puppet/util/lockfile.rb b/lib/puppet/util/lockfile.rb index 4f1ad5716..a2d0d2482 100644 --- a/lib/puppet/util/lockfile.rb +++ b/lib/puppet/util/lockfile.rb @@ -31,7 +31,7 @@ class Puppet::Util::Lockfile def unlock if locked? - Puppet::FileSystem::File.unlink(@file_path) + Puppet::FileSystem.unlink(@file_path) true else false @@ -56,7 +56,7 @@ class Puppet::Util::Lockfile # being overridden by child classes. # @return [boolean] true if the file is locked, false if it is not. def file_locked?() - Puppet::FileSystem::File.exist? @file_path + Puppet::FileSystem.exist? @file_path end private :file_locked? end diff --git a/lib/puppet/util/log/destinations.rb b/lib/puppet/util/log/destinations.rb index ecb19c66b..a2b448624 100644 --- a/lib/puppet/util/log/destinations.rb +++ b/lib/puppet/util/log/destinations.rb @@ -68,7 +68,7 @@ Puppet::Util::Log.newdesttype :file do # first make sure the directory exists # We can't just use 'Config.use' here, because they've # specified a "special" destination. - unless Puppet::FileSystem::File.exist?(File.dirname(path)) + unless Puppet::FileSystem.exist?(File.dirname(path)) FileUtils.mkdir_p(File.dirname(path), :mode => 0755) Puppet.info "Creating log directory #{File.dirname(path)}" end diff --git a/lib/puppet/util/metric.rb b/lib/puppet/util/metric.rb index f37bbcfea..86df95c13 100644 --- a/lib/puppet/util/metric.rb +++ b/lib/puppet/util/metric.rb @@ -161,7 +161,7 @@ class Puppet::Util::Metric Puppet.warning "RRD library is missing; cannot store metrics" return end - self.create(time - 5) unless Puppet::FileSystem::File.exist?(self.path) + self.create(time - 5) unless Puppet::FileSystem.exist?(self.path) if Puppet.features.rrd_legacy? && ! Puppet.features.rrd? @rrd ||= RRDtool.new(self.path) diff --git a/lib/puppet/util/network_device/config.rb b/lib/puppet/util/network_device/config.rb index fe355708a..cba6009ab 100644 --- a/lib/puppet/util/network_device/config.rb +++ b/lib/puppet/util/network_device/config.rb @@ -15,7 +15,7 @@ class Puppet::Util::NetworkDevice::Config attr_reader :devices def exists? - Puppet::FileSystem::File.exist?(@file) + Puppet::FileSystem.exist?(@file) end def initialize diff --git a/lib/puppet/util/plugins.rb b/lib/puppet/util/plugins.rb index dde496d35..c39f6b138 100644 --- a/lib/puppet/util/plugins.rb +++ b/lib/puppet/util/plugins.rb @@ -37,7 +37,7 @@ module Puppet def self.known Paths[Loaded.length...Paths.length].each { |path| file = File.join(path,'plugin_init.rb') - Loaded << (Puppet::FileSystem::File.exist?(file) && new(file)) + Loaded << (Puppet::FileSystem.exist?(file) && new(file)) } Loaded.compact end diff --git a/lib/puppet/util/rdoc/generators/puppet_generator.rb b/lib/puppet/util/rdoc/generators/puppet_generator.rb index 142124769..2ad15c264 100644 --- a/lib/puppet/util/rdoc/generators/puppet_generator.rb +++ b/lib/puppet/util/rdoc/generators/puppet_generator.rb @@ -246,7 +246,7 @@ module Generators end def gen_composite_index(collection, template, filename)\ - return if Puppet::FileSystem::File.exist?(filename) + return if Puppet::FileSystem.exist?(filename) template = TemplatePage.new(RDoc::Page::FR_INDEX_BODY, template) res1 = [] diff --git a/lib/puppet/util/reference.rb b/lib/puppet/util/reference.rb index 491e39b09..81d3af0d5 100644 --- a/lib/puppet/util/reference.rb +++ b/lib/puppet/util/reference.rb @@ -46,7 +46,7 @@ class Puppet::Util::Reference # There used to be an attempt to use secure_open / replace_file to secure # the target, too, but that did nothing: the race was still here. We can # get exactly the same benefit from running this effort: - Puppet::FileSystem::File.unlink('/tmp/puppetdoc.tex') rescue nil + Puppet::FileSystem.unlink('/tmp/puppetdoc.tex') rescue nil output = %x{#{cmd}} unless $CHILD_STATUS == 0 $stderr.puts "rst2latex failed" diff --git a/lib/puppet/util/resource_template.rb b/lib/puppet/util/resource_template.rb index bed585b21..d401c4b55 100644 --- a/lib/puppet/util/resource_template.rb +++ b/lib/puppet/util/resource_template.rb @@ -44,7 +44,7 @@ class Puppet::Util::ResourceTemplate end def initialize(file, resource) - raise ArgumentError, "Template #{file} does not exist" unless Puppet::FileSystem::File.exist?(file) + raise ArgumentError, "Template #{file} does not exist" unless Puppet::FileSystem.exist?(file) @file = file @resource = resource end diff --git a/lib/puppet/util/selinux.rb b/lib/puppet/util/selinux.rb index 78c3c4dfa..6d5f0cd65 100644 --- a/lib/puppet/util/selinux.rb +++ b/lib/puppet/util/selinux.rb @@ -216,7 +216,7 @@ module Puppet::Util::SELinux # # @return [File::Stat] File.lstat result def file_lstat(path) - Puppet::FileSystem::File.new(path).lstat + Puppet::FileSystem.lstat(path) end private :file_lstat end diff --git a/lib/puppet/util/storage.rb b/lib/puppet/util/storage.rb index 9df1cb501..d01bcc3f4 100644 --- a/lib/puppet/util/storage.rb +++ b/lib/puppet/util/storage.rb @@ -45,7 +45,7 @@ class Puppet::Util::Storage Puppet.settings.use(:main) unless FileTest.directory?(Puppet[:statedir]) filename = Puppet[:statefile] - unless Puppet::FileSystem::File.exist?(filename) + unless Puppet::FileSystem.exist?(filename) self.init if @@state.nil? return end @@ -80,7 +80,7 @@ class Puppet::Util::Storage def self.store Puppet.debug "Storing state" - Puppet.info "Creating state file #{Puppet[:statefile]}" unless Puppet::FileSystem::File.exist?(Puppet[:statefile]) + Puppet.info "Creating state file #{Puppet[:statefile]}" unless Puppet::FileSystem.exist?(Puppet[:statefile]) Puppet::Util.benchmark(:debug, "Stored state") do Puppet::Util::Yaml.dump(@@state, Puppet[:statefile]) diff --git a/lib/puppet/util/watched_file.rb b/lib/puppet/util/watched_file.rb index 3e1195700..8396b55ac 100644 --- a/lib/puppet/util/watched_file.rb +++ b/lib/puppet/util/watched_file.rb @@ -26,7 +26,7 @@ class Puppet::Util::WatchedFile end # Allow this to be used as the name of the file being watched in various - # other methods (such as Puppet::FileSystem::File.exist?) + # other methods (such as Puppet::FileSystem.exist?) def to_str @filename end diff --git a/lib/puppet/util/watcher.rb b/lib/puppet/util/watcher.rb index 547c24c9e..a65788bd5 100644 --- a/lib/puppet/util/watcher.rb +++ b/lib/puppet/util/watcher.rb @@ -7,7 +7,7 @@ module Puppet::Util::Watcher def self.file_ctime_change_watcher(filename) Puppet::Util::Watcher::ChangeWatcher.watch(lambda do begin - Puppet::FileSystem::File.new(filename).stat.ctime + Puppet::FileSystem.stat(filename).ctime rescue Errno::ENOENT, Errno::ENOTDIR :absent end diff --git a/spec/fixtures/releases/jamtur01-apache/lib/puppet/provider/a2mod/debian.rb b/spec/fixtures/releases/jamtur01-apache/lib/puppet/provider/a2mod/debian.rb index f0cbc446b..9d8c12983 100644 --- a/spec/fixtures/releases/jamtur01-apache/lib/puppet/provider/a2mod/debian.rb +++ b/spec/fixtures/releases/jamtur01-apache/lib/puppet/provider/a2mod/debian.rb @@ -16,6 +16,6 @@ Puppet::Type.type(:a2mod).provide(:debian) do def exists? mod= "/etc/apache2/mods-enabled/" + resource[:name] + ".load" - Puppet::FileSystem::File.exist?(mod) + Puppet::FileSystem.exist?(mod) end end diff --git a/spec/integration/application/apply_spec.rb b/spec/integration/application/apply_spec.rb index 43fb3cd66..b040576bb 100755 --- a/spec/integration/application/apply_spec.rb +++ b/spec/integration/application/apply_spec.rb @@ -26,7 +26,7 @@ describe "apply" do puppet.apply - Puppet::FileSystem::File.exist?(file_to_create).should be_true + Puppet::FileSystem.exist?(file_to_create).should be_true File.read(file_to_create).should == "my stuff" end end diff --git a/spec/integration/application/doc_spec.rb b/spec/integration/application/doc_spec.rb index 97ef026e7..312c787b8 100755 --- a/spec/integration/application/doc_spec.rb +++ b/spec/integration/application/doc_spec.rb @@ -40,7 +40,7 @@ describe Puppet::Application::Doc do expect { puppet.run_command }.to exit_with 0 - Puppet::FileSystem::File.exist?('doc').should be_true + Puppet::FileSystem.exist?('doc').should be_true ensure Dir.chdir(old_dir) end diff --git a/spec/integration/configurer_spec.rb b/spec/integration/configurer_spec.rb index 191a79b06..32927a8de 100755 --- a/spec/integration/configurer_spec.rb +++ b/spec/integration/configurer_spec.rb @@ -62,7 +62,7 @@ describe Puppet::Configurer do file_mode = Puppet.features.microsoft_windows? ? '100644' : '100666' - Puppet::FileSystem::File.new(Puppet[:lastrunfile]).stat.mode.to_s(8).should == file_mode + Puppet::FileSystem.new(Puppet[:lastrunfile]).stat.mode.to_s(8).should == file_mode summary = nil File.open(Puppet[:lastrunfile], "r") do |fd| diff --git a/spec/integration/indirector/direct_file_server_spec.rb b/spec/integration/indirector/direct_file_server_spec.rb index 3a6b96250..cd0b32b74 100755 --- a/spec/integration/indirector/direct_file_server_spec.rb +++ b/spec/integration/indirector/direct_file_server_spec.rb @@ -15,7 +15,7 @@ describe Puppet::Indirector::DirectFileServer, " when interacting with the files it "should return an instance of the model" do pending("porting to Windows", :if => Puppet.features.microsoft_windows?) do - Puppet::FileSystem::File.expects(:exist?).with(@filepath).returns(true) + Puppet::FileSystem.expects(:exist?).with(@filepath).returns(true) @terminus.find(@terminus.indirection.request(:find, "file://host#{@filepath}", nil)).should be_instance_of(Puppet::FileServing::Content) end diff --git a/spec/integration/indirector/file_content/file_server_spec.rb b/spec/integration/indirector/file_content/file_server_spec.rb index 44e2fabee..bfa2f2017 100755 --- a/spec/integration/indirector/file_content/file_server_spec.rb +++ b/spec/integration/indirector/file_content/file_server_spec.rb @@ -59,8 +59,8 @@ describe Puppet::Indirector::FileContent::FileServer, " when finding files" do end it "should find file content in files when node name expansions are used" do - Puppet::FileSystem::File.stubs(:exist?).returns true - Puppet::FileSystem::File.stubs(:exist?).with(Puppet[:fileserverconfig]).returns(true) + Puppet::FileSystem.stubs(:exist?).returns true + Puppet::FileSystem.stubs(:exist?).with(Puppet[:fileserverconfig]).returns(true) @path = tmpfile("file_server_testing") diff --git a/spec/integration/node/facts_spec.rb b/spec/integration/node/facts_spec.rb index fa9fd4905..cf465b797 100755 --- a/spec/integration/node/facts_spec.rb +++ b/spec/integration/node/facts_spec.rb @@ -23,7 +23,7 @@ describe Puppet::Node::Facts do terminus = Puppet::Node::Facts.indirection.terminus(:yaml) terminus.expects(:path).with("me").returns "/my/yaml/file" - Puppet::FileSystem::File.expects(:exist?).with("/my/yaml/file").returns false + Puppet::FileSystem.expects(:exist?).with("/my/yaml/file").returns false Puppet::Node::Facts.indirection.find("me").should be_nil end diff --git a/spec/integration/node_spec.rb b/spec/integration/node_spec.rb index ef862ef28..8337c2a66 100755 --- a/spec/integration/node_spec.rb +++ b/spec/integration/node_spec.rb @@ -21,7 +21,7 @@ describe Puppet::Node do terminus.expects(:path).with(@name).returns "/my/yaml/file" - Puppet::FileSystem::File.expects(:exist?).with("/my/yaml/file").returns false + Puppet::FileSystem.expects(:exist?).with("/my/yaml/file").returns false Puppet::Node.indirection.find(@name).should be_nil end diff --git a/spec/integration/resource/catalog_spec.rb b/spec/integration/resource/catalog_spec.rb index 1469cc063..583792103 100755 --- a/spec/integration/resource/catalog_spec.rb +++ b/spec/integration/resource/catalog_spec.rb @@ -21,7 +21,7 @@ describe Puppet::Resource::Catalog do terminus = Puppet::Resource::Catalog.indirection.terminus(:yaml) terminus.expects(:path).with("me").returns "/my/yaml/file" - Puppet::FileSystem::File.expects(:exist?).with("/my/yaml/file").returns false + Puppet::FileSystem.expects(:exist?).with("/my/yaml/file").returns false Puppet::Resource::Catalog.indirection.find("me").should be_nil end diff --git a/spec/integration/ssl/autosign_spec.rb b/spec/integration/ssl/autosign_spec.rb index 003796ef0..2812d1fcc 100644 --- a/spec/integration/ssl/autosign_spec.rb +++ b/spec/integration/ssl/autosign_spec.rb @@ -39,7 +39,7 @@ describe "autosigning" do context "when the csr_attributes file is valid, but empty" do it "generates a CSR when the file is empty" do - Puppet::FileSystem::File.new(Puppet.settings[:csr_attributes]).touch + Puppet::FileSystem.touch(Puppet.settings[:csr_attributes]) host.generate_certificate_request end diff --git a/spec/integration/ssl/certificate_revocation_list_spec.rb b/spec/integration/ssl/certificate_revocation_list_spec.rb index 530f03ed9..06a69a741 100755 --- a/spec/integration/ssl/certificate_revocation_list_spec.rb +++ b/spec/integration/ssl/certificate_revocation_list_spec.rb @@ -29,7 +29,7 @@ describe Puppet::SSL::CertificateRevocationList do it "should be able to read in written out CRLs with no revoked certificates" do ca = Puppet::SSL::CertificateAuthority.new - raise "CRL not created" unless Puppet::FileSystem::File.exist?(Puppet[:hostcrl]) + raise "CRL not created" unless Puppet::FileSystem.exist?(Puppet[:hostcrl]) crl = Puppet::SSL::CertificateRevocationList.new("crl_int_testing") crl.read(Puppet[:hostcrl]) diff --git a/spec/integration/ssl/host_spec.rb b/spec/integration/ssl/host_spec.rb index fb6b4e1e0..fbb108db7 100755 --- a/spec/integration/ssl/host_spec.rb +++ b/spec/integration/ssl/host_spec.rb @@ -70,7 +70,7 @@ describe Puppet::SSL::Host do @ca = Puppet::SSL::Host.new(Puppet::SSL::Host.ca_name) @ca.generate_key - Puppet::FileSystem::File.exist?(File.join(Puppet[:privatekeydir], "ca.pem")).should be_false + Puppet::FileSystem.exist?(File.join(Puppet[:privatekeydir], "ca.pem")).should be_false end end diff --git a/spec/integration/transaction_spec.rb b/spec/integration/transaction_spec.rb index 851954dbe..fc1fff228 100755 --- a/spec/integration/transaction_spec.rb +++ b/spec/integration/transaction_spec.rb @@ -64,7 +64,7 @@ describe Puppet::Transaction do catalog.add_resource resource catalog.apply - Puppet::FileSystem::File.exist?(path).should be_true + Puppet::FileSystem.exist?(path).should be_true end it "should not apply virtual exported resources" do @@ -189,8 +189,8 @@ describe Puppet::Transaction do catalog = mk_catalog(file, exec1, exec2) catalog.apply - Puppet::FileSystem::File.exist?(file1).should be_true - Puppet::FileSystem::File.exist?(file2).should be_true + Puppet::FileSystem.exist?(file1).should be_true + Puppet::FileSystem.exist?(file2).should be_true end it "should not let one failed refresh result in other refreshes failing" do @@ -223,7 +223,7 @@ describe Puppet::Transaction do catalog = mk_catalog(file, exec1, exec2) catalog.apply - Puppet::FileSystem::File.exist?(newfile).should be_true + Puppet::FileSystem.exist?(newfile).should be_true end it "should still trigger skipped resources" do @@ -251,18 +251,18 @@ describe Puppet::Transaction do # Run it once catalog.apply - Puppet::FileSystem::File.exist?(fname).should be_true + Puppet::FileSystem.exist?(fname).should be_true # Now remove it, so it can get created again - Puppet::FileSystem::File.unlink(fname) + Puppet::FileSystem.unlink(fname) file[:content] = "some content" catalog.apply - Puppet::FileSystem::File.exist?(fname).should be_true + Puppet::FileSystem.exist?(fname).should be_true # Now remove it, so it can get created again - Puppet::FileSystem::File.unlink(fname) + Puppet::FileSystem.unlink(fname) # And tag our exec exec.tag("testrun") @@ -275,7 +275,7 @@ describe Puppet::Transaction do file[:content] = "totally different content" catalog.apply - Puppet::FileSystem::File.exist?(fname).should be_true + Puppet::FileSystem.exist?(fname).should be_true end it "should not attempt to evaluate resources with failed dependencies" do @@ -302,8 +302,8 @@ describe Puppet::Transaction do catalog = mk_catalog(exec, file1, file2) catalog.apply - Puppet::FileSystem::File.exist?(file1[:path]).should be_false - Puppet::FileSystem::File.exist?(file2[:path]).should be_false + Puppet::FileSystem.exist?(file1[:path]).should be_false + Puppet::FileSystem.exist?(file2[:path]).should be_false end it "should not trigger subscribing resources on failure" do @@ -328,8 +328,8 @@ describe Puppet::Transaction do catalog = mk_catalog(exec, create_file1, create_file2) catalog.apply - Puppet::FileSystem::File.exist?(file1).should be_false - Puppet::FileSystem::File.exist?(file2).should be_false + Puppet::FileSystem.exist?(file1).should be_false + Puppet::FileSystem.exist?(file2).should be_false end # #801 -- resources only checked in noop should be rescheduled immediately. diff --git a/spec/integration/type/exec_spec.rb b/spec/integration/type/exec_spec.rb index 2b044473f..1e39bdb9f 100755 --- a/spec/integration/type/exec_spec.rb +++ b/spec/integration/type/exec_spec.rb @@ -33,7 +33,7 @@ describe Puppet::Type.type(:exec) do catalog.add_resource exec catalog.apply - Puppet::FileSystem::File.exist?(path).should be_false + Puppet::FileSystem.exist?(path).should be_false end it "should execute the command if onlyif returns zero" do @@ -72,6 +72,6 @@ describe Puppet::Type.type(:exec) do catalog.add_resource exec catalog.apply - Puppet::FileSystem::File.exist?(path).should be_false + Puppet::FileSystem.exist?(path).should be_false end end diff --git a/spec/integration/type/file_spec.rb b/spec/integration/type/file_spec.rb index b244b70b8..c6a11549d 100755 --- a/spec/integration/type/file_spec.rb +++ b/spec/integration/type/file_spec.rb @@ -34,15 +34,15 @@ describe Puppet::Type.type(:file) do end def get_mode(file) - Puppet::FileSystem::File.new(file).lstat.mode + Puppet::FileSystem.lstat(file).mode end def get_owner(file) - Puppet::FileSystem::File.new(file).lstat.uid + Puppet::FileSystem.lstat(file).uid end def get_group(file) - Puppet::FileSystem::File.new(file).lstat.gid + Puppet::FileSystem.lstat(file).gid end else class SecurityHelper @@ -83,7 +83,7 @@ describe Puppet::Type.type(:file) do status = catalog.apply.report.resource_statuses["File[#{source}]"] status.should_not be_failed status.should_not be_changed - Puppet::FileSystem::File.exist?(source).should be_false + Puppet::FileSystem.exist?(source).should be_false end describe "when ensure is absent" do @@ -92,14 +92,14 @@ describe Puppet::Type.type(:file) do catalog.add_resource(described_class.new(:path => path, :ensure => :absent, :backup => :false)) report = catalog.apply.report report.resource_statuses["File[#{path}]"].should_not be_failed - Puppet::FileSystem::File.exist?(path).should be_false + Puppet::FileSystem.exist?(path).should be_false end it "should do nothing if file is not present" do catalog.add_resource(described_class.new(:path => path, :ensure => :absent, :backup => :false)) report = catalog.apply.report report.resource_statuses["File[#{path}]"].should_not be_failed - Puppet::FileSystem::File.exist?(path).should be_false + Puppet::FileSystem.exist?(path).should be_false end # issue #14599 @@ -225,7 +225,7 @@ describe Puppet::Type.type(:file) do FileUtils.touch(link_target) File.chmod(0444, link_target) - Puppet::FileSystem::File.new(link_target).symlink(link) + Puppet::FileSystem.symlink(link_target, link) end it "should not set the executable bit on the link nor the target" do @@ -233,8 +233,8 @@ describe Puppet::Type.type(:file) do catalog.apply - (Puppet::FileSystem::File.new(link).stat.mode & 07777) == 0666 - (Puppet::FileSystem::File.new(link_target).lstat.mode & 07777) == 0444 + (Puppet::FileSystem.stat(link).mode & 07777) == 0666 + (Puppet::FileSystem.lstat(link_target).mode & 07777) == 0444 end it "should ignore dangling symlinks (#6856)" do @@ -243,7 +243,7 @@ describe Puppet::Type.type(:file) do catalog.add_resource described_class.new(:path => link, :ensure => :link, :mode => 0666, :target => link_target, :links => :manage) catalog.apply - Puppet::FileSystem::File.exist?(link).should be_false + Puppet::FileSystem.exist?(link).should be_false end it "should create a link to the target if ensure is omitted" do @@ -251,9 +251,9 @@ describe Puppet::Type.type(:file) do catalog.add_resource described_class.new(:path => link, :target => link_target) catalog.apply - Puppet::FileSystem::File.exist?(link).should be_true - Puppet::FileSystem::File.new(link).lstat.ftype.should == 'link' - Puppet::FileSystem::File.new(link).readlink().should == link_target + Puppet::FileSystem.exist?(link).should be_true + Puppet::FileSystem.lstat(link).ftype.should == 'link' + Puppet::FileSystem.readlink(link).should == link_target end end @@ -262,7 +262,7 @@ describe Puppet::Type.type(:file) do target = tmpfile('dangling') FileUtils.touch(target) - Puppet::FileSystem::File.new(target).symlink(link) + Puppet::FileSystem.symlink(target, link) File.delete(target) catalog.add_resource described_class.new(:path => path, :source => link, :mode => 0600, :links => :follow) @@ -275,7 +275,7 @@ describe Puppet::Type.type(:file) do before :each do File.chmod(0600, link_target) - Puppet::FileSystem::File.new(link_target).symlink(link) + Puppet::FileSystem.symlink(link_target, link) end after :each do @@ -338,7 +338,7 @@ describe Puppet::Type.type(:file) do before :each do FileUtils.touch(link_target) - Puppet::FileSystem::File.new(link_target).symlink(link) + Puppet::FileSystem.symlink(link_target, link) end it "should create the file, not a symlink (#2817, #10315)" do @@ -368,8 +368,8 @@ describe Puppet::Type.type(:file) do File.chmod(0666, real_target) # link -> target -> real_target - Puppet::FileSystem::File.new(real_target).symlink(target) - Puppet::FileSystem::File.new(target).symlink(link) + Puppet::FileSystem.symlink(real_target, target) + Puppet::FileSystem.symlink(target, link) end after :each do @@ -426,7 +426,7 @@ describe Puppet::Type.type(:file) do catalog.apply backup = file[:path] + ".bak" - Puppet::FileSystem::File.exist?(backup).should be_true + Puppet::FileSystem.exist?(backup).should be_true File.read(backup).should == "bar\n" end @@ -458,14 +458,14 @@ describe Puppet::Type.type(:file) do catalog.add_resource bucket File.open(dest1, "w") { |f| f.puts "whatever" } - Puppet::FileSystem::File.new(dest1).symlink(link) + Puppet::FileSystem.symlink(dest1, link) md5 = Digest::MD5.hexdigest(File.read(file[:path])) catalog.apply - Puppet::FileSystem::File.new(link).readlink().should == dest2 - Puppet::FileSystem::File.exist?(bucket[:path]).should be_false + Puppet::FileSystem.readlink(link).should == dest2 + Puppet::FileSystem.exist?(bucket[:path]).should be_false end it "should backup directories to the local filesystem by copying the whole directory" do @@ -587,13 +587,13 @@ describe Puppet::Type.type(:file) do @dirs.each do |path| link_path = path.sub(source, dest) - Puppet::FileSystem::File.new(link_path).lstat.should be_directory + Puppet::FileSystem.lstat(link_path).should be_directory end @files.each do |path| link_path = path.sub(source, dest) - Puppet::FileSystem::File.new(link_path).lstat.ftype.should == "link" + Puppet::FileSystem.lstat(link_path).ftype.should == "link" end end @@ -613,13 +613,13 @@ describe Puppet::Type.type(:file) do @dirs.each do |path| newpath = path.sub(source, dest) - Puppet::FileSystem::File.new(newpath).lstat.should be_directory + Puppet::FileSystem.lstat(newpath).should be_directory end @files.each do |path| newpath = path.sub(source, dest) - Puppet::FileSystem::File.new(newpath).lstat.ftype.should == "file" + Puppet::FileSystem.lstat(newpath).ftype.should == "file" end end @@ -682,8 +682,8 @@ describe Puppet::Type.type(:file) do catalog.apply File.should be_directory(path) - Puppet::FileSystem::File.exist?(File.join(path, 'one')).should be_false - Puppet::FileSystem::File.exist?(File.join(path, 'three', 'four')).should be_true + Puppet::FileSystem.exist?(File.join(path, 'one')).should be_false + Puppet::FileSystem.exist?(File.join(path, 'three', 'four')).should be_true end it "should recursively copy an empty directory" do @@ -704,7 +704,7 @@ describe Puppet::Type.type(:file) do catalog.apply File.should be_directory(path) - Puppet::FileSystem::File.exist?(File.join(path, 'a')).should be_false + Puppet::FileSystem.exist?(File.join(path, 'a')).should be_false end it "should only recurse one level" do @@ -728,9 +728,9 @@ describe Puppet::Type.type(:file) do catalog.apply - Puppet::FileSystem::File.exist?(File.join(path, 'a')).should be_true - Puppet::FileSystem::File.exist?(File.join(path, 'a', 'b')).should be_false - Puppet::FileSystem::File.exist?(File.join(path, 'z')).should be_false + Puppet::FileSystem.exist?(File.join(path, 'a')).should be_true + Puppet::FileSystem.exist?(File.join(path, 'a', 'b')).should be_false + Puppet::FileSystem.exist?(File.join(path, 'z')).should be_false end end @@ -827,10 +827,10 @@ describe Puppet::Type.type(:file) do catalog.add_resource obj catalog.apply - Puppet::FileSystem::File.exist?(File.join(path, 'a')).should be_true - Puppet::FileSystem::File.exist?(File.join(path, 'a', 'b')).should be_false - Puppet::FileSystem::File.exist?(File.join(path, 'z')).should be_true - Puppet::FileSystem::File.exist?(File.join(path, 'z', 'y')).should be_false + Puppet::FileSystem.exist?(File.join(path, 'a')).should be_true + Puppet::FileSystem.exist?(File.join(path, 'a', 'b')).should be_false + Puppet::FileSystem.exist?(File.join(path, 'z')).should be_true + Puppet::FileSystem.exist?(File.join(path, 'z', 'y')).should be_false end end end @@ -894,7 +894,7 @@ describe Puppet::Type.type(:file) do expected_mode = Puppet.features.microsoft_windows? ? 0644 : 0755 File.read(dest).should == "foo" - (Puppet::FileSystem::File.new(dest).stat.mode & 007777).should == expected_mode + (Puppet::FileSystem.stat(dest).mode & 007777).should == expected_mode end it "should be able to copy individual files even if recurse has been specified" do @@ -946,7 +946,7 @@ describe Puppet::Type.type(:file) do catalog.add_resource file catalog.apply - Puppet::FileSystem::File.exist?(dest).should be_false + Puppet::FileSystem.exist?(dest).should be_false end describe "when sourcing" do @@ -1325,7 +1325,7 @@ describe Puppet::Type.type(:file) do end it "should purge files that are neither remote nor otherwise managed" do - Puppet::FileSystem::File.exist?(@purgee).should be_false + Puppet::FileSystem.exist?(@purgee).should be_false end end diff --git a/spec/integration/type/tidy_spec.rb b/spec/integration/type/tidy_spec.rb index 562ae17e3..9c044d703 100755 --- a/spec/integration/type/tidy_spec.rb +++ b/spec/integration/type/tidy_spec.rb @@ -17,7 +17,7 @@ describe Puppet::Type.type(:tidy) do link = File.join(dir, "link") target = tmpfile("no_such_file_tidy_link_testing") Dir.mkdir(dir) - Puppet::FileSystem::File.new(target).symlink(link) + Puppet::FileSystem.symlink(target, link) tidy = Puppet::Type.type(:tidy).new :path => dir, :recurse => true @@ -26,6 +26,6 @@ describe Puppet::Type.type(:tidy) do catalog.apply - Puppet::FileSystem::File.new(link).symlink?.should be_false + Puppet::FileSystem.symlink?(link).should be_false end end diff --git a/spec/integration/util/rdoc/parser_spec.rb b/spec/integration/util/rdoc/parser_spec.rb index 58c0a882d..d3bbef45c 100755 --- a/spec/integration/util/rdoc/parser_spec.rb +++ b/spec/integration/util/rdoc/parser_spec.rb @@ -113,7 +113,7 @@ end end def file_exists_and_matches_content(file, *content_patterns) - Puppet::FileSystem::File.exist?(file).should(be_true, "Cannot find #{file}") + Puppet::FileSystem.exist?(file).should(be_true, "Cannot find #{file}") content_patterns.each do |pattern| content = File.read(file) content.should match(pattern) diff --git a/spec/integration/util/settings_spec.rb b/spec/integration/util/settings_spec.rb index 0da0938e9..240faf078 100755 --- a/spec/integration/util/settings_spec.rb +++ b/spec/integration/util/settings_spec.rb @@ -41,7 +41,7 @@ describe Puppet::Settings do settings.use(:main) - expect(Puppet::FileSystem::File.new(settings[:maindir]).stat.mode & 007777).to eq(Puppet.features.microsoft_windows? ? 0755 : 0750) + expect(Puppet::FileSystem.new(settings[:maindir]).stat.mode & 007777).to eq(Puppet.features.microsoft_windows? ? 0755 : 0750) end it "reparses configuration if configuration file is touched", :if => !Puppet.features.microsoft_windows? do diff --git a/spec/integration/util/windows/security_spec.rb b/spec/integration/util/windows/security_spec.rb index f839ec230..37756879c 100755 --- a/spec/integration/util/windows/security_spec.rb +++ b/spec/integration/util/windows/security_spec.rb @@ -104,7 +104,7 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win after :each do winsec.set_mode(WindowsSecurityTester::S_IRWXU, parent) - winsec.set_mode(WindowsSecurityTester::S_IRWXU, path) if Puppet::FileSystem::File.exist?(path) + winsec.set_mode(WindowsSecurityTester::S_IRWXU, path) if Puppet::FileSystem.exist?(path) end describe "#supports_acl?" do @@ -371,7 +371,7 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win end after :each do - if Puppet::FileSystem::File.exist?(path) + if Puppet::FileSystem.exist?(path) winsec.set_owner(sids[:current_user], path) winsec.set_mode(WindowsSecurityTester::S_IRWXU, path) end diff --git a/spec/shared_behaviours/file_server_terminus.rb b/spec/shared_behaviours/file_server_terminus.rb index ff122f8ad..25d24682a 100755 --- a/spec/shared_behaviours/file_server_terminus.rb +++ b/spec/shared_behaviours/file_server_terminus.rb @@ -4,8 +4,8 @@ shared_examples_for "Puppet::Indirector::FileServerTerminus" do # the 'before' block in the including context. before do Puppet::FileServing::Configuration.instance_variable_set(:@configuration, nil) - Puppet::FileSystem::File.stubs(:exist?).returns true - Puppet::FileSystem::File.stubs(:exist?).with(Puppet[:fileserverconfig]).returns(true) + Puppet::FileSystem.stubs(:exist?).returns true + Puppet::FileSystem.stubs(:exist?).with(Puppet[:fileserverconfig]).returns(true) @path = Tempfile.new("file_server_testing") path = @path.path diff --git a/spec/unit/application/agent_spec.rb b/spec/unit/application/agent_spec.rb index 0ce175b79..c6fbba48f 100755 --- a/spec/unit/application/agent_spec.rb +++ b/spec/unit/application/agent_spec.rb @@ -451,7 +451,7 @@ describe Puppet::Application::Agent do describe "when setting up listen" do before :each do - Puppet::FileSystem::File.stubs(:exist?).with(Puppet[:rest_authconfig]).returns(true) + Puppet::FileSystem.stubs(:exist?).with(Puppet[:rest_authconfig]).returns(true) @puppetd.options[:serve] = [] @server = stub_everything 'server' Puppet::Network::Server.stubs(:new).returns(@server) @@ -461,7 +461,7 @@ describe Puppet::Application::Agent do it "should exit if no authorization file" do Puppet[:listen] = true Puppet.stubs(:err) - Puppet::FileSystem::File.stubs(:exist?).with(Puppet[:rest_authconfig]).returns(false) + Puppet::FileSystem.stubs(:exist?).with(Puppet[:rest_authconfig]).returns(false) expect do execute_agent diff --git a/spec/unit/application/filebucket_spec.rb b/spec/unit/application/filebucket_spec.rb index 4301c7dcd..926ea9c1d 100755 --- a/spec/unit/application/filebucket_spec.rb +++ b/spec/unit/application/filebucket_spec.rb @@ -178,7 +178,7 @@ describe Puppet::Application::Filebucket do it "should call the client backup method for each given parameter" do @filebucket.stubs(:puts) - Puppet::FileSystem::File.stubs(:exist?).returns(true) + Puppet::FileSystem.stubs(:exist?).returns(true) FileTest.stubs(:readable?).returns(true) @filebucket.stubs(:args).returns(["file1", "file2"]) diff --git a/spec/unit/configurer/downloader_spec.rb b/spec/unit/configurer/downloader_spec.rb index a818370fc..0952fcd7b 100755 --- a/spec/unit/configurer/downloader_spec.rb +++ b/spec/unit/configurer/downloader_spec.rb @@ -136,7 +136,7 @@ describe Puppet::Configurer::Downloader do Puppet[:tags] = 'maytag' @dler.evaluate - Puppet::FileSystem::File.exist?(@dl_name).should be_true + Puppet::FileSystem.exist?(@dl_name).should be_true end it "should log that it is downloading" do diff --git a/spec/unit/confine/exists_spec.rb b/spec/unit/confine/exists_spec.rb index 87959fe34..a2a575bd2 100755 --- a/spec/unit/confine/exists_spec.rb +++ b/spec/unit/confine/exists_spec.rb @@ -30,12 +30,12 @@ describe Puppet::Confine::Exists do end it "should return false if the value does not point to a file" do - Puppet::FileSystem::File.expects(:exist?).with("/my/file").returns false + Puppet::FileSystem.expects(:exist?).with("/my/file").returns false @confine.pass?("/my/file").should be_false end it "should return true if the value points to a file" do - Puppet::FileSystem::File.expects(:exist?).with("/my/file").returns true + Puppet::FileSystem.expects(:exist?).with("/my/file").returns true @confine.pass?("/my/file").should be_true end @@ -62,9 +62,9 @@ describe Puppet::Confine::Exists do end it "should produce a summary containing all missing files" do - Puppet::FileSystem::File.stubs(:exist?).returns true - Puppet::FileSystem::File.expects(:exist?).with("/two").returns false - Puppet::FileSystem::File.expects(:exist?).with("/four").returns false + Puppet::FileSystem.stubs(:exist?).returns true + Puppet::FileSystem.expects(:exist?).with("/two").returns false + Puppet::FileSystem.expects(:exist?).with("/four").returns false confine = Puppet::Confine::Exists.new %w{/one /two /three /four} confine.summary.should == %w{/two /four} diff --git a/spec/unit/file_bucket/dipper_spec.rb b/spec/unit/file_bucket/dipper_spec.rb index 83e914851..a6d961695 100755 --- a/spec/unit/file_bucket/dipper_spec.rb +++ b/spec/unit/file_bucket/dipper_spec.rb @@ -45,7 +45,7 @@ describe Puppet::FileBucket::Dipper do Digest::MD5.hexdigest("my\r\ncontents").should == checksum @dipper.backup(file).should == checksum - Puppet::FileSystem::File.exist?("#{file_bucket}/f/0/d/7/d/4/e/4/f0d7d4e480ad698ed56aeec8b6bd6dea/contents").should == true + Puppet::FileSystem.exist?("#{file_bucket}/f/0/d/7/d/4/e/4/f0d7d4e480ad698ed56aeec8b6bd6dea/contents").should == true end it "should not backup a file that is already in the bucket" do @@ -123,7 +123,7 @@ describe Puppet::FileBucket::Dipper do klass.any_instance.expects(:find).with { |r| request = r }.returns(Puppet::FileBucket::File.new(contents)) dipper.restore(dest, md5).should == md5 - Digest::MD5.hexdigest(Puppet::FileSystem::File.new(dest).binread).should == md5 + Digest::MD5.hexdigest(Puppet::FileSystem.binread(dest)).should == md5 request.key.should == "md5/#{md5}" request.server.should == server diff --git a/spec/unit/file_serving/base_spec.rb b/spec/unit/file_serving/base_spec.rb index 65168d3a3..a5652d4ff 100755 --- a/spec/unit/file_serving/base_spec.rb +++ b/spec/unit/file_serving/base_spec.rb @@ -42,12 +42,12 @@ describe Puppet::FileServing::Base do end it "should allow specification of a path" do - Puppet::FileSystem::File.stubs(:exist?).returns(true) + Puppet::FileSystem.stubs(:exist?).returns(true) Puppet::FileServing::Base.new(path, :path => file).path.should == file end it "should allow specification of a relative path" do - Puppet::FileSystem::File.stubs(:exist?).returns(true) + Puppet::FileSystem.stubs(:exist?).returns(true) Puppet::FileServing::Base.new(path, :relative_path => "my/file").relative_path.should == "my/file" end @@ -57,21 +57,19 @@ describe Puppet::FileServing::Base do it "should correctly indicate if the file is present" do mock_file = mock(file, :lstat => stub('stat')) - Puppet::FileSystem::File.expects(:new).with(file).returns mock_file + Puppet::FileSystem.expects(:new).with(file).returns mock_file Puppet::FileServing::Base.new(file).exist?.should be_true end it "should correctly indicate if the file is absent" do - mock_file = mock(file) - Puppet::FileSystem::File.expects(:new).with(file).returns mock_file - mock_file.expects(:lstat).raises RuntimeError + Puppet::FileSystem.expects(:lstat).with(file).raises RuntimeError Puppet::FileServing::Base.new(file).exist?.should be_false end describe "when setting the relative path" do it "should require that the relative path be unqualified" do @file = Puppet::FileServing::Base.new(path) - Puppet::FileSystem::File.stubs(:exist?).returns(true) + Puppet::FileSystem.stubs(:exist?).returns(true) proc { @file.relative_path = File.expand_path("/qualified/file") }.should raise_error(ArgumentError) end end @@ -129,23 +127,22 @@ describe Puppet::FileServing::Base do let(:stubbed_file) { stub(path, :stat => stat, :lstat => stat)} it "should stat the file's full path" do - Puppet::FileSystem::File.expects(:new).with(path).returns stubbed_file + Puppet::FileSystem.expects(:stat).with(path).returns stat file.stat end it "should fail if the file does not exist" do - Puppet::FileSystem::File.expects(:new).with(path).returns stubbed_file - stubbed_file.expects(:lstat).raises(Errno::ENOENT) + Puppet::FileSystem.expects(:lstat).with(path).raises(Errno::ENOENT) proc { file.stat }.should raise_error(Errno::ENOENT) end it "should use :lstat if :links is set to :manage" do - Puppet::FileSystem::File.expects(:new).with(path).returns stubbed_file + Puppet::FileSystem.expects(:lstat).with(path).returns stubbed_file file.stat end it "should use :stat if :links is set to :follow" do - Puppet::FileSystem::File.expects(:new).with(path).returns stubbed_file + Puppet::FileSystem.expects(:stat).with(path).returns stubbed_file file.links = :follow file.stat end diff --git a/spec/unit/file_serving/configuration_spec.rb b/spec/unit/file_serving/configuration_spec.rb index b6999e086..a2c808051 100755 --- a/spec/unit/file_serving/configuration_spec.rb +++ b/spec/unit/file_serving/configuration_spec.rb @@ -27,12 +27,12 @@ describe Puppet::FileServing::Configuration do describe "when initializing" do it "should work without a configuration file" do - Puppet::FileSystem::File.stubs(:exist?).with(@path).returns(false) + Puppet::FileSystem.stubs(:exist?).with(@path).returns(false) expect { Puppet::FileServing::Configuration.configuration }.to_not raise_error end it "should parse the configuration file if present" do - Puppet::FileSystem::File.stubs(:exist?).with(@path).returns(true) + Puppet::FileSystem.stubs(:exist?).with(@path).returns(true) @parser = mock 'parser' @parser.expects(:parse).returns({}) Puppet::FileServing::Configuration::Parser.stubs(:new).returns(@parser) @@ -47,7 +47,7 @@ describe Puppet::FileServing::Configuration do describe "when parsing the configuration file" do before do - Puppet::FileSystem::File.stubs(:exist?).with(@path).returns(true) + Puppet::FileSystem.stubs(:exist?).with(@path).returns(true) @parser = mock 'parser' Puppet::FileServing::Configuration::Parser.stubs(:new).returns(@parser) end @@ -84,14 +84,14 @@ describe Puppet::FileServing::Configuration do end it "should add modules and plugins mounts even if the file does not exist" do - Puppet::FileSystem::File.expects(:exist?).returns false # the file doesn't exist + Puppet::FileSystem.expects(:exist?).returns false # the file doesn't exist config = Puppet::FileServing::Configuration.configuration config.mounted?("modules").should be_true config.mounted?("plugins").should be_true end it "should allow all access to modules and plugins if no fileserver.conf exists" do - Puppet::FileSystem::File.expects(:exist?).returns false # the file doesn't exist + Puppet::FileSystem.expects(:exist?).returns false # the file doesn't exist modules = stub 'modules', :empty? => true Puppet::FileServing::Mount::Modules.stubs(:new).returns(modules) modules.expects(:allow).with('*') @@ -104,7 +104,7 @@ describe Puppet::FileServing::Configuration do end it "should not allow access from all to modules and plugins if the fileserver.conf provided some rules" do - Puppet::FileSystem::File.expects(:exist?).returns false # the file doesn't exist + Puppet::FileSystem.expects(:exist?).returns false # the file doesn't exist modules = stub 'modules', :empty? => false Puppet::FileServing::Mount::Modules.stubs(:new).returns(modules) @@ -119,7 +119,7 @@ describe Puppet::FileServing::Configuration do it "should add modules and plugins mounts even if they are not returned by the parser" do @parser.expects(:parse).returns("one" => mock("mount")) - Puppet::FileSystem::File.expects(:exist?).returns true # the file doesn't exist + Puppet::FileSystem.expects(:exist?).returns true # the file doesn't exist config = Puppet::FileServing::Configuration.configuration config.mounted?("modules").should be_true config.mounted?("plugins").should be_true diff --git a/spec/unit/file_serving/content_spec.rb b/spec/unit/file_serving/content_spec.rb index 2cb159f0a..1e9bb4a3a 100755 --- a/spec/unit/file_serving/content_spec.rb +++ b/spec/unit/file_serving/content_spec.rb @@ -27,7 +27,7 @@ describe Puppet::FileServing::Content do result = "foo" stub_file = stub(path, :lstat => stub('stat', :ftype => "file")) - Puppet::FileSystem::File.expects(:new).with(path).returns stub_file + Puppet::FileSystem.expects(:new).with(path).returns stub_file File.expects(:read).with(path).never content.collect @@ -39,7 +39,7 @@ describe Puppet::FileServing::Content do result = "foo" stub_file = stub(path, :lstat => stub('stat', :ftype => "directory")) - Puppet::FileSystem::File.expects(:new).with(path).returns stub_file + Puppet::FileSystem.expects(:new).with(path).returns stub_file File.expects(:read).with(path).never content.collect @@ -84,6 +84,7 @@ describe Puppet::FileServing::Content, "when returning the contents" do let(:content) { Puppet::FileServing::Content.new(path, :links => :follow) } it "should fail if the file is a symlink and links are set to :manage" do + pending "HELP FIX MOCKING CRAP" content.links = :manage stub_file = stub(path, :lstat => stub("stat", :ftype => "symlink")) Puppet::FileSystem::File.expects(:new).with(path).returns stub_file @@ -100,6 +101,7 @@ describe Puppet::FileServing::Content, "when returning the contents" do end it "should return the contents of the path if the file exists" do + pending "HELP FIX MOCKING CRAP" mocked_file = mock(path, :stat => stub('stat', :ftype => 'file')) Puppet::FileSystem::File.expects(:new).with(path).twice.returns(mocked_file) mocked_file.expects(:binread).returns(:mycontent) @@ -107,6 +109,7 @@ describe Puppet::FileServing::Content, "when returning the contents" do end it "should cache the returned contents" do + pending "HELP FIX MOCKING CRAP" mocked_file = mock(path, :stat => stub('stat', :ftype => 'file')) Puppet::FileSystem::File.expects(:new).with(path).twice.returns(mocked_file) mocked_file.expects(:binread).returns(:mycontent) diff --git a/spec/unit/file_serving/mount/file_spec.rb b/spec/unit/file_serving/mount/file_spec.rb index 5821f3f98..423e82bd6 100755 --- a/spec/unit/file_serving/mount/file_spec.rb +++ b/spec/unit/file_serving/mount/file_spec.rb @@ -85,7 +85,7 @@ describe Puppet::FileServing::Mount::File do include FileServingMountTesting before do - Puppet::FileSystem::File.stubs(:exist?).returns(true) + Puppet::FileSystem.stubs(:exist?).returns(true) FileTest.stubs(:directory?).returns(true) FileTest.stubs(:readable?).returns(true) @mount = Puppet::FileServing::Mount::File.new("test") @@ -95,12 +95,12 @@ describe Puppet::FileServing::Mount::File do end it "should return nil if the file is absent" do - Puppet::FileSystem::File.stubs(:exist?).returns(false) + Puppet::FileSystem.stubs(:exist?).returns(false) @mount.complete_path("/my/path", nil).should be_nil end it "should write a log message if the file is absent" do - Puppet::FileSystem::File.stubs(:exist?).returns(false) + Puppet::FileSystem.stubs(:exist?).returns(false) Puppet.expects(:info).with("File does not exist or is not accessible: /mount/my/path") @@ -108,12 +108,12 @@ describe Puppet::FileServing::Mount::File do end it "should return the file path if the file is present" do - Puppet::FileSystem::File.stubs(:exist?).with("/my/path").returns(true) + Puppet::FileSystem.stubs(:exist?).with("/my/path").returns(true) @mount.complete_path("/my/path", nil).should == "/mount/my/path" end it "should treat a nil file name as the path to the mount itself" do - Puppet::FileSystem::File.stubs(:exist?).returns(true) + Puppet::FileSystem.stubs(:exist?).returns(true) @mount.complete_path(nil, nil).should == "/mount" end @@ -153,7 +153,7 @@ describe Puppet::FileServing::Mount::File do end it "should return the results of the complete file path" do - Puppet::FileSystem::File.stubs(:exist?).returns(false) + Puppet::FileSystem.stubs(:exist?).returns(false) @mount.expects(:complete_path).with("/my/path", "foo").returns "eh" @mount.find("/my/path", @request).should == "eh" end @@ -163,7 +163,7 @@ describe Puppet::FileServing::Mount::File do include FileServingMountTesting before do - Puppet::FileSystem::File.stubs(:exist?).returns(true) + Puppet::FileSystem.stubs(:exist?).returns(true) FileTest.stubs(:directory?).returns(true) FileTest.stubs(:readable?).returns(true) @mount = Puppet::FileServing::Mount::File.new("test") @@ -175,13 +175,13 @@ describe Puppet::FileServing::Mount::File do end it "should return the results of the complete file path as an array" do - Puppet::FileSystem::File.stubs(:exist?).returns(false) + Puppet::FileSystem.stubs(:exist?).returns(false) @mount.expects(:complete_path).with("/my/path", "foo").returns "eh" @mount.search("/my/path", @request).should == ["eh"] end it "should return nil if the complete path is nil" do - Puppet::FileSystem::File.stubs(:exist?).returns(false) + Puppet::FileSystem.stubs(:exist?).returns(false) @mount.expects(:complete_path).with("/my/path", "foo").returns nil @mount.search("/my/path", @request).should be_nil end From 4592401084335cfff669c9a03f462e44ca1f89c6 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 4 Jan 2014 00:53:36 +0100 Subject: [PATCH 350/800] (PUP-716) Change more tests after refactoring This also fixes the spec_helper which had a reference to FileSystem::File --- lib/puppet/file_system.rb | 8 ++-- spec/spec_helper.rb | 2 +- spec/unit/file_serving/fileset_spec.rb | 56 +++++++++++--------------- 3 files changed, 28 insertions(+), 38 deletions(-) diff --git a/lib/puppet/file_system.rb b/lib/puppet/file_system.rb index 9f253a242..7e8761719 100644 --- a/lib/puppet/file_system.rb +++ b/lib/puppet/file_system.rb @@ -230,7 +230,7 @@ module Puppet::FileSystem # # @api public # - def stat(path) + def self.stat(path) @impl.stat(assert_path(path)) end @@ -238,7 +238,7 @@ module Puppet::FileSystem # # @api public # - def size(path) + def self.size(path) @impl.size(assert_path(path)) end @@ -247,7 +247,7 @@ module Puppet::FileSystem # # @api public # - def lstat(path) + def self.lstat(path) @impl.lstat(assert_path(path)) end @@ -258,7 +258,7 @@ module Puppet::FileSystem # # @api public # - def compare_stream(path, stream) + def self.compare_stream(path, stream) @impl.compare_stream(assert_path(path), stream) end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index a79feb54c..de1368951 100755 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -179,7 +179,7 @@ RSpec.configure do |config| # Clean up switch of TMPDIR, don't know if needed after this, so needs to reset it # to old before removing it ENV['TMPDIR'] = oldtmpdir - FileUtils.rm_rf(tmpdir) if Puppet::FileSystem::File.exist?(tmpdir) && tmpdir.to_s.start_with?(oldtmpdir) + FileUtils.rm_rf(tmpdir) if Puppet::FileSystem.exist?(tmpdir) && tmpdir.to_s.start_with?(oldtmpdir) end if ENV['PROFILE'] diff --git a/spec/unit/file_serving/fileset_spec.rb b/spec/unit/file_serving/fileset_spec.rb index 0e61a2a59..b0d0e448c 100755 --- a/spec/unit/file_serving/fileset_spec.rb +++ b/spec/unit/file_serving/fileset_spec.rb @@ -18,58 +18,49 @@ describe Puppet::FileServing::Fileset do it "removes a trailing file path separator" do path_with_separator = "#{somefile}#{File::SEPARATOR}" - stub_file = stub(somefile, :lstat => stub('stat')) - Puppet::FileSystem::File.expects(:new).with(somefile).returns stub_file + Puppet::FileSystem.expects(:lstat).with(somefile).returns stub('stat') fileset = Puppet::FileServing::Fileset.new(path_with_separator) fileset.path.should == somefile end it "can be created from the root directory" do path = File.expand_path(File::SEPARATOR) - stub_file = stub(path, :lstat => stub('stat')) - Puppet::FileSystem::File.expects(:new).with(path).returns stub_file + Puppet::FileSystem.expects(:lstat).with(path).returns stub('stat') fileset = Puppet::FileServing::Fileset.new(path) fileset.path.should == path end it "fails if its path does not exist" do - mock_file = mock(somefile) - Puppet::FileSystem::File.expects(:new).with(somefile).returns mock_file - mock_file.expects(:lstat).raises(Errno::ENOENT) + Puppet::FileSystem.expects(:lstat).with(somefile).raises(Errno::ENOENT) expect { Puppet::FileServing::Fileset.new(somefile) }.to raise_error(ArgumentError, "Fileset paths must exist") end it "accepts a 'recurse' option" do - stub_file = stub(somefile, :lstat => stub('stat')) - Puppet::FileSystem::File.expects(:new).with(somefile).returns stub_file + Puppet::FileSystem.expects(:lstat).with(somefile).returns stub('stat') set = Puppet::FileServing::Fileset.new(somefile, :recurse => true) set.recurse.should be_true end it "accepts a 'recurselimit' option" do - stub_file = stub(somefile, :lstat => stub('stat')) - Puppet::FileSystem::File.expects(:new).with(somefile).returns stub_file + Puppet::FileSystem.expects(:lstat).with(somefile).returns stub('stat') set = Puppet::FileServing::Fileset.new(somefile, :recurselimit => 3) set.recurselimit.should == 3 end it "accepts an 'ignore' option" do - stub_file = stub(somefile, :lstat => stub('stat')) - Puppet::FileSystem::File.expects(:new).with(somefile).returns stub_file + Puppet::FileSystem.expects(:lstat).with(somefile).returns stub('stat') set = Puppet::FileServing::Fileset.new(somefile, :ignore => ".svn") set.ignore.should == [".svn"] end it "accepts a 'links' option" do - stub_file = stub(somefile, :lstat => stub('stat')) - Puppet::FileSystem::File.expects(:new).with(somefile).returns stub_file + Puppet::FileSystem.expects(:lstat).with(somefile).returns stub('stat') set = Puppet::FileServing::Fileset.new(somefile, :links => :manage) set.links.should == :manage end it "accepts a 'checksum_type' option" do - stub_file = stub(somefile, :lstat => stub('stat')) - Puppet::FileSystem::File.expects(:new).with(somefile).returns stub_file + Puppet::FileSystem.expects(:lstat).with(somefile).returns stub('stat') set = Puppet::FileServing::Fileset.new(somefile, :checksum_type => :test) set.checksum_type.should == :test end @@ -79,26 +70,22 @@ describe Puppet::FileServing::Fileset do end it "defaults to 'false' for recurse" do - stub_file = stub(somefile, :lstat => stub('stat')) - Puppet::FileSystem::File.expects(:new).with(somefile).returns stub_file + Puppet::FileSystem.expects(:lstat).with(somefile).returns stub('stat') Puppet::FileServing::Fileset.new(somefile).recurse.should == false end it "defaults to :infinite for recurselimit" do - stub_file = stub(somefile, :lstat => stub('stat')) - Puppet::FileSystem::File.expects(:new).with(somefile).returns stub_file + Puppet::FileSystem.expects(:lstat).with(somefile).returns stub('stat') Puppet::FileServing::Fileset.new(somefile).recurselimit.should == :infinite end it "defaults to an empty ignore list" do - stub_file = stub(somefile, :lstat => stub('stat')) - Puppet::FileSystem::File.expects(:new).with(somefile).returns stub_file + Puppet::FileSystem.expects(:lstat).with(somefile).returns stub('stat') Puppet::FileServing::Fileset.new(somefile).ignore.should == [] end it "defaults to :manage for links" do - stub_file = stub(somefile, :lstat => stub('stat')) - Puppet::FileSystem::File.expects(:new).with(somefile).returns stub_file + Puppet::FileSystem.expects(:lstat).with(somefile).returns stub('stat') Puppet::FileServing::Fileset.new(somefile).links.should == :manage end @@ -107,7 +94,7 @@ describe Puppet::FileServing::Fileset do let(:stub_file) { stub(somefile, :lstat => stub('stat')) } before :each do - Puppet::FileSystem::File.expects(:new).with(somefile).returns stub_file + Puppet::FileSystem.expects(:lstat).with(somefile).returns stub('stat') end [:recurse, :recurselimit, :ignore, :links].each do |option| @@ -144,8 +131,10 @@ describe Puppet::FileServing::Fileset do context "when recursing" do before do @path = make_absolute("/my/path") - @stub_file = stub(@path, :lstat => stub('stat', :directory? => true)) - Puppet::FileSystem::File.stubs(:new).with(@path).returns @stub_file +# @stub_file = stub(@path, :lstat => stub('stat', :directory? => true)) +# Puppet::FileSystem::File.stubs(:new).with(@path).returns @stub_file + Puppet::FileSystem.expects(:lstat).with(@path).returns stub('stat', :directory? => true) + @fileset = Puppet::FileServing::Fileset.new(@path) @dirstat = stub 'dirstat', :directory? => true @@ -153,7 +142,8 @@ describe Puppet::FileServing::Fileset do end def mock_dir_structure(path, stat_method = :lstat) - @stub_file.stubs(stat_method).returns(@dirstat) + Puppet::FileSystem.expects(stat_method).with(@path).returns @dirstat +# @stub_file.stubs(stat_method).returns(@dirstat) Dir.stubs(:entries).with(path).returns(%w{one two .svn CVS}) # Keep track of the files we're stubbing. @@ -162,14 +152,14 @@ describe Puppet::FileServing::Fileset do %w{one two .svn CVS}.each do |subdir| @files << subdir # relative path subpath = File.join(path, subdir) - stub_subpath = stub(subpath, stat_method => @dirstat) - Puppet::FileSystem::File.stubs(:new).with(subpath).returns stub_subpath +# stub_subpath = stub(subpath, stat_method => @dirstat) + Puppet::FileSystem.stubs(stat_method).with(subpath).returns @dirstat Dir.stubs(:entries).with(subpath).returns(%w{.svn CVS file1 file2}) %w{file1 file2 .svn CVS}.each do |file| @files << File.join(subdir, file) # relative path subfile_path = File.join(subpath, file) - stub_subfile_path = stub(subfile_path, stat_method => @filestat) - Puppet::FileSystem::File.stubs(:new).with(subfile_path).returns stub_subfile_path +# stub_subfile_path = stub(subfile_path, stat_method => @filestat) + Puppet::FileSystem.stubs(stat_method).with(subfile_path).returns(@filestat) end end end From b5d8439e44dc4d6d770b60d847e00f87f0c76a66 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 4 Jan 2014 01:33:11 +0100 Subject: [PATCH 351/800] (PUP-716) Make more tests in fileset_spec pass --- spec/unit/file_serving/fileset_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/unit/file_serving/fileset_spec.rb b/spec/unit/file_serving/fileset_spec.rb index b0d0e448c..d0616df00 100755 --- a/spec/unit/file_serving/fileset_spec.rb +++ b/spec/unit/file_serving/fileset_spec.rb @@ -188,8 +188,8 @@ describe Puppet::FileServing::Fileset do def mock(base_path) extend Mocha::API path = File.join(base_path, name) - stub_file = stub(path, :lstat => MockStat.new(path, false)) - Puppet::FileSystem::File.stubs(:new).with(path).returns stub_file +# stub_file = stub(path, :lstat => MockStat.new(path, false)) + Puppet::FileSystem.stubs(:lstat).with(path).returns MockStat.new(path, false) end end @@ -269,7 +269,7 @@ describe Puppet::FileServing::Fileset do @path = make_absolute("/my/path/rV1x2DafFr0R6tGG+1bbk++++TM") stat = stub('dir_stat', :directory? => true) stub_file = stub(@path, :stat => stat, :lstat => stat) - Puppet::FileSystem::File.expects(:new).with(@path).twice.returns stub_file + Puppet::FileSystem.expects(:lstat).with(@path).returns stub(@path, :stat => stat, :lstat => stat) @fileset = Puppet::FileServing::Fileset.new(@path) mock_dir_structure(@path) @fileset.recurse = true From 3c67f61dcde8087785b45165213919f702269c77 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 3 Jan 2014 16:45:22 -0800 Subject: [PATCH 352/800] (PUP-716) Fix up YAML specs --- lib/puppet/file_system.rb | 2 +- lib/puppet/provider/package/pacman.rb | 2 +- lib/puppet/util.rb | 2 +- spec/unit/util/yaml_spec.rb | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/puppet/file_system.rb b/lib/puppet/file_system.rb index 7e8761719..1a1a6e7f9 100644 --- a/lib/puppet/file_system.rb +++ b/lib/puppet/file_system.rb @@ -67,7 +67,7 @@ module Puppet::FileSystem # # @api public # - def self.basename(path) + def self.size(path) @impl.size(assert_path(path.basename)) end diff --git a/lib/puppet/provider/package/pacman.rb b/lib/puppet/provider/package/pacman.rb index 3bc87635d..d3fcdea66 100644 --- a/lib/puppet/provider/package/pacman.rb +++ b/lib/puppet/provider/package/pacman.rb @@ -6,7 +6,7 @@ Puppet::Type.type(:package).provide :pacman, :parent => Puppet::Provider::Packag commands :pacman => "/usr/bin/pacman" # Yaourt is a common AUR helper which, if installed, we can use to query the AUR - commands :yaourt => "/usr/bin/yaourt" if Puppet::FileSystem::File.exist? '/usr/bin/yaourt' + commands :yaourt => "/usr/bin/yaourt" if Puppet::FileSystem.exist? '/usr/bin/yaourt' confine :operatingsystem => :archlinux defaultfor :operatingsystem => :archlinux diff --git a/lib/puppet/util.rb b/lib/puppet/util.rb index 4ecdd60c0..583a92f95 100644 --- a/lib/puppet/util.rb +++ b/lib/puppet/util.rb @@ -397,7 +397,7 @@ module Util end file = Puppet::FileSystem.pathname(file) - tempfile = Tempfile.new(file.basename, Puppet::FileSystem.path_string(Puppet::FileSystem.dir(file))) + tempfile = Tempfile.new(Puppet::FileSystem.path_string(Puppet::FileSystem.basename(file)), Puppet::FileSystem.path_string(Puppet::FileSystem.dir(file))) # Set properties of the temporary file before we write the content, because # Tempfile doesn't promise to be safe from reading by other people, just diff --git a/spec/unit/util/yaml_spec.rb b/spec/unit/util/yaml_spec.rb index 978afb0a2..a535d79c7 100644 --- a/spec/unit/util/yaml_spec.rb +++ b/spec/unit/util/yaml_spec.rb @@ -35,13 +35,13 @@ describe Puppet::Util::Yaml do context "when the file is empty" do it "returns false" do - Puppet::FileSystem::File.new(filename).touch + Puppet::FileSystem.touch(filename) expect(Puppet::Util::Yaml.load_file(filename)).to be_false end it "allows return value to be overridden" do - Puppet::FileSystem::File.new(filename).touch + Puppet::FileSystem.touch(filename) expect(Puppet::Util::Yaml.load_file(filename, {})).to eq({}) end From 268483c4d5310e4beed8d99c10bd77f862bf2797 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 4 Jan 2014 02:23:32 +0100 Subject: [PATCH 353/800] (PUP-716) Fix up fileset_spec (1 failing test remains) One test is still failing. --- spec/unit/file_serving/fileset_spec.rb | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/spec/unit/file_serving/fileset_spec.rb b/spec/unit/file_serving/fileset_spec.rb index d0616df00..ff90e4ce4 100755 --- a/spec/unit/file_serving/fileset_spec.rb +++ b/spec/unit/file_serving/fileset_spec.rb @@ -131,9 +131,7 @@ describe Puppet::FileServing::Fileset do context "when recursing" do before do @path = make_absolute("/my/path") -# @stub_file = stub(@path, :lstat => stub('stat', :directory? => true)) -# Puppet::FileSystem::File.stubs(:new).with(@path).returns @stub_file - Puppet::FileSystem.expects(:lstat).with(@path).returns stub('stat', :directory? => true) + Puppet::FileSystem.stubs(:lstat).with(@path).returns stub('stat', :directory? => true) @fileset = Puppet::FileServing::Fileset.new(@path) @@ -143,7 +141,6 @@ describe Puppet::FileServing::Fileset do def mock_dir_structure(path, stat_method = :lstat) Puppet::FileSystem.expects(stat_method).with(@path).returns @dirstat -# @stub_file.stubs(stat_method).returns(@dirstat) Dir.stubs(:entries).with(path).returns(%w{one two .svn CVS}) # Keep track of the files we're stubbing. @@ -152,13 +149,11 @@ describe Puppet::FileServing::Fileset do %w{one two .svn CVS}.each do |subdir| @files << subdir # relative path subpath = File.join(path, subdir) -# stub_subpath = stub(subpath, stat_method => @dirstat) Puppet::FileSystem.stubs(stat_method).with(subpath).returns @dirstat Dir.stubs(:entries).with(subpath).returns(%w{.svn CVS file1 file2}) %w{file1 file2 .svn CVS}.each do |file| @files << File.join(subdir, file) # relative path subfile_path = File.join(subpath, file) -# stub_subfile_path = stub(subfile_path, stat_method => @filestat) Puppet::FileSystem.stubs(stat_method).with(subfile_path).returns(@filestat) end end @@ -175,8 +170,7 @@ describe Puppet::FileServing::Fileset do def mock(base_path) extend Mocha::API path = File.join(base_path, name) - stub_dir = stub(path, :lstat => MockStat.new(path, true)) - Puppet::FileSystem::File.stubs(:new).with(path).returns stub_dir + Puppet::FileSystem.stubs(:lstat).with(path).returns MockStat.new(path, true) Dir.stubs(:entries).with(path).returns(['.', '..'] + entries.map(&:name)) entries.each do |entry| entry.mock(path) @@ -188,7 +182,6 @@ describe Puppet::FileServing::Fileset do def mock(base_path) extend Mocha::API path = File.join(base_path, name) -# stub_file = stub(path, :lstat => MockStat.new(path, false)) Puppet::FileSystem.stubs(:lstat).with(path).returns MockStat.new(path, false) end end @@ -281,13 +274,11 @@ describe Puppet::FileServing::Fileset do path = make_absolute("/my/path") stat = stub 'stat', :directory? => true - mock_file = mock(path, :lstat => stat, :stat => stat) - Puppet::FileSystem::File.expects(:new).with(path).twice.returns mock_file + Puppet::FileSystem.expects(:stat).with(path).returns stat + Puppet::FileSystem.expects(:lstat).with(path).returns stat link_path = File.join(path, "mylink") - mock_link = mock(link_path) - Puppet::FileSystem::File.expects(:new).with(link_path).returns mock_link - mock_link.expects(:stat).raises(Errno::ENOENT) + Puppet::FileSystem.expects(:stat).with(link_path).raises(Errno::ENOENT) Dir.stubs(:entries).with(path).returns(["mylink"]) @@ -302,12 +293,10 @@ describe Puppet::FileServing::Fileset do context "when merging other filesets" do before do @paths = [make_absolute("/first/path"), make_absolute("/second/path"), make_absolute("/third/path")] - stub_file = stub(:lstat => stub('stat', :directory? => false)) - Puppet::FileSystem::File.stubs(:new).returns stub_file + Puppet::FileSystem.stubs(:lstat).returns stub('stat', :directory? => false) @filesets = @paths.collect do |path| - stub_dir = stub(path, :lstat => stub('stat', :directory? => true)) - Puppet::FileSystem::File.stubs(:new).with(path).returns stub_dir + Puppet::FileSystem.stubs(:lstat).with(path).returns stub('stat', :directory? => true) Puppet::FileServing::Fileset.new(path, :recurse => true) end From 0e02e6bbaafd3aa801bfaad462fc36688b6caec8 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 4 Jan 2014 02:34:39 +0100 Subject: [PATCH 354/800] (PUP-716) Fix up content_spec.rb --- spec/unit/file_serving/content_spec.rb | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/spec/unit/file_serving/content_spec.rb b/spec/unit/file_serving/content_spec.rb index 1e9bb4a3a..168ef9292 100755 --- a/spec/unit/file_serving/content_spec.rb +++ b/spec/unit/file_serving/content_spec.rb @@ -26,8 +26,7 @@ describe Puppet::FileServing::Content do content = Puppet::FileServing::Content.new(path) result = "foo" - stub_file = stub(path, :lstat => stub('stat', :ftype => "file")) - Puppet::FileSystem.expects(:new).with(path).returns stub_file + Puppet::FileSystem.expects(:lstat).with(path).returns stub('stat', :ftype => "file") File.expects(:read).with(path).never content.collect @@ -38,8 +37,7 @@ describe Puppet::FileServing::Content do content = Puppet::FileServing::Content.new(path) result = "foo" - stub_file = stub(path, :lstat => stub('stat', :ftype => "directory")) - Puppet::FileSystem.expects(:new).with(path).returns stub_file + Puppet::FileSystem.expects(:lstat).with(path).returns stub('stat', :ftype => "directory") File.expects(:read).with(path).never content.collect @@ -84,10 +82,8 @@ describe Puppet::FileServing::Content, "when returning the contents" do let(:content) { Puppet::FileServing::Content.new(path, :links => :follow) } it "should fail if the file is a symlink and links are set to :manage" do - pending "HELP FIX MOCKING CRAP" content.links = :manage - stub_file = stub(path, :lstat => stub("stat", :ftype => "symlink")) - Puppet::FileSystem::File.expects(:new).with(path).returns stub_file + Puppet::FileSystem.expects(:lstat).with(path).returns stub("stat", :ftype => "symlink") proc { content.content }.should raise_error(ArgumentError) end @@ -101,18 +97,14 @@ describe Puppet::FileServing::Content, "when returning the contents" do end it "should return the contents of the path if the file exists" do - pending "HELP FIX MOCKING CRAP" - mocked_file = mock(path, :stat => stub('stat', :ftype => 'file')) - Puppet::FileSystem::File.expects(:new).with(path).twice.returns(mocked_file) - mocked_file.expects(:binread).returns(:mycontent) + Puppet::FileSystem.expects(:stat).with(path).returns(stub('stat', :ftype => 'file')) + Puppet::FileSystem.expects(:binread).with(path).returns(:mycontent) content.content.should == :mycontent end it "should cache the returned contents" do - pending "HELP FIX MOCKING CRAP" - mocked_file = mock(path, :stat => stub('stat', :ftype => 'file')) - Puppet::FileSystem::File.expects(:new).with(path).twice.returns(mocked_file) - mocked_file.expects(:binread).returns(:mycontent) + Puppet::FileSystem.expects(:stat).with(path).returns(stub('stat', :ftype => 'file')) + Puppet::FileSystem.expects(:binread).with(path).returns(:mycontent) content.content # The second run would throw a failure if the content weren't being cached. content.content From 2699ca975fb2aa3ec0697897eb5794c80ed9afe7 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 3 Jan 2014 16:50:23 -0800 Subject: [PATCH 355/800] (PUP-716) Fix up watcher spec --- spec/unit/util/watcher_spec.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/spec/unit/util/watcher_spec.rb b/spec/unit/util/watcher_spec.rb index 33f75ab12..83439c246 100644 --- a/spec/unit/util/watcher_spec.rb +++ b/spec/unit/util/watcher_spec.rb @@ -14,10 +14,7 @@ describe Puppet::Util::Watcher do let(:filename) { "fake" } def after_reading_the_sequence(initial, *results) - mock_file = mock(filename) - Puppet::FileSystem::File.expects(:new).with(filename).at_least(1).returns mock_file - - expectation = mock_file.stubs(:stat) + expectation = Puppet::FileSystem.expects(:stat).with(filename).at_least(1) ([initial] + results).each do |result| expectation = if result.is_a? Class expectation.raises(result) From b4b18e1f04dea2add0d9a52b12a00c7d395839a4 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 3 Jan 2014 17:07:59 -0800 Subject: [PATCH 356/800] (PUP-716) Fix up all of util --- lib/puppet/util/network_device/config.rb | 2 +- spec/unit/util/autoload_spec.rb | 28 ++++++------- spec/unit/util/backups_spec.rb | 53 +++++++++++------------- spec/unit/util/checksums_spec.rb | 4 +- spec/unit/util/execution_spec.rb | 2 +- spec/unit/util/filetype_spec.rb | 14 +++---- spec/unit/util/lockfile_spec.rb | 4 +- spec/unit/util/pidlock_spec.rb | 12 +++--- spec/unit/util/resource_template_spec.rb | 6 +-- spec/unit/util/selinux_spec.rb | 8 ++-- spec/unit/util/storage_spec.rb | 8 ++-- 11 files changed, 68 insertions(+), 73 deletions(-) diff --git a/lib/puppet/util/network_device/config.rb b/lib/puppet/util/network_device/config.rb index cba6009ab..2d6283c64 100644 --- a/lib/puppet/util/network_device/config.rb +++ b/lib/puppet/util/network_device/config.rb @@ -15,7 +15,7 @@ class Puppet::Util::NetworkDevice::Config attr_reader :devices def exists? - Puppet::FileSystem.exist?(@file) + Puppet::FileSystem.exist?(@file.to_str) end def initialize diff --git a/spec/unit/util/autoload_spec.rb b/spec/unit/util/autoload_spec.rb index 2f8b5353f..68f4e2e61 100755 --- a/spec/unit/util/autoload_spec.rb +++ b/spec/unit/util/autoload_spec.rb @@ -79,7 +79,7 @@ describe Puppet::Util::Autoload do [RuntimeError, LoadError, SyntaxError].each do |error| it "should die with Puppet::Error if a #{error.to_s} exception is thrown" do - Puppet::FileSystem::File.stubs(:exist?).returns true + Puppet::FileSystem.stubs(:exist?).returns true Kernel.expects(:load).raises error @@ -92,7 +92,7 @@ describe Puppet::Util::Autoload do end it "should register loaded files with the autoloader" do - Puppet::FileSystem::File.stubs(:exist?).returns true + Puppet::FileSystem.stubs(:exist?).returns true Kernel.stubs(:load) @autoload.load("myfile") @@ -102,7 +102,7 @@ describe Puppet::Util::Autoload do end it "should be seen by loaded? on the instance using the short name" do - Puppet::FileSystem::File.stubs(:exist?).returns true + Puppet::FileSystem.stubs(:exist?).returns true Kernel.stubs(:load) @autoload.load("myfile") @@ -112,7 +112,7 @@ describe Puppet::Util::Autoload do end it "should register loaded files with the main loaded file list so they are not reloaded by ruby" do - Puppet::FileSystem::File.stubs(:exist?).returns true + Puppet::FileSystem.stubs(:exist?).returns true Kernel.stubs(:load) @autoload.load("myfile") @@ -125,7 +125,7 @@ describe Puppet::Util::Autoload do it "should load the first file in the searchpath" do @autoload.stubs(:search_directories).returns [make_absolute("/a"), make_absolute("/b")] FileTest.stubs(:directory?).returns true - Puppet::FileSystem::File.stubs(:exist?).returns true + Puppet::FileSystem.stubs(:exist?).returns true Kernel.expects(:load).with(make_absolute("/a/tmp/myfile.rb"), optionally(anything)) @autoload.load("myfile") @@ -134,7 +134,7 @@ describe Puppet::Util::Autoload do end it "should treat equivalent paths to a loaded file as loaded" do - Puppet::FileSystem::File.stubs(:exist?).returns true + Puppet::FileSystem.stubs(:exist?).returns true Kernel.stubs(:load) @autoload.load("myfile") @@ -152,7 +152,7 @@ describe Puppet::Util::Autoload do @autoload.class.stubs(:search_directories).returns [make_absolute("/a")] FileTest.stubs(:directory?).returns true Dir.stubs(:glob).returns [make_absolute("/a/foo/file.rb")] - Puppet::FileSystem::File.stubs(:exist?).returns true + Puppet::FileSystem.stubs(:exist?).returns true @time_a = Time.utc(2010, 'jan', 1, 6, 30) File.stubs(:mtime).returns @time_a @@ -193,7 +193,7 @@ describe Puppet::Util::Autoload do it "changes should be seen by changed? on the instance using the short name" do File.stubs(:mtime).returns(@first_time) - Puppet::FileSystem::File.stubs(:exist?).returns true + Puppet::FileSystem.stubs(:exist?).returns true Kernel.stubs(:load) @autoload.load("myfile") @autoload.loaded?("myfile").should be @@ -214,14 +214,14 @@ describe Puppet::Util::Autoload do it "should reload if mtime changes" do File.stubs(:mtime).with(@file_a).returns(@first_time + 60) - Puppet::FileSystem::File.stubs(:exist?).with(@file_a).returns true + Puppet::FileSystem.stubs(:exist?).with(@file_a).returns true Kernel.expects(:load).with(@file_a, optionally(anything)) @autoload.class.reload_changed end it "should do nothing if the file is deleted" do File.stubs(:mtime).with(@file_a).raises(Errno::ENOENT) - Puppet::FileSystem::File.stubs(:exist?).with(@file_a).returns false + Puppet::FileSystem.stubs(:exist?).with(@file_a).returns false Kernel.expects(:load).never @autoload.class.reload_changed end @@ -236,8 +236,8 @@ describe Puppet::Util::Autoload do File.expects(:mtime).with(@file_a).returns(@first_time) @autoload.class.mark_loaded("file", @file_a) File.stubs(:mtime).with(@file_a).raises(Errno::ENOENT) - Puppet::FileSystem::File.stubs(:exist?).with(@file_a).returns false - Puppet::FileSystem::File.stubs(:exist?).with(@file_b).returns true + Puppet::FileSystem.stubs(:exist?).with(@file_a).returns false + Puppet::FileSystem.stubs(:exist?).with(@file_b).returns true File.stubs(:mtime).with(@file_b).returns @first_time Kernel.expects(:load).with(@file_b, optionally(anything)) @autoload.class.reload_changed @@ -246,11 +246,11 @@ describe Puppet::Util::Autoload do it "should load a/file when b/file is loaded and a/file is created" do File.stubs(:mtime).with(@file_b).returns @first_time - Puppet::FileSystem::File.stubs(:exist?).with(@file_b).returns true + Puppet::FileSystem.stubs(:exist?).with(@file_b).returns true @autoload.class.mark_loaded("file", @file_b) File.stubs(:mtime).with(@file_a).returns @first_time - Puppet::FileSystem::File.stubs(:exist?).with(@file_a).returns true + Puppet::FileSystem.stubs(:exist?).with(@file_a).returns true Kernel.expects(:load).with(@file_a, optionally(anything)) @autoload.class.reload_changed @autoload.class.send(:loaded)["file"].should == [@file_a, @first_time] diff --git a/spec/unit/util/backups_spec.rb b/spec/unit/util/backups_spec.rb index 654ddb788..ce7b9b756 100755 --- a/spec/unit/util/backups_spec.rb +++ b/spec/unit/util/backups_spec.rb @@ -20,7 +20,7 @@ describe Puppet::Util::Backups do file = Puppet::Type.type(:file).new(:name => path) file.expects(:bucket).never - Puppet::FileSystem::File.expects(:exist?).with(path).returns false + Puppet::FileSystem.expects(:exist?).with(path).returns false file.perform_backup end @@ -29,25 +29,23 @@ describe Puppet::Util::Backups do file = Puppet::Type.type(:file).new(:name => path, :backup => false) file.expects(:bucket).never - Puppet::FileSystem::File.expects(:exist?).never + Puppet::FileSystem.expects(:exist?).never file.perform_backup end it "a bucket should be used when provided" do - stub_file = stub(path, :lstat => mock('lstat', :ftype => 'file')) - Puppet::FileSystem::File.expects(:new).with(path).returns stub_file + lstat_path_as(path, 'file') bucket.expects(:backup).with(path).returns("mysum") - Puppet::FileSystem::File.expects(:exist?).with(path).returns(true) + Puppet::FileSystem.expects(:exist?).with(path).returns(true) file.perform_backup end it "should propagate any exceptions encountered when backing up to a filebucket" do - stub_file = stub(path, :lstat => mock('lstat', :ftype => 'file')) - Puppet::FileSystem::File.expects(:new).with(path).returns stub_file + lstat_path_as(path, 'file') bucket.expects(:backup).raises ArgumentError - Puppet::FileSystem::File.expects(:exist?).with(path).returns(true) + Puppet::FileSystem.expects(:exist?).with(path).returns(true) lambda { file.perform_backup }.should raise_error(ArgumentError) end @@ -58,39 +56,35 @@ describe Puppet::Util::Backups do let(:file) { Puppet::Type.type(:file).new(:name => path, :backup => '.'+ext) } it "should remove any local backup if one exists" do - stub_file = stub(backup, :lstat => stub('stat', :ftype => 'file')) - Puppet::FileSystem::File.expects(:new).with(backup).returns stub_file - Puppet::FileSystem::File.expects(:unlink).with(backup) + lstat_path_as(backup, 'file') + Puppet::FileSystem.expects(:unlink).with(backup) FileUtils.stubs(:cp_r) - Puppet::FileSystem::File.expects(:exist?).with(path).returns(true) + Puppet::FileSystem.expects(:exist?).with(path).returns(true) file.perform_backup end it "should fail when the old backup can't be removed" do - stub_file = stub(backup, :lstat => stub('stat', :ftype => 'file')) - Puppet::FileSystem::File.expects(:new).with(backup).returns stub_file - Puppet::FileSystem::File.expects(:unlink).with(backup).raises ArgumentError + lstat_path_as(backup, 'file') + Puppet::FileSystem.expects(:unlink).with(backup).raises ArgumentError FileUtils.expects(:cp_r).never - Puppet::FileSystem::File.expects(:exist?).with(path).returns(true) + Puppet::FileSystem.expects(:exist?).with(path).returns(true) lambda { file.perform_backup }.should raise_error(Puppet::Error) end it "should not try to remove backups that don't exist" do - stub_file = stub(backup) - Puppet::FileSystem::File.expects(:new).with(backup).returns stub_file - stub_file.expects(:lstat).raises(Errno::ENOENT) - Puppet::FileSystem::File.expects(:unlink).with(backup).never + Puppet::FileSystem.expects(:lstat).with(backup).raises(Errno::ENOENT) + Puppet::FileSystem.expects(:unlink).with(backup).never FileUtils.stubs(:cp_r) - Puppet::FileSystem::File.expects(:exist?).with(path).returns(true) + Puppet::FileSystem.expects(:exist?).with(path).returns(true) file.perform_backup end it "a copy should be created in the local directory" do FileUtils.expects(:cp_r).with(path, backup, :preserve => true) - Puppet::FileSystem::File.stubs(:exist?).with(path).returns(true) + Puppet::FileSystem.stubs(:exist?).with(path).returns(true) file.perform_backup.should be_true end @@ -98,7 +92,7 @@ describe Puppet::Util::Backups do it "should propagate exceptions if no backup can be created" do FileUtils.expects(:cp_r).raises ArgumentError - Puppet::FileSystem::File.stubs(:exist?).with(path).returns(true) + Puppet::FileSystem.stubs(:exist?).with(path).returns(true) lambda { file.perform_backup }.should raise_error(Puppet::Error) end end @@ -114,11 +108,10 @@ describe Puppet::Util::Backups do bucket.expects(:backup).with(filename).returns true - stub_file = stub(path, :lstat => stub('stat', :ftype => 'directory')) - Puppet::FileSystem::File.expects(:new).with(path).returns stub_file + lstat_path_as(path, 'directory') - Puppet::FileSystem::File.stubs(:exist?).with(path).returns(true) - Puppet::FileSystem::File.stubs(:exist?).with(filename).returns(true) + Puppet::FileSystem.stubs(:exist?).with(path).returns(true) + Puppet::FileSystem.stubs(:exist?).with(filename).returns(true) file.perform_backup end @@ -128,10 +121,14 @@ describe Puppet::Util::Backups do bucket.expects(:backup).never stub_file = stub('file', :stat => stub('stat', :ftype => 'directory')) - Puppet::FileSystem::File.stubs(:new).with(path).returns stub_file + Puppet::FileSystem.stubs(:new).with(path).returns stub_file Find.expects(:find).never file.perform_backup end end + + def lstat_path_as(path, ftype) + Puppet::FileSystem.expects(:lstat).with(path).returns(stub('File::Stat', :ftype => ftype)) + end end diff --git a/spec/unit/util/checksums_spec.rb b/spec/unit/util/checksums_spec.rb index 0adff7a9a..f4b85e402 100755 --- a/spec/unit/util/checksums_spec.rb +++ b/spec/unit/util/checksums_spec.rb @@ -131,9 +131,7 @@ describe Puppet::Util::Checksums do it "should use the '#{sum}' on the file to determine the ctime" do file = "/my/file" stat = mock 'stat', sum => "mysum" - - stub_file = stub(file, :stat => stat) - Puppet::FileSystem::File.expects(:new).with(file).returns stub_file + Puppet::FileSystem.expects(:stat).with(file).returns(stat) @summer.send(sum.to_s + "_file", file).should == "mysum" end diff --git a/spec/unit/util/execution_spec.rb b/spec/unit/util/execution_spec.rb index 7bb15cd75..7c6238f9f 100755 --- a/spec/unit/util/execution_spec.rb +++ b/spec/unit/util/execution_spec.rb @@ -529,7 +529,7 @@ describe Puppet::Util::Execution do Puppet::Util::Execution.execute('test command') - Puppet::FileSystem::File.exist?(path).should be_false + Puppet::FileSystem.exist?(path).should be_false end it "should not raise an error if the file is open" do diff --git a/spec/unit/util/filetype_spec.rb b/spec/unit/util/filetype_spec.rb index 5d8f0b36d..304b49352 100755 --- a/spec/unit/util/filetype_spec.rb +++ b/spec/unit/util/filetype_spec.rb @@ -16,15 +16,15 @@ describe Puppet::Util::FileType do describe "when the file already exists" do it "should return the file's contents when asked to read it" do - Puppet::FileSystem::File.expects(:exist?).with(path).returns true + Puppet::FileSystem.expects(:exist?).with(path).returns true File.expects(:read).with(path).returns "my text" file.read.should == "my text" end it "should unlink the file when asked to remove it" do - Puppet::FileSystem::File.expects(:exist?).with(path).returns true - Puppet::FileSystem::File.expects(:unlink).with(path) + Puppet::FileSystem.expects(:exist?).with(path).returns true + Puppet::FileSystem.expects(:unlink).with(path) file.remove end @@ -32,7 +32,7 @@ describe Puppet::Util::FileType do describe "when the file does not exist" do it "should return an empty string when asked to read the file" do - Puppet::FileSystem::File.expects(:exist?).with(path).returns false + Puppet::FileSystem.expects(:exist?).with(path).returns false file.read.should == "" end @@ -63,13 +63,13 @@ describe Puppet::Util::FileType do describe "when backing up a file" do it "should do nothing if the file does not exist" do - Puppet::FileSystem::File.expects(:exist?).with(path).returns false + Puppet::FileSystem.expects(:exist?).with(path).returns false file.expects(:bucket).never file.backup end it "should use its filebucket to backup the file if it exists" do - Puppet::FileSystem::File.expects(:exist?).with(path).returns true + Puppet::FileSystem.expects(:exist?).with(path).returns true bucket = mock 'bucket' bucket.expects(:backup).with(path) @@ -155,7 +155,7 @@ describe Puppet::Util::FileType do end after :each do - Puppet::FileSystem::File.exist?(@tmp_cron_path).should be_false + Puppet::FileSystem.exist?(@tmp_cron_path).should be_false end it "should run crontab as the target user on a temporary file" do diff --git a/spec/unit/util/lockfile_spec.rb b/spec/unit/util/lockfile_spec.rb index 8d10d5106..c203f0b06 100644 --- a/spec/unit/util/lockfile_spec.rb +++ b/spec/unit/util/lockfile_spec.rb @@ -25,7 +25,7 @@ describe Puppet::Util::Lockfile do it "should create a lock file" do @lock.lock - Puppet::FileSystem::File.exist?(@lockfile).should be_true + Puppet::FileSystem.exist?(@lockfile).should be_true end it "should create a lock file containing a string" do @@ -49,7 +49,7 @@ describe Puppet::Util::Lockfile do it "should clear the lock file" do File.open(@lockfile, 'w') { |fd| fd.print("locked") } @lock.unlock - Puppet::FileSystem::File.exist?(@lockfile).should be_false + Puppet::FileSystem.exist?(@lockfile).should be_false end end diff --git a/spec/unit/util/pidlock_spec.rb b/spec/unit/util/pidlock_spec.rb index e9c28f2b0..2ebe7dec8 100644 --- a/spec/unit/util/pidlock_spec.rb +++ b/spec/unit/util/pidlock_spec.rb @@ -47,7 +47,7 @@ describe Puppet::Util::Pidlock do it "should create a lock file" do @lock.lock - Puppet::FileSystem::File.exist?(@lockfile).should be_true + Puppet::FileSystem.exist?(@lockfile).should be_true end it "should expose the lock file_path" do @@ -74,7 +74,7 @@ describe Puppet::Util::Pidlock do it "should get rid of the lock file" do @lock.lock @lock.unlock - Puppet::FileSystem::File.exist?(@lockfile).should be_false + Puppet::FileSystem.exist?(@lockfile).should be_false end end @@ -106,12 +106,12 @@ describe Puppet::Util::Pidlock do describe "#lock" do it "should clear stale locks" do @lock.locked? - Puppet::FileSystem::File.exist?(@lockfile).should be_false + Puppet::FileSystem.exist?(@lockfile).should be_false end it "should replace with new locks" do @lock.lock - Puppet::FileSystem::File.exist?(@lockfile).should be_true + Puppet::FileSystem.exist?(@lockfile).should be_true @lock.lock_pid.should == 6789 @lock.should be_mine @lock.should be_locked @@ -125,7 +125,7 @@ describe Puppet::Util::Pidlock do it "should not remove the lock file" do @lock.unlock - Puppet::FileSystem::File.exist?(@lockfile).should be_true + Puppet::FileSystem.exist?(@lockfile).should be_true end end end @@ -170,7 +170,7 @@ describe Puppet::Util::Pidlock do it "should not remove the lock file" do @lock.unlock - Puppet::FileSystem::File.exist?(@lockfile).should be_true + Puppet::FileSystem.exist?(@lockfile).should be_true end it "should still not be our lock" do diff --git a/spec/unit/util/resource_template_spec.rb b/spec/unit/util/resource_template_spec.rb index 0e6037119..182a78ab0 100755 --- a/spec/unit/util/resource_template_spec.rb +++ b/spec/unit/util/resource_template_spec.rb @@ -6,20 +6,20 @@ require 'puppet/util/resource_template' describe Puppet::Util::ResourceTemplate do describe "when initializing" do it "should fail if the template does not exist" do - Puppet::FileSystem::File.expects(:exist?).with("/my/template").returns false + Puppet::FileSystem.expects(:exist?).with("/my/template").returns false lambda { Puppet::Util::ResourceTemplate.new("/my/template", mock('resource')) }.should raise_error(ArgumentError) end it "should not create the ERB template" do ERB.expects(:new).never - Puppet::FileSystem::File.expects(:exist?).with("/my/template").returns true + Puppet::FileSystem.expects(:exist?).with("/my/template").returns true Puppet::Util::ResourceTemplate.new("/my/template", mock('resource')) end end describe "when evaluating" do before do - Puppet::FileSystem::File.stubs(:exist?).returns true + Puppet::FileSystem.stubs(:exist?).returns true File.stubs(:read).returns "eh" @template = stub 'template', :result => nil diff --git a/spec/unit/util/selinux_spec.rb b/spec/unit/util/selinux_spec.rb index 1357b981a..eeb8ca5f7 100755 --- a/spec/unit/util/selinux_spec.rb +++ b/spec/unit/util/selinux_spec.rb @@ -123,10 +123,10 @@ describe Puppet::Util::SELinux do it "should return a context if a default context exists" do self.expects(:selinux_support?).returns true fstat = stub 'File::Stat', :mode => 0 - stub_file = stub('/foo', :lstat => fstat) - Puppet::FileSystem::File.expects(:new).with('/foo').returns stub_file + Puppet::FileSystem.expects(:lstat).with('/foo').returns(fstat) self.expects(:find_fs).with("/foo").returns "ext3" Selinux.expects(:matchpathcon).with("/foo", 0).returns [0, "user_u:role_r:type_t:s0"] + get_selinux_default_context("/foo").should == "user_u:role_r:type_t:s0" end @@ -151,10 +151,10 @@ describe Puppet::Util::SELinux do it "should return nil if matchpathcon returns failure" do self.expects(:selinux_support?).returns true fstat = stub 'File::Stat', :mode => 0 - stub_file = stub('/foo', :lstat => fstat) - Puppet::FileSystem::File.expects(:new).with('/foo').returns stub_file + Puppet::FileSystem.expects(:lstat).with('/foo').returns(fstat) self.expects(:find_fs).with("/foo").returns "ext3" Selinux.expects(:matchpathcon).with("/foo", 0).returns -1 + get_selinux_default_context("/foo").should be_nil end diff --git a/spec/unit/util/storage_spec.rb b/spec/unit/util/storage_spec.rb index 582bd2422..fe6b422c9 100755 --- a/spec/unit/util/storage_spec.rb +++ b/spec/unit/util/storage_spec.rb @@ -77,7 +77,7 @@ describe Puppet::Util::Storage do end it "should not fail to load" do - Puppet::FileSystem::File.exist?(@path).should be_false + Puppet::FileSystem.exist?(@path).should be_false Puppet[:statedir] = @path Puppet::Util::Storage.load Puppet[:statefile] = @path @@ -85,7 +85,7 @@ describe Puppet::Util::Storage do end it "should not lose its internal state when load() is called" do - Puppet::FileSystem::File.exist?(@path).should be_false + Puppet::FileSystem.exist?(@path).should be_false Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.state.should == {:yayness=>{}} @@ -176,12 +176,12 @@ describe Puppet::Util::Storage do end it "should create the state file if it does not exist" do - Puppet::FileSystem::File.exist?(Puppet[:statefile]).should be_false + Puppet::FileSystem.exist?(Puppet[:statefile]).should be_false Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.store - Puppet::FileSystem::File.exist?(Puppet[:statefile]).should be_true + Puppet::FileSystem.exist?(Puppet[:statefile]).should be_true end it "should raise an exception if the state file is not a regular file" do From 5d26652c761358575a643ca7d444d51d2e5d18c9 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 3 Jan 2014 17:29:08 -0800 Subject: [PATCH 357/800] (PUP-716) Fix up the type tests --- lib/puppet/file_system.rb | 2 +- spec/unit/type/file/content_spec.rb | 6 ++---- spec/unit/type/file/ctime_spec.rb | 2 +- spec/unit/type/file/mode_spec.rb | 6 +++--- spec/unit/type/file/mtime_spec.rb | 2 +- spec/unit/type/file/source_spec.rb | 2 +- spec/unit/type/file_spec.rb | 16 +++++++-------- spec/unit/type/k5login_spec.rb | 6 +++--- spec/unit/type/service_spec.rb | 6 +++--- spec/unit/type/tidy_spec.rb | 31 ++++++++++++++++++----------- 10 files changed, 42 insertions(+), 37 deletions(-) diff --git a/lib/puppet/file_system.rb b/lib/puppet/file_system.rb index 1a1a6e7f9..607d272a0 100644 --- a/lib/puppet/file_system.rb +++ b/lib/puppet/file_system.rb @@ -114,7 +114,7 @@ module Puppet::FileSystem # # @api public # - def binread(path) + def self.binread(path) @impl.binread(assert_path(path)) end diff --git a/spec/unit/type/file/content_spec.rb b/spec/unit/type/file/content_spec.rb index 5a73dceb1..08c345781 100755 --- a/spec/unit/type/file/content_spec.rb +++ b/spec/unit/type/file/content_spec.rb @@ -336,10 +336,9 @@ describe content do end it "should copy content from the source to the file" do - dest_file = Puppet::FileSystem::File.new(@filename) @resource.write(@source) - dest_file.binread.should == @source_content + Puppet::FileSystem.binread(@filename).should == @source_content end it "should return the checksum computed" do @@ -367,10 +366,9 @@ describe content do end it "should write the contents to the file" do - dest_file = Puppet::FileSystem::File.new(@filename) @resource.write(@source) - dest_file.binread.should == @source_content + Puppet::FileSystem.binread(@filename).should == @source_content end it "should not write anything if source is not found" do diff --git a/spec/unit/type/file/ctime_spec.rb b/spec/unit/type/file/ctime_spec.rb index eea0f1f92..ecb7458bc 100755 --- a/spec/unit/type/file/ctime_spec.rb +++ b/spec/unit/type/file/ctime_spec.rb @@ -16,7 +16,7 @@ describe Puppet::Type.type(:file).attrclass(:ctime) do @resource[:audit] = [:ctime] # this .to_resource audit behavior is magical :-( - @resource.to_resource[:ctime].should == Puppet::FileSystem::File.new(@filename).stat.ctime + @resource.to_resource[:ctime].should == Puppet::FileSystem.stat(@filename).ctime end it "should return absent if auditing an absent file" do diff --git a/spec/unit/type/file/mode_spec.rb b/spec/unit/type/file/mode_spec.rb index 8663fe57d..3f2dd90bb 100755 --- a/spec/unit/type/file/mode_spec.rb +++ b/spec/unit/type/file/mode_spec.rb @@ -81,7 +81,7 @@ describe Puppet::Type.type(:file).attrclass(:mode) do end it "should return true if the file is a link and we are managing links", :if => Puppet.features.manages_symlinks? do - Puppet::FileSystem::File.new('anything').symlink(path) + Puppet::FileSystem.symlink('anything', path) mode.must be_insync('644') end @@ -187,8 +187,8 @@ describe Puppet::Type.type(:file).attrclass(:mode) do # lower nibble must be set to 4 for the sake of passing on Windows FileUtils.chmod 0464, path mode_sym.sync - file = Puppet::FileSystem::File.new(path) - (file.stat.mode & 0777).to_s(8).should == "644" + stat = Puppet::FileSystem.stat(path) + (stat.mode & 0777).to_s(8).should == "644" end end end diff --git a/spec/unit/type/file/mtime_spec.rb b/spec/unit/type/file/mtime_spec.rb index a20bdf196..5456ec38b 100755 --- a/spec/unit/type/file/mtime_spec.rb +++ b/spec/unit/type/file/mtime_spec.rb @@ -16,7 +16,7 @@ describe Puppet::Type.type(:file).attrclass(:mtime) do @resource[:audit] = [:mtime] # this .to_resource audit behavior is magical :-( - @resource.to_resource[:mtime].should == Puppet::FileSystem::File.new(@filename).stat.mtime + @resource.to_resource[:mtime].should == Puppet::FileSystem.stat(@filename).mtime end it "should return absent if auditing an absent file" do diff --git a/spec/unit/type/file/source_spec.rb b/spec/unit/type/file/source_spec.rb index e645842c0..70e9a07ab 100755 --- a/spec/unit/type/file/source_spec.rb +++ b/spec/unit/type/file/source_spec.rb @@ -270,7 +270,7 @@ describe Puppet::Type.type(:file).attrclass(:source) do context "when managing an existing file" do before :each do - Puppet::FileSystem::File.stubs(:exist?).with(@resource[:path]).returns(true) + Puppet::FileSystem.stubs(:exist?).with(@resource[:path]).returns(true) end it "should not copy owner, group or mode from local sources" do diff --git a/spec/unit/type/file_spec.rb b/spec/unit/type/file_spec.rb index c7b529e2d..12aed3e42 100755 --- a/spec/unit/type/file_spec.rb +++ b/spec/unit/type/file_spec.rb @@ -920,7 +920,7 @@ describe Puppet::Type.type(:file) do file.remove_existing(:directory).should == true - Puppet::FileSystem::File.exist?(file[:path]).should == false + Puppet::FileSystem.exist?(file[:path]).should == false end it "should remove an existing link", :if => described_class.defaultprovider.feature?(:manages_symlinks) do @@ -928,12 +928,12 @@ describe Puppet::Type.type(:file) do target = tmpfile('link_target') FileUtils.touch(target) - Puppet::FileSystem::File.new(target).symlink(path) + Puppet::FileSystem.symlink(target, path) file[:target] = target file.remove_existing(:directory).should == true - Puppet::FileSystem::File.exist?(file[:path]).should == false + Puppet::FileSystem.exist?(file[:path]).should == false end it "should fail if the file is not a file, link, or directory" do @@ -947,7 +947,7 @@ describe Puppet::Type.type(:file) do file.stat file.stubs(:stat).returns stub('stat', :ftype => 'file') - Puppet::FileSystem::File.stubs(:unlink) + Puppet::FileSystem.stubs(:unlink) file.remove_existing(:directory).should == true file.instance_variable_get(:@stat).should == :needs_stat @@ -1009,7 +1009,7 @@ describe Puppet::Type.type(:file) do before do target = tmpfile('link_target') FileUtils.touch(target) - Puppet::FileSystem::File.new(target).symlink(path) + Puppet::FileSystem.symlink(target, path) file[:target] = target file[:links] = :manage # so we always use :lstat @@ -1373,7 +1373,7 @@ describe Puppet::Type.type(:file) do catalog.apply # I convert them to strings so they display correctly if there's an error. - (Puppet::FileSystem::File.new(@target).stat.mode & 007777).to_s(8).should == '644' + (Puppet::FileSystem.stat(@target).mode & 007777).to_s(8).should == '644' end it "should manage the mode of the followed link" do @@ -1382,7 +1382,7 @@ describe Puppet::Type.type(:file) do @link_resource[:links] = :follow catalog.apply - (Puppet::FileSystem::File.new(@target).stat.mode & 007777).to_s(8).should == '755' + (Puppet::FileSystem.stat(@target).mode & 007777).to_s(8).should == '755' end end end @@ -1466,7 +1466,7 @@ describe Puppet::Type.type(:file) do catalog.apply - Puppet::FileSystem::File.exist?(path).should be_true + Puppet::FileSystem.exist?(path).should be_true @logs.should_not be_any {|l| l.level != :notice } end end diff --git a/spec/unit/type/k5login_spec.rb b/spec/unit/type/k5login_spec.rb index 484ddf8e7..6c0dbb16d 100755 --- a/spec/unit/type/k5login_spec.rb +++ b/spec/unit/type/k5login_spec.rb @@ -46,7 +46,7 @@ describe Puppet::Type.type(:k5login), :unless => Puppet.features.microsoft_windo it "should create the file when synced" do resource(:ensure => 'present').parameter(:ensure).sync - Puppet::FileSystem::File.exist?(path).should be_true + Puppet::FileSystem.exist?(path).should be_true end end @@ -83,7 +83,7 @@ describe Puppet::Type.type(:k5login), :unless => Puppet.features.microsoft_windo it "should remove the file ensure is absent" do resource(:ensure => 'absent').property(:ensure).sync - Puppet::FileSystem::File.exist?(path).should be_false + Puppet::FileSystem.exist?(path).should be_false end it "should write one principal to the file" do @@ -106,7 +106,7 @@ describe Puppet::Type.type(:k5login), :unless => Puppet.features.microsoft_windo it "should update the mode to #{mode}" do resource(:mode => mode).property(:mode).sync - (Puppet::FileSystem::File.new(path).stat.mode & 07777).to_s(8).should == mode + (Puppet::FileSystem.stat(path).mode & 07777).to_s(8).should == mode end end end diff --git a/spec/unit/type/service_spec.rb b/spec/unit/type/service_spec.rb index e36d11a56..cfb701d0c 100755 --- a/spec/unit/type/service_spec.rb +++ b/spec/unit/type/service_spec.rb @@ -120,14 +120,14 @@ describe Puppet::Type.type(:service), "when validating attribute values" do end it "should split paths on '#{File::PATH_SEPARATOR}'" do - Puppet::FileSystem::File.stubs(:exist?).returns(true) + Puppet::FileSystem.stubs(:exist?).returns(true) FileTest.stubs(:directory?).returns(true) svc = Puppet::Type.type(:service).new(:name => "yay", :path => "/one/two#{File::PATH_SEPARATOR}/three/four") svc[:path].should == %w{/one/two /three/four} end it "should accept arrays of paths joined by '#{File::PATH_SEPARATOR}'" do - Puppet::FileSystem::File.stubs(:exist?).returns(true) + Puppet::FileSystem.stubs(:exist?).returns(true) FileTest.stubs(:directory?).returns(true) svc = Puppet::Type.type(:service).new(:name => "yay", :path => ["/one#{File::PATH_SEPARATOR}/two", "/three#{File::PATH_SEPARATOR}/four"]) svc[:path].should == %w{/one /two /three /four} @@ -137,7 +137,7 @@ end describe Puppet::Type.type(:service), "when setting default attribute values" do it "should default to the provider's default path if one is available" do FileTest.stubs(:directory?).returns(true) - Puppet::FileSystem::File.stubs(:exist?).returns(true) + Puppet::FileSystem.stubs(:exist?).returns(true) Puppet::Type.type(:service).defaultprovider.stubs(:respond_to?).returns(true) Puppet::Type.type(:service).defaultprovider.stubs(:defpath).returns("testing") diff --git a/spec/unit/type/tidy_spec.rb b/spec/unit/type/tidy_spec.rb index abe5d26ff..48a930b66 100755 --- a/spec/unit/type/tidy_spec.rb +++ b/spec/unit/type/tidy_spec.rb @@ -14,10 +14,11 @@ describe tidy do it "should use :lstat when stating a file" do path = '/foo/bar' - resource = tidy.new :path => path, :age => "1d" stat = mock 'stat' - stub_file = stub(path, :lstat => stat) - Puppet::FileSystem::File.expects(:new).with(path).returns stub_file + Puppet::FileSystem.expects(:lstat).with(path).returns stat + + resource = tidy.new :path => path, :age => "1d" + resource.stat(path).should == stat end @@ -128,8 +129,7 @@ describe tidy do before do @tidy = Puppet::Type.type(:tidy).new :path => @basepath @stat = stub 'stat', :ftype => "directory" - @stub_file = stub(@basepath, :lstat => @stat) - Puppet::FileSystem::File.stubs(:new).with(@basepath).returns @stub_file + lstat_is(@basepath, @stat) end describe "and generating files" do @@ -159,7 +159,7 @@ describe tidy do end it "should do nothing if the targeted file does not exist" do - @stub_file.expects(:lstat).raises Errno::ENOENT + lstat_raises(@basepath, Errno::ENOENT) @tidy.generate.should == [] end @@ -310,33 +310,32 @@ describe tidy do before do @tidy = Puppet::Type.type(:tidy).new :path => @basepath @stat = stub 'stat', :ftype => "file" - @stub_file = stub(@basepath, :lstat => @stat) - Puppet::FileSystem::File.expects(:new).with(@basepath).returns @stub_file + lstat_is(@basepath, @stat) end it "should not try to recurse if the file does not exist" do @tidy[:recurse] = true - @stub_file.stubs(:lstat).returns nil + lstat_is(@basepath, nil) @tidy.generate.should == [] end it "should not be tidied if the file does not exist" do - @stub_file.expects(:lstat).raises Errno::ENOENT + lstat_raises(@basepath, Errno::ENOENT) @tidy.should_not be_tidy(@basepath) end it "should not be tidied if the user has no access to the file" do - @stub_file.expects(:lstat).raises Errno::EACCES + lstat_raises(@basepath, Errno::EACCES) @tidy.should_not be_tidy(@basepath) end it "should not be tidied if it is a directory and rmdirs is set to false" do stat = mock 'stat', :ftype => "directory" - @stub_file.expects(:lstat).returns stat + lstat_is(@basepath, stat) @tidy.should_not be_tidy(@basepath) end @@ -423,4 +422,12 @@ describe tidy do end end end + + def lstat_is(path, stat) + Puppet::FileSystem.stubs(:lstat).with(path).returns(stat) + end + + def lstat_raises(path, error_class) + Puppet::FileSystem.expects(:lstat).with(path).raises Errno::ENOENT + end end From 018075729b2deaec87c5b526c185e618f4f7b887 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 4 Jan 2014 03:26:27 +0100 Subject: [PATCH 358/800] (PUP-716) Fix up metadata_spec after refactored FileSystem changes --- spec/unit/file_serving/metadata_spec.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spec/unit/file_serving/metadata_spec.rb b/spec/unit/file_serving/metadata_spec.rb index 8b74d4b7e..306e560c9 100755 --- a/spec/unit/file_serving/metadata_spec.rb +++ b/spec/unit/file_serving/metadata_spec.rb @@ -201,13 +201,13 @@ describe Puppet::FileServing::Metadata do let(:path) { tmpfile('file_serving_metadata_link') } let(:target) { tmpfile('file_serving_metadata_target') } let(:checksum) { Digest::MD5.hexdigest("some content\n") } - let(:fmode) { Puppet::FileSystem::File.new(path).lstat.mode & 0777 } + let(:fmode) { Puppet::FileSystem.lstat(path).mode & 0777 } before :each do File.open(target, "wb") {|f| f.print("some content\n")} set_mode(0644, target) - Puppet::FileSystem::File.new(target).symlink(path) + Puppet::FileSystem.symlink(target, path) end it "should read links instead of returning their checksums" do @@ -325,7 +325,8 @@ describe Puppet::FileServing::Metadata, " when pointing to a link", :if => Puppe @file = Puppet::FileServing::Metadata.new(path, :links => :manage) stat = stub("stat", :uid => 1, :gid => 2, :ftype => "link", :mode => 0755) stub_file = stub(:readlink => "/some/other/path", :lstat => stat) - Puppet::FileSystem::File.expects(:new).with(path).at_least_once.returns stub_file + Puppet::FileSystem.expects(:lstat).with(path).at_least_once.returns stat + Puppet::FileSystem.expects(:readlink).with(path).at_least_once.returns "/some/other/path" @checksum = Digest::MD5.hexdigest("some content\n") # Remove these when :managed links are no longer checksumed. @file.stubs(:md5_file).returns(@checksum) # @@ -356,9 +357,8 @@ describe Puppet::FileServing::Metadata, " when pointing to a link", :if => Puppe path = "/base/path/my/file" @file = Puppet::FileServing::Metadata.new(path, :links => :follow) stat = stub("stat", :uid => 1, :gid => 2, :ftype => "file", :mode => 0755) - mocked_file = mock(path, :stat => stat) - Puppet::FileSystem::File.expects(:new).with(path).at_least_once.returns mocked_file - mocked_file.expects(:readlink).never + Puppet::FileSystem.expects(:stat).with(path).at_least_once.returns stat + Puppet::FileSystem.expects(:readlink).never if Puppet.features.microsoft_windows? win_stat = stub('win_stat', :owner => 'snarf', :group => 'thundercats', From 3acea267a6603f9f2201dd356f6eb62a7ab163e0 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 3 Jan 2014 17:31:12 -0800 Subject: [PATCH 359/800] (PUP-716) Fix up transaction tests --- spec/unit/transaction/resource_harness_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/unit/transaction/resource_harness_spec.rb b/spec/unit/transaction/resource_harness_spec.rb index afe1ec912..98027dd09 100755 --- a/spec/unit/transaction/resource_harness_spec.rb +++ b/spec/unit/transaction/resource_harness_spec.rb @@ -253,7 +253,7 @@ describe Puppet::Transaction::ResourceHarness do resource.expects(:err).never # make sure no exceptions get swallowed @harness.expects(:allow_changes?).with(resource).returns false status = @harness.evaluate(resource) - Puppet::FileSystem::File.exist?(test_file).should == false + Puppet::FileSystem.exist?(test_file).should == false end end From 3d03292de72bacfcffc778ed718fc86c794737e4 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 3 Jan 2014 18:11:00 -0800 Subject: [PATCH 360/800] (PUP-716) Fix up tests in ssl dir This also migrates the overlay support from the previous form of the file system abstraction to this form. --- lib/puppet/file_system.rb | 20 ++++++++++--------- lib/puppet/file_system/memory_file.rb | 2 +- lib/puppet/settings/file_setting.rb | 2 +- spec/unit/ssl/certificate_authority_spec.rb | 16 +++++++-------- .../certificate_request_attributes_spec.rb | 2 +- spec/unit/ssl/inventory_spec.rb | 4 ++-- spec/unit/ssl/key_spec.rb | 8 ++++---- 7 files changed, 28 insertions(+), 26 deletions(-) diff --git a/lib/puppet/file_system.rb b/lib/puppet/file_system.rb index 607d272a0..9e32d00d9 100644 --- a/lib/puppet/file_system.rb +++ b/lib/puppet/file_system.rb @@ -2,9 +2,9 @@ module Puppet::FileSystem require 'puppet/file_system/path_pattern' require 'puppet/file_system/file_impl' require 'puppet/file_system/memory_file' + require 'puppet/file_system/memory_impl' require 'puppet/file_system/tempfile' - # create instance of the file system implementation to use for the current platform @impl = if RUBY_VERSION =~ /^1\.8/ require 'puppet/file_system/file18' @@ -17,16 +17,18 @@ module Puppet::FileSystem Puppet::FileSystem::File19 end.new() - # Overrides the automatic file system implementation selection that is based on the current platform - # Should only be used for testing. - # @return [Object] the previous file system implementation (to allow it to be restored) + # Allows overriding the filesystem for the duration of the given block. The filesystem will contain only the file(s) provided. + # + # @param files [Puppet::FileSystem::MemoryFile] the files to have available # # @api private # - def self.set_file_system_implementation(impl) - tmp = @impl - @impl = impl - tmp + def self.overlay(*files, &block) + old_impl = @impl + @impl = Puppet::FileSystem::MemoryImpl.new(*files) + yield + ensure + @impl = old_impl end # Opens the given path with given mode, and options and optionally yields it to the given block. @@ -34,7 +36,7 @@ module Puppet::FileSystem # @api public # def self.open(path, mode, options, &block) - @impl.open(assert_path(path), options, mode, &block) + @impl.open(assert_path(path), mode, options, &block) end # @return [Pathname] The directory of this file diff --git a/lib/puppet/file_system/memory_file.rb b/lib/puppet/file_system/memory_file.rb index 4605a7a01..94280bfcc 100644 --- a/lib/puppet/file_system/memory_file.rb +++ b/lib/puppet/file_system/memory_file.rb @@ -16,7 +16,7 @@ class Puppet::FileSystem::MemoryFile end def initialize(path, options) - @path = Pathname.new(path) + @path = path @exist = options[:exist?] @executable = options[:executable?] @content = options[:content] diff --git a/lib/puppet/settings/file_setting.rb b/lib/puppet/settings/file_setting.rb index 14822bef0..e20767374 100644 --- a/lib/puppet/settings/file_setting.rb +++ b/lib/puppet/settings/file_setting.rb @@ -194,7 +194,7 @@ class Puppet::Settings::FileSetting < Puppet::Settings::StringSetting # @api private def open(option = 'r', &block) controlled_access do |mode| - file.open(mode, option, &block) + Puppet::FileSystem.open(file, mode, option, &block) end end diff --git a/spec/unit/ssl/certificate_authority_spec.rb b/spec/unit/ssl/certificate_authority_spec.rb index 13c169a0a..758c31d58 100755 --- a/spec/unit/ssl/certificate_authority_spec.rb +++ b/spec/unit/ssl/certificate_authority_spec.rb @@ -165,7 +165,7 @@ describe Puppet::SSL::CertificateAuthority do it "should create and store a password at :capass" do Puppet[:capass] = File.expand_path("/path/to/pass") - Puppet::FileSystem::File.expects(:exist?).with(Puppet[:capass]).returns false + Puppet::FileSystem.expects(:exist?).with(Puppet[:capass]).returns false fh = StringIO.new Puppet.settings.setting(:capass).expects(:open).with('w').yields fh @@ -619,7 +619,7 @@ describe Puppet::SSL::CertificateAuthority do it "should do nothing if no autosign.conf exists" do Puppet[:autosign] = autosign non_existent_file = Puppet::FileSystem::MemoryFile.a_missing_file(autosign) - Puppet::FileSystem::File.overlay(non_existent_file) do + Puppet::FileSystem.overlay(non_existent_file) do @ca.expects(:sign).never @ca.autosign(csr) end @@ -634,7 +634,7 @@ describe Puppet::SSL::CertificateAuthority do describe "when creating the AuthStore instance to verify autosigning" do it "should create an AuthStore with each line in the configuration file allowed to be autosigned" do - Puppet::FileSystem::File.overlay(Puppet::FileSystem::MemoryFile.a_regular_file_containing(autosign, "one\ntwo\n")) do + Puppet::FileSystem.overlay(Puppet::FileSystem::MemoryFile.a_regular_file_containing(autosign, "one\ntwo\n")) do Puppet::Network::AuthStore.stubs(:new).returns store store.expects(:allow).with("one") @@ -645,7 +645,7 @@ describe Puppet::SSL::CertificateAuthority do end it "should reparse the autosign configuration on each call" do - Puppet::FileSystem::File.overlay(Puppet::FileSystem::MemoryFile.a_regular_file_containing(autosign, "one")) do + Puppet::FileSystem.overlay(Puppet::FileSystem::MemoryFile.a_regular_file_containing(autosign, "one")) do Puppet::Network::AuthStore.stubs(:new).times(2).returns store @ca.autosign(csr) @@ -654,7 +654,7 @@ describe Puppet::SSL::CertificateAuthority do end it "should ignore comments" do - Puppet::FileSystem::File.overlay(Puppet::FileSystem::MemoryFile.a_regular_file_containing(autosign, "one\n#two\n")) do + Puppet::FileSystem.overlay(Puppet::FileSystem::MemoryFile.a_regular_file_containing(autosign, "one\n#two\n")) do Puppet::Network::AuthStore.stubs(:new).returns store store.expects(:allow).with("one") @@ -664,7 +664,7 @@ describe Puppet::SSL::CertificateAuthority do end it "should ignore blank lines" do - Puppet::FileSystem::File.overlay(Puppet::FileSystem::MemoryFile.a_regular_file_containing(autosign, "one\n\n")) do + Puppet::FileSystem.overlay(Puppet::FileSystem::MemoryFile.a_regular_file_containing(autosign, "one\n\n")) do Puppet::Network::AuthStore.stubs(:new).returns store store.expects(:allow).with("one") @@ -687,7 +687,7 @@ describe Puppet::SSL::CertificateAuthority do end it "autosigns the CSR if the autosign command returned true" do - Puppet::FileSystem::File.overlay(autosign_executable) do + Puppet::FileSystem.overlay(autosign_executable) do autosign_cmd.expects(:allowed?).with(csr).returns true @ca.expects(:sign).with('host') @@ -696,7 +696,7 @@ describe Puppet::SSL::CertificateAuthority do end it "doesn't autosign the CSR if the autosign_command returned false" do - Puppet::FileSystem::File.overlay(autosign_executable) do + Puppet::FileSystem.overlay(autosign_executable) do autosign_cmd.expects(:allowed?).with(csr).returns false @ca.expects(:sign).never diff --git a/spec/unit/ssl/certificate_request_attributes_spec.rb b/spec/unit/ssl/certificate_request_attributes_spec.rb index 6165330aa..5c8f93b1b 100644 --- a/spec/unit/ssl/certificate_request_attributes_spec.rb +++ b/spec/unit/ssl/certificate_request_attributes_spec.rb @@ -27,7 +27,7 @@ describe Puppet::SSL::CertificateRequestAttributes do context "with an available attributes file" do before do - Puppet::FileSystem::File.expects(:exist?).with(csr_attributes_path).returns(true) + Puppet::FileSystem.expects(:exist?).with(csr_attributes_path).returns(true) Puppet::Util::Yaml.expects(:load_file).with(csr_attributes_path, {}).returns(csr_attributes_hash) end diff --git a/spec/unit/ssl/inventory_spec.rb b/spec/unit/ssl/inventory_spec.rb index 9bcbbcea5..6e4fbd340 100755 --- a/spec/unit/ssl/inventory_spec.rb +++ b/spec/unit/ssl/inventory_spec.rb @@ -20,7 +20,7 @@ describe Puppet::SSL::Inventory, :unless => Puppet.features.microsoft_windows? d before do Puppet[:cert_inventory] = cert_inventory - Puppet::FileSystem::File.stubs(:exist?).with(cert_inventory).returns true + Puppet::FileSystem.stubs(:exist?).with(cert_inventory).returns true @inventory = @class.new @@ -117,7 +117,7 @@ describe Puppet::SSL::Inventory, :unless => Puppet.features.microsoft_windows? d describe "and finding a serial number" do it "should return nil if the inventory file is missing" do - Puppet::FileSystem::File.expects(:exist?).with(cert_inventory).returns false + Puppet::FileSystem.expects(:exist?).with(cert_inventory).returns false @inventory.serial(:whatever).should be_nil end diff --git a/spec/unit/ssl/key_spec.rb b/spec/unit/ssl/key_spec.rb index 4cea5491c..c7f54ff5d 100755 --- a/spec/unit/ssl/key_spec.rb +++ b/spec/unit/ssl/key_spec.rb @@ -71,7 +71,7 @@ describe Puppet::SSL::Key do end it "should not try to use the provided password file if the file does not exist" do - Puppet::FileSystem::File.stubs(:exist?).returns false + Puppet::FileSystem.stubs(:exist?).returns false @key.password_file = "/path/to/password" path = "/my/path" @@ -84,7 +84,7 @@ describe Puppet::SSL::Key do end it "should read the key with the password retrieved from the password file if one is provided" do - Puppet::FileSystem::File.stubs(:exist?).returns true + Puppet::FileSystem.stubs(:exist?).returns true @key.password_file = "/path/to/password" path = "/my/path" @@ -154,7 +154,7 @@ describe Puppet::SSL::Key do describe "with a password file set" do it "should return a nil password if the password file does not exist" do - Puppet::FileSystem::File.expects(:exist?).with("/path/to/pass").returns false + Puppet::FileSystem.expects(:exist?).with("/path/to/pass").returns false File.expects(:read).with("/path/to/pass").never @instance.password_file = "/path/to/pass" @@ -163,7 +163,7 @@ describe Puppet::SSL::Key do end it "should return the contents of the password file as its password" do - Puppet::FileSystem::File.expects(:exist?).with("/path/to/pass").returns true + Puppet::FileSystem.expects(:exist?).with("/path/to/pass").returns true File.expects(:read).with("/path/to/pass").returns "my password" @instance.password_file = "/path/to/pass" From a96e7f2b732f58e1c7f97c4d87e834d57c8845dd Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 5 Jan 2014 01:46:56 +0100 Subject: [PATCH 361/800] (PUP-716) Refactor and mae file_system/file_spec green This refactors the file_system/file_spec to comply with the new API. Several problems found with the implementation. * Bad include of memory_impl * facade methods did not relay to _impl * assert_path did not consider objects coercable to string (e.g.watched file) --- lib/puppet/file_system.rb | 9 +- lib/puppet/file_system/file_impl.rb | 8 +- spec/unit/file_system/file_spec.rb | 324 ++++++++++++++-------------- 3 files changed, 172 insertions(+), 169 deletions(-) diff --git a/lib/puppet/file_system.rb b/lib/puppet/file_system.rb index 9e32d00d9..c0a287db0 100644 --- a/lib/puppet/file_system.rb +++ b/lib/puppet/file_system.rb @@ -2,7 +2,7 @@ module Puppet::FileSystem require 'puppet/file_system/path_pattern' require 'puppet/file_system/file_impl' require 'puppet/file_system/memory_file' - require 'puppet/file_system/memory_impl' + #require 'puppet/file_system/memory_impl' require 'puppet/file_system/tempfile' # create instance of the file system implementation to use for the current platform @@ -62,7 +62,7 @@ module Puppet::FileSystem # @api public # def self.basename(path) - @impl.basename(assert_path(path.basename)) + @impl.basename(assert_path(path)) end # @return [Integer] the size of the file @@ -109,7 +109,7 @@ module Puppet::FileSystem # @api public # def self.read(path) - path.read + @impl.read(assert_path(path)) end # @return [String] The binary contents of the file @@ -224,8 +224,7 @@ module Puppet::FileSystem # @api public # def self.unlink(*paths) - paths.each {|p| assert_path(p) } - @impl.unlink(*paths) + @impl.unlink(*(paths.map {|p| assert_path(p) })) end # @return [File::Stat] object for the named file. diff --git a/lib/puppet/file_system/file_impl.rb b/lib/puppet/file_system/file_impl.rb index 19bb04e60..8f0f50bad 100644 --- a/lib/puppet/file_system/file_impl.rb +++ b/lib/puppet/file_system/file_impl.rb @@ -8,7 +8,13 @@ class Puppet::FileSystem::FileImpl def assert_path(path) return path if path.is_a?(Pathname) - raise ArgumentError, "FileSystem implementation expected Pathname, got: '#{path.class}" unless path.is_a?(String) + # Some paths are string, or (in the case of WatchedFile, it pretends to be one by implementing to_str. + # (sigh). + # + unless path.is_a?(String) || path.respond_to?(:to_str) + raise ArgumentError, "FileSystem implementation expected Pathname, got: '#{path.class}" + end + # converts String and #to_str to Pathname Pathname.new(path) end diff --git a/spec/unit/file_system/file_spec.rb b/spec/unit/file_system/file_spec.rb index fa0786b44..79fe13e5a 100644 --- a/spec/unit/file_system/file_spec.rb +++ b/spec/unit/file_system/file_spec.rb @@ -2,47 +2,48 @@ require 'spec_helper' require 'puppet/file_system' require 'puppet/util/platform' -describe Puppet::FileSystem::File do +describe "Puppet::FileSystem" do include PuppetSpec::Files context "#exclusive_open" do it "opens ands allows updating of an existing file" do - file = Puppet::FileSystem::File.new(file_containing("file_to_update", "the contents")) + file = file_containing("file_to_update", "the contents") +# file = Puppet::FileSystem::File.new(file_containing("file_to_update", "the contents")) - file.exclusive_open(0660, 'r+') do |fh| + Puppet::FileSystem.exclusive_open(file, 0660, 'r+') do |fh| old = fh.read fh.truncate(0) fh.rewind fh.write("updated #{old}") end - expect(file.read).to eq("updated the contents") + expect(Puppet::FileSystem.read(file)).to eq("updated the contents") end it "opens, creates ands allows updating of a new file" do - file = Puppet::FileSystem::File.new(tmpfile("file_to_update")) + file = tmpfile("file_to_update") - file.exclusive_open(0660, 'w') do |fh| + Puppet::FileSystem.exclusive_open(file, 0660, 'w') do |fh| fh.write("updated new file") end - expect(file.read).to eq("updated new file") + expect(Puppet::FileSystem.read(file)).to eq("updated new file") end it "excludes other processes from updating at the same time", :unless => Puppet::Util::Platform.windows? do - file = Puppet::FileSystem::File.new(file_containing("file_to_update", "0")) + file = file_containing("file_to_update", "0") increment_counter_in_multiple_processes(file, 5, 'r+') - expect(file.read).to eq("5") + expect(Puppet::FileSystem.read(file)).to eq("5") end it "excludes other processes from updating at the same time even when creating the file", :unless => Puppet::Util::Platform.windows? do - file = Puppet::FileSystem::File.new(tmpfile("file_to_update")) + file = tmpfile("file_to_update") increment_counter_in_multiple_processes(file, 5, 'a+') - expect(file.read).to eq("5") + expect(Puppet::FileSystem.read(file)).to eq("5") end it "times out if the lock cannot be aquired in a specified amount of time", :unless => Puppet::Util::Platform.windows? do @@ -51,7 +52,7 @@ describe Puppet::FileSystem::File do child = spawn_process_that_locks(file) expect do - Puppet::FileSystem::File.new(file).exclusive_open(0666, 'a', 0.1) do |f| + Puppet::FileSystem.exclusive_open(file, 0666, 'a', 0.1) do |f| end end.to raise_error(Timeout::Error) @@ -63,7 +64,7 @@ describe Puppet::FileSystem::File do child = Kernel.fork do read.close - Puppet::FileSystem::File.new(file).exclusive_open(0666, 'a') do |fh| + Puppet::FileSystem.exclusive_open(file, 0666, 'a') do |fh| write.write(true) write.close sleep 10 @@ -81,7 +82,7 @@ describe Puppet::FileSystem::File do children = [] 5.times do |number| children << Kernel.fork do - file.exclusive_open(0660, options) do |fh| + Puppet::FileSystem.exclusive_open(file, 0660, options) do |fh| fh.rewind contents = (fh.read || 0).to_i fh.truncate(0) @@ -100,194 +101,191 @@ describe Puppet::FileSystem::File do :if => ! Puppet.features.manages_symlinks? && Puppet.features.microsoft_windows? do - let (:file) { Puppet::FileSystem::File.new(tmpfile("somefile")) } - let (:missing_file) { Puppet::FileSystem::File.new(tmpfile("missingfile")) } - let (:expected_msg) { "This version of Windows does not support symlinks. Windows Vista / 2008 or higher is required." } + let(:file) { tmpfile("somefile") } + let(:missing_file) { tmpfile("missingfile") } + let(:expected_msg) { "This version of Windows does not support symlinks. Windows Vista / 2008 or higher is required." } before :each do - FileUtils.touch(file.path) + FileUtils.touch(file) end it "should raise an error when trying to create a symlink" do - expect { file.symlink('foo') }.to raise_error(Puppet::Util::Windows::Error) + expect { Puppet::FileSystem.symlink(file, 'foo') }.to raise_error(Puppet::Util::Windows::Error) end it "should return false when trying to check if a path is a symlink" do - file.symlink?.should be_false + Puppet::FileSystem.symlink?(file).should be_false end it "should raise an error when trying to read a symlink" do - expect { file.readlink }.to raise_error(Puppet::Util::Windows::Error) + expect { Puppet::FileSystem.readlink(file) }.to raise_error(Puppet::Util::Windows::Error) end it "should return a File::Stat instance when calling stat on an existing file" do - file.stat.should be_instance_of(File::Stat) + Puppet::FileSystem.stat(file).should be_instance_of(File::Stat) end it "should raise Errno::ENOENT when calling stat on a missing file" do - expect { missing_file.stat }.to raise_error(Errno::ENOENT) + expect { Puppet::FileSystem.stat(missing_file) }.to raise_error(Errno::ENOENT) end it "should fall back to stat when trying to lstat a file" do - Puppet::Util::Windows::File.expects(:stat).with(file.path) + Puppet::Util::Windows::File.expects(:stat).with(file) - file.lstat + Puppet::FileSystem.lstat(file) end end describe "symlink", :if => Puppet.features.manages_symlinks? do - let (:file) { Puppet::FileSystem::File.new(tmpfile("somefile")) } - let (:missing_file) { Puppet::FileSystem::File.new(tmpfile("missingfile")) } - let (:dir) { Puppet::FileSystem::File.new(tmpdir("somedir")) } + let(:file) { tmpfile("somefile") } + let(:missing_file) { tmpfile("missingfile") } + let(:dir) { tmpdir("somedir") } before :each do - FileUtils.touch(file.path) + FileUtils.touch(file) end it "should return true for exist? on a present file" do - file.exist?.should be_true - Puppet::FileSystem::File.exist?(file.path).should be_true + Puppet::FileSystem.exist?(file).should be_true end it "should return false for exist? on a non-existant file" do - missing_file.exist?.should be_false - Puppet::FileSystem::File.exist?(missing_file.path).should be_false + Puppet::FileSystem.exist?(missing_file).should be_false end it "should return true for exist? on a present directory" do - dir.exist?.should be_true - Puppet::FileSystem::File.exist?(dir.path).should be_true + Puppet::FileSystem.exist?(dir).should be_true end it "should return false for exist? on a dangling symlink" do - symlink = Puppet::FileSystem::File.new(tmpfile("somefile_link")) - missing_file.symlink(symlink.path) + symlink = tmpfile("somefile_link") + Puppet::FileSystem.symlink(missing_file, symlink) - missing_file.exist?.should be_false - symlink.exist?.should be_false + Puppet::FileSystem.exist?(missing_file).should be_false + Puppet::FileSystem.exist?(symlink).should be_false end it "should return true for exist? on valid symlinks" do [file, dir].each do |target| - symlink = Puppet::FileSystem::File.new(tmpfile("#{target.path.basename.to_s}_link")) - target.symlink(symlink.path) + symlink = tmpfile("#{Puppet::FileSystem.basename(target).to_s}_link") + Puppet::FileSystem.symlink(target, symlink) - target.exist?.should be_true - symlink.exist?.should be_true + Puppet::FileSystem.exist?(target).should be_true + Puppet::FileSystem.exist?(symlink).should be_true end end it "should not create a symlink when the :noop option is specified" do [file, dir].each do |target| - symlink = Puppet::FileSystem::File.new(tmpfile("#{target.path.basename.to_s}_link")) - target.symlink(symlink.path, { :noop => true }) + symlink = tmpfile("#{Puppet::FileSystem.basename(target)}_link") + Puppet::FileSystem.symlink(target, symlink, { :noop => true }) - target.exist?.should be_true - symlink.exist?.should be_false + Puppet::FileSystem.exist?(target).should be_true + Puppet::FileSystem.exist?(symlink).should be_false end end it "should raise Errno::EEXIST if trying to create a file / directory symlink when the symlink path already exists as a file" do - existing_file = Puppet::FileSystem::File.new(tmpfile("#{file.path.basename.to_s}_link")) - FileUtils.touch(existing_file.path) + existing_file = tmpfile("#{Puppet::FileSystem.basename(file)}_link") + FileUtils.touch(existing_file) [file, dir].each do |target| - expect { target.symlink(existing_file.path) }.to raise_error(Errno::EEXIST) + expect { Puppet::FileSystem.symlink(target, existing_file) }.to raise_error(Errno::EEXIST) - existing_file.exist?.should be_true - existing_file.symlink?.should be_false + Puppet::FileSystem.exist?(existing_file).should be_true + Puppet::FileSystem.symlink?(existing_file).should be_false end end it "should silently fail if trying to create a file / directory symlink when the symlink path already exists as a directory" do - existing_dir = Puppet::FileSystem::File.new(tmpdir("#{file.path.basename.to_s}_dir")) + existing_dir = tmpdir("#{Puppet::FileSystem.basename(file)}_dir") [file, dir].each do |target| - target.symlink(existing_dir.path).should == 0 + Puppet::FileSystem.symlink(target, existing_dir).should == 0 - existing_dir.exist?.should be_true - File.directory?(existing_dir.path).should be_true - existing_dir.symlink?.should be_false + Puppet::FileSystem.exist?(existing_dir).should be_true + File.directory?(existing_dir).should be_true + Puppet::FileSystem.symlink?(existing_dir).should be_false end end it "should silently fail to modify an existing directory symlink to reference a new file or directory" do [file, dir].each do |target| - existing_dir = Puppet::FileSystem::File.new(tmpdir("#{target.path.basename.to_s}_dir")) - symlink = Puppet::FileSystem::File.new(tmpfile("#{existing_dir.path.basename.to_s}_link")) - existing_dir.symlink(symlink.path) + existing_dir = tmpdir("#{Puppet::FileSystem.basename(target)}_dir") + symlink = tmpfile("#{Puppet::FileSystem.basename(existing_dir)}_link") + Puppet::FileSystem.symlink(existing_dir, symlink) - symlink.readlink.should == existing_dir.path.to_s + Puppet::FileSystem.readlink(symlink).should == Puppet::FileSystem.path_string(existing_dir) # now try to point it at the new target, no error raised, but file system unchanged - target.symlink(symlink.path).should == 0 - symlink.readlink.should == existing_dir.path.to_s + Puppet::FileSystem.symlink(target, symlink).should == 0 + Puppet::FileSystem.readlink(symlink).should == existing_dir.to_s end end it "should raise Errno::EEXIST if trying to modify a file symlink to reference a new file or directory" do - symlink = Puppet::FileSystem::File.new(tmpfile("#{file.path.basename.to_s}_link")) - file_2 = Puppet::FileSystem::File.new(tmpfile("#{file.path.basename.to_s}_2")) - FileUtils.touch(file_2.path) + symlink = tmpfile("#{Puppet::FileSystem.basename(file)}_link") + file_2 = tmpfile("#{Puppet::FileSystem.basename(file)}_2") + FileUtils.touch(file_2) # symlink -> file_2 - file_2.symlink(symlink.path) + Puppet::FileSystem.symlink(file_2, symlink) [file, dir].each do |target| - expect { target.symlink(symlink.path) }.to raise_error(Errno::EEXIST) - symlink.readlink.should == file_2.path.to_s + expect { Puppet::FileSystem.symlink(target, symlink) }.to raise_error(Errno::EEXIST) + Puppet::FileSystem.readlink(symlink).should == file_2.to_s end end it "should delete the existing file when creating a file / directory symlink with :force when the symlink path exists as a file" do [file, dir].each do |target| - existing_file = Puppet::FileSystem::File.new(tmpfile("#{target.path.basename.to_s}_existing")) - FileUtils.touch(existing_file.path) - existing_file.symlink?.should be_false + existing_file = tmpfile("#{Puppet::FileSystem.basename(target)}_existing") + FileUtils.touch(existing_file) + Puppet::FileSystem.symlink?(existing_file).should be_false - target.symlink(existing_file.path, { :force => true }) + Puppet::FileSystem.symlink(target, existing_file, { :force => true }) - existing_file.symlink?.should be_true - existing_file.readlink.should == target.path.to_s + Puppet::FileSystem.symlink?(existing_file).should be_true + Puppet::FileSystem.readlink(existing_file).should == target.to_s end end it "should modify an existing file symlink when using :force to reference a new file or directory" do [file, dir].each do |target| - existing_file = Puppet::FileSystem::File.new(tmpfile("#{target.path.basename.to_s}_existing")) - FileUtils.touch(existing_file.path) - existing_symlink = Puppet::FileSystem::File.new(tmpfile("#{existing_file.path.basename.to_s}_link")) - existing_file.symlink(existing_symlink.path) + existing_file = tmpfile("#{Puppet::FileSystem.basename(target)}_existing") + FileUtils.touch(existing_file) + existing_symlink = tmpfile("#{Puppet::FileSystem.basename(existing_file)}_link") + Puppet::FileSystem.symlink(existing_file, existing_symlink) - existing_symlink.readlink.should == existing_file.path.to_s + Puppet::FileSystem.readlink(existing_symlink).should == existing_file.to_s - target.symlink(existing_symlink.path, { :force => true }) + Puppet::FileSystem.symlink(target, existing_symlink, { :force => true }) - existing_symlink.readlink.should == target.path.to_s + Puppet::FileSystem.readlink(existing_symlink).should == target.to_s end end it "should silently fail if trying to overwrite an existing directory with a new symlink when using :force to reference a file or directory" do [file, dir].each do |target| - existing_dir = Puppet::FileSystem::File.new(tmpdir("#{target.path.basename.to_s}_existing")) + existing_dir = tmpdir("#{Puppet::FileSystem.basename(target)}_existing") - target.symlink(existing_dir.path, { :force => true }).should == 0 + Puppet::FileSystem.symlink(target, existing_dir, { :force => true }).should == 0 - existing_dir.symlink?.should be_false + Puppet::FileSystem.symlink?(existing_dir).should be_false end end it "should silently fail if trying to modify an existing directory symlink when using :force to reference a new file or directory" do [file, dir].each do |target| - existing_dir = Puppet::FileSystem::File.new(tmpdir("#{target.path.basename.to_s}_existing")) - existing_symlink = Puppet::FileSystem::File.new(tmpfile("#{existing_dir.path.basename.to_s}_link")) - existing_dir.symlink(existing_symlink.path) + existing_dir = tmpdir("#{Puppet::FileSystem.basename(target)}_existing") + existing_symlink = tmpfile("#{Puppet::FileSystem.basename(existing_dir)}_link") + Puppet::FileSystem.symlink(existing_dir, existing_symlink) - existing_symlink.readlink.should == existing_dir.path.to_s + Puppet::FileSystem.readlink(existing_symlink).should == existing_dir.to_s - target.symlink(existing_symlink.path, { :force => true }).should == 0 + Puppet::FileSystem.symlink(target, existing_symlink, { :force => true }).should == 0 - existing_symlink.readlink.should == existing_dir.path.to_s + Puppet::FileSystem.readlink(existing_symlink).should == existing_dir.to_s end end @@ -295,184 +293,184 @@ describe Puppet::FileSystem::File do [ tmpfile('bogus1'), Pathname.new(tmpfile('bogus2')), Puppet::Util::WatchedFile.new(tmpfile('bogus3')) - ].each { |f| Puppet::FileSystem::File.exist?(f).should be_false } + ].each { |f| Puppet::FileSystem.exist?(f).should be_false } end it "should return a File::Stat instance when calling stat on an existing file" do - file.stat.should be_instance_of(File::Stat) + Puppet::FileSystem.stat(file).should be_instance_of(File::Stat) end it "should raise Errno::ENOENT when calling stat on a missing file" do - expect { missing_file.stat }.to raise_error(Errno::ENOENT) + expect { Puppet::FileSystem.stat(missing_file) }.to raise_error(Errno::ENOENT) end it "should be able to create a symlink, and verify it with symlink?" do - symlink = Puppet::FileSystem::File.new(tmpfile("somefile_link")) - file.symlink(symlink.path) + symlink = tmpfile("somefile_link") + Puppet::FileSystem.symlink(file, symlink) - symlink.symlink?.should be_true + Puppet::FileSystem.symlink?(symlink).should be_true end it "should report symlink? as false on file, directory and missing files" do [file, dir, missing_file].each do |f| - f.symlink?.should be_false + Puppet::FileSystem.symlink?(f).should be_false end end it "should return a File::Stat with ftype 'link' when calling lstat on a symlink pointing to existing file" do - symlink = Puppet::FileSystem::File.new(tmpfile("somefile_link")) - file.symlink(symlink.path) + symlink = tmpfile("somefile_link") + Puppet::FileSystem.symlink(file, symlink) - stat = symlink.lstat + stat = Puppet::FileSystem.lstat(symlink) stat.should be_instance_of(File::Stat) stat.ftype.should == 'link' end it "should return a File::Stat of ftype 'link' when calling lstat on a symlink pointing to missing file" do - symlink = Puppet::FileSystem::File.new(tmpfile("somefile_link")) - missing_file.symlink(symlink.path) + symlink = tmpfile("somefile_link") + Puppet::FileSystem.symlink(missing_file, symlink) - stat = symlink.lstat + stat = Puppet::FileSystem.lstat(symlink) stat.should be_instance_of(File::Stat) stat.ftype.should == 'link' end it "should return a File::Stat of ftype 'file' when calling stat on a symlink pointing to existing file" do - symlink = Puppet::FileSystem::File.new(tmpfile("somefile_link")) - file.symlink(symlink.path) + symlink = tmpfile("somefile_link") + Puppet::FileSystem.symlink(file, symlink) - stat = symlink.stat + stat = Puppet::FileSystem.stat(symlink) stat.should be_instance_of(File::Stat) stat.ftype.should == 'file' end it "should return a File::Stat of ftype 'directory' when calling stat on a symlink pointing to existing directory" do - symlink = Puppet::FileSystem::File.new(tmpfile("somefile_link")) - dir.symlink(symlink.path) + symlink = tmpfile("somefile_link") + Puppet::FileSystem.symlink(dir, symlink) - stat = symlink.stat + stat = Puppet::FileSystem.stat(symlink) stat.should be_instance_of(File::Stat) stat.ftype.should == 'directory' # on Windows, this won't get cleaned up if still linked - symlink.unlink + Puppet::FileSystem.unlink(symlink) end it "should return a File::Stat of ftype 'file' when calling stat on a symlink pointing to another symlink" do # point symlink -> file - symlink = Puppet::FileSystem::File.new(tmpfile("somefile_link")) - file.symlink(symlink.path) + symlink = tmpfile("somefile_link") + Puppet::FileSystem.symlink(file, symlink) # point symlink2 -> symlink - symlink2 = Puppet::FileSystem::File.new(tmpfile("somefile_link2")) - symlink.symlink(symlink2.path) + symlink2 = tmpfile("somefile_link2") + Puppet::FileSystem.symlink(symlink, symlink2) - symlink2.stat.ftype.should == 'file' + Puppet::FileSystem.stat(symlink2).ftype.should == 'file' end it "should raise Errno::ENOENT when calling stat on a dangling symlink" do - symlink = Puppet::FileSystem::File.new(tmpfile("somefile_link")) - missing_file.symlink(symlink.path) + symlink = tmpfile("somefile_link") + Puppet::FileSystem.symlink(missing_file, symlink) - expect { symlink.stat }.to raise_error(Errno::ENOENT) + expect { Puppet::FileSystem.stat(symlink) }.to raise_error(Errno::ENOENT) end it "should be able to readlink to resolve the physical path to a symlink" do - symlink = Puppet::FileSystem::File.new(tmpfile("somefile_link")) - file.symlink(symlink.path) + symlink = tmpfile("somefile_link") + Puppet::FileSystem.symlink(file, symlink) - file.exist?.should be_true - symlink.readlink.should == file.path.to_s + Puppet::FileSystem.exist?(file).should be_true + Puppet::FileSystem.readlink(symlink).should == file.to_s end it "should not resolve entire symlink chain with readlink on a symlink'd symlink" do # point symlink -> file - symlink = Puppet::FileSystem::File.new(tmpfile("somefile_link")) - file.symlink(symlink.path) + symlink = tmpfile("somefile_link") + Puppet::FileSystem.symlink(file, symlink) # point symlink2 -> symlink - symlink2 = Puppet::FileSystem::File.new(tmpfile("somefile_link2")) - symlink.symlink(symlink2.path) + symlink2 = tmpfile("somefile_link2") + Puppet::FileSystem.symlink(symlink, symlink2) - file.exist?.should be_true - symlink2.readlink.should == symlink.path.to_s + Puppet::FileSystem.exist?(file).should be_true + Puppet::FileSystem.readlink(symlink2).should == symlink.to_s end it "should be able to readlink to resolve the physical path to a dangling symlink" do - symlink = Puppet::FileSystem::File.new(tmpfile("somefile_link")) - missing_file.symlink(symlink.path) + symlink = tmpfile("somefile_link") + Puppet::FileSystem.symlink(missing_file, symlink) - missing_file.exist?.should be_false - symlink.readlink.should == missing_file.path.to_s + Puppet::FileSystem.exist?(missing_file).should be_false + Puppet::FileSystem.readlink(symlink).should == missing_file.to_s end it "should delete only the symlink and not the target when calling unlink instance method" do [file, dir].each do |target| - symlink = Puppet::FileSystem::File.new(tmpfile("#{target.path.basename.to_s}_link")) - target.symlink(symlink.path) + symlink = tmpfile("#{Puppet::FileSystem.basename(target)}_link") + Puppet::FileSystem.symlink(target, symlink) - target.exist?.should be_true - symlink.readlink.should == target.path.to_s + Puppet::FileSystem.exist?(target).should be_true + Puppet::FileSystem.readlink(symlink).should == target.to_s - symlink.unlink.should == 1 # count of files + Puppet::FileSystem.unlink(symlink).should == 1 # count of files - target.exist?.should be_true - symlink.exist?.should be_false + Puppet::FileSystem.exist?(target).should be_true + Puppet::FileSystem.exist?(symlink).should be_false end end it "should delete only the symlink and not the target when calling unlink class method" do [file, dir].each do |target| - symlink = Puppet::FileSystem::File.new(tmpfile("#{target.path.basename.to_s}_link")) - target.symlink(symlink.path) + symlink = tmpfile("#{Puppet::FileSystem.basename(target)}_link") + Puppet::FileSystem.symlink(target, symlink) - target.exist?.should be_true - symlink.readlink.should == target.path.to_s + Puppet::FileSystem.exist?(target).should be_true + Puppet::FileSystem.readlink(symlink).should == target.to_s - Puppet::FileSystem::File.unlink(symlink.path).should == 1 # count of files + Puppet::FileSystem.unlink(symlink).should == 1 # count of files - target.exist?.should be_true - symlink.exist?.should be_false + Puppet::FileSystem.exist?(target).should be_true + Puppet::FileSystem.exist?(symlink).should be_false end end describe "unlink" do it "should delete files with unlink" do - file.exist?.should be_true + Puppet::FileSystem.exist?(file).should be_true - file.unlink.should == 1 # count of files + Puppet::FileSystem.unlink(file).should == 1 # count of files - file.exist?.should be_false + Puppet::FileSystem.exist?(file).should be_false end it "should delete files with unlink class method" do - file.exist?.should be_true + Puppet::FileSystem.exist?(file).should be_true - Puppet::FileSystem::File.unlink(file.path).should == 1 # count of files + Puppet::FileSystem.unlink(file).should == 1 # count of files - file.exist?.should be_false + Puppet::FileSystem.exist?(file).should be_false end it "should delete multiple files with unlink class method" do paths = (1..3).collect do |i| - f = Puppet::FileSystem::File.new(tmpfile("somefile_#{i}")) - FileUtils.touch(f.path) - f.exist?.should be_true - f.path.to_s + f = tmpfile("somefile_#{i}") + FileUtils.touch(f) + Puppet::FileSystem.exist?(f).should be_true + f.to_s end - Puppet::FileSystem::File.unlink(*paths).should == 3 # count of files + Puppet::FileSystem.unlink(*paths).should == 3 # count of files - paths.each { |p| Puppet::FileSystem::File.exist?(p).should be_false } + paths.each { |p| Puppet::FileSystem.exist?(p).should be_false } end it "should raise Errno::EPERM or Errno::EISDIR when trying to delete a directory with the unlink class method" do - dir.exist?.should be_true + Puppet::FileSystem.exist?(dir).should be_true ex = nil begin - Puppet::FileSystem::File.unlink(dir.path) + Puppet::FileSystem.unlink(dir) rescue Exception => e ex = e end @@ -480,9 +478,9 @@ describe Puppet::FileSystem::File do [ Errno::EPERM, # Windows and OSX Errno::EISDIR # Linux - ].should include ex.class + ].should include(ex.class) - dir.exist?.should be_true + Puppet::FileSystem.exist?(dir).should be_true end end end From 2cdcce4a2a7c0eb4e8c440c78f2f776d82478011 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 5 Jan 2014 01:53:29 +0100 Subject: [PATCH 362/800] (PUP-716) Refactor tempfile_spec after FileSystem API change --- spec/unit/file_system/tempfile_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/unit/file_system/tempfile_spec.rb b/spec/unit/file_system/tempfile_spec.rb index 5ad0a8abc..eb13b0406 100644 --- a/spec/unit/file_system/tempfile_spec.rb +++ b/spec/unit/file_system/tempfile_spec.rb @@ -12,7 +12,7 @@ describe Puppet::FileSystem::Tempfile do file.write("stuff") file.flush - expect(Puppet::FileSystem::File.new(file.path).read).to eq("stuff") + expect(Puppet::FileSystem.read(file.path)).to eq("stuff") end end @@ -29,7 +29,7 @@ describe Puppet::FileSystem::Tempfile do file.path end - expect(Puppet::FileSystem::File.new(filename).exist?).to be_false + expect(Puppet::FileSystem.exist?(filename)).to be_false end it "unlinks the temporary file even if the block raises an error" do @@ -43,6 +43,6 @@ describe Puppet::FileSystem::Tempfile do rescue end - expect(Puppet::FileSystem::File.new(filename).exist?).to be_false + expect(Puppet::FileSystem.exist?(filename)).to be_false end end From f28781c12626b1e8c74f7afec610244055d2e3ff Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 5 Jan 2014 02:01:26 +0100 Subject: [PATCH 363/800] (PUP-716) Fix up base_spec tests after FileSystem API change --- spec/unit/file_serving/base_spec.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/spec/unit/file_serving/base_spec.rb b/spec/unit/file_serving/base_spec.rb index a5652d4ff..5b9794211 100755 --- a/spec/unit/file_serving/base_spec.rb +++ b/spec/unit/file_serving/base_spec.rb @@ -56,8 +56,7 @@ describe Puppet::FileServing::Base do end it "should correctly indicate if the file is present" do - mock_file = mock(file, :lstat => stub('stat')) - Puppet::FileSystem.expects(:new).with(file).returns mock_file + Puppet::FileSystem.expects(:lstat).with(file).returns stub('stat') Puppet::FileServing::Base.new(file).exist?.should be_true end @@ -127,7 +126,7 @@ describe Puppet::FileServing::Base do let(:stubbed_file) { stub(path, :stat => stat, :lstat => stat)} it "should stat the file's full path" do - Puppet::FileSystem.expects(:stat).with(path).returns stat + Puppet::FileSystem.expects(:lstat).with(path).returns stat file.stat end From 7c0d05143d9231c496d9b4e504863298c33b3c5a Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 5 Jan 2014 02:06:18 +0100 Subject: [PATCH 364/800] (PUP-716) Fix reference to removed FileSystem::File --- lib/puppet/file_serving/mount/file.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/file_serving/mount/file.rb b/lib/puppet/file_serving/mount/file.rb index e72a7ba63..2b48c88ea 100644 --- a/lib/puppet/file_serving/mount/file.rb +++ b/lib/puppet/file_serving/mount/file.rb @@ -22,7 +22,7 @@ class Puppet::FileServing::Mount::File < Puppet::FileServing::Mount file = ::File.join(full_path, relative_path) - if !(Puppet::FileSystem::File.exist?(file) or Puppet::FileSystem.symlink?(file)) + if !(Puppet::FileSystem.exist?(file) or Puppet::FileSystem.symlink?(file)) Puppet.info("File does not exist or is not accessible: #{file}") return nil end From dae0a9dbe00e66e71d143d62f1a4af650c66373f Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 5 Jan 2014 02:19:35 +0100 Subject: [PATCH 365/800] (PUP-716) Fix typos after change of FileSystem API writable? is spelled just like that A BucketFile is not a Pathname Removed reference to FileSystem::File --- lib/puppet/file_system.rb | 4 ++-- lib/puppet/indirector/file_bucket_file/file.rb | 2 +- spec/unit/file_serving/mount/file_spec.rb | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/puppet/file_system.rb b/lib/puppet/file_system.rb index c0a287db0..52ff3fd94 100644 --- a/lib/puppet/file_system.rb +++ b/lib/puppet/file_system.rb @@ -70,7 +70,7 @@ module Puppet::FileSystem # @api public # def self.size(path) - @impl.size(assert_path(path.basename)) + @impl.size(assert_path(path)) end # Allows exclusive updates to a file to be made by excluding concurrent @@ -148,7 +148,7 @@ module Puppet::FileSystem # @api public # def self.writable?(path) - @impl.writeable?(assert_path(path)) + @impl.writable?(assert_path(path)) end # Touches the file. On most systems this updates the mtime of the file. diff --git a/lib/puppet/indirector/file_bucket_file/file.rb b/lib/puppet/indirector/file_bucket_file/file.rb index 858cc30df..f9669e987 100644 --- a/lib/puppet/indirector/file_bucket_file/file.rb +++ b/lib/puppet/indirector/file_bucket_file/file.rb @@ -87,7 +87,7 @@ module Puppet::FileBucketFile Puppet::FileSystem.touch(contents_file) else Puppet::FileSystem.open(contents_file, 0440, 'wb') do |of| - of.write(Puppet::FileSystem.contents(bucket_file)) + of.write(bucket_file.contents) end end diff --git a/spec/unit/file_serving/mount/file_spec.rb b/spec/unit/file_serving/mount/file_spec.rb index 423e82bd6..f889b8eb1 100755 --- a/spec/unit/file_serving/mount/file_spec.rb +++ b/spec/unit/file_serving/mount/file_spec.rb @@ -141,7 +141,7 @@ describe Puppet::FileServing::Mount::File do include FileServingMountTesting before do - Puppet::FileSystem::File.stubs(:exist?).returns(true) + Puppet::FileSystem.stubs(:exist?).returns(true) FileTest.stubs(:directory?).returns(true) FileTest.stubs(:readable?).returns(true) @mount = Puppet::FileServing::Mount::File.new("test") From 45a7fd26a09fc33932d1d4e74b7d7c744efa77f4 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 5 Jan 2014 02:24:59 +0100 Subject: [PATCH 366/800] (PUP-716) Fix reference to FileSystem::File in implementation This also removed the file_2.rb that was merged into Puppet::FileSystem --- lib/puppet/file_system/file_2.rb | 279 --------------------- lib/puppet/pops/parser/lexer2.rb | 2 +- lib/puppet/provider/service/daemontools.rb | 2 +- lib/puppet/provider/service/freebsd.rb | 2 +- lib/puppet/provider/service/init.rb | 2 +- lib/puppet/provider/service/runit.rb | 2 +- 6 files changed, 5 insertions(+), 284 deletions(-) delete mode 100644 lib/puppet/file_system/file_2.rb diff --git a/lib/puppet/file_system/file_2.rb b/lib/puppet/file_system/file_2.rb deleted file mode 100644 index e674cc82f..000000000 --- a/lib/puppet/file_system/file_2.rb +++ /dev/null @@ -1,279 +0,0 @@ -# An abstraction over the ruby file system operations for a single file. -# -# For the time being this is being kept private so that we can evolve it for a -# while. -# -# @api private -class Puppet::FileSystem::File - - # create instance of the file system implementation to use for the current platform - @impl = if RUBY_VERSION =~ /^1\.8/ - require 'puppet/file_system/file18' - Puppet::FileSystem::File18 - elsif Puppet::Util::Platform.windows? - require 'puppet/file_system/file19windows' - Puppet::FileSystem::File19Windows - else - require 'puppet/file_system/file19' - Puppet::FileSystem::File19 - end.new() - - # Overrides the automatic file system implementation selection that is based on the current platform - # Should only be used for testing. - # @return [Object] the previous file system implementation (to allow it to be restored) - # - # @api private - # - def self.set_file_system_implementation(impl) - tmp = @impl - @impl = impl - tmp - end - - # Opens the given path with given mode, and options and optionally yields it to the given block. - # - # @api public - # - def self.open(path, mode, options, &block) - @impl.open(assert_path(path), options, mode, &block) - end - - # @return [Pathname] The directory of this file - # - # @api public - # - def dir(path) - @impl.dir(assert_path(path)) - end - - # @return [String] the name of the file - # - # @api public - # - def self.basename(path) - @impl.basename(assert_path(path.basename)) - end - - # @return [Integer] the size of the file - # - # @api public - # - def self.basename(path) - @impl.size(assert_path(path.basename)) - end - - # Allows exclusive updates to a file to be made by excluding concurrent - # access using flock. This means that if the file is on a filesystem that - # does not support flock, this method will provide no protection. - # - # While polling to aquire the lock the process will wait ever increasing - # amounts of time in order to prevent multiple processes from wasting - # resources. - # - # @param path [Pathname] the path to the file to operate on - # @param mode [Integer] The mode to apply to the file if it is created - # @param options [Integer] Extra file operation mode information to use - # (defaults to read-only mode) - # @param timeout [Integer] Number of seconds to wait for the lock (defaults to 300) - # @yield The file handle, in read-write mode - # @return [Void] - # @raise [Timeout::Error] If the timeout is exceeded while waiting to acquire the lock - # - # @api public - # - def self.exclusive_open(path, mode, options = 'r', timeout = 300, &block) - @impl.exclusive_open(assert_path(path), mode, options, timeout, &block) - end - - # Processes each line of the file by yielding it to the given block - # - # @api public - # - def self.each_line(path, &block) - @impl.each_line(assert_path(path), &block) - end - - # @return [String] The contents of the file - # - # @api public - # - def self.read(path) - path.read - end - - # @return [String] The binary contents of the file - # - # @api public - # - def binread(path) - @impl.binread(assert_path(path)) - end - - # Determines if a file exists by verifying that the file can be stat'd. - # Will follow symlinks and verify that the actual target path exists. - # - # @return [Boolean] true if the named file exists. - # - # @api public - # - def self.exist?(path) - @impl.exist?(assert_path(path)) - end - - # Determines if a file is executable. - # - # @todo Should this take into account extensions on the windows platform? - # - # @return [Boolean] true if this file can be executed - # - # @api public - # - def self.executable?(path) - @impl.executable?(assert_path(path)) - end - - # @return [Boolean] Whether the file is writable by the current process - # - # @api public - # - def self.writable?(path) - @impl.writeable?(assert_path(path)) - end - - # Touches the file. On most systems this updates the mtime of the file. - # - # @api public - # - def self.touch(path) - @impl.touch(assert_path(path)) - end - - # Creates directories for all parts of the given path. - # - # @api public - # - def self.mkpath(path) - @impl.mkpath(assert_path(path)) - end - - # Creates a symbolic link dest which points to the current file. - # If dest already exists: - # - # * and is a file, will raise Errno::EEXIST - # * and is a directory, will return 0 but perform no action - # * and is a symlink referencing a file, will raise Errno::EEXIST - # * and is a symlink referencing a directory, will return 0 but perform no action - # - # With the :force option set to true, when dest already exists: - # - # * and is a file, will replace the existing file with a symlink (DANGEROUS) - # * and is a directory, will return 0 but perform no action - # * and is a symlink referencing a file, will modify the existing symlink - # * and is a symlink referencing a directory, will return 0 but perform no action - # - # @param dest [String] The path to create the new symlink at - # @param [Hash] options the options to create the symlink with - # @option options [Boolean] :force overwrite dest - # @option options [Boolean] :noop do not perform the operation - # @option options [Boolean] :verbose verbose output - # - # @raise [Errno::EEXIST] dest already exists as a file and, :force is not set - # - # @return [Integer] 0 - # - # @api public - # - def self.symlink(path, dest, options = {}) - @impl.symlink(assert_path(path), dest, options) - end - - # @return [Boolean] true if the file is a symbolic link. - # - # @api public - # - def self.symlink?(path) - @impl.symlink?(assert_path(path)) - end - - # @return [String] the name of the file referenced by the given link. - # - # @api public - # - def self.readlink(path) - @impl.readlink(assert_path(path)) - end - - # Deletes the given paths, returning the number of names passed as arguments. - # See also Dir::rmdir. - # - # @raise an exception on any error. - # - # @return [Integer] the number of paths passed as arguments - # - # @api public - # - def self.unlink(*paths) - paths.each {|p| assert_path(p) } - @impl.unlink(*paths) - end - - # @return [File::Stat] object for the named file. - # - # @api public - # - def stat(path) - @impl.stat(assert_path(path)) - end - - # @return [File::Stat] Same as stat, but does not follow the last symbolic - # link. Instead, reports on the link itself. - # - # @api public - # - def lstat(path) - @impl.lstat(assert_path(path)) - end - - # Compares the contents of this file against the contents of a stream. - # - # @param stream [IO] The stream to compare the contents against - # @return [Boolean] Whether the contents were the same - # - # @api public - # - def compare_stream(path, stream) - @impl.compare_stream(assert_path(path), stream) - end - - # Produces an opaque pathname "handle" object representing the given path. - # Different implementations of the underlying file system may use different runtime - # objects. The produced "handle" should be used in all other operations - # that take a "path". No operation should be directly invoked on the returned opaque object - # - # @return [Object] An opaque path handle on which no operations should be directly performed - # - # @api public - # - def self.pathname(path) - @impl.pathname(path) - end - - # Asserts that the given path is of the expected type produced by #pathname - # - # @raise [ArgumentError] when path is not of the expected type - # - # @api public - # - def self.assert_path(path) - @impl.assert_path(path) - end - - # Produces a string representation of the opaque path handle. - # - # @return [String] a string representation of the path - # - def self.path_string(path) - @impl.path_string(path) - end - -end diff --git a/lib/puppet/pops/parser/lexer2.rb b/lib/puppet/pops/parser/lexer2.rb index 9633c1c54..a15225346 100644 --- a/lib/puppet/pops/parser/lexer2.rb +++ b/lib/puppet/pops/parser/lexer2.rb @@ -225,7 +225,7 @@ class Puppet::Pops::Parser::Lexer2 # def lex_file(file) initvars - contents = Puppet::FileSystem::File.exist?(file) ? File.read(file) : "" + contents = Puppet::FileSystem.exist?(file) ? File.read(file) : "" @scanner = StringScanner.new(contents.freeze) @locator = Puppet::Pops::Parser::Locator.locator(contents, file) end diff --git a/lib/puppet/provider/service/daemontools.rb b/lib/puppet/provider/service/daemontools.rb index b6766560e..7f7f40632 100644 --- a/lib/puppet/provider/service/daemontools.rb +++ b/lib/puppet/provider/service/daemontools.rb @@ -74,7 +74,7 @@ Puppet::Type.type(:service).provide :daemontools, :parent => :base do # or don't contain a run file Dir.entries(path).reject { |e| fullpath = File.join(path, e) - e =~ /^\./ or ! FileTest.directory?(fullpath) or ! Puppet::FileSystem::File.exist?(File.join(fullpath,"run")) + e =~ /^\./ or ! FileTest.directory?(fullpath) or ! Puppet::FileSystem.exist?(File.join(fullpath,"run")) }.collect do |name| new(:name => name, :path => path) end diff --git a/lib/puppet/provider/service/freebsd.rb b/lib/puppet/provider/service/freebsd.rb index 36386cb24..1e59fc47d 100644 --- a/lib/puppet/provider/service/freebsd.rb +++ b/lib/puppet/provider/service/freebsd.rb @@ -71,7 +71,7 @@ Puppet::Type.type(:service).provide :freebsd, :parent => :init do success = false # Replace in all files, not just in the first found with a match [rcconf, rcconf_local, rcconf_dir + "/#{service}"].each do |filename| - if Puppet::FileSystem::File.exist?(filename) + if Puppet::FileSystem.exist?(filename) s = File.read(filename) if s.gsub!(/^(#{rcvar}(_enable)?)=\"?(YES|NO)\"?/, "\\1=\"#{yesno}\"") File.open(filename, File::WRONLY) { |f| f << s } diff --git a/lib/puppet/provider/service/init.rb b/lib/puppet/provider/service/init.rb index b38c98034..291cc5b06 100644 --- a/lib/puppet/provider/service/init.rb +++ b/lib/puppet/provider/service/init.rb @@ -99,7 +99,7 @@ Puppet::Type.type(:service).provide :init, :parent => :base do if File.directory?(path) true else - if Puppet::FileSystem::File.exist?(path) + if Puppet::FileSystem.exist?(path) self.debug "Search path #{path} is not a directory" else self.debug "Search path #{path} does not exist" diff --git a/lib/puppet/provider/service/runit.rb b/lib/puppet/provider/service/runit.rb index 57df0a78b..8395c6da3 100644 --- a/lib/puppet/provider/service/runit.rb +++ b/lib/puppet/provider/service/runit.rb @@ -42,7 +42,7 @@ Puppet::Type.type(:service).provide :runit, :parent => :daemontools do def defpath(dummy_argument=:work_arround_for_ruby_GC_bug) unless @defpath ["/etc/sv", "/var/lib/service"].each do |path| - if Puppet::FileSystem::File.exist?(path) + if Puppet::FileSystem.exist?(path) @defpath = path break end From e9d2c997da953b417177c2f2c9cd7896568596da Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 5 Jan 2014 02:29:11 +0100 Subject: [PATCH 367/800] (PUP-716) Remove references to FileSystem::File in non code --- spec/unit/file_serving/fileset_spec.rb | 4 ++-- spec/unit/file_system/file_spec.rb | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/spec/unit/file_serving/fileset_spec.rb b/spec/unit/file_serving/fileset_spec.rb index ff90e4ce4..5f7e76db1 100755 --- a/spec/unit/file_serving/fileset_spec.rb +++ b/spec/unit/file_serving/fileset_spec.rb @@ -244,14 +244,14 @@ describe Puppet::FileServing::Fileset do @fileset.files.find { |file| file.include?(".svn") or file.include?("CVS") }.should be_nil end - it "uses Puppet::FileSystem::File#stat if :links is set to :follow" do + it "uses Puppet::FileSystem#stat if :links is set to :follow" do mock_dir_structure(@path, :stat) @fileset.recurse = true @fileset.links = :follow @fileset.files.sort.should == @files.sort end - it "uses Puppet::FileSystem::File#lstat if :links is set to :manage" do + it "uses Puppet::FileSystem#lstat if :links is set to :manage" do mock_dir_structure(@path, :lstat) @fileset.recurse = true @fileset.links = :manage diff --git a/spec/unit/file_system/file_spec.rb b/spec/unit/file_system/file_spec.rb index 79fe13e5a..1d7e2efcb 100644 --- a/spec/unit/file_system/file_spec.rb +++ b/spec/unit/file_system/file_spec.rb @@ -8,7 +8,6 @@ describe "Puppet::FileSystem" do context "#exclusive_open" do it "opens ands allows updating of an existing file" do file = file_containing("file_to_update", "the contents") -# file = Puppet::FileSystem::File.new(file_containing("file_to_update", "the contents")) Puppet::FileSystem.exclusive_open(file, 0660, 'r+') do |fh| old = fh.read From 3aa4c98e8f0456b6e553bf68287f136cf14cec52 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 5 Jan 2014 15:53:07 +0100 Subject: [PATCH 368/800] (PUP-716) Fix up direct_file_server_spec after FileServer API change --- spec/unit/indirector/direct_file_server_spec.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spec/unit/indirector/direct_file_server_spec.rb b/spec/unit/indirector/direct_file_server_spec.rb index 8b08195bb..b47b13a11 100755 --- a/spec/unit/indirector/direct_file_server_spec.rb +++ b/spec/unit/indirector/direct_file_server_spec.rb @@ -26,12 +26,12 @@ describe Puppet::Indirector::DirectFileServer do describe Puppet::Indirector::DirectFileServer, "when finding a single file" do it "should return nil if the file does not exist" do - Puppet::FileSystem::File.expects(:exist?).with(@path).returns false + Puppet::FileSystem.expects(:exist?).with(@path).returns false @server.find(@request).should be_nil end it "should return a Content instance created with the full path to the file if the file exists" do - Puppet::FileSystem::File.expects(:exist?).with(@path).returns true + Puppet::FileSystem.expects(:exist?).with(@path).returns true @model.expects(:new).returns(:mycontent) @server.find(@request).should == :mycontent end @@ -42,7 +42,7 @@ describe Puppet::Indirector::DirectFileServer do before do @data = mock 'content' @data.stubs(:collect) - Puppet::FileSystem::File.expects(:exist?).with(@path).returns true + Puppet::FileSystem.expects(:exist?).with(@path).returns true end it "should pass the full path to the instance" do @@ -61,18 +61,18 @@ describe Puppet::Indirector::DirectFileServer do describe Puppet::Indirector::DirectFileServer, "when searching for multiple files" do it "should return nil if the file does not exist" do - Puppet::FileSystem::File.expects(:exist?).with(@path).returns false + Puppet::FileSystem.expects(:exist?).with(@path).returns false @server.find(@request).should be_nil end it "should use :path2instances from the terminus_helper to return instances if the file exists" do - Puppet::FileSystem::File.expects(:exist?).with(@path).returns true + Puppet::FileSystem.expects(:exist?).with(@path).returns true @server.expects(:path2instances) @server.search(@request) end it "should pass the original request to :path2instances" do - Puppet::FileSystem::File.expects(:exist?).with(@path).returns true + Puppet::FileSystem.expects(:exist?).with(@path).returns true @server.expects(:path2instances).with(@request, @path) @server.search(@request) end From b79bcfafe9a6318493fe8f8f058c4de88fe86117 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 5 Jan 2014 15:54:21 +0100 Subject: [PATCH 369/800] (PUP-716) Fix up file_server_spec after FileSystem API change --- spec/unit/indirector/file_server_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/unit/indirector/file_server_spec.rb b/spec/unit/indirector/file_server_spec.rb index 6f491b065..9e354556d 100755 --- a/spec/unit/indirector/file_server_spec.rb +++ b/spec/unit/indirector/file_server_spec.rb @@ -154,7 +154,7 @@ describe Puppet::Indirector::FileServer do @mount.expects(:search).with { |key, request| key == "rel/path" }.returns %w{/one /two} - Puppet::FileSystem::File.stubs(:exist?).returns true + Puppet::FileSystem.stubs(:exist?).returns true one = mock 'fileset_one' Puppet::FileServing::Fileset.expects(:new).with("/one", @request).returns(one) @@ -171,7 +171,7 @@ describe Puppet::Indirector::FileServer do @mount.expects(:search).with { |key, request| key == "rel/path" }.returns [] - Puppet::FileSystem::File.stubs(:exist?).returns true + Puppet::FileSystem.stubs(:exist?).returns true Puppet::FileServing::Fileset.expects(:merge).returns("one" => "/one", "two" => "/two") @@ -193,7 +193,7 @@ describe Puppet::Indirector::FileServer do @mount.expects(:search).with { |key, request| key == "rel/path" }.returns [] - Puppet::FileSystem::File.stubs(:exist?).returns true + Puppet::FileSystem.stubs(:exist?).returns true Puppet::FileServing::Fileset.expects(:merge).returns("one" => "/one") @@ -211,7 +211,7 @@ describe Puppet::Indirector::FileServer do @mount.expects(:search).with { |key, options| key == "rel/path" }.returns [] - Puppet::FileSystem::File.stubs(:exist?).returns true + Puppet::FileSystem.stubs(:exist?).returns true Puppet::FileServing::Fileset.expects(:merge).returns("one" => "/one") From 1970b55fdab8a9d2c3b2336c573b760cd8903968 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 5 Jan 2014 15:56:07 +0100 Subject: [PATCH 370/800] (PUP-716) Fix up json_spec after FileSystem API change --- spec/unit/indirector/json_spec.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spec/unit/indirector/json_spec.rb b/spec/unit/indirector/json_spec.rb index 664a32749..53431fc8e 100755 --- a/spec/unit/indirector/json_spec.rb +++ b/spec/unit/indirector/json_spec.rb @@ -120,7 +120,7 @@ describe Puppet::Indirector::JSON do subject.save(request) - File.should be_directory target + File.should be_directory(target) end end @@ -131,20 +131,20 @@ describe Puppet::Indirector::JSON do with_content('hello') do subject.destroy(request) end - Puppet::FileSystem::File.exist?(file).should be_false + Puppet::FileSystem.exist?(file).should be_false end it "silently succeeds when files don't exist" do - Puppet::FileSystem::File.unlink(file) rescue nil + Puppet::FileSystem.unlink(file) rescue nil subject.destroy(request).should be_true end it "raises an informative error for other failures" do - Puppet::FileSystem::File.stubs(:unlink).with(file).raises(Errno::EPERM, 'fake permission problem') + Puppet::FileSystem.stubs(:unlink).with(file).raises(Errno::EPERM, 'fake permission problem') with_content('hello') do - expect { subject.destroy(request) }.to raise_error Puppet::Error + expect { subject.destroy(request) }.to raise_error(Puppet::Error) end - Puppet::FileSystem::File.unstub(:unlink) # thanks, mocha + Puppet::FileSystem.unstub(:unlink) # thanks, mocha end end end From 25d71566a702abf75780afc191d289db025982b4 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 5 Jan 2014 16:01:17 +0100 Subject: [PATCH 371/800] (PUP-716) Fix up ssl_file_spec, yaml_spec after FileSystem API change --- spec/unit/indirector/ssl_file_spec.rb | 23 ++++++++++++----------- spec/unit/indirector/yaml_spec.rb | 8 ++++---- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/spec/unit/indirector/ssl_file_spec.rb b/spec/unit/indirector/ssl_file_spec.rb index 8406cc9d7..8d13bdc94 100755 --- a/spec/unit/indirector/ssl_file_spec.rb +++ b/spec/unit/indirector/ssl_file_spec.rb @@ -121,9 +121,9 @@ describe Puppet::Indirector::SslFile do describe "when finding certificates on disk" do describe "and no certificate is present" do it "should return nil" do - Puppet::FileSystem::File.expects(:exist?).with(@path).returns(true) + Puppet::FileSystem.expects(:exist?).with(@path).returns(true) Dir.expects(:entries).with(@path).returns([]) - Puppet::FileSystem::File.expects(:exist?).with(@certpath).returns(false) + Puppet::FileSystem.expects(:exist?).with(@certpath).returns(false) @searcher.find(@request).should be_nil end @@ -139,7 +139,7 @@ describe Puppet::Indirector::SslFile do context "is readable" do it "should return an instance of the model, which it should use to read the certificate" do - Puppet::FileSystem::File.expects(:exist?).with(@certpath).returns true + Puppet::FileSystem.expects(:exist?).with(@certpath).returns true model.expects(:new).with("myname").returns cert cert.expects(:read).with(@certpath) @@ -150,7 +150,7 @@ describe Puppet::Indirector::SslFile do context "is unreadable" do it "should raise an exception" do - Puppet::FileSystem::File.expects(:exist?).with(@certpath).returns(true) + Puppet::FileSystem.expects(:exist?).with(@certpath).returns(true) model.expects(:new).with("myname").returns cert cert.expects(:read).with(@certpath).raises(Errno::EACCES) @@ -171,9 +171,9 @@ describe Puppet::Indirector::SslFile do # the support for upper-case certs can be removed around mid-2009. it "should rename the existing file to the lower-case path" do @path = @searcher.path("myhost") - Puppet::FileSystem::File.expects(:exist?).with(@path).returns(false) + Puppet::FileSystem.expects(:exist?).with(@path).returns(false) dir, file = File.split(@path) - Puppet::FileSystem::File.expects(:exist?).with(dir).returns true + Puppet::FileSystem.expects(:exist?).with(dir).returns true Dir.expects(:entries).with(dir).returns [".", "..", "something.pem", file.upcase] File.expects(:rename).with(File.join(dir, file.upcase), @path) @@ -256,7 +256,7 @@ describe Puppet::Indirector::SslFile do describe "when destroying certificates" do describe "that do not exist" do before do - Puppet::FileSystem::File.expects(:exist?).with(@certpath).returns false + Puppet::FileSystem.expects(:exist?).with(Puppet::FileSystem.pathname(@certpath)).returns false end it "should return false" do @@ -266,14 +266,15 @@ describe Puppet::Indirector::SslFile do describe "that exist" do it "should unlink the certificate file" do - Puppet::FileSystem::File.expects(:exist?).with(@certpath).returns true - Puppet::FileSystem::File.expects(:unlink).with(@certpath) + path = Puppet::FileSystem.pathname(@certpath) + Puppet::FileSystem.expects(:exist?).with(path).returns true + Puppet::FileSystem.expects(:unlink).with(path) @searcher.destroy(@request) end it "should log that is removing the file" do - Puppet::FileSystem::File.stubs(:exist?).returns true - Puppet::FileSystem::File.stubs(:unlink) + Puppet::FileSystem.stubs(:exist?).returns true + Puppet::FileSystem.stubs(:unlink) Puppet.expects(:notice) @searcher.destroy(@request) end diff --git a/spec/unit/indirector/yaml_spec.rb b/spec/unit/indirector/yaml_spec.rb index 6830869b1..6fae8831c 100755 --- a/spec/unit/indirector/yaml_spec.rb +++ b/spec/unit/indirector/yaml_spec.rb @@ -149,15 +149,15 @@ describe Puppet::Indirector::Yaml do end it "should unlink the right yaml file if it exists" do - Puppet::FileSystem::File.expects(:exist?).with(path).returns true - Puppet::FileSystem::File.expects(:unlink).with(path) + Puppet::FileSystem.expects(:exist?).with(path).returns true + Puppet::FileSystem.expects(:unlink).with(path) @store.destroy(@request) end it "should not unlink the yaml file if it does not exists" do - Puppet::FileSystem::File.expects(:exist?).with(path).returns false - Puppet::FileSystem::File.expects(:unlink).with(path).never + Puppet::FileSystem.expects(:exist?).with(path).returns false + Puppet::FileSystem.expects(:unlink).with(path).never @store.destroy(@request) end From 09a4d48ae4e1d827a01050ffa354c4ab8e22c12e Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 5 Jan 2014 16:06:01 +0100 Subject: [PATCH 372/800] (PUP-716) Fix up unit/network specs after FileSystem API change --- spec/unit/network/authconfig_spec.rb | 3 +-- spec/unit/network/authentication_spec.rb | 8 ++++---- spec/unit/network/http_pool_spec.rb | 4 ++-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/spec/unit/network/authconfig_spec.rb b/spec/unit/network/authconfig_spec.rb index be45152c0..fbca5dfb5 100755 --- a/spec/unit/network/authconfig_spec.rb +++ b/spec/unit/network/authconfig_spec.rb @@ -5,8 +5,7 @@ require 'puppet/network/authconfig' describe Puppet::Network::AuthConfig do before :each do - stub_file = stub('file', :stat => stub('stat', :ctime => :now)) - Puppet::FileSystem::File.stubs(:new).returns stub_file + Puppet::FileSystem.stubs(:stat).returns stub('stat', :ctime => :now) Time.stubs(:now).returns Time.now Puppet::Network::AuthConfig.any_instance.stubs(:exists?).returns(true) diff --git a/spec/unit/network/authentication_spec.rb b/spec/unit/network/authentication_spec.rb index 90308e924..8f3653cad 100755 --- a/spec/unit/network/authentication_spec.rb +++ b/spec/unit/network/authentication_spec.rb @@ -20,7 +20,7 @@ describe Puppet::Network::Authentication do describe "when warning about upcoming expirations" do before do Puppet::SSL::CertificateAuthority.stubs(:ca?).returns(false) - Puppet::FileSystem::File.stubs(:exist?).returns(false) + Puppet::FileSystem.stubs(:exist?).returns(false) end it "should check the expiration of the CA certificate" do @@ -34,19 +34,19 @@ describe Puppet::Network::Authentication do context "when examining the local host" do before do Puppet::SSL::Host.stubs(:localhost).returns(host) - Puppet::FileSystem::File.stubs(:exist?).with(Puppet[:hostcert]).returns(true) + Puppet::FileSystem.stubs(:exist?).with(Puppet[:hostcert]).returns(true) end it "should not load the localhost certificate if the local CA certificate is missing" do # Redmine-21869: Infinite recursion occurs if CA cert is missing. - Puppet::FileSystem::File.stubs(:exist?).with(Puppet[:localcacert]).returns(false) + Puppet::FileSystem.stubs(:exist?).with(Puppet[:localcacert]).returns(false) host.unstub(:certificate) host.expects(:certificate).never subject.warn_if_near_expiration end it "should check the expiration of the localhost certificate if the local CA certificate is present" do - Puppet::FileSystem::File.stubs(:exist?).with(Puppet[:localcacert]).returns(true) + Puppet::FileSystem.stubs(:exist?).with(Puppet[:localcacert]).returns(true) cert.expects(:near_expiration?).returns(false) subject.warn_if_near_expiration end diff --git a/spec/unit/network/http_pool_spec.rb b/spec/unit/network/http_pool_spec.rb index 4ee507568..98b8e043a 100755 --- a/spec/unit/network/http_pool_spec.rb +++ b/spec/unit/network/http_pool_spec.rb @@ -32,12 +32,12 @@ describe Puppet::Network::HttpPool do ca_cert_file = File.expand_path('/path/to/ssl/certs/ca_cert.pem') Puppet[:ssl_client_ca_auth] = ca_cert_file - Puppet::FileSystem::File.stubs(:exist?).with(ca_cert_file).returns(true) + Puppet::FileSystem.stubs(:exist?).with(ca_cert_file).returns(true) end def setup_standard_hostcert host_cert_file = File.expand_path('/path/to/ssl/certs/host_cert.pem') - Puppet::FileSystem::File.stubs(:exist?).with(host_cert_file).returns(true) + Puppet::FileSystem.stubs(:exist?).with(host_cert_file).returns(true) Puppet[:hostcert] = host_cert_file end From 897f317a5f1f23757623edef341ae4f5b02a9d52 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 5 Jan 2014 16:06:48 +0100 Subject: [PATCH 373/800] (maint) Add () around potentially problematic Ruby expression --- spec/unit/network/http_pool_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/unit/network/http_pool_spec.rb b/spec/unit/network/http_pool_spec.rb index 98b8e043a..fc10a9827 100755 --- a/spec/unit/network/http_pool_spec.rb +++ b/spec/unit/network/http_pool_spec.rb @@ -67,7 +67,7 @@ describe Puppet::Network::HttpPool do it "should not cache http instances" do Puppet::Network::HttpPool.http_instance("me", 54321). - should_not equal Puppet::Network::HttpPool.http_instance("me", 54321) + should_not equal(Puppet::Network::HttpPool.http_instance("me", 54321)) end end From 6d3075cf3ea44a67afe6cf57533f613a76aa0b3c Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 5 Jan 2014 16:10:45 +0100 Subject: [PATCH 374/800] (PUP-716) Fix up FileSystem API change, tests indirector, parser, node --- .../indirector/file_bucket_file/file_spec.rb | 8 +++---- .../indirector/file_metadata/file_spec.rb | 4 ++-- spec/unit/indirector/key/file_spec.rb | 16 +++++++------- spec/unit/node/environment_spec.rb | 2 +- spec/unit/parser/files_spec.rb | 22 +++++++++---------- spec/unit/parser/functions/generate_spec.rb | 6 ++--- spec/unit/parser/lexer_spec.rb | 2 +- spec/unit/parser/parser_spec.rb | 2 +- 8 files changed, 31 insertions(+), 31 deletions(-) diff --git a/spec/unit/indirector/file_bucket_file/file_spec.rb b/spec/unit/indirector/file_bucket_file/file_spec.rb index 2f9e140f7..338aaf999 100755 --- a/spec/unit/indirector/file_bucket_file/file_spec.rb +++ b/spec/unit/indirector/file_bucket_file/file_spec.rb @@ -68,10 +68,10 @@ describe Puppet::FileBucketFile::File do checksum = save_bucket_file("stuff\r\n", "/foo/bar") dir_path = "#{Puppet[:bucketdir]}/f/c/7/7/7/c/0/b/fc777c0bc467e1ab98b4c6915af802ec" - contents_file = Puppet::FileSystem::File.new("#{dir_path}/contents") - paths_file = Puppet::FileSystem::File.new("#{dir_path}/paths") - contents_file.binread.should == "stuff\r\n" - paths_file.read.should == "foo/bar\n" + contents_file = "#{dir_path}/contents" + paths_file = "#{dir_path}/paths" + Puppet::FileSystem.binread(contents_file).should == "stuff\r\n" + Puppet::FileSystem.read(paths_file).should == "foo/bar\n" end it "should leave the paths file alone if the path is already stored" do diff --git a/spec/unit/indirector/file_metadata/file_spec.rb b/spec/unit/indirector/file_metadata/file_spec.rb index 459c1dbe8..fd6ef8ce5 100755 --- a/spec/unit/indirector/file_metadata/file_spec.rb +++ b/spec/unit/indirector/file_metadata/file_spec.rb @@ -19,7 +19,7 @@ describe Puppet::Indirector::FileMetadata::File do @uri = Puppet::Util.path_to_uri(@path).to_s @data = mock 'metadata' @data.stubs(:collect) - Puppet::FileSystem::File.expects(:exist?).with(@path).returns true + Puppet::FileSystem.expects(:exist?).with(@path).returns true @request = Puppet::Indirector::Request.new(:file_metadata, :find, @uri, nil) end @@ -42,7 +42,7 @@ describe Puppet::Indirector::FileMetadata::File do end it "should collect the attributes of the instances returned" do - Puppet::FileSystem::File.expects(:exist?).with(@path).returns true + Puppet::FileSystem.expects(:exist?).with(@path).returns true @metadata.expects(:path2instances).returns( [mock("one", :collect => nil), mock("two", :collect => nil)] ) @metadata.search(@request) end diff --git a/spec/unit/indirector/key/file_spec.rb b/spec/unit/indirector/key/file_spec.rb index 95c212523..86abc83ad 100755 --- a/spec/unit/indirector/key/file_spec.rb +++ b/spec/unit/indirector/key/file_spec.rb @@ -76,20 +76,20 @@ describe Puppet::SSL::Key::File do end it "should destroy the public key when destroying the private key" do - Puppet::FileSystem::File.stubs(:unlink).with(@private_key_path) - Puppet::FileSystem::File.stubs(:exist?).with(@private_key_path).returns true - Puppet::FileSystem::File.expects(:exist?).with(@public_key_path).returns true - Puppet::FileSystem::File.expects(:unlink).with(@public_key_path) + Puppet::FileSystem.expects(:unlink).with(@private_key_path) + Puppet::FileSystem.expects(:exist?).with(@private_key_path).returns true + Puppet::FileSystem.expects(:exist?).with(@public_key_path).returns true + Puppet::FileSystem.expects(:unlink).with(@public_key_path) @searcher.destroy(@request) end it "should not fail if the public key does not exist when deleting the private key" do - Puppet::FileSystem::File.stubs(:unlink).with(@private_key_path) + Puppet::FileSystem.stubs(:unlink).with(@private_key_path) - Puppet::FileSystem::File.stubs(:exist?).with(@private_key_path).returns true - Puppet::FileSystem::File.expects(:exist?).with(@public_key_path).returns false - Puppet::FileSystem::File.expects(:unlink).with(@public_key_path).never + Puppet::FileSystem.stubs(:exist?).with(@private_key_path).returns true + Puppet::FileSystem.expects(:exist?).with(@public_key_path).returns false + Puppet::FileSystem.expects(:unlink).with(@public_key_path).never @searcher.destroy(@request) end diff --git a/spec/unit/node/environment_spec.rb b/spec/unit/node/environment_spec.rb index 9fd149f18..4420d6744 100755 --- a/spec/unit/node/environment_spec.rb +++ b/spec/unit/node/environment_spec.rb @@ -435,7 +435,7 @@ describe Puppet::Node::Environment do end it "should fail helpfully if there is an error importing" do - Puppet::FileSystem::File.stubs(:exist?).returns true + Puppet::FileSystem.stubs(:exist?).returns true env.stubs(:known_resource_types).returns Puppet::Resource::TypeCollection.new(env) @parser.expects(:file=).once @parser.expects(:parse).raises ArgumentError diff --git a/spec/unit/parser/files_spec.rb b/spec/unit/parser/files_spec.rb index ca7e45b13..3813260b4 100755 --- a/spec/unit/parser/files_spec.rb +++ b/spec/unit/parser/files_spec.rb @@ -28,7 +28,7 @@ describe Puppet::Parser::Files do Puppet[:templatedir] = "/my/templates" Puppet[:modulepath] = "/one:/two" File.stubs(:directory?).returns(true) - Puppet::FileSystem::File.stubs(:exist?).returns(true) + Puppet::FileSystem.stubs(:exist?).returns(true) Puppet::Parser::Files.find_template("mymod/mytemplate").should == File.join(Puppet[:templatedir], "mymod/mytemplate") end @@ -43,59 +43,59 @@ describe Puppet::Parser::Files do end it "should return unqualified templates if they exist in the template dir" do - Puppet::FileSystem::File.stubs(:exist?).returns true + Puppet::FileSystem.stubs(:exist?).returns true Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(["/my/templates"]) Puppet::Parser::Files.find_template("mytemplate").should == "/my/templates/mytemplate" end it "should only return templates if they actually exist" do - Puppet::FileSystem::File.expects(:exist?).with("/my/templates/mytemplate").returns true + Puppet::FileSystem.expects(:exist?).with("/my/templates/mytemplate").returns true Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(["/my/templates"]) Puppet::Parser::Files.find_template("mytemplate").should == "/my/templates/mytemplate" end it "should return nil when asked for a template that doesn't exist" do - Puppet::FileSystem::File.expects(:exist?).with("/my/templates/mytemplate").returns false + Puppet::FileSystem.expects(:exist?).with("/my/templates/mytemplate").returns false Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(["/my/templates"]) Puppet::Parser::Files.find_template("mytemplate").should be_nil end it "should search in the template directories before modules" do - Puppet::FileSystem::File.stubs(:exist?).returns true + Puppet::FileSystem.stubs(:exist?).returns true Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(["/my/templates"]) Puppet::Module.expects(:find).never Puppet::Parser::Files.find_template("mytemplate") end it "should accept relative templatedirs" do - Puppet::FileSystem::File.stubs(:exist?).returns true + Puppet::FileSystem.stubs(:exist?).returns true Puppet[:templatedir] = "my/templates" 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 - Puppet::FileSystem::File.stubs(:exist?).returns true + Puppet::FileSystem.stubs(:exist?).returns true Puppet::Parser::Files.stubs(:templatepath).with("myenv").returns(["/myenv/templates"]) Puppet::Parser::Files.find_template("mymod/mytemplate", "myenv").should == "/myenv/templates/mymod/mytemplate" end it "should use first dir from environment templatedir if no module is found and an environment is specified" do - Puppet::FileSystem::File.stubs(:exist?).returns true + Puppet::FileSystem.stubs(:exist?).returns true Puppet::Parser::Files.stubs(:templatepath).with("myenv").returns(["/myenv/templates", "/two/templates"]) Puppet::Parser::Files.find_template("mymod/mytemplate", "myenv").should == "/myenv/templates/mymod/mytemplate" end it "should use a valid dir when templatedir is a path for unqualified templates and the first dir contains template" do Puppet::Parser::Files.stubs(:templatepath).returns(["/one/templates", "/two/templates"]) - Puppet::FileSystem::File.expects(:exist?).with("/one/templates/mytemplate").returns(true) + Puppet::FileSystem.expects(:exist?).with("/one/templates/mytemplate").returns(true) Puppet::Parser::Files.find_template("mytemplate").should == "/one/templates/mytemplate" end it "should use a valid dir when templatedir is a path for unqualified templates and only second dir contains template" do Puppet::Parser::Files.stubs(:templatepath).returns(["/one/templates", "/two/templates"]) - Puppet::FileSystem::File.expects(:exist?).with("/one/templates/mytemplate").returns(false) - Puppet::FileSystem::File.expects(:exist?).with("/two/templates/mytemplate").returns(true) + Puppet::FileSystem.expects(:exist?).with("/one/templates/mytemplate").returns(false) + Puppet::FileSystem.expects(:exist?).with("/two/templates/mytemplate").returns(true) Puppet::Parser::Files.find_template("mytemplate").should == "/two/templates/mytemplate" end diff --git a/spec/unit/parser/functions/generate_spec.rb b/spec/unit/parser/functions/generate_spec.rb index f4b85b8f2..832ac13ae 100755 --- a/spec/unit/parser/functions/generate_spec.rb +++ b/spec/unit/parser/functions/generate_spec.rb @@ -107,7 +107,7 @@ describe "the generate function" do end after :each do - File.delete(command) if Puppet::FileSystem::File.exist?(command) + File.delete(command) if Puppet::FileSystem.exist?(command) end it "should call generator with no arguments" do @@ -123,10 +123,10 @@ describe "the generate function" do end it "should fail if generator is not absolute" do - expect { scope.function_generate(['boo']) }.to raise_error Puppet::ParseError + expect { scope.function_generate(['boo']) }.to raise_error(Puppet::ParseError) end it "should fail if generator fails" do - expect { scope.function_generate(['/boo']) }.to raise_error Puppet::ParseError + expect { scope.function_generate(['/boo']) }.to raise_error(Puppet::ParseError) end end diff --git a/spec/unit/parser/lexer_spec.rb b/spec/unit/parser/lexer_spec.rb index 972a8f1bf..62234e214 100755 --- a/spec/unit/parser/lexer_spec.rb +++ b/spec/unit/parser/lexer_spec.rb @@ -861,7 +861,7 @@ describe "when trying to lex a non-existent file" do it "should return an empty list of tokens" do lexer = Puppet::Parser::Lexer.new lexer.file = nofile = tmpfile('lexer') - Puppet::FileSystem::File.exist?(nofile).should == false + Puppet::FileSystem.exist?(nofile).should == false lexer.fullscan.should == [[false,false]] end diff --git a/spec/unit/parser/parser_spec.rb b/spec/unit/parser/parser_spec.rb index c87881f72..0d9cf66d0 100755 --- a/spec/unit/parser/parser_spec.rb +++ b/spec/unit/parser/parser_spec.rb @@ -53,7 +53,7 @@ describe Puppet::Parser do describe "when parsing files" do before do - Puppet::FileSystem::File.stubs(:exist?).returns true + Puppet::FileSystem.stubs(:exist?).returns true File.stubs(:read).returns "" @parser.stubs(:watch_file) end From 6574ecc50281a51da2c3e29c75f98e0e025b2cca Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 5 Jan 2014 16:52:08 +0100 Subject: [PATCH 375/800] (PUP-716) Fix up tests after FileSystem API change One fixed test still failing on expectations --- spec/unit/pops/parser/lexer_spec.rb | 2 +- spec/unit/provider/augeas/augeas_spec.rb | 6 ++-- spec/unit/provider/file/posix_spec.rb | 4 +-- .../nameservice/directoryservice_spec.rb | 6 ++-- spec/unit/provider/package/apt_spec.rb | 2 +- spec/unit/provider/package/openbsd_spec.rb | 20 ++++++------- spec/unit/provider/service/base_spec.rb | 8 ++--- .../unit/provider/service/daemontools_spec.rb | 24 +++++++-------- spec/unit/provider/service/freebsd_spec.rb | 6 ++-- spec/unit/provider/service/gentoo_spec.rb | 8 ++--- spec/unit/provider/service/init_spec.rb | 30 +++++++++---------- spec/unit/provider/service/openwrt_spec.rb | 2 +- spec/unit/provider/service/runit_spec.rb | 13 ++++---- spec/unit/provider/service/upstart_spec.rb | 8 ++--- 14 files changed, 67 insertions(+), 72 deletions(-) diff --git a/spec/unit/pops/parser/lexer_spec.rb b/spec/unit/pops/parser/lexer_spec.rb index 19f52ce8c..40d9b3e51 100755 --- a/spec/unit/pops/parser/lexer_spec.rb +++ b/spec/unit/pops/parser/lexer_spec.rb @@ -725,7 +725,7 @@ describe "when trying to lex a non-existent file" do it "should return an empty list of tokens" do lexer = Puppet::Pops::Parser::Lexer.new lexer.file = nofile = tmpfile('lexer') - Puppet::FileSystem::File.exist?(nofile).should == false + Puppet::FileSystem.exist?(nofile).should == false lexer.fullscan.should == [[false,false]] end diff --git a/spec/unit/provider/augeas/augeas_spec.rb b/spec/unit/provider/augeas/augeas_spec.rb index 3593d69f6..13c0412b1 100755 --- a/spec/unit/provider/augeas/augeas_spec.rb +++ b/spec/unit/provider/augeas/augeas_spec.rb @@ -661,7 +661,7 @@ describe provider_class do @augeas.expects(:respond_to?).with("clearm").returns(false) @augeas.expects(:set).with("/foo/test[1]/Jar/Jar", "Foo").returns(true) @augeas.expects(:set).with("/foo/test[2]/Jar/Jar", "Bar").returns(true) - expect { @provider.execute_changes }.to raise_error RuntimeError, /command 'clearm' not supported/ + expect { @provider.execute_changes }.to raise_error(RuntimeError, /command 'clearm' not supported/) end end @@ -674,7 +674,7 @@ describe provider_class do link = tmpfile('link') target = tmpfile('target') FileUtils.touch(target) - Puppet::FileSystem::File.new(target).symlink(link) + Puppet::FileSystem.symlink(target, link) resource = Puppet::Type.type(:augeas).new( :name => 'test', @@ -689,7 +689,7 @@ describe provider_class do catalog.apply File.ftype(link).should == 'link' - Puppet::FileSystem::File.new(link).readlink().should == target + Puppet::FileSystem.readlink(link).should == target File.read(target).should =~ /PermitRootLogin no/ end end diff --git a/spec/unit/provider/file/posix_spec.rb b/spec/unit/provider/file/posix_spec.rb index 48eaae30a..b4fe4b9ce 100755 --- a/spec/unit/provider/file/posix_spec.rb +++ b/spec/unit/provider/file/posix_spec.rb @@ -85,7 +85,7 @@ describe Puppet::Type.type(:file).provider(:posix), :if => Puppet.features.posix describe "#owner" do it "should return the uid of the file owner" do FileUtils.touch(path) - owner = Puppet::FileSystem::File.new(path).stat.uid + owner = Puppet::FileSystem.stat(path).uid provider.owner.should == owner end @@ -178,7 +178,7 @@ describe Puppet::Type.type(:file).provider(:posix), :if => Puppet.features.posix describe "#group" do it "should return the gid of the file group" do FileUtils.touch(path) - group = Puppet::FileSystem::File.new(path).stat.gid + group = Puppet::FileSystem.stat(path).gid provider.group.should == group end diff --git a/spec/unit/provider/nameservice/directoryservice_spec.rb b/spec/unit/provider/nameservice/directoryservice_spec.rb index fbb74d0f3..2b8c29d74 100755 --- a/spec/unit/provider/nameservice/directoryservice_spec.rb +++ b/spec/unit/provider/nameservice/directoryservice_spec.rb @@ -110,7 +110,7 @@ describe 'DirectoryService password behavior' do it 'should execute convert_binary_to_xml once when getting the password on >= 10.7' do subject.expects(:convert_binary_to_xml).returns({'SALTED-SHA512' => StringIO.new(pw_string)}) - Puppet::FileSystem::File.expects(:exist?).with(plist_path).once.returns(true) + Puppet::FileSystem.expects(:exist?).with(plist_path).once.returns(true) Plist.expects(:parse_xml).returns(shadow_hash_data) # On Mac OS X 10.7 we first need to convert to xml when reading the password subject.expects(:plutil).with('-convert', 'xml1', '-o', '/dev/stdout', plist_path) @@ -126,7 +126,7 @@ describe 'DirectoryService password behavior' do it 'should convert xml-to-binary and binary-to-xml when setting the pw on >= 10.7' do subject.expects(:convert_binary_to_xml).returns({'SALTED-SHA512' => StringIO.new(pw_string)}) subject.expects(:convert_xml_to_binary).returns(binary_plist) - Puppet::FileSystem::File.expects(:exist?).with(plist_path).once.returns(true) + Puppet::FileSystem.expects(:exist?).with(plist_path).once.returns(true) Plist.expects(:parse_xml).returns(shadow_hash_data) # On Mac OS X 10.7 we first need to convert to xml subject.expects(:plutil).with('-convert', 'xml1', '-o', '/dev/stdout', plist_path) @@ -138,7 +138,7 @@ describe 'DirectoryService password behavior' do it '[#13686] should handle an empty ShadowHashData field in the users plist' do subject.expects(:convert_xml_to_binary).returns(binary_plist) - Puppet::FileSystem::File.expects(:exist?).with(plist_path).once.returns(true) + Puppet::FileSystem.expects(:exist?).with(plist_path).once.returns(true) Plist.expects(:parse_xml).returns({'ShadowHashData' => nil}) subject.expects(:plutil).with('-convert', 'xml1', '-o', '/dev/stdout', plist_path) subject.expects(:plutil).with('-convert', 'binary1', plist_path) diff --git a/spec/unit/provider/package/apt_spec.rb b/spec/unit/provider/package/apt_spec.rb index 5d7b3e4e6..a60e1f206 100755 --- a/spec/unit/provider/package/apt_spec.rb +++ b/spec/unit/provider/package/apt_spec.rb @@ -63,7 +63,7 @@ Version table: it "should preseed with the provided responsefile when preseeding is called for" do @resource.expects(:[]).with(:responsefile).returns "/my/file" - Puppet::FileSystem::File.expects(:exist?).with("/my/file").returns true + Puppet::FileSystem.expects(:exist?).with("/my/file").returns true @provider.expects(:info) @provider.expects(:preseed).with("/my/file") diff --git a/spec/unit/provider/package/openbsd_spec.rb b/spec/unit/provider/package/openbsd_spec.rb index 48ec4bedb..8d4f079fe 100755 --- a/spec/unit/provider/package/openbsd_spec.rb +++ b/spec/unit/provider/package/openbsd_spec.rb @@ -10,22 +10,22 @@ describe provider_class do def expect_read_from_pkgconf(lines) pkgconf = stub(:readlines => lines) - Puppet::FileSystem::File.expects(:exist?).with('/etc/pkg.conf').returns(true) + Puppet::FileSystem.expects(:exist?).with('/etc/pkg.conf').returns(true) File.expects(:open).with('/etc/pkg.conf', 'rb').returns(pkgconf) end def expect_pkgadd_with_source(source) provider.expects(:pkgadd).with do |fullname| - ENV.should_not be_key 'PKG_PATH' + ENV.should_not be_key('PKG_PATH') fullname.should == source end end def expect_pkgadd_with_env_and_name(source, &block) - ENV.should_not be_key 'PKG_PATH' + ENV.should_not be_key('PKG_PATH') provider.expects(:pkgadd).with do |fullname| - ENV.should be_key 'PKG_PATH' + ENV.should be_key('PKG_PATH') ENV['PKG_PATH'].should == source fullname.should == provider.resource[:name] @@ -34,7 +34,7 @@ describe provider_class do yield - ENV.should_not be_key 'PKG_PATH' + ENV.should_not be_key('PKG_PATH') end before :each do @@ -76,27 +76,27 @@ describe provider_class do context "#install" do it "should fail if the resource doesn't have a source" do - Puppet::FileSystem::File.expects(:exist?).with('/etc/pkg.conf').returns(false) + Puppet::FileSystem.expects(:exist?).with('/etc/pkg.conf').returns(false) expect { provider.install - }.to raise_error Puppet::Error, /must specify a package source/ + }.to raise_error(Puppet::Error, /must specify a package source/) end it "should fail if /etc/pkg.conf exists, but is not readable" do - Puppet::FileSystem::File.expects(:exist?).with('/etc/pkg.conf').returns(true) + Puppet::FileSystem.expects(:exist?).with('/etc/pkg.conf').returns(true) File.expects(:open).with('/etc/pkg.conf', 'rb').raises(Errno::EACCES) expect { provider.install - }.to raise_error Errno::EACCES, /Permission denied/ + }.to raise_error(Errno::EACCES, /Permission denied/) end it "should fail if /etc/pkg.conf exists, but there is no installpath" do expect_read_from_pkgconf([]) expect { provider.install - }.to raise_error Puppet::Error, /No valid installpath found in \/etc\/pkg\.conf and no source was set/ + }.to raise_error(Puppet::Error, /No valid installpath found in \/etc\/pkg\.conf and no source was set/) end it "should install correctly when given a directory-unlike source" do diff --git a/spec/unit/provider/service/base_spec.rb b/spec/unit/provider/service/base_spec.rb index 9d6a7fdcc..eb8c65034 100755 --- a/spec/unit/provider/service/base_spec.rb +++ b/spec/unit/provider/service/base_spec.rb @@ -34,15 +34,15 @@ describe "base service provider" do end before :each do - Puppet::FileSystem::File.unlink(flag) if Puppet::FileSystem::File.exist?(flag) + Puppet::FileSystem.unlink(flag) if Puppet::FileSystem.exist?(flag) end it { should be } it "should invoke the start command if not running" do - File.should_not be_file flag + File.should_not be_file(flag) subject.start - File.should be_file flag + File.should be_file(flag) end it "should be stopped before being started" do @@ -59,7 +59,7 @@ describe "base service provider" do subject.status.should == :running subject.stop subject.status.should == :stopped - File.should_not be_file flag + File.should_not be_file(flag) end it "should start again even if already running" do diff --git a/spec/unit/provider/service/daemontools_spec.rb b/spec/unit/provider/service/daemontools_spec.rb index 4c35e0586..a1041f9e5 100755 --- a/spec/unit/provider/service/daemontools_spec.rb +++ b/spec/unit/provider/service/daemontools_spec.rb @@ -101,12 +101,13 @@ describe provider_class do describe "when enabling" do it "should create a symlink between daemon dir and service dir", :if => Puppet.features.manages_symlinks? do daemon_path = File.join(@daemondir, "myservice") - stub_daemon = stub(daemon_path, :symlink? => false) - Puppet::FileSystem::File.expects(:new).with(daemon_path).returns(stub_daemon) +# stub_daemon = stub(daemon_path, :symlink? => false) + Puppet::FileSystem.expects(:symlink?).with(daemon_path).returns(false) # stub_daemon) service_path = File.join(@servicedir, "myservice") - mock_service = mock(service_path, :symlink? => false) - Puppet::FileSystem::File.expects(:new).with(service_path).returns(mock_service) - stub_daemon.expects(:symlink).returns(0) +# mock_service = mock(service_path, :symlink? => false) + Puppet::FileSystem.expects(:symlink?).with(service_path).returns(false) #mock_service) + Puppet::FileSystem.expects(:symlink).returns(0) #mock_service) +# stub_daemon.expects(:symlink).returns(0) @provider.enable end end @@ -115,18 +116,16 @@ describe provider_class do it "should remove the symlink between daemon dir and service dir" do FileTest.stubs(:directory?).returns(false) path = File.join(@servicedir,"myservice") - mocked_file = mock(path, :symlink? => true) - Puppet::FileSystem::File.expects(:new).with(path).returns(mocked_file) - Puppet::FileSystem::File.expects(:unlink).with(path) + Puppet::FileSystem.expects(:symlink?).with(path).returns(true) + Puppet::FileSystem.expects(:unlink).with(path) @provider.stubs(:texecute).returns("") @provider.disable end it "should stop the service" do FileTest.stubs(:directory?).returns(false) - mocked_file = mock('anything', :symlink? => true) - Puppet::FileSystem::File.expects(:new).returns(mocked_file) - Puppet::FileSystem::File.stubs(:unlink) + Puppet::FileSystem.expects(:symlink?).returns(true) + Puppet::FileSystem.stubs(:unlink) @provider.expects(:stop) @provider.disable end @@ -143,8 +142,7 @@ describe provider_class do it "should return #{t} if the symlink exists" do @provider.stubs(:status).returns(:stopped) path = File.join(@servicedir,"myservice") - mocked_file = mock(path, :symlink? => t) - Puppet::FileSystem::File.expects(:new).with(path).returns(mocked_file) + Puppet::FileSystem.expects(:symlink?).with(path).returns(t) @provider.enabled?.should == "#{t}".to_sym end diff --git a/spec/unit/provider/service/freebsd_spec.rb b/spec/unit/provider/service/freebsd_spec.rb index 69b923f9a..8ef5fee78 100755 --- a/spec/unit/provider/service/freebsd_spec.rb +++ b/spec/unit/provider/service/freebsd_spec.rb @@ -62,13 +62,13 @@ OUTPUT end it "should enable only the selected service" do - Puppet::FileSystem::File.stubs(:exist?).with('/etc/rc.conf').returns(true) + Puppet::FileSystem.stubs(:exist?).with('/etc/rc.conf').returns(true) File.stubs(:read).with('/etc/rc.conf').returns("openntpd_enable=\"NO\"\nntpd_enable=\"NO\"\n") fh = stub 'fh' File.stubs(:open).with('/etc/rc.conf', File::WRONLY).yields(fh) fh.expects(:<<).with("openntpd_enable=\"NO\"\nntpd_enable=\"YES\"\n") - Puppet::FileSystem::File.stubs(:exist?).with('/etc/rc.conf.local').returns(false) - Puppet::FileSystem::File.stubs(:exist?).with('/etc/rc.conf.d/ntpd').returns(false) + Puppet::FileSystem.stubs(:exist?).with('/etc/rc.conf.local').returns(false) + Puppet::FileSystem.stubs(:exist?).with('/etc/rc.conf.d/ntpd').returns(false) @provider.rc_replace('ntpd', 'ntpd', 'YES') end diff --git a/spec/unit/provider/service/gentoo_spec.rb b/spec/unit/provider/service/gentoo_spec.rb index aa802430e..c68832a5b 100755 --- a/spec/unit/provider/service/gentoo_spec.rb +++ b/spec/unit/provider/service/gentoo_spec.rb @@ -14,8 +14,8 @@ describe Puppet::Type.type(:service).provider(:gentoo) do # before it even tries to execute an initscript. We use sshd in all the # tests so make sure it is considered present. sshd_path = '/etc/init.d/sshd' - stub_file = stub(sshd_path, :stat => stub('stat')) - Puppet::FileSystem::File.stubs(:new).with(sshd_path).returns stub_file +# stub_file = stub(sshd_path, :stat => stub('stat')) + Puppet::FileSystem.stubs(:stat).with(sshd_path).returns stub('stat') end let :initscripts do @@ -45,7 +45,7 @@ describe Puppet::Type.type(:service).provider(:gentoo) do describe ".instances" do it "should have an instances method" do - described_class.should respond_to :instances + described_class.should respond_to(:instances) end it "should get a list of services from /etc/init.d but exclude helper scripts" do @@ -58,7 +58,7 @@ describe Puppet::Type.type(:service).provider(:gentoo) do FileTest.expects(:executable?).with("/etc/init.d/#{script}").never end - Puppet::FileSystem::File.stubs(:new).returns stub('file', :symlink? => false) + Puppet::FileSystem.stubs(:symlink?).returns false # stub('file', :symlink? => false) described_class.instances.map(&:name).should == [ 'alsasound', 'bootmisc', diff --git a/spec/unit/provider/service/init_spec.rb b/spec/unit/provider/service/init_spec.rb index 896f13ac1..7890ba9ab 100755 --- a/spec/unit/provider/service/init_spec.rb +++ b/spec/unit/provider/service/init_spec.rb @@ -65,9 +65,9 @@ describe Puppet::Type.type(:service).provider(:init) do it "should discard upstart jobs", :if => Puppet.features.manages_symlinks? do not_init_service, *valid_services = @services path = "tmp/#{not_init_service}" - mocked_file = mock(path, :symlink? => true, :readlink => "/lib/init/upstart-job") - Puppet::FileSystem::File.stubs(:new).returns stub('file', :symlink? => false) - Puppet::FileSystem::File.expects(:new).with(path).returns(mocked_file) + Puppet::FileSystem.expects(:symlink?).at_least_once.returns false + Puppet::FileSystem.expects(:symlink?).with(Puppet::FileSystem.pathname(path)).returns(true) + Puppet::FileSystem.expects(:readlink).with(Puppet::FileSystem.pathname(path)).returns("/lib/init/upstart-job") described_class.instances.map(&:name).should == valid_services end @@ -82,7 +82,7 @@ describe Puppet::Type.type(:service).provider(:init) do describe "when checking valid paths" do it "should discard paths that do not exist" do File.expects(:directory?).with(paths[0]).returns false - Puppet::FileSystem::File.expects(:exist?).with(paths[0]).returns false + Puppet::FileSystem.expects(:exist?).with(paths[0]).returns false File.expects(:directory?).with(paths[1]).returns true provider.paths.should == [paths[1]] @@ -90,7 +90,7 @@ describe Puppet::Type.type(:service).provider(:init) do it "should discard paths that are not directories" do paths.each do |path| - Puppet::FileSystem::File.expects(:exist?).with(path).returns true + Puppet::FileSystem.expects(:exist?).with(path).returns true File.expects(:directory?).with(path).returns false end provider.paths.should be_empty @@ -103,28 +103,28 @@ describe Puppet::Type.type(:service).provider(:init) do end it "should be able to find the init script in the service path" do - Puppet::FileSystem::File.expects(:exist?).with("#{paths[0]}/myservice").returns true - Puppet::FileSystem::File.expects(:exist?).with("#{paths[1]}/myservice").never # first one wins + Puppet::FileSystem.expects(:exist?).with("#{paths[0]}/myservice").returns true + Puppet::FileSystem.expects(:exist?).with("#{paths[1]}/myservice").never # first one wins provider.initscript.should == "/service/path/myservice" end it "should be able to find the init script in an alternate service path" do - Puppet::FileSystem::File.expects(:exist?).with("#{paths[0]}/myservice").returns false - Puppet::FileSystem::File.expects(:exist?).with("#{paths[1]}/myservice").returns true + Puppet::FileSystem.expects(:exist?).with("#{paths[0]}/myservice").returns false + Puppet::FileSystem.expects(:exist?).with("#{paths[1]}/myservice").returns true provider.initscript.should == "/alt/service/path/myservice" end it "should be able to find the init script if it ends with .sh" do - Puppet::FileSystem::File.expects(:exist?).with("#{paths[0]}/myservice").returns false - Puppet::FileSystem::File.expects(:exist?).with("#{paths[1]}/myservice").returns false - Puppet::FileSystem::File.expects(:exist?).with("#{paths[0]}/myservice.sh").returns true + Puppet::FileSystem.expects(:exist?).with("#{paths[0]}/myservice").returns false + Puppet::FileSystem.expects(:exist?).with("#{paths[1]}/myservice").returns false + Puppet::FileSystem.expects(:exist?).with("#{paths[0]}/myservice.sh").returns true provider.initscript.should == "/service/path/myservice.sh" end it "should fail if the service isn't there" do paths.each do |path| - Puppet::FileSystem::File.expects(:exist?).with("#{path}/myservice").returns false - Puppet::FileSystem::File.expects(:exist?).with("#{path}/myservice.sh").returns false + Puppet::FileSystem.expects(:exist?).with("#{path}/myservice").returns false + Puppet::FileSystem.expects(:exist?).with("#{path}/myservice.sh").returns false end expect { provider.initscript }.to raise_error(Puppet::Error, "Could not find init script for 'myservice'") end @@ -134,7 +134,7 @@ describe Puppet::Type.type(:service).provider(:init) do before :each do File.stubs(:directory?).with("/service/path").returns true File.stubs(:directory?).with("/alt/service/path").returns true - Puppet::FileSystem::File.stubs(:exist?).with("/service/path/myservice").returns true + Puppet::FileSystem.stubs(:exist?).with("/service/path/myservice").returns true end [:start, :stop, :status, :restart].each do |method| diff --git a/spec/unit/provider/service/openwrt_spec.rb b/spec/unit/provider/service/openwrt_spec.rb index 2113aceb4..8d385d175 100755 --- a/spec/unit/provider/service/openwrt_spec.rb +++ b/spec/unit/provider/service/openwrt_spec.rb @@ -33,7 +33,7 @@ describe Puppet::Type.type(:service).provider(:openwrt), :as_platform => :posix # All OpenWrt tests operate on the init script directly. It must exist. File.stubs(:directory?).with('/etc/init.d').returns true - Puppet::FileSystem::File.stubs(:exist?).with('/etc/init.d/myservice').returns true + Puppet::FileSystem.stubs(:exist?).with('/etc/init.d/myservice').returns true FileTest.stubs(:file?).with('/etc/init.d/myservice').returns true FileTest.stubs(:executable?).with('/etc/init.d/myservice').returns true end diff --git a/spec/unit/provider/service/runit_spec.rb b/spec/unit/provider/service/runit_spec.rb index cbdc19ba6..58701309f 100755 --- a/spec/unit/provider/service/runit_spec.rb +++ b/spec/unit/provider/service/runit_spec.rb @@ -98,12 +98,9 @@ describe provider_class do describe "when enabling" do it "should create a symlink between daemon dir and service dir", :if => Puppet.features.manages_symlinks? do daemon_path = File.join(@daemondir,"myservice") - mock_daemon = mock(daemon_path) - Puppet::FileSystem::File.expects(:new).with(daemon_path).returns(mock_daemon) service_path = File.join(@servicedir,"myservice") - mock_service = mock(service_path, :symlink? => false) - Puppet::FileSystem::File.expects(:new).with(service_path).returns(mock_service) - mock_daemon.expects(:symlink).with(File.join(@servicedir,"myservice")).returns(0) + Puppet::FileSystem.expects(:symlink?).with(service_path).returns(false) + Puppet::FileSystem.expects(:symlink).with(daemon_path, File.join(@servicedir,"myservice")).returns(0) @provider.enable end end @@ -111,10 +108,10 @@ describe provider_class do describe "when disabling" do it "should remove the '/etc/service/myservice' symlink" do path = File.join(@servicedir,"myservice") - mocked_file = mock(path, :symlink? => true) +# mocked_file = mock(path, :symlink? => true) FileTest.stubs(:directory?).returns(false) - Puppet::FileSystem::File.expects(:new).with(path).returns(mocked_file) - Puppet::FileSystem::File.expects(:unlink).with(path).returns(0) + Puppet::FileSystem.expects(:symlink?).with(path).returns(true) # mocked_file) + Puppet::FileSystem.expects(:unlink).with(path).returns(0) @provider.disable end end diff --git a/spec/unit/provider/service/upstart_spec.rb b/spec/unit/provider/service/upstart_spec.rb index ed93386b3..249777e58 100755 --- a/spec/unit/provider/service/upstart_spec.rb +++ b/spec/unit/provider/service/upstart_spec.rb @@ -51,8 +51,8 @@ describe Puppet::Type.type(:service).provider(:upstart) do describe "#search" do it "searches through paths to find a matching conf file" do File.stubs(:directory?).returns(true) - Puppet::FileSystem::File.stubs(:exist?).returns(false) - Puppet::FileSystem::File.expects(:exist?).with("/etc/init/foo-bar.conf").returns(true) + Puppet::FileSystem.stubs(:exist?).returns(false) + Puppet::FileSystem.expects(:exist?).with("/etc/init/foo-bar.conf").returns(true) resource = Puppet::Type.type(:service).new(:name => "foo-bar", :provider => :upstart) provider = provider_class.new(resource) @@ -61,8 +61,8 @@ describe Puppet::Type.type(:service).provider(:upstart) do it "searches for just the name of a compound named service" do File.stubs(:directory?).returns(true) - Puppet::FileSystem::File.stubs(:exist?).returns(false) - Puppet::FileSystem::File.expects(:exist?).with("/etc/init/network-interface.conf").returns(true) + Puppet::FileSystem.stubs(:exist?).returns(false) + Puppet::FileSystem.expects(:exist?).with("/etc/init/network-interface.conf").returns(true) resource = Puppet::Type.type(:service).new(:name => "network-interface INTERFACE=lo", :provider => :upstart) provider = provider_class.new(resource) From 28e63af8f7ca1c94148e63391f34e6a168af7caa Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 5 Jan 2014 16:56:01 +0100 Subject: [PATCH 376/800] (PUP-716) Fix up tests after FileSystem API change (unit/provider) Still one test failing on expectation --- .../provider/ssh_authorized_key/parsed_spec.rb | 10 +++++----- spec/unit/provider/user/directoryservice_spec.rb | 14 +++++++------- spec/unit/provider/zone/solaris_spec.rb | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/spec/unit/provider/ssh_authorized_key/parsed_spec.rb b/spec/unit/provider/ssh_authorized_key/parsed_spec.rb index 65c94bfad..9a908a380 100755 --- a/spec/unit/provider/ssh_authorized_key/parsed_spec.rb +++ b/spec/unit/provider/ssh_authorized_key/parsed_spec.rb @@ -164,7 +164,7 @@ describe provider_class, :unless => Puppet.features.microsoft_windows? do end it "should create the directory" do - Puppet::FileSystem::File.stubs(:exist?).with("/tmp/.ssh_dir").returns false + Puppet::FileSystem.stubs(:exist?).with("/tmp/.ssh_dir").returns false Dir.expects(:mkdir).with("/tmp/.ssh_dir", 0700) @provider.flush end @@ -201,19 +201,19 @@ describe provider_class, :unless => Puppet.features.microsoft_windows? do end it "should create the directory if it doesn't exist" do - Puppet::FileSystem::File.stubs(:exist?).with(@dir).returns false + Puppet::FileSystem.stubs(:exist?).with(@dir).returns false Dir.expects(:mkdir).with(@dir,0700) @provider.flush end it "should not create or chown the directory if it already exist" do - Puppet::FileSystem::File.stubs(:exist?).with(@dir).returns false + Puppet::FileSystem.stubs(:exist?).with(@dir).returns false Dir.expects(:mkdir).never @provider.flush end it "should absolutely not chown the directory to the user if it creates it" do - Puppet::FileSystem::File.stubs(:exist?).with(@dir).returns false + Puppet::FileSystem.stubs(:exist?).with(@dir).returns false Dir.stubs(:mkdir).with(@dir,0700) uid = Puppet::Util.uid("nobody") File.expects(:chown).never @@ -221,7 +221,7 @@ describe provider_class, :unless => Puppet.features.microsoft_windows? do end it "should not create or chown the directory if it already exist" do - Puppet::FileSystem::File.stubs(:exist?).with(@dir).returns false + Puppet::FileSystem.stubs(:exist?).with(@dir).returns false Dir.expects(:mkdir).never File.expects(:chown).never @provider.flush diff --git a/spec/unit/provider/user/directoryservice_spec.rb b/spec/unit/provider/user/directoryservice_spec.rb index fe72bfc4f..ccf37742a 100755 --- a/spec/unit/provider/user/directoryservice_spec.rb +++ b/spec/unit/provider/user/directoryservice_spec.rb @@ -692,10 +692,10 @@ describe Puppet::Type.type(:user).provider(:directoryservice) do provider.class.get_salted_sha512_pbkdf2('iterations', pbkdf2_embedded_bplist_hash).should == pbkdf2_iterations_value end it "should return a Fixnum value when looking up the PBKDF2 iterations value" do - provider.class.get_salted_sha512_pbkdf2('iterations', pbkdf2_embedded_bplist_hash).should be_a_kind_of Fixnum + provider.class.get_salted_sha512_pbkdf2('iterations', pbkdf2_embedded_bplist_hash).should be_a_kind_of(Fixnum) end it "should raise an error if a field other than 'entropy', 'salt', or 'iterations' is passed" do - expect { provider.class.get_salted_sha512_pbkdf2('othervalue', pbkdf2_embedded_bplist_hash) }.to raise_error Puppet::Error, /Puppet has tried to read an incorrect value from the SALTED-SHA512-PBKDF2 hash. Acceptable fields are 'salt', 'entropy', or 'iterations'/ + expect { provider.class.get_salted_sha512_pbkdf2('othervalue', pbkdf2_embedded_bplist_hash) }.to raise_error(Puppet::Error, /Puppet has tried to read an incorrect value from the SALTED-SHA512-PBKDF2 hash. Acceptable fields are 'salt', 'entropy', or 'iterations'/) end end @@ -704,7 +704,7 @@ describe Puppet::Type.type(:user).provider(:directoryservice) do let(:stub_password_file) { stub('connection') } it 'should return a sha1 hash read from disk' do - Puppet::FileSystem::File.expects(:exist?).with(password_hash_file).returns(true) + Puppet::FileSystem.expects(:exist?).with(password_hash_file).returns(true) File.expects(:file?).with(password_hash_file).returns(true) File.expects(:readable?).with(password_hash_file).returns(true) File.expects(:new).with(password_hash_file).returns(stub_password_file) @@ -714,21 +714,21 @@ describe Puppet::Type.type(:user).provider(:directoryservice) do end it 'should return nil if the password_hash_file does not exist' do - Puppet::FileSystem::File.expects(:exist?).with(password_hash_file).returns(false) + Puppet::FileSystem.expects(:exist?).with(password_hash_file).returns(false) provider.class.get_sha1('user_guid').should == nil end it 'should return nil if the password_hash_file is not a file' do - Puppet::FileSystem::File.expects(:exist?).with(password_hash_file).returns(true) + Puppet::FileSystem.expects(:exist?).with(password_hash_file).returns(true) File.expects(:file?).with(password_hash_file).returns(false) provider.class.get_sha1('user_guid').should == nil end it 'should raise an error if the password_hash_file is not readable' do - Puppet::FileSystem::File.expects(:exist?).with(password_hash_file).returns(true) + Puppet::FileSystem.expects(:exist?).with(password_hash_file).returns(true) File.expects(:file?).with(password_hash_file).returns(true) File.expects(:readable?).with(password_hash_file).returns(false) - expect { provider.class.get_sha1('user_guid').should == nil }.to raise_error Puppet::Error, /Could not read password hash file at #{password_hash_file}/ + expect { provider.class.get_sha1('user_guid').should == nil }.to raise_error(Puppet::Error, /Could not read password hash file at #{password_hash_file}/) end end diff --git a/spec/unit/provider/zone/solaris_spec.rb b/spec/unit/provider/zone/solaris_spec.rb index c143cabd1..393df6b51 100755 --- a/spec/unit/provider/zone/solaris_spec.rb +++ b/spec/unit/provider/zone/solaris_spec.rb @@ -138,7 +138,7 @@ net: it "should not require path if sysidcfg is specified" do resource[:path] = '/mypath' resource[:sysidcfg] = 'dummy' - Puppet::FileSystem::File.stubs(:exist?).with('/mypath/root/etc/sysidcfg').returns true + Puppet::FileSystem.stubs(:exist?).with('/mypath/root/etc/sysidcfg').returns true File.stubs(:directory?).with('/mypath/root/etc').returns true provider.expects(:zoneadm).with(:boot) provider.start From eec0c81bb1bdf4dba094bd17ebc6f3df215f27ca Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 5 Jan 2014 22:40:21 +0100 Subject: [PATCH 377/800] (PUP-761) Fix issues with unit/indirector/key/filespec FileSystem API Mocking was wrong expected strings not the Patnames being passed. --- spec/unit/indirector/key/file_spec.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/spec/unit/indirector/key/file_spec.rb b/spec/unit/indirector/key/file_spec.rb index 86abc83ad..44b658cc2 100755 --- a/spec/unit/indirector/key/file_spec.rb +++ b/spec/unit/indirector/key/file_spec.rb @@ -76,20 +76,20 @@ describe Puppet::SSL::Key::File do end it "should destroy the public key when destroying the private key" do - Puppet::FileSystem.expects(:unlink).with(@private_key_path) - Puppet::FileSystem.expects(:exist?).with(@private_key_path).returns true - Puppet::FileSystem.expects(:exist?).with(@public_key_path).returns true - Puppet::FileSystem.expects(:unlink).with(@public_key_path) + Puppet::FileSystem.expects(:unlink).with(Puppet::FileSystem.pathname(@private_key_path)) + Puppet::FileSystem.expects(:exist?).with(Puppet::FileSystem.pathname(@private_key_path)).returns true + Puppet::FileSystem.expects(:exist?).with(Puppet::FileSystem.pathname(@public_key_path)).returns true + Puppet::FileSystem.expects(:unlink).with(Puppet::FileSystem.pathname(@public_key_path)) @searcher.destroy(@request) end it "should not fail if the public key does not exist when deleting the private key" do - Puppet::FileSystem.stubs(:unlink).with(@private_key_path) + Puppet::FileSystem.stubs(:unlink).with(Puppet::FileSystem.pathname(@private_key_path)) - Puppet::FileSystem.stubs(:exist?).with(@private_key_path).returns true - Puppet::FileSystem.expects(:exist?).with(@public_key_path).returns false - Puppet::FileSystem.expects(:unlink).with(@public_key_path).never + Puppet::FileSystem.stubs(:exist?).with(Puppet::FileSystem.pathname(@private_key_path)).returns true + Puppet::FileSystem.expects(:exist?).with(Puppet::FileSystem.pathname(@public_key_path)).returns false + Puppet::FileSystem.expects(:unlink).with(Puppet::FileSystem.pathname(@public_key_path)).never @searcher.destroy(@request) end From e15cae88317b770a95afd626e01e666106ff35ee Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 5 Jan 2014 22:41:14 +0100 Subject: [PATCH 378/800] (PUP-761) Make most FileSystem API change related test pass --- spec/unit/configurer_spec.rb | 4 +- spec/unit/hiera_puppet_spec.rb | 4 +- spec/unit/module_spec.rb | 38 +++++++-------- spec/unit/provider_spec.rb | 34 +++++++------- spec/unit/reports/store_spec.rb | 4 +- spec/unit/settings/autosign_setting_spec.rb | 2 +- spec/unit/settings/file_setting_spec.rb | 4 +- spec/unit/settings_spec.rb | 52 ++++++++++----------- spec/unit/util/rdoc/parser_spec.rb | 2 +- spec/unit/util_spec.rb | 14 +++--- 10 files changed, 79 insertions(+), 79 deletions(-) diff --git a/spec/unit/configurer_spec.rb b/spec/unit/configurer_spec.rb index 7732bea08..4157ba8e3 100755 --- a/spec/unit/configurer_spec.rb +++ b/spec/unit/configurer_spec.rb @@ -464,7 +464,7 @@ describe Puppet::Configurer do it "should write the last run file" do @configurer.save_last_run_summary(@report) - Puppet::FileSystem::File.exist?(Puppet[:lastrunfile]).should be_true + Puppet::FileSystem.exist?(Puppet[:lastrunfile]).should be_true end it "should write the raw summary as yaml" do @@ -496,7 +496,7 @@ describe Puppet::Configurer do require 'puppet/util/windows/security' mode = Puppet::Util::Windows::Security.get_mode(Puppet[:lastrunfile]) else - mode = Puppet::FileSystem::File.new(Puppet[:lastrunfile]).stat.mode + mode = Puppet::FileSystem.stat(Puppet[:lastrunfile]).mode end (mode & 0777).should == 0664 end diff --git a/spec/unit/hiera_puppet_spec.rb b/spec/unit/hiera_puppet_spec.rb index 545e8ace1..b35ed68b2 100644 --- a/spec/unit/hiera_puppet_spec.rb +++ b/spec/unit/hiera_puppet_spec.rb @@ -55,7 +55,7 @@ describe 'HieraPuppet' do pending("This example does not apply to Puppet #{Puppet.version} because it does not have this setting") end - Puppet::FileSystem::File.stubs(:exist?).with(Puppet[:hiera_config]).returns(true) + Puppet::FileSystem.stubs(:exist?).with(Puppet[:hiera_config]).returns(true) HieraPuppet.send(:hiera_config_file).should == Puppet[:hiera_config] end @@ -67,7 +67,7 @@ describe 'HieraPuppet' do end Puppet.settings[:confdir] = "/dev/null/puppet" hiera_config = File.join(Puppet[:confdir], 'hiera.yaml') - Puppet::FileSystem::File.stubs(:exist?).with(hiera_config).returns(true) + Puppet::FileSystem.stubs(:exist?).with(hiera_config).returns(true) HieraPuppet.send(:hiera_config_file).should == hiera_config end diff --git a/spec/unit/module_spec.rb b/spec/unit/module_spec.rb index 5c4065e14..2a0945468 100755 --- a/spec/unit/module_spec.rb +++ b/spec/unit/module_spec.rb @@ -15,7 +15,7 @@ describe Puppet::Module do before do # This is necessary because of the extra checks we have for the deprecated # 'plugins' directory - Puppet::FileSystem::File.stubs(:exist?).returns false + Puppet::FileSystem.stubs(:exist?).returns false end it "should have a class method that returns a named module from a given environment" do @@ -90,14 +90,14 @@ describe Puppet::Module do describe "when finding unmet dependencies" do before do - Puppet::FileSystem::File.unstub(:exist?) + Puppet::FileSystem.unstub(:exist?) @modpath = tmpdir('modpath') Puppet.settings[:modulepath] = @modpath end it "should list modules that are missing" do metadata_file = "#{@modpath}/needy/metadata.json" - Puppet::FileSystem::File.expects(:exist?).twice.with(metadata_file).returns true + Puppet::FileSystem.expects(:exist?).twice.with(metadata_file).returns true mod = PuppetSpec::Modules.create( 'needy', @modpath, @@ -119,7 +119,7 @@ describe Puppet::Module do it "should list modules that are missing and have invalid names" do metadata_file = "#{@modpath}/needy/metadata.json" - Puppet::FileSystem::File.expects(:exist?).with(metadata_file).twice.returns true + Puppet::FileSystem.expects(:exist?).with(metadata_file).twice.returns true mod = PuppetSpec::Modules.create( 'needy', @modpath, @@ -142,7 +142,7 @@ describe Puppet::Module do it "should list modules with unmet version requirement" do ['foobar', 'foobaz'].each do |mod_name| metadata_file = "#{@modpath}/#{mod_name}/metadata.json" - Puppet::FileSystem::File.stubs(:exist?).with(metadata_file).returns true + Puppet::FileSystem.stubs(:exist?).with(metadata_file).returns true end mod = PuppetSpec::Modules.create( 'foobar', @@ -213,7 +213,7 @@ describe Puppet::Module do it "should consider a dependency without a semantic version to be unmet" do metadata_file = "#{@modpath}/foobar/metadata.json" - Puppet::FileSystem::File.expects(:exist?).with(metadata_file).times(3).returns true + Puppet::FileSystem.expects(:exist?).with(metadata_file).times(3).returns true mod = PuppetSpec::Modules.create( 'foobar', @modpath, @@ -256,7 +256,7 @@ describe Puppet::Module do it "should only list unmet dependencies" do [name, 'satisfied'].each do |mod_name| metadata_file = "#{@modpath}/#{mod_name}/metadata.json" - Puppet::FileSystem::File.expects(:exist?).with(metadata_file).twice.returns true + Puppet::FileSystem.expects(:exist?).with(metadata_file).twice.returns true end mod = PuppetSpec::Modules.create( name, @@ -382,31 +382,31 @@ describe Puppet::Module do end it "should be able to return individual #{filetype}" do module_file = File.join(path, dirname, "my/file") - Puppet::FileSystem::File.expects(:exist?).with(module_file).returns true + Puppet::FileSystem.expects(:exist?).with(module_file).returns true mod.send(filetype.to_s.sub(/s$/, ''), "my/file").should == module_file end it "should consider #{filetype} to be present if their base directory exists" do module_file = File.join(path, dirname) - Puppet::FileSystem::File.expects(:exist?).with(module_file).returns true + Puppet::FileSystem.expects(:exist?).with(module_file).returns true mod.send(filetype.to_s + "?").should be_true end it "should consider #{filetype} to be absent if their base directory does not exist" do module_file = File.join(path, dirname) - Puppet::FileSystem::File.expects(:exist?).with(module_file).returns false + Puppet::FileSystem.expects(:exist?).with(module_file).returns false mod.send(filetype.to_s + "?").should be_false end it "should return nil if asked to return individual #{filetype} that don't exist" do module_file = File.join(path, dirname, "my/file") - Puppet::FileSystem::File.expects(:exist?).with(module_file).returns false + Puppet::FileSystem.expects(:exist?).with(module_file).returns false mod.send(filetype.to_s.sub(/s$/, ''), "my/file").should be_nil end it "should return the base directory if asked for a nil path" do base = File.join(path, dirname) - Puppet::FileSystem::File.expects(:exist?).with(base).returns true + Puppet::FileSystem.expects(:exist?).with(base).returns true mod.send(filetype.to_s.sub(/s$/, ''), nil).should == base end end @@ -439,8 +439,8 @@ describe Puppet::Module, "when finding matching manifests" do end it "should default to the 'init' file if no glob pattern is specified" do - Puppet::FileSystem::File.expects(:exist?).with("/a/manifests/init.pp").returns(true) - Puppet::FileSystem::File.expects(:exist?).with("/a/manifests/init.rb").returns(false) + Puppet::FileSystem.expects(:exist?).with("/a/manifests/init.pp").returns(true) + Puppet::FileSystem.expects(:exist?).with("/a/manifests/init.rb").returns(false) @mod.match_manifests(nil).should == %w{/a/manifests/init.pp} end @@ -492,21 +492,21 @@ describe Puppet::Module do end it "should have metadata if it has a metadata file and its data is not empty" do - Puppet::FileSystem::File.expects(:exist?).with(@module.metadata_file).returns true + Puppet::FileSystem.expects(:exist?).with(@module.metadata_file).returns true File.stubs(:read).with(@module.metadata_file).returns "{\"foo\" : \"bar\"}" @module.should be_has_metadata end it "should have metadata if it has a metadata file and its data is not empty" do - Puppet::FileSystem::File.expects(:exist?).with(@module.metadata_file).returns true + Puppet::FileSystem.expects(:exist?).with(@module.metadata_file).returns true File.stubs(:read).with(@module.metadata_file).returns "{\"foo\" : \"bar\"}" @module.should be_has_metadata end it "should not have metadata if has a metadata file and its data is empty" do - Puppet::FileSystem::File.expects(:exist?).with(@module.metadata_file).returns true + Puppet::FileSystem.expects(:exist?).with(@module.metadata_file).returns true File.stubs(:read).with(@module.metadata_file).returns "/* +-----------------------------------------------------------------------+ | | @@ -524,7 +524,7 @@ describe Puppet::Module do end it "should know if it is missing a metadata file" do - Puppet::FileSystem::File.expects(:exist?).with(@module.metadata_file).returns false + Puppet::FileSystem.expects(:exist?).with(@module.metadata_file).returns false @module.should_not be_has_metadata end @@ -541,7 +541,7 @@ describe Puppet::Module do end it "should tolerate failure to parse" do - Puppet::FileSystem::File.expects(:exist?).with(@module.metadata_file).returns true + Puppet::FileSystem.expects(:exist?).with(@module.metadata_file).returns true File.stubs(:read).with(@module.metadata_file).returns(my_fixture('trailing-comma.json')) @module.has_metadata?.should be_false diff --git a/spec/unit/provider_spec.rb b/spec/unit/provider_spec.rb index 31e69f799..2435895a9 100755 --- a/spec/unit/provider_spec.rb +++ b/spec/unit/provider_spec.rb @@ -339,7 +339,7 @@ describe Puppet::Provider do context "provider commands" do it "should raise for unknown commands" do - expect { subject.command(:something) }.to raise_error Puppet::DevError + expect { subject.command(:something) }.to raise_error(Puppet::DevError) end it "should handle command inheritance" do @@ -349,10 +349,10 @@ describe Puppet::Provider do command = Puppet::Util.which('sh') || Puppet::Util.which('cmd.exe') parent.commands :sh => command - Puppet::FileSystem::File.exist?(parent.command(:sh)).should be_true + Puppet::FileSystem.exist?(parent.command(:sh)).should be_true parent.command(:sh).should =~ /#{Regexp.escape(command)}$/ - Puppet::FileSystem::File.exist?(child.command(:sh)).should be_true + Puppet::FileSystem.exist?(child.command(:sh)).should be_true child.command(:sh).should =~ /#{Regexp.escape(command)}$/ end @@ -381,7 +381,7 @@ describe Puppet::Provider do end it "should define a wrapper for the command" do - subject.should respond_to :cmd + subject.should respond_to(:cmd) end it "should return nil if the command is requested" do @@ -389,7 +389,7 @@ describe Puppet::Provider do end it "should raise if the command is invoked" do - expect { subject.cmd }.to raise_error Puppet::Error, /Command cmd is missing/ + expect { subject.cmd }.to raise_error(Puppet::Error, /Command cmd is missing/) end end end @@ -545,18 +545,18 @@ describe Puppet::Provider do subject { provider } - it { should respond_to :has_features } - it { should respond_to :has_feature } + it { should respond_to(:has_features) } + it { should respond_to(:has_feature) } context "provider class" do - it { should respond_to :nomethods? } + it { should respond_to(:nomethods?) } it { should_not be_nomethods } - it { should respond_to :numeric? } + it { should respond_to(:numeric?) } it { subject.send(numeric?, be_numeric) } it { subject.send(numeric?, be_satisfies(:numeric)) } - it { should respond_to :alpha? } + it { should respond_to(:alpha?) } it { subject.send(alpha?, be_alpha) } it { subject.send(alpha?, be_satisfies(:alpha)) } end @@ -564,11 +564,11 @@ describe Puppet::Provider do context "provider instance" do subject { provider.new } - it { should respond_to :numeric? } + it { should respond_to(:numeric?) } it { subject.send(numeric?, be_numeric) } it { subject.send(numeric?, be_satisfies(:numeric)) } - it { should respond_to :alpha? } + it { should respond_to(:alpha?) } it { subject.send(alpha?, be_alpha) } it { subject.send(alpha?, be_satisfies(:alpha)) } end @@ -580,11 +580,11 @@ describe Puppet::Provider do type.feature :undemanding, '' end - it { should respond_to :undemanding? } + it { should respond_to(:undemanding?) } context "when the feature is not declared" do it { should_not be_undemanding } - it { should_not be_satisfies :undemanding } + it { should_not be_satisfies(:undemanding) } end context "when the feature is declared" do @@ -593,7 +593,7 @@ describe Puppet::Provider do end it { should be_undemanding } - it { should be_satisfies :undemanding } + it { should be_satisfies(:undemanding) } end end @@ -618,13 +618,13 @@ describe Puppet::Provider do }.each do |name, data| data[:yes].each do |param| it "should support #{param} with provider #{name}" do - providers[name].should be_supports_parameter param + providers[name].should be_supports_parameter(param) end end data[:no].each do |param| it "should not support #{param} with provider #{name}" do - providers[name].should_not be_supports_parameter param + providers[name].should_not be_supports_parameter(param) end end end diff --git a/spec/unit/reports/store_spec.rb b/spec/unit/reports/store_spec.rb index 421f8404d..7866571a1 100755 --- a/spec/unit/reports/store_spec.rb +++ b/spec/unit/reports/store_spec.rb @@ -47,7 +47,7 @@ describe processor do it "rejects invalid hostnames" do @report.host = ".." - Puppet::FileSystem::File.expects(:exist?).never + Puppet::FileSystem.expects(:exist?).never Tempfile.expects(:new).never expect { @report.process }.to raise_error(ArgumentError, /Invalid node/) end @@ -55,7 +55,7 @@ describe processor do describe "::destroy" do it "rejects invalid hostnames" do - Puppet::FileSystem::File.expects(:unlink).never + Puppet::FileSystem.expects(:unlink).never expect { processor.destroy("..") }.to raise_error(ArgumentError, /Invalid node/) end end diff --git a/spec/unit/settings/autosign_setting_spec.rb b/spec/unit/settings/autosign_setting_spec.rb index 5a7105153..0c8184c8a 100644 --- a/spec/unit/settings/autosign_setting_spec.rb +++ b/spec/unit/settings/autosign_setting_spec.rb @@ -74,7 +74,7 @@ describe Puppet::Settings::AutosignSetting do it "converts the file path to a file resource" do path = File.expand_path('/path/to/autosign.conf') settings.stubs(:value).with('autosign').returns(path) - Puppet::FileSystem::File.stubs(:exist?).with(path).returns true + Puppet::FileSystem.stubs(:exist?).with(path).returns true Puppet.stubs(:features).returns(stub(:root? => true, :microsoft_windows? => false)) setting.mode = '0664' diff --git a/spec/unit/settings/file_setting_spec.rb b/spec/unit/settings/file_setting_spec.rb index d9c6f5629..b31d0ccb3 100755 --- a/spec/unit/settings/file_setting_spec.rb +++ b/spec/unit/settings/file_setting_spec.rb @@ -139,14 +139,14 @@ describe Puppet::Settings::FileSetting do it "should skip non-existent files if 'create_files' is not enabled" do @file.expects(:create_files?).returns false @file.expects(:type).returns :file - Puppet::FileSystem::File.expects(:exist?).with(@basepath).returns false + Puppet::FileSystem.expects(:exist?).with(@basepath).returns false @file.to_resource.should be_nil end it "should manage existent files even if 'create_files' is not enabled" do @file.expects(:create_files?).returns false @file.expects(:type).returns :file - Puppet::FileSystem::File.expects(:exist?).with(@basepath).returns true + Puppet::FileSystem.expects(:exist?).with(@basepath).returns true @file.to_resource.should be_instance_of(Puppet::Resource) end diff --git a/spec/unit/settings_spec.rb b/spec/unit/settings_spec.rb index 6223e9ff6..b201b3eb8 100755 --- a/spec/unit/settings_spec.rb +++ b/spec/unit/settings_spec.rb @@ -103,8 +103,8 @@ describe Puppet::Settings do end describe "#call_hooks_deferred_to_application_initialization" do - let (:good_default) { "yay" } - let (:bad_default) { "$doesntexist" } + let(:good_default) { "yay" } + let(:bad_default) { "$doesntexist" } before(:each) do @settings = Puppet::Settings.new end @@ -148,7 +148,7 @@ describe Puppet::Settings do ) expect do @settings.send(:call_hooks_deferred_to_application_initialization, options) - end.to raise_error Puppet::Settings::InterpolationError + end.to raise_error(Puppet::Settings::InterpolationError) end it "should contain the setting name in error message" do hook_values = [] @@ -163,7 +163,7 @@ describe Puppet::Settings do ) expect do @settings.send(:call_hooks_deferred_to_application_initialization, options) - end.to raise_error Puppet::Settings::InterpolationError, /badhook/ + end.to raise_error(Puppet::Settings::InterpolationError, /badhook/) end end describe "if no interpolation error" do @@ -281,7 +281,7 @@ describe Puppet::Settings do @settings.handlearg("--myval", "blah") expect do @settings.setting(:myval).setbycli = nil - end.to raise_error ArgumentError, /unset/ + end.to raise_error(ArgumentError, /unset/) end end @@ -318,12 +318,12 @@ describe Puppet::Settings do it "should raise error if no hook defined" do expect do @settings.define_settings(:section, :hooker => {:default => "yay", :desc => "boo", :call_hook => val}) - end.to raise_error ArgumentError, /no :hook/ + end.to raise_error(ArgumentError, /no :hook/) end it "should include the setting name in the error message" do expect do @settings.define_settings(:section, :hooker => {:default => "yay", :desc => "boo", :call_hook => val}) - end.to raise_error ArgumentError, /for :hooker/ + end.to raise_error(ArgumentError, /for :hooker/) end end describe "and definition valid" do @@ -360,7 +360,7 @@ describe Puppet::Settings do it "should raise an error" do expect do @settings.define_settings(:section, :hooker => {:default => "yay", :desc => "boo", :call_hook => :foo, :hook => lambda { |v| hook_values << v }}) - end.to raise_error ArgumentError, /invalid.*call_hook/i + end.to raise_error(ArgumentError, /invalid.*call_hook/i) end end @@ -485,7 +485,7 @@ describe Puppet::Settings do :three => { :default => "$one $two THREE", :desc => "c"}, :four => { :default => "$two $three FOUR", :desc => "d"}, :five => { :default => nil, :desc => "e" } - Puppet::FileSystem::File.stubs(:exist?).returns true + Puppet::FileSystem.stubs(:exist?).returns true end describe "call_on_define" do @@ -589,7 +589,7 @@ describe Puppet::Settings do :config => { :type => :file, :default => "/my/file", :desc => "a" }, :one => { :default => "ONE", :desc => "a" }, :two => { :default => "TWO", :desc => "b" } - Puppet::FileSystem::File.stubs(:exist?).returns true + Puppet::FileSystem.stubs(:exist?).returns true @settings.preferred_run_mode = :agent end @@ -666,8 +666,8 @@ describe Puppet::Settings do describe "when root" do it "should look for the main config file default location config settings haven't been overridden'" do Puppet.features.stubs(:root?).returns(true) - Puppet::FileSystem::File.expects(:exist?).with(main_config_file_default_location).returns(false) - Puppet::FileSystem::File.expects(:exist?).with(user_config_file_default_location).never + Puppet::FileSystem.expects(:exist?).with(main_config_file_default_location).returns(false) + Puppet::FileSystem.expects(:exist?).with(user_config_file_default_location).never @settings.send(:parse_config_files) end @@ -678,7 +678,7 @@ describe Puppet::Settings do Puppet.features.stubs(:root?).returns(false) seq = sequence "load config files" - Puppet::FileSystem::File.expects(:exist?).with(user_config_file_default_location).returns(false).in_sequence(seq) + Puppet::FileSystem.expects(:exist?).with(user_config_file_default_location).returns(false).in_sequence(seq) @settings.send(:parse_config_files) end @@ -699,8 +699,8 @@ describe Puppet::Settings do :two => { :default => "$one TWO", :desc => "b" }, :three => { :default => "$one $two THREE", :desc => "c" } @settings.stubs(:user_config_file).returns(@userconfig) - Puppet::FileSystem::File.stubs(:exist?).with(@file).returns true - Puppet::FileSystem::File.stubs(:exist?).with(@userconfig).returns false + Puppet::FileSystem.stubs(:exist?).with(@file).returns true + Puppet::FileSystem.stubs(:exist?).with(@userconfig).returns false end it "should not ignore the report setting" do @@ -712,7 +712,7 @@ describe Puppet::Settings do [puppetd] report=true CONF - Puppet::FileSystem::File.expects(:exist?).with(myfile).returns(true) + Puppet::FileSystem.expects(:exist?).with(myfile).returns(true) @settings.expects(:read_file).returns(text) @settings.send(:parse_config_files) @settings[:report].should be_true @@ -722,7 +722,7 @@ describe Puppet::Settings do myfile = make_absolute("/my/file") # do not stub expand_path here, as this leads to a stack overflow, when mocha tries to use it @settings[:config] = myfile - Puppet::FileSystem::File.expects(:exist?).with(myfile).returns(true) + Puppet::FileSystem.expects(:exist?).with(myfile).returns(true) File.expects(:read).with(myfile).returns "[main]" @@ -730,7 +730,7 @@ describe Puppet::Settings do end it "should not try to parse non-existent files" do - Puppet::FileSystem::File.expects(:exist?).with(@file).returns false + Puppet::FileSystem.expects(:exist?).with(@file).returns false File.expects(:read).with(@file).never @@ -936,7 +936,7 @@ describe Puppet::Settings do @settings.initialize_app_defaults(:logdir => '/path/to/logdir', :confdir => '/path/to/confdir', :vardir => '/path/to/vardir') hook_invoked.should be_true - @settings[:deferred].should eq File.expand_path('/path/to/confdir/goose') + @settings[:deferred].should eq(File.expand_path('/path/to/confdir/goose')) end it "does not require the value for a setting without a hook to resolve during global setup" do @@ -957,7 +957,7 @@ describe Puppet::Settings do @settings.initialize_global_settings @settings.initialize_app_defaults(:logdir => '/path/to/logdir', :confdir => '/path/to/confdir', :vardir => '/path/to/vardir') - @settings[:can_cause_problems].should eq File.expand_path('/path/to/confdir/goose') + @settings[:can_cause_problems].should eq(File.expand_path('/path/to/confdir/goose')) end it "should allow empty values" do @@ -1004,7 +1004,7 @@ describe Puppet::Settings do context "running non-root without explicit config file" do before :each do Puppet.features.stubs(:root?).returns(false) - Puppet::FileSystem::File.expects(:exist?). + Puppet::FileSystem.expects(:exist?). with(user_config_file_default_location). returns(true).in_sequence(seq) @settings.expects(:read_file). @@ -1026,7 +1026,7 @@ describe Puppet::Settings do context "running as root without explicit config file" do before :each do Puppet.features.stubs(:root?).returns(true) - Puppet::FileSystem::File.expects(:exist?). + Puppet::FileSystem.expects(:exist?). with(main_config_file_default_location). returns(true).in_sequence(seq) @settings.expects(:read_file). @@ -1049,7 +1049,7 @@ describe Puppet::Settings do before :each do Puppet.features.stubs(:root?).returns(false) @settings[:confdir] = File.dirname(main_config_file_default_location) - Puppet::FileSystem::File.expects(:exist?). + Puppet::FileSystem.expects(:exist?). with(main_config_file_default_location). returns(true).in_sequence(seq) @settings.expects(:read_file). @@ -1079,13 +1079,13 @@ describe Puppet::Settings do :one => { :default => "ONE", :desc => "a" }, :two => { :default => "$one TWO", :desc => "b" }, :three => { :default => "$one $two THREE", :desc => "c" } - Puppet::FileSystem::File.stubs(:exist?).with(@file).returns true - Puppet::FileSystem::File.stubs(:exist?).with(@userconfig).returns false + Puppet::FileSystem.stubs(:exist?).with(@file).returns true + Puppet::FileSystem.stubs(:exist?).with(@userconfig).returns false @settings.stubs(:user_config_file).returns(@userconfig) end it "does not create the WatchedFile instance and should not parse if the file does not exist" do - Puppet::FileSystem::File.expects(:exist?).with(@file).returns false + Puppet::FileSystem.expects(:exist?).with(@file).returns false Puppet::Util::WatchedFile.expects(:new).never @settings.expects(:parse_config_files).never diff --git a/spec/unit/util/rdoc/parser_spec.rb b/spec/unit/util/rdoc/parser_spec.rb index eb2ecd248..72f7ba926 100755 --- a/spec/unit/util/rdoc/parser_spec.rb +++ b/spec/unit/util/rdoc/parser_spec.rb @@ -15,7 +15,7 @@ describe "RDoc::Parser", :if => Puppet.features.rdoc1? do before :each do stub_file = stub('init.pp', :stat => stub()) # Ruby 1.8.7 needs the following call to be stubs and not expects - Puppet::FileSystem::File.stubs(:new).with('init.pp').returns stub_file + Puppet::FileSystem.stubs(:stat).with('init.pp').returns stub() # stub_file @top_level = stub_everything 'toplevel', :file_relative_name => "init.pp" @parser = RDoc::Parser.new(@top_level, "module/manifests/init.pp", nil, Options.instance, RDoc::Stats.new) end diff --git a/spec/unit/util_spec.rb b/spec/unit/util_spec.rb index 58cc4bbd8..1c6f30367 100755 --- a/spec/unit/util_spec.rb +++ b/spec/unit/util_spec.rb @@ -19,7 +19,7 @@ describe Puppet::Util do end def get_mode(file) - Puppet::FileSystem::File.new(file).lstat.mode & 07777 + Puppet::FileSystem.lstat(file).mode & 07777 end end @@ -449,7 +449,7 @@ describe Puppet::Util do it "should copy the permissions of the source file before yielding on Unix", :if => !Puppet.features.microsoft_windows? do set_mode(0555, target.path) - inode = Puppet::FileSystem::File.new(target.path).stat.ino + inode = Puppet::FileSystem.stat(target.path).ino yielded = false subject.replace_file(target.path, 0600) do |fh| @@ -458,19 +458,19 @@ describe Puppet::Util do end yielded.should be_true - Puppet::FileSystem::File.new(target.path).stat.ino.should_not == inode + Puppet::FileSystem.stat(target.path).ino.should_not == inode get_mode(target.path).should == 0555 end it "should use the default permissions if the source file doesn't exist" do new_target = target.path + '.foo' - Puppet::FileSystem::File.exist?(new_target).should be_false + Puppet::FileSystem.exist?(new_target).should be_false begin subject.replace_file(new_target, 0555) {|fh| fh.puts "foo" } get_mode(new_target).should == 0555 ensure - Puppet::FileSystem::File.unlink(new_target) if Puppet::FileSystem::File.exist?(new_target) + Puppet::FileSystem.unlink(new_target) if Puppet::FileSystem.exist?(new_target) end end @@ -502,14 +502,14 @@ describe Puppet::Util do {:string => '664', :number => 0664, :symbolic => "ug=rw-,o=r--" }.each do |label,mode| it "should support #{label} format permissions" do new_target = target.path + "#{mode}.foo" - Puppet::FileSystem::File.exist?(new_target).should be_false + Puppet::FileSystem.exist?(new_target).should be_false begin subject.replace_file(new_target, mode) {|fh| fh.puts "this is an interesting content" } get_mode(new_target).should == 0664 ensure - Puppet::FileSystem::File.unlink(new_target) if Puppet::FileSystem::File.exist?(new_target) + Puppet::FileSystem.unlink(new_target) if Puppet::FileSystem.exist?(new_target) end end end From 1959c1ab60e7b062bb53e631119f7d02deb98429 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 5 Jan 2014 22:48:57 +0100 Subject: [PATCH 379/800] (maint) Remove unused file_system/file.rb The functionality was moved to FileSystem, and to file_system/file_impl.rb. The now removed file was kept for reference. Now no longer needed. --- lib/puppet/file_system/file.rb | 271 --------------------------------- 1 file changed, 271 deletions(-) delete mode 100644 lib/puppet/file_system/file.rb diff --git a/lib/puppet/file_system/file.rb b/lib/puppet/file_system/file.rb deleted file mode 100644 index f9a3ad766..000000000 --- a/lib/puppet/file_system/file.rb +++ /dev/null @@ -1,271 +0,0 @@ -# An abstraction over the ruby file system operations for a single file. -# -# For the time being this is being kept private so that we can evolve it for a -# while. -# -# @api private -class Puppet::FileSystem::FileXXX - attr_reader :path - - IMPL = if RUBY_VERSION =~ /^1\.8/ - require 'puppet/file_system/file18' - Puppet::FileSystem::File18 - elsif Puppet::Util::Platform.windows? - require 'puppet/file_system/file19windows' - Puppet::FileSystem::File19Windows - else - require 'puppet/file_system/file19' - Puppet::FileSystem::File19 - end - - @remembered = {} - - def self.new(path) - if @remembered.include?(path.to_s) - @remembered[path.to_s] - else - file = IMPL.allocate - file.send(:initialize, path) - file - end - end - - # Run a block of code with a file accessible in the filesystem. - # @note This API should only be used for testing - # - # @param file [Object] an object that conforms to the Puppet::FileSystem::File interface - # @api private - def self.overlay(file, &block) - remember(file) - yield - ensure - forget(file) - end - - # Create a binding between a filename and a particular instance of a file object. - # @note This API should only be used for testing - # - # @param file [Object] an object that conforms to the Puppet::FileSystem::File interface - # @api private - def self.remember(file) - @remembered[file.path.to_s] = file - end - - # Forget a remembered file - # @note This API should only be used for testing - # - # @param file [Object] an object that conforms to the Puppet::FileSystem::File interface - # @api private - def self.forget(file) - @remembered.delete(file.path.to_s) - end - - def initialize(path) - if path.is_a?(Pathname) - @path = path - else - @path = Pathname.new(path) - end - end - - def open(mode, options, &block) - ::File.open(@path, options, mode, &block) - end - - # @return [Puppet::FileSystem::File] The directory of this file - # @api public - def dir - Puppet::FileSystem::File.new(@path.dirname) - end - - # @return [String] the name of the file - # @api public - def basename - @path.basename.to_s - end - - # @return [Num] The size of this file - # @api public - def size - @path.size - end - - # Allows exclusive updates to a file to be made by excluding concurrent - # access using flock. This means that if the file is on a filesystem that - # does not support flock, this method will provide no protection. - # - # While polling to aquire the lock the process will wait ever increasing - # amounts of time in order to prevent multiple processes from wasting - # resources. - # - # @param mode [Integer] The mode to apply to the file if it is created - # @param options [Integer] Extra file operation mode information to use - # (defaults to read-only mode) - # @param timeout [Integer] Number of seconds to wait for the lock (defaults to 300) - # @yield The file handle, in read-write mode - # @return [Void] - # @raise [Timeout::Error] If the timeout is exceeded while waiting to aquire the lock - # @api public - def exclusive_open(mode, options = 'r', timeout = 300, &block) - wait = 0.001 + (Kernel.rand / 1000) - written = false - while !written - ::File.open(@path, options, mode) do |rf| - if rf.flock(::File::LOCK_EX|::File::LOCK_NB) - yield rf - written = true - else - sleep wait - timeout -= wait - wait *= 2 - if timeout < 0 - raise Timeout::Error, "Timeout waiting for exclusive lock on #{@path}" - end - end - end - end - end - - def each_line(&block) - ::File.open(@path) do |f| - f.each_line do |line| - yield line - end - end - end - - # @return [String] The contents of the file - def read - @path.read - end - - # @return [String] The binary contents of the file - def binread - raise NotImplementedError - end - - # Determine if a file exists by verifying that the file can be stat'd. - # Will follow symlinks and verify that the actual target path exists. - # - # @return [Boolean] true if the named file exists. - def self.exist?(path) - return IMPL.exist?(path) if IMPL.method(:exist?) != self.method(:exist?) - File.exist?(path) - end - - # Determine if a file exists by verifying that the file can be stat'd. - # Will follow symlinks and verify that the actual target path exists. - # - # @return [Boolean] true if the path of this file is present - def exist? - self.class.exist?(@path) - end - - # Determine if a file is executable. - # - # @todo Should this take into account extensions on the windows platform? - # - # @return [Boolean] true if this file can be executed - def executable? - ::File.executable?(@path) - end - - # @return [Boolean] Whether the file is writable by the current - # process - def writable? - @path.writable? - end - - # Touches the file. On most systems this updates the mtime of the file. - def touch - ::FileUtils.touch(@path) - end - - # Create the entire path as directories - def mkpath - @path.mkpath - end - - # Creates a symbolic link dest which points to the current file. - # If dest already exists: - # - # * and is a file, will raise Errno::EEXIST - # * and is a directory, will return 0 but perform no action - # * and is a symlink referencing a file, will raise Errno::EEXIST - # * and is a symlink referencing a directory, will return 0 but perform no action - # - # With the :force option set to true, when dest already exists: - # - # * and is a file, will replace the existing file with a symlink (DANGEROUS) - # * and is a directory, will return 0 but perform no action - # * and is a symlink referencing a file, will modify the existing symlink - # * and is a symlink referencing a directory, will return 0 but perform no action - # - # @param dest [String] The path to create the new symlink at - # @param [Hash] options the options to create the symlink with - # @option options [Boolean] :force overwrite dest - # @option options [Boolean] :noop do not perform the operation - # @option options [Boolean] :verbose verbose output - # - # @raise [Errno::EEXIST] dest already exists as a file and, :force is not set - # - # @return [Integer] 0 - def symlink(dest, options = {}) - FileUtils.symlink(@path, dest, options) - end - - # @return [Boolean] true if the file is a symbolic link. - def symlink? - File.symlink?(@path) - end - - # @return [String] the name of the file referenced by the given link. - def readlink - File.readlink(@path) - end - - # Deletes the named files, returning the number of names passed as arguments. - # See also Dir::rmdir. - # - # @raise an exception on any error. - # - # @return [Integer] the number of names passed as arguments - def self.unlink(*file_names) - return IMPL.unlink(*file_names) if IMPL.method(:unlink) != self.method(:unlink) - File.unlink(*file_names) - end - - # Deletes the file. - # See also Dir::rmdir. - # - # @raise an exception on any error. - # - # @return [Integer] the number of names passed as arguments, in this case 1 - def unlink - self.class.unlink(@path) - end - - # @return [File::Stat] object for the named file. - def stat - File.stat(@path) - end - - # @return [File::Stat] Same as stat, but does not follow the last symbolic - # link. Instead, reports on the link itself. - def lstat - File.lstat(@path) - end - - # Compare the contents of this file against the contents of a stream. - # @param stream [IO] The stream to compare the contents against - # @return [Boolean] Whether the contents were the same - def compare_stream(stream) - open(0, 'rb') do |this| - FileUtils.compare_stream(this, stream) - end - end - - def to_s - @path.to_s - end -end From 36499863dade72749dfea6d16c7526d40e1769b0 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 5 Jan 2014 22:56:04 +0100 Subject: [PATCH 380/800] (PUP-761) Fix integration tests after FileSystem API change --- spec/integration/configurer_spec.rb | 2 +- spec/integration/util/settings_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/integration/configurer_spec.rb b/spec/integration/configurer_spec.rb index 32927a8de..5e65355c5 100755 --- a/spec/integration/configurer_spec.rb +++ b/spec/integration/configurer_spec.rb @@ -62,7 +62,7 @@ describe Puppet::Configurer do file_mode = Puppet.features.microsoft_windows? ? '100644' : '100666' - Puppet::FileSystem.new(Puppet[:lastrunfile]).stat.mode.to_s(8).should == file_mode + Puppet::FileSystem.stat(Puppet[:lastrunfile]).mode.to_s(8).should == file_mode summary = nil File.open(Puppet[:lastrunfile], "r") do |fd| diff --git a/spec/integration/util/settings_spec.rb b/spec/integration/util/settings_spec.rb index 240faf078..783f56032 100755 --- a/spec/integration/util/settings_spec.rb +++ b/spec/integration/util/settings_spec.rb @@ -41,7 +41,7 @@ describe Puppet::Settings do settings.use(:main) - expect(Puppet::FileSystem.new(settings[:maindir]).stat.mode & 007777).to eq(Puppet.features.microsoft_windows? ? 0755 : 0750) + expect(Puppet::FileSystem.stat(settings[:maindir]).mode & 007777).to eq(Puppet.features.microsoft_windows? ? 0755 : 0750) end it "reparses configuration if configuration file is touched", :if => !Puppet.features.microsoft_windows? do From 47238bd8d735b67cf727e33d8234c63dd55bd85e Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Sun, 5 Jan 2014 14:41:14 -0800 Subject: [PATCH 381/800] (PUP-716) Add Memory based file impl --- lib/puppet/file_system/memory_impl.rb | 31 +++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 lib/puppet/file_system/memory_impl.rb diff --git a/lib/puppet/file_system/memory_impl.rb b/lib/puppet/file_system/memory_impl.rb new file mode 100644 index 000000000..6114483e5 --- /dev/null +++ b/lib/puppet/file_system/memory_impl.rb @@ -0,0 +1,31 @@ +class Puppet::FileSystem::MemoryImpl + def initialize(*files) + @files = files + end + + def exist?(path) + find(path).exist? + end + + def executable?(path) + find(path).executable? + end + + def each_line(path, &block) + find(path).each_line(&block) + end + + def pathname(path) + path.to_s + end + + def assert_path(path) + path + end + + private + + def find(path) + @files.find { |file| file.path == path } + end +end From 56fb51bcf60c0d52d8d237c92831d4268f57f164 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Sun, 5 Jan 2014 15:02:19 -0800 Subject: [PATCH 382/800] (PUP-716) Re-add memory impl The memory impl require had been removed because I had forgotten to add the memory_impl.rb file. --- lib/puppet/file_system.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/file_system.rb b/lib/puppet/file_system.rb index 52ff3fd94..063c5d3bb 100644 --- a/lib/puppet/file_system.rb +++ b/lib/puppet/file_system.rb @@ -2,7 +2,7 @@ module Puppet::FileSystem require 'puppet/file_system/path_pattern' require 'puppet/file_system/file_impl' require 'puppet/file_system/memory_file' - #require 'puppet/file_system/memory_impl' + require 'puppet/file_system/memory_impl' require 'puppet/file_system/tempfile' # create instance of the file system implementation to use for the current platform From c342ede755448404195842f514519cc3583db56f Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Sun, 5 Jan 2014 15:04:12 -0800 Subject: [PATCH 383/800] (PUP-716) Change back to stubs from mocks During changes a few calls had been inadvertently changed from stubs (any number of calls allowed, including 0) to mocks (at least one call required). In the case of the file_set this caused a problem for when there was no expectaiton of the methods being called. In the daemontools case, this exposed a mistake that was originally in the tests where the stub was not even needed. --- lib/puppet/provider/service/daemontools.rb | 22 +++++++++---------- spec/unit/file_serving/fileset_spec.rb | 2 +- .../unit/provider/service/daemontools_spec.rb | 9 +++----- 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/lib/puppet/provider/service/daemontools.rb b/lib/puppet/provider/service/daemontools.rb index 7f7f40632..033b1ea46 100644 --- a/lib/puppet/provider/service/daemontools.rb +++ b/lib/puppet/provider/service/daemontools.rb @@ -147,18 +147,18 @@ Puppet::Type.type(:service).provide :daemontools, :parent => :base do end def enable - if ! FileTest.directory?(self.daemon) - Puppet.notice "No daemon dir, calling setupservice for #{resource[:name]}" - self.setupservice + if ! FileTest.directory?(self.daemon) + Puppet.notice "No daemon dir, calling setupservice for #{resource[:name]}" + self.setupservice + end + if self.daemon + if ! Puppet::FileSystem.symlink?(self.service) + Puppet.notice "Enabling #{self.service}: linking #{self.daemon} -> #{self.service}" + Puppet::FileSystem.symlink(self.daemon, self.service) end - if self.daemon - if ! Puppet::FileSystem.symlink?(self.service) - Puppet.notice "Enabling #{self.service}: linking #{self.daemon} -> #{self.service}" - Puppet::FileSystem.symlink(self.daemon, self.service) - end - end - rescue Puppet::ExecutionFailure - raise Puppet::Error.new( "No daemon directory found for #{self.service}") + end +rescue Puppet::ExecutionFailure + raise Puppet::Error.new( "No daemon directory found for #{self.service}") end def disable diff --git a/spec/unit/file_serving/fileset_spec.rb b/spec/unit/file_serving/fileset_spec.rb index 5f7e76db1..463d2a2b6 100755 --- a/spec/unit/file_serving/fileset_spec.rb +++ b/spec/unit/file_serving/fileset_spec.rb @@ -140,7 +140,7 @@ describe Puppet::FileServing::Fileset do end def mock_dir_structure(path, stat_method = :lstat) - Puppet::FileSystem.expects(stat_method).with(@path).returns @dirstat + Puppet::FileSystem.stubs(stat_method).with(@path).returns @dirstat Dir.stubs(:entries).with(path).returns(%w{one two .svn CVS}) # Keep track of the files we're stubbing. diff --git a/spec/unit/provider/service/daemontools_spec.rb b/spec/unit/provider/service/daemontools_spec.rb index a1041f9e5..57bf632c2 100755 --- a/spec/unit/provider/service/daemontools_spec.rb +++ b/spec/unit/provider/service/daemontools_spec.rb @@ -101,13 +101,10 @@ describe provider_class do describe "when enabling" do it "should create a symlink between daemon dir and service dir", :if => Puppet.features.manages_symlinks? do daemon_path = File.join(@daemondir, "myservice") -# stub_daemon = stub(daemon_path, :symlink? => false) - Puppet::FileSystem.expects(:symlink?).with(daemon_path).returns(false) # stub_daemon) service_path = File.join(@servicedir, "myservice") -# mock_service = mock(service_path, :symlink? => false) - Puppet::FileSystem.expects(:symlink?).with(service_path).returns(false) #mock_service) - Puppet::FileSystem.expects(:symlink).returns(0) #mock_service) -# stub_daemon.expects(:symlink).returns(0) + Puppet::FileSystem.expects(:symlink?).with(service_path).returns(false) + Puppet::FileSystem.expects(:symlink).with(daemon_path, service_path).returns(0) + @provider.enable end end From c6846c2b92c035020920d392964fb9690846c537 Mon Sep 17 00:00:00 2001 From: "Jeremy T. Bouse" Date: Wed, 18 Dec 2013 07:50:20 -0500 Subject: [PATCH 384/800] (PUP-1120) Fix private key permissions for group read Have the hostprivkey saved with 0640 permissions to allow the service group to be able to read by other puppet services like PuppetDB and Foreman. --- lib/puppet/defaults.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index 80c13c824..948796e9e 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -560,6 +560,7 @@ EOT :type => :directory, :mode => 0750, :owner => "service", + :group => "service", :desc => "The private key directory." }, :privatedir => { @@ -594,8 +595,9 @@ EOT :hostprivkey => { :default => "$privatekeydir/$certname.pem", :type => :file, - :mode => 0600, + :mode => 0640, :owner => "service", + :group => "service", :desc => "Where individual hosts store and look for their private key." }, :hostpubkey => { From d56347ad5d9d452295fc0ce804756fc3d90786ed Mon Sep 17 00:00:00 2001 From: fhrbek Date: Mon, 30 Dec 2013 16:41:44 +0100 Subject: [PATCH 385/800] PUP-1322 Do not fail hard on unparseable files --- lib/puppet/node/environment.rb | 21 +++++++++++++++++---- lib/puppet/parser/type_loader.rb | 11 ++++++++++- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb index b3ea64ab7..8c03952f0 100644 --- a/lib/puppet/node/environment.rb +++ b/lib/puppet/node/environment.rb @@ -208,12 +208,25 @@ class Puppet::Node::Environment # per environment semantics with an efficient most common cases; we almost # always just return our thread's known-resource types. Only at the start # of a compilation (after our thread var has been set to nil) or when the - # environment has changed do we delve deeper. - $known_resource_types = nil if $known_resource_types && $known_resource_types.environment != self + # environment has changed or when the known resource types have become stale + # do we delve deeper. + $known_resource_types = nil if $known_resource_types && + ($known_resource_types.environment != self || !@known_resource_types_being_imported && $known_resource_types.stale?) $known_resource_types ||= if @known_resource_types.nil? or @known_resource_types.require_reparse? - @known_resource_types = Puppet::Resource::TypeCollection.new(self) - @known_resource_types.import_ast(perform_initial_import, '') + #set the global variable $known_resource_types immediately as it will be queried + #resursively from the parser which would set it anyway, just executing more code in vain + @known_resource_types = $known_resource_types = Puppet::Resource::TypeCollection.new(self) + + #avoid an infinite recursion (called from the parser) if Puppet[:filetimeout] is set to -1 and + #$known_resource_types.stale? returns always true; let's set a flag that we're importing + #so if this method is called recursively we'll skip testing the stale status + begin + @known_resource_types_being_imported = true + @known_resource_types.import_ast(perform_initial_import, '') + ensure + @known_resource_types_being_imported = false + end @known_resource_types else @known_resource_types diff --git a/lib/puppet/parser/type_loader.rb b/lib/puppet/parser/type_loader.rb index 4ace9f90a..9e03fd8af 100644 --- a/lib/puppet/parser/type_loader.rb +++ b/lib/puppet/parser/type_loader.rb @@ -95,7 +95,16 @@ class Puppet::Parser::TypeLoader @loaded ||= {} loaded_asts = [] files.reject { |file| @loaded[file] }.each do |file| - loaded_asts << parse_file(file) + begin + loaded_asts << parse_file(file) + rescue => e + # Resume from errors so that all parseable files would + # still be parsed. Mark this file as loaded so that + # it would not be parsed next time (handle it as if + # it was successfully parsed). + Puppet.debug("Unable to parse '#{file}': #{e.message}") + end + @loaded[file] = true end From f880bb7e71ce1471e96e69a0d4545f6c1ebed359 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 7 Jan 2014 22:47:28 +0100 Subject: [PATCH 386/800] (PUP-800) Clean up and improve handling of location / position There were several issues with the implementation of location handling - primarily missing the ability to be able to serialize/de-serialize the model with retained positioning information. This changes the design to bring the two values offset and length into each model object and that location information is recored in the top level Program construct. With this change a Program is always returned as the result of a parse. The change touches many files, due to the change to always return a Model::Program. Most of the change is removal of now no longer needed constructs. --- lib/puppet/pops/adapters.rb | 65 +++++----- lib/puppet/pops/binder/bindings_factory.rb | 3 + lib/puppet/pops/binder/bindings_model.rb | 15 ++- lib/puppet/pops/evaluator/runtime3_support.rb | 9 +- lib/puppet/pops/model/ast_transformer.rb | 2 +- lib/puppet/pops/model/factory.rb | 67 +++------- lib/puppet/pops/model/model.rb | 44 +++++-- lib/puppet/pops/parser/epp_parser.rb | 1 - lib/puppet/pops/parser/evaluating_parser.rb | 5 +- lib/puppet/pops/parser/locatable.rb | 118 +----------------- lib/puppet/pops/parser/locator.rb | 24 ++-- lib/puppet/pops/parser/parser_support.rb | 4 +- lib/puppet/pops/types/type_parser.rb | 6 +- lib/puppet/pops/utils.rb | 17 ++- lib/puppet/pops/validation.rb | 6 +- spec/unit/parser/functions/lookup_spec.rb | 2 +- spec/unit/pops/binder/injector_spec.rb | 2 +- spec/unit/pops/model/ast_transformer_spec.rb | 8 +- spec/unit/pops/parser/parser_spec.rb | 3 +- spec/unit/pops/types/type_parser_spec.rb | 2 +- 20 files changed, 162 insertions(+), 241 deletions(-) diff --git a/lib/puppet/pops/adapters.rb b/lib/puppet/pops/adapters.rb index ac84e4e92..54e30f952 100644 --- a/lib/puppet/pops/adapters.rb +++ b/lib/puppet/pops/adapters.rb @@ -10,71 +10,70 @@ module Puppet::Pops::Adapters attr_accessor :documentation end - # An origin adapter adapts an object with where it came from. This origin - # describes the resource (a file, etc.) where source text originates. - # Instances of SourcePosAdapter is then used on other objects in a model to - # describe their relative position versus the origin. - # - # @see Puppet::Pops::Utils#find_adapter - # - class OriginAdapter < Puppet::Pops::Adaptable::Adapter - # @return [String] the origin of the adapted (usually a filename) - attr_accessor :origin - end - - # A SourcePosAdapter holds a reference to something *locateable* (a position in source text). - # This is represented by an instance of Puppet::Pops::Parser::Locateable (it has an offset, a length, and - # a Puppet::Pops::Parser::Locator) that are used together to provide derived information (line, and position - # on line). + # A SourcePosAdapter holds a reference to a *Positioned* object (object that has offset and length). # This somewhat complex structure makes it possible to correctly refer to a source position # in source that is embedded in some resource; a parser only sees the embedded snippet of source text # and does not know where it was embedded. It also enables lazy evaluation of source positions (they are # rarely needed - typically just when there is an error to report. # - # @note It is relatively expensive to compute line and postion on line - it is not something that + # @note It is relatively expensive to compute line and position on line - it is not something that # should be done for every token or model object. # - # @see Puppet::Pops::Utils#find_adapter + # @see Puppet::Pops::Utils#find_adapter, Puppet::Pops::Utils#find_closest_positioned # class SourcePosAdapter < Puppet::Pops::Adaptable::Adapter - attr_accessor :locatable + attr_accessor :locator - def locator - locatable.locator + def self.create_adapter(o) + new(o) end + def initialize(o) + @adapted = o + end + + def locator + @locator ||= find_locator(@adapted.eContainer) + end + + def find_locator(o) + if o.nil? + raise ArgumentError, "InternalError: SourcePosAdapter for something that has no locator among parents" + end + return o.locator if o.is_a?(Puppet::Pops::Model::Program) + if adapter = self.class.get(o) + return adapter.locator + else + find_locator(o.eContainer) + end + end + private :find_locator + def offset - locatable.offset + @adapted.offset end def length - locatable.length + @adapted.length end # Produces the line number for the given offset. # @note This is an expensive operation # def line - locatable.locator.line_for_offset(offset) + locator.line_for_offset(offset) end # Produces the position on the line of the given offset. # @note This is an expensive operation # def pos - locatable.locator.pos_on_line(offset) + locator.pos_on_line(offset) end # Extracts the text represented by this source position (the string is obtained from the locator) def extract_text - locatable.locator.string.slice(offset, length) - end - - # Extracts the text represented by this source position from a given string (which needs to be identical - # to what is held in the locator - why is this needed ? - # TODO: - def extract_text_from_string(string) - string.slice(offset, length) + locator.string.slice(offset, length) end end diff --git a/lib/puppet/pops/binder/bindings_factory.rb b/lib/puppet/pops/binder/bindings_factory.rb index d00442b90..3f85df48a 100644 --- a/lib/puppet/pops/binder/bindings_factory.rb +++ b/lib/puppet/pops/binder/bindings_factory.rb @@ -356,6 +356,9 @@ module Puppet::Pops::Binder::BindingsFactory case producer when Class producer = Puppet::Pops::Binder::BindingsFactory.instance_producer(producer.name, *args) + when Puppet::Pops::Model::Program + # program is not an expression + producer = Puppet::Pops::Binder::BindingsFactory.evaluating_producer(producer.body) when Puppet::Pops::Model::Expression producer = Puppet::Pops::Binder::BindingsFactory.evaluating_producer(producer) when Puppet::Pops::Binder::Bindings::ProducerDescriptor diff --git a/lib/puppet/pops/binder/bindings_model.rb b/lib/puppet/pops/binder/bindings_model.rb index c909c66fe..4d041fca1 100644 --- a/lib/puppet/pops/binder/bindings_model.rb +++ b/lib/puppet/pops/binder/bindings_model.rb @@ -47,11 +47,16 @@ module Puppet::Pops::Binder::Bindings has_attr 'value', Object end - # Produces a value by evaluating a Puppet DSL expression + # Produces a value by evaluating a Puppet DSL expression. + # Note that the expression is not contained as it is part of a Puppet::Pops::Model::Program. + # To include the expression in the serialization, the Program it is contained in must be + # contained in the same serialization. This can be achieved by containing it in the + # ContributedBindings that is the top of a BindingsModel produced and given to the Injector. + # # @api public # class EvaluatingProducerDescriptor < ProducerDescriptor - contains_one_uni 'expression', Puppet::Pops::Model::Expression + has_one 'expression', Puppet::Pops::Model::Expression end # An InstanceProducer creates an instance of the given class @@ -183,8 +188,14 @@ module Puppet::Pops::Binder::Bindings # as opposed to the names of the different set of bindings. The ContributorBindings name is typically # a technical name that indicates their source (a service). # + # When EvaluatingProducerDescriptor is used, it holds a reference to an Expression. That expression + # should be contained in the programs referenced in the ContributedBindings that contains that producer. + # While the bindings model will still work if this is not the case, it will not serialize and deserialize + # correctly. + # # @api public # class ContributedBindings < NamedLayer + contains_many_uni 'programs', Puppet::Pops::Model::Program end end diff --git a/lib/puppet/pops/evaluator/runtime3_support.rb b/lib/puppet/pops/evaluator/runtime3_support.rb index 835c978d9..30fd46973 100644 --- a/lib/puppet/pops/evaluator/runtime3_support.rb +++ b/lib/puppet/pops/evaluator/runtime3_support.rb @@ -152,7 +152,7 @@ module Puppet::Pops::Evaluator::Runtime3Support # until the relationship is evaluated by the compiler (at the end). When evaluation takes place, the (empty reference) Resource instances # are converted to String (!?! WTF) on the simple format "#{type}[#{title}]", and the catalog is told to find a resource, by giving # it this string. If it cannot find the resource it fails, else the before/notify parameter is appended with the target. - # The search for the resource being with (you guessed it) again creating an (empty reference) resource from type and title (WTF?!?!). + # The search for the resource begin with (you guessed it) again creating an (empty reference) resource from type and title (WTF?!?!). # The catalog now uses the reference resource to compute a key [r.type, r.title.to_s] and also gets a uniqueness key from the # resource (This is only a reference type created from title and type). If it cannot find it with the first key, it uses the # uniqueness key to lookup. @@ -449,11 +449,16 @@ module Puppet::Pops::Evaluator::Runtime3Support end def extract_file_line(o) - source_pos = Puppet::Pops::Utils.find_adapter(o, Puppet::Pops::Adapters::SourcePosAdapter) + source_pos = Puppet::Pops::Utils.find_closest_positioned(o) return [nil, -1] unless source_pos [source_pos.locator.file, source_pos.line] end + def find_closest_positioned(o) + return nil if o.nil? || o.is_a?(Puppet::Pops::Model::Program) + o.offset.nil? ? find_closest_positioned(o.eContainer) : Puppet::Pops::Adapters::SourcePosAdapter.adapt(o) + end + # Creates a diagnostic producer def diagnostic_producer Puppet::Pops::Validation::DiagnosticProducer.new( diff --git a/lib/puppet/pops/model/ast_transformer.rb b/lib/puppet/pops/model/ast_transformer.rb index f1d270c04..75f0ef26f 100644 --- a/lib/puppet/pops/model/ast_transformer.rb +++ b/lib/puppet/pops/model/ast_transformer.rb @@ -42,7 +42,7 @@ class Puppet::Pops::Model::AstTransformer def merge_location(hash, o) if o pos = {} - source_pos = Puppet::Pops::Utils.find_adapter(o, Puppet::Pops::Adapters::SourcePosAdapter) + source_pos = Puppet::Pops::Utils.find_closest_positioned(o) if source_pos pos[:line] = source_pos.line pos[:pos] = source_pos.pos diff --git a/lib/puppet/pops/model/factory.rb b/lib/puppet/pops/model/factory.rb index 08ba69991..8c86d80ac 100644 --- a/lib/puppet/pops/model/factory.rb +++ b/lib/puppet/pops/model/factory.rb @@ -16,6 +16,7 @@ class Puppet::Pops::Model::Factory # Shared build_visitor, since there are many instances of Factory being used @@build_visitor = Puppet::Pops::Visitor.new(self, "build") @@interpolation_visitor = Puppet::Pops::Visitor.new(self, "interpolate") + # Initialize a factory with a single object, or a class with arguments applied to build of # created instance # @@ -43,7 +44,7 @@ class Puppet::Pops::Model::Factory # Polymorphic interpolate def interpolate() begin - @@interpolation_visitor.visit_this(self, current) + @@interpolation_visitor.visit_this_0(self, current) rescue =>e # require 'debugger'; debugger # enable this when in trouble... raise e @@ -133,20 +134,6 @@ class Puppet::Pops::Model::Factory o end - # # @param name [String] a valid classname - # # @param parameters [Array] may be empty - # # @param body [Array, Expression, nil] expression that constitute the body - # # @return [Model::HostClassDefinition] configured from the parameters - # # - # def build_ResourceTypeDefinition(o, name, parameters, body) - # build_NamedDefinition(o, name, parameters, body) - # o.name = name - # parameters.each {|p| o.addParameters(build(p)) } - # b = f_build_body(body) - # o.body = b.current if b - # o - # end - def build_ResourceOverrideExpression(o, resources, attribute_operations) o.resources = build(resources) attribute_operations.each {|ao| o.addOperations(build(ao)) } @@ -297,12 +284,14 @@ class Puppet::Pops::Model::Factory o end - def build_Program(o, body, definitions, source_text, line_index) + def build_Program(o, body, definitions, locator) o.body = to_ops(body) # non containment definitions.each { |d| o.addDefinitions(d) } - o.source_text = source_text - o.line_offsets = line_index + o.source_ref = locator.file + o.source_text = locator.string + o.line_offsets = locator.line_index + o.locator = locator o end @@ -446,27 +435,13 @@ class Puppet::Pops::Model::Factory # Records the position (start -> end) and computes the resulting length. # def record_position(start_locatable, end_locatable) - Puppet::Pops::Adapters::SourcePosAdapter.adapt(current) do |a| - if start_locatable && end_locatable - a.locatable = Puppet::Pops::Parser::Locatable::Range.new(start_locatable, end_locatable) - else - a.locatable = Puppet::Pops::Parser::Locatable::Lazy.new(start_locatable) - end - end - self - end - - # Records the origin file of an element - # Does nothing if file is nil. - # - # @param file [String,nil] the file/path to the origin, may contain URI scheme of file: or some other URI scheme - # @return [Factory] returns self - # - def record_origin(file) - return self unless file - Puppet::Pops::Adapters::OriginAdapter.adapt(current) do |a| - a.origin = file - end + from = start_locatable.is_a?(Puppet::Pops::Model::Factory) ? start_locatable.current : start_locatable + to = end_locatable.is_a?(Puppet::Pops::Model::Factory) ? end_locatable.current : end_locatable + to = from if to.nil? + o = current + # record information directly in the Model::Positioned object + o.offset = from.offset + o.length ||= to.offset - from.offset + to.length self end @@ -624,7 +599,6 @@ class Puppet::Pops::Model::Factory def self.COLLECT(type_expr, query_expr, attribute_operations) new(Model::CollectExpression, type_expr, query_expr, attribute_operations) -# new(Model::CollectExpression, Puppet::Pops::Model::Factory.fqr(type_expr), query_expr, attribute_operations) end def self.NAMED_ACCESS(type_name, bodies) @@ -647,8 +621,8 @@ class Puppet::Pops::Model::Factory new(Model::ResourceBody, resource_title, attribute_operations) end - def self.PROGRAM(body, definitions, source_text, line_index) - new(Model::Program, body, definitions, source_text, line_index) + def self.PROGRAM(body, definitions, locator) + new(Model::Program, body, definitions, locator) end # Builds a BlockExpression if args size > 1, else the single expression/value in args @@ -791,15 +765,6 @@ class Puppet::Pops::Model::Factory o end - # # @param rval_required [Boolean] if the call must produce a value - # def build_CallNamedFunctionExpression(o, name, rval_required, *args) - # build_CallExpression(o, name, rval_required, *args) - ## o.functor_expr = build(name) - ## o.rval_required = rval_required - ## args.each {|x| o.addArguments(build(x)) } - # o - # end - def build_CallMethodExpression(o, functor, rval_required, lambda, *args) build_CallExpression(o, functor, rval_required, *args) o.lambda = lambda diff --git a/lib/puppet/pops/model/model.rb b/lib/puppet/pops/model/model.rb index 4cb2888b1..540e251d7 100644 --- a/lib/puppet/pops/model/model.rb +++ b/lib/puppet/pops/model/model.rb @@ -33,18 +33,22 @@ module Puppet::Pops::Model abstract end - # A locateable object has an offset measured in characters from the start of a source text (starting - # from 0), and a length measured in number of characters. + # A Positioned object has an offset measured in an opaque unit (representing characters) from the start + # of a source text (starting + # from 0), and a length measured in the same opaque unit. The resolution of the opaque unit requires the + # aid of a Locator instance that knows about the measure. This information is stored in the model's + # root node - a Program. + # # The offset and length are optional if the source of the model is not from parsed text. # - class Locatable < PopsObject + class Positioned < PopsObject abstract has_attr 'offset', Integer has_attr 'length', Integer end # @abstract base class for expressions - class Expression < Locatable + class Expression < Positioned abstract end @@ -155,7 +159,7 @@ module Puppet::Pops::Model # A Keyed entry has a key and a value expression. It it typically used as an entry in a Hash. # - class KeyedEntry < Locatable + class KeyedEntry < Positioned contains_one_uni 'key', Expression, :lowerBound => 1 contains_one_uni 'value', Expression, :lowerBound => 1 end @@ -209,7 +213,7 @@ module Puppet::Pops::Model # An attribute operation sets or appends a value to a named attribute. # - class AttributeOperation < Locatable + class AttributeOperation < Positioned has_attr 'attribute_name', String, :lowerBound => 1 has_attr 'operator', RGen::MetamodelBuilder::DataTypes::Enum.new([:'=>', :'+>', ]), :lowerBound => 1 contains_one_uni 'value_expr', Expression, :lowerBound => 1 @@ -224,7 +228,7 @@ module Puppet::Pops::Model contains_many_uni 'operations', AttributeOperation end - class Parameter < Locatable + class Parameter < Positioned has_attr 'name', String, :lowerBound => 1 contains_one_uni 'value', Expression end @@ -420,7 +424,7 @@ module Puppet::Pops::Model # A resource body describes one resource instance # - class ResourceBody < Locatable + class ResourceBody < Positioned contains_one_uni 'title', Expression contains_many_uni 'operations', AttributeOperation end @@ -473,7 +477,7 @@ module Puppet::Pops::Model # A selector entry describes a map from matching_expr to value_expr. # - class SelectorEntry < Locatable + class SelectorEntry < Positioned contains_one_uni 'matching_expr', Expression, :lowerBound => 1 contains_one_uni 'value_expr', Expression, :lowerBound => 1 end @@ -489,10 +493,32 @@ module Puppet::Pops::Model # class NamedAccessExpression < BinaryExpression; end + # A Program is the top level construct returned by the parser + # it contains the parsed result in the body, and has a reference to the full source text, + # and its origin. The line_offset's is an array with the start offset of each line. + # class Program < PopsObject contains_one_uni 'body', Expression has_many 'definitions', Definition has_attr 'source_text', String + has_attr 'source_ref', String has_many_attr 'line_offsets', Integer + has_attr 'locator', Object, :lowerBound => 1, :transient => true + + module ClassModule + # Go through the gymnastics of making either value or pattern settable + # with synchronization to the other form. A derived value cannot be serialized + # and we want to serialize the pattern. When recreating the object we need to + # recreate it from the pattern string. + # The below sets both values if one is changed. + # + def locator + unless result = getLocator + setLocator(result = Puppet::Pops::Parser::Locator.locator(source_text, source_ref(), line_offsets)) + end + result + end + end + end end diff --git a/lib/puppet/pops/parser/epp_parser.rb b/lib/puppet/pops/parser/epp_parser.rb index 15dcd4fe5..f5ab90d3d 100644 --- a/lib/puppet/pops/parser/epp_parser.rb +++ b/lib/puppet/pops/parser/epp_parser.rb @@ -44,7 +44,6 @@ class Puppet::Pops::Parser::EppParser < Puppet::Pops::Parser::Parser # rescue => except # raise Puppet::ParseError.new(except.message, @lexer.file, @lexer.line, @lexer.pos, except) end - main.record_origin(@lexer.file) if main return main ensure @lexer.clear diff --git a/lib/puppet/pops/parser/evaluating_parser.rb b/lib/puppet/pops/parser/evaluating_parser.rb index fba96cbc9..94ab47e03 100644 --- a/lib/puppet/pops/parser/evaluating_parser.rb +++ b/lib/puppet/pops/parser/evaluating_parser.rb @@ -66,9 +66,8 @@ class Puppet::Pops::Parser::EvaluatingParser def assert_and_report(parse_result) return nil unless parse_result - # make sure the result has an origin (if parsed from a string) - unless Puppet::Pops::Adapters::OriginAdapter.get(parse_result.model) - Puppet::Pops::Adapters::OriginAdapter.adapt(parse_result.model).origin = @file_source + if parse_result.source_ref.nil? or parse_result.source_ref == '' + parse_result.source_ref = @file_source end validation_result = validate(parse_result) diff --git a/lib/puppet/pops/parser/locatable.rb b/lib/puppet/pops/parser/locatable.rb index 604668603..45fc5cecd 100644 --- a/lib/puppet/pops/parser/locatable.rb +++ b/lib/puppet/pops/parser/locatable.rb @@ -1,10 +1,5 @@ -# Interface for something that is "locateable" -# It holds a reference to a Locator which can compute line number, position on line etc. -# The basic information is offset and length. +# Interface for something that is "locateable" (holds offset and length). class Puppet::Pops::Parser::Locatable - # The locator for this locatable - def locator - end # The offset in the locator's content def offset @@ -14,124 +9,15 @@ class Puppet::Pops::Parser::Locatable def length end - # The file (if given) of the locator's content - def file - locator.file - end - # This class is useful for testing class Fixed < Puppet::Pops::Parser::Locatable attr_reader :offset - attr_reader :pos - attr_reader :line attr_reader :length - def initialize(line, column, offset, length) - @line = line - @pos = column + def initialize(offset, length) @offset = offset @length = length end - - def locator - self - end - - def line_for_offset(offset) - @line - end - - def pos_on_line(offset) - @pos - end end - class Lazy < Puppet::Pops::Parser::Locatable - attr_reader :morphed - - def initialize(loc_reference) - @ref = loc_reference # some object from which location information can be derived - @morphed = false - end - - def locator - ensure_morphed - @ref.locator - end - - def offset - ensure_morphed - @ref.offset - end - - def length - ensure_morphed - @ref.length - end - - def ensure_morphed - unless @morphed - @morphed = true - @ref = morph(@ref) or raise ArgumentError.new("Internal Error: Range not given something locatable in 'from'") - end - end - - def morph(o) - # the object may be a Locatable (= done), a PopsObject (find its source pos and locator), or - # a factory wrapping a pops object - # - o = o.current if o.is_a? Puppet::Pops::Model::Factory - case o - when Puppet::Pops::Model::PopsObject - adapter = Puppet::Pops::Adapters::SourcePosAdapter.get(o) - adapter.nil? ? nil : adapter.locatable - when Puppet::Pops::Parser::Locatable - o - else - raise ArgumentError, "InternalError: Locator Range can not handle an instance of #{o.class}" - end - end - - end - # Combines two Locators into a range. The given from locator must have smaller offset, but - # may overlap with given to-locator. - # - class Range < Puppet::Pops::Parser::Locatable::Lazy - attr_reader :from - attr_reader :to - attr_reader :morphed - - def initialize(from, to) - @from = from - @to = to - @morphed = false - end - - def locator - ensure_morphed - @from.locator - end - - def offset - ensure_morphed - @from.offset - end - - def length - ensure_morphed - @length ||= @to.offset - @from.offset + @to.length - end - - def ensure_morphed - return if @morphed - @morphed = true - @from = morph(@from) or raise ArgumentError.new("Internal Error: Range not given something locatable in 'from'") - if @to.nil? - @to = @from # i.e. no range if @to is nil - else - @to = morph(@to) or raise ArgumentError.new("Internal Error: Range not given something locatable in 'to'") - end - end - - end end diff --git a/lib/puppet/pops/parser/locator.rb b/lib/puppet/pops/parser/locator.rb index ef0d2f4f4..eaada1e06 100644 --- a/lib/puppet/pops/parser/locator.rb +++ b/lib/puppet/pops/parser/locator.rb @@ -24,12 +24,15 @@ class Puppet::Pops::Parser::Locator # Constant set to true if multibyte is supported (includes multibyte extended regular expressions) MULTIBYTE = !!(LOCATOR_VERSION == :ruby19 || LOCATOR_VERSION == :ruby20) - def self.locator(string, file) + # Creates, or recreates a Locator. A Locator is created if index is not given (a scan is then + # performed of the given source string. + # + def self.locator(string, file, index = nil) case LOCATOR_VERSION when :ruby20, :ruby19 - Locator19.new(string, file) + Locator19.new(string, file, index) else - Locator18.new(string, file) + Locator18.new(string, file, index) end end @@ -62,6 +65,12 @@ class Puppet::Pops::Parser::Locator def char_length(offset, end_offset) end + # Returns the line index - an array of line offsets for the start position of each line, starting at 0 for + # the first line. + # + def line_index() + end + private class AbstractLocator < Puppet::Pops::Parser::Locator @@ -75,12 +84,13 @@ class Puppet::Pops::Parser::Locator # Create a locator based on a content string, and a boolean indicating if ruby version support multi-byte strings # or not. # - def initialize(string, file) + def initialize(string, file, index = nil) @string = string.freeze @file = file.freeze @prev_offset = nil @prev_line = nil - compute_line_index + @line_index = index + compute_line_index unless !index.nil? end # Returns the position on line (first position on a line is 1) @@ -101,7 +111,7 @@ class Puppet::Pops::Parser::Locator # the corresponding method in C in Ruby 2.0.0 - the main benefit to use this method over # the Ruby C version is that it returns the index (not the value) which means there is not need # to have an additional structure to get the index (or record the index in the structure). This - # saes both memory and CPU. It also does not require passing a block that is called since this + # saves both memory and CPU. It also does not require passing a block that is called since this # method is specialized to search the line index. # def ary_bsearch_i(ary, value) @@ -143,7 +153,7 @@ class Puppet::Pops::Parser::Locator while scanner.scan_until(/\n/) result << scanner.pos end - self.line_index = result + self.line_index = result.freeze end # Returns the line number (first line is 1) for the given offset diff --git a/lib/puppet/pops/parser/parser_support.rb b/lib/puppet/pops/parser/parser_support.rb index ee57ba130..2fe082351 100644 --- a/lib/puppet/pops/parser/parser_support.rb +++ b/lib/puppet/pops/parser/parser_support.rb @@ -184,7 +184,8 @@ class Puppet::Pops::Parser::Parser # If there are definitions that require initialization a Program is produced, else the body def create_program(body) - definitions.empty? ? body : Factory.PROGRAM(body, definitions) + locator = @lexer.locator + Factory.PROGRAM(body, definitions, locator) end # Performs the parsing and returns the resulting model. @@ -211,7 +212,6 @@ class Puppet::Pops::Parser::Parser # rescue => except # raise Puppet::ParseError.new(except.message, @lexer.file, @lexer.line, @lexer.pos, except) end - main.record_origin(@lexer.file) if main return main ensure @lexer.clear diff --git a/lib/puppet/pops/types/type_parser.rb b/lib/puppet/pops/types/type_parser.rb index b4a826c10..f351ffe14 100644 --- a/lib/puppet/pops/types/type_parser.rb +++ b/lib/puppet/pops/types/type_parser.rb @@ -46,6 +46,7 @@ class Puppet::Pops::Types::TypeParser # @api private def interpret(ast) result = @type_transformer.visit_this_0(self, ast) + result = result.body if result.is_a?(Puppet::Pops::Model::Program) raise_invalid_type_specification_error unless result.is_a?(Puppet::Pops::Types::PAbstractType) result end @@ -317,7 +318,8 @@ class Puppet::Pops::Types::TypeParser end when "string" - size_type = case parameters.size + size_type = + case parameters.size when 1 if parameters[0].is_a?(Puppet::Pops::Types::PIntegerType) parameters[0].copy @@ -396,6 +398,6 @@ class Puppet::Pops::Types::TypeParser def original_text_of(ast) position = Puppet::Pops::Adapters::SourcePosAdapter.adapt(ast) - position.extract_text_from_string(@string || position.locator.string) + position.extract_text() end end diff --git a/lib/puppet/pops/utils.rb b/lib/puppet/pops/utils.rb index fec084d38..45e166984 100644 --- a/lib/puppet/pops/utils.rb +++ b/lib/puppet/pops/utils.rb @@ -93,10 +93,12 @@ module Puppet::Pops::Utils is_absolute?(name) ? name[2..-1] : name end - # Finds an adapter for o or for one of its containers, or nil, if none of the containers + # Finds an existing adapter for o or for one of its containers, or nil, if none of the containers # was adapted with the given adapter. # This method can only be used with objects that respond to `:eContainer`. - # with true, and Adaptable#adapters. + # with true. + # + # @see #find_closest_positioned # def self.find_adapter(o, adapter) return nil if o.nil? || (o.is_a?(Array) && o.empty?) @@ -104,4 +106,15 @@ module Puppet::Pops::Utils return a if a return find_adapter(o.eContainer, adapter) end + + # Finds the closest positioned Puppet::Pops::Model::Positioned object, or object decorated with + # a SourcePosAdapter, and returns + # a SourcePosAdapter for the first found, or nil if not found. + # + def self.find_closest_positioned(o) + return nil if o.nil? || o.is_a?(Puppet::Pops::Model::Program) || (o.is_a?(Array) && o.empty?) + return find_adapter(o, Puppet::Pops::Adapters::SourcePosAdapter) unless o.is_a?(Puppet::Pops::Model::Positioned) + o.offset.nil? ? find_closest_positioned(o.eContainer) : Puppet::Pops::Adapters::SourcePosAdapter.adapt(o) + end + end diff --git a/lib/puppet/pops/validation.rb b/lib/puppet/pops/validation.rb index 38976a9dd..58e97be5e 100644 --- a/lib/puppet/pops/validation.rb +++ b/lib/puppet/pops/validation.rb @@ -198,9 +198,9 @@ module Puppet::Pops::Validation # TODO: this support is questionable, it requires knowledge that :detail is special arguments[:detail] ||= '' - origin_adapter = Puppet::Pops::Utils.find_adapter(semantic, Puppet::Pops::Adapters::OriginAdapter) - file = origin_adapter ? origin_adapter.origin : nil - source_pos = Puppet::Pops::Utils.find_adapter(semantic, Puppet::Pops::Adapters::SourcePosAdapter) + source_pos = Puppet::Pops::Utils.find_closest_positioned(semantic) + file = source_pos ? source_pos.locator.file : nil + severity = @severity_producer.severity(issue) @acceptor.accept(Diagnostic.new(severity, issue, file, source_pos, arguments)) end diff --git a/spec/unit/parser/functions/lookup_spec.rb b/spec/unit/parser/functions/lookup_spec.rb index 05484adfa..b6d909b6f 100644 --- a/spec/unit/parser/functions/lookup_spec.rb +++ b/spec/unit/parser/functions/lookup_spec.rb @@ -140,7 +140,7 @@ describe "lookup function" do def ast_lambda(puppet_source) puppet_source = "fake_func() " + puppet_source model = Puppet::Pops::Parser::EvaluatingParser.new().parse_string(puppet_source, __FILE__).current - model = model.lambda + model = model.body.lambda Puppet::Pops::Model::AstTransformer.new(@file_source, nil).transform(model) end end diff --git a/spec/unit/pops/binder/injector_spec.rb b/spec/unit/pops/binder/injector_spec.rb index 1c2c66a49..32eba3260 100644 --- a/spec/unit/pops/binder/injector_spec.rb +++ b/spec/unit/pops/binder/injector_spec.rb @@ -745,7 +745,7 @@ describe 'Injector' do it "should be possible to post process lookup with a puppet lambda" do model = parser.parse_string('fake() |$value| {$value + 1 }').current - bindings.bind.name('an_int').to(42).producer_options( :transformer => model.lambda) + bindings.bind.name('an_int').to(42).producer_options( :transformer => model.body.lambda) injector(lbinder).lookup(scope, 'an_int').should == 43 end diff --git a/spec/unit/pops/model/ast_transformer_spec.rb b/spec/unit/pops/model/ast_transformer_spec.rb index beec804e4..d62d2eaec 100644 --- a/spec/unit/pops/model/ast_transformer_spec.rb +++ b/spec/unit/pops/model/ast_transformer_spec.rb @@ -55,7 +55,9 @@ describe Puppet::Pops::Model::AstTransformer do it "preserves the file location" do model = literal(1) - model.record_position(location(3, 1, 10, 1), nil) + adapter = Puppet::Pops::Adapters::SourcePosAdapter.adapt(model.current) + adapter.locator = Puppet::Pops::Parser::Locator.locator("\n\n1",filename) + model.record_position(location(2, 1), nil) ast = transform(model) @@ -68,7 +70,7 @@ describe Puppet::Pops::Model::AstTransformer do transformer.transform(model) end - def location(line, column, offset, length) - Puppet::Pops::Parser::Locatable::Fixed.new(line, column, offset, length) + def location(offset, length) + Puppet::Pops::Parser::Locatable::Fixed.new(offset, length) end end diff --git a/spec/unit/pops/parser/parser_spec.rb b/spec/unit/pops/parser/parser_spec.rb index e7a9c7cbe..fb44a08c9 100644 --- a/spec/unit/pops/parser/parser_spec.rb +++ b/spec/unit/pops/parser/parser_spec.rb @@ -10,7 +10,8 @@ describe Puppet::Pops::Parser::Parser do it "should parse a code string and return a model" do parser = Puppet::Pops::Parser::Parser.new() model = parser.parse_string("$a = 10").current - model.class.should == Puppet::Pops::Model::AssignmentExpression + model.class.should == Puppet::Pops::Model::Program + model.body.class.should == Puppet::Pops::Model::AssignmentExpression end end diff --git a/spec/unit/pops/types/type_parser_spec.rb b/spec/unit/pops/types/type_parser_spec.rb index 54a3283ac..691d7ff81 100644 --- a/spec/unit/pops/types/type_parser_spec.rb +++ b/spec/unit/pops/types/type_parser_spec.rb @@ -32,7 +32,7 @@ describe Puppet::Pops::Types::TypeParser do [ 'Object', 'Data', 'CatalogEntry', 'Boolean', 'Literal', 'Undef', 'Numeric', ].each do |name| - it "does not support parameterizing unparameterized type <#{name}" do + it "does not support parameterizing unparameterized type <#{name}>" do expect { parser.parse("#{name}[Integer]") }.to raise_unparameterized_error_for(name) end end From fcfe176d2765648ecf241c4430a9264770e3f7cf Mon Sep 17 00:00:00 2001 From: Bert Hajee Date: Wed, 27 Nov 2013 23:05:07 +0100 Subject: [PATCH 387/800] (pup-899) Add deprecation message for use of parent in new type --- lib/puppet/metatype/manager.rb | 12 ++++++------ spec/unit/type_spec.rb | 11 +++++++++++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/lib/puppet/metatype/manager.rb b/lib/puppet/metatype/manager.rb index 3d640879f..a0a21b1ca 100644 --- a/lib/puppet/metatype/manager.rb +++ b/lib/puppet/metatype/manager.rb @@ -49,9 +49,8 @@ module Manager # method is kept). # # @param name [String] the name of the type to create or redefine. - # @param options [Hash] options passed on to {Puppet::Util::ClassGen#genclass} as the option `:attributes` after - # first having removed any present `:parent` option. - # @option options [Puppet::Type] :parent the parent (super type) of this type. If nil, the default is + # @param options [Hash] options passed on to {Puppet::Util::ClassGen#genclass} as the option `:attributes`. + # @option options [Puppet::Type] # Puppet::Type. This option is not passed on as an attribute to genclass. # @yield [ ] a block evaluated in the context of the created class, thus allowing further detailing of # that class. @@ -64,7 +63,6 @@ module Manager # Handle backward compatibility unless options.is_a?(Hash) Puppet.warning "Puppet::Type.newtype(#{name}) now expects a hash as the second argument, not #{options.inspect}" - options = {:parent => options} end # First make sure we don't have a method sitting around @@ -85,7 +83,9 @@ module Manager options = symbolize_options(options) - if parent = options[:parent] + + if options.include?(:parent) + Puppet.deprecation_warning "option :parent is deprecated. It has no effect" options.delete(:parent) end @@ -93,7 +93,7 @@ module Manager klass = genclass( name, - :parent => (parent || Puppet::Type), + :parent => Puppet::Type, :overwrite => true, :hash => @types, :attributes => options, diff --git a/spec/unit/type_spec.rb b/spec/unit/type_spec.rb index ca29d680e..75a5a0768 100755 --- a/spec/unit/type_spec.rb +++ b/spec/unit/type_spec.rb @@ -347,6 +347,17 @@ describe Puppet::Type, :unless => Puppet.features.microsoft_windows? do end end + + describe "when defining a parent on a newtype" do + it "prints a deprecation message" do + Puppet.expects(:deprecation_warning) + type = Puppet::Type.newtype(:test_with_parent, :parent => Puppet::Type) do + newparam(:name) do end + end + end + end + + describe "when initializing" do describe "and passed a Puppet::Resource instance" do it "should set its title to the title of the resource if the resource type is equal to the current type" do From 207bb32ec20569e1c71defe614a1f6150e28d9ae Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Wed, 8 Jan 2014 03:15:35 +0100 Subject: [PATCH 388/800] (PUP-716) Fix issues found in review (mainly typos) --- lib/puppet/application/agent.rb | 2 +- lib/puppet/face/config.rb | 2 +- lib/puppet/file_serving/metadata.rb | 2 +- lib/puppet/file_system.rb | 23 ++++++++++++++++++++--- lib/puppet/parser/lexer.rb | 2 +- lib/puppet/pops/parser/lexer.rb | 2 +- lib/puppet/pops/parser/lexer2.rb | 2 +- lib/puppet/util.rb | 4 ++-- lib/puppet/util/log/destinations.rb | 2 +- spec/unit/parser/parser_spec.rb | 2 +- 10 files changed, 30 insertions(+), 13 deletions(-) diff --git a/lib/puppet/application/agent.rb b/lib/puppet/application/agent.rb index b84003e92..7c7c11f4e 100644 --- a/lib/puppet/application/agent.rb +++ b/lib/puppet/application/agent.rb @@ -437,7 +437,7 @@ Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License def setup_listen(daemon) Puppet.warning "Puppet --listen / kick is deprecated. See http://links.puppetlabs.com/puppet-kick-deprecation" - unless Puppet::FileSystem::exist?(Puppet[:rest_authconfig]) + unless Puppet::FileSystem.exist?(Puppet[:rest_authconfig]) Puppet.err "Will not start without authorization file #{Puppet[:rest_authconfig]}" exit(14) end diff --git a/lib/puppet/face/config.rb b/lib/puppet/face/config.rb index 28af1d9e7..dd51502dd 100644 --- a/lib/puppet/face/config.rb +++ b/lib/puppet/face/config.rb @@ -78,7 +78,7 @@ Puppet::Face.define(:config, '0.0.1') do when_invoked do |name, value, options| path = Puppet::FileSystem.pathname(Puppet.settings.which_configuration_file) Puppet::FileSystem.touch(path) - Puppet::FileSystem..open(path, nil, 'r+') do |file| + Puppet::FileSystem.open(path, nil, 'r+') do |file| Puppet::Settings::IniFile.update(file) do |config| config.set(options[:section], name, value) end diff --git a/lib/puppet/file_serving/metadata.rb b/lib/puppet/file_serving/metadata.rb index a47f0fcc9..ae9376180 100644 --- a/lib/puppet/file_serving/metadata.rb +++ b/lib/puppet/file_serving/metadata.rb @@ -65,7 +65,7 @@ class Puppet::FileServing::Metadata < Puppet::FileServing::Base end # Retrieve the attributes for this file, relative to a base directory. - # Note that Puppet::FileSystemstat(path) raises Errno::ENOENT + # Note that Puppet::FileSystem.stat(path) raises Errno::ENOENT # if the file is absent and this method does not catch that exception. def collect real_path = full_path diff --git a/lib/puppet/file_system.rb b/lib/puppet/file_system.rb index 063c5d3bb..cf7583d1b 100644 --- a/lib/puppet/file_system.rb +++ b/lib/puppet/file_system.rb @@ -17,7 +17,8 @@ module Puppet::FileSystem Puppet::FileSystem::File19 end.new() - # Allows overriding the filesystem for the duration of the given block. The filesystem will contain only the file(s) provided. + # Allows overriding the filesystem for the duration of the given block. + # The filesystem will only contain the given file(s). # # @param files [Puppet::FileSystem::MemoryFile] the files to have available # @@ -39,7 +40,7 @@ module Puppet::FileSystem @impl.open(assert_path(path), mode, options, &block) end - # @return [Pathname] The directory of this file + # @return [Object] The directory of this file as an opaque handle # # @api public # @@ -47,6 +48,14 @@ module Puppet::FileSystem @impl.dir(assert_path(path)) end + # @return [String] The directory of this file as a String + # + # @api public + # + def self.dir_string(path) + @impl.path_string(@impl.dir(assert_path(path))) + end + # @return [Boolean] Does the directory of the given path exist? def self.dir_exist?(path) @impl.exist?(@impl.dir(assert_path(path))) @@ -57,7 +66,7 @@ module Puppet::FileSystem @impl.mkpath(@impl.dir(assert_path(path))) end - # @return [String] the name of the file + # @return [Object] the name of the file as a opaque handle # # @api public # @@ -65,6 +74,14 @@ module Puppet::FileSystem @impl.basename(assert_path(path)) end + # @return [String] the name of the file + # + # @api public + # + def self.basename_string(path) + @impl.path_string(@impl.basename(assert_path(path))) + end + # @return [Integer] the size of the file # # @api public diff --git a/lib/puppet/parser/lexer.rb b/lib/puppet/parser/lexer.rb index bb0e6f6f2..08e3fab18 100644 --- a/lib/puppet/parser/lexer.rb +++ b/lib/puppet/parser/lexer.rb @@ -347,7 +347,7 @@ class Puppet::Parser::Lexer def file=(file) @file = file @line = 1 - contents = Puppet::FileSystem.exist?(file) ? File.read(file) : "" + contents = Puppet::FileSystem.exist?(file) ? Puppet::FileSystem.read(file) : "" @scanner = StringScanner.new(contents) end diff --git a/lib/puppet/pops/parser/lexer.rb b/lib/puppet/pops/parser/lexer.rb index 65cc9d969..97a330a56 100644 --- a/lib/puppet/pops/parser/lexer.rb +++ b/lib/puppet/pops/parser/lexer.rb @@ -398,7 +398,7 @@ class Puppet::Pops::Parser::Lexer def file=(file) @file = file - contents = Puppet::FileSystem.exist?(file) ? File.read(file) : "" + contents = Puppet::FileSystem.exist?(file) ? Puppet::FileSystem.read(file) : "" @scanner = StringScanner.new(contents.freeze) @locator = Puppet::Pops::Parser::Locator.locator(contents, file) end diff --git a/lib/puppet/pops/parser/lexer2.rb b/lib/puppet/pops/parser/lexer2.rb index a15225346..d2ac2eab4 100644 --- a/lib/puppet/pops/parser/lexer2.rb +++ b/lib/puppet/pops/parser/lexer2.rb @@ -225,7 +225,7 @@ class Puppet::Pops::Parser::Lexer2 # def lex_file(file) initvars - contents = Puppet::FileSystem.exist?(file) ? File.read(file) : "" + contents = Puppet::FileSystem.exist?(file) ? Puppet::FileSystem.read(file) : "" @scanner = StringScanner.new(contents.freeze) @locator = Puppet::Pops::Parser::Locator.locator(contents, file) end diff --git a/lib/puppet/util.rb b/lib/puppet/util.rb index 583a92f95..628de3b2d 100644 --- a/lib/puppet/util.rb +++ b/lib/puppet/util.rb @@ -397,7 +397,7 @@ module Util end file = Puppet::FileSystem.pathname(file) - tempfile = Tempfile.new(Puppet::FileSystem.path_string(Puppet::FileSystem.basename(file)), Puppet::FileSystem.path_string(Puppet::FileSystem.dir(file))) + tempfile = Tempfile.new(Puppet::FileSystem.basename_string(file), Puppet::FileSystem.dir_string(file)) # Set properties of the temporary file before we write the content, because # Tempfile doesn't promise to be safe from reading by other people, just @@ -454,7 +454,7 @@ module Util end # Yes, the arguments are reversed compared to the rename in the rest # of the world. - Puppet::Util::Windows::File.replace_file(file, tempfile.path) + Puppet::Util::Windows::File.replace_file(FileSystem.path_string(file), tempfile.path) else File.rename(tempfile.path, Puppet::FileSystem.path_string(file)) diff --git a/lib/puppet/util/log/destinations.rb b/lib/puppet/util/log/destinations.rb index a2b448624..32041066a 100644 --- a/lib/puppet/util/log/destinations.rb +++ b/lib/puppet/util/log/destinations.rb @@ -68,7 +68,7 @@ Puppet::Util::Log.newdesttype :file do # first make sure the directory exists # We can't just use 'Config.use' here, because they've # specified a "special" destination. - unless Puppet::FileSystem.exist?(File.dirname(path)) + unless Puppet::FileSystem.exist?(Puppet::FileSystem.dir(path)) FileUtils.mkdir_p(File.dirname(path), :mode => 0755) Puppet.info "Creating log directory #{File.dirname(path)}" end diff --git a/spec/unit/parser/parser_spec.rb b/spec/unit/parser/parser_spec.rb index 0d9cf66d0..466bfea13 100755 --- a/spec/unit/parser/parser_spec.rb +++ b/spec/unit/parser/parser_spec.rb @@ -54,7 +54,7 @@ describe Puppet::Parser do describe "when parsing files" do before do Puppet::FileSystem.stubs(:exist?).returns true - File.stubs(:read).returns "" + Puppet::FileSystem.stubs(:read).returns "" @parser.stubs(:watch_file) end From f9bcdf64ffbb148016b63ab58a88067a92a1931a Mon Sep 17 00:00:00 2001 From: Carlos Sanchez Date: Fri, 3 Jan 2014 10:38:05 +0100 Subject: [PATCH 389/800] (PUP-1350) keep original stacktrace when error happens parsing inline template Helps with debugging errors in template --- lib/puppet/parser/functions/inline_template.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/parser/functions/inline_template.rb b/lib/puppet/parser/functions/inline_template.rb index 03a3a3879..b3ab9d30e 100644 --- a/lib/puppet/parser/functions/inline_template.rb +++ b/lib/puppet/parser/functions/inline_template.rb @@ -15,7 +15,7 @@ Puppet::Parser::Functions::newfunction(:inline_template, :type => :rvalue, :arit wrapper.result(string) rescue => detail raise Puppet::ParseError, - "Failed to parse inline template: #{detail}" + "Failed to parse inline template: #{detail}", detail.backtrace end end.join("") end From f9b76ae4c0b1fe59ca81edd362d7dc1a641b32e6 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Wed, 8 Jan 2014 11:00:01 -0800 Subject: [PATCH 390/800] (maint) Improve docs for acceptance ci:test:git from another FORK --- acceptance/README.md | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/acceptance/README.md b/acceptance/README.md index 13fc175a4..ae4411a04 100644 --- a/acceptance/README.md +++ b/acceptance/README.md @@ -14,6 +14,12 @@ General Notes The rake tasks for running the tests are defined by the Rakefile in the same directory as this file. These tasks come with some documentation: `rake -T` will give short descriptions, and a `rake -D` will give full descriptions with information on ENV options required and optional for the various tasks. +If you are setting up a new repository for acceptance, you will need to bundle install first. This step assumes you have ruby and the bundler gem installed. + +```sh +cd /path/to/repo/acceptance +bundle install --path=.bundle/gems +``` Running Tests on the vcloud --------------------------- @@ -38,9 +44,11 @@ You will also need QA credentials to vsphere in a ~/.fog file. These credential In order to run the tests on hosts provisioned from packages produced by Delivery, you will need to reference a Puppet commit sha that has been packaged using Delivery's pl:jenkins:uber_build task. This is the snippet used by 'Puppet Packaging' Jenkins jobs: - rake --trace package:implode - rake --trace package:bootstrap - rake --trace pl:jenkins:uber_build +```sh +rake --trace package:implode +rake --trace package:bootstrap +rake --trace pl:jenkins:uber_build +``` The above Rake tasks were run from the root of a Puppet checkout. They are quoted just for reference. Typically if you are investigating a failure, you will have a SHA from a failed jenkins run which should correspond to a successful pipeline run, and you should not need to run the pipeline manually. @@ -48,15 +56,31 @@ A finished pipeline will have repository information available at http://builds. When executing the ci:test:packages task, you must set the SHA, and also set CONFIG to point to a valid Beaker hosts_file. Configurations used in the Jenkins jobs are available under config/nodes - bundle exec rake ci:test:packages SHA=abcdef CONFIG=config/nodes/rhel.yaml +```sh +bundle exec rake ci:test:packages SHA=abcdef CONFIG=config/nodes/rhel.yaml +``` Optionally you may set the TEST (TEST=a/test.rb,and/another/test.rb), and may pass additional OPTIONS to beaker (OPTIONS='--opt foo'). -You may also edit a ./local_options.rb hash which will override config/ options, and in turn be oferriden by commandline options set in the environment variables CONFIG, TEST and OPTIONS. This file is a ruby file containing a Ruby hash with configuration expected by Beaker. See Beaker source, and examples in config/. +You may also edit a ./local_options.rb hash which will override config/ options, and in turn be overriden by commandline options set in the environment variables CONFIG, TEST and OPTIONS. This file is a ruby file containing a Ruby hash with configuration expected by Beaker. See Beaker source, and examples in config/. ### Git -Alternatively you may provision via git clone by calling the ci:test:git task. Currently we don't have packages for Windows or Solaris from the Delivery pipeline, and must use ci:test:git to privision and test these platforms. +Alternatively you may provision via git clone by calling the ci:test:git task. Currently we don't have packages for Windows or Solaris from the Delivery pipeline, and must use ci:test:git to provision and test these platforms. + +#### Source Checkout for Different Fork + +If you have a branch pushed to your fork which you wish to test prior to merging into puppetlabs/puppet, you can do so be setting the FORK environment variable. So, if I have a branch 'issue/master/wonder-if-this-explodes' pushed to my jpartlow puppet fork that I want to test on Windows, I could invoke the following: + +```sh +bundle exec ci:test:git CONFIG=config/nodes/win2008r2.yaml SHA=issue/master/wonder-if-this-explodes FORK=jpartlow +``` + +#### Source Checkout for Local Branch + +See notes on running acceptance with Vagrant for more details on using a local git daemon. + +TODO Fix up the Rakefile's handling of git urls so that there is a simple way to specify both a branch on a github fork, and a branch on some other git server daemon, so that you have fewer steps when serving from a local git daemon. ### Preserving Hosts From d5849777b894edf5a1ff010f2c205eb308548aae Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 9 Jan 2014 00:07:14 +0100 Subject: [PATCH 391/800] (PUP-1212) Fix uninformative stack backtrace in evaluator errors Now the backtrace used is the original, not the place where the evaluator handles exceptions. --- lib/puppet/pops/issue_reporter.rb | 7 ++++++- lib/puppet/pops/validation.rb | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/puppet/pops/issue_reporter.rb b/lib/puppet/pops/issue_reporter.rb index bc9511cbe..28ee971fe 100644 --- a/lib/puppet/pops/issue_reporter.rb +++ b/lib/puppet/pops/issue_reporter.rb @@ -48,7 +48,12 @@ class Puppet::Pops::IssueReporter formatter = Puppet::Pops::Validation::DiagnosticFormatterPuppetStyle.new if errors.size == 1 || max_errors <= 1 # raise immediately - raise emit_exception.new(format_with_prefix(emit_message, formatter.format(errors[0]))) + exception = emit_exception.new(format_with_prefix(emit_message, formatter.format(errors[0]))) + # if an exception was given as cause, use it's backtrace instead of the one indicating "here" + if errors[0].exception + exception.set_backtrace(errors[0].exception.backtrace) + end + raise exception end emitted = 0 if emit_message diff --git a/lib/puppet/pops/validation.rb b/lib/puppet/pops/validation.rb index 58e97be5e..eddd7ab2d 100644 --- a/lib/puppet/pops/validation.rb +++ b/lib/puppet/pops/validation.rb @@ -202,7 +202,7 @@ module Puppet::Pops::Validation file = source_pos ? source_pos.locator.file : nil severity = @severity_producer.severity(issue) - @acceptor.accept(Diagnostic.new(severity, issue, file, source_pos, arguments)) + @acceptor.accept(Diagnostic.new(severity, issue, file, source_pos, arguments, except)) end def will_accept? issue From a08de7949db8fb8437d46e6b06babb267244dce1 Mon Sep 17 00:00:00 2001 From: Dan Lidral-Porter Date: Mon, 6 Jan 2014 14:27:25 -0800 Subject: [PATCH 392/800] (PUP-1151) Environment enumeration authorization test. Add a test that unauthorized requests to the environment enumeration endpoint receive a 403 response. Check for the hardcoded endpoint in Puppet::Network::HTTP::Handler and raise a not authorized error no matter what. --- .../environment/can_enumerate_environments.rb | 15 ++++++++ lib/puppet/network/http/handler.rb | 37 ++++++++++++------- 2 files changed, 38 insertions(+), 14 deletions(-) create mode 100644 acceptance/tests/environment/can_enumerate_environments.rb diff --git a/acceptance/tests/environment/can_enumerate_environments.rb b/acceptance/tests/environment/can_enumerate_environments.rb new file mode 100644 index 000000000..12180515a --- /dev/null +++ b/acceptance/tests/environment/can_enumerate_environments.rb @@ -0,0 +1,15 @@ +test_name "Can enumerate enviromnents via an HTTP endpoint" + +def master_url_for(agent, path) + master_port = on(agent, "puppet config print masterport --section agent").stdout.chomp + master_host = on(agent, "puppet config print server --section agent").stdout.chomp + "https://#{master_host}:#{master_port}#{path}" +end + +with_puppet_running_on(master, {}) do + agents.each do |agent| + on agent, "curl -ksv #{master_url_for(agent, '/v2/environments')}", :acceptable_exit_codes => [0,7] do + assert_match(/< HTTP\/1\.\d 403/, stderr) + end + end +end diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb index 63a470e6c..23b6b7c7c 100644 --- a/lib/puppet/network/http/handler.rb +++ b/lib/puppet/network/http/handler.rb @@ -39,6 +39,12 @@ module Puppet::Network::HTTP::Handler end end + class HTTPNotAuthorizedError < HTTPError + def initialize(message) + super("Not Authorized: " + message, 403) + end + end + attr_reader :server, :handler # Retrieve all headers from the http request, as a hash with the header names @@ -87,24 +93,27 @@ module Puppet::Network::HTTP::Handler response[Puppet::Network::HTTP::HEADER_PUPPET_VERSION] = Puppet.version configure_profiler(request_headers, request_params) + if request_path == "/v2/environments" + raise HTTPNotAuthorizedError, "You shall not pass!" + else + Puppet::Util::Profiler.profile("Processed request #{request_method} #{request_path}") do + indirection_name, method, key, params = uri2indirection(request_method, request_path, request_params) + certificate = client_cert(request) - Puppet::Util::Profiler.profile("Processed request #{request_method} #{request_path}") do - indirection_name, method, key, params = uri2indirection(request_method, request_path, request_params) - certificate = client_cert(request) + check_authorization(indirection_name, method, key, params) + warn_if_near_expiration(certificate) - check_authorization(indirection_name, method, key, params) - warn_if_near_expiration(certificate) + indirection = Puppet::Indirector::Indirection.instance(indirection_name.to_sym) + raise ArgumentError, "Could not find indirection '#{indirection_name}'" unless indirection - indirection = Puppet::Indirector::Indirection.instance(indirection_name.to_sym) - raise ArgumentError, "Could not find indirection '#{indirection_name}'" unless indirection + if !indirection.allow_remote_requests? + raise HTTPNotFoundError, "No handler for #{indirection.name}" + end - if !indirection.allow_remote_requests? - raise HTTPNotFoundError, "No handler for #{indirection.name}" - end - - trusted = Puppet::Context::TrustedInformation.remote(params[:authenticated], params[:node], certificate) - Puppet::Context.override(:trusted_information => trusted) do - send("do_#{method}", indirection, key, params, request, response) + trusted = Puppet::Context::TrustedInformation.remote(params[:authenticated], params[:node], certificate) + Puppet::Context.override(:trusted_information => trusted) do + send("do_#{method}", indirection, key, params, request, response) + end end end rescue HTTPError => e From ecddcc502351a02d934c7244e09a8c55fadfb647 Mon Sep 17 00:00:00 2001 From: Dan Lidral-Porter Date: Tue, 7 Jan 2014 14:02:29 -0800 Subject: [PATCH 393/800] (PUP-1151) Factor out v1 HTTP request processing into own request processor. The end goal is to have v1 and v2 API endpoints in their own processors, with the routes registered on a shared handler that performs client authentication and dispatch. This commit rips out the v1 endpoints to their own processor, but does not define anything about the handler including registration. v1-related behavorial tests have been moved from the handler spec to the v1 spec. --- lib/puppet/indirector/memory.rb | 4 + lib/puppet/indirector/rest.rb | 11 +- lib/puppet/network/http/api/v1.rb | 143 +++++- lib/puppet/network/http/handler.rb | 267 ++++------- lib/puppet/network/http/rack/rest.rb | 15 +- lib/puppet/network/http/webrick/rest.rb | 8 - lib/puppet/type/file/content.rb | 3 +- .../indirector/indirector_testing/memory.rb | 7 + spec/lib/puppet/indirector_testing.rb | 6 + spec/unit/indirector/rest_spec.rb | 4 - spec/unit/network/http/api/v1_spec.rb | 433 +++++++++++++++--- spec/unit/network/http/handler_spec.rb | 415 +---------------- spec/unit/network/http/rack/rest_spec.rb | 10 +- spec/unit/network/http/webrick/rest_spec.rb | 10 - 14 files changed, 625 insertions(+), 711 deletions(-) create mode 100644 spec/lib/puppet/indirector/indirector_testing/memory.rb diff --git a/lib/puppet/indirector/memory.rb b/lib/puppet/indirector/memory.rb index 0e3afaef7..c16b7e975 100644 --- a/lib/puppet/indirector/memory.rb +++ b/lib/puppet/indirector/memory.rb @@ -3,6 +3,10 @@ require 'puppet/indirector/terminus' # Manage a memory-cached list of instances. class Puppet::Indirector::Memory < Puppet::Indirector::Terminus def initialize + clear + end + + def clear @instances = {} end diff --git a/lib/puppet/indirector/rest.rb b/lib/puppet/indirector/rest.rb index d70b43bdf..ea2eaadcd 100644 --- a/lib/puppet/indirector/rest.rb +++ b/lib/puppet/indirector/rest.rb @@ -8,7 +8,6 @@ require 'puppet/network/http/compression' # Access objects via REST class Puppet::Indirector::REST < Puppet::Indirector::Terminus - include Puppet::Network::HTTP::API::V1 include Puppet::Network::HTTP::Compression.module class << self @@ -85,7 +84,7 @@ class Puppet::Indirector::REST < Puppet::Indirector::Terminus end def find(request) - uri, body = request_to_uri_and_body(request) + uri, body = Puppet::Network::HTTP::API::V1.request_to_uri_and_body(request) uri_with_query_string = "#{uri}?#{body}" response = do_request(request) do |request| @@ -111,7 +110,7 @@ class Puppet::Indirector::REST < Puppet::Indirector::Terminus def head(request) response = do_request(request) do |request| - http_head(request, indirection2uri(request), headers) + http_head(request, Puppet::Network::HTTP::API::V1.indirection2uri(request), headers) end if is_http_200?(response) @@ -124,7 +123,7 @@ class Puppet::Indirector::REST < Puppet::Indirector::Terminus def search(request) response = do_request(request) do |request| - http_get(request, indirection2uri(request), headers) + http_get(request, Puppet::Network::HTTP::API::V1.indirection2uri(request), headers) end if is_http_200?(response) @@ -140,7 +139,7 @@ class Puppet::Indirector::REST < Puppet::Indirector::Terminus raise ArgumentError, "DELETE does not accept options" unless request.options.empty? response = do_request(request) do |request| - http_delete(request, indirection2uri(request), headers) + http_delete(request, Puppet::Network::HTTP::API::V1.indirection2uri(request), headers) end if is_http_200?(response) @@ -156,7 +155,7 @@ class Puppet::Indirector::REST < Puppet::Indirector::Terminus raise ArgumentError, "PUT does not accept options" unless request.options.empty? response = do_request(request) do |request| - http_put(request, indirection2uri(request), request.instance.render, headers.merge({ "Content-Type" => request.instance.mime })) + http_put(request, Puppet::Network::HTTP::API::V1.indirection2uri(request), request.instance.render, headers.merge({ "Content-Type" => request.instance.mime })) end if is_http_200?(response) diff --git a/lib/puppet/network/http/api/v1.rb b/lib/puppet/network/http/api/v1.rb index 29146ff9b..fd8f0deca 100644 --- a/lib/puppet/network/http/api/v1.rb +++ b/lib/puppet/network/http/api/v1.rb @@ -1,6 +1,9 @@ +require 'puppet/network/authorization' require 'puppet/network/http/api' -module Puppet::Network::HTTP::API::V1 +class Puppet::Network::HTTP::API::V1 + include Puppet::Network::Authorization + # How we map http methods and the indirection name in the URI # to an indirection method. METHOD_MAP = { @@ -22,6 +25,31 @@ module Puppet::Network::HTTP::API::V1 } } + + # handle an HTTP request + def process(request, response) + indirection_name, method, key, params = uri2indirection(request.method, request.path, request.params) + certificate = request.client_cert + + check_authorization(indirection_name, method, key, params) + + indirection = Puppet::Indirector::Indirection.instance(indirection_name.to_sym) + raise ArgumentError, "Could not find indirection '#{indirection_name}'" unless indirection + + if !indirection.allow_remote_requests? + raise Puppet::Network::HTTP::Handler::HTTPNotFoundError, "No handler for #{indirection.name}" + end + + trusted = Puppet::Context::TrustedInformation.remote(params[:authenticated], params[:node], certificate) + Puppet::Context.override(:trusted_information => trusted) do + send("do_#{method}", indirection, key, params, request, response) + end + rescue Puppet::Network::HTTP::Handler::HTTPError => e + return do_http_control_exception(response, e) + rescue Exception => e + return do_exception(response, e) + end + def uri2indirection(http_method, uri, params) environment, indirection, key = uri.split("/", 4)[1..-1] # the first field is always nil because of the leading slash @@ -40,14 +68,103 @@ module Puppet::Network::HTTP::API::V1 [indirection, method, key, params] end - def indirection2uri(request) - indirection = request.method == :search ? pluralize(request.indirection_name.to_s) : request.indirection_name.to_s - "/#{request.environment.to_s}/#{indirection}/#{request.escaped_key}#{request.query_string}" + private + + def do_http_control_exception(response, exception) + msg = exception.message + Puppet.info(msg) + response.respond_with(exception.status, "text/plain", msg) end - def request_to_uri_and_body(request) - indirection = request.method == :search ? pluralize(request.indirection_name.to_s) : request.indirection_name.to_s - ["/#{request.environment.to_s}/#{indirection}/#{request.escaped_key}", request.query_string.sub(/^\?/,'')] + def do_exception(response, exception, status=400) + if exception.is_a?(Puppet::Network::AuthorizationError) + # make sure we return the correct status code + # for authorization issues + status = 403 if status == 400 + end + + Puppet.log_exception(exception) + + response.respond_with(status, "text/plain", exception.to_s) + end + + # Execute our find. + def do_find(indirection, key, params, request, response) + unless result = indirection.find(key, params) + raise Puppet::Network::HTTP::Handler::HTTPNotFoundError, "Could not find #{indirection.name} #{key}" + end + + format = accepted_response_formatter_for(indirection.model, request) + + rendered_result = result + if result.respond_to?(:render) + Puppet::Util::Profiler.profile("Rendered result in #{format}") do + rendered_result = result.render(format) + end + end + + Puppet::Util::Profiler.profile("Sent response") do + response.respond_with(200, format, rendered_result) + end + end + + # Execute our head. + def do_head(indirection, key, params, request, response) + unless indirection.head(key, params) + raise Puppet::Network::HTTP::Handler::HTTPNotFoundError, "Could not find #{indirection.name} #{key}" + end + + # No need to set a response because no response is expected from a + # HEAD request. All we need to do is not die. + end + + # Execute our search. + def do_search(indirection, key, params, request, response) + result = indirection.search(key, params) + + if result.nil? + raise Puppet::Network::HTTP::Handler::HTTPNotFoundError, "Could not find instances in #{indirection.name} with '#{key}'" + end + + format = accepted_response_formatter_for(indirection.model, request) + + response.respond_with(200, format, indirection.model.render_multiple(format, result)) + end + + # Execute our destroy. + def do_destroy(indirection, key, params, request, response) + formatter = accepted_response_formatter_or_yaml_for(indirection.model, request) + + result = indirection.destroy(key, params) + + response.respond_with(200, formatter, formatter.render(result)) + end + + # Execute our save. + def do_save(indirection, key, params, request, response) + formatter = accepted_response_formatter_or_yaml_for(indirection.model, request) + sent_object = read_body_into_model(indirection.model, request) + + result = indirection.save(sent_object, key) + + response.respond_with(200, formatter, formatter.render(result)) + end + + def accepted_response_formatter_for(model_class, request) + accepted_formats = request.headers['accept'] or raise Puppet::Network::HTTP::Handler::HTTPNotAcceptableError, "Missing required Accept header" + request.response_formatter_for(model_class.supported_formats, accepted_formats) + end + + def accepted_response_formatter_or_yaml_for(model_class, request) + accepted_formats = request.headers['accept'] || "yaml" + request.response_formatter_for(model_class.supported_formats, accepted_formats) + end + + def read_body_into_model(model_class, request) + data = request.body.to_s + + format = request.format + model_class.convert_from(format, data) end def indirection_method(http_method, indirection) @@ -60,7 +177,17 @@ module Puppet::Network::HTTP::API::V1 method end - def pluralize(indirection) + def self.indirection2uri(request) + indirection = request.method == :search ? pluralize(request.indirection_name.to_s) : request.indirection_name.to_s + "/#{request.environment.to_s}/#{indirection}/#{request.escaped_key}#{request.query_string}" + end + + def self.request_to_uri_and_body(request) + indirection = request.method == :search ? pluralize(request.indirection_name.to_s) : request.indirection_name.to_s + ["/#{request.environment.to_s}/#{indirection}/#{request.escaped_key}", request.query_string.sub(/^\?/,'')] + end + + def self.pluralize(indirection) return(indirection == "status" ? "statuses" : indirection + "s") end diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb index 23b6b7c7c..a594c761d 100644 --- a/lib/puppet/network/http/handler.rb +++ b/lib/puppet/network/http/handler.rb @@ -3,15 +3,12 @@ end require 'puppet/network/http' require 'puppet/network/http/api/v1' -require 'puppet/network/authorization' require 'puppet/network/authentication' require 'puppet/network/rights' require 'puppet/util/profiler' require 'resolv' module Puppet::Network::HTTP::Handler - include Puppet::Network::HTTP::API::V1 - include Puppet::Network::Authorization include Puppet::Network::Authentication # These shouldn't be allowed to be set by clients @@ -28,20 +25,23 @@ module Puppet::Network::HTTP::Handler end class HTTPNotAcceptableError < HTTPError + CODE = 406 def initialize(message) - super("Not Acceptable: " + message, 406) + super("Not Acceptable: " + message, CODE) end end class HTTPNotFoundError < HTTPError + CODE = 404 def initialize(message) - super("Not Found: " + message, 404) + super("Not Found: " + message, CODE) end end class HTTPNotAuthorizedError < HTTPError + CODE = 403 def initialize(message) - super("Not Authorized: " + message, 403) + super("Not Authorized: " + message, CODE) end end @@ -53,28 +53,6 @@ module Puppet::Network::HTTP::Handler raise NotImplementedError end - # Retrieve the accept header from the http request. - def accept_header(request) - raise NotImplementedError - end - - # Retrieve the Content-Type header from the http request. - def content_type_header(request) - raise NotImplementedError - end - - def request_format(request) - if header = content_type_header(request) - header.gsub!(/\s*;.*$/,'') # strip any charset - format = Puppet::Network::FormatHandler.mime(header) - raise "Client sent a mime-type (#{header}) that doesn't correspond to a format we support" if format.nil? - report_if_deprecated(format) - return format.name.to_s if format.suitable? - end - - raise "No Content-Type header was received, it isn't possible to unserialize the request" - end - def format_to_mime(format) format.is_a?(Puppet::Network::Format) ? format.mime : format end @@ -83,6 +61,75 @@ module Puppet::Network::HTTP::Handler @server = server end + Request = Struct.new(:headers, :params, :method, :path, :client_cert, :body) do + def self.from_hash(hash) + symbol_members = members.collect(&:intern) + unknown = hash.keys - symbol_members + if unknown.empty? + new(*(symbol_members.collect { |m| hash[m] })) + else + raise ArgumentError, "Unknown arguments: #{unknown.collect(&:inspect).join(', ')}" + end + end + + def format + if header = headers['content-type'] + header.gsub!(/\s*;.*$/,'') # strip any charset + format = Puppet::Network::FormatHandler.mime(header) + if format.nil? + raise "Client sent a mime-type (#{header}) that doesn't correspond to a format we support" + else + report_if_deprecated(format) + return format.name.to_s if format.suitable? + end + end + + raise "No Content-Type header was received, it isn't possible to unserialize the request" + end + + def response_formatter_for(supported_formats, accepted_formats = headers['accept']) + formatter = Puppet::Network::FormatHandler.most_suitable_format_for( + accepted_formats.split(/\s*,\s*/), + supported_formats) + + if formatter.nil? + raise HTTPNotAcceptableError, "No supported formats are acceptable (Accept: #{accepted_formats})" + end + + report_if_deprecated(formatter) + + formatter + 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") + end + end + end + + class Response + def initialize(handler, response) + @handler = handler + @response = response + end + + def respond_with(code, type, body) + @handler.set_content_type(@response, type) + @handler.set_response(@response, body, code) + end + end + + class MemoryResponse + attr_reader :code, :type, :body + + def respond_with(code, type, body) + @code = code + @type = type + @body = body + end + end + # handle an HTTP request def process(request, response) request_headers = headers(request) @@ -90,36 +137,26 @@ module Puppet::Network::HTTP::Handler request_method = http_method(request) request_path = path(request) + new_request = Request.new(request_headers, request_params, request_method, request_path, client_cert(request), body(request)) + new_response = Response.new(self, response) + response[Puppet::Network::HTTP::HEADER_PUPPET_VERSION] = Puppet.version configure_profiler(request_headers, request_params) + warn_if_near_expiration(new_request.client_cert) + if request_path == "/v2/environments" raise HTTPNotAuthorizedError, "You shall not pass!" + check_authorization(request_method, request_path, request_params) else Puppet::Util::Profiler.profile("Processed request #{request_method} #{request_path}") do - indirection_name, method, key, params = uri2indirection(request_method, request_path, request_params) - certificate = client_cert(request) - - check_authorization(indirection_name, method, key, params) - warn_if_near_expiration(certificate) - - indirection = Puppet::Indirector::Indirection.instance(indirection_name.to_sym) - raise ArgumentError, "Could not find indirection '#{indirection_name}'" unless indirection - - if !indirection.allow_remote_requests? - raise HTTPNotFoundError, "No handler for #{indirection.name}" - end - - trusted = Puppet::Context::TrustedInformation.remote(params[:authenticated], params[:node], certificate) - Puppet::Context.override(:trusted_information => trusted) do - send("do_#{method}", indirection, key, params, request, response) - end + Puppet::Network::HTTP::API::V1.new.process(new_request, new_response) end end rescue HTTPError => e - return do_http_control_exception(response, e) - rescue Exception => e - return do_exception(response, e) + msg = e.message + Puppet.info(msg) + new_response.respond_with(e.status, "text/plain", msg) ensure cleanup(request) end @@ -134,85 +171,6 @@ module Puppet::Network::HTTP::Handler raise NotImplementedError end - def do_exception(response, exception, status=400) - if exception.is_a?(Puppet::Network::AuthorizationError) - # make sure we return the correct status code - # for authorization issues - status = 403 if status == 400 - end - - Puppet.log_exception(exception) - - set_content_type(response, "text/plain") - set_response(response, exception.to_s, status) - end - - # Execute our find. - def do_find(indirection, key, params, request, response) - unless result = indirection.find(key, params) - raise HTTPNotFoundError, "Could not find #{indirection.name} #{key}" - end - - format = accepted_response_formatter_for(indirection.model, request) - set_content_type(response, format) - - rendered_result = result - if result.respond_to?(:render) - Puppet::Util::Profiler.profile("Rendered result in #{format}") do - rendered_result = result.render(format) - end - end - - Puppet::Util::Profiler.profile("Sent response") do - set_response(response, rendered_result) - end - end - - # Execute our head. - def do_head(indirection, key, params, request, response) - unless indirection.head(key, params) - raise HTTPNotFoundError, "Could not find #{indirection.name} #{key}" - end - - # No need to set a response because no response is expected from a - # HEAD request. All we need to do is not die. - end - - # Execute our search. - def do_search(indirection, key, params, request, response) - result = indirection.search(key, params) - - if result.nil? - raise HTTPNotFoundError, "Could not find instances in #{indirection.name} with '#{key}'" - end - - format = accepted_response_formatter_for(indirection.model, request) - set_content_type(response, format) - - set_response(response, indirection.model.render_multiple(format, result)) - end - - # Execute our destroy. - def do_destroy(indirection, key, params, request, response) - formatter = accepted_response_formatter_or_yaml_for(indirection.model, request) - - result = indirection.destroy(key, params) - - set_content_type(response, formatter) - set_response(response, formatter.render(result)) - end - - # Execute our save. - def do_save(indirection, key, params, request, response) - formatter = accepted_response_formatter_or_yaml_for(indirection.model, request) - sent_object = read_body_into_model(indirection.model, request) - - result = indirection.save(sent_object, key) - - set_content_type(response, formatter) - set_response(response, formatter.render(result)) - end - # resolve node name from peer's ip address # this is used when the request is unauthenticated def resolve_node(result) @@ -226,61 +184,6 @@ 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") - end - end - - def accepted_response_formatter_for(model_class, request) - accepted_formats = accept_header(request) or raise HTTPNotAcceptableError, "Missing required Accept header" - response_formatter_for(model_class, request, accepted_formats) - end - - def accepted_response_formatter_or_yaml_for(model_class, request) - accepted_formats = accept_header(request) || "yaml" - response_formatter_for(model_class, request, accepted_formats) - end - - def response_formatter_for(model_class, request, accepted_formats) - formatter = Puppet::Network::FormatHandler.most_suitable_format_for( - accepted_formats.split(/\s*,\s*/), - model_class.supported_formats) - - if formatter.nil? - raise HTTPNotAcceptableError, "No supported formats are acceptable (Accept: #{accepted_formats})" - end - - report_if_deprecated(formatter) - formatter - end - - def read_body_into_model(model_class, request) - data = body(request).to_s - - format = request_format(request) - model_class.convert_from(format, data) - end - - def get?(request) - http_method(request) == 'GET' - end - - def put?(request) - http_method(request) == 'PUT' - end - - def delete?(request) - http_method(request) == 'DELETE' - end - # methods to be overridden by the including web server class def http_method(request) diff --git a/lib/puppet/network/http/rack/rest.rb b/lib/puppet/network/http/rack/rest.rb index c3cf40eb4..fd2b71caa 100644 --- a/lib/puppet/network/http/rack/rest.rb +++ b/lib/puppet/network/http/rack/rest.rb @@ -8,7 +8,6 @@ class Puppet::Network::HTTP::RackREST < Puppet::Network::HTTP::RackHttpHandler include Puppet::Network::HTTP::Handler - HEADER_ACCEPT = 'HTTP_ACCEPT'.freeze ContentType = 'Content-Type'.freeze CHUNK_SIZE = 8192 @@ -51,20 +50,12 @@ class Puppet::Network::HTTP::RackREST < Puppet::Network::HTTP::RackHttpHandler # Retrieve all headers from the http request, as a map. def headers(request) - request.env.select {|k,v| k.start_with? 'HTTP_'}.inject({}) do |m, (k,v)| + headers = request.env.select {|k,v| k.start_with? 'HTTP_'}.inject({}) do |m, (k,v)| m[k.sub(/^HTTP_/, '').gsub('_','-').downcase] = v m end - end - - # Retrieve the accept header from the http request. - def accept_header(request) - request.env[HEADER_ACCEPT] - end - - # Retrieve the accept header from the http request. - def content_type_header(request) - request.content_type + headers['content-type'] = request.content_type + headers end # Return which HTTP verb was used in this request. diff --git a/lib/puppet/network/http/webrick/rest.rb b/lib/puppet/network/http/webrick/rest.rb index d25c388de..ccd5e1424 100644 --- a/lib/puppet/network/http/webrick/rest.rb +++ b/lib/puppet/network/http/webrick/rest.rb @@ -39,14 +39,6 @@ class Puppet::Network::HTTP::WEBrickREST < WEBrick::HTTPServlet::AbstractServlet result end - def accept_header(request) - request["accept"] - end - - def content_type_header(request) - request["content-type"] - end - def http_method(request) request.request_method end diff --git a/lib/puppet/type/file/content.rb b/lib/puppet/type/file/content.rb index 6a2eef676..2ea7fbe69 100644 --- a/lib/puppet/type/file/content.rb +++ b/lib/puppet/type/file/content.rb @@ -10,7 +10,6 @@ module Puppet Puppet::Type.type(:file).newproperty(:content) do include Puppet::Util::Diff include Puppet::Util::Checksums - include Puppet::Network::HTTP::API::V1 include Puppet::Network::HTTP::Compression.module attr_reader :actual_content @@ -211,7 +210,7 @@ module Puppet request.do_request(:fileserver) do |req| connection = Puppet::Network::HttpPool.http_instance(req.server, req.port) - connection.request_get(indirection2uri(req), add_accept_encoding({"Accept" => "raw"}), &block) + connection.request_get(Puppet::Network::HTTP::API::V1.indirection2uri(req), add_accept_encoding({"Accept" => "raw"}), &block) end end diff --git a/spec/lib/puppet/indirector/indirector_testing/memory.rb b/spec/lib/puppet/indirector/indirector_testing/memory.rb new file mode 100644 index 000000000..edfe2b6f7 --- /dev/null +++ b/spec/lib/puppet/indirector/indirector_testing/memory.rb @@ -0,0 +1,7 @@ +require 'puppet/indirector/memory' + +class Puppet::IndirectorTesting::Memory < Puppet::Indirector::Memory + def supports_remote_requests? + true + end +end diff --git a/spec/lib/puppet/indirector_testing.rb b/spec/lib/puppet/indirector_testing.rb index 883812158..e6b88ddcd 100644 --- a/spec/lib/puppet/indirector_testing.rb +++ b/spec/lib/puppet/indirector_testing.rb @@ -8,10 +8,16 @@ class Puppet::IndirectorTesting # We should have some way to identify if we got a valid object back with the # current values, no? attr_accessor :value + alias_method :name, :value + alias_method :name=, :value= def initialize(value) self.value = value end + def self.from_raw(raw) + new(raw) + end + PSON.register_document_type('IndirectorTesting',self) def self.from_pson(data) new(data['value']) diff --git a/spec/unit/indirector/rest_spec.rb b/spec/unit/indirector/rest_spec.rb index 0bf0bcb48..59558ab93 100755 --- a/spec/unit/indirector/rest_spec.rb +++ b/spec/unit/indirector/rest_spec.rb @@ -164,10 +164,6 @@ describe Puppet::Indirector::REST do Puppet::Indirector::Request.new(:test_model, :save, key, instance, options) end - it "should include the v1 REST API module" do - Puppet::Indirector::REST.ancestors.should be_include(Puppet::Network::HTTP::API::V1) - end - it "should have a method for specifying what setting a subclass should use to retrieve its server" do terminus_class.should respond_to(:use_server_setting) end diff --git a/spec/unit/network/http/api/v1_spec.rb b/spec/unit/network/http/api/v1_spec.rb index c5b39a082..06cc6fc56 100755 --- a/spec/unit/network/http/api/v1_spec.rb +++ b/spec/unit/network/http/api/v1_spec.rb @@ -1,216 +1,519 @@ #! /usr/bin/env ruby require 'spec_helper' +require 'puppet/network/http/handler' require 'puppet/network/http/api/v1' - -class V1RestApiTester - include Puppet::Network::HTTP::API::V1 -end +require 'puppet/indirector_testing' describe Puppet::Network::HTTP::API::V1 do + let(:not_found_code) { Puppet::Network::HTTP::Handler::HTTPNotFoundError::CODE } + let(:not_acceptable_code) { Puppet::Network::HTTP::Handler::HTTPNotAcceptableError::CODE } + let(:not_authorized_code) { Puppet::Network::HTTP::Handler::HTTPNotAuthorizedError::CODE } + + let(:indirection) { Puppet::IndirectorTesting.indirection } + let(:handler) { Puppet::Network::HTTP::API::V1.new } + let(:response) { Puppet::Network::HTTP::Handler::MemoryResponse.new } + + def a_request_that_heads(data, request = {}) + Puppet::Network::HTTP::Handler::Request.from_hash({ + :headers => { + 'accept' => request[:accept_header], + 'content-type' => "text/yaml", }, + :method => "HEAD", + :path => "/production/#{indirection.name}/#{data.value}", + :params => {}, + }) + end + + def a_request_that_submits(data, request = {}) + Puppet::Network::HTTP::Handler::Request.from_hash({ + :headers => { + 'accept' => request[:accept_header], + 'content-type' => request[:content_type_header] || "text/yaml", }, + :method => "PUT", + :path => "/production/#{indirection.name}/#{data.value}", + :params => {}, + :body => request[:body] || data.render("text/yaml") + }) + end + + def a_request_that_destroys(data, request = {}) + Puppet::Network::HTTP::Handler::Request.from_hash({ + :headers => { + 'accept' => request[:accept_header], + 'content-type' => "text/yaml", }, + :method => "DELETE", + :path => "/production/#{indirection.name}/#{data.value}", + :params => {}, + :body => '' + }) + end + + def a_request_that_finds(data, request = {}) + Puppet::Network::HTTP::Handler::Request.from_hash({ + :headers => { + 'accept' => request[:accept_header], + 'content-type' => "text/yaml", }, + :method => "GET", + :path => "/production/#{indirection.name}/#{data.value}", + :params => {}, + :body => '' + }) + end + + def a_request_that_searches(key, request = {}) + Puppet::Network::HTTP::Handler::Request.from_hash({ + :headers => { + 'accept' => request[:accept_header], + 'content-type' => "text/yaml", }, + :method => "GET", + :path => "/production/#{indirection.name}s/#{key}", + :params => {}, + :body => '' + }) + end + + before do - @tester = V1RestApiTester.new - end - - it "should be able to convert a URI into a request" do - @tester.should respond_to(:uri2indirection) - end - - it "should be able to convert a request into a URI" do - @tester.should respond_to(:indirection2uri) + Puppet::IndirectorTesting.indirection.terminus_class = :memory + Puppet::IndirectorTesting.indirection.terminus.clear + handler.stubs(:check_authorization) + handler.stubs(:warn_if_near_expiration) end describe "when converting a URI into a request" do before do - @tester.stubs(:handler).returns "foo" + handler.stubs(:handler).returns "foo" end it "should require the http method, the URI, and the query parameters" do # Not a terribly useful test, but an important statement for the spec - lambda { @tester.uri2indirection("/foo") }.should raise_error(ArgumentError) + lambda { handler.uri2indirection("/foo") }.should raise_error(ArgumentError) end it "should use the first field of the URI as the environment" do - @tester.uri2indirection("GET", "/env/foo/bar", {})[3][:environment].to_s.should == "env" + handler.uri2indirection("GET", "/env/foo/bar", {})[3][:environment].to_s.should == "env" end it "should fail if the environment is not alphanumeric" do - lambda { @tester.uri2indirection("GET", "/env ness/foo/bar", {}) }.should raise_error(ArgumentError) + lambda { handler.uri2indirection("GET", "/env ness/foo/bar", {}) }.should raise_error(ArgumentError) end it "should use the environment from the URI even if one is specified in the parameters" do - @tester.uri2indirection("GET", "/env/foo/bar", {:environment => "otherenv"})[3][:environment].to_s.should == "env" + handler.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" }) + handler.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" }) + handler.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 + handler.uri2indirection("GET", "/env/foo/bar", {})[3][:environment].should be_a Puppet::Node::Environment 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" }) + handler.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" }) + handler.uri2indirection("GET", "/env/foo/bar", { :allowed_param => "value" })[3].should include({ :allowed_param => "value" }) end it "should use the second field of the URI as the indirection name" do - @tester.uri2indirection("GET", "/env/foo/bar", {})[0].should == "foo" + handler.uri2indirection("GET", "/env/foo/bar", {})[0].should == "foo" end it "should fail if the indirection name is not alphanumeric" do - lambda { @tester.uri2indirection("GET", "/env/foo ness/bar", {}) }.should raise_error(ArgumentError) + lambda { handler.uri2indirection("GET", "/env/foo ness/bar", {}) }.should raise_error(ArgumentError) end it "should use the remainder of the URI as the indirection key" do - @tester.uri2indirection("GET", "/env/foo/bar", {})[2].should == "bar" + handler.uri2indirection("GET", "/env/foo/bar", {})[2].should == "bar" end it "should support the indirection key being a /-separated file path" do - @tester.uri2indirection("GET", "/env/foo/bee/baz/bomb", {})[2].should == "bee/baz/bomb" + handler.uri2indirection("GET", "/env/foo/bee/baz/bomb", {})[2].should == "bee/baz/bomb" end it "should fail if no indirection key is specified" do - lambda { @tester.uri2indirection("GET", "/env/foo/", {}) }.should raise_error(ArgumentError) - lambda { @tester.uri2indirection("GET", "/env/foo", {}) }.should raise_error(ArgumentError) + lambda { handler.uri2indirection("GET", "/env/foo/", {}) }.should raise_error(ArgumentError) + lambda { handler.uri2indirection("GET", "/env/foo", {}) }.should raise_error(ArgumentError) end it "should choose 'find' as the indirection method if the http method is a GET and the indirection name is singular" do - @tester.uri2indirection("GET", "/env/foo/bar", {})[1].should == :find + handler.uri2indirection("GET", "/env/foo/bar", {})[1].should == :find end it "should choose 'find' as the indirection method if the http method is a POST and the indirection name is singular" do - @tester.uri2indirection("POST", "/env/foo/bar", {})[1].should == :find + handler.uri2indirection("POST", "/env/foo/bar", {})[1].should == :find end it "should choose 'head' as the indirection method if the http method is a HEAD and the indirection name is singular" do - @tester.uri2indirection("HEAD", "/env/foo/bar", {})[1].should == :head + handler.uri2indirection("HEAD", "/env/foo/bar", {})[1].should == :head end it "should choose 'search' as the indirection method if the http method is a GET and the indirection name is plural" do - @tester.uri2indirection("GET", "/env/foos/bar", {})[1].should == :search + handler.uri2indirection("GET", "/env/foos/bar", {})[1].should == :search end it "should choose 'find' as the indirection method if the http method is a GET and the indirection name is facts" do - @tester.uri2indirection("GET", "/env/facts/bar", {})[1].should == :find + handler.uri2indirection("GET", "/env/facts/bar", {})[1].should == :find end it "should choose 'save' as the indirection method if the http method is a PUT and the indirection name is facts" do - @tester.uri2indirection("PUT", "/env/facts/bar", {})[1].should == :save + handler.uri2indirection("PUT", "/env/facts/bar", {})[1].should == :save end it "should choose 'search' as the indirection method if the http method is a GET and the indirection name is inventory" do - @tester.uri2indirection("GET", "/env/inventory/search", {})[1].should == :search + handler.uri2indirection("GET", "/env/inventory/search", {})[1].should == :search end it "should choose 'find' as the indirection method if the http method is a GET and the indirection name is facts" do - @tester.uri2indirection("GET", "/env/facts/bar", {})[1].should == :find + handler.uri2indirection("GET", "/env/facts/bar", {})[1].should == :find end it "should choose 'save' as the indirection method if the http method is a PUT and the indirection name is facts" do - @tester.uri2indirection("PUT", "/env/facts/bar", {})[1].should == :save + handler.uri2indirection("PUT", "/env/facts/bar", {})[1].should == :save end it "should choose 'search' as the indirection method if the http method is a GET and the indirection name is inventory" do - @tester.uri2indirection("GET", "/env/inventory/search", {})[1].should == :search + handler.uri2indirection("GET", "/env/inventory/search", {})[1].should == :search end it "should choose 'search' as the indirection method if the http method is a GET and the indirection name is facts_search" do - @tester.uri2indirection("GET", "/env/facts_search/bar", {})[1].should == :search + handler.uri2indirection("GET", "/env/facts_search/bar", {})[1].should == :search end it "should change indirection name to 'facts' if the http method is a GET and the indirection name is facts_search" do - @tester.uri2indirection("GET", "/env/facts_search/bar", {})[0].should == 'facts' + handler.uri2indirection("GET", "/env/facts_search/bar", {})[0].should == 'facts' end it "should not change indirection name from 'facts' if the http method is a GET and the indirection name is facts" do - @tester.uri2indirection("GET", "/env/facts/bar", {})[0].should == 'facts' + handler.uri2indirection("GET", "/env/facts/bar", {})[0].should == 'facts' end it "should change indirection name to 'status' if the http method is a GET and the indirection name is statuses" do - @tester.uri2indirection("GET", "/env/statuses/bar", {})[0].should == 'status' + handler.uri2indirection("GET", "/env/statuses/bar", {})[0].should == 'status' end it "should change indirection name to 'probe' if the http method is a GET and the indirection name is probes" do - @tester.uri2indirection("GET", "/env/probes/bar", {})[0].should == 'probe' + handler.uri2indirection("GET", "/env/probes/bar", {})[0].should == 'probe' end it "should choose 'delete' as the indirection method if the http method is a DELETE and the indirection name is singular" do - @tester.uri2indirection("DELETE", "/env/foo/bar", {})[1].should == :destroy + handler.uri2indirection("DELETE", "/env/foo/bar", {})[1].should == :destroy end it "should choose 'save' as the indirection method if the http method is a PUT and the indirection name is singular" do - @tester.uri2indirection("PUT", "/env/foo/bar", {})[1].should == :save + handler.uri2indirection("PUT", "/env/foo/bar", {})[1].should == :save end it "should fail if an indirection method cannot be picked" do - lambda { @tester.uri2indirection("UPDATE", "/env/foo/bar", {}) }.should raise_error(ArgumentError) + lambda { handler.uri2indirection("UPDATE", "/env/foo/bar", {}) }.should raise_error(ArgumentError) end it "should URI unescape the indirection key" do escaped = URI.escape("foo bar") - indirection_name, method, key, params = @tester.uri2indirection("GET", "/env/foo/#{escaped}", {}) + indirection_name, method, key, params = handler.uri2indirection("GET", "/env/foo/#{escaped}", {}) key.should == "foo bar" end end describe "when converting a request into a URI" do - before do - @request = Puppet::Indirector::Request.new(:foo, :find, "with spaces", nil, :foo => :bar, :environment => "myenv") - end + let(:request) { Puppet::Indirector::Request.new(:foo, :find, "with spaces", nil, :foo => :bar, :environment => "myenv") } it "should use the environment as the first field of the URI" do - @tester.indirection2uri(@request).split("/")[1].should == "myenv" + handler.class.indirection2uri(request).split("/")[1].should == "myenv" end it "should use the indirection as the second field of the URI" do - @tester.indirection2uri(@request).split("/")[2].should == "foo" + handler.class.indirection2uri(request).split("/")[2].should == "foo" end it "should pluralize the indirection name if the method is 'search'" do - @request.stubs(:method).returns :search - @tester.indirection2uri(@request).split("/")[2].should == "foos" + request.stubs(:method).returns :search + handler.class.indirection2uri(request).split("/")[2].should == "foos" end it "should use the escaped key as the remainder of the URI" do escaped = URI.escape("with spaces") - @tester.indirection2uri(@request).split("/")[3].sub(/\?.+/, '').should == escaped + handler.class.indirection2uri(request).split("/")[3].sub(/\?.+/, '').should == escaped end it "should add the query string to the URI" do - @request.expects(:query_string).returns "?query" - @tester.indirection2uri(@request).should =~ /\?query$/ + request.expects(:query_string).returns "?query" + handler.class.indirection2uri(request).should =~ /\?query$/ end end describe "when converting a request into a URI with body" do - before :each do - @request = Puppet::Indirector::Request.new(:foo, :find, "with spaces", nil, :foo => :bar, :environment => "myenv") - end + let(:request) { Puppet::Indirector::Request.new(:foo, :find, "with spaces", nil, :foo => :bar, :environment => "myenv") } it "should use the environment as the first field of the URI" do - @tester.request_to_uri_and_body(@request).first.split("/")[1].should == "myenv" + handler.class.request_to_uri_and_body(request).first.split("/")[1].should == "myenv" end it "should use the indirection as the second field of the URI" do - @tester.request_to_uri_and_body(@request).first.split("/")[2].should == "foo" + handler.class.request_to_uri_and_body(request).first.split("/")[2].should == "foo" end it "should use the escaped key as the remainder of the URI" do escaped = URI.escape("with spaces") - @tester.request_to_uri_and_body(@request).first.split("/")[3].sub(/\?.+/, '').should == escaped + handler.class.request_to_uri_and_body(request).first.split("/")[3].sub(/\?.+/, '').should == escaped end it "should return the URI and body separately" do - @tester.request_to_uri_and_body(@request).should == ["/myenv/foo/with%20spaces", "foo=bar"] + handler.class.request_to_uri_and_body(request).should == ["/myenv/foo/with%20spaces", "foo=bar"] + end + end + + describe "when processing a request" do + it "should return not_authorized_code if the request is not authorized" do + request = a_request_that_heads(Puppet::IndirectorTesting.new("my data")) + + handler.expects(:check_authorization).raises(Puppet::Network::AuthorizationError.new("forbidden")) + + handler.process(request, response) + + expect(response.code).to eq(not_authorized_code) + end + + it "should return an error code if the indirection does not support remote requests" do + request = a_request_that_heads(Puppet::IndirectorTesting.new("my data")) + + indirection.expects(:allow_remote_requests?).returns(false) + + handler.process(request, response) + + expect(response.code).to eq(not_found_code) + end + + it "should serialize a controller exception when an exception is thrown while finding the model instance" do + request = a_request_that_finds(Puppet::IndirectorTesting.new("key")) + handler.expects(:do_find).raises(ArgumentError, "The exception") + + handler.process(request, response) + + expect(response.code).to eq(400) + expect(response.body).to eq("The exception") + expect(response.type).to eq("text/plain") + end + end + + describe "when finding a model instance" do + it "uses the first supported format for the response" do + data = Puppet::IndirectorTesting.new("my data") + indirection.save(data, "my data") + request = a_request_that_finds(data, :accept_header => "unknown, pson, yaml") + + handler.process(request, response) + + expect(response.body).to eq(data.render(:pson)) + expect(response.type).to eq(Puppet::Network::FormatHandler.format(:pson)) + end + + it "responds with a not_acceptable_code error when no accept header is provided" do + data = Puppet::IndirectorTesting.new("my data") + indirection.save(data, "my data") + request = a_request_that_finds(data, :accept_header => nil) + + handler.process(request, response) + + expect(response.code).to eq(not_acceptable_code) + end + + it "raises an error when no accepted formats are known" do + data = Puppet::IndirectorTesting.new("my data") + indirection.save(data, "my data") + request = a_request_that_finds(data, :accept_header => "unknown, also/unknown") + + handler.process(request, response) + + expect(response.code).to eq(not_acceptable_code) + end + + it "should pass the result through without rendering it if the result is a string" do + data = Puppet::IndirectorTesting.new("my data") + data_string = "my data string" + request = a_request_that_finds(data, :accept_header => "pson") + indirection.expects(:find).returns(data_string) + + handler.process(request, response) + + expect(response.body).to eq(data_string) + expect(response.type).to eq(Puppet::Network::FormatHandler.format(:pson)) + end + + it "should return a not_found_code when no model instance can be found" do + data = Puppet::IndirectorTesting.new("my data") + request = a_request_that_finds(data, :accept_header => "unknown, pson, yaml") + + handler.process(request, response) + expect(response.code).to eq(not_found_code) + end + end + + describe "when searching for model instances" do + it "uses the first supported format for the response" do + data = Puppet::IndirectorTesting.new("my data") + indirection.save(data, "my data") + request = a_request_that_searches("my", :accept_header => "unknown, pson, yaml") + + handler.process(request, response) + + expect(response.type).to eq(Puppet::Network::FormatHandler.format(:pson)) + expect(response.body).to eq(Puppet::IndirectorTesting.render_multiple(:pson, [data])) + end + + it "should return [] when searching returns an empty array" do + request = a_request_that_searches("nothing", :accept_header => "unknown, pson, yaml") + + handler.process(request, response) + + expect(response.body).to eq("[]") + expect(response.type).to eq(Puppet::Network::FormatHandler.format(:pson)) + end + + it "should return a not_found_code when searching returns nil" do + request = a_request_that_searches("nothing", :accept_header => "unknown, pson, yaml") + indirection.expects(:search).returns(nil) + + handler.process(request, response) + + expect(response.code).to eq(not_found_code) + end + end + + describe "when destroying a model instance" do + it "destroys the data indicated in the request" do + data = Puppet::IndirectorTesting.new("my data") + indirection.save(data, "my data") + request = a_request_that_destroys(data) + + handler.process(request, response) + + Puppet::IndirectorTesting.indirection.find("my data").should be_nil + end + + it "responds with yaml when no Accept header is given" do + data = Puppet::IndirectorTesting.new("my data") + indirection.save(data, "my data") + request = a_request_that_destroys(data, :accept_header => nil) + + handler.process(request, response) + + expect(response.body).to eq(data.render(:yaml)) + expect(response.type).to eq(Puppet::Network::FormatHandler.format(:yaml)) + end + + it "uses the first supported format for the response" do + data = Puppet::IndirectorTesting.new("my data") + indirection.save(data, "my data") + request = a_request_that_destroys(data, :accept_header => "unknown, pson, yaml") + + handler.process(request, response) + + expect(response.body).to eq(data.render(:pson)) + expect(response.type).to eq(Puppet::Network::FormatHandler.format(:pson)) + end + + it "raises an error and does not destroy when no accepted formats are known" do + data = Puppet::IndirectorTesting.new("my data") + indirection.save(data, "my data") + request = a_request_that_submits(data, :accept_header => "unknown, also/unknown") + + handler.process(request, response) + + expect(response.code).to eq(not_acceptable_code) + Puppet::IndirectorTesting.indirection.find("my data").should_not be_nil + end + end + + describe "when saving a model instance" do + it "allows an empty body when the format supports it" do + class Puppet::IndirectorTesting::Nonvalidatingmemory < Puppet::IndirectorTesting::Memory + def validate_key(_) + # nothing + end + end + + indirection.terminus_class = :nonvalidatingmemory + + data = Puppet::IndirectorTesting.new("test") + request = a_request_that_submits(data, + :content_type_header => "application/x-raw", + :body => '') + + handler.process(request, response) + + Puppet::IndirectorTesting.indirection.find("test").name.should == '' + end + + it "saves the data sent in the request" do + data = Puppet::IndirectorTesting.new("my data") + request = a_request_that_submits(data) + + handler.process(request, response) + + saved = Puppet::IndirectorTesting.indirection.find("my data") + expect(saved.name).to eq(data.name) + end + + it "responds with yaml when no Accept header is given" do + data = Puppet::IndirectorTesting.new("my data") + request = a_request_that_submits(data, :accept_header => nil) + + handler.process(request, response) + + expect(response.body).to eq(data.render(:yaml)) + expect(response.type).to eq(Puppet::Network::FormatHandler.format(:yaml)) + end + + it "uses the first supported format for the response" do + data = Puppet::IndirectorTesting.new("my data") + request = a_request_that_submits(data, :accept_header => "unknown, pson, yaml") + + handler.process(request, response) + + expect(response.body).to eq(data.render(:pson)) + expect(response.type).to eq(Puppet::Network::FormatHandler.format(:pson)) + end + + it "raises an error and does not save when no accepted formats are known" do + data = Puppet::IndirectorTesting.new("my data") + request = a_request_that_submits(data, :accept_header => "unknown, also/unknown") + + handler.process(request, response) + + expect(Puppet::IndirectorTesting.indirection.find("my data")).to be_nil + expect(response.code).to eq(not_acceptable_code) + end + end + + describe "when performing head operation" do + it "should not generate a response when a model head call succeeds" do + data = Puppet::IndirectorTesting.new("my data") + indirection.save(data, "my data") + request = a_request_that_heads(data) + + handler.process(request, response) + + expect(response.code).to eq(nil) + end + + it "should return a not_found_code when the model head call returns false" do + data = Puppet::IndirectorTesting.new("my data") + request = a_request_that_heads(data) + + handler.process(request, response) + + expect(response.code).to eq(not_found_code) + expect(response.type).to eq("text/plain") + expect(response.body).to eq("Not Found: Could not find indirector_testing my data") end end end diff --git a/spec/unit/network/http/handler_spec.rb b/spec/unit/network/http/handler_spec.rb index 53da5be01..fc9140441 100755 --- a/spec/unit/network/http/handler_spec.rb +++ b/spec/unit/network/http/handler_spec.rb @@ -1,61 +1,18 @@ #! /usr/bin/env ruby require 'spec_helper' +require 'puppet/indirector_testing' + require 'puppet/network/http' require 'puppet/network/http/handler' require 'puppet/network/authorization' require 'puppet/network/authentication' -require 'puppet/indirector/memory' describe Puppet::Network::HTTP::Handler do before :each do - class Puppet::TestModel - extend Puppet::Indirector - indirects :test_model - attr_accessor :name, :data - def initialize(name = "name", data = '') - @name = name - @data = data - end - - def self.from_raw(raw) - new(nil, raw) - end - - def self.from_pson(pson) - new(pson["name"], pson["data"]) - end - - def to_pson - { - "name" => @name, - "data" => @data - }.to_pson - end - - def ==(other) - other.is_a? Puppet::TestModel and other.name == name and other.data == data - end - end - - class Puppet::TestModel::Memory < Puppet::Indirector::Memory - def supports_remote_requests? - true - end - end - - Puppet::TestModel.indirection.terminus_class = :memory + Puppet::IndirectorTesting.indirection.terminus_class = :memory end - after :each do - Puppet::TestModel.indirection.delete - # Remove the class, unlinking it from the rest of the system. - Puppet.send(:remove_const, :TestModel) - end - - let(:terminus_class) { Puppet::TestModel::Memory } - let(:terminus) { Puppet::TestModel.indirection.terminus(:memory) } - let(:indirection) { Puppet::TestModel.indirection } - let(:model) { Puppet::TestModel } + let(:indirection) { Puppet::IndirectorTesting.indirection } def a_request { @@ -70,76 +27,8 @@ describe Puppet::Network::HTTP::Handler do } end - def a_request_that_heads(data, request = {}) - { - :accept_header => request[:accept_header], - :content_type_header => "text/yaml", - :http_method => "HEAD", - :path => "/production/#{indirection.name}/#{data.name}", - :params => {}, - :client_cert => nil, - :body => nil - } - end - - def a_request_that_submits(data, request = {}) - { - :accept_header => request[:accept_header], - :content_type_header => "text/yaml", - :http_method => "PUT", - :path => "/production/#{indirection.name}/#{data.name}", - :params => {}, - :client_cert => nil, - :body => data.render("text/yaml") - } - end - - def a_request_that_destroys(data, request = {}) - { - :accept_header => request[:accept_header], - :content_type_header => "text/yaml", - :http_method => "DELETE", - :path => "/production/#{indirection.name}/#{data.name}", - :params => {}, - :client_cert => nil, - :body => '' - } - end - - def a_request_that_finds(data, request = {}) - { - :accept_header => request[:accept_header], - :content_type_header => "text/yaml", - :http_method => "GET", - :path => "/production/#{indirection.name}/#{data.name}", - :params => {}, - :client_cert => nil, - :body => '' - } - end - - def a_request_that_searches(key, request = {}) - { - :accept_header => request[:accept_header], - :content_type_header => "text/yaml", - :http_method => "GET", - :path => "/production/#{indirection.name}s/#{key}", - :params => {}, - :client_cert => nil, - :body => '' - } - end - let(:handler) { TestingHandler.new } - it "should include the v1 REST API" do - Puppet::Network::HTTP::Handler.ancestors.should be_include(Puppet::Network::HTTP::API::V1) - end - - it "should include the Rest Authorization system" do - Puppet::Network::HTTP::Handler.ancestors.should be_include(Puppet::Network::Authorization) - end - describe "when initializing" do it "should fail when no server type has been provided" do lambda { handler.initialize_for_puppet }.should raise_error(ArgumentError) @@ -188,91 +77,15 @@ describe Puppet::Network::HTTP::Handler do Puppet::Util::Profiler.current.should == Puppet::Util::Profiler::NONE end - it "should create an indirection request from the path, parameters, and http method" do - request = a_request - request[:path] = "mypath" - request[:http_method] = "mymethod" - request[:params] = { :params => "mine" } - - handler.expects(:uri2indirection).with("mymethod", "mypath", { :params => "mine" }).returns stub("request", :method => :find) - - handler.stubs(:do_find) - - handler.process(request, response) - end - - it "should return 403 if the request is not authorized" do - request = a_request - handler.expects(:uri2indirection).returns(["facts", :mymethod, "key", {:node => "name"}]) - - handler.expects(:do_mymethod).never - - handler.expects(:check_authorization).with("facts", :mymethod, "key", {:node => "name"}).raises(Puppet::Network::AuthorizationError.new("forbidden")) - - handler.expects(:set_response).with(anything, anything, 403) - - handler.process(request, response) - end - - it "should return an error code if the indirection does not support remote requests" do - request = a_request - - indirection.expects(:allow_remote_requests?).returns(false) - - handler.process(request, response) - - expect(response[:status]).to eq 404 - end - - it "should serialize a controller exception when an exception is thrown while finding the model instance" do - request = a_request_that_finds(Puppet::TestModel.new("key")) - - handler.expects(:do_find).raises(ArgumentError, "The exception") - handler.expects(:set_response).with(anything, "The exception", 400) - handler.process(request, response) - end - - 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, 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, Exception.new("A test"), 404) - end - - it "sends an exception error with the exception's status" do - data = Puppet::TestModel.new("not_found", "not found") - request = a_request_that_finds(data, :accept_header => "pson") - - error = Puppet::Network::HTTP::Handler::HTTPNotFoundError.new("Could not find test_model not_found") - handler.expects(:set_response).with(response, error.to_s, error.status) - - 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 end it "should still find the correct format if content type contains charset information" do - request = a_request - handler.stubs(:content_type_header).returns "text/plain; charset=UTF-8" - handler.request_format(request).should == "s" + request = Puppet::Network::HTTP::Handler::Request.new({ 'content-type' => "text/plain; charset=UTF-8" }, + {}, 'GET', '/', nil) + request.format.should == "s" end it "should deserialize YAML parameters" do @@ -290,221 +103,9 @@ describe Puppet::Network::HTTP::Handler do decoded_params[:my_param].should be_a(Hash) end - - describe "when finding a model instance" do - it "uses the first supported format for the response" do - data = Puppet::TestModel.new("my data", "some data") - indirection.save(data, "my data") - request = a_request_that_finds(data, :accept_header => "unknown, pson, yaml") - - handler.expects(:set_response).with(response, data.render(:pson)) - handler.expects(:set_content_type).with(response, Puppet::Network::FormatHandler.format(:pson)) - - handler.do_find(indirection, "my data", {}, request, response) - end - - it "responds with a 406 error when no accept header is provided" do - data = Puppet::TestModel.new("my data", "some data") - indirection.save(data, "my data") - request = a_request_that_finds(data, :accept_header => nil) - - expect do - handler.do_find(indirection, "my data", {}, request, response) - end.to raise_error(Puppet::Network::HTTP::Handler::HTTPNotAcceptableError) - end - - it "raises an error when no accepted formats are known" do - data = Puppet::TestModel.new("my data", "some data") - indirection.save(data, "my data") - request = a_request_that_finds(data, :accept_header => "unknown, also/unknown") - - expect do - handler.do_find(indirection, "my data", {}, request, response) - end.to raise_error(Puppet::Network::HTTP::Handler::HTTPNotAcceptableError) - end - - it "should pass the result through without rendering it if the result is a string" do - data = Puppet::TestModel.new("my data", "some data") - data_string = "my data string" - request = a_request_that_finds(data, :accept_header => "pson") - indirection.expects(:find).returns(data_string) - - handler.expects(:set_response).with(response, data_string) - handler.expects(:set_content_type).with(response, Puppet::Network::FormatHandler.format(:pson)) - - handler.do_find(indirection, "my data", {}, request, response) - end - - it "should return a 404 when no model instance can be found" do - data = Puppet::TestModel.new("my data", "some data") - request = a_request_that_finds(data, :accept_header => "unknown, pson, yaml") - - expect do - handler.do_find(indirection, "my data", {}, request, response) - end.to raise_error(Puppet::Network::HTTP::Handler::HTTPNotFoundError) - end - end - - describe "when performing head operation" do - it "should not generate a response when a model head call succeeds" do - data = Puppet::TestModel.new("my data", "some data") - indirection.save(data, "my data") - request = a_request_that_heads(data) - - handler.expects(:set_response).never - - handler.process(request, response) - end - - it "should return a 404 when the model head call returns false" do - data = Puppet::TestModel.new("my data", "data not there") - request = a_request_that_heads(data) - - handler.expects(:set_response).with(response, "Not Found: Could not find test_model my data", 404) - - handler.process(request, response) - end - end - - describe "when searching for model instances" do - it "uses the first supported format for the response" do - data = Puppet::TestModel.new("my data", "some data") - indirection.save(data, "my data") - request = a_request_that_searches("my", :accept_header => "unknown, pson, yaml") - - handler.expects(:set_response).with(response, Puppet::TestModel.render_multiple(:pson, [data])) - handler.expects(:set_content_type).with(response, Puppet::Network::FormatHandler.format(:pson)) - - handler.do_search(indirection, "my", {}, request, response) - end - - it "should return [] when searching returns an empty array" do - request = a_request_that_searches("nothing", :accept_header => "unknown, pson, yaml") - - handler.expects(:set_response).with(response, Puppet::TestModel.render_multiple(:pson, [])) - handler.expects(:set_content_type).with(response, Puppet::Network::FormatHandler.format(:pson)) - - handler.do_search(indirection, "nothing", {}, request, response) - end - - it "should return a 404 when searching returns nil" do - request = a_request_that_searches("nothing", :accept_header => "unknown, pson, yaml") - indirection.expects(:search).returns(nil) - - expect do - handler.do_search(indirection, "nothing", {}, request, response) - end.to raise_error(Puppet::Network::HTTP::Handler::HTTPNotFoundError) - end - end - - describe "when destroying a model instance" do - it "destroys the data indicated in the request" do - data = Puppet::TestModel.new("my data", "some data") - indirection.save(data, "my data") - request = a_request_that_destroys(data) - - handler.do_destroy(indirection, "my data", {}, request, response) - - Puppet::TestModel.indirection.find("my data").should be_nil - end - - it "responds with yaml when no Accept header is given" do - data = Puppet::TestModel.new("my data", "some data") - indirection.save(data, "my data") - request = a_request_that_destroys(data, :accept_header => nil) - - handler.expects(:set_response).with(response, data.render(:yaml)) - handler.expects(:set_content_type).with(response, Puppet::Network::FormatHandler.format(:yaml)) - - handler.do_destroy(indirection, "my data", {}, request, response) - end - - it "uses the first supported format for the response" do - data = Puppet::TestModel.new("my data", "some data") - indirection.save(data, "my data") - request = a_request_that_destroys(data, :accept_header => "unknown, pson, yaml") - - handler.expects(:set_response).with(response, data.render(:pson)) - handler.expects(:set_content_type).with(response, Puppet::Network::FormatHandler.format(:pson)) - - handler.do_destroy(indirection, "my data", {}, request, response) - end - - it "raises an error and does not destory when no accepted formats are known" do - data = Puppet::TestModel.new("my data", "some data") - indirection.save(data, "my data") - request = a_request_that_submits(data, :accept_header => "unknown, also/unknown") - - expect do - handler.do_destroy(indirection, "my data", {}, request, response) - end.to raise_error(Puppet::Network::HTTP::Handler::HTTPNotAcceptableError) - - Puppet::TestModel.indirection.find("my data").should_not be_nil - end - end - - describe "when saving a model instance" do - 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] = '' - - handler.do_save(indirection, "test", {}, request, response) - - Puppet::TestModel.indirection.find("test").data.should == '' - end - - it "saves the data sent in the request" do - data = Puppet::TestModel.new("my data", "some data") - request = a_request_that_submits(data) - - handler.do_save(indirection, "my data", {}, request, response) - - Puppet::TestModel.indirection.find("my data").should == data - end - - it "responds with yaml when no Accept header is given" do - data = Puppet::TestModel.new("my data", "some data") - request = a_request_that_submits(data, :accept_header => nil) - - handler.expects(:set_response).with(response, data.render(:yaml)) - handler.expects(:set_content_type).with(response, Puppet::Network::FormatHandler.format(:yaml)) - - handler.do_save(indirection, "my data", {}, request, response) - end - - it "uses the first supported format for the response" do - data = Puppet::TestModel.new("my data", "some data") - request = a_request_that_submits(data, :accept_header => "unknown, pson, yaml") - - handler.expects(:set_response).with(response, data.render(:pson)) - handler.expects(:set_content_type).with(response, Puppet::Network::FormatHandler.format(:pson)) - - handler.do_save(indirection, "my data", {}, request, response) - end - - it "raises an error and does not save when no accepted formats are known" do - data = Puppet::TestModel.new("my data", "some data") - request = a_request_that_submits(data, :accept_header => "unknown, also/unknown") - - expect do - handler.do_save(indirection, "my data", {}, request, response) - end.to raise_error(Puppet::Network::HTTP::Handler::HTTPNotAcceptableError) - - Puppet::TestModel.indirection.find("my data").should be_nil - end - end end + describe "when resolving node" do it "should use a look-up from the ip address" do Resolv.expects(:getname).with("1.2.3.4").returns("host.domain.com") diff --git a/spec/unit/network/http/rack/rest_spec.rb b/spec/unit/network/http/rack/rest_spec.rb index 82e49f153..988894b6a 100755 --- a/spec/unit/network/http/rack/rest_spec.rb +++ b/spec/unit/network/http/rack/rest_spec.rb @@ -48,19 +48,15 @@ describe "Puppet::Network::HTTP::RackREST", :if => Puppet.features.rack? do 'HTTP_X_Custom_Header' => 'mycustom', 'NOT_HTTP_foo' => 'not an http header'}) @handler.headers(req).should == {"accept" => 'myaccept', - "x-custom-header" => 'mycustom'} + "x-custom-header" => 'mycustom', + "content-type" => nil } end end describe "and using the HTTP Handler interface" do - it "should return the HTTP_ACCEPT parameter as the accept header" do - req = mk_req('/', 'HTTP_ACCEPT' => 'myaccept') - @handler.accept_header(req).should == "myaccept" - end - it "should return the CONTENT_TYPE parameter as the content type header" do req = mk_req('/', 'CONTENT_TYPE' => 'mycontent') - @handler.content_type_header(req).should == "mycontent" + @handler.headers(req)['content-type'].should == "mycontent" end it "should use the REQUEST_METHOD as the http method" do diff --git a/spec/unit/network/http/webrick/rest_spec.rb b/spec/unit/network/http/webrick/rest_spec.rb index 10ffe7a16..7b5e9387b 100755 --- a/spec/unit/network/http/webrick/rest_spec.rb +++ b/spec/unit/network/http/webrick/rest_spec.rb @@ -56,16 +56,6 @@ describe Puppet::Network::HTTP::WEBrickREST do end describe "when using the Handler interface" do - it "should use the 'accept' request parameter as the Accept header" do - @request.expects(:[]).with("accept").returns "foobar" - @handler.accept_header(@request).should == "foobar" - end - - it "should use the 'content-type' request header as the Content-Type header" do - @request.expects(:[]).with("content-type").returns "foobar" - @handler.content_type_header(@request).should == "foobar" - end - it "should use the request method as the http method" do @request.expects(:request_method).returns "FOO" @handler.http_method(@request).should == "FOO" From 3f0c113d6403c18394dc1d7e84c9676654da18e9 Mon Sep 17 00:00:00 2001 From: Dan Lidral-Porter Date: Tue, 7 Jan 2014 14:34:07 -0800 Subject: [PATCH 394/800] (maint) Get rid of http handler's initialize_for_puppet method. It doesn't seem to be used anywhere except in some tests that expect it to be called. Also rescue any exception when processing a request in the http handler, and respond with a 500(!). --- lib/puppet/network/http/handler.rb | 11 ++++++----- lib/puppet/network/http/rack/rest.rb | 1 - lib/puppet/network/http/webrick/rest.rb | 1 - spec/unit/network/http/handler_spec.rb | 11 ----------- spec/unit/network/http/rack/rest_spec.rb | 10 ++-------- spec/unit/network/http/webrick/rest_spec.rb | 17 ----------------- 6 files changed, 8 insertions(+), 43 deletions(-) diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb index a594c761d..4ca2db8f1 100644 --- a/lib/puppet/network/http/handler.rb +++ b/lib/puppet/network/http/handler.rb @@ -57,10 +57,6 @@ module Puppet::Network::HTTP::Handler format.is_a?(Puppet::Network::Format) ? format.mime : format end - def initialize_for_puppet(server) - @server = server - end - Request = Struct.new(:headers, :params, :method, :path, :client_cert, :body) do def self.from_hash(hash) symbol_members = members.collect(&:intern) @@ -132,13 +128,14 @@ module Puppet::Network::HTTP::Handler # handle an HTTP request def process(request, response) + new_response = Response.new(self, response) + request_headers = headers(request) request_params = params(request) request_method = http_method(request) request_path = path(request) new_request = Request.new(request_headers, request_params, request_method, request_path, client_cert(request), body(request)) - new_response = Response.new(self, response) response[Puppet::Network::HTTP::HEADER_PUPPET_VERSION] = Puppet.version @@ -157,6 +154,10 @@ module Puppet::Network::HTTP::Handler msg = e.message Puppet.info(msg) new_response.respond_with(e.status, "text/plain", msg) + rescue Exception => e + msg = e.message + Puppet.err(msg) + new_response.respond_with(500, "text/plain", msg) ensure cleanup(request) end diff --git a/lib/puppet/network/http/rack/rest.rb b/lib/puppet/network/http/rack/rest.rb index fd2b71caa..5f5578bc1 100644 --- a/lib/puppet/network/http/rack/rest.rb +++ b/lib/puppet/network/http/rack/rest.rb @@ -30,7 +30,6 @@ class Puppet::Network::HTTP::RackREST < Puppet::Network::HTTP::RackHttpHandler def initialize(args={}) super() - initialize_for_puppet(args) end def set_content_type(response, format) diff --git a/lib/puppet/network/http/webrick/rest.rb b/lib/puppet/network/http/webrick/rest.rb index ccd5e1424..a0dd74945 100644 --- a/lib/puppet/network/http/webrick/rest.rb +++ b/lib/puppet/network/http/webrick/rest.rb @@ -10,7 +10,6 @@ class Puppet::Network::HTTP::WEBrickREST < WEBrick::HTTPServlet::AbstractServlet def initialize(server, handler) raise ArgumentError, "server is required" unless server super(server) - initialize_for_puppet(:server => server, :handler => handler) end # Retrieve the request parameters, including authentication information. diff --git a/spec/unit/network/http/handler_spec.rb b/spec/unit/network/http/handler_spec.rb index fc9140441..8c1f71aad 100755 --- a/spec/unit/network/http/handler_spec.rb +++ b/spec/unit/network/http/handler_spec.rb @@ -29,17 +29,6 @@ describe Puppet::Network::HTTP::Handler do let(:handler) { TestingHandler.new } - describe "when initializing" do - it "should fail when no server type has been provided" do - lambda { handler.initialize_for_puppet }.should raise_error(ArgumentError) - end - - it "should set server type" do - handler.initialize_for_puppet("foo") - handler.server.should == "foo" - end - end - describe "when processing a request" do let(:response) do { :status => 200 } diff --git a/spec/unit/network/http/rack/rest_spec.rb b/spec/unit/network/http/rack/rest_spec.rb index 988894b6a..165b6ceb9 100755 --- a/spec/unit/network/http/rack/rest_spec.rb +++ b/spec/unit/network/http/rack/rest_spec.rb @@ -8,13 +8,6 @@ describe "Puppet::Network::HTTP::RackREST", :if => Puppet.features.rack? do Puppet::Network::HTTP::RackREST.ancestors.should be_include(Puppet::Network::HTTP::Handler) end - describe "when initializing" do - it "should call the Handler's initialization hook with its provided arguments" do - Puppet::Network::HTTP::RackREST.any_instance.expects(:initialize_for_puppet).with(:server => "my", :handler => "arguments") - Puppet::Network::HTTP::RackREST.new(:server => "my", :handler => "arguments") - end - end - describe "when serving a request" do before :all do @model_class = stub('indirected model class') @@ -132,7 +125,8 @@ describe "Puppet::Network::HTTP::RackREST", :if => Puppet.features.rack? do it "should ensure the body has been partially read on failure" do req = mk_req('/production/report/foo') req.body.expects(:read).with(1) - req.stubs(:check_authorization).raises(Exception) + + @handler.stubs(:headers).raises(Exception) @handler.process(req, @response) end diff --git a/spec/unit/network/http/webrick/rest_spec.rb b/spec/unit/network/http/webrick/rest_spec.rb index 7b5e9387b..40911de3d 100755 --- a/spec/unit/network/http/webrick/rest_spec.rb +++ b/spec/unit/network/http/webrick/rest_spec.rb @@ -9,23 +9,6 @@ describe Puppet::Network::HTTP::WEBrickREST do Puppet::Network::HTTP::WEBrickREST.ancestors.should be_include(Puppet::Network::HTTP::Handler) end - describe "when initializing" do - it "should call the Handler's initialization hook with its provided arguments as the server and handler" do - server = WEBrick::HTTPServer.new(:BindAddress => '127.0.0.1', - # Probablistically going to succeed - # even if we run more than one test - # instance at once. - :Port => 40000 + rand(10000), - # Just discard any log output, thanks. - :Logger => stub_everything('logger')) - - Puppet::Network::HTTP::WEBrickREST.any_instance. - expects(:initialize_for_puppet).with(:server => server, :handler => "arguments") - - Puppet::Network::HTTP::WEBrickREST.new(server, "arguments") - end - end - describe "when receiving a request" do before do @request = stub('webrick http request', :query => {}, :peeraddr => %w{eh boo host ip}, :client_cert => nil) From 5189d6e8febda932a28de77fb95de63c8ae2e8ec Mon Sep 17 00:00:00 2001 From: Dan Lidral-Porter Date: Tue, 7 Jan 2014 17:35:12 -0800 Subject: [PATCH 395/800] (PUP-1151) Add routing framework. Add a routing framework for the HTTP module that routes requests to either the V1 or V2 API as appropriate (first it sees if the V2 API would like to handle a request, and if not it's passed off to the V1 API). To support the routing framework, several new classes in the HTTP module are introduced: Request, Route, Response, and MemoryResponse (for testing). The V2 API is implemented in lib/puppet/network/http/api/v2.rb. Change the method to handle the request from process to call, which allows using lamdbas and procs as route handlers. Change the require path in the HTTP module to break a cycle, which necessitates changing requires in a few other files to just require the entire Puppet::Network::HTTP module (rather than just the V1 API class). Delete Puppet::Network::HTTP:RackHttpHandler because it was useless. Change the initialize method of Puppet::Network::HTTP::WEBrickRest to omit the unused handler argument. --- lib/puppet/network/http.rb | 9 ++ lib/puppet/network/http/api.rb | 2 - lib/puppet/network/http/api/v1.rb | 6 +- lib/puppet/network/http/api/v2.rb | 6 ++ lib/puppet/network/http/handler.rb | 94 +++------------- lib/puppet/network/http/memory_response.rb | 9 ++ lib/puppet/network/http/rack/httphandler.rb | 13 --- lib/puppet/network/http/rack/rest.rb | 5 +- lib/puppet/network/http/request.rb | 46 ++++++++ lib/puppet/network/http/response.rb | 11 ++ lib/puppet/network/http/route.rb | 41 +++++++ lib/puppet/network/http/webrick.rb | 2 +- lib/puppet/network/http/webrick/rest.rb | 3 +- lib/puppet/type/file/content.rb | 2 +- spec/unit/network/http/api/v1_spec.rb | 58 +++++----- spec/unit/network/http/handler_spec.rb | 114 +++++++++++++++++--- spec/unit/network/http/webrick/rest_spec.rb | 2 +- 17 files changed, 278 insertions(+), 145 deletions(-) create mode 100644 lib/puppet/network/http/api/v2.rb create mode 100644 lib/puppet/network/http/memory_response.rb delete mode 100644 lib/puppet/network/http/rack/httphandler.rb create mode 100644 lib/puppet/network/http/request.rb create mode 100644 lib/puppet/network/http/response.rb create mode 100644 lib/puppet/network/http/route.rb diff --git a/lib/puppet/network/http.rb b/lib/puppet/network/http.rb index 3519efd11..00547d30f 100644 --- a/lib/puppet/network/http.rb +++ b/lib/puppet/network/http.rb @@ -1,4 +1,13 @@ module Puppet::Network::HTTP HEADER_ENABLE_PROFILING = "X-Puppet-Profiling" HEADER_PUPPET_VERSION = "X-Puppet-Version" + + require 'puppet/network/http/api' + require 'puppet/network/http/api/v1' + require 'puppet/network/http/api/v2' + require 'puppet/network/http/handler' + require 'puppet/network/http/response' + require 'puppet/network/http/request' + require 'puppet/network/http/memory_response' + require 'puppet/network/http/route' end diff --git a/lib/puppet/network/http/api.rb b/lib/puppet/network/http/api.rb index 8b1b747ac..ba0b0b7af 100644 --- a/lib/puppet/network/http/api.rb +++ b/lib/puppet/network/http/api.rb @@ -1,4 +1,2 @@ -require 'puppet/network/http' - class Puppet::Network::HTTP::API end diff --git a/lib/puppet/network/http/api/v1.rb b/lib/puppet/network/http/api/v1.rb index fd8f0deca..fba8ea460 100644 --- a/lib/puppet/network/http/api/v1.rb +++ b/lib/puppet/network/http/api/v1.rb @@ -1,5 +1,4 @@ require 'puppet/network/authorization' -require 'puppet/network/http/api' class Puppet::Network::HTTP::API::V1 include Puppet::Network::Authorization @@ -25,9 +24,12 @@ class Puppet::Network::HTTP::API::V1 } } + def self.routes + [Puppet::Network::HTTP::Route.any(/.*/, new)] + end # handle an HTTP request - def process(request, response) + def call(request, response) indirection_name, method, key, params = uri2indirection(request.method, request.path, request.params) certificate = request.client_cert diff --git a/lib/puppet/network/http/api/v2.rb b/lib/puppet/network/http/api/v2.rb new file mode 100644 index 000000000..e2e7c8409 --- /dev/null +++ b/lib/puppet/network/http/api/v2.rb @@ -0,0 +1,6 @@ +class Puppet::Network::HTTP::API::V2 + def self.routes + [Puppet::Network::HTTP::Route.get( + %r{^/v2/environments$}, lambda { |req, res| raise Puppet::Network::HTTP::Handler::HTTPNotAuthorizedError, "You shall not pass!" })] + end +end diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb index 4ca2db8f1..c102e6e13 100644 --- a/lib/puppet/network/http/handler.rb +++ b/lib/puppet/network/http/handler.rb @@ -45,7 +45,14 @@ module Puppet::Network::HTTP::Handler end end - attr_reader :server, :handler + def register(routes) + @routes = routes + + Puppet.debug("Routes Registered:") + @routes.each do |route| + Puppet.debug(route.inspect) + end + end # Retrieve all headers from the http request, as a hash with the header names # (lower-cased) as the keys @@ -57,99 +64,30 @@ module Puppet::Network::HTTP::Handler format.is_a?(Puppet::Network::Format) ? format.mime : format end - Request = Struct.new(:headers, :params, :method, :path, :client_cert, :body) do - def self.from_hash(hash) - symbol_members = members.collect(&:intern) - unknown = hash.keys - symbol_members - if unknown.empty? - new(*(symbol_members.collect { |m| hash[m] })) - else - raise ArgumentError, "Unknown arguments: #{unknown.collect(&:inspect).join(', ')}" - end - end - - def format - if header = headers['content-type'] - header.gsub!(/\s*;.*$/,'') # strip any charset - format = Puppet::Network::FormatHandler.mime(header) - if format.nil? - raise "Client sent a mime-type (#{header}) that doesn't correspond to a format we support" - else - report_if_deprecated(format) - return format.name.to_s if format.suitable? - end - end - - raise "No Content-Type header was received, it isn't possible to unserialize the request" - end - - def response_formatter_for(supported_formats, accepted_formats = headers['accept']) - formatter = Puppet::Network::FormatHandler.most_suitable_format_for( - accepted_formats.split(/\s*,\s*/), - supported_formats) - - if formatter.nil? - raise HTTPNotAcceptableError, "No supported formats are acceptable (Accept: #{accepted_formats})" - end - - report_if_deprecated(formatter) - - formatter - 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") - end - end - end - - class Response - def initialize(handler, response) - @handler = handler - @response = response - end - - def respond_with(code, type, body) - @handler.set_content_type(@response, type) - @handler.set_response(@response, body, code) - end - end - - class MemoryResponse - attr_reader :code, :type, :body - - def respond_with(code, type, body) - @code = code - @type = type - @body = body - end - end - # handle an HTTP request def process(request, response) - new_response = Response.new(self, response) + new_response = Puppet::Network::HTTP::Response.new(self, response) request_headers = headers(request) request_params = params(request) request_method = http_method(request) request_path = path(request) - new_request = Request.new(request_headers, request_params, request_method, request_path, client_cert(request), body(request)) + new_request = Puppet::Network::HTTP::Request.new(request_headers, request_params, request_method, request_path, client_cert(request), body(request)) response[Puppet::Network::HTTP::HEADER_PUPPET_VERSION] = Puppet.version configure_profiler(request_headers, request_params) warn_if_near_expiration(new_request.client_cert) - if request_path == "/v2/environments" - raise HTTPNotAuthorizedError, "You shall not pass!" - check_authorization(request_method, request_path, request_params) - else - Puppet::Util::Profiler.profile("Processed request #{request_method} #{request_path}") do - Puppet::Network::HTTP::API::V1.new.process(new_request, new_response) + Puppet::Util::Profiler.profile("Processed request #{request_method} #{request_path}") do + if route = @routes.find { |route| route.matches?(new_request) } + route.process(new_request, new_response) + else + raise HTTPNotFoundError, "No route for #{new_request.method} #{new_request.path}" end end + rescue HTTPError => e msg = e.message Puppet.info(msg) diff --git a/lib/puppet/network/http/memory_response.rb b/lib/puppet/network/http/memory_response.rb new file mode 100644 index 000000000..af770f1c2 --- /dev/null +++ b/lib/puppet/network/http/memory_response.rb @@ -0,0 +1,9 @@ +class Puppet::Network::HTTP::MemoryResponse + attr_reader :code, :type, :body + + def respond_with(code, type, body) + @code = code + @type = type + @body = body + end +end diff --git a/lib/puppet/network/http/rack/httphandler.rb b/lib/puppet/network/http/rack/httphandler.rb deleted file mode 100644 index e4fa93aa5..000000000 --- a/lib/puppet/network/http/rack/httphandler.rb +++ /dev/null @@ -1,13 +0,0 @@ -require 'openssl' -require 'puppet/ssl/certificate' - -class Puppet::Network::HTTP::RackHttpHandler - - # do something useful with request (a Rack::Request) and use - # response to fill your Rack::Response - def process(request, response) - raise NotImplementedError, "Your RackHttpHandler subclass is supposed to override service(request)" - end - -end - diff --git a/lib/puppet/network/http/rack/rest.rb b/lib/puppet/network/http/rack/rest.rb index 5f5578bc1..6179895d6 100644 --- a/lib/puppet/network/http/rack/rest.rb +++ b/lib/puppet/network/http/rack/rest.rb @@ -1,11 +1,9 @@ require 'openssl' require 'cgi' require 'puppet/network/http/handler' -require 'puppet/network/http/rack/httphandler' require 'puppet/util/ssl' -class Puppet::Network::HTTP::RackREST < Puppet::Network::HTTP::RackHttpHandler - +class Puppet::Network::HTTP::RackREST include Puppet::Network::HTTP::Handler ContentType = 'Content-Type'.freeze @@ -30,6 +28,7 @@ class Puppet::Network::HTTP::RackREST < Puppet::Network::HTTP::RackHttpHandler def initialize(args={}) super() + register(Puppet::Network::HTTP::API::V2.routes + Puppet::Network::HTTP::API::V1.routes) end def set_content_type(response, format) diff --git a/lib/puppet/network/http/request.rb b/lib/puppet/network/http/request.rb new file mode 100644 index 000000000..b625fc075 --- /dev/null +++ b/lib/puppet/network/http/request.rb @@ -0,0 +1,46 @@ +Puppet::Network::HTTP::Request = Struct.new(:headers, :params, :method, :path, :client_cert, :body) do + def self.from_hash(hash) + symbol_members = members.collect(&:intern) + unknown = hash.keys - symbol_members + if unknown.empty? + new(*(symbol_members.collect { |m| hash[m] })) + else + raise ArgumentError, "Unknown arguments: #{unknown.collect(&:inspect).join(', ')}" + end + end + + def format + if header = headers['content-type'] + header.gsub!(/\s*;.*$/,'') # strip any charset + format = Puppet::Network::FormatHandler.mime(header) + if format.nil? + raise "Client sent a mime-type (#{header}) that doesn't correspond to a format we support" + else + report_if_deprecated(format) + return format.name.to_s if format.suitable? + end + end + + raise "No Content-Type header was received, it isn't possible to unserialize the request" + end + + def response_formatter_for(supported_formats, accepted_formats = headers['accept']) + formatter = Puppet::Network::FormatHandler.most_suitable_format_for( + accepted_formats.split(/\s*,\s*/), + supported_formats) + + if formatter.nil? + raise Puppet::Network::HTTP::Handler::HTTPNotAcceptableError, "No supported formats are acceptable (Accept: #{accepted_formats})" + end + + report_if_deprecated(formatter) + + formatter + 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") + end + end +end diff --git a/lib/puppet/network/http/response.rb b/lib/puppet/network/http/response.rb new file mode 100644 index 000000000..f73399d4b --- /dev/null +++ b/lib/puppet/network/http/response.rb @@ -0,0 +1,11 @@ +class Puppet::Network::HTTP::Response + def initialize(handler, response) + @handler = handler + @response = response + end + + def respond_with(code, type, body) + @handler.set_content_type(@response, type) + @handler.set_response(@response, body, code) + end +end diff --git a/lib/puppet/network/http/route.rb b/lib/puppet/network/http/route.rb new file mode 100644 index 000000000..3b0cbfb7d --- /dev/null +++ b/lib/puppet/network/http/route.rb @@ -0,0 +1,41 @@ +class Puppet::Network::HTTP::Route + def self.post(path_matcher, *handlers) + new("POST", lambda { |method| method == "POST" }, path_matcher, *handlers) + end + + def self.get(path_matcher, *handlers) + new("GET", lambda { |method| method == "GET" }, path_matcher, *handlers) + end + + def self.any(path_matcher, *handlers) + new("ANY", lambda { |method| true }, path_matcher, *handlers) + end + + def initialize(method_description, method_matcher, path_matcher, *handlers) + @method_description = method_description + @method_matcher = method_matcher + @handlers = handlers + @path_matcher = path_matcher + end + + def matches?(request) + Puppet.debug("Evaluating match for #{self.inspect}") + if @method_matcher.call(request.method) + if @path_matcher.match(request.path) + return true + else + Puppet.debug("Matched method (#{request.method.inspect}) but not path (#{request.path.inspect})") + end + else + Puppet.debug("Did not match method (#{request.method.inspect})") + end + end + + def process(request, response) + @handlers.each { |handler| handler.call(request, response) } + end + + def inspect + "Route #{@method_description} #{@path_matcher} to <#{@handlers.collect(&:inspect).join(', ')}>" + end +end diff --git a/lib/puppet/network/http/webrick.rb b/lib/puppet/network/http/webrick.rb index 869900dad..87642e3e6 100644 --- a/lib/puppet/network/http/webrick.rb +++ b/lib/puppet/network/http/webrick.rb @@ -22,7 +22,7 @@ class Puppet::Network::HTTP::WEBrick @server = WEBrick::HTTPServer.new(arguments) @server.listeners.each { |l| l.start_immediately = false } - @server.mount('/', Puppet::Network::HTTP::WEBrickREST, :this_value_is_apparently_necessary_but_unused) + @server.mount('/', Puppet::Network::HTTP::WEBrickREST) raise "WEBrick server is already listening" if @listening @listening = true diff --git a/lib/puppet/network/http/webrick/rest.rb b/lib/puppet/network/http/webrick/rest.rb index a0dd74945..625740506 100644 --- a/lib/puppet/network/http/webrick/rest.rb +++ b/lib/puppet/network/http/webrick/rest.rb @@ -7,8 +7,9 @@ class Puppet::Network::HTTP::WEBrickREST < WEBrick::HTTPServlet::AbstractServlet include Puppet::Network::HTTP::Handler - def initialize(server, handler) + def initialize(server) raise ArgumentError, "server is required" unless server + register(Puppet::Network::HTTP::API::V2.routes + Puppet::Network::HTTP::API::V1.routes) super(server) end diff --git a/lib/puppet/type/file/content.rb b/lib/puppet/type/file/content.rb index 2ea7fbe69..8a2d3c423 100644 --- a/lib/puppet/type/file/content.rb +++ b/lib/puppet/type/file/content.rb @@ -3,7 +3,7 @@ require 'uri' require 'tempfile' require 'puppet/util/checksums' -require 'puppet/network/http/api/v1' +require 'puppet/network/http' require 'puppet/network/http/compression' module Puppet diff --git a/spec/unit/network/http/api/v1_spec.rb b/spec/unit/network/http/api/v1_spec.rb index 06cc6fc56..ff31d043c 100755 --- a/spec/unit/network/http/api/v1_spec.rb +++ b/spec/unit/network/http/api/v1_spec.rb @@ -1,7 +1,7 @@ #! /usr/bin/env ruby require 'spec_helper' -require 'puppet/network/http/handler' +require 'puppet/network/http' require 'puppet/network/http/api/v1' require 'puppet/indirector_testing' @@ -12,10 +12,10 @@ describe Puppet::Network::HTTP::API::V1 do let(:indirection) { Puppet::IndirectorTesting.indirection } let(:handler) { Puppet::Network::HTTP::API::V1.new } - let(:response) { Puppet::Network::HTTP::Handler::MemoryResponse.new } + let(:response) { Puppet::Network::HTTP::MemoryResponse.new } def a_request_that_heads(data, request = {}) - Puppet::Network::HTTP::Handler::Request.from_hash({ + Puppet::Network::HTTP::Request.from_hash({ :headers => { 'accept' => request[:accept_header], 'content-type' => "text/yaml", }, @@ -26,7 +26,7 @@ describe Puppet::Network::HTTP::API::V1 do end def a_request_that_submits(data, request = {}) - Puppet::Network::HTTP::Handler::Request.from_hash({ + Puppet::Network::HTTP::Request.from_hash({ :headers => { 'accept' => request[:accept_header], 'content-type' => request[:content_type_header] || "text/yaml", }, @@ -38,7 +38,7 @@ describe Puppet::Network::HTTP::API::V1 do end def a_request_that_destroys(data, request = {}) - Puppet::Network::HTTP::Handler::Request.from_hash({ + Puppet::Network::HTTP::Request.from_hash({ :headers => { 'accept' => request[:accept_header], 'content-type' => "text/yaml", }, @@ -50,7 +50,7 @@ describe Puppet::Network::HTTP::API::V1 do end def a_request_that_finds(data, request = {}) - Puppet::Network::HTTP::Handler::Request.from_hash({ + Puppet::Network::HTTP::Request.from_hash({ :headers => { 'accept' => request[:accept_header], 'content-type' => "text/yaml", }, @@ -62,7 +62,7 @@ describe Puppet::Network::HTTP::API::V1 do end def a_request_that_searches(key, request = {}) - Puppet::Network::HTTP::Handler::Request.from_hash({ + Puppet::Network::HTTP::Request.from_hash({ :headers => { 'accept' => request[:accept_header], 'content-type' => "text/yaml", }, @@ -277,7 +277,7 @@ describe Puppet::Network::HTTP::API::V1 do handler.expects(:check_authorization).raises(Puppet::Network::AuthorizationError.new("forbidden")) - handler.process(request, response) + handler.call(request, response) expect(response.code).to eq(not_authorized_code) end @@ -287,7 +287,7 @@ describe Puppet::Network::HTTP::API::V1 do indirection.expects(:allow_remote_requests?).returns(false) - handler.process(request, response) + handler.call(request, response) expect(response.code).to eq(not_found_code) end @@ -296,7 +296,7 @@ describe Puppet::Network::HTTP::API::V1 do request = a_request_that_finds(Puppet::IndirectorTesting.new("key")) handler.expects(:do_find).raises(ArgumentError, "The exception") - handler.process(request, response) + handler.call(request, response) expect(response.code).to eq(400) expect(response.body).to eq("The exception") @@ -310,7 +310,7 @@ describe Puppet::Network::HTTP::API::V1 do indirection.save(data, "my data") request = a_request_that_finds(data, :accept_header => "unknown, pson, yaml") - handler.process(request, response) + handler.call(request, response) expect(response.body).to eq(data.render(:pson)) expect(response.type).to eq(Puppet::Network::FormatHandler.format(:pson)) @@ -321,7 +321,7 @@ describe Puppet::Network::HTTP::API::V1 do indirection.save(data, "my data") request = a_request_that_finds(data, :accept_header => nil) - handler.process(request, response) + handler.call(request, response) expect(response.code).to eq(not_acceptable_code) end @@ -331,7 +331,7 @@ describe Puppet::Network::HTTP::API::V1 do indirection.save(data, "my data") request = a_request_that_finds(data, :accept_header => "unknown, also/unknown") - handler.process(request, response) + handler.call(request, response) expect(response.code).to eq(not_acceptable_code) end @@ -342,7 +342,7 @@ describe Puppet::Network::HTTP::API::V1 do request = a_request_that_finds(data, :accept_header => "pson") indirection.expects(:find).returns(data_string) - handler.process(request, response) + handler.call(request, response) expect(response.body).to eq(data_string) expect(response.type).to eq(Puppet::Network::FormatHandler.format(:pson)) @@ -352,7 +352,7 @@ describe Puppet::Network::HTTP::API::V1 do data = Puppet::IndirectorTesting.new("my data") request = a_request_that_finds(data, :accept_header => "unknown, pson, yaml") - handler.process(request, response) + handler.call(request, response) expect(response.code).to eq(not_found_code) end end @@ -363,7 +363,7 @@ describe Puppet::Network::HTTP::API::V1 do indirection.save(data, "my data") request = a_request_that_searches("my", :accept_header => "unknown, pson, yaml") - handler.process(request, response) + handler.call(request, response) expect(response.type).to eq(Puppet::Network::FormatHandler.format(:pson)) expect(response.body).to eq(Puppet::IndirectorTesting.render_multiple(:pson, [data])) @@ -372,7 +372,7 @@ describe Puppet::Network::HTTP::API::V1 do it "should return [] when searching returns an empty array" do request = a_request_that_searches("nothing", :accept_header => "unknown, pson, yaml") - handler.process(request, response) + handler.call(request, response) expect(response.body).to eq("[]") expect(response.type).to eq(Puppet::Network::FormatHandler.format(:pson)) @@ -382,7 +382,7 @@ describe Puppet::Network::HTTP::API::V1 do request = a_request_that_searches("nothing", :accept_header => "unknown, pson, yaml") indirection.expects(:search).returns(nil) - handler.process(request, response) + handler.call(request, response) expect(response.code).to eq(not_found_code) end @@ -394,7 +394,7 @@ describe Puppet::Network::HTTP::API::V1 do indirection.save(data, "my data") request = a_request_that_destroys(data) - handler.process(request, response) + handler.call(request, response) Puppet::IndirectorTesting.indirection.find("my data").should be_nil end @@ -404,7 +404,7 @@ describe Puppet::Network::HTTP::API::V1 do indirection.save(data, "my data") request = a_request_that_destroys(data, :accept_header => nil) - handler.process(request, response) + handler.call(request, response) expect(response.body).to eq(data.render(:yaml)) expect(response.type).to eq(Puppet::Network::FormatHandler.format(:yaml)) @@ -415,7 +415,7 @@ describe Puppet::Network::HTTP::API::V1 do indirection.save(data, "my data") request = a_request_that_destroys(data, :accept_header => "unknown, pson, yaml") - handler.process(request, response) + handler.call(request, response) expect(response.body).to eq(data.render(:pson)) expect(response.type).to eq(Puppet::Network::FormatHandler.format(:pson)) @@ -426,7 +426,7 @@ describe Puppet::Network::HTTP::API::V1 do indirection.save(data, "my data") request = a_request_that_submits(data, :accept_header => "unknown, also/unknown") - handler.process(request, response) + handler.call(request, response) expect(response.code).to eq(not_acceptable_code) Puppet::IndirectorTesting.indirection.find("my data").should_not be_nil @@ -448,7 +448,7 @@ describe Puppet::Network::HTTP::API::V1 do :content_type_header => "application/x-raw", :body => '') - handler.process(request, response) + handler.call(request, response) Puppet::IndirectorTesting.indirection.find("test").name.should == '' end @@ -457,7 +457,7 @@ describe Puppet::Network::HTTP::API::V1 do data = Puppet::IndirectorTesting.new("my data") request = a_request_that_submits(data) - handler.process(request, response) + handler.call(request, response) saved = Puppet::IndirectorTesting.indirection.find("my data") expect(saved.name).to eq(data.name) @@ -467,7 +467,7 @@ describe Puppet::Network::HTTP::API::V1 do data = Puppet::IndirectorTesting.new("my data") request = a_request_that_submits(data, :accept_header => nil) - handler.process(request, response) + handler.call(request, response) expect(response.body).to eq(data.render(:yaml)) expect(response.type).to eq(Puppet::Network::FormatHandler.format(:yaml)) @@ -477,7 +477,7 @@ describe Puppet::Network::HTTP::API::V1 do data = Puppet::IndirectorTesting.new("my data") request = a_request_that_submits(data, :accept_header => "unknown, pson, yaml") - handler.process(request, response) + handler.call(request, response) expect(response.body).to eq(data.render(:pson)) expect(response.type).to eq(Puppet::Network::FormatHandler.format(:pson)) @@ -487,7 +487,7 @@ describe Puppet::Network::HTTP::API::V1 do data = Puppet::IndirectorTesting.new("my data") request = a_request_that_submits(data, :accept_header => "unknown, also/unknown") - handler.process(request, response) + handler.call(request, response) expect(Puppet::IndirectorTesting.indirection.find("my data")).to be_nil expect(response.code).to eq(not_acceptable_code) @@ -500,7 +500,7 @@ describe Puppet::Network::HTTP::API::V1 do indirection.save(data, "my data") request = a_request_that_heads(data) - handler.process(request, response) + handler.call(request, response) expect(response.code).to eq(nil) end @@ -509,7 +509,7 @@ describe Puppet::Network::HTTP::API::V1 do data = Puppet::IndirectorTesting.new("my data") request = a_request_that_heads(data) - handler.process(request, response) + handler.call(request, response) expect(response.code).to eq(not_found_code) expect(response.type).to eq("text/plain") diff --git a/spec/unit/network/http/handler_spec.rb b/spec/unit/network/http/handler_spec.rb index 8c1f71aad..e03f7075a 100755 --- a/spec/unit/network/http/handler_spec.rb +++ b/spec/unit/network/http/handler_spec.rb @@ -2,11 +2,11 @@ require 'spec_helper' require 'puppet/indirector_testing' -require 'puppet/network/http' -require 'puppet/network/http/handler' require 'puppet/network/authorization' require 'puppet/network/authentication' +require 'puppet/network/http' + describe Puppet::Network::HTTP::Handler do before :each do Puppet::IndirectorTesting.indirection.terminus_class = :memory @@ -14,12 +14,12 @@ describe Puppet::Network::HTTP::Handler do let(:indirection) { Puppet::IndirectorTesting.indirection } - def a_request + def a_request(method = "HEAD", path = "/production/#{indirection.name}/unknown") { :accept_header => "pson", :content_type_header => "text/yaml", - :http_method => "HEAD", - :path => "/production/#{indirection.name}/unknown", + :http_method => method, + :path => path, :params => {}, :client_cert => nil, :headers => {}, @@ -27,7 +27,97 @@ describe Puppet::Network::HTTP::Handler do } end - let(:handler) { TestingHandler.new } + let(:handler) { TestingHandler.new(nil) } + + describe "when creating a handler" do + def respond(text) + lambda { |req, res| res.respond_with(200, "text/plain", text) } + end + + it "hands the request to the first handler that matches the request path" do + handler = TestingHandler.new( + Puppet::Network::HTTP::Route.get(%r{^/foo}, respond("skipped")), + Puppet::Network::HTTP::Route.get(%r{^/vtest}, respond("used")), + Puppet::Network::HTTP::Route.get(%r{^/vtest/foo}, respond("never consulted"))) + + req = a_request("GET", "/vtest/foo") + res = {} + + handler.process(req, res) + + expect(res[:body]).to eq("used") + end + + it "does not hand requests to routes that specify a different HTTP method than the request" do + handler = TestingHandler.new( + Puppet::Network::HTTP::Route.post(%r{^/vtest}, respond("skipped")), + Puppet::Network::HTTP::Route.get(%r{^/vtest}, respond("used"))) + + req = a_request("GET", "/vtest/foo") + res = {} + + handler.process(req, res) + + expect(res[:body]).to eq("used") + end + + it "allows routes to match any HTTP method" do + handler = TestingHandler.new( + Puppet::Network::HTTP::Route.post(%r{^/vtest/foo}, respond("skipped")), + Puppet::Network::HTTP::Route.any(%r{^/vtest/foo}, respond("used")), + Puppet::Network::HTTP::Route.get(%r{^/vtest/foo}, respond("ignored"))) + + req = a_request("GET", "/vtest/foo") + res = {} + + handler.process(req, res) + + expect(res[:body]).to eq("used") + end + + it "raises an HTTP not found error if no routes match" do + handler = TestingHandler.new + + req = a_request("GET", "/vtest/foo") + res = {} + + handler.process(req, res) + + expect(res[:body]).to eq("Not Found: No route for GET /vtest/foo") + expect(res[:status]).to eq(404) + end + + it "calls each route's handlers in turn" do + call_count = 0 + route_handler = lambda { |request, response| call_count += 1 } + handler = TestingHandler.new( + Puppet::Network::HTTP::Route.get(%r{^/vtest/foo}, route_handler, route_handler)) + + req = a_request("GET", "/vtest/foo") + res = {} + + handler.process(req, res) + + expect(call_count).to eq(2) + end + + it "stops calling handlers if one of them raises an error" do + ignored_called = false + ignored = lambda { |req, res| ignored_called = true } + raise_error = lambda { |req, res| raise Puppet::Network::HTTP::Handler::HTTPNotAuthorizedError, "go away" } + + handler = TestingHandler.new( + Puppet::Network::HTTP::Route.get(%r{^/vtest/foo}, raise_error, ignored)) + + req = a_request("GET", "/vtest/foo") + res = {} + + handler.process(req, res) + + expect(res[:status]).to eq(403) + expect(ignored_called).to be_false + end + end describe "when processing a request" do let(:response) do @@ -72,8 +162,8 @@ describe Puppet::Network::HTTP::Handler do end it "should still find the correct format if content type contains charset information" do - request = Puppet::Network::HTTP::Handler::Request.new({ 'content-type' => "text/plain; charset=UTF-8" }, - {}, 'GET', '/', nil) + request = Puppet::Network::HTTP::Request.new({ 'content-type' => "text/plain; charset=UTF-8" }, + {}, 'GET', '/', nil) request.format.should == "s" end @@ -118,12 +208,8 @@ describe Puppet::Network::HTTP::Handler do class TestingHandler include Puppet::Network::HTTP::Handler - def accept_header(request) - request[:accept_header] - end - - def content_type_header(request) - request[:content_type_header] + def initialize(* routes) + register(routes) end def set_content_type(response, format) diff --git a/spec/unit/network/http/webrick/rest_spec.rb b/spec/unit/network/http/webrick/rest_spec.rb index 40911de3d..c2e6d2a5f 100755 --- a/spec/unit/network/http/webrick/rest_spec.rb +++ b/spec/unit/network/http/webrick/rest_spec.rb @@ -16,7 +16,7 @@ describe Puppet::Network::HTTP::WEBrickREST do @model_class = stub('indirected model class') @webrick = stub('webrick http server', :mount => true, :[] => {}) Puppet::Indirector::Indirection.stubs(:model).with(:foo).returns(@model_class) - @handler = Puppet::Network::HTTP::WEBrickREST.new(@webrick, :foo) + @handler = Puppet::Network::HTTP::WEBrickREST.new(@webrick) end it "should delegate its :service method to its :process method" do From 7ac514e5f62374f53c853805d4870502050b0971 Mon Sep 17 00:00:00 2001 From: Dan Lidral-Porter Date: Thu, 9 Jan 2014 09:29:36 -0800 Subject: [PATCH 396/800] (PUP-1151) Add a 405 error class to http handler Now that we can actually dispatch based on path and HTTP verb, we can reasonably respond with 405 for verbs that aren't supported. --- lib/puppet/network/http/handler.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb index c102e6e13..64f748eef 100644 --- a/lib/puppet/network/http/handler.rb +++ b/lib/puppet/network/http/handler.rb @@ -45,6 +45,13 @@ module Puppet::Network::HTTP::Handler end end + class HTTPMethodNotAllowedError < HTTPError + CODE = 405 + def initialize(message) + super("Method Not Allowed: " + message, CODE) + end + end + def register(routes) @routes = routes From bf002ce0b6188e468f60182d0a35f2bc06ec8032 Mon Sep 17 00:00:00 2001 From: Dan Lidral-Porter Date: Thu, 9 Jan 2014 10:02:43 -0800 Subject: [PATCH 397/800] (PUP-1151) Change Route matching semantic specs. Rather than Routes having to match on path and method, they match only on path and can then decide whether the method is allowed or raise a method not allowed error. Most of the handler routing spec tests are really route spec tests, so those have been moved over. --- spec/unit/network/http/handler_spec.rb | 74 ++++------------------- spec/unit/network/http/route_spec.rb | 83 ++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 62 deletions(-) create mode 100644 spec/unit/network/http/route_spec.rb diff --git a/spec/unit/network/http/handler_spec.rb b/spec/unit/network/http/handler_spec.rb index e03f7075a..31fc46e11 100755 --- a/spec/unit/network/http/handler_spec.rb +++ b/spec/unit/network/http/handler_spec.rb @@ -27,18 +27,18 @@ describe Puppet::Network::HTTP::Handler do } end - let(:handler) { TestingHandler.new(nil) } + let(:handler) { TestingHandler.new() } - describe "when creating a handler" do + describe "the HTTP Handler" do def respond(text) lambda { |req, res| res.respond_with(200, "text/plain", text) } end - it "hands the request to the first handler that matches the request path" do + it "hands the request to the first route that matches the request path" do handler = TestingHandler.new( - Puppet::Network::HTTP::Route.get(%r{^/foo}, respond("skipped")), - Puppet::Network::HTTP::Route.get(%r{^/vtest}, respond("used")), - Puppet::Network::HTTP::Route.get(%r{^/vtest/foo}, respond("never consulted"))) + Puppet::Network::HTTP::Route.path(%r{^/foo}).get(respond("skipped")), + Puppet::Network::HTTP::Route.path(%r{^/vtest}).get(respond("used")), + Puppet::Network::HTTP::Route.path(%r{^/vtest/foo}).get(respond("ignored"))) req = a_request("GET", "/vtest/foo") res = {} @@ -48,31 +48,12 @@ describe Puppet::Network::HTTP::Handler do expect(res[:body]).to eq("used") end - it "does not hand requests to routes that specify a different HTTP method than the request" do - handler = TestingHandler.new( - Puppet::Network::HTTP::Route.post(%r{^/vtest}, respond("skipped")), - Puppet::Network::HTTP::Route.get(%r{^/vtest}, respond("used"))) - - req = a_request("GET", "/vtest/foo") - res = {} - - handler.process(req, res) - - expect(res[:body]).to eq("used") - end - - it "allows routes to match any HTTP method" do - handler = TestingHandler.new( - Puppet::Network::HTTP::Route.post(%r{^/vtest/foo}, respond("skipped")), - Puppet::Network::HTTP::Route.any(%r{^/vtest/foo}, respond("used")), - Puppet::Network::HTTP::Route.get(%r{^/vtest/foo}, respond("ignored"))) - - req = a_request("GET", "/vtest/foo") - res = {} - - handler.process(req, res) - - expect(res[:body]).to eq("used") + it "raises an error if multiple routes with the same path regex are registered" do + expect do + handler = TestingHandler.new( + Puppet::Network::HTTP::Route.path(%r{^/foo}).get(respond("ignored")), + Puppet::Network::HTTP::Route.path(%r{^/foo}).post(respond("also ignored"))) + end.to raise_error(ArgumentError) end it "raises an HTTP not found error if no routes match" do @@ -86,37 +67,6 @@ describe Puppet::Network::HTTP::Handler do expect(res[:body]).to eq("Not Found: No route for GET /vtest/foo") expect(res[:status]).to eq(404) end - - it "calls each route's handlers in turn" do - call_count = 0 - route_handler = lambda { |request, response| call_count += 1 } - handler = TestingHandler.new( - Puppet::Network::HTTP::Route.get(%r{^/vtest/foo}, route_handler, route_handler)) - - req = a_request("GET", "/vtest/foo") - res = {} - - handler.process(req, res) - - expect(call_count).to eq(2) - end - - it "stops calling handlers if one of them raises an error" do - ignored_called = false - ignored = lambda { |req, res| ignored_called = true } - raise_error = lambda { |req, res| raise Puppet::Network::HTTP::Handler::HTTPNotAuthorizedError, "go away" } - - handler = TestingHandler.new( - Puppet::Network::HTTP::Route.get(%r{^/vtest/foo}, raise_error, ignored)) - - req = a_request("GET", "/vtest/foo") - res = {} - - handler.process(req, res) - - expect(res[:status]).to eq(403) - expect(ignored_called).to be_false - end end describe "when processing a request" do diff --git a/spec/unit/network/http/route_spec.rb b/spec/unit/network/http/route_spec.rb new file mode 100644 index 000000000..a47914d69 --- /dev/null +++ b/spec/unit/network/http/route_spec.rb @@ -0,0 +1,83 @@ +#! /usr/bin/env ruby +require 'spec_helper' +require 'puppet/indirector_testing' + +require 'puppet/network/http' + +describe Puppet::Network::HTTP::Route do + def new_request(method, path) + Puppet::Network::HTTP::Request.new({'accept' => 'pson', 'content-type' => 'text/yaml'}, {}, method, path, nil, nil) + end + + def respond(text) + lambda { |req, res| res.respond_with(200, "text/plain", text) } + end + + let(:req) { new_request("GET", "/vtest/foo") } + let(:res) { Puppet::Network::HTTP::Response.new(TestingHandler.new(), {}) } + + describe "an HTTP Route" do + it "can match a request" do + route = Puppet::Network::HTTP::Route.path(%r{^/vtest}) + expect(route.matches?(req)).to be_true + end + + it "will raise a Method Not Allowed error when no handler for the request's method is given" do + route = Puppet::Network::HTTP::Route.path(%r{^/vtest}).post(respond("ignored")) + expect do + route.process(req, res) + end.to raise_error(Puppet::Network::HTTP::Handler::HTTPMethodNotAllowedError) + end + + it "can match any HTTP method" do + route = Puppet::Network::HTTP::Route.path(%r{^/vtest/foo}).any(respond("used")) + expect(route.matches?(req)).to be_true + + route.process(req, res) + + expect(res.fields[:body]).to eq("used") + end + + it "calls the method handlers in turn" do + call_count = 0 + handler = lambda { |request, response| call_count += 1 } + route = Puppet::Network::HTTP::Route.path(%r{^/vtest/foo}).get(handler, handler) + + route.process(req, res) + expect(call_count).to eq(2) + end + + it "stops calling handlers if one of them raises an error" do + ignored_called = false + ignored = lambda { |req, res| ignored_called = true } + raise_error = lambda { |req, res| raise Puppet::Network::HTTP::Handler::HTTPNotAuthorizedError, "go away" } + route = Puppet::Network::HTTP::Route.path(%r{^/vtest/foo}).get(raise_error, ignored) + + expect do + route.process(req, res) + end.to raise_error(Puppet::Network::HTTP::Handler::HTTPNotAuthorizedError) + expect(ignored_called).to be_false + end + end + + class TestingHandler + include Puppet::Network::HTTP::Handler + def initialize(* routes) + register(routes) + end + + def set_content_type(response, format) + end + + def set_response(response, body, status = 200) + response[:body] = body + response[:status] = status + end + end + + class Puppet::Network::HTTP::Response + def fields + return @response + end + end +end From 64b9d96d5f2c967039c76beed9ea1370ee38026d Mon Sep 17 00:00:00 2001 From: Dan Lidral-Porter Date: Thu, 9 Jan 2014 10:09:45 -0800 Subject: [PATCH 398/800] (PUP-1151) Implement Route changes. Rather than creating a route with a path and method, routes are created with just a patch matcher and method handlers can be added by calling the HTTP method-named methods on the route. By default, handlers for all methods just raise a method not allowed error; user-defined handlers replace those. When the handler is given a list of routes to register, it checks to see if any of them have identical path matching regexes and throws an error if they do. However, it is still entirely possible for regexes that do not compare as equal to express the same regular language, so users still bear responsibility to ensure their routes cooperate. --- lib/puppet/network/http/api/v1.rb | 2 +- lib/puppet/network/http/api/v2.rb | 4 +- lib/puppet/network/http/handler.rb | 9 +++- lib/puppet/network/http/route.rb | 75 +++++++++++++++++++++--------- 4 files changed, 65 insertions(+), 25 deletions(-) diff --git a/lib/puppet/network/http/api/v1.rb b/lib/puppet/network/http/api/v1.rb index fba8ea460..e03b5afc7 100644 --- a/lib/puppet/network/http/api/v1.rb +++ b/lib/puppet/network/http/api/v1.rb @@ -25,7 +25,7 @@ class Puppet::Network::HTTP::API::V1 } def self.routes - [Puppet::Network::HTTP::Route.any(/.*/, new)] + [Puppet::Network::HTTP::Route.path(/.*/).any(new)] end # handle an HTTP request diff --git a/lib/puppet/network/http/api/v2.rb b/lib/puppet/network/http/api/v2.rb index e2e7c8409..8d2b05ae2 100644 --- a/lib/puppet/network/http/api/v2.rb +++ b/lib/puppet/network/http/api/v2.rb @@ -1,6 +1,6 @@ class Puppet::Network::HTTP::API::V2 def self.routes - [Puppet::Network::HTTP::Route.get( - %r{^/v2/environments$}, lambda { |req, res| raise Puppet::Network::HTTP::Handler::HTTPNotAuthorizedError, "You shall not pass!" })] + [Puppet::Network::HTTP::Route.path(%r{^/v2/environments$}).get( + lambda { |req, res| raise Puppet::Network::HTTP::Handler::HTTPNotAuthorizedError, "You shall not pass!" })] end end diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb index 64f748eef..a0345bb11 100644 --- a/lib/puppet/network/http/handler.rb +++ b/lib/puppet/network/http/handler.rb @@ -53,8 +53,15 @@ module Puppet::Network::HTTP::Handler end def register(routes) - @routes = routes + # There's got to be a simpler way to do this, right? + dupes = {} + routes.each { |r| dupes[r.path_matcher] = (dupes[r.path_matcher] || 0) + 1 } + dupes = dupes.collect { |pm, count| pm if count > 1 }.compact + if dupes.count > 0 + raise ArgumentError, "Given multiple routes with identical path regexes: #{dupes.map{ |rgx| rgx.inspect }.join(', ')}" + end + @routes = routes Puppet.debug("Routes Registered:") @routes.each do |route| Puppet.debug(route.inspect) diff --git a/lib/puppet/network/http/route.rb b/lib/puppet/network/http/route.rb index 3b0cbfb7d..085796fda 100644 --- a/lib/puppet/network/http/route.rb +++ b/lib/puppet/network/http/route.rb @@ -1,41 +1,74 @@ class Puppet::Network::HTTP::Route - def self.post(path_matcher, *handlers) - new("POST", lambda { |method| method == "POST" }, path_matcher, *handlers) + MethodNotAllowedHandler = lambda do |req, res| + raise Puppet::Network::HTTP::Handler::HTTPMethodNotAllowedError, "method #{req.method} not allowed for route #{req.path}" end - def self.get(path_matcher, *handlers) - new("GET", lambda { |method| method == "GET" }, path_matcher, *handlers) + attr_reader :path_matcher + + def self.path(path_matcher) + new(path_matcher) end - def self.any(path_matcher, *handlers) - new("ANY", lambda { |method| true }, path_matcher, *handlers) - end - - def initialize(method_description, method_matcher, path_matcher, *handlers) - @method_description = method_description - @method_matcher = method_matcher - @handlers = handlers + def initialize(path_matcher) @path_matcher = path_matcher + @method_handlers = { + :GET => [MethodNotAllowedHandler], + :HEAD => [MethodNotAllowedHandler], + :OPTIONS => [MethodNotAllowedHandler], + :POST => [MethodNotAllowedHandler], + :PUT => [MethodNotAllowedHandler] + } + end + + def get(*handlers) + @method_handlers[:GET] = handlers + return self + end + + def head(*handlers) + @method_handlers[:GET] = handlers + return self + end + + def options(*handlers) + @method_handlers[:GET] = handlers + return self + end + + def post(*handlers) + @method_handlers[:POST] = handlers + return self + end + + def put(*handlers) + @method_handlers[:POST] = handlers + return self + end + + def any(*handlers) + @method_handlers.each do |method, registered_handlers| + @method_handlers[method] = handlers + end + return self end def matches?(request) Puppet.debug("Evaluating match for #{self.inspect}") - if @method_matcher.call(request.method) - if @path_matcher.match(request.path) - return true - else - Puppet.debug("Matched method (#{request.method.inspect}) but not path (#{request.path.inspect})") - end + if @path_matcher.match(request.path) + return true else - Puppet.debug("Did not match method (#{request.method.inspect})") + Puppet.debug("Did not match path (#{request.path.inspect})") end + return false end def process(request, response) - @handlers.each { |handler| handler.call(request, response) } + @method_handlers[request.method.upcase.intern].each do |handler| + handler.call(request, response) + end end def inspect - "Route #{@method_description} #{@path_matcher} to <#{@handlers.collect(&:inspect).join(', ')}>" + "Route #{@path_matcher.inspect}" end end From 32e54bdea8c3b10beb0ba322bca34172d2dbfeab Mon Sep 17 00:00:00 2001 From: Dan Lidral-Porter Date: Thu, 9 Jan 2014 13:38:49 -0800 Subject: [PATCH 399/800] (PUP-1151) Add documentation for environments endpoint --- api/docs/http_api_index.md | 33 +++++++++++++++++++++++++--- api/docs/http_environments.md | 41 +++++++++++++++++++++++++++++++++++ api/schemas/environments.json | 40 ++++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 api/docs/http_environments.md create mode 100644 api/schemas/environments.json diff --git a/api/docs/http_api_index.md b/api/docs/http_api_index.md index f5fcc36f3..95eda3e69 100644 --- a/api/docs/http_api_index.md +++ b/api/docs/http_api_index.md @@ -1,5 +1,5 @@ -Services --------- +V1 API Services +--------------- Puppet Agents use various network services which the Puppet Master provides in order to manage systems. Other systems can access these services in order to @@ -41,7 +41,34 @@ Serialization Formats --------------------- Puppet sends messages using several different serialization formats. Not all -REST services support all of the formats. +REST services support all of the formats; notably, V2 API endpoints only support +PSON. * {file:api/docs/pson.md PSON} * {http://www.yaml.org/spec/1.2/spec.html YAML} + + +V2 HTTP API +----------- + +These endpoints differ uniformly from the others in the following ways: + +* The first component of the URL path is always `v2`, rather than an environment name, and environment name is no longer a required path component. +* The only acceptable serialization format is PSON. + +### Endpoints + +* {file:api/docs/http_environments.md Environments} + +### V2 API Errors + +V2 API endpoints may return several different HTTP error responses: + +* When the client submits a malformed request, the API will return a 400 Bad Request response. +* When the client is not authorized, the API will return a 403 Not Authorized response. +* When the client attempts to use an HTTP method that is not permissible for the route, the API will return a 405 Method Not Allowed response. +* When the client asks for a response in a format other than PSON, the API will return a 406 Unacceptable response. +* When the server encounters an unexpected error during the handling of a request, it will return a 500 Server Error response. + +Note that the V1 API is a fallback for the V2 API, meaning that if a request does not match a V2 API endpoint, that request will be handed off to the V1 API. +This means that the V2 API is not able to return 404 Not Found responses for requests that do not match a V2 endpoint; instead you will receive a 400 Bad Request response from the V1 API. diff --git a/api/docs/http_environments.md b/api/docs/http_environments.md new file mode 100644 index 000000000..f06d42958 --- /dev/null +++ b/api/docs/http_environments.md @@ -0,0 +1,41 @@ +Environments +============ + +The `environments` endpoint allows for enumeration of the environments known to the master, along with the modules available in each. +This endpoint is by default accessible to any client with a valid certificate, though this may be changed by `auth.conf`. + +Get +--- + +Get the list of known environments. + + GET /v2/environments + +### Supported Response Formats + +PSON + +### Parameters + +None + +### Example Request & Response + +* Note: module lists shortened for readability. + + GET /v2/environments + + HTTP 200 OK + Content-Type: text/pson + + { + "search_paths": ["/etc/puppet/environments"] + "environments": { + "production": { + "modules": { + "a-module": { "version": "1.3.5" } + "a-different-module": { "version": "2.4.6" } + } + } + } + } diff --git a/api/schemas/environments.json b/api/schemas/environments.json new file mode 100644 index 000000000..b9b1f15b7 --- /dev/null +++ b/api/schemas/environments.json @@ -0,0 +1,40 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Environment Enumeration", + "description": "An enumeration of environments and their modules", + "type": "object", + "properties": { + "search_paths": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "description": "An array of the paths where the master looked for environments." + }, + "environments": { + "type": "object", + "patternProperties": { + "^[a-z0-9_]+$": { + "type": "object", + "properties": { + "modules" : { + "type": "object", + "patternProperties": { + "^[a-z0-9_\-]+$": { + "type": "object", + "properties": { + "version": { "type": "string" } + }, + "required": ["version"] + } + } + } + }, + "required": ["modules"] + } + } + } + }, + "required": ["search_path", "environments"] +} From ff429b6d5468159062fd4905aeba1ed1a255d9b3 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Thu, 9 Jan 2014 15:18:38 -0800 Subject: [PATCH 400/800] (PUP-1151) Specify JSON and error responses We need to move away from PSON in puppet. Using V2 as a switching point is as good as any. This also specifies how error responses are formatted. --- api/docs/http_api_index.md | 79 ++++++++++++++++++++++++----------- api/docs/http_environments.md | 8 +--- 2 files changed, 57 insertions(+), 30 deletions(-) diff --git a/api/docs/http_api_index.md b/api/docs/http_api_index.md index 95eda3e69..230ad9ae5 100644 --- a/api/docs/http_api_index.md +++ b/api/docs/http_api_index.md @@ -5,6 +5,20 @@ Puppet Agents use various network services which the Puppet Master provides in order to manage systems. Other systems can access these services in order to put the information that the Puppet Master has to use. +The V1 API is all based off of dispatching to puppet's internal "indirector" +framework. Every HTTP endpoint in V1 follows the form +`/:environment/:indirection/:key`, where + * `:environment` is the name of the environment that should be in effect for + the request. Not all endpoints need an environment, but the path component + must always be specified. + * `:indirection` is the indirection to dispatch the request to. + * `:key` is the "key" portion of the indirection call. + +Using this API requires a significant amount of understanding of how puppet's +internal services are structured. The following documents provide some +specification for what is available and the ways in which they can be +interacted with. + ### Configuration Management Services These services are all related to how the Puppet Agent is able to manage the @@ -36,39 +50,56 @@ These services are all in support of Puppet's PKI system. * {file:api/docs/http_certificate_status.md Certificate Status} * {file:api/docs/http_certificate_revocation_list.md Certificate Revocation List} - -Serialization Formats ---------------------- - -Puppet sends messages using several different serialization formats. Not all -REST services support all of the formats; notably, V2 API endpoints only support -PSON. - -* {file:api/docs/pson.md PSON} -* {http://www.yaml.org/spec/1.2/spec.html YAML} - - V2 HTTP API ----------- -These endpoints differ uniformly from the others in the following ways: +The V2 HTTP API is accessed by prefixing requests with `/v2`. Authorization for +these endpoints is still controlled with the `auth.conf` authorization system +in puppet. When specifying the authorization of the V2 endpoints in `auth.conf` +the `/v2` prefix of the path should be left off. -* The first component of the URL path is always `v2`, rather than an environment name, and environment name is no longer a required path component. -* The only acceptable serialization format is PSON. +The V2 API will only accept payloads formatted as JSON and respond with JSON +(MIME application/json). ### Endpoints * {file:api/docs/http_environments.md Environments} -### V2 API Errors +### Error Responses -V2 API endpoints may return several different HTTP error responses: +All V2 API endpoints will respond to error conditions in a uniform manner and +use standard HTTP response code to signify those errors. -* When the client submits a malformed request, the API will return a 400 Bad Request response. -* When the client is not authorized, the API will return a 403 Not Authorized response. -* When the client attempts to use an HTTP method that is not permissible for the route, the API will return a 405 Method Not Allowed response. -* When the client asks for a response in a format other than PSON, the API will return a 406 Unacceptable response. -* When the server encounters an unexpected error during the handling of a request, it will return a 500 Server Error response. +* When the client submits a malformed request, the API will return a 400 Bad + Request response. +* When the client is not authorized, the API will return a 403 Not Authorized + response. +* When the client attempts to use an HTTP method that is not permissible for + the endpoint, the API will return a 405 Method Not Allowed response. +* When the client asks for a response in a format other than JSON, the API will + return a 406 Unacceptable response. +* When the server encounters an unexpected error during the handling of a + request, it will return a 500 Server Error response. + +Note that the V1 API is a fallback for the V2 API, meaning that if a request +does not match a V2 API endpoint, that request will be handed off to the V1 +API. This means that the V2 API is not able to return 404 Not Found responses for +requests that do not match a V2 endpoint; instead you will receive a 400 Bad +Request response from the V1 API. + +All error responses will contain a body, except when it is a HEAD request. The +error responses will uniformly be a JSON object with the following properties: + + * `message`: [String] A human readable message explaining the error. + * `code`: [String] A unique code to identify the error condition. + * `stacktrace` (only for 5xx errors): [Array] A stacktrace to where the error occurred. + +Serialization Formats +--------------------- + +Puppet sends messages using several different serialization formats. Not all +REST services support all of the formats. + +* {file:api/docs/pson.md PSON} +* {http://www.yaml.org/spec/1.2/spec.html YAML} -Note that the V1 API is a fallback for the V2 API, meaning that if a request does not match a V2 API endpoint, that request will be handed off to the V1 API. -This means that the V2 API is not able to return 404 Not Found responses for requests that do not match a V2 endpoint; instead you will receive a 400 Bad Request response from the V1 API. diff --git a/api/docs/http_environments.md b/api/docs/http_environments.md index f06d42958..3fefbcb01 100644 --- a/api/docs/http_environments.md +++ b/api/docs/http_environments.md @@ -11,22 +11,18 @@ Get the list of known environments. GET /v2/environments -### Supported Response Formats - -PSON - ### Parameters None ### Example Request & Response -* Note: module lists shortened for readability. +Note: module lists shortened for readability. GET /v2/environments HTTP 200 OK - Content-Type: text/pson + Content-Type: application/json { "search_paths": ["/etc/puppet/environments"] From 3a68c736ea368c7098ea3895607e9865c68b6b22 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Thu, 9 Jan 2014 15:32:31 -0800 Subject: [PATCH 401/800] (PUP-1151) Have puppet depend on json In order to have the v2 api emit and consume JSON puppet will need to have access to a json library. This adds it to our dependencies for debian, redhat, and gem packages. This should be a safe thing to do since puppet already needs hiera and hiera depends on JSON, so this is just taking the same dependency that hiera already had and applying it directly to puppet. --- ext/debian/control | 2 +- ext/project_data.yaml | 1 + ext/redhat/puppet.spec.erb | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ext/debian/control b/ext/debian/control index 7ee8ee831..d180d062d 100644 --- a/ext/debian/control +++ b/ext/debian/control @@ -11,7 +11,7 @@ Homepage: http://projects.puppetlabs.com/projects/puppet Package: puppet-common Architecture: all -Depends: ${misc:Depends}, ruby | ruby-interpreter, libxmlrpc-ruby, libopenssl-ruby, ruby-shadow | libshadow-ruby1.8, libaugeas-ruby | libaugeas-ruby1.9.1 | libaugeas-ruby1.8, adduser, lsb-base, sysv-rc (>= 2.86) | file-rc, hiera (>= 1.0.0), facter (>= 1.6.12), ruby-rgen (>= 0.6.5) +Depends: ${misc:Depends}, ruby | ruby-interpreter, libxmlrpc-ruby, libopenssl-ruby, ruby-shadow | libshadow-ruby1.8, libaugeas-ruby | libaugeas-ruby1.9.1 | libaugeas-ruby1.8, adduser, lsb-base, sysv-rc (>= 2.86) | file-rc, hiera (>= 1.0.0), facter (>= 1.6.12), ruby-rgen (>= 0.6.5), libjson-ruby | ruby-json Recommends: lsb-release, debconf-utils Suggests: ruby-selinux | libselinux-ruby1.8, librrd-ruby1.9.1 | librrd-ruby1.8 Breaks: puppet (<< 2.6.0~rc2-1), puppetmaster (<< 0.25.4-1) diff --git a/ext/project_data.yaml b/ext/project_data.yaml index f5f74cdbe..91a9fbcf0 100644 --- a/ext/project_data.yaml +++ b/ext/project_data.yaml @@ -18,6 +18,7 @@ gem_runtime_dependencies: facter: '~> 1.6' hiera: '~> 1.0' rgen: '~> 0.6.5' + json_pure: gem_rdoc_options: - --title - "Puppet - Configuration Management" diff --git a/ext/redhat/puppet.spec.erb b/ext/redhat/puppet.spec.erb index 095e6ff5b..dd934f1cb 100644 --- a/ext/redhat/puppet.spec.erb +++ b/ext/redhat/puppet.spec.erb @@ -40,6 +40,7 @@ BuildRequires: ruby >= 1.8.7 BuildArch: noarch Requires: ruby >= 1.8 Requires: ruby-shadow +Requires: rubygem-json # Pull in ruby selinux bindings where available %if 0%{?fedora} || 0%{?rhel} >= 6 From 64b76292b4aa02d6ef8358c22d40c3e673d2d87a Mon Sep 17 00:00:00 2001 From: Dan Lidral-Porter Date: Thu, 9 Jan 2014 16:27:22 -0800 Subject: [PATCH 402/800] (PUP-1151) Fix environment enumeration endpoint test --- .../tests/environment/can_enumerate_environments.rb | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/acceptance/tests/environment/can_enumerate_environments.rb b/acceptance/tests/environment/can_enumerate_environments.rb index 12180515a..9b16c75af 100644 --- a/acceptance/tests/environment/can_enumerate_environments.rb +++ b/acceptance/tests/environment/can_enumerate_environments.rb @@ -1,14 +1,12 @@ -test_name "Can enumerate enviromnents via an HTTP endpoint" +test_name "Can enumerate environments via an HTTP endpoint" -def master_url_for(agent, path) - master_port = on(agent, "puppet config print masterport --section agent").stdout.chomp - master_host = on(agent, "puppet config print server --section agent").stdout.chomp - "https://#{master_host}:#{master_port}#{path}" +def master_port(agent) + on(agent, "puppet config print masterport --section agent").stdout.chomp end with_puppet_running_on(master, {}) do agents.each do |agent| - on agent, "curl -ksv #{master_url_for(agent, '/v2/environments')}", :acceptable_exit_codes => [0,7] do + on agent, "curl -ksv https://#{master}:#{master_port(agent)}/v2/environments", :acceptable_exit_codes => [0,7] do assert_match(/< HTTP\/1\.\d 403/, stderr) end end From b390fc37155b4e87a0f45688a62c957e30bc88c9 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Thu, 9 Jan 2014 16:33:35 -0800 Subject: [PATCH 403/800] (PUP-716) File19Windows#symlink calls exist? on instance not class. Fixes a bug that was calling exist? on the File19Windows impl class when it was now an instance method of the same. --- lib/puppet/file_system/file19windows.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/file_system/file19windows.rb b/lib/puppet/file_system/file19windows.rb index d9264f100..1b33bcad1 100644 --- a/lib/puppet/file_system/file19windows.rb +++ b/lib/puppet/file_system/file19windows.rb @@ -24,7 +24,7 @@ class Puppet::FileSystem::File19Windows < Puppet::FileSystem::File19 def symlink(path, dest, options = {}) raise_if_symlinks_unsupported - dest_exists = self.class.exist?(dest) # returns false on dangling symlink + dest_exists = exist?(dest) # returns false on dangling symlink dest_stat = Puppet::Util::Windows::File.stat(dest) if dest_exists dest_symlink = Puppet::Util::Windows::File.symlink?(dest) From 2327f689b9d8548b97586abd695718e98a22414b Mon Sep 17 00:00:00 2001 From: Adrien Thebo Date: Thu, 9 Jan 2014 18:04:40 -0800 Subject: [PATCH 404/800] (maint) Ensure HieraPuppet cached instance is reset Running the following spec files in the exact given order - spec/unit/hiera_puppet_spec.rb - spec/unit/indirector/data_binding/hiera_spec.rb - spec/unit/parser/functions/lookup_spec.rb could cause the lookup function spec tests to fail. The HieraPuppet module would create an instance of Hiera and the specs would not properly clean it up, the Puppet::DataBinding::Hiera class would populate stored backend information with invalid data, and trying to interact with hiera later on could cause bad cached data to be read, causing failures that couldn't normally happen. This commit explicitly wipes the HieraPuppet cached instance after the tests are run, in an effort to prevent any other developers from spending thousands of hours tracing through mazes of twisty spec passages. --- spec/unit/hiera_puppet_spec.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/unit/hiera_puppet_spec.rb b/spec/unit/hiera_puppet_spec.rb index b35ed68b2..782f12e25 100644 --- a/spec/unit/hiera_puppet_spec.rb +++ b/spec/unit/hiera_puppet_spec.rb @@ -5,6 +5,10 @@ require 'puppet_spec/scope' describe 'HieraPuppet' do include PuppetSpec::Scope + after(:all) do + HieraPuppet.instance_variable_set(:@hiera, nil) + end + describe 'HieraPuppet#hiera_config' do let(:hiera_config_data) do { :backend => 'yaml' } From 1fa50a475cab553a84d9bda6c14abf68db23833a Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Thu, 9 Jan 2014 23:08:02 -0800 Subject: [PATCH 405/800] (PUP-1151) Fix the master port # (thus addressing a failure on windows) --- acceptance/tests/environment/can_enumerate_environments.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/acceptance/tests/environment/can_enumerate_environments.rb b/acceptance/tests/environment/can_enumerate_environments.rb index 9b16c75af..0ea1bb82a 100644 --- a/acceptance/tests/environment/can_enumerate_environments.rb +++ b/acceptance/tests/environment/can_enumerate_environments.rb @@ -1,12 +1,8 @@ test_name "Can enumerate environments via an HTTP endpoint" -def master_port(agent) - on(agent, "puppet config print masterport --section agent").stdout.chomp -end - with_puppet_running_on(master, {}) do agents.each do |agent| - on agent, "curl -ksv https://#{master}:#{master_port(agent)}/v2/environments", :acceptable_exit_codes => [0,7] do + on agent, "curl -ksv https://#{master}:8140/v2/environments", :acceptable_exit_codes => [0,7] do assert_match(/< HTTP\/1\.\d 403/, stderr) end end From 33db03faba3d7bc6d5025b54f3b822c7e62f8277 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Fri, 10 Jan 2014 09:46:55 -0800 Subject: [PATCH 406/800] (PUP-716) Assert path before making stat expectation File system changes ensure that Strings get converted to Pathnames; so the Puppet::Util::Windows::File.stat call needs to expect a Pathname now. --- spec/unit/file_system/file_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/unit/file_system/file_spec.rb b/spec/unit/file_system/file_spec.rb index 1d7e2efcb..52adf14f4 100644 --- a/spec/unit/file_system/file_spec.rb +++ b/spec/unit/file_system/file_spec.rb @@ -129,7 +129,7 @@ describe "Puppet::FileSystem" do end it "should fall back to stat when trying to lstat a file" do - Puppet::Util::Windows::File.expects(:stat).with(file) + Puppet::Util::Windows::File.expects(:stat).with(Puppet::FileSystem.assert_path(file)) Puppet::FileSystem.lstat(file) end From f2e7760016b158d50e13d081fe8b99f9eb509d83 Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Fri, 10 Jan 2014 21:22:03 -0800 Subject: [PATCH 407/800] (maint) Derive path for status.json (rather than fixed offset from cwd) --- spec/unit/status_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/unit/status_spec.rb b/spec/unit/status_spec.rb index 8f598f579..c21eed094 100755 --- a/spec/unit/status_spec.rb +++ b/spec/unit/status_spec.rb @@ -39,7 +39,7 @@ describe Puppet::Status do end it "serializes to PSON that conforms to the status schema", :unless => Puppet.features.microsoft_windows? do - schema = JSON.parse(File.read('api/schemas/status.json')) + schema = JSON.parse(File.read(File.join(File.dirname(__FILE__), '../../api/schemas/status.json'))) status = Puppet::Status.new status.version = Puppet.version From a6858973c24b47eab781901184681386ce37c10d Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Fri, 10 Jan 2014 22:22:55 -0800 Subject: [PATCH 408/800] (maint) Fix typo in a RuntimeError name --- lib/puppet/pops/binder/lookup.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/puppet/pops/binder/lookup.rb b/lib/puppet/pops/binder/lookup.rb index 0718095c7..d44d5269c 100644 --- a/lib/puppet/pops/binder/lookup.rb +++ b/lib/puppet/pops/binder/lookup.rb @@ -34,7 +34,7 @@ class Puppet::Pops::Binder::Lookup options[ :type ] = args[ 1 ] options[ :default ] = args[ 2 ] else - raise Puppet::PareError, "The lookup function accepts 1-3 arguments, got #{args.size}" + raise Puppet::ParseError, "The lookup function accepts 1-3 arguments, got #{args.size}" end options[:pblock] = pblock options @@ -58,7 +58,7 @@ class Puppet::Pops::Binder::Lookup def self.fail_lookup(names) name_part = if names.size == 1 "the name '#{names[0]}'" - else + else "any of the names ['" + names.join(', ') + "']" end fail("did not find a value for #{name_part}") From eac21e820a46e83a5fc4eefda4582064dfc44edb Mon Sep 17 00:00:00 2001 From: Adrien Thebo Date: Sat, 11 Jan 2014 11:12:55 -0800 Subject: [PATCH 409/800] (PUP-1218) Add spec coverage for ssh_authorized_key ssh-ed25519 --- .../ssh_authorized_key/parsed_spec.rb | 36 +++++++++---------- spec/unit/type/ssh_authorized_key_spec.rb | 9 ++++- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/spec/unit/provider/ssh_authorized_key/parsed_spec.rb b/spec/unit/provider/ssh_authorized_key/parsed_spec.rb index 65c94bfad..2fcce03b7 100755 --- a/spec/unit/provider/ssh_authorized_key/parsed_spec.rb +++ b/spec/unit/provider/ssh_authorized_key/parsed_spec.rb @@ -105,31 +105,27 @@ describe provider_class, :unless => Puppet.features.microsoft_windows? do @provider_class.parse(line)[0][:name].should == "" end - ['ssh-dss', 'ssh-rsa', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521'].each do |keytype| + { + # ssh-keygen -t dsa -b 1024 + 'ssh-dss' => 'AAAAB3NzaC1kc3MAAACBANGTefWMXS780qLMMgysq3GNMKzg55LXZODif6Tqv1vtTh4Wuk3J5X5u644jTyNdAIn1RiBI9MnwnZMZ6nXKvucMcMQWMibYS9W2MhkRj3oqsLWMMsdGXJL18SWM5A6oC3oIRC4JHJZtkm0OctR2trKxmX+MGhdCd+Xpsh9CNK8XAAAAFQD4olFiwv+QQUFdaZbWUy1CLEG9xQAAAIByCkXKgoriZF8bQ0OX1sKuR69M/6n5ngmQGVBKB7BQkpUjbK/OggB6iJgst5utKkDcaqYRnrTYG9q3jJ/flv7yYePuoSreS0nCMMx9gpEYuq+7Sljg9IecmN/IHrNd9qdYoASy5iuROQMvEZM7KFHA8vBv0tWdBOsp4hZKyiL1DAAAAIEAjkZlOps9L+cD/MTzxDj7toYYypdLOvjlcPBaglkPZoFZ0MAKTI0zXlVX1cWAnkd0Yfo4EpP+6XAjlZkod+QXKXM4Tb4PnR34ASMeU6sEjM61Na24S7JD3gpPKataFU/oH3hzXsBdK2ttKYmoqvf61h32IA/3Z5PjCCD9pPLPpAY', + # ssh-keygen -t rsa -b 2048 + 'ssh-rsa' => 'AAAAB3NzaC1yc2EAAAADAQABAAABAQDYtEaWa1mlxaAh9vtiz6RCVKDiJHDY15nsqqWU7F7A1+U1498+sWDyRDkZ8vXWQpzyOMBzBSHIxhsprlKhkjomy8BuJP+bHDBIKx4zgSFDrklrPIf467Iuug8J0qqDLxO4rOOjeAiLEyC0t2ZGnsTEea+rmat0bJ2cv3g5L4gH/OFz2pI4ZLp1HGN83ipl5UH8CjXQKwo3Db1E3WJCqKgszVX0Z4/qjnBRxFMoqky/1mGb/mX1eoT9JyQ8OhU9uENZOShkksSpgUqjlrjpj0Yd14hBlnE3M18pE4ivxjzectA/XRKNZaxOL1YREtU8sXusAwmlEY4aJ64aR0JrXfgx', + # ssh-keygen -t ecdsa -b 256 + 'ecdsa-sha2-nistp256' => 'AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBBO5PfBf0c2jAuqD+Lj3j+SuXOXNT2uqESLVOn5jVQfEF9GzllOw+CMOpUvV1CiOOn+F1ET15vcsfmD7z05WUTA=', + # ssh-keygen -t ecdsa -b 384 + 'ecdsa-sha2-nistp384' => 'AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBJIfxNoVK4FX3RuMlkHOwwxXwAh6Fqx5uAp4ftXrJ+64qYuIzb+/zSAkJV698Sre1b1lb0G4LyDdVAvXwaYK9kN25vy8umV3WdfZeHKXJGCcrplMCbbOERWARlpiPNEblg==', + # ssh-keygen -t ecdsa -b 521 + 'ecdsa-sha2-nistp521' => 'AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBADLK+u12xwB0JOwpmaxYXv8KnPK4p+SE2405qoo+vpAQ569fMwPMgKzltd770amdeuFogw/MJu17PN9LDdrD3o0uwHMjWee6TpHQDkuEetaxiou6K0WAzgbxx9QsY0MsJgXf1BuMLqdK+xT183wOSXwwumv99G7T32dOJZ5tYrH0y4XMw==', + # ssh-keygen -t ed25519 + 'ssh-ed25519' => 'AAAAC3NzaC1lZDI1NTE5AAAAIBWvu7D1KHBPaNXQcEuBsp48+JyPelXAq8ds6K5Du9gd', + }.each_pair do |keytype, keydata| it "should be able to parse a #{keytype} key entry" do - # use some real world examples generated with ssh-keygen - key = case keytype - when 'ssh-dss' # ssh-keygen -t dsa -b 1024 - 'AAAAB3NzaC1kc3MAAACBANGTefWMXS780qLMMgysq3GNMKzg55LXZODif6Tqv1vtTh4Wuk3J5X5u644jTyNdAIn1RiBI9MnwnZMZ6nXKvucMcMQWMibYS9W2MhkRj3oqsLWMMsdGXJL18SWM5A6oC3oIRC4JHJZtkm0OctR2trKxmX+MGhdCd+Xpsh9CNK8XAAAAFQD4olFiwv+QQUFdaZbWUy1CLEG9xQAAAIByCkXKgoriZF8bQ0OX1sKuR69M/6n5ngmQGVBKB7BQkpUjbK/OggB6iJgst5utKkDcaqYRnrTYG9q3jJ/flv7yYePuoSreS0nCMMx9gpEYuq+7Sljg9IecmN/IHrNd9qdYoASy5iuROQMvEZM7KFHA8vBv0tWdBOsp4hZKyiL1DAAAAIEAjkZlOps9L+cD/MTzxDj7toYYypdLOvjlcPBaglkPZoFZ0MAKTI0zXlVX1cWAnkd0Yfo4EpP+6XAjlZkod+QXKXM4Tb4PnR34ASMeU6sEjM61Na24S7JD3gpPKataFU/oH3hzXsBdK2ttKYmoqvf61h32IA/3Z5PjCCD9pPLPpAY' - when 'ssh-rsa' # ssh-keygen -t rsa -b 2048 - 'AAAAB3NzaC1yc2EAAAADAQABAAABAQDYtEaWa1mlxaAh9vtiz6RCVKDiJHDY15nsqqWU7F7A1+U1498+sWDyRDkZ8vXWQpzyOMBzBSHIxhsprlKhkjomy8BuJP+bHDBIKx4zgSFDrklrPIf467Iuug8J0qqDLxO4rOOjeAiLEyC0t2ZGnsTEea+rmat0bJ2cv3g5L4gH/OFz2pI4ZLp1HGN83ipl5UH8CjXQKwo3Db1E3WJCqKgszVX0Z4/qjnBRxFMoqky/1mGb/mX1eoT9JyQ8OhU9uENZOShkksSpgUqjlrjpj0Yd14hBlnE3M18pE4ivxjzectA/XRKNZaxOL1YREtU8sXusAwmlEY4aJ64aR0JrXfgx' - when 'ecdsa-sha2-nistp256' # ssh-keygen -t ecdsa -b 256 - 'AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBBO5PfBf0c2jAuqD+Lj3j+SuXOXNT2uqESLVOn5jVQfEF9GzllOw+CMOpUvV1CiOOn+F1ET15vcsfmD7z05WUTA=' - when 'ecdsa-sha2-nistp384' # ssh-keygen -t ecdsa -b 384 - 'AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBJIfxNoVK4FX3RuMlkHOwwxXwAh6Fqx5uAp4ftXrJ+64qYuIzb+/zSAkJV698Sre1b1lb0G4LyDdVAvXwaYK9kN25vy8umV3WdfZeHKXJGCcrplMCbbOERWARlpiPNEblg==' - when 'ecdsa-sha2-nistp521' #ssh-keygen -t ecdsa -b 521 - 'AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBADLK+u12xwB0JOwpmaxYXv8KnPK4p+SE2405qoo+vpAQ569fMwPMgKzltd770amdeuFogw/MJu17PN9LDdrD3o0uwHMjWee6TpHQDkuEetaxiou6K0WAzgbxx9QsY0MsJgXf1BuMLqdK+xT183wOSXwwumv99G7T32dOJZ5tYrH0y4XMw==' - when 'ssh-ed25519' #ssh-keygen -t ed25519 - 'AAAAC3NzaC1lZDI1NTE5AAAAIBWvu7D1KHBPaNXQcEuBsp48+JyPelXAq8ds6K5Du9gd' - else - pending("No sample key for #{keytype} yet") - end comment = 'sample_key' - record = @provider_class.parse_line("#{keytype} #{key} #{comment}") + record = @provider_class.parse_line("#{keytype} #{keydata} #{comment}") record.should_not be_nil record[:name].should == comment - record[:key].should == key + record[:key].should == keydata record[:type].should == keytype end end diff --git a/spec/unit/type/ssh_authorized_key_spec.rb b/spec/unit/type/ssh_authorized_key_spec.rb index 70861e61d..1772a2461 100755 --- a/spec/unit/type/ssh_authorized_key_spec.rb +++ b/spec/unit/type/ssh_authorized_key_spec.rb @@ -71,7 +71,14 @@ describe ssh_authorized_key, :unless => Puppet.features.microsoft_windows? do describe "for type" do - [:'ssh-dss', :'ssh-rsa', :rsa, :dsa, :'ecdsa-sha2-nistp256', :'ecdsa-sha2-nistp384', :'ecdsa-sha2-nistp521'].each do |keytype| + [ + :'ssh-dss', :dsa, + :'ssh-rsa', :rsa, + :'ecdsa-sha2-nistp256', + :'ecdsa-sha2-nistp384', + :'ecdsa-sha2-nistp521', + :ed25519, :'ssh-ed25519', + ].each do |keytype| it "should support #{keytype}" do proc { @class.new(:name => "whev", :type => keytype, :user => "nobody") }.should_not raise_error end From 6a6ef5cc0455d8633355a9b7f86fff6ff536da09 Mon Sep 17 00:00:00 2001 From: Adrien Thebo Date: Sat, 11 Jan 2014 11:33:30 -0800 Subject: [PATCH 410/800] (PUP-1218) Add spec coverage for sshkey ssh-ed25519 --- spec/unit/type/sshkey_spec.rb | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/spec/unit/type/sshkey_spec.rb b/spec/unit/type/sshkey_spec.rb index 37a4865b7..573b3bb83 100755 --- a/spec/unit/type/sshkey_spec.rb +++ b/spec/unit/type/sshkey_spec.rb @@ -28,7 +28,14 @@ describe sshkey do describe "when validating values" do - [:'ssh-dss', :'ssh-rsa', :rsa, :dsa, :'ecdsa-sha2-nistp256', :'ecdsa-sha2-nistp384', :'ecdsa-sha2-nistp521'].each do |keytype| + [ + :'ssh-dss', :dsa, + :'ssh-rsa', :rsa, + :'ecdsa-sha2-nistp256', + :'ecdsa-sha2-nistp384', + :'ecdsa-sha2-nistp521', + :'ssh-ed25519', :ed25519, + ].each do |keytype| it "should support #{keytype} as a type value" do proc { @class.new(:name => "foo", :type => keytype) }.should_not raise_error end From 76ae6a241ed807e3068ba20be40142f44d80e22f Mon Sep 17 00:00:00 2001 From: Adrien Thebo Date: Sat, 11 Jan 2014 11:51:57 -0800 Subject: [PATCH 411/800] (maint) Clean up sshkey and ssh_authorizd_key type specs --- spec/unit/type/ssh_authorized_key_spec.rb | 180 +++++++++++----------- spec/unit/type/sshkey_spec.rb | 60 ++++---- 2 files changed, 119 insertions(+), 121 deletions(-) diff --git a/spec/unit/type/ssh_authorized_key_spec.rb b/spec/unit/type/ssh_authorized_key_spec.rb index 1772a2461..fa82941c7 100755 --- a/spec/unit/type/ssh_authorized_key_spec.rb +++ b/spec/unit/type/ssh_authorized_key_spec.rb @@ -1,38 +1,34 @@ #! /usr/bin/env ruby require 'spec_helper' -ssh_authorized_key = Puppet::Type.type(:ssh_authorized_key) -describe ssh_authorized_key, :unless => Puppet.features.microsoft_windows? do +describe Puppet::Type.type(:ssh_authorized_key), :unless => Puppet.features.microsoft_windows? do include PuppetSpec::Files before do - @class = Puppet::Type.type(:ssh_authorized_key) + provider_class = stub 'provider_class', :name => "fake", :suitable? => true, :supports_parameter? => true + described_class.stubs(:defaultprovider).returns(provider_class) + described_class.stubs(:provider).returns(provider_class) - @provider_class = stub 'provider_class', :name => "fake", :suitable? => true, :supports_parameter? => true - @class.stubs(:defaultprovider).returns(@provider_class) - @class.stubs(:provider).returns(@provider_class) - - @provider = stub 'provider', :class => @provider_class, :file_path => make_absolute("/tmp/whatever"), :clear => nil - @provider_class.stubs(:new).returns(@provider) - @catalog = Puppet::Resource::Catalog.new + provider = stub 'provider', :class => provider_class, :file_path => make_absolute("/tmp/whatever"), :clear => nil + provider_class.stubs(:new).returns(provider) end - it "should have :name be its namevar" do - @class.key_attributes.should == [:name] + it "has :name as its namevar" do + expect(described_class.key_attributes).to eq [:name] end describe "when validating attributes" do [:name, :provider].each do |param| - it "should have a #{param} parameter" do - @class.attrtype(param).should == :param + it "has a #{param} parameter" do + expect(described_class.attrtype(param)).to eq :param end end [:type, :key, :user, :target, :options, :ensure].each do |property| - it "should have a #{property} property" do - @class.attrtype(property).should == :property + it "has a #{property} property" do + expect(described_class.attrtype(property)).to eq :property end end @@ -42,29 +38,29 @@ describe ssh_authorized_key, :unless => Puppet.features.microsoft_windows? do describe "for name" do - it "should support valid names" do - proc { @class.new(:name => "username", :ensure => :present, :user => "nobody") }.should_not raise_error - proc { @class.new(:name => "username@hostname", :ensure => :present, :user => "nobody") }.should_not raise_error + it "supports valid names" do + described_class.new(:name => "username", :ensure => :present, :user => "nobody") + described_class.new(:name => "username@hostname", :ensure => :present, :user => "nobody") end - it "should support whitespace" do - proc { @class.new(:name => "my test", :ensure => :present, :user => "nobody") }.should_not raise_error + it "supports whitespace" do + described_class.new(:name => "my test", :ensure => :present, :user => "nobody") end end describe "for ensure" do - it "should support :present" do - proc { @class.new(:name => "whev", :ensure => :present, :user => "nobody") }.should_not raise_error + it "supports :present" do + described_class.new(:name => "whev", :ensure => :present, :user => "nobody") end - it "should support :absent" do - proc { @class.new(:name => "whev", :ensure => :absent, :user => "nobody") }.should_not raise_error + it "supports :absent" do + described_class.new(:name => "whev", :ensure => :absent, :user => "nobody") end - it "should not support other values" do - proc { @class.new(:name => "whev", :ensure => :foo, :user => "nobody") }.should raise_error(Puppet::Error, /Invalid value/) + it "nots support other values" do + expect { described_class.new(:name => "whev", :ensure => :foo, :user => "nobody") }.to raise_error(Puppet::Error, /Invalid value/) end end @@ -79,119 +75,119 @@ describe ssh_authorized_key, :unless => Puppet.features.microsoft_windows? do :'ecdsa-sha2-nistp521', :ed25519, :'ssh-ed25519', ].each do |keytype| - it "should support #{keytype}" do - proc { @class.new(:name => "whev", :type => keytype, :user => "nobody") }.should_not raise_error + it "supports #{keytype}" do + described_class.new(:name => "whev", :type => keytype, :user => "nobody") end end - it "should alias :rsa to :ssh-rsa" do - key = @class.new(:name => "whev", :type => :rsa, :user => "nobody") - key.should(:type).should == :'ssh-rsa' + it "aliases :rsa to :ssh-rsa" do + key = described_class.new(:name => "whev", :type => :rsa, :user => "nobody") + expect(key.should(:type)).to eq :'ssh-rsa' end - it "should alias :dsa to :ssh-dss" do - key = @class.new(:name => "whev", :type => :dsa, :user => "nobody") - key.should(:type).should == :'ssh-dss' + it "aliases :dsa to :ssh-dss" do + key = described_class.new(:name => "whev", :type => :dsa, :user => "nobody") + expect(key.should(:type)).to eq :'ssh-dss' end - it "should not support values other than ssh-dss, ssh-rsa, dsa, rsa" do - proc { @class.new(:name => "whev", :type => :something) }.should raise_error(Puppet::Error,/Invalid value/) + it "doesn't support values other than ssh-dss, ssh-rsa, dsa, rsa" do + expect { described_class.new(:name => "whev", :type => :something) }.to raise_error(Puppet::Error,/Invalid value/) end end describe "for key" do - it "should support a valid key like a 1024 bit rsa key" do - proc { @class.new(:name => "whev", :type => :rsa, :user => "nobody", :key => 'AAAAB3NzaC1yc2EAAAADAQABAAAAgQDCPfzW2ry7XvMc6E5Kj2e5fF/YofhKEvsNMUogR3PGL/HCIcBlsEjKisrY0aYgD8Ikp7ZidpXLbz5dBsmPy8hJiBWs5px9ZQrB/EOQAwXljvj69EyhEoGawmxQMtYw+OAIKHLJYRuk1QiHAMHLp5piqem8ZCV2mLb9AsJ6f7zUVw==')}.should_not raise_error + it "supports a valid key like a 1024 bit rsa key" do + expect { described_class.new(:name => "whev", :type => :rsa, :user => "nobody", :key => 'AAAAB3NzaC1yc2EAAAADAQABAAAAgQDCPfzW2ry7XvMc6E5Kj2e5fF/YofhKEvsNMUogR3PGL/HCIcBlsEjKisrY0aYgD8Ikp7ZidpXLbz5dBsmPy8hJiBWs5px9ZQrB/EOQAwXljvj69EyhEoGawmxQMtYw+OAIKHLJYRuk1QiHAMHLp5piqem8ZCV2mLb9AsJ6f7zUVw==')}.to_not raise_error end - it "should support a valid key like a 4096 bit rsa key" do - proc { @class.new(:name => "whev", :type => :rsa, :user => "nobody", :key => 'AAAAB3NzaC1yc2EAAAADAQABAAACAQDEY4pZFyzSfRc9wVWI3DfkgT/EL033UZm/7x1M+d+lBD00qcpkZ6CPT7lD3Z+vylQlJ5S8Wcw6C5Smt6okZWY2WXA9RCjNJMIHQbJAzwuQwgnwU/1VMy9YPp0tNVslg0sUUgpXb13WW4mYhwxyGmIVLJnUrjrQmIFhtfHsJAH8ZVqCWaxKgzUoC/YIu1u1ScH93lEdoBPLlwm6J0aiM7KWXRb7Oq1nEDZtug1zpX5lhgkQWrs0BwceqpUbY+n9sqeHU5e7DCyX/yEIzoPRW2fe2Gx1Iq6JKM/5NNlFfaW8rGxh3Z3S1NpzPHTRjw8js3IeGiV+OPFoaTtM1LsWgPDSBlzIdyTbSQR7gKh0qWYCNV/7qILEfa0yIFB5wIo4667iSPZw2pNgESVtenm8uXyoJdk8iWQ4mecdoposV/znknNb2GPgH+n/2vme4btZ0Sl1A6rev22GQjVgbWOn8zaDglJ2vgCN1UAwmq41RXprPxENGeLnWQppTnibhsngu0VFllZR5kvSIMlekLRSOFLFt92vfd+tk9hZIiKm9exxcbVCGGQPsf6dZ27rTOmg0xM2Sm4J6RRKuz79HQgA4Eg18+bqRP7j/itb89DmtXEtoZFAsEJw8IgIfeGGDtHTkfAlAC92mtK8byeaxGq57XCTKbO/r5gcOMElZHy1AcB8kw==')}.should_not raise_error + it "supports a valid key like a 4096 bit rsa key" do + expect { described_class.new(:name => "whev", :type => :rsa, :user => "nobody", :key => 'AAAAB3NzaC1yc2EAAAADAQABAAACAQDEY4pZFyzSfRc9wVWI3DfkgT/EL033UZm/7x1M+d+lBD00qcpkZ6CPT7lD3Z+vylQlJ5S8Wcw6C5Smt6okZWY2WXA9RCjNJMIHQbJAzwuQwgnwU/1VMy9YPp0tNVslg0sUUgpXb13WW4mYhwxyGmIVLJnUrjrQmIFhtfHsJAH8ZVqCWaxKgzUoC/YIu1u1ScH93lEdoBPLlwm6J0aiM7KWXRb7Oq1nEDZtug1zpX5lhgkQWrs0BwceqpUbY+n9sqeHU5e7DCyX/yEIzoPRW2fe2Gx1Iq6JKM/5NNlFfaW8rGxh3Z3S1NpzPHTRjw8js3IeGiV+OPFoaTtM1LsWgPDSBlzIdyTbSQR7gKh0qWYCNV/7qILEfa0yIFB5wIo4667iSPZw2pNgESVtenm8uXyoJdk8iWQ4mecdoposV/znknNb2GPgH+n/2vme4btZ0Sl1A6rev22GQjVgbWOn8zaDglJ2vgCN1UAwmq41RXprPxENGeLnWQppTnibhsngu0VFllZR5kvSIMlekLRSOFLFt92vfd+tk9hZIiKm9exxcbVCGGQPsf6dZ27rTOmg0xM2Sm4J6RRKuz79HQgA4Eg18+bqRP7j/itb89DmtXEtoZFAsEJw8IgIfeGGDtHTkfAlAC92mtK8byeaxGq57XCTKbO/r5gcOMElZHy1AcB8kw==')}.to_not raise_error end - it "should support a valid key like a 1024 bit dsa key" do - proc { @class.new(:name => "whev", :type => :dsa, :user => "nobody", :key => 'AAAAB3NzaC1kc3MAAACBAI80iR78QCgpO4WabVqHHdEDigOjUEHwIjYHIubR/7u7DYrXY+e+TUmZ0CVGkiwB/0yLHK5dix3Y/bpj8ZiWCIhFeunnXccOdE4rq5sT2V3l1p6WP33RpyVYbLmeuHHl5VQ1CecMlca24nHhKpfh6TO/FIwkMjghHBfJIhXK+0w/AAAAFQDYzLupuMY5uz+GVrcP+Kgd8YqMmwAAAIB3SVN71whLWjFPNTqGyyIlMy50624UfNOaH4REwO+Of3wm/cE6eP8n75vzTwQGBpJX3BPaBGW1S1Zp/DpTOxhCSAwZzAwyf4WgW7YyAOdxN3EwTDJZeyiyjWMAOjW9/AOWt9gtKg0kqaylbMHD4kfiIhBzo31ZY81twUzAfN7angAAAIBfva8sTSDUGKsWWIXkdbVdvM4X14K4gFdy0ZJVzaVOtZ6alysW6UQypnsl6jfnbKvsZ0tFgvcX/CPyqNY/gMR9lyh/TCZ4XQcbqeqYPuceGehz+jL5vArfqsW2fJYFzgCcklmr/VxtP5h6J/T0c9YcDgc/xIfWdZAlznOnphI/FA==')}.should_not raise_error + it "supports a valid key like a 1024 bit dsa key" do + expect { described_class.new(:name => "whev", :type => :dsa, :user => "nobody", :key => 'AAAAB3NzaC1kc3MAAACBAI80iR78QCgpO4WabVqHHdEDigOjUEHwIjYHIubR/7u7DYrXY+e+TUmZ0CVGkiwB/0yLHK5dix3Y/bpj8ZiWCIhFeunnXccOdE4rq5sT2V3l1p6WP33RpyVYbLmeuHHl5VQ1CecMlca24nHhKpfh6TO/FIwkMjghHBfJIhXK+0w/AAAAFQDYzLupuMY5uz+GVrcP+Kgd8YqMmwAAAIB3SVN71whLWjFPNTqGyyIlMy50624UfNOaH4REwO+Of3wm/cE6eP8n75vzTwQGBpJX3BPaBGW1S1Zp/DpTOxhCSAwZzAwyf4WgW7YyAOdxN3EwTDJZeyiyjWMAOjW9/AOWt9gtKg0kqaylbMHD4kfiIhBzo31ZY81twUzAfN7angAAAIBfva8sTSDUGKsWWIXkdbVdvM4X14K4gFdy0ZJVzaVOtZ6alysW6UQypnsl6jfnbKvsZ0tFgvcX/CPyqNY/gMR9lyh/TCZ4XQcbqeqYPuceGehz+jL5vArfqsW2fJYFzgCcklmr/VxtP5h6J/T0c9YcDgc/xIfWdZAlznOnphI/FA==')}.to_not raise_error end - it "should not support whitespaces" do - proc { @class.new(:name => "whev", :type => :rsa, :user => "nobody", :key => 'AAA FA==')}.should raise_error(Puppet::Error,/Key must not contain whitespace/) + it "doesn't support whitespaces" do + expect { described_class.new(:name => "whev", :type => :rsa, :user => "nobody", :key => 'AAA FA==')}.to raise_error(Puppet::Error,/Key must not contain whitespace/) end end describe "for options" do - it "should support flags as options" do - proc { @class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => 'cert-authority')}.should_not raise_error - proc { @class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => 'no-port-forwarding')}.should_not raise_error + it "supports flags as options" do + expect { described_class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => 'cert-authority')}.to_not raise_error + expect { described_class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => 'no-port-forwarding')}.to_not raise_error end - it "should support key-value pairs as options" do - proc { @class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => 'command="command"')}.should_not raise_error + it "supports key-value pairs as options" do + expect { described_class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => 'command="command"')}.to_not raise_error end - it "should support key-value pairs where value consist of multiple items" do - proc { @class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => 'from="*.domain1,host1.domain2"')}.should_not raise_error + it "supports key-value pairs where value consist of multiple items" do + expect { described_class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => 'from="*.domain1,host1.domain2"')}.to_not raise_error end - it "should support environments as options" do - proc { @class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => 'environment="NAME=value"')}.should_not raise_error + it "supports environments as options" do + expect { described_class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => 'environment="NAME=value"')}.to_not raise_error end - it "should support multiple options as an array" do - proc { @class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => ['cert-authority','environment="NAME=value"'])}.should_not raise_error + it "supports multiple options as an array" do + expect { described_class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => ['cert-authority','environment="NAME=value"'])}.to_not raise_error end - it "should not support a comma separated list" do - proc { @class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => 'cert-authority,no-port-forwarding')}.should raise_error(Puppet::Error, /must be provided as an array/) + it "doesn't support a comma separated list" do + expect { described_class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => 'cert-authority,no-port-forwarding')}.to raise_error(Puppet::Error, /must be provided as an array/) end - it "should use :absent as a default value" do - @class.new(:name => "whev", :type => :rsa, :user => "nobody").should(:options).should == [:absent] + it "uses :absent as a default value" do + expect(described_class.new(:name => "whev", :type => :rsa, :user => "nobody").should(:options)).to eq [:absent] end it "property should return well formed string of arrays from is_to_s" do - resource = @class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => ["a","b","c"]) - resource.property(:options).is_to_s(["a","b","c"]).should == "a,b,c" + resource = described_class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => ["a","b","c"]) + expect(resource.property(:options).is_to_s(["a","b","c"])).to eq "a,b,c" end it "property should return well formed string of arrays from should_to_s" do - resource = @class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => ["a","b","c"]) - resource.property(:options).should_to_s(["a","b","c"]).should == "a,b,c" + resource = described_class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => ["a","b","c"]) + expect(resource.property(:options).should_to_s(["a","b","c"])).to eq "a,b,c" end end describe "for user" do - it "should support present users" do - proc { @class.new(:name => "whev", :type => :rsa, :user => "root") }.should_not raise_error + it "supports present users" do + described_class.new(:name => "whev", :type => :rsa, :user => "root") end - it "should support absent users" do - proc { @class.new(:name => "whev", :type => :rsa, :user => "ihopeimabsent") }.should_not raise_error + it "supports absent users" do + described_class.new(:name => "whev", :type => :rsa, :user => "ihopeimabsent") end end describe "for target" do - it "should support absolute paths" do - proc { @class.new(:name => "whev", :type => :rsa, :target => "/tmp/here") }.should_not raise_error + it "supports absolute paths" do + described_class.new(:name => "whev", :type => :rsa, :target => "/tmp/here") end - it "should use the user's path if not explicitly specified" do - @class.new(:name => "whev", :user => 'root').should(:target).should == File.expand_path("~root/.ssh/authorized_keys") + it "uses the user's path if not explicitly specified" do + expect(described_class.new(:name => "whev", :user => 'root').should(:target)).to eq File.expand_path("~root/.ssh/authorized_keys") end - it "should not consider the user's path if explicitly specified" do - @class.new(:name => "whev", :user => 'root', :target => '/tmp/here').should(:target).should == '/tmp/here' + it "doesn't consider the user's path if explicitly specified" do + expect(described_class.new(:name => "whev", :user => 'root', :target => '/tmp/here').should(:target)).to eq '/tmp/here' end - it "should inform about an absent user" do + it "informs about an absent user" do Puppet::Log.level = :debug - @class.new(:name => "whev", :user => 'idontexist').should(:target) + described_class.new(:name => "whev", :user => 'idontexist').should(:target) @logs.map(&:message).should include("The required user is not yet present on the system") end @@ -201,27 +197,27 @@ describe ssh_authorized_key, :unless => Puppet.features.microsoft_windows? do describe "when neither user nor target is specified" do - it "should raise an error" do - proc do - @class.new( + it "raises an error" do + expect do + described_class.new( :name => "Test", :key => "AAA", :type => "ssh-rsa", :ensure => :present) - end.should raise_error(Puppet::Error,/user.*or.*target.*mandatory/) + end.to raise_error(Puppet::Error,/user.*or.*target.*mandatory/) end end describe "when both target and user are specified" do - it "should use target" do - resource = @class.new( + it "uses target" do + resource = described_class.new( :name => "Test", :user => "root", :target => "/tmp/blah" ) - resource.should(:target).should == "/tmp/blah" + expect(resource.should(:target)).to eq "/tmp/blah" end end @@ -229,32 +225,32 @@ describe ssh_authorized_key, :unless => Puppet.features.microsoft_windows? do describe "when user is specified" do - it "should determine target" do - resource = @class.new( + it "determines target" do + resource = described_class.new( :name => "Test", :user => "root" ) target = File.expand_path("~root/.ssh/authorized_keys") - resource.should(:target).should == target + expect(resource.should(:target)).to eq target end # Bug #2124 - ssh_authorized_key always changes target if target is not defined - it "should not raise spurious change events" do - resource = @class.new(:name => "Test", :user => "root") + it "doesn't raise spurious change events" do + resource = described_class.new(:name => "Test", :user => "root") target = File.expand_path("~root/.ssh/authorized_keys") - resource.property(:target).safe_insync?(target).should == true + expect(resource.property(:target).safe_insync?(target)).to eq true end end describe "when calling validate" do - it "should not crash on a non-existant user" do - resource = @class.new( + it "doesn't crash on a non-existant user" do + resource = described_class.new( :name => "Test", :user => "ihopesuchuserdoesnotexist" ) - proc { resource.validate }.should_not raise_error + resource.validate end end diff --git a/spec/unit/type/sshkey_spec.rb b/spec/unit/type/sshkey_spec.rb index 573b3bb83..d16e59556 100755 --- a/spec/unit/type/sshkey_spec.rb +++ b/spec/unit/type/sshkey_spec.rb @@ -1,27 +1,23 @@ #! /usr/bin/env ruby require 'spec_helper' -sshkey = Puppet::Type.type(:sshkey) -describe sshkey do - before do - @class = sshkey - end +describe Puppet::Type.type(:sshkey) do - it "should have :name its namevar" do - @class.key_attributes.should == [:name] + it "uses :name as its namevar" do + expect(described_class.key_attributes).to eq [:name] end describe "when validating attributes" do [:name, :provider].each do |param| - it "should have a #{param} parameter" do - @class.attrtype(param).should == :param + it "has a #{param} parameter" do + expect(described_class.attrtype(param)).to eq :param end end [:host_aliases, :ensure, :key, :type].each do |property| - it "should have a #{property} property" do - @class.attrtype(property).should == :property + it "has a #{property} property" do + expect(described_class.attrtype(property)).to eq :property end end end @@ -36,39 +32,45 @@ describe sshkey do :'ecdsa-sha2-nistp521', :'ssh-ed25519', :ed25519, ].each do |keytype| - it "should support #{keytype} as a type value" do - proc { @class.new(:name => "foo", :type => keytype) }.should_not raise_error + it "supports #{keytype} as a type value" do + described_class.new(:name => "foo", :type => keytype) end end - it "should alias :rsa to :ssh-rsa" do - key = @class.new(:name => "foo", :type => :rsa) - key.should(:type).should == :'ssh-rsa' + it "aliases :rsa to :ssh-rsa" do + key = described_class.new(:name => "foo", :type => :rsa) + expect(key.should(:type)).to eq :'ssh-rsa' end - it "should alias :dsa to :ssh-dss" do - key = @class.new(:name => "foo", :type => :dsa) - key.should(:type).should == :'ssh-dss' + it "aliases :dsa to :ssh-dss" do + key = described_class.new(:name => "foo", :type => :dsa) + expect(key.should(:type)).to eq :'ssh-dss' end - it "should not support values other than ssh-dss, ssh-rsa, dsa, rsa for type" do - proc { @class.new(:name => "whev", :type => :'ssh-dsa') }.should raise_error(Puppet::Error) + it "doesn't support values other than ssh-dss, ssh-rsa, dsa, rsa for type" do + expect { + described_class.new(:name => "whev", :type => :'ssh-dsa') + }.to raise_error(Puppet::Error, /Invalid value.*ssh-dsa/) end - it "should accept one host_alias" do - proc { @class.new(:name => "foo", :host_aliases => 'foo.bar.tld') }.should_not raise_error + it "accepts one host_alias" do + described_class.new(:name => "foo", :host_aliases => 'foo.bar.tld') end - it "should accept multiple host_aliases as an array" do - proc { @class.new(:name => "foo", :host_aliases => ['foo.bar.tld','10.0.9.9']) }.should_not raise_error + it "accepts multiple host_aliases as an array" do + described_class.new(:name => "foo", :host_aliases => ['foo.bar.tld','10.0.9.9']) end - it "should not accept spaces in any host_alias" do - proc { @class.new(:name => "foo", :host_aliases => ['foo.bar.tld','foo bar']) }.should raise_error(Puppet::Error) + it "doesn't accept spaces in any host_alias" do + expect { + described_class.new(:name => "foo", :host_aliases => ['foo.bar.tld','foo bar']) + }.to raise_error(Puppet::Error, /cannot include whitespace/) end - it "should not accept aliases in the resourcename" do - proc { @class.new(:name => 'host,host.domain,ip') }.should raise_error(Puppet::Error) + it "doesn't accept aliases in the resourcename" do + expect { + described_class.new(:name => 'host,host.domain,ip') + }.to raise_error(Puppet::Error, /No comma in resourcename/) end end From f73142ee2531911271310e9d2281b181d560df84 Mon Sep 17 00:00:00 2001 From: James Ralston Date: Sun, 1 Dec 2013 15:56:50 -0500 Subject: [PATCH 412/800] (PUP-2118) (#22330) add btrfs to SELinux filesystem whitelist Many Linux distributions run SELinux in enforcing mode by default. On these systems, it is critical that Puppet properly sets SELinux file contexts on the files it manages, because incorrect contexts can lead to spurious problems that can be very difficult to diagnose (e.g., system services that mysteriously fail to start). In order to avoid errors caused by attempting to set SELinux file contexts on filesystems that do not support them, Puppet maintains a whitelist of filesystems that support SELinux file contexts, and only attempts to set SELinux file contexts if the file resides on one of the whitelisted filesystems. The btrfs filesystem has supported SELinux file contexts (file xattr) since 2009, and it is highly unlikely that any version of btrfs older than that will be seen in the wild. (In 2009, about the only people using btrfs were btrfs developers, and they have long since migrated to more recent versions.) However, the btrfs filesystem is not in Puppet's whitelist of filesystems that support SELinux contexts. This commit adds the btrfs filesystem to the whitelist of filesystem supports SELinux file contexts. --- lib/puppet/util/selinux.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/util/selinux.rb b/lib/puppet/util/selinux.rb index 78c3c4dfa..3444eccc5 100644 --- a/lib/puppet/util/selinux.rb +++ b/lib/puppet/util/selinux.rb @@ -148,7 +148,7 @@ module Puppet::Util::SELinux def selinux_label_support?(file) fstype = find_fs(file) return false if fstype.nil? - filesystems = ['ext2', 'ext3', 'ext4', 'gfs', 'gfs2', 'xfs', 'jfs'] + filesystems = ['ext2', 'ext3', 'ext4', 'gfs', 'gfs2', 'xfs', 'jfs', 'btrfs'] filesystems.include?(fstype) end From 6c4539cc02297a382e5a4564c837444242fc88ac Mon Sep 17 00:00:00 2001 From: Anderson Mills Date: Sat, 11 Jan 2014 14:42:19 -0800 Subject: [PATCH 413/800] (PUP-759) Remove reference to ca_days in ca_ttl default --- lib/puppet/defaults.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index 8b12b9fcb..8881ef9b5 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -778,7 +778,7 @@ EOT :ca_ttl => { :default => "5y", :type => :duration, - :desc => "The default TTL for new certificates. If this setting is set, ca_days is ignored. + :desc => "The default TTL for new certificates. #{AS_DURATION}" }, :req_bits => { From 14e257d6525ff1111b91ab5c18ac9a1d5d67d011 Mon Sep 17 00:00:00 2001 From: Jonathan Webb Date: Sun, 12 Jan 2014 11:11:35 -0500 Subject: [PATCH 414/800] (PUP-1421) Remove flawed source validation in package provider appdmg. * appdmg's "source" may be a URL that redirects to the actual dmg (and thus not end in .dmg) * actual download is renamed to "name" anyway * hdiutil doesn't require the dmg file to have a filename ending in .dmg --- lib/puppet/provider/package/appdmg.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/puppet/provider/package/appdmg.rb b/lib/puppet/provider/package/appdmg.rb index 1f8aee14f..46db070e8 100644 --- a/lib/puppet/provider/package/appdmg.rb +++ b/lib/puppet/provider/package/appdmg.rb @@ -49,9 +49,6 @@ Puppet::Type.type(:package).provide(:appdmg, :parent => Puppet::Provider::Packag end def self.installpkgdmg(source, name) - unless source =~ /\.dmg$/i - 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 From 2292f594265e95275081750dc45dee862f34b835 Mon Sep 17 00:00:00 2001 From: Josh Sharpe Date: Sun, 12 Jan 2014 20:05:38 -0500 Subject: [PATCH 415/800] (PUP-1420) Move global StateMachine to Puppet::Zone --- lib/puppet/type/zone.rb | 4 +++- spec/unit/type/zone_spec.rb | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/puppet/type/zone.rb b/lib/puppet/type/zone.rb index c61debada..6c010f249 100644 --- a/lib/puppet/type/zone.rb +++ b/lib/puppet/type/zone.rb @@ -6,6 +6,7 @@ Puppet::Type.newtype(:zone) do the zone's filesystem (with the `path` attribute), the zone resource will autorequire that directory." +module Puppet::Zone class StateMachine # A silly little state machine. def initialize @@ -47,6 +48,7 @@ autorequire that directory." index(a) < index(b) end end +end ensurable do desc "The running state of the zone. The valid states directly reflect @@ -57,7 +59,7 @@ autorequire that directory." def self.fsm return @fsm if @fsm - @fsm = StateMachine.new + @fsm = Puppet::Zone::StateMachine.new end def self.alias_state(values) diff --git a/spec/unit/type/zone_spec.rb b/spec/unit/type/zone_spec.rb index b34cf5841..e54902627 100755 --- a/spec/unit/type/zone_spec.rb +++ b/spec/unit/type/zone_spec.rb @@ -74,8 +74,8 @@ describe Puppet::Type.type(:zone) do relationship_graph.populate_from(catalog) relationship_graph.dependencies(zone).should == [zfs] end - describe StateMachine do - let (:sm) { StateMachine.new } + describe Puppet::Zone::StateMachine do + let (:sm) { Puppet::Zone::StateMachine.new } before :each do sm.insert_state :absent, :down => :destroy sm.insert_state :configured, :up => :configure, :down => :uninstall From 8fc95442ba6100ce12aa22da8ac584039dc2cc6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Dal=C3=A9n?= Date: Mon, 13 Jan 2014 17:16:33 +0100 Subject: [PATCH 416/800] (maint) remove dead code for indirector cache @use_cache was never set, so use_cache? always returned true. The option to set for not using cache is ignore_cache. --- lib/puppet/indirector/indirection.rb | 2 +- lib/puppet/indirector/request.rb | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/lib/puppet/indirector/indirection.rb b/lib/puppet/indirector/indirection.rb index 0c0cb2075..a22f465ac 100644 --- a/lib/puppet/indirector/indirection.rb +++ b/lib/puppet/indirector/indirection.rb @@ -201,7 +201,7 @@ class Puppet::Indirector::Indirection result = terminus.find(request) if not result.nil? result.expiration ||= self.expiration if result.respond_to?(:expiration) - if cache? and request.use_cache? + if cache? Puppet.info "Caching #{self.name} for #{request.key}" cache.save request(:save, key, result, options) end diff --git a/lib/puppet/indirector/request.rb b/lib/puppet/indirector/request.rb index 07ff46a1a..142b753bd 100644 --- a/lib/puppet/indirector/request.rb +++ b/lib/puppet/indirector/request.rb @@ -149,15 +149,6 @@ class Puppet::Indirector::Request i.model end - # Should we allow use of the cached object? - def use_cache? - if defined?(@use_cache) - ! ! use_cache - else - true - end - end - # Are we trying to interact with multiple resources, or just one? def plural? method == :search From ca995cc97f294cc462f6357bc7c5ba1f503b03f5 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 13 Jan 2014 22:00:47 +0100 Subject: [PATCH 417/800] (maint) Correct documentation of each function The documentation of the each function used deprecated syntax. --- lib/puppet/parser/functions/each.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/puppet/parser/functions/each.rb b/lib/puppet/parser/functions/each.rb index 569a6c416..3799f18ea 100644 --- a/lib/puppet/parser/functions/each.rb +++ b/lib/puppet/parser/functions/each.rb @@ -9,21 +9,21 @@ Puppet::Parser::Functions::newfunction( This function takes two mandatory arguments: the first should be an Array or a Hash, and the second a parameterized block as produced by the puppet syntax: - $a.each {|$x| ... } + $a.each |$x| { ... } When the first argument is an Array, the parameterized block should define one or two block parameters. For each application of the block, the next element from the array is selected, and it is passed to the block if the block has one parameter. If the block has two parameters, the first is the elements index, and the second the value. The index starts from 0. - $a.each {|$index, $value| ... } + $a.each |$index, $value| { ... } When the first argument is a Hash, the parameterized block should define one or two parameters. When one parameter is defined, the iteration is performed with each entry as an array of `[key, value]`, and when two parameters are defined the iteration is performed with key and value. - $a.each {|$entry| ..."key ${$entry[0]}, value ${$entry[1]}" } - $a.each {|$key, $value| ..."key ${key}, value ${value}" } + $a.each |$entry| { ..."key ${$entry[0]}, value ${$entry[1]}" } + $a.each |$key, $value| { ..."key ${key}, value ${value}" } When the first argument is a Type and the type is Enumerable, the parameterized block should define one or two block parameters. @@ -31,7 +31,7 @@ Puppet::Parser::Functions::newfunction( the block if the block has one parameter. If the block has two parameters, the first is the elements index, and the second the value. The index starts from 0. - Integer[ 10, 20 ].each {|$index, $value| ... } + Integer[ 10, 20 ].each |$index, $value| { ... } - Since 3.2 - requires `parser = future`. From 22a00327d2d146978c731ee9d959b117b32b92a6 Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Mon, 13 Jan 2014 15:23:46 -0800 Subject: [PATCH 418/800] (maint) Fix typo in spec test description --- spec/unit/util/selinux_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/unit/util/selinux_spec.rb b/spec/unit/util/selinux_spec.rb index eeb8ca5f7..b15a1971c 100755 --- a/spec/unit/util/selinux_spec.rb +++ b/spec/unit/util/selinux_spec.rb @@ -69,7 +69,7 @@ describe Puppet::Util::SELinux do find_fs('/mnt/nfs/testfile/foobar').should == "nfs" end - it "should reture true for a capable filesystem" do + it "should return true for a capable filesystem" do selinux_label_support?('/etc/puppet/testfile').should be_true end From 3d55fa920916a991d43c441f61efa3186a0df767 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 14 Jan 2014 02:15:52 +0100 Subject: [PATCH 419/800] (PUP-1212) Add stacktrace to all evaluator issues The former change improving the error message output did only work for errors that originated from an exception. Now, all errors have a backtrace. This also fixes issues with failures from an underlying exception but where the exception was not passed on. --- lib/puppet/pops/evaluator/evaluator_impl.rb | 6 +++--- lib/puppet/pops/evaluator/runtime3_support.rb | 10 ++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index b493f51fa..2771da543 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -326,7 +326,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl assign(name, delete(get_variable_value(name, o, scope), value), o, scope) end rescue ArgumentError => e - fail(Issues::APPEND_FAILED, o, {:message => e.message}) + fail(Issues::APPEND_FAILED, o, {:message => e.message}, e) end else fail(Issues::UNSUPPORTED_OPERATOR, o, {:operator => o.operator}) @@ -461,7 +461,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl :operator => o.operator, :left_value => left, :right_value => right, - :detail => e.message}) + :detail => e.message}, e) end end @@ -497,7 +497,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl begin pattern = Regexp.new(pattern) unless pattern.is_a?(Regexp) rescue StandardError => e - fail(Issues::MATCH_NOT_REGEXP, o.right_expr, {:detail => e.message}) + fail(Issues::MATCH_NOT_REGEXP, o.right_expr, {:detail => e.message}, e) end unless left.is_a?(String) fail(Issues::MATCH_NOT_STRING, o.left_expr, {:left_value => left}) diff --git a/lib/puppet/pops/evaluator/runtime3_support.rb b/lib/puppet/pops/evaluator/runtime3_support.rb index 30fd46973..d7c1ee062 100644 --- a/lib/puppet/pops/evaluator/runtime3_support.rb +++ b/lib/puppet/pops/evaluator/runtime3_support.rb @@ -13,6 +13,14 @@ module Puppet::Pops::Evaluator::Runtime3Support # @raise [Puppet::ParseError] an evaluation error initialized from the arguments (TODO: Change to EvaluationError?) # def fail(issue, semantic, options={}, except=nil) + if except.nil? + # Want a stacktrace, and it must be passed as an exception + begin + raise EvaluationError.new() + rescue EvaluationError => e + except = e + end + end diagnostic_producer.accept(issue, semantic, options, except) end @@ -476,4 +484,6 @@ module Puppet::Pops::Evaluator::Runtime3Support end end + class EvaluationError < StandardError + end end \ No newline at end of file From 828a759268c738e8767b6a6a9a0196c16fdbf007 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 10 Jan 2014 10:53:35 -0800 Subject: [PATCH 420/800] (PUP-1151) Move HTTP Errors to own module They aren't really part of the handler, and many more areas of HTTP processing are referring to them. --- lib/puppet/network/http.rb | 1 + lib/puppet/network/http/api/v1.rb | 12 ++++---- lib/puppet/network/http/api/v2.rb | 2 +- lib/puppet/network/http/error.rb | 38 +++++++++++++++++++++++++ lib/puppet/network/http/handler.rb | 41 ++------------------------- lib/puppet/network/http/request.rb | 2 +- lib/puppet/network/http/route.rb | 2 +- spec/unit/network/http/api/v1_spec.rb | 6 ++-- spec/unit/network/http/route_spec.rb | 6 ++-- 9 files changed, 56 insertions(+), 54 deletions(-) create mode 100644 lib/puppet/network/http/error.rb diff --git a/lib/puppet/network/http.rb b/lib/puppet/network/http.rb index 00547d30f..e4d421f9c 100644 --- a/lib/puppet/network/http.rb +++ b/lib/puppet/network/http.rb @@ -2,6 +2,7 @@ module Puppet::Network::HTTP HEADER_ENABLE_PROFILING = "X-Puppet-Profiling" HEADER_PUPPET_VERSION = "X-Puppet-Version" + require 'puppet/network/http/error' require 'puppet/network/http/api' require 'puppet/network/http/api/v1' require 'puppet/network/http/api/v2' diff --git a/lib/puppet/network/http/api/v1.rb b/lib/puppet/network/http/api/v1.rb index e03b5afc7..ea8bb591a 100644 --- a/lib/puppet/network/http/api/v1.rb +++ b/lib/puppet/network/http/api/v1.rb @@ -39,14 +39,14 @@ class Puppet::Network::HTTP::API::V1 raise ArgumentError, "Could not find indirection '#{indirection_name}'" unless indirection if !indirection.allow_remote_requests? - raise Puppet::Network::HTTP::Handler::HTTPNotFoundError, "No handler for #{indirection.name}" + raise Puppet::Network::HTTP::Error::HTTPNotFoundError, "No handler for #{indirection.name}" end trusted = Puppet::Context::TrustedInformation.remote(params[:authenticated], params[:node], certificate) Puppet::Context.override(:trusted_information => trusted) do send("do_#{method}", indirection, key, params, request, response) end - rescue Puppet::Network::HTTP::Handler::HTTPError => e + rescue Puppet::Network::HTTP::Error::HTTPError => e return do_http_control_exception(response, e) rescue Exception => e return do_exception(response, e) @@ -93,7 +93,7 @@ class Puppet::Network::HTTP::API::V1 # Execute our find. def do_find(indirection, key, params, request, response) unless result = indirection.find(key, params) - raise Puppet::Network::HTTP::Handler::HTTPNotFoundError, "Could not find #{indirection.name} #{key}" + raise Puppet::Network::HTTP::Error::HTTPNotFoundError, "Could not find #{indirection.name} #{key}" end format = accepted_response_formatter_for(indirection.model, request) @@ -113,7 +113,7 @@ class Puppet::Network::HTTP::API::V1 # Execute our head. def do_head(indirection, key, params, request, response) unless indirection.head(key, params) - raise Puppet::Network::HTTP::Handler::HTTPNotFoundError, "Could not find #{indirection.name} #{key}" + raise Puppet::Network::HTTP::Error::HTTPNotFoundError, "Could not find #{indirection.name} #{key}" end # No need to set a response because no response is expected from a @@ -125,7 +125,7 @@ class Puppet::Network::HTTP::API::V1 result = indirection.search(key, params) if result.nil? - raise Puppet::Network::HTTP::Handler::HTTPNotFoundError, "Could not find instances in #{indirection.name} with '#{key}'" + raise Puppet::Network::HTTP::Error::HTTPNotFoundError, "Could not find instances in #{indirection.name} with '#{key}'" end format = accepted_response_formatter_for(indirection.model, request) @@ -153,7 +153,7 @@ class Puppet::Network::HTTP::API::V1 end def accepted_response_formatter_for(model_class, request) - accepted_formats = request.headers['accept'] or raise Puppet::Network::HTTP::Handler::HTTPNotAcceptableError, "Missing required Accept header" + accepted_formats = request.headers['accept'] or raise Puppet::Network::HTTP::Error::HTTPNotAcceptableError, "Missing required Accept header" request.response_formatter_for(model_class.supported_formats, accepted_formats) end diff --git a/lib/puppet/network/http/api/v2.rb b/lib/puppet/network/http/api/v2.rb index 8d2b05ae2..895626a46 100644 --- a/lib/puppet/network/http/api/v2.rb +++ b/lib/puppet/network/http/api/v2.rb @@ -1,6 +1,6 @@ class Puppet::Network::HTTP::API::V2 def self.routes [Puppet::Network::HTTP::Route.path(%r{^/v2/environments$}).get( - lambda { |req, res| raise Puppet::Network::HTTP::Handler::HTTPNotAuthorizedError, "You shall not pass!" })] + lambda { |req, res| raise Puppet::Network::HTTP::Error::HTTPNotAuthorizedError, "You shall not pass!" })] end end diff --git a/lib/puppet/network/http/error.rb b/lib/puppet/network/http/error.rb new file mode 100644 index 000000000..9722a943d --- /dev/null +++ b/lib/puppet/network/http/error.rb @@ -0,0 +1,38 @@ +module Puppet::Network::HTTP::Error + class HTTPError < Exception + attr_reader :status + + def initialize(message, status) + super(message) + @status = status + end + end + + class HTTPNotAcceptableError < HTTPError + CODE = 406 + def initialize(message) + super("Not Acceptable: " + message, CODE) + end + end + + class HTTPNotFoundError < HTTPError + CODE = 404 + def initialize(message) + super("Not Found: " + message, CODE) + end + end + + class HTTPNotAuthorizedError < HTTPError + CODE = 403 + def initialize(message) + super("Not Authorized: " + message, CODE) + end + end + + class HTTPMethodNotAllowedError < HTTPError + CODE = 405 + def initialize(message) + super("Method Not Allowed: " + message, CODE) + end + end +end diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb index a0345bb11..35ae56f3f 100644 --- a/lib/puppet/network/http/handler.rb +++ b/lib/puppet/network/http/handler.rb @@ -15,43 +15,6 @@ module Puppet::Network::HTTP::Handler # in the query string, for security reasons. DISALLOWED_KEYS = ["node", "ip"] - class HTTPError < Exception - attr_reader :status - - def initialize(message, status) - super(message) - @status = status - end - end - - class HTTPNotAcceptableError < HTTPError - CODE = 406 - def initialize(message) - super("Not Acceptable: " + message, CODE) - end - end - - class HTTPNotFoundError < HTTPError - CODE = 404 - def initialize(message) - super("Not Found: " + message, CODE) - end - end - - class HTTPNotAuthorizedError < HTTPError - CODE = 403 - def initialize(message) - super("Not Authorized: " + message, CODE) - end - end - - class HTTPMethodNotAllowedError < HTTPError - CODE = 405 - def initialize(message) - super("Method Not Allowed: " + message, CODE) - end - end - def register(routes) # There's got to be a simpler way to do this, right? dupes = {} @@ -98,11 +61,11 @@ module Puppet::Network::HTTP::Handler if route = @routes.find { |route| route.matches?(new_request) } route.process(new_request, new_response) else - raise HTTPNotFoundError, "No route for #{new_request.method} #{new_request.path}" + raise Puppet::Network::HTTP::Error::HTTPNotFoundError, "No route for #{new_request.method} #{new_request.path}" end end - rescue HTTPError => e + rescue Puppet::Network::HTTP::Error::HTTPError => e msg = e.message Puppet.info(msg) new_response.respond_with(e.status, "text/plain", msg) diff --git a/lib/puppet/network/http/request.rb b/lib/puppet/network/http/request.rb index b625fc075..2b45e07a7 100644 --- a/lib/puppet/network/http/request.rb +++ b/lib/puppet/network/http/request.rb @@ -30,7 +30,7 @@ Puppet::Network::HTTP::Request = Struct.new(:headers, :params, :method, :path, : supported_formats) if formatter.nil? - raise Puppet::Network::HTTP::Handler::HTTPNotAcceptableError, "No supported formats are acceptable (Accept: #{accepted_formats})" + raise Puppet::Network::HTTP::Error::HTTPNotAcceptableError, "No supported formats are acceptable (Accept: #{accepted_formats})" end report_if_deprecated(formatter) diff --git a/lib/puppet/network/http/route.rb b/lib/puppet/network/http/route.rb index 085796fda..744de1886 100644 --- a/lib/puppet/network/http/route.rb +++ b/lib/puppet/network/http/route.rb @@ -1,6 +1,6 @@ class Puppet::Network::HTTP::Route MethodNotAllowedHandler = lambda do |req, res| - raise Puppet::Network::HTTP::Handler::HTTPMethodNotAllowedError, "method #{req.method} not allowed for route #{req.path}" + raise Puppet::Network::HTTP::Error::HTTPMethodNotAllowedError, "method #{req.method} not allowed for route #{req.path}" end attr_reader :path_matcher diff --git a/spec/unit/network/http/api/v1_spec.rb b/spec/unit/network/http/api/v1_spec.rb index ff31d043c..a80838150 100755 --- a/spec/unit/network/http/api/v1_spec.rb +++ b/spec/unit/network/http/api/v1_spec.rb @@ -6,9 +6,9 @@ require 'puppet/network/http/api/v1' require 'puppet/indirector_testing' describe Puppet::Network::HTTP::API::V1 do - let(:not_found_code) { Puppet::Network::HTTP::Handler::HTTPNotFoundError::CODE } - let(:not_acceptable_code) { Puppet::Network::HTTP::Handler::HTTPNotAcceptableError::CODE } - let(:not_authorized_code) { Puppet::Network::HTTP::Handler::HTTPNotAuthorizedError::CODE } + let(:not_found_code) { Puppet::Network::HTTP::Error::HTTPNotFoundError::CODE } + let(:not_acceptable_code) { Puppet::Network::HTTP::Error::HTTPNotAcceptableError::CODE } + let(:not_authorized_code) { Puppet::Network::HTTP::Error::HTTPNotAuthorizedError::CODE } let(:indirection) { Puppet::IndirectorTesting.indirection } let(:handler) { Puppet::Network::HTTP::API::V1.new } diff --git a/spec/unit/network/http/route_spec.rb b/spec/unit/network/http/route_spec.rb index a47914d69..beb268910 100644 --- a/spec/unit/network/http/route_spec.rb +++ b/spec/unit/network/http/route_spec.rb @@ -26,7 +26,7 @@ describe Puppet::Network::HTTP::Route do route = Puppet::Network::HTTP::Route.path(%r{^/vtest}).post(respond("ignored")) expect do route.process(req, res) - end.to raise_error(Puppet::Network::HTTP::Handler::HTTPMethodNotAllowedError) + end.to raise_error(Puppet::Network::HTTP::Error::HTTPMethodNotAllowedError) end it "can match any HTTP method" do @@ -50,12 +50,12 @@ describe Puppet::Network::HTTP::Route do it "stops calling handlers if one of them raises an error" do ignored_called = false ignored = lambda { |req, res| ignored_called = true } - raise_error = lambda { |req, res| raise Puppet::Network::HTTP::Handler::HTTPNotAuthorizedError, "go away" } + raise_error = lambda { |req, res| raise Puppet::Network::HTTP::Error::HTTPNotAuthorizedError, "go away" } route = Puppet::Network::HTTP::Route.path(%r{^/vtest/foo}).get(raise_error, ignored) expect do route.process(req, res) - end.to raise_error(Puppet::Network::HTTP::Handler::HTTPNotAuthorizedError) + end.to raise_error(Puppet::Network::HTTP::Error::HTTPNotAuthorizedError) expect(ignored_called).to be_false end end From 2e9d8dd0cae581c667cc4c935a836c96ca98fe86 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 10 Jan 2014 13:20:29 -0800 Subject: [PATCH 421/800] (PUP-1151) Authorize v2 api requests This commit does several things, all in order to make authorizing API V2 requests a bit easier: * Renames /v2 to /v2.0, which was chosen because it doesn't conflict with a legal environment name in the V1 API. * Adds route chaining so that a handler can deal with a request prefixed with /v2.0 and then continue on to another route * Changes how calls to authorization are handled so that full paths are checked rather than indirection/key pairs. * Introduces an authorization step in the /v2.0 request chain. This is currently limited to only handle GET requests (seen as find in auth.conf). --- api/docs/http_api_index.md | 14 ++--- lib/puppet/network/authconfig.rb | 7 ++- lib/puppet/network/authorization.rb | 4 +- lib/puppet/network/http.rb | 2 +- lib/puppet/network/http/api/v1.rb | 2 +- lib/puppet/network/http/api/v2.rb | 12 +++- .../network/http/api/v2/authorization.rb | 13 +++++ .../network/http/api/v2/environments.rb | 4 ++ lib/puppet/network/http/handler.rb | 2 +- lib/puppet/network/http/memory_response.rb | 6 +- lib/puppet/network/http/request.rb | 6 +- lib/puppet/network/http/route.rb | 21 +++++-- lib/puppet/network/rights.rb | 4 +- spec/integration/network/authconfig_spec.rb | 8 +-- spec/unit/network/authconfig_spec.rb | 4 +- .../network/http/api/v2/authorization_spec.rb | 57 +++++++++++++++++++ spec/unit/network/http/route_spec.rb | 36 +++++------- spec/unit/network/rights_spec.rb | 4 +- 18 files changed, 150 insertions(+), 56 deletions(-) create mode 100644 lib/puppet/network/http/api/v2/authorization.rb create mode 100644 lib/puppet/network/http/api/v2/environments.rb create mode 100644 spec/unit/network/http/api/v2/authorization_spec.rb diff --git a/api/docs/http_api_index.md b/api/docs/http_api_index.md index 230ad9ae5..e133e3d73 100644 --- a/api/docs/http_api_index.md +++ b/api/docs/http_api_index.md @@ -53,10 +53,10 @@ These services are all in support of Puppet's PKI system. V2 HTTP API ----------- -The V2 HTTP API is accessed by prefixing requests with `/v2`. Authorization for +The V2 HTTP API is accessed by prefixing requests with `/v2.0`. Authorization for these endpoints is still controlled with the `auth.conf` authorization system in puppet. When specifying the authorization of the V2 endpoints in `auth.conf` -the `/v2` prefix of the path should be left off. +the `/v2.0` prefix on V2 API paths must be retained; the full request path is used. The V2 API will only accept payloads formatted as JSON and respond with JSON (MIME application/json). @@ -80,12 +80,12 @@ use standard HTTP response code to signify those errors. return a 406 Unacceptable response. * When the server encounters an unexpected error during the handling of a request, it will return a 500 Server Error response. +* When the server is unable to find an endpoint handler for the request that + starts with `/v2.0`, it will return a 404 Not Found response -Note that the V1 API is a fallback for the V2 API, meaning that if a request -does not match a V2 API endpoint, that request will be handed off to the V1 -API. This means that the V2 API is not able to return 404 Not Found responses for -requests that do not match a V2 endpoint; instead you will receive a 400 Bad -Request response from the V1 API. +The V2 API paths are prefixed with `/v2.0` instead of `/v2` so that it is able +to respond with 404, but not interfere with any environments in the V1 API. +`v2` is a valid environment name, but `v2.0` is not. All error responses will contain a body, except when it is a HEAD request. The error responses will uniformly be a JSON object with the following properties: diff --git a/lib/puppet/network/authconfig.rb b/lib/puppet/network/authconfig.rb index 527774598..f99c12952 100644 --- a/lib/puppet/network/authconfig.rb +++ b/lib/puppet/network/authconfig.rb @@ -19,6 +19,9 @@ module Puppet { :acl => "/certificate/", :method => :find, :authenticated => :any }, { :acl => "/certificate_request", :method => [:find, :save], :authenticated => :any }, { :acl => "/status", :method => [:find], :authenticated => true }, + + # API V2.0 + { :acl => "/v2.0/environments", :method => :find, :allow => '*', :authenticated => true }, ] # Just proxy the setting methods to our rights stuff @@ -58,8 +61,8 @@ module Puppet # check whether this request is allowed in our ACL # raise an Puppet::Network::AuthorizedError if the request # is denied. - def check_authorization(indirection, method, key, params) - if authorization_failure_exception = @rights.is_request_forbidden_and_why?(indirection, method, key, params) + def check_authorization(method, path, params) + if authorization_failure_exception = @rights.is_request_forbidden_and_why?(method, path, params) Puppet.warning("Denying access: #{authorization_failure_exception}") raise authorization_failure_exception end diff --git a/lib/puppet/network/authorization.rb b/lib/puppet/network/authorization.rb index 43b8d0633..82e4bc3f7 100644 --- a/lib/puppet/network/authorization.rb +++ b/lib/puppet/network/authorization.rb @@ -26,8 +26,8 @@ module Puppet::Network end # Verify that our client has access. - def check_authorization(indirection, method, key, params) - authconfig.check_authorization(indirection, method, key, params) + def check_authorization(method, path, params) + authconfig.check_authorization(method, path, params) end end end diff --git a/lib/puppet/network/http.rb b/lib/puppet/network/http.rb index e4d421f9c..f15e679f2 100644 --- a/lib/puppet/network/http.rb +++ b/lib/puppet/network/http.rb @@ -3,6 +3,7 @@ module Puppet::Network::HTTP HEADER_PUPPET_VERSION = "X-Puppet-Version" require 'puppet/network/http/error' + require 'puppet/network/http/route' require 'puppet/network/http/api' require 'puppet/network/http/api/v1' require 'puppet/network/http/api/v2' @@ -10,5 +11,4 @@ module Puppet::Network::HTTP require 'puppet/network/http/response' require 'puppet/network/http/request' require 'puppet/network/http/memory_response' - require 'puppet/network/http/route' end diff --git a/lib/puppet/network/http/api/v1.rb b/lib/puppet/network/http/api/v1.rb index ea8bb591a..926408ac9 100644 --- a/lib/puppet/network/http/api/v1.rb +++ b/lib/puppet/network/http/api/v1.rb @@ -33,7 +33,7 @@ class Puppet::Network::HTTP::API::V1 indirection_name, method, key, params = uri2indirection(request.method, request.path, request.params) certificate = request.client_cert - check_authorization(indirection_name, method, key, params) + check_authorization(method, "/#{indirection_name}/#{key}", params) indirection = Puppet::Indirector::Indirection.instance(indirection_name.to_sym) raise ArgumentError, "Could not find indirection '#{indirection_name}'" unless indirection diff --git a/lib/puppet/network/http/api/v2.rb b/lib/puppet/network/http/api/v2.rb index 895626a46..509ee0a98 100644 --- a/lib/puppet/network/http/api/v2.rb +++ b/lib/puppet/network/http/api/v2.rb @@ -1,6 +1,12 @@ -class Puppet::Network::HTTP::API::V2 +module Puppet::Network::HTTP::API::V2 + require 'puppet/network/http/api/v2/environments' + require 'puppet/network/http/api/v2/authorization' + + NOT_FOUND = Puppet::Network::HTTP::Route. + path(/.*/). + any(lambda { |req, res| raise Puppet::Network::HTTP::Handler::HTTPNotFoundError, req.path }) + def self.routes - [Puppet::Network::HTTP::Route.path(%r{^/v2/environments$}).get( - lambda { |req, res| raise Puppet::Network::HTTP::Error::HTTPNotAuthorizedError, "You shall not pass!" })] + [Puppet::Network::HTTP::Route.path(%r{^/v2\.0}).get(Authorization.new).chain(Environments::ROUTE, NOT_FOUND)] end end diff --git a/lib/puppet/network/http/api/v2/authorization.rb b/lib/puppet/network/http/api/v2/authorization.rb new file mode 100644 index 000000000..ceb7cda54 --- /dev/null +++ b/lib/puppet/network/http/api/v2/authorization.rb @@ -0,0 +1,13 @@ +class Puppet::Network::HTTP::API::V2::Authorization + include Puppet::Network::Authorization + + def call(request, response) + raise Puppet::Network::HTTP::Error::HTTPNotAuthorizedError, "Only GET requests are authorized for V2 endpoints" unless request.method == "GET" + + begin + check_authorization(:find, request.path, request.params) + rescue Puppet::Network::AuthorizationError => e + raise Puppet::Network::HTTP::Error::HTTPNotAuthorizedError, e.message + end + end +end diff --git a/lib/puppet/network/http/api/v2/environments.rb b/lib/puppet/network/http/api/v2/environments.rb new file mode 100644 index 000000000..efe4f5c97 --- /dev/null +++ b/lib/puppet/network/http/api/v2/environments.rb @@ -0,0 +1,4 @@ +class Puppet::Network::HTTP::API::V2::Environments + ROUTE = Puppet::Network::HTTP::Route.path(%r{^/environments$}).get( + lambda { |req, res| raise Puppet::Network::HTTP::Error::HTTPNotAuthorizedError, "You shall not pass!" }) +end diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb index 35ae56f3f..6fd44ede1 100644 --- a/lib/puppet/network/http/handler.rb +++ b/lib/puppet/network/http/handler.rb @@ -50,7 +50,7 @@ module Puppet::Network::HTTP::Handler request_method = http_method(request) request_path = path(request) - new_request = Puppet::Network::HTTP::Request.new(request_headers, request_params, request_method, request_path, client_cert(request), body(request)) + new_request = Puppet::Network::HTTP::Request.new(request_headers, request_params, request_method, request_path, request_path, client_cert(request), body(request)) response[Puppet::Network::HTTP::HEADER_PUPPET_VERSION] = Puppet.version diff --git a/lib/puppet/network/http/memory_response.rb b/lib/puppet/network/http/memory_response.rb index af770f1c2..8171cfbb7 100644 --- a/lib/puppet/network/http/memory_response.rb +++ b/lib/puppet/network/http/memory_response.rb @@ -1,9 +1,13 @@ class Puppet::Network::HTTP::MemoryResponse attr_reader :code, :type, :body + def initialize + @body = "" + end + def respond_with(code, type, body) @code = code @type = type - @body = body + @body += body end end diff --git a/lib/puppet/network/http/request.rb b/lib/puppet/network/http/request.rb index 2b45e07a7..21f96a155 100644 --- a/lib/puppet/network/http/request.rb +++ b/lib/puppet/network/http/request.rb @@ -1,4 +1,4 @@ -Puppet::Network::HTTP::Request = Struct.new(:headers, :params, :method, :path, :client_cert, :body) do +Puppet::Network::HTTP::Request = Struct.new(:headers, :params, :method, :path, :routing_path, :client_cert, :body) do def self.from_hash(hash) symbol_members = members.collect(&:intern) unknown = hash.keys - symbol_members @@ -9,6 +9,10 @@ Puppet::Network::HTTP::Request = Struct.new(:headers, :params, :method, :path, : end end + def route_into(prefix) + self.class.new(headers, params, method, path, routing_path.sub(prefix, ''), client_cert, body) + end + def format if header = headers['content-type'] header.gsub!(/\s*;.*$/,'') # strip any charset diff --git a/lib/puppet/network/http/route.rb b/lib/puppet/network/http/route.rb index 744de1886..b3b42d2d4 100644 --- a/lib/puppet/network/http/route.rb +++ b/lib/puppet/network/http/route.rb @@ -18,6 +18,7 @@ class Puppet::Network::HTTP::Route :POST => [MethodNotAllowedHandler], :PUT => [MethodNotAllowedHandler] } + @chained = [] end def get(*handlers) @@ -26,12 +27,12 @@ class Puppet::Network::HTTP::Route end def head(*handlers) - @method_handlers[:GET] = handlers + @method_handlers[:HEAD] = handlers return self end def options(*handlers) - @method_handlers[:GET] = handlers + @method_handlers[:OPTIONS] = handlers return self end @@ -41,7 +42,7 @@ class Puppet::Network::HTTP::Route end def put(*handlers) - @method_handlers[:POST] = handlers + @method_handlers[:PUT] = handlers return self end @@ -52,12 +53,17 @@ class Puppet::Network::HTTP::Route return self end + def chain(*routes) + @chained = routes + self + end + def matches?(request) Puppet.debug("Evaluating match for #{self.inspect}") - if @path_matcher.match(request.path) + if m = @path_matcher.match(request.routing_path) return true else - Puppet.debug("Did not match path (#{request.path.inspect})") + Puppet.debug("Did not match path (#{request.routing_path.inspect})") end return false end @@ -66,6 +72,11 @@ class Puppet::Network::HTTP::Route @method_handlers[request.method.upcase.intern].each do |handler| handler.call(request, response) end + + subrequest = request.route_into(@path_matcher.match(request.routing_path).to_s) + if chained_route = @chained.find { |route| route.matches?(subrequest) } + chained_route.process(subrequest, response) + end end def inspect diff --git a/lib/puppet/network/rights.rb b/lib/puppet/network/rights.rb index f7420a90e..d81473bd1 100644 --- a/lib/puppet/network/rights.rb +++ b/lib/puppet/network/rights.rb @@ -13,7 +13,7 @@ class Rights !is_forbidden_and_why?(name, :node => args[0], :ip => args[1]) end - def is_request_forbidden_and_why?(indirection, method, key, params) + def is_request_forbidden_and_why?(method, path, params) methods_to_check = if method == :head # :head is ok if either :find or :save is ok. [:find, :save] @@ -21,7 +21,7 @@ class Rights [method] end authorization_failure_exceptions = methods_to_check.map do |method| - is_forbidden_and_why?("/#{indirection}/#{key}", params.merge({:method => method})) + is_forbidden_and_why?(path, params.merge({:method => method})) end if authorization_failure_exceptions.include? nil # One of the methods we checked is ok, therefore this request is ok. diff --git a/spec/integration/network/authconfig_spec.rb b/spec/integration/network/authconfig_spec.rb index 9db0c1099..cdd9a2559 100644 --- a/spec/integration/network/authconfig_spec.rb +++ b/spec/integration/network/authconfig_spec.rb @@ -7,7 +7,7 @@ RSpec::Matchers.define :allow do |params| match do |auth| begin - auth.check_authorization(params[0], params[1], params[2], params[3]) + auth.check_authorization(*params) true rescue Puppet::Network::AuthorizationError false @@ -15,11 +15,11 @@ RSpec::Matchers.define :allow do |params| end failure_message_for_should do |instance| - "expected #{params[3][:node]}/#{params[3][:ip]} to be allowed" + "expected #{params[2][:node]}/#{params[2][:ip]} to be allowed" end failure_message_for_should_not do |instance| - "expected #{params[3][:node]}/#{params[3][:ip]} to be forbidden" + "expected #{params[2][:node]}/#{params[2][:ip]} to be forbidden" end end @@ -54,7 +54,7 @@ describe Puppet::Network::AuthConfig do :ip => '10.1.1.1', :authenticated => true }.merge(args) - ['test', :find, args[:key], args] + [:find, "/test/#{args[:key]}", args] end describe "allow" do diff --git a/spec/unit/network/authconfig_spec.rb b/spec/unit/network/authconfig_spec.rb index fbca5dfb5..12ff1c0d3 100755 --- a/spec/unit/network/authconfig_spec.rb +++ b/spec/unit/network/authconfig_spec.rb @@ -101,9 +101,9 @@ describe Puppet::Network::AuthConfig do :authenticated => true } - Puppet::Network::Rights.any_instance.expects(:is_request_forbidden_and_why?).with("path", :save, "to/resource", params) + Puppet::Network::Rights.any_instance.expects(:is_request_forbidden_and_why?).with(:save, "/path/to/resource", params) - described_class.new.check_authorization("path", :save, "to/resource", params) + described_class.new.check_authorization(:save, "/path/to/resource", params) end end end diff --git a/spec/unit/network/http/api/v2/authorization_spec.rb b/spec/unit/network/http/api/v2/authorization_spec.rb new file mode 100644 index 000000000..ecdb76192 --- /dev/null +++ b/spec/unit/network/http/api/v2/authorization_spec.rb @@ -0,0 +1,57 @@ +require 'spec_helper' + +require 'puppet/network/http' + +describe Puppet::Network::HTTP::API::V2::Authorization do + HTTP = Puppet::Network::HTTP + + let(:response) { HTTP::MemoryResponse.new } + let(:authz) { HTTP::API::V2::Authorization.new } + + it "only authorizes GET requests" do + request = HTTP::Request.from_hash({ + :method => "POST" + }) + + expect do + authz.call(request, response) + end.to raise_error(HTTP::Error::HTTPNotAuthorizedError) + end + + it "accepts v2 api requests that match allowed authconfig entries" do + request = HTTP::Request.from_hash({ + :path => "/v2.0/environments", + :method => "GET", + :params => { :authenticated => true, :node => "testing", :ip => "127.0.0.1" } + }) + + authz.stubs(:authconfig).returns(Puppet::Network::AuthConfigParser.new(<<-AUTH).parse) +path /v2.0/environments +method find +allow * + AUTH + + expect do + authz.call(request, response) + end.to_not raise_error + end + + it "rejects v2 api requests that are disallowed by authconfig entries" do + request = HTTP::Request.from_hash({ + :path => "/v2.0/environments", + :method => "GET", + :params => { :node => "testing", :ip => "127.0.0.1" } + }) + + authz.stubs(:authconfig).returns(Puppet::Network::AuthConfigParser.new(<<-AUTH).parse) +path /v2.0/environments +method find +auth any +deny testing + AUTH + + expect do + authz.call(request, response) + end.to raise_error(HTTP::Error::HTTPNotAuthorizedError, /Forbidden request/) + end +end diff --git a/spec/unit/network/http/route_spec.rb b/spec/unit/network/http/route_spec.rb index beb268910..04cc0e875 100644 --- a/spec/unit/network/http/route_spec.rb +++ b/spec/unit/network/http/route_spec.rb @@ -5,16 +5,19 @@ require 'puppet/indirector_testing' require 'puppet/network/http' describe Puppet::Network::HTTP::Route do - def new_request(method, path) - Puppet::Network::HTTP::Request.new({'accept' => 'pson', 'content-type' => 'text/yaml'}, {}, method, path, nil, nil) + def request(method, path) + Puppet::Network::HTTP::Request.from_hash({ + :method => method, + :path => path, + :routing_path => path }) end def respond(text) lambda { |req, res| res.respond_with(200, "text/plain", text) } end - let(:req) { new_request("GET", "/vtest/foo") } - let(:res) { Puppet::Network::HTTP::Response.new(TestingHandler.new(), {}) } + let(:req) { request("GET", "/vtest/foo") } + let(:res) { Puppet::Network::HTTP::MemoryResponse.new } describe "an HTTP Route" do it "can match a request" do @@ -35,7 +38,7 @@ describe Puppet::Network::HTTP::Route do route.process(req, res) - expect(res.fields[:body]).to eq("used") + expect(res.body).to eq("used") end it "calls the method handlers in turn" do @@ -58,26 +61,15 @@ describe Puppet::Network::HTTP::Route do end.to raise_error(Puppet::Network::HTTP::Error::HTTPNotAuthorizedError) expect(ignored_called).to be_false end - end - class TestingHandler - include Puppet::Network::HTTP::Handler - def initialize(* routes) - register(routes) - end + it "chains to other routes after calling its handlers" do + inner_route = Puppet::Network::HTTP::Route.path(%r{^/inner}).any(respond("inner")) + unused_inner_route = Puppet::Network::HTTP::Route.path(%r{^/unused_inner}).any(respond("unused")) - def set_content_type(response, format) - end + top_route = Puppet::Network::HTTP::Route.path(%r{^/vtest}).any(respond("top")).chain(unused_inner_route, inner_route) + top_route.process(request("GET", "/vtest/inner"), res) - def set_response(response, body, status = 200) - response[:body] = body - response[:status] = status - end - end - - class Puppet::Network::HTTP::Response - def fields - return @response + expect(res.body).to eq("topinner") end end end diff --git a/spec/unit/network/rights_spec.rb b/spec/unit/network/rights_spec.rb index 539f7a808..5440ae19d 100755 --- a/spec/unit/network/rights_spec.rb +++ b/spec/unit/network/rights_spec.rb @@ -16,13 +16,13 @@ describe Puppet::Network::Rights do right.allow("*") right.restrict_method(allowed_method) right.restrict_authenticated(:any) - rights.is_request_forbidden_and_why?(:indirection_name, :head, "key", {}).should == nil + rights.is_request_forbidden_and_why?(:head, "/indirection_name/key", {}).should == nil end end it "should disallow the request if neither :find nor :save is allowed" do rights = Puppet::Network::Rights.new - why_forbidden = rights.is_request_forbidden_and_why?(:indirection_name, :head, "key", {}) + why_forbidden = rights.is_request_forbidden_and_why?(:head, "/indirection_name/key", {}) why_forbidden.should be_instance_of(Puppet::Network::AuthorizationError) why_forbidden.to_s.should == "Forbidden request: access to /indirection_name/key [find]" end From 1e04f6e64e54251c59e6ce2798a32e4b2ab21288 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 10 Jan 2014 15:16:15 -0800 Subject: [PATCH 422/800] (PUP-1151) Hard code return of only production environment This plumbs in the first step and getting the list of environments returned. At the moment it is hard coded to only ever return the prodution environment. --- .../environment/can_enumerate_environments.rb | 30 ++++++++- .../network/http/api/v2/environments.rb | 33 +++++++++- lib/puppet/network/http/request.rb | 8 ++- lib/puppet/node/environment.rb | 7 +-- .../network/http/api/v2/environments_spec.rb | 63 +++++++++++++++++++ 5 files changed, 131 insertions(+), 10 deletions(-) create mode 100644 spec/unit/network/http/api/v2/environments_spec.rb diff --git a/acceptance/tests/environment/can_enumerate_environments.rb b/acceptance/tests/environment/can_enumerate_environments.rb index 0ea1bb82a..fcb1cc154 100644 --- a/acceptance/tests/environment/can_enumerate_environments.rb +++ b/acceptance/tests/environment/can_enumerate_environments.rb @@ -1,9 +1,35 @@ test_name "Can enumerate environments via an HTTP endpoint" +def master_port(agent) + setting_on(agent, "agent", "masterport") +end + +def setting_on(host, section, name) + on(host, puppet("config", "pring", name, "--section", section)).stdout.chomp +end + +def curl_master_from(agent, path, headers = '', &block) + url = "https://#{master}:#{master_port(agent)}#{path}" + cert_path = setting_on(agent, "agent", "hostcert") + key_path = setting_on(agent, "agent", "hostprivkey") + curl_base = "curl -g --cert \"#{cert_path}\" --key \"#{key_path}\" -k -H '#{headers}'" + + on agent, "#{curl_base} '#{url}'", &block +end + with_puppet_running_on(master, {}) do agents.each do |agent| - on agent, "curl -ksv https://#{master}:8140/v2/environments", :acceptable_exit_codes => [0,7] do - assert_match(/< HTTP\/1\.\d 403/, stderr) + step "Ensure that an unauthenticated client cannot access the environments list" do + on agent, "curl -ksv https://#{master}:#{master_port(agent)}/v2.0/environments", :acceptable_exit_codes => [0,7] do + assert_match(/< HTTP\/1\.\d 403/, stderr) + end + end + + step "Ensure that an authenticated client can retrieve the list of environments" do + curl_master_from(agent, '/v2.0/environments') do + data = JSON.parse(stdout) + assert_equal(["production"], data["environments"].keys) + end end end end diff --git a/lib/puppet/network/http/api/v2/environments.rb b/lib/puppet/network/http/api/v2/environments.rb index efe4f5c97..c28d43a8b 100644 --- a/lib/puppet/network/http/api/v2/environments.rb +++ b/lib/puppet/network/http/api/v2/environments.rb @@ -1,4 +1,35 @@ +require 'json' + class Puppet::Network::HTTP::API::V2::Environments + def initialize(env_loader) + @env_loader = env_loader + end + + def call(request, response) + response.respond_with(200, "application/json", JSON.dump({ + "search_path" => @env_loader.search_paths, + "environments" => Hash[@env_loader.list.collect do |env| + [env.name, { + "modules" => Hash[env.modules.collect do |mod| + [mod.name, { + "version" => mod.version + }] + end] + }] + end] + })) + end + + class OnlyProductionLoder + def search_paths + [] + end + + def list + [Puppet::Node::Environment.new(:production)] + end + end + ROUTE = Puppet::Network::HTTP::Route.path(%r{^/environments$}).get( - lambda { |req, res| raise Puppet::Network::HTTP::Error::HTTPNotAuthorizedError, "You shall not pass!" }) + new(OnlyProductionLoder.new)) end diff --git a/lib/puppet/network/http/request.rb b/lib/puppet/network/http/request.rb index 21f96a155..3936508cc 100644 --- a/lib/puppet/network/http/request.rb +++ b/lib/puppet/network/http/request.rb @@ -3,7 +3,13 @@ Puppet::Network::HTTP::Request = Struct.new(:headers, :params, :method, :path, : symbol_members = members.collect(&:intern) unknown = hash.keys - symbol_members if unknown.empty? - new(*(symbol_members.collect { |m| hash[m] })) + new(hash[:headers] || {}, + hash[:params] || {}, + hash[:method] || "GET", + hash[:path], + hash[:routing_path] || hash[:path], + hash[:client_cert], + hash[:body]) else raise ArgumentError, "Unknown arguments: #{unknown.collect(&:inspect).join(', ')}" end diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb index b44ab01b6..02d46c986 100644 --- a/lib/puppet/node/environment.rb +++ b/lib/puppet/node/environment.rb @@ -75,12 +75,6 @@ class Puppet::Node::Environment include Puppet::Util::Cacher - # @!attribute seen - # @scope class - # @api private - # @return [Hash] All memoized environments - @seen = {} - # Create a new environment with the given name, or return an existing one # # The environment class memoizes instances so that attempts to instantiate an @@ -111,6 +105,7 @@ class Puppet::Node::Environment symbol = name.to_sym + @seen ||= {} return @seen[symbol] if @seen[symbol] obj = self.allocate diff --git a/spec/unit/network/http/api/v2/environments_spec.rb b/spec/unit/network/http/api/v2/environments_spec.rb new file mode 100644 index 000000000..ae1607f80 --- /dev/null +++ b/spec/unit/network/http/api/v2/environments_spec.rb @@ -0,0 +1,63 @@ +require 'spec_helper' + +require 'puppet/node/environment' +require 'puppet/network/http' + +describe Puppet::Network::HTTP::API::V2::Environments do + it "responds with all of the available environments environments" do + handler = Puppet::Network::HTTP::API::V2::Environments.new(TestingEnvironmentLoader.new) + response = Puppet::Network::HTTP::MemoryResponse.new + + handler.call(Puppet::Network::HTTP::Request.from_hash(:headers => { 'accept' => 'application/json' }), response) + + expect(response.code).to eq(200) + expect(response.type).to eq("application/json") + expect(JSON.parse(response.body)).to eq({ + "search_path" => ["file:///fake"], + "environments" => { + "production" => { + "modules" => { + "testing" => { + "version" => "1.2.3" + } + } + } + } + }) + end + + it "the response conforms to the environments schema" do + handler = Puppet::Network::HTTP::API::V2::Environments.new(TestingEnvironmentLoader.new) + response = Puppet::Network::HTTP::MemoryResponse.new + + handler.call(Puppet::Network::HTTP::Request.from_hash(:headers => { 'accept' => 'application/json' }), response) + + expect(response.body).to validates_against('api/schemas/environments.json') + end + + matcher :validates_against do |schema_file| + match do |json| + schema = JSON.parse(File.read(schema_file)) + JSON::Validator.validate!(JSON_META_SCHEMA, schema) + JSON::Validator.validate!(schema, json) + end + end + + class TestingEnvironmentLoader + def search_paths + ["file:///fake"] + end + + def list + [FakeEnvironment.new(:production)] + end + end + + class FakeEnvironment < Puppet::Node::Environment + def modules + fake_module = Puppet::Module.new('testing', '/somewhere/on/disk', self) + fake_module.version = "1.2.3" + [fake_module] + end + end +end From fc42c6478ade714aab4e6e168be9184951959da8 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 10 Jan 2014 16:18:16 -0800 Subject: [PATCH 423/800] (PUP-1151) Inject source of environments This pulls out the source of the environments from the V2::Environments code and instead injects it from a "provide"d handler. The "provide" method on V2 allows a handler to be constructed lazily, which is needed when it needs to be instantiated with information that isn't known until request time (in this case the source of the environments). --- lib/puppet/environments.rb | 11 ++++++++ lib/puppet/network/http/api/v2.rb | 28 ++++++++++++++++--- .../network/http/api/v2/environments.rb | 13 --------- lib/puppet/network/http/handler.rb | 19 +++++++++---- lib/puppet/network/http/route.rb | 10 +++++-- 5 files changed, 57 insertions(+), 24 deletions(-) create mode 100644 lib/puppet/environments.rb diff --git a/lib/puppet/environments.rb b/lib/puppet/environments.rb new file mode 100644 index 000000000..f0b6cbca4 --- /dev/null +++ b/lib/puppet/environments.rb @@ -0,0 +1,11 @@ +module Puppet::Environments + class OnlyProduction + def search_paths + [] + end + + def list + [Puppet::Node::Environment.new(:production)] + end + end +end diff --git a/lib/puppet/network/http/api/v2.rb b/lib/puppet/network/http/api/v2.rb index 509ee0a98..08e2f8efc 100644 --- a/lib/puppet/network/http/api/v2.rb +++ b/lib/puppet/network/http/api/v2.rb @@ -2,11 +2,31 @@ module Puppet::Network::HTTP::API::V2 require 'puppet/network/http/api/v2/environments' require 'puppet/network/http/api/v2/authorization' + def self.routes + [path(%r{^/v2\.0}). + get(Authorization.new). + chain(ENVIRONMENTS, NOT_FOUND)] + end + + private + + def self.path(path) + Puppet::Network::HTTP::Route.path(path) + end + + def self.provide(&block) + lambda do |request, response| + block.call.call(request, response) + end + end + NOT_FOUND = Puppet::Network::HTTP::Route. path(/.*/). - any(lambda { |req, res| raise Puppet::Network::HTTP::Handler::HTTPNotFoundError, req.path }) + any(lambda do |req, res| + raise Puppet::Network::HTTP::Handler::HTTPNotFoundError, req.path + end) - def self.routes - [Puppet::Network::HTTP::Route.path(%r{^/v2\.0}).get(Authorization.new).chain(Environments::ROUTE, NOT_FOUND)] - end + ENVIRONMENTS = path(%r{^/environments$}).get(provide do + Environments.new(Puppet::Context.lookup(:environments)) + end) end diff --git a/lib/puppet/network/http/api/v2/environments.rb b/lib/puppet/network/http/api/v2/environments.rb index c28d43a8b..2be83bb72 100644 --- a/lib/puppet/network/http/api/v2/environments.rb +++ b/lib/puppet/network/http/api/v2/environments.rb @@ -19,17 +19,4 @@ class Puppet::Network::HTTP::API::V2::Environments end] })) end - - class OnlyProductionLoder - def search_paths - [] - end - - def list - [Puppet::Node::Environment.new(:production)] - end - end - - ROUTE = Puppet::Network::HTTP::Route.path(%r{^/environments$}).get( - new(OnlyProductionLoder.new)) end diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb index 6fd44ede1..e3366f8e8 100644 --- a/lib/puppet/network/http/handler.rb +++ b/lib/puppet/network/http/handler.rb @@ -1,6 +1,7 @@ module Puppet::Network::HTTP end +require 'puppet/environments' require 'puppet/network/http' require 'puppet/network/http/api/v1' require 'puppet/network/authentication' @@ -57,11 +58,13 @@ module Puppet::Network::HTTP::Handler configure_profiler(request_headers, request_params) warn_if_near_expiration(new_request.client_cert) - Puppet::Util::Profiler.profile("Processed request #{request_method} #{request_path}") do - if route = @routes.find { |route| route.matches?(new_request) } - route.process(new_request, new_response) - else - raise Puppet::Network::HTTP::Error::HTTPNotFoundError, "No route for #{new_request.method} #{new_request.path}" + Puppet::Context.override(request_bindings()) do + Puppet::Util::Profiler.profile("Processed request #{request_method} #{request_path}") do + if route = @routes.find { |route| route.matches?(new_request) } + route.process(new_request, new_response) + else + raise Puppet::Network::HTTP::Error::HTTPNotFoundError, "No route for #{new_request.method} #{new_request.path}" + end end end @@ -177,4 +180,10 @@ module Puppet::Network::HTTP::Handler Puppet::Util::Profiler.current = Puppet::Util::Profiler::NONE end end + + def request_bindings + { + :environments => Puppet::Environments::OnlyProduction.new + } + end end diff --git a/lib/puppet/network/http/route.rb b/lib/puppet/network/http/route.rb index b3b42d2d4..3e8d89a68 100644 --- a/lib/puppet/network/http/route.rb +++ b/lib/puppet/network/http/route.rb @@ -60,7 +60,7 @@ class Puppet::Network::HTTP::Route def matches?(request) Puppet.debug("Evaluating match for #{self.inspect}") - if m = @path_matcher.match(request.routing_path) + if match(request.routing_path) return true else Puppet.debug("Did not match path (#{request.routing_path.inspect})") @@ -73,7 +73,7 @@ class Puppet::Network::HTTP::Route handler.call(request, response) end - subrequest = request.route_into(@path_matcher.match(request.routing_path).to_s) + subrequest = request.route_into(match(request.routing_path).to_s) if chained_route = @chained.find { |route| route.matches?(subrequest) } chained_route.process(subrequest, response) end @@ -82,4 +82,10 @@ class Puppet::Network::HTTP::Route def inspect "Route #{@path_matcher.inspect}" end + + private + + def match(path) + @path_matcher.match(path) + end end From 7cdb196076ede8ebf7b80df5a9727ebda7c97cc6 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Mon, 13 Jan 2014 17:27:13 -0800 Subject: [PATCH 424/800] (PUP-1151) Add access to environments in auth.conf The default auth.conf that we send out with puppet also needs to include the default policy. --- conf/auth.conf | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/conf/auth.conf b/conf/auth.conf index b31906bae..96f078c48 100644 --- a/conf/auth.conf +++ b/conf/auth.conf @@ -110,6 +110,10 @@ auth any method find, save allow * +path /v2.0/environments +method find +allow * + # deny everything else; this ACL is not strictly necessary, but # illustrates the default policy. path / From 6dcd9b11e4e8a3980e83b393d6e30dc39e086bbe Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Mon, 13 Jan 2014 17:57:41 -0800 Subject: [PATCH 425/800] (PUP-1151) Ensure that json is installed for tests With the introduction of the new v2.0 api using JSON we need to make sure that all of our test platforms have JSON installed. Some already have it (because we only support 1.9 on windows), others have to have it installed. --- acceptance/setup/git/pre-suite/000_EnvSetup.rb | 10 +++++++++- .../tests/environment/can_enumerate_environments.rb | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/acceptance/setup/git/pre-suite/000_EnvSetup.rb b/acceptance/setup/git/pre-suite/000_EnvSetup.rb index 889d6cc4b..79fb45e8f 100644 --- a/acceptance/setup/git/pre-suite/000_EnvSetup.rb +++ b/acceptance/setup/git/pre-suite/000_EnvSetup.rb @@ -11,24 +11,29 @@ PACKAGES = { :redhat => [ 'git', 'ruby', + 'rubygem-json', ], :debian => [ ['git', 'git-core'], 'ruby', + 'libjson-ruby', ], :solaris => [ ['git', 'developer/versioning/git'], ['ruby', 'runtime/ruby-18'], + # there isn't a package for json, so it is installed later via gems ], :windows => [ 'git', + # there isn't a need for json on windows because it is bundled in ruby 1.9 ], } install_packages_on(hosts, PACKAGES, :check_if_exists => true) hosts.each do |host| - if host['platform'] =~ /windows/ + case host['platform'] + when /windows/ step "#{host} Install ruby from git" install_from_git(host, "/opt/puppet-git-repos", :name => 'puppet-win32-ruby', :path => 'git://github.com/puppetlabs/puppet-win32-ruby') on host, 'cd /opt/puppet-git-repos/puppet-win32-ruby; cp -r ruby/* /' @@ -36,5 +41,8 @@ hosts.each do |host| on host, 'cd /lib; icacls ruby /reset /T' on host, 'ruby --version' on host, 'cmd /c gem list' + when /solaris/ + step "#{host} Install json from rubygems" + on host, 'gem install json' end end diff --git a/acceptance/tests/environment/can_enumerate_environments.rb b/acceptance/tests/environment/can_enumerate_environments.rb index fcb1cc154..e938b1a2e 100644 --- a/acceptance/tests/environment/can_enumerate_environments.rb +++ b/acceptance/tests/environment/can_enumerate_environments.rb @@ -5,7 +5,7 @@ def master_port(agent) end def setting_on(host, section, name) - on(host, puppet("config", "pring", name, "--section", section)).stdout.chomp + on(host, puppet("config", "print", name, "--section", section)).stdout.chomp end def curl_master_from(agent, path, headers = '', &block) From 2e7ba15d32590b2e3c36542c2727641bad7ac134 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Tue, 14 Jan 2014 11:00:33 -0800 Subject: [PATCH 426/800] (PUP-1151) Consolidate JSON schema validation This adds a new matcher to the JSONMatchers to validate json against a given json-schema. All of the json schema validation now goes through this one matcher, which also handles skipping the checks on windows. --- spec/lib/matchers/json.rb | 256 +++++++++++------- spec/spec_helper.rb | 12 - spec/unit/configurer/fact_handler_spec.rb | 21 +- spec/unit/file_serving/metadata_spec.rb | 34 +-- spec/unit/indirector/request_spec.rb | 1 + .../network/http/api/v2/environments_spec.rb | 13 +- spec/unit/node/facts_spec.rb | 17 +- spec/unit/node_spec.rb | 17 +- spec/unit/resource/catalog_spec.rb | 46 ++-- spec/unit/resource/type_spec.rb | 26 +- spec/unit/ssl/host_spec.rb | 26 +- spec/unit/status_spec.rb | 10 +- spec/unit/transaction/report_spec.rb | 20 +- spec/unit/util/instrumentation/data_spec.rb | 2 + .../instrumentation/indirection_probe_spec.rb | 2 + .../util/instrumentation/listener_spec.rb | 1 + 16 files changed, 232 insertions(+), 272 deletions(-) diff --git a/spec/lib/matchers/json.rb b/spec/lib/matchers/json.rb index 798e4cd21..dbeebf992 100644 --- a/spec/lib/matchers/json.rb +++ b/spec/lib/matchers/json.rb @@ -1,111 +1,167 @@ -RSpec::Matchers.define :set_json_attribute do |*attributes| - def format - @format ||= Puppet::Network::FormatHandler.format('pson') - end - - chain :to do |value| - @value = value - end - - def json(instance) - PSON.parse(instance.to_pson) - end - - def attr_value(attrs, instance) - attrs = attrs.dup - hash = json(instance)['data'] - while attrs.length > 0 - name = attrs.shift - hash = hash[name] +module JSONMatchers + class SetJsonAttribute + def initialize(attributes) + @attributes = attributes + end + + def format + @format ||= Puppet::Network::FormatHandler.format('pson') + end + + def json(instance) + PSON.parse(instance.to_pson) + end + + def attr_value(attrs, instance) + attrs = attrs.dup + hash = json(instance)['data'] + while attrs.length > 0 + name = attrs.shift + hash = hash[name] + end + hash + end + + def to(value) + @value = value + self + end + + def matches?(instance) + result = attr_value(@attributes, instance) + if @value + result == @value + else + ! result.nil? + end + end + + def failure_message_for_should(instance) + if @value + "expected #{instance.inspect} to set #{@attributes.inspect} to #{@value.inspect}; got #{attr_value(@attributes, instance).inspect}" + else + "expected #{instance.inspect} to set #{@attributes.inspect} but was nil" + end + end + + def failure_message_for_should_not(instance) + if @value + "expected #{instance.inspect} not to set #{@attributes.inspect} to #{@value.inspect}" + else + "expected #{instance.inspect} not to set #{@attributes.inspect} to nil" + end end - hash end - match do |instance| - result = attr_value(attributes, instance) - if @value - result == @value + class SetJsonDocumentTypeTo + def initialize(type) + @type = type + end + + def format + @format ||= Puppet::Network::FormatHandler.format('pson') + end + + def matches?(instance) + json(instance)['document_type'] == @type + end + + def json(instance) + PSON.parse(instance.to_pson) + end + + def failure_message_for_should(instance) + "expected #{instance.inspect} to set document_type to #{@type.inspect}; got #{json(instance)['document_type'].inspect}" + end + + def failure_message_for_should_not(instance) + "expected #{instance.inspect} not to set document_type to #{@type.inspect}" + end + end + + class ReadJsonAttribute + def initialize(attribute) + @attribute = attribute + end + + def format + @format ||= Puppet::Network::FormatHandler.format('pson') + end + + def from(value) + @json = value + self + end + + def as(as) + @value = as + self + end + + def matches?(klass) + raise "Must specify json with 'from'" unless @json + + @instance = format.intern(klass, @json) + if @value + @instance.send(@attribute) == @value + else + ! @instance.send(@attribute).nil? + end + end + + def failure_message_for_should(klass) + if @value + "expected #{klass} to read #{@attribute} from #{@json} as #{@value.inspect}; got #{@instance.send(@attribute).inspect}" + else + "expected #{klass} to read #{@attribute} from #{@json} but was nil" + end + end + + def failure_message_for_should_not(klass) + if @value + "expected #{klass} not to set #{@attribute} to #{@value}" + else + "expected #{klass} not to set #{@attribute} to nil" + end + end + end + + if !Puppet.features.microsoft_windows? + require 'json' + require 'json-schema' + + class SchemaMatcher + JSON_META_SCHEMA = JSON.parse(File.read('api/schemas/json-meta-schema.json')) + + def initialize(schema) + @schema = schema + end + + def matches?(json) + JSON::Validator.validate!(JSON_META_SCHEMA, @schema) + JSON::Validator.validate!(@schema, json) + end + end + end + + def validate_against(schema_file) + if Puppet.features.microsoft_windows? + pending("Schema checks cannot be done on windows because of json-schema problems") else - ! result.nil? + schema = JSON.parse(File.read(schema_file)) + SchemaMatcher.new(schema) end end - failure_message_for_should do |instance| - if @value - "expected #{instance.inspect} to set #{attributes.inspect} to #{@value.inspect}; got #{attr_value(attributes, instance).inspect}" - else - "expected #{instance.inspect} to set #{attributes.inspect} but was nil" - end + def set_json_attribute(*attributes) + SetJsonAttribute.new(attributes) end - failure_message_for_should_not do |instance| - if @value - "expected #{instance.inspect} not to set #{attributes.inspect} to #{@value.inspect}" - else - "expected #{instance.inspect} not to set #{attributes.inspect} to nil" - end - end -end - -RSpec::Matchers.define :set_json_document_type_to do |type| - def format - @format ||= Puppet::Network::FormatHandler.format('pson') - end - - match do |instance| - json(instance)['document_type'] == type - end - - def json(instance) - PSON.parse(instance.to_pson) - end - - failure_message_for_should do |instance| - "expected #{instance.inspect} to set document_type to #{type.inspect}; got #{json(instance)['document_type'].inspect}" - end - - failure_message_for_should_not do |instance| - "expected #{instance.inspect} not to set document_type to #{type.inspect}" - end -end - -RSpec::Matchers.define :read_json_attribute do |attribute| - def format - @format ||= Puppet::Network::FormatHandler.format('pson') - end - - chain :from do |value| - @json = value - end - - chain :as do |as| - @value = as - end - - match do |klass| - raise "Must specify json with 'from'" unless @json - - @instance = format.intern(klass, @json) - if @value - @instance.send(attribute) == @value - else - ! @instance.send(attribute).nil? - end - end - - failure_message_for_should do |klass| - if @value - "expected #{klass} to read #{attribute} from #{@json} as #{@value.inspect}; got #{@instance.send(attribute).inspect}" - else - "expected #{klass} to read #{attribute} from #{@json} but was nil" - end - end - - failure_message_for_should_not do |klass| - if @value - "expected #{klass} not to set #{attribute} to #{@value}" - else - "expected #{klass} not to set #{attribute} to nil" - end + def set_json_document_type_to(type) + SetJsonDocumentTypeTo.new(type) + end + + def read_json_attribute(attribute) + ReadJsonAttribute.new(attribute) end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index de1368951..9b7af8552 100755 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -43,18 +43,6 @@ Pathname.glob("#{dir}/shared_behaviours/**/*.rb") do |behaviour| require behaviour.relative_path_from(Pathname.new(dir)) end -# various spec tests now use json schema validation -# the json-schema gem doesn't support windows -if not Puppet.features.microsoft_windows? - require 'json' - require 'json-schema' - - JSON_META_SCHEMA = JSON.parse(File.read(File.join(File.dirname(__FILE__), '../api/schemas/json-meta-schema.json'))) - - # FACTS_SCHEMA is shared across two spec files so promote constant to here - FACTS_SCHEMA = JSON.parse(File.read(File.join(File.dirname(__FILE__), '../api/schemas/facts.json'))) -end - RSpec.configure do |config| include PuppetSpec::Fixtures diff --git a/spec/unit/configurer/fact_handler_spec.rb b/spec/unit/configurer/fact_handler_spec.rb index a92ad7d4f..3e3c033ae 100755 --- a/spec/unit/configurer/fact_handler_spec.rb +++ b/spec/unit/configurer/fact_handler_spec.rb @@ -2,16 +2,7 @@ require 'spec_helper' require 'puppet/configurer' require 'puppet/configurer/fact_handler' - -# the json-schema gem doesn't support windows -if not Puppet.features.microsoft_windows? - describe "catalog facts schema" do - it "should validate against the json meta-schema" do - JSON::Validator.validate!(JSON_META_SCHEMA, FACTS_SCHEMA) - end - end - - end +require 'matchers/json' class FactHandlerTester include Puppet::Configurer::FactHandler @@ -22,6 +13,8 @@ class FactHandlerTester end describe Puppet::Configurer::FactHandler do + include JSONMatchers + before :each do @facthandler = FactHandlerTester.new Puppet::Node::Facts.indirection.terminus_class = :memory @@ -85,15 +78,11 @@ describe Puppet::Configurer::FactHandler do @facthandler.facts_for_uploading.should == {:facts_format => :pson, :facts => text} end - def validate_json_for_facts(catalog_facts) - JSON::Validator.validate!(FACTS_SCHEMA, catalog_facts) - end - - it "should generate valid facts data against the facts schema", :unless => Puppet.features.microsoft_windows? do + it "should generate valid facts data against the facts schema" do facts = Puppet::Node::Facts.new(Puppet[:node_name_value], 'my_name_fact' => 'other_node_name') Puppet::Node::Facts.indirection.save(facts) - validate_json_for_facts(CGI.unescape(@facthandler.facts_for_uploading[:facts])) + expect(CGI.unescape(@facthandler.facts_for_uploading[:facts])).to validate_against('api/schemas/facts.json') end end diff --git a/spec/unit/file_serving/metadata_spec.rb b/spec/unit/file_serving/metadata_spec.rb index 306e560c9..4dccb780c 100755 --- a/spec/unit/file_serving/metadata_spec.rb +++ b/spec/unit/file_serving/metadata_spec.rb @@ -1,22 +1,7 @@ #! /usr/bin/env ruby require 'spec_helper' - require 'puppet/file_serving/metadata' - -# the json-schema gem doesn't support windows -if not Puppet.features.microsoft_windows? - FILE_METADATA_SCHEMA = JSON.parse(File.read(File.join(File.dirname(__FILE__), '../../../api/schemas/file_metadata.json'))) - - describe "catalog schema" do - it "should validate against the json meta-schema" do - JSON::Validator.validate!(JSON_META_SCHEMA, FILE_METADATA_SCHEMA) - end - end - - def validate_json_for_file_metadata(file_metadata) - JSON::Validator.validate!(FILE_METADATA_SCHEMA, file_metadata.to_pson) - end -end +require 'matchers/json' describe Puppet::FileServing::Metadata do let(:foobar) { File.expand_path('/foo/bar') } @@ -103,6 +88,7 @@ describe Puppet::FileServing::Metadata do end describe Puppet::FileServing::Metadata do + include JSONMatchers include PuppetSpec::Files shared_examples_for "metadata collector" do @@ -154,8 +140,8 @@ describe Puppet::FileServing::Metadata do end end - it "should validate against the schema", :unless => Puppet.features.microsoft_windows? do - validate_json_for_file_metadata(metadata) + it "should validate against the schema" do + expect(metadata.to_pson).to validate_against('api/schemas/file_metadata.json') end end @@ -179,9 +165,9 @@ describe Puppet::FileServing::Metadata do metadata.checksum.should == "{ctime}#{time}" end - it "should validate against the schema", :unless => Puppet.features.microsoft_windows? do + it "should validate against the schema" do metadata.collect - validate_json_for_file_metadata(metadata) + expect(metadata.to_pson).to validate_against('api/schemas/file_metadata.json') end end end @@ -214,8 +200,8 @@ describe Puppet::FileServing::Metadata do metadata.destination.should == target end - it "should validate against the schema", :unless => Puppet.features.microsoft_windows? do - validate_json_for_file_metadata(metadata) + it "should validate against the schema" do + expect(metadata.to_pson).to validate_against('api/schemas/file_metadata.json') end end end @@ -248,8 +234,8 @@ describe Puppet::FileServing::Metadata do proc { metadata.collect}.should raise_error(Errno::ENOENT) end - it "should validate against the schema", :unless => Puppet.features.microsoft_windows? do - validate_json_for_file_metadata(metadata) + it "should validate against the schema" do + expect(metadata.to_pson).to validate_against('api/schemas/file_metadata.json') end end end diff --git a/spec/unit/indirector/request_spec.rb b/spec/unit/indirector/request_spec.rb index c43c46709..859d5ba0b 100755 --- a/spec/unit/indirector/request_spec.rb +++ b/spec/unit/indirector/request_spec.rb @@ -5,6 +5,7 @@ require 'puppet/indirector/request' require 'puppet/util/pson' describe Puppet::Indirector::Request do + include JSONMatchers describe "when registering the document type" do it "should register its document type with JSON" do diff --git a/spec/unit/network/http/api/v2/environments_spec.rb b/spec/unit/network/http/api/v2/environments_spec.rb index ae1607f80..adeea57be 100644 --- a/spec/unit/network/http/api/v2/environments_spec.rb +++ b/spec/unit/network/http/api/v2/environments_spec.rb @@ -2,8 +2,11 @@ require 'spec_helper' require 'puppet/node/environment' require 'puppet/network/http' +require 'matchers/json' describe Puppet::Network::HTTP::API::V2::Environments do + include JSONMatchers + it "responds with all of the available environments environments" do handler = Puppet::Network::HTTP::API::V2::Environments.new(TestingEnvironmentLoader.new) response = Puppet::Network::HTTP::MemoryResponse.new @@ -32,15 +35,7 @@ describe Puppet::Network::HTTP::API::V2::Environments do handler.call(Puppet::Network::HTTP::Request.from_hash(:headers => { 'accept' => 'application/json' }), response) - expect(response.body).to validates_against('api/schemas/environments.json') - end - - matcher :validates_against do |schema_file| - match do |json| - schema = JSON.parse(File.read(schema_file)) - JSON::Validator.validate!(JSON_META_SCHEMA, schema) - JSON::Validator.validate!(schema, json) - end + expect(response.body).to validate_against('api/schemas/environments.json') end class TestingEnvironmentLoader diff --git a/spec/unit/node/facts_spec.rb b/spec/unit/node/facts_spec.rb index 55e3dbbf6..f4b50b37e 100755 --- a/spec/unit/node/facts_spec.rb +++ b/spec/unit/node/facts_spec.rb @@ -1,18 +1,11 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/node/facts' - -# the json-schema gem doesn't support windows -if not Puppet.features.microsoft_windows? - describe "catalog facts schema" do - it "should validate against the json meta-schema" do - JSON::Validator.validate!(JSON_META_SCHEMA, FACTS_SCHEMA) - end - end - -end +require 'matchers/json' describe Puppet::Node::Facts, "when indirecting" do + include JSONMatchers + before do @facts = Puppet::Node::Facts.new("me") end @@ -156,12 +149,12 @@ describe Puppet::Node::Facts, "when indirecting" do result['expiration'].should == facts.expiration.iso8601(9) end - it "should generate valid facts data against the facts schema", :unless => Puppet.features.microsoft_windows? do + it "should generate valid facts data against the facts schema" do Time.stubs(:now).returns(@timestamp) facts = Puppet::Node::Facts.new("foo", {'a' => 1, 'b' => 2, 'c' => 3}) facts.expiration = @expiration - JSON::Validator.validate!(FACTS_SCHEMA, facts.to_pson) + expect(facts.to_pson).to validate_against('api/schemas/facts.json') end it "should not include nil values" do diff --git a/spec/unit/node_spec.rb b/spec/unit/node_spec.rb index f8691ed9d..ae0a33f62 100755 --- a/spec/unit/node_spec.rb +++ b/spec/unit/node_spec.rb @@ -2,18 +2,9 @@ require 'spec_helper' require 'matchers/json' -# the json-schema gem doesn't support windows -if not Puppet.features.microsoft_windows? - NODE_SCHEMA = JSON.parse(File.read(File.join(File.dirname(__FILE__), '../../api/schemas/node.json'))) - - describe "node schema" do - it "should validate against the json meta-schema" do - JSON::Validator.validate!(JSON_META_SCHEMA, NODE_SCHEMA) - end - end -end - describe Puppet::Node do + include JSONMatchers + it "should register its document type as Node" do PSON.registered_document_types["Node"].should equal(Puppet::Node) end @@ -87,7 +78,7 @@ describe Puppet::Node do :classes => ['erth', 'aiu'], :parameters => {"hostname"=>"food"} ) - JSON::Validator.validate!(NODE_SCHEMA, node.to_pson) + expect(node.to_pson).to validate_against('api/schemas/node.json') end it "when missing optional parameters validates against the node json schema", :unless => Puppet.features.microsoft_windows? do @@ -95,7 +86,7 @@ describe Puppet::Node do node = Puppet::Node.new("hello", :environment => 'kjhgrg' ) - JSON::Validator.validate!(NODE_SCHEMA, node.to_pson) + expect(node.to_pson).to validate_against('api/schemas/node.json') end describe "when converting to json" do diff --git a/spec/unit/resource/catalog_spec.rb b/spec/unit/resource/catalog_spec.rb index dd88e8e20..8b042c8fb 100755 --- a/spec/unit/resource/catalog_spec.rb +++ b/spec/unit/resource/catalog_spec.rb @@ -2,19 +2,10 @@ require 'spec_helper' require 'puppet_spec/compiler' -# the json-schema gem doesn't support windows -if not Puppet.features.microsoft_windows? - CATALOG_SCHEMA = JSON.parse(File.read(File.join(File.dirname(__FILE__), '../../../api/schemas/catalog.json'))) - - describe "catalog schema" do - it "should validate against the json meta-schema" do - JSON::Validator.validate!(JSON_META_SCHEMA, CATALOG_SCHEMA) - end - end - -end +require 'matchers/json' describe Puppet::Resource::Catalog, "when compiling" do + include JSONMatchers include PuppetSpec::Files before do @@ -736,43 +727,40 @@ describe Puppet::Resource::Catalog, "when compiling" do end describe Puppet::Resource::Catalog, "when converting a resource catalog to pson" do + include JSONMatchers include PuppetSpec::Compiler - def validate_json_for_catalog(catalog) - JSON::Validator.validate!(CATALOG_SCHEMA, catalog.to_pson) - end - - it "should validate an empty catalog against the schema", :unless => Puppet.features.microsoft_windows? do + it "should validate an empty catalog against the schema" do empty_catalog = compile_to_catalog("") - validate_json_for_catalog(empty_catalog) + expect(empty_catalog.to_pson).to validate_against('api/schemas/catalog.json') end - it "should validate a noop catalog against the schema", :unless => Puppet.features.microsoft_windows? do + it "should validate a noop catalog against the schema" do noop_catalog = compile_to_catalog("create_resources('file', {})") - validate_json_for_catalog(noop_catalog) + expect(noop_catalog.to_pson).to validate_against('api/schemas/catalog.json') end - it "should validate a single resource catalog against the schema", :unless => Puppet.features.microsoft_windows? do + it "should validate a single resource catalog against the schema" do catalog = compile_to_catalog("create_resources('file', {'/etc/foo'=>{'ensure'=>'present'}})") - validate_json_for_catalog(catalog) + expect(catalog.to_pson).to validate_against('api/schemas/catalog.json') end - it "should validate a virtual resource catalog against the schema", :unless => Puppet.features.microsoft_windows? do + it "should validate a virtual resource catalog against the schema" do catalog = compile_to_catalog("create_resources('@file', {'/etc/foo'=>{'ensure'=>'present'}})\nrealize(File['/etc/foo'])") - validate_json_for_catalog(catalog) + expect(catalog.to_pson).to validate_against('api/schemas/catalog.json') end - it "should validate a single exported resource catalog against the schema", :unless => Puppet.features.microsoft_windows? do + it "should validate a single exported resource catalog against the schema" do catalog = compile_to_catalog("create_resources('@@file', {'/etc/foo'=>{'ensure'=>'present'}})") - validate_json_for_catalog(catalog) + expect(catalog.to_pson).to validate_against('api/schemas/catalog.json') end - it "should validate a two resource catalog against the schema", :unless => Puppet.features.microsoft_windows? do + it "should validate a two resource catalog against the schema" do catalog = compile_to_catalog("create_resources('notify', {'foo'=>{'message'=>'one'}, 'bar'=>{'message'=>'two'}})") - validate_json_for_catalog(catalog) + expect(catalog.to_pson).to validate_against('api/schemas/catalog.json') end - it "should validate a two parameter class catalog against the schema", :unless => Puppet.features.microsoft_windows? do + it "should validate a two parameter class catalog against the schema" do catalog = compile_to_catalog(<<-MANIFEST) class multi_param_class ($one, $two) { notify {'foo': @@ -785,7 +773,7 @@ describe Puppet::Resource::Catalog, "when converting a resource catalog to pson" two => 'world', } MANIFEST - validate_json_for_catalog(catalog) + expect(catalog.to_pson).to validate_against('api/schemas/catalog.json') end end diff --git a/spec/unit/resource/type_spec.rb b/spec/unit/resource/type_spec.rb index 1b5a4727b..1ed1ebcd6 100755 --- a/spec/unit/resource/type_spec.rb +++ b/spec/unit/resource/type_spec.rb @@ -2,19 +2,11 @@ require 'spec_helper' require 'puppet/resource/type' -# the json-schema gem doesn't support windows -if not Puppet.features.microsoft_windows? - RESOURCE_TYPE_SCHEMA = JSON.parse(File.read(File.join(File.dirname(__FILE__), '../../../api/schemas/resource_type.json'))) - - describe "resource type schema" do - it "should validate against the json meta-schema" do - JSON::Validator.validate!(JSON_META_SCHEMA, RESOURCE_TYPE_SCHEMA) - end - end - -end +require 'matchers/json' describe Puppet::Resource::Type do + include JSONMatchers + it "should have a 'name' attribute" do Puppet::Resource::Type.new(:hostclass, "foo").name.should == "foo" end @@ -42,10 +34,6 @@ describe Puppet::Resource::Type do end describe "when converting to json" do - def validate_json_for_type(type) - JSON::Validator.validate!(RESOURCE_TYPE_SCHEMA, type.to_pson) - end - before do @type = Puppet::Resource::Type.new(:hostclass, "foo") end @@ -63,18 +51,18 @@ describe Puppet::Resource::Type do double_convert.type.should == @type.type end - it "should validate with only name and kind", :unless => Puppet.features.microsoft_windows? do - validate_json_for_type(@type) + it "should validate with only name and kind" do + expect(@type.to_pson).to validate_against('api/schemas/resource_type.json') end - it "should validate with all fields set", :unless => Puppet.features.microsoft_windows? do + it "should validate with all fields set" do @type.set_arguments("one" => nil, "two" => "foo") @type.line = 100 @type.doc = "A weird type" @type.file = "/etc/manifests/thing.pp" @type.parent = "one::two" - validate_json_for_type(@type) + expect(@type.to_pson).to validate_against('api/schemas/resource_type.json') end it "should include any arguments" do diff --git a/spec/unit/ssl/host_spec.rb b/spec/unit/ssl/host_spec.rb index 3b341cc4e..a9ab9b51e 100755 --- a/spec/unit/ssl/host_spec.rb +++ b/spec/unit/ssl/host_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' require 'puppet/ssl/host' +require 'matchers/json' def base_pson_comparison(result, pson_hash) result["fingerprint"].should == pson_hash["fingerprint"] @@ -9,24 +10,10 @@ def base_pson_comparison(result, pson_hash) result["state"].should == pson_hash["desired_state"] end -# the json-schema gem doesn't support windows -if not Puppet.features.microsoft_windows? - HOST_SCHEMA = JSON.parse(File.read(File.join(File.dirname(__FILE__), '../../../api/schemas/host.json'))) - - describe "host schema" do - it "should validate against the json meta-schema" do - JSON::Validator.validate!(JSON_META_SCHEMA, HOST_SCHEMA) - end - end -end - describe Puppet::SSL::Host do + include JSONMatchers include PuppetSpec::Files - def validate_json_for_host(host) - JSON::Validator.validate!(HOST_SCHEMA, host.to_pson) - end - before do Puppet::SSL::Host.indirection.terminus_class = :file @@ -855,9 +842,10 @@ describe Puppet::SSL::Host do base_pson_comparison result, pson_hash end - it "should validate against the schema", :unless => Puppet.features.microsoft_windows? do + it "should validate against the schema" do host.generate_certificate_request - validate_json_for_host(host) + + expect(host.to_pson).to validate_against('api/schemas/host.json') end describe "explicit fingerprints" do @@ -903,8 +891,8 @@ describe Puppet::SSL::Host do result["dns_alt_names"].should == pson_hash["desired_alt_names"] end - it "should validate against the schema", :unless => Puppet.features.microsoft_windows? do - validate_json_for_host(host) + it "should validate against the schema" do + expect(host.to_pson).to validate_against('api/schemas/host.json') end end end diff --git a/spec/unit/status_spec.rb b/spec/unit/status_spec.rb index c21eed094..e71faa666 100755 --- a/spec/unit/status_spec.rb +++ b/spec/unit/status_spec.rb @@ -1,7 +1,11 @@ #! /usr/bin/env ruby require 'spec_helper' +require 'matchers/json' + describe Puppet::Status do + include JSONMatchers + it "should implement find" do Puppet::Status.indirection.find( :default ).should be_is_a(Puppet::Status) Puppet::Status.indirection.find( :default ).status["is_alive"].should == true @@ -38,12 +42,10 @@ describe Puppet::Status do new_status.should equal_attributes_of(status) end - it "serializes to PSON that conforms to the status schema", :unless => Puppet.features.microsoft_windows? do - schema = JSON.parse(File.read(File.join(File.dirname(__FILE__), '../../api/schemas/status.json'))) + it "serializes to PSON that conforms to the status schema" do status = Puppet::Status.new status.version = Puppet.version - JSON::Validator.validate!(JSON_META_SCHEMA, schema) - JSON::Validator.validate!(schema, status.render('pson')) + expect(status.render('pson')).to validate_against('api/schemas/status.json') end end diff --git a/spec/unit/transaction/report_spec.rb b/spec/unit/transaction/report_spec.rb index 661349dab..32dd280fb 100755 --- a/spec/unit/transaction/report_spec.rb +++ b/spec/unit/transaction/report_spec.rb @@ -3,22 +3,12 @@ require 'spec_helper' require 'puppet' require 'puppet/transaction/report' - -# the json-schema gem doesn't support windows -if not Puppet.features.microsoft_windows? - REPORT_SCHEMA_URI = File.join(File.dirname(__FILE__), '../../../api/schemas/report.json') - REPORT_SCHEMA = JSON.parse(File.read(REPORT_SCHEMA_URI)) - - describe "report schema" do - it "should validate against the json meta-schema" do - JSON::Validator.validate!(JSON_META_SCHEMA, REPORT_SCHEMA) - end - end - -end +require 'matchers/json' describe Puppet::Transaction::Report do + include JSONMatchers include PuppetSpec::Files + before do Puppet::Util::Storage.stubs(:store) end @@ -405,10 +395,10 @@ describe Puppet::Transaction::Report do expect_equivalent_reports(tripped, report) end - it "generates pson which validates against the report schema", :unless => Puppet.features.microsoft_windows? do + it "generates pson which validates against the report schema" do Puppet[:report_serialization_format] = "pson" report = generate_report - JSON::Validator.validate!(REPORT_SCHEMA, report.render) + expect(report.render).to validate_against('api/schemas/report.json') end it "can make a round trip through yaml" do diff --git a/spec/unit/util/instrumentation/data_spec.rb b/spec/unit/util/instrumentation/data_spec.rb index 847ff9ad2..d8a6b32d0 100755 --- a/spec/unit/util/instrumentation/data_spec.rb +++ b/spec/unit/util/instrumentation/data_spec.rb @@ -6,6 +6,8 @@ require 'puppet/util/instrumentation' require 'puppet/util/instrumentation/data' describe Puppet::Util::Instrumentation::Data do + include JSONMatchers + Puppet::Util::Instrumentation::Data before(:each) do diff --git a/spec/unit/util/instrumentation/indirection_probe_spec.rb b/spec/unit/util/instrumentation/indirection_probe_spec.rb index 337777ca6..d06849e5e 100644 --- a/spec/unit/util/instrumentation/indirection_probe_spec.rb +++ b/spec/unit/util/instrumentation/indirection_probe_spec.rb @@ -6,6 +6,8 @@ require 'puppet/util/instrumentation' require 'puppet/util/instrumentation/indirection_probe' describe Puppet::Util::Instrumentation::IndirectionProbe do + include JSONMatchers + Puppet::Util::Instrumentation::IndirectionProbe it "should indirect instrumentation_probe" do diff --git a/spec/unit/util/instrumentation/listener_spec.rb b/spec/unit/util/instrumentation/listener_spec.rb index 5072c4fe5..90c76978f 100755 --- a/spec/unit/util/instrumentation/listener_spec.rb +++ b/spec/unit/util/instrumentation/listener_spec.rb @@ -7,6 +7,7 @@ require 'puppet/util/instrumentation' require 'puppet/util/instrumentation/listener' describe Puppet::Util::Instrumentation::Listener do + include JSONMatchers Listener = Puppet::Util::Instrumentation::Listener From 44ae49548ee3d4ceed5e2f7320a0f11d7cc4decf Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Tue, 14 Jan 2014 12:47:14 -0800 Subject: [PATCH 427/800] (pup-923) Add a fedora 20 config --- acceptance/config/nodes/fedora20.yaml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 acceptance/config/nodes/fedora20.yaml diff --git a/acceptance/config/nodes/fedora20.yaml b/acceptance/config/nodes/fedora20.yaml new file mode 100644 index 000000000..9a7bb3a80 --- /dev/null +++ b/acceptance/config/nodes/fedora20.yaml @@ -0,0 +1,20 @@ +HOSTS: + master: + roles: + - master + - agent + platform: fedora-20-x86_64 + hypervisor: vcloud + template: Delivery/Quality Assurance/Templates/vCloud/fedora-20-x86_64 + agent: + roles: + - agent + platform: fedora-20-i386 + hypervisor: vcloud + template: Delivery/Quality Assurance/Templates/vCloud/fedora-20-i386 +CONFIG: + filecount: 12 + datastore: instance0 + resourcepool: delivery/Quality Assurance/FOSS/Dynamic + folder: Delivery/Quality Assurance/FOSS/Dynamic + pooling_api: http://vcloud.delivery.puppetlabs.net/ From 8142d090e21a5659a56205a3be3c4fcb71415fe8 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Tue, 14 Jan 2014 13:22:40 -0800 Subject: [PATCH 428/800] (PUP-1151) Convert to cygwin paths on windows The curl command on windows doesn't handle windows path (C:\foo\bar), so we need to convert these to posix paths using cygpath. --- .../tests/environment/can_enumerate_environments.rb | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/acceptance/tests/environment/can_enumerate_environments.rb b/acceptance/tests/environment/can_enumerate_environments.rb index e938b1a2e..0b30bc3a0 100644 --- a/acceptance/tests/environment/can_enumerate_environments.rb +++ b/acceptance/tests/environment/can_enumerate_environments.rb @@ -8,10 +8,18 @@ def setting_on(host, section, name) on(host, puppet("config", "print", name, "--section", section)).stdout.chomp end +def full_path(host, path) + if host['platform'] =~ /win/ + on(host, "cygpath '#{path}'").stdout.chomp + else + path + end +end + def curl_master_from(agent, path, headers = '', &block) url = "https://#{master}:#{master_port(agent)}#{path}" - cert_path = setting_on(agent, "agent", "hostcert") - key_path = setting_on(agent, "agent", "hostprivkey") + cert_path = full_path(agent, setting_on(agent, "agent", "hostcert")) + key_path = full_path(agent, setting_on(agent, "agent", "hostprivkey")) curl_base = "curl -g --cert \"#{cert_path}\" --key \"#{key_path}\" -k -H '#{headers}'" on agent, "#{curl_base} '#{url}'", &block From d6f67ea54019175bc153261bb7dbc71159d286e4 Mon Sep 17 00:00:00 2001 From: Glenn Pratt Date: Tue, 14 Jan 2014 16:08:39 -0600 Subject: [PATCH 429/800] (maint) Fix unused argument and variable in file_spec.rb --- spec/unit/file_system/file_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/unit/file_system/file_spec.rb b/spec/unit/file_system/file_spec.rb index 52adf14f4..79ea4fb42 100644 --- a/spec/unit/file_system/file_spec.rb +++ b/spec/unit/file_system/file_spec.rb @@ -79,7 +79,7 @@ describe "Puppet::FileSystem" do def increment_counter_in_multiple_processes(file, num_procs, options) children = [] - 5.times do |number| + num_procs.times do children << Kernel.fork do Puppet::FileSystem.exclusive_open(file, 0660, options) do |fh| fh.rewind From 403a9a398c52382bb1982f610f7588144ff11ccc Mon Sep 17 00:00:00 2001 From: "Ethan J. Brown" Date: Mon, 13 Jan 2014 15:36:31 -0800 Subject: [PATCH 430/800] (PUP-266) Ensure ADSI Group SIDs may be looked up - Previously, calling Puppet::Util::ADSI::Group.exists? for a well-known group SID would return false, because the WinNT:// style URI used to find the group would not examine / lookup the SID, but would instead create a broken Uri that the underlying OS could not understand - Now, the given group name is checked to see if it's a SID before the Uri is constructed, and a SID style Uri is created where appropriate --- lib/puppet/util/adsi.rb | 15 +++++++++ spec/unit/provider/group/windows_adsi_spec.rb | 1 + spec/unit/provider/user/windows_adsi_spec.rb | 1 + spec/unit/util/adsi_spec.rb | 31 ++++++++++++++++++- 4 files changed, 47 insertions(+), 1 deletion(-) diff --git a/lib/puppet/util/adsi.rb b/lib/puppet/util/adsi.rb index 98639eabd..941413ca1 100644 --- a/lib/puppet/util/adsi.rb +++ b/lib/puppet/util/adsi.rb @@ -41,6 +41,17 @@ module Puppet::Util::ADSI "winmgmts:{impersonationLevel=impersonate}!//#{host}/root/cimv2" end + def sid_uri_safe(sid) + return Puppet::Util::ADSI.sid_uri(sid) if sid.kind_of?(Win32::Security::SID) + + begin + sid = Win32::Security::SID.new(Win32::Security::SID.string_to_sid(sid)) + Puppet::Util::ADSI.sid_uri(sid) + rescue + return nil + end + end + def sid_uri(sid) raise Puppet::Error.new( "Must use a valid SID object" ) if !sid.kind_of?(Win32::Security::SID) "WinNT://#{sid.to_s}" @@ -96,6 +107,8 @@ module Puppet::Util::ADSI end def self.uri(name, host = '.') + if sid_uri = Puppet::Util::ADSI.sid_uri_safe(name) then return sid_uri end + host = '.' if ['NT AUTHORITY', 'BUILTIN', Socket.gethostname].include?(host) Puppet::Util::ADSI.uri(name, 'user', host) @@ -240,6 +253,8 @@ module Puppet::Util::ADSI end def self.uri(name, host = '.') + if sid_uri = Puppet::Util::ADSI.sid_uri_safe(name) then return sid_uri end + Puppet::Util::ADSI.uri(name, 'group', host) end diff --git a/spec/unit/provider/group/windows_adsi_spec.rb b/spec/unit/provider/group/windows_adsi_spec.rb index d28601172..a7de859da 100644 --- a/spec/unit/provider/group/windows_adsi_spec.rb +++ b/spec/unit/provider/group/windows_adsi_spec.rb @@ -138,6 +138,7 @@ describe Puppet::Type.type(:group).provider(:windows_adsi) do end it "should be able to test whether a group exists" do + Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil) Puppet::Util::ADSI.stubs(:connect).returns stub('connection') provider.should be_exists diff --git a/spec/unit/provider/user/windows_adsi_spec.rb b/spec/unit/provider/user/windows_adsi_spec.rb index c25ccaf95..8d3ed1d0a 100755 --- a/spec/unit/provider/user/windows_adsi_spec.rb +++ b/spec/unit/provider/user/windows_adsi_spec.rb @@ -122,6 +122,7 @@ describe Puppet::Type.type(:user).provider(:windows_adsi) do end it 'should be able to test whether a user exists' do + Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil) Puppet::Util::ADSI.stubs(:connect).returns stub('connection') provider.should be_exists diff --git a/spec/unit/util/adsi_spec.rb b/spec/unit/util/adsi_spec.rb index 974aba79c..5e9eb129d 100755 --- a/spec/unit/util/adsi_spec.rb +++ b/spec/unit/util/adsi_spec.rb @@ -73,10 +73,12 @@ describe Puppet::Util::ADSI do let(:domain_username) { "#{domain}\\#{username}"} it "should generate the correct URI" do + Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil) Puppet::Util::ADSI::User.uri(username).should == "WinNT://./#{username},user" end it "should generate the correct URI for a user with a domain" do + Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil) Puppet::Util::ADSI::User.uri(username, domain).should == "WinNT://#{domain}/#{username},user" end @@ -107,15 +109,26 @@ describe Puppet::Util::ADSI do end it "should be able to check the existence of a user" do + Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil) Puppet::Util::ADSI.expects(:connect).with("WinNT://./#{username},user").returns connection Puppet::Util::ADSI::User.exists?(username).should be_true end it "should be able to check the existence of a domain user" do + Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil) Puppet::Util::ADSI.expects(:connect).with("WinNT://#{domain}/#{username},user").returns connection Puppet::Util::ADSI::User.exists?(domain_username).should be_true end + it "should be able to confirm the existence of a user with a well-known SID", + :if => Puppet.features.microsoft_windows? do + + system_user = Win32::Security::SID::LocalSystem + # ensure that the underlying OS is queried here + Puppet::Util::ADSI.unstub(:connect) + Puppet::Util::ADSI::User.exists?(system_user).should be_true + end + it "should be able to delete a user" do connection.expects(:Delete).with('user', username) @@ -123,6 +136,8 @@ describe Puppet::Util::ADSI do end it "should return an enumeration of IADsUser wrapped objects" do + Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil) + name = 'Administrator' wmi_users = [stub('WMI', :name => name)] Puppet::Util::ADSI.expects(:execquery).with('select name from win32_useraccount where localaccount = "TRUE"').returns(wmi_users) @@ -174,7 +189,7 @@ describe Puppet::Util::ADSI do user.password = 'pwd' end - it "should generate the correct URI",:if => Puppet.features.microsoft_windows? do + it "should generate the correct URI", :if => Puppet.features.microsoft_windows? do Puppet::Util::Windows::Security.stubs(:octet_string_to_sid_object).returns(sid) user.uri.should == "WinNT://testcomputername/#{username},user" end @@ -321,11 +336,13 @@ describe Puppet::Util::ADSI do end it "should generate the correct URI" do + Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil) group.uri.should == "WinNT://./#{groupname},group" end end it "should generate the correct URI" do + Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil) Puppet::Util::ADSI::Group.uri("people").should == "WinNT://./people,group" end @@ -342,11 +359,21 @@ describe Puppet::Util::ADSI do end it "should be able to confirm the existence of a group" do + Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil) Puppet::Util::ADSI.expects(:connect).with("WinNT://./#{groupname},group").returns connection Puppet::Util::ADSI::Group.exists?(groupname).should be_true end + it "should be able to confirm the existence of a group with a well-known SID", + :if => Puppet.features.microsoft_windows? do + + service_group = Win32::Security::SID::Service + # ensure that the underlying OS is queried here + Puppet::Util::ADSI.unstub(:connect) + Puppet::Util::ADSI::Group.exists?(service_group).should be_true + end + it "should be able to delete a group" do connection.expects(:Delete).with('group', groupname) @@ -354,6 +381,8 @@ describe Puppet::Util::ADSI do end it "should return an enumeration of IADsGroup wrapped objects" do + Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil) + name = 'Administrators' wmi_groups = [stub('WMI', :name => name)] Puppet::Util::ADSI.expects(:execquery).with('select name from win32_group where localaccount = "TRUE"').returns(wmi_groups) From d17b9386e36c34256492a417905650ac7e3d7f77 Mon Sep 17 00:00:00 2001 From: "Ethan J. Brown" Date: Thu, 12 Dec 2013 15:43:25 -0800 Subject: [PATCH 431/800] (PUP-266) Puppet manages group and owner on config - Previously, Puppet would ignore the configuration in defaults.rb for setting a group / owner on a given file or directory -- even when the Windows machine was running with administrative permissions - Since Windows now has the capability of properly setting a group and owner, allow Windows to manage these permissions when appropriate - The existing Puppet::Settings::FileSettings::Service class depends on looking up the appropriate default service accounts for use in a number of locations. To fall in line with this usage pattern, "Administrators" is set as the default owner and the "Service" group S-1-5-6 is set as the default group. This special OS managed group includes all accounts with the logon as a service token privilege. --- ...apply_file_metadata_specified_in_config.rb | 3 - lib/puppet/settings/file_setting.rb | 30 +++++-- spec/integration/defaults_spec.rb | 87 +++++++++++++++++++ spec/unit/settings/file_setting_spec.rb | 36 ++------ spec/unit/settings_spec.rb | 3 +- spec/unit/util/windows/sid_spec.rb | 6 ++ 6 files changed, 128 insertions(+), 37 deletions(-) diff --git a/acceptance/tests/config/apply_file_metadata_specified_in_config.rb b/acceptance/tests/config/apply_file_metadata_specified_in_config.rb index 932ec74f3..de9ccc0f6 100644 --- a/acceptance/tests/config/apply_file_metadata_specified_in_config.rb +++ b/acceptance/tests/config/apply_file_metadata_specified_in_config.rb @@ -1,8 +1,5 @@ test_name "#17371 file metadata specified in puppet.conf needs to be applied" -# when owner/group works on windows for settings, this confine should be removed. -confine :except, :platform => 'windows' - require 'puppet/acceptance/temp_file_utils' extend Puppet::Acceptance::TempFileUtils initialize_temp_dirs() diff --git a/lib/puppet/settings/file_setting.rb b/lib/puppet/settings/file_setting.rb index e20767374..791b66464 100644 --- a/lib/puppet/settings/file_setting.rb +++ b/lib/puppet/settings/file_setting.rb @@ -16,7 +16,11 @@ class Puppet::Settings::FileSetting < Puppet::Settings::StringSetting # @api private class Root def value - "root" + if Puppet.features.microsoft_windows? + return Win32::Security::SID::BuiltinAdministrators + end + + 'root' end end @@ -75,7 +79,7 @@ class Puppet::Settings::FileSetting < Puppet::Settings::StringSetting when "service" # Group falls back to `nil` because we cannot assume that a "root" group exists. # Some systems have root group, others have wheel, others have something else. - Service.new(:group, nil, @settings, :service_group_available?) + Service.new(:group, service_group_fallback, @settings, :service_group_available?) else unknown_value(':group', value) end @@ -88,7 +92,7 @@ class Puppet::Settings::FileSetting < Puppet::Settings::StringSetting when "root" Root.new when "service" - Service.new(:user, "root", @settings, :service_user_available?) + Service.new(:user, service_owner_fallback, @settings, :service_user_available?) else unknown_value(':owner', value) end @@ -155,8 +159,7 @@ class Puppet::Settings::FileSetting < Puppet::Settings::StringSetting resource[:mode] = mode end - # REMIND fails on Windows because chown/chgrp functionality not supported yet - if Puppet.features.root? and !Puppet.features.microsoft_windows? + if Puppet.features.root? resource[:owner] = self.owner if self.owner resource[:group] = self.group if self.group end @@ -198,7 +201,22 @@ class Puppet::Settings::FileSetting < Puppet::Settings::StringSetting end end - private +private + def service_owner_fallback + if Puppet.features.microsoft_windows? + return Win32::Security::SID::BuiltinAdministrators + end + 'root' + end + + def service_group_fallback + # Service group includes all security principals that have logged on as a service. + # Membership is controlled by the operating system. + if Puppet.features.microsoft_windows? + return Win32::Security::SID::Service + end + nil + end def file Puppet::FileSystem.pathname(value) diff --git a/spec/integration/defaults_spec.rb b/spec/integration/defaults_spec.rb index 8c8432b6f..25507f4f2 100755 --- a/spec/integration/defaults_spec.rb +++ b/spec/integration/defaults_spec.rb @@ -312,4 +312,91 @@ describe "Puppet defaults" do Puppet.settings.setting(:agent_catalog_run_lockfile).should be_a Puppet::Settings::StringSetting end end + + describe "properly enforces permissions on" do + describe "vardir" do + before :each do + # Puppet will create this dir with requested permissions + @vardir = Puppet::FileSystem.pathname(Puppet.settings[:vardir]) + @path = @vardir.to_s + + # instruct Puppet to load settings, which resets perms + Puppet.settings.use(:main) + end + + describe "on POSIX", :if => Puppet.features.root? && + !Puppet.features.microsoft_windows? do + + it "with a group and owner of service" do + provider = Puppet::Type.type(:file).provider(:posix).new + + stat = Puppet::FileSystem.stat(@path) + provider.uid2name(stat.uid).should == Puppet.settings[:user] + provider.gid2name(stat.gid).should == Puppet.settings[:group] + end + end + + describe "on Windows", :if => Puppet.features.root? && + Puppet.features.microsoft_windows? do + + it "with a group and owner of service" do + sd = Puppet::Util::Windows::Security.get_security_descriptor(@path) + + sd.owner.should == Win32::Security::SID::BuiltinAdministrators + sd.group.should == Win32::Security::SID::Service + end + end + end + + # really, any file that relies on 'root' would do here + # we simply want to verify that a file with 'root' owner and mode works + describe "classfile" do + + before :each do + @classfile = Puppet::FileSystem.pathname(Puppet.settings[:classfile]) + @path = @classfile.to_s + + FileUtils.mkdir_p(@classfile.parent) + FileUtils.touch(@classfile) + end + + describe "on POSIX", :if => Puppet.features.root? && + !Puppet.features.microsoft_windows? do + + it "with an owner of root" do + provider = Puppet::Type.type(:file).provider(:posix).new + + # change owner to nobody + nobody_id = provider.name2uid('nobody') + File.chown(nobody_id, nil, @path) + + # instruct Puppet to load settings, which resets perms + Puppet.settings.use(:agent) + + provider.uid2name(classfile.stat.uid).should == 'root' + provider.mode.should == '640' + end + end + + describe "on Windows", :if => Puppet.features.root? && + Puppet.features.microsoft_windows? do + + it "with an owner of Administrators" do + # change the owner to everyone + sd = Puppet::Util::Windows::Security.get_security_descriptor(@path) + sd.owner = Win32::Security::SID::Everyone + Puppet::Util::Windows::Security.set_security_descriptor(@path, sd) + + # instruct Puppet to load settings, which resets perms + Puppet.settings.use(:agent) + + sd = Puppet::Util::Windows::Security.get_security_descriptor(@path) + sd.owner.should == Win32::Security::SID::BuiltinAdministrators + + mode = Puppet::Util::Windows::Security.get_mode(@path) + mode.to_s(8).should == '640' + end + end + end + end end diff --git a/spec/unit/settings/file_setting_spec.rb b/spec/unit/settings/file_setting_spec.rb index b31d0ccb3..70c93732a 100755 --- a/spec/unit/settings/file_setting_spec.rb +++ b/spec/unit/settings/file_setting_spec.rb @@ -36,10 +36,11 @@ describe Puppet::Settings::FileSetting do setting = FileSetting.new(:settings => settings, :owner => "root", :desc => "a setting") - setting.owner.should == "root" + setting.owner.should == FileSetting::Root.new.value end - it "is the service user if we are making users" do + it "is the service user if we are making users", + :unless => Puppet.features.microsoft_windows? do settings = settings(:user => "the_service", :mkusers => true, :service_user_available? => false) setting = FileSetting.new(:settings => settings, :owner => "service", :desc => "a setting") @@ -60,7 +61,7 @@ describe Puppet::Settings::FileSetting do setting = FileSetting.new(:settings => settings, :owner => "service", :desc => "a setting") - setting.owner.should == "root" + setting.owner.should == FileSetting::Root.new.value end it "is unspecified when no specific owner is wanted" do @@ -85,7 +86,7 @@ describe Puppet::Settings::FileSetting do setting = FileSetting.new(:settings => settings, :group => "root", :desc => "a setting") - setting.group.should == "root" + setting.group.should == FileSetting::Root.new.value end it "is the service group if we are making users" do @@ -104,12 +105,14 @@ describe Puppet::Settings::FileSetting do setting.group.should == "the_service" end - it "is unspecified when the setting specifies service and the group is not available on the system" do + it "is a fallback default value (unspecified on POSIX, 'Service' on Windows) when the setting specifies service and the group is not available on the system" do settings = settings(:group => "the_service", :mkusers => false, :service_group_available? => false) setting = FileSetting.new(:settings => settings, :group => "service", :desc => "a setting") - setting.group.should be_nil + group = Puppet.features.microsoft_windows? ? Win32::Security::SID::Service : nil + + setting.group.should == group end it "does not allow other groups" do @@ -196,7 +199,6 @@ describe Puppet::Settings::FileSetting do it "should set the owner if running as root and the owner is provided" do Puppet.features.expects(:root?).returns true - Puppet.features.stubs(:microsoft_windows?).returns false @file.stubs(:owner).returns "foo" @file.to_resource[:owner].should == "foo" @@ -212,7 +214,6 @@ describe Puppet::Settings::FileSetting do it "should set the group if running as root and the group is provided" do Puppet.features.expects(:root?).returns true - Puppet.features.stubs(:microsoft_windows?).returns false @file.stubs(:group).returns "foo" @file.to_resource[:group].should == "foo" @@ -226,37 +227,18 @@ describe Puppet::Settings::FileSetting do @file.to_resource[:group].should == nil end - it "should not set owner if not running as root" do Puppet.features.expects(:root?).returns false - Puppet.features.stubs(:microsoft_windows?).returns false @file.stubs(:owner).returns "foo" @file.to_resource[:owner].should be_nil end it "should not set group if not running as root" do Puppet.features.expects(:root?).returns false - Puppet.features.stubs(:microsoft_windows?).returns false @file.stubs(:group).returns "foo" @file.to_resource[:group].should be_nil end - describe "on Microsoft Windows systems" do - before :each do - Puppet.features.stubs(:microsoft_windows?).returns true - end - - it "should not set owner" do - @file.stubs(:owner).returns "foo" - @file.to_resource[:owner].should be_nil - end - - it "should not set group" do - @file.stubs(:group).returns "foo" - @file.to_resource[:group].should be_nil - end - end - it "should set :ensure to the file type" do @file.expects(:type).returns :directory @file.to_resource[:ensure].should == :directory diff --git a/spec/unit/settings_spec.rb b/spec/unit/settings_spec.rb index b201b3eb8..6f35f18f9 100755 --- a/spec/unit/settings_spec.rb +++ b/spec/unit/settings_spec.rb @@ -1229,7 +1229,8 @@ describe Puppet::Settings do @settings.to_catalog end - describe "on Microsoft Windows" do + describe "on Microsoft Windows", + :if => Puppet.features.microsoft_windows? do before :each do Puppet.features.stubs(:root?).returns true Puppet.features.stubs(:microsoft_windows?).returns true diff --git a/spec/unit/util/windows/sid_spec.rb b/spec/unit/util/windows/sid_spec.rb index 770512188..a05aee6aa 100755 --- a/spec/unit/util/windows/sid_spec.rb +++ b/spec/unit/util/windows/sid_spec.rb @@ -11,6 +11,7 @@ describe "Puppet::Util::Windows::SID", :if => Puppet.features.microsoft_windows? let(:subject) { SIDTester.new } let(:sid) { Win32::Security::SID::LocalSystem } + let(:service_sid) { Win32::Security::SID::Service } let(:invalid_sid) { 'bogus' } let(:unknown_sid) { 'S-0-0-0' } let(:unknown_name) { 'chewbacca' } @@ -78,6 +79,11 @@ describe "Puppet::Util::Windows::SID", :if => Puppet.features.microsoft_windows? it "should be the identity function for any sid" do subject.name_to_sid(sid).should == sid end + + it "should map Puppet 'service' user to service group" do + # this test is only kept around to ensure the mapping never breaks + subject.name_to_sid('service').should == service_sid + end end context "#name_to_sid_object" do From 4810aebcd36a28266c5464fa3d0cb338c4adca0f Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Tue, 14 Jan 2014 17:30:10 -0800 Subject: [PATCH 432/800] (PUP-1118) Fetch the environment's manifest & modulepath at init After trying to get the manifest and modulepath during the initialization for an environment, it turned out that a lot of other things depended on a very specific load order. Some of those needed to change around, others needed to delay executing until later, after everything had been loaded and initialized (creation of the root environment was one of these). The reset of the Parser::Functions also moved so that it doesn't happen until settings have been initialized. --- lib/puppet.rb | 4 +- lib/puppet/indirector.rb | 1 + lib/puppet/module.rb | 5 +- .../module_tool/applications/installer.rb | 2 +- .../module_tool/applications/uninstaller.rb | 15 ++-- .../module_tool/applications/upgrader.rb | 15 ++-- lib/puppet/node/environment.rb | 31 ++++--- lib/puppet/parser/functions.rb | 4 +- lib/puppet/test/test_helper.rb | 2 +- lib/puppet/type.rb | 3 - lib/puppet/util/autoload.rb | 4 +- .../applications/uninstaller_spec.rb | 4 +- spec/unit/node/environment_spec.rb | 83 ++++++++++++------- spec/unit/parser/functions/defined_spec.rb | 1 - spec/unit/parser/functions/include_spec.rb | 1 - 15 files changed, 106 insertions(+), 69 deletions(-) diff --git a/lib/puppet.rb b/lib/puppet.rb index 427290339..05ad5917d 100644 --- a/lib/puppet.rb +++ b/lib/puppet.rb @@ -148,6 +148,7 @@ module Puppet Puppet.settings.initialize_global_settings(args) run_mode = Puppet::Util::RunMode[run_mode] Puppet.settings.initialize_app_defaults(Puppet::Settings.app_defaults_for_run_mode(run_mode)) + Puppet::Parser::Functions.reset end private_class_method :do_initialize_settings_for_run_mode @@ -172,9 +173,10 @@ end # anywhere besides the very top of a file. That would not be possible at the moment without a great deal of # effort, but I think we should strive for it and revisit this at some point. --cprice 2012-03-16 +require 'puppet/indirector' require 'puppet/type' -require 'puppet/parser' require 'puppet/resource' +require 'puppet/parser' require 'puppet/network' require 'puppet/ssl' require 'puppet/module' diff --git a/lib/puppet/indirector.rb b/lib/puppet/indirector.rb index 65f852ba8..9385a615d 100644 --- a/lib/puppet/indirector.rb +++ b/lib/puppet/indirector.rb @@ -9,6 +9,7 @@ module Puppet::Indirector require 'puppet/indirector/indirection' require 'puppet/indirector/terminus' + require 'puppet/indirector/code' require 'puppet/indirector/envelope' require 'puppet/network/format_support' diff --git a/lib/puppet/module.rb b/lib/puppet/module.rb index c69de2226..f562fb414 100644 --- a/lib/puppet/module.rb +++ b/lib/puppet/module.rb @@ -1,6 +1,5 @@ require 'puppet/util/logging' require 'semver' -require 'puppet/module_tool/applications' # Support for modules class Puppet::Module @@ -209,11 +208,15 @@ class Puppet::Module end def has_local_changes? + Puppet.deprecation_warning("This method is being removed.") + require 'puppet/module_tool/applications' changes = Puppet::ModuleTool::Applications::Checksummer.run(path) !changes.empty? end def local_changes + Puppet.deprecation_warning("This method is being removed.") + require 'puppet/module_tool/applications' Puppet::ModuleTool::Applications::Checksummer.run(path) end diff --git a/lib/puppet/module_tool/applications/installer.rb b/lib/puppet/module_tool/applications/installer.rb index 3f7db78b2..e3805a149 100644 --- a/lib/puppet/module_tool/applications/installer.rb +++ b/lib/puppet/module_tool/applications/installer.rb @@ -91,7 +91,7 @@ module Puppet::ModuleTool :module_name => @module_name, :installed_version => @installed[@module_name].first.version, :requested_version => @version || (@conditions[@module_name].empty? ? :latest : :best), - :local_changes => @installed[@module_name].first.local_changes + :local_changes => Puppet::ModuleTool::Applications::Checksummer.run(@installed[@module_name].first.path) end if @ignore_dependencies && @source == :filesystem diff --git a/lib/puppet/module_tool/applications/uninstaller.rb b/lib/puppet/module_tool/applications/uninstaller.rb index 006a88b8e..f81ff9514 100644 --- a/lib/puppet/module_tool/applications/uninstaller.rb +++ b/lib/puppet/module_tool/applications/uninstaller.rb @@ -86,12 +86,15 @@ module Puppet::ModuleTool def validate_module mod = @installed.first - if !@options[:force] && mod.has_metadata? && mod.has_local_changes? - raise LocalChangesError, - :action => :uninstall, - :module_name => (mod.forge_name || mod.name).gsub('/', '-'), - :requested_version => @options[:version], - :installed_version => mod.version + if !@options[:force] && mod.has_metadata? + changes = Puppet::ModuleTool::Applications::Checksummer.run(mod.path) + if !changes.empty? + raise LocalChangesError, + :action => :uninstall, + :module_name => (mod.forge_name || mod.name).gsub('/', '-'), + :requested_version => @options[:version], + :installed_version => mod.version + end end if !@options[:force] && !mod.required_by.empty? diff --git a/lib/puppet/module_tool/applications/upgrader.rb b/lib/puppet/module_tool/applications/upgrader.rb index 226f24050..8f6999ff2 100644 --- a/lib/puppet/module_tool/applications/upgrader.rb +++ b/lib/puppet/module_tool/applications/upgrader.rb @@ -38,12 +38,15 @@ module Puppet::ModuleTool dir = @module.modulepath Puppet.notice "Found '#{@module_name}' (#{colorize(:cyan, results[:installed_version] || '???')}) in #{dir} ..." - if !@options[:force] && @module.has_metadata? && @module.has_local_changes? - raise LocalChangesError, - :action => :upgrade, - :module_name => @module_name, - :requested_version => @version || (@conditions[@module_name].empty? ? :latest : :best), - :installed_version => @module.version + if !@options[:force] && @module.has_metadata? + changes = Puppet::ModuleTool::Applications::Checksummer.run(@module.path) + if !changes.empty? + raise LocalChangesError, + :action => :upgrade, + :module_name => @module_name, + :requested_version => @version || (@conditions[@module_name].empty? ? :latest : :best), + :installed_version => @module.version + end end begin diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb index 02d46c986..53b429ad3 100644 --- a/lib/puppet/node/environment.rb +++ b/lib/puppet/node/environment.rb @@ -75,6 +75,11 @@ class Puppet::Node::Environment include Puppet::Util::Cacher + # @api private + def self.seen + @seen ||= {} + end + # Create a new environment with the given name, or return an existing one # # The environment class memoizes instances so that attempts to instantiate an @@ -105,12 +110,11 @@ class Puppet::Node::Environment symbol = name.to_sym - @seen ||= {} - return @seen[symbol] if @seen[symbol] + return seen[symbol] if seen[symbol] obj = self.allocate obj.send :initialize, symbol - @seen[symbol] = obj + seen[symbol] = obj end # Retrieve the environment for the current thread @@ -149,14 +153,14 @@ class Puppet::Node::Environment # # @api private def self.root - @root + @root ||= new(:'*root*') end # Clear all memoized environments and the 'current' environment # # @api private def self.clear - @seen.clear + seen.clear $environment = nil end @@ -166,6 +170,11 @@ class Puppet::Node::Environment # environment identifier attr_reader :name + # @!attribute [r] manifest + # @api public + # @return [String] path to the manifest file or directory. + attr_reader :manifest + # Return an environment-specific Puppet setting. # # @api public @@ -185,6 +194,8 @@ class Puppet::Node::Environment # @param name [Symbol] The environment name def initialize(name) @name = name + @modulepath = Puppet.settings.value(:modulepath, name) + @manifest = Puppet.settings.value(:manifest, name) end # The current global TypeCollection @@ -274,7 +285,7 @@ class Puppet::Node::Environment # @api public # @return [Array] All directories present in the modulepath cached_attr(:modulepath, Puppet[:filetimeout]) do - dirs = self[:modulepath].split(File::PATH_SEPARATOR) + dirs = @modulepath.split(File::PATH_SEPARATOR) dirs = ENV["PUPPETLIB"].split(File::PATH_SEPARATOR) + dirs if ENV["PUPPETLIB"] validate_dirs(dirs) end @@ -469,13 +480,13 @@ class Puppet::Node::Environment # @return [Puppet::Parser::AST::Hostclass] The AST hostclass object # representing the 'main' hostclass def perform_initial_import - return empty_parse_result if Puppet.settings[:ignoreimport] + return empty_parse_result if Puppet[:ignoreimport] parser = Puppet::Parser::ParserFactory.parser(self) - if code = Puppet.settings.value(:code, name.to_s) and code != "" + if code = Puppet[:code] and code != "" parser.string = code parser.parse else - file = Puppet.settings.value(:manifest, name.to_s) + file = self.manifest # if the manifest file is a reference to a directory, parse and combine all .pp files in that # directory if File.directory?(file) @@ -508,6 +519,4 @@ class Puppet::Node::Environment def empty_parse_result return Puppet::Parser::AST::Hostclass.new('') end - - @root = new(:'*root*') end diff --git a/lib/puppet/parser/functions.rb b/lib/puppet/parser/functions.rb index ab8f7038d..4e3c93b54 100644 --- a/lib/puppet/parser/functions.rb +++ b/lib/puppet/parser/functions.rb @@ -45,7 +45,7 @@ module Puppet::Parser::Functions if env and ! env.is_a?(Puppet::Node::Environment) env = Puppet::Node::Environment.new(env) end - @modules[ (env || Environment.current || Environment.root).name ] ||= Module.new + @modules[ (env || Environment.current).name ] ||= Module.new end # Create a new Puppet DSL function. @@ -238,6 +238,4 @@ module Puppet::Parser::Functions @functions[Environment.current][name] = func end end - - reset # initialize the class instance variables end diff --git a/lib/puppet/test/test_helper.rb b/lib/puppet/test/test_helper.rb index d3970b82d..2b45b24ef 100644 --- a/lib/puppet/test/test_helper.rb +++ b/lib/puppet/test/test_helper.rb @@ -40,7 +40,7 @@ module Puppet::Test # any individual tests. # @return nil def self.before_all_tests() - + Puppet::Parser::Functions.reset end # Call this method once, at the end of a test run, when no more tests diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb index 4c7924ad3..07ec3ac6b 100644 --- a/lib/puppet/type.rb +++ b/lib/puppet/type.rb @@ -2420,6 +2420,3 @@ end end require 'puppet/provider' - -# Always load these types. -Puppet::Type.type(:component) diff --git a/lib/puppet/util/autoload.rb b/lib/puppet/util/autoload.rb index ddd36ded8..166dab51f 100644 --- a/lib/puppet/util/autoload.rb +++ b/lib/puppet/util/autoload.rb @@ -107,8 +107,6 @@ class Puppet::Util::Autoload # world testing. --daniel 2012-07-10 require 'puppet/node/environment' unless defined?(Puppet::Node::Environment) - real_env = Puppet::Node::Environment.new(env) - # We're using a per-thread cache of module directories so that we don't # scan the filesystem each time we try to load something. This is reset # at the beginning of compilation and at the end of an agent run. @@ -135,6 +133,8 @@ class Puppet::Util::Autoload # "app_defaults_initialized?" method on the main puppet Settings object. # --cprice 2012-03-16 if Puppet.settings.app_defaults_initialized? + real_env = Puppet::Node::Environment.new(env) + # if the app defaults have been initialized then it should be safe to access the module path setting. $env_module_directories[real_env] ||= real_env.modulepath.collect do |dir| Dir.entries(dir).reject { |f| f =~ /^\./ }.collect { |f| File.join(dir, f) } diff --git a/spec/unit/module_tool/applications/uninstaller_spec.rb b/spec/unit/module_tool/applications/uninstaller_spec.rb index 9ec52fc4c..da9ca1224 100644 --- a/spec/unit/module_tool/applications/uninstaller_spec.rb +++ b/spec/unit/module_tool/applications/uninstaller_spec.rb @@ -120,8 +120,8 @@ describe Puppet::ModuleTool::Applications::Uninstaller do context "when the module has local changes" do it "should not uninstall the module" do - PuppetSpec::Modules.create('foo', modpath1, :metadata => foo_metadata) - Puppet::Module.any_instance.stubs(:has_local_changes?).returns(true) + mod = PuppetSpec::Modules.create('foo', modpath1, :metadata => foo_metadata) + Puppet::ModuleTool::Applications::Checksummer.expects(:run).with(mod.path).returns(['change']) @uninstaller.new("puppetlabs-foo", options).run[:result].should == :failure end diff --git a/spec/unit/node/environment_spec.rb b/spec/unit/node/environment_spec.rb index 4420d6744..b77e3bcce 100755 --- a/spec/unit/node/environment_spec.rb +++ b/spec/unit/node/environment_spec.rb @@ -117,12 +117,14 @@ describe Puppet::Node::Environment do end it "should prefix the value of the 'PUPPETLIB' environment variable to the module path if present" do - Puppet::Util.withenv("PUPPETLIB" => %w{/l1 /l2}.join(File::PATH_SEPARATOR)) do - module_path = %w{/one /two}.join(File::PATH_SEPARATOR) - env.expects(:validate_dirs).with(%w{/l1 /l2 /one /two}).returns %w{/l1 /l2 /one /two} - env.expects(:[]).with(:modulepath).returns module_path + first_puppetlib = tmpdir('puppetlib1') + second_puppetlib = tmpdir('puppetlib2') + first_moduledir = tmpdir('moduledir1') + second_moduledir = tmpdir('moduledir2') + Puppet::Util.withenv("PUPPETLIB" => [first_puppetlib, second_puppetlib].join(File::PATH_SEPARATOR)) do + Puppet[:modulepath] = [first_moduledir, second_moduledir].join(File::PATH_SEPARATOR) - env.modulepath.should == %w{/l1 /l2 /one /two} + env.modulepath.should == [first_puppetlib, second_puppetlib, first_moduledir, second_moduledir] end end @@ -402,59 +404,80 @@ describe Puppet::Node::Environment do end describe "when performing initial import" do - let(:env) { Puppet::Node::Environment.new("test") } - before do - @parser = Puppet::Parser::ParserFactory.parser(env) -# @parser = Puppet::Parser::EParserAdapter.new(Puppet::Parser::Parser.new("test")) # TODO: FIX PARSER FACTORY - Puppet::Parser::ParserFactory.stubs(:parser).returns @parser + def parser_and_environment(name) + env = Puppet::Node::Environment.new(name) + parser = Puppet::Parser::ParserFactory.parser(env) + Puppet::Parser::ParserFactory.stubs(:parser).returns(parser) + + [parser, env] end it "should set the parser's string to the 'code' setting and parse if code is available" do - Puppet.settings[:code] = "my code" - @parser.expects(:string=).with "my code" - @parser.expects(:parse) + Puppet[:code] = "my code" + parser, env = parser_and_environment('testing') + + parser.expects(:string=).with "my code" + parser.expects(:parse) + env.instance_eval { perform_initial_import } end it "should set the parser's file to the 'manifest' setting and parse if no code is available and the manifest is available" do filename = tmpfile('myfile') - File.open(filename, 'w'){|f| } - Puppet.settings[:manifest] = filename - @parser.expects(:file=).with filename - @parser.expects(:parse) + Puppet[:manifest] = filename + parser, env = parser_and_environment('testing') + + parser.expects(:file=).with filename + parser.expects(:parse) + env.instance_eval { perform_initial_import } end it "should pass the manifest file to the parser even if it does not exist on disk" do filename = tmpfile('myfile') - Puppet.settings[:code] = "" - Puppet.settings[:manifest] = filename - @parser.expects(:file=).with(filename).once - @parser.expects(:parse).once + Puppet[:code] = "" + Puppet[:manifest] = filename + parser, env = parser_and_environment('testing') + + parser.expects(:file=).with(filename).once + parser.expects(:parse).once + env.instance_eval { perform_initial_import } end it "should fail helpfully if there is an error importing" do Puppet::FileSystem.stubs(:exist?).returns true env.stubs(:known_resource_types).returns Puppet::Resource::TypeCollection.new(env) - @parser.expects(:file=).once - @parser.expects(:parse).raises ArgumentError - lambda { env.instance_eval { perform_initial_import } }.should raise_error(Puppet::Error) + parser, env = parser_and_environment('testing') + + parser.expects(:file=).once + parser.expects(:parse).raises ArgumentError + + expect do + env.instance_eval { perform_initial_import } + end.to raise_error(Puppet::Error) end it "should not do anything if the ignore_import settings is set" do - Puppet.settings[:ignoreimport] = true - @parser.expects(:string=).never - @parser.expects(:file=).never - @parser.expects(:parse).never + Puppet[:ignoreimport] = true + parser, env = parser_and_environment('testing') + + parser.expects(:string=).never + parser.expects(:file=).never + parser.expects(:parse).never + env.instance_eval { perform_initial_import } end it "should mark the type collection as needing a reparse when there is an error parsing" do - @parser.expects(:parse).raises Puppet::ParseError.new("Syntax error at ...") + parser, env = parser_and_environment('testing') env.stubs(:known_resource_types).returns Puppet::Resource::TypeCollection.new(env) - lambda { env.instance_eval { perform_initial_import } }.should raise_error(Puppet::Error, /Syntax error at .../) + parser.expects(:parse).raises Puppet::ParseError.new("Syntax error at ...") + + expect do + env.instance_eval { perform_initial_import } + end.to raise_error(Puppet::Error, /Syntax error at .../) env.known_resource_types.require_reparse?.should be_true end end diff --git a/spec/unit/parser/functions/defined_spec.rb b/spec/unit/parser/functions/defined_spec.rb index 044d264de..b8659063f 100755 --- a/spec/unit/parser/functions/defined_spec.rb +++ b/spec/unit/parser/functions/defined_spec.rb @@ -7,7 +7,6 @@ describe "the 'defined' function" do end before :each do - Puppet::Node::Environment.stubs(:current).returns(nil) @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("foo")) @scope = Puppet::Parser::Scope.new(@compiler) end diff --git a/spec/unit/parser/functions/include_spec.rb b/spec/unit/parser/functions/include_spec.rb index fbe51e7fb..c1a5cbd5c 100755 --- a/spec/unit/parser/functions/include_spec.rb +++ b/spec/unit/parser/functions/include_spec.rb @@ -7,7 +7,6 @@ describe "the 'include' function" do end before :each do - Puppet::Node::Environment.stubs(:current).returns(nil) @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("foo")) @scope = Puppet::Parser::Scope.new(@compiler) end From 488bcc2ac58c948361948ff43202b67d5428b8bb Mon Sep 17 00:00:00 2001 From: Glenn Pratt Date: Wed, 15 Jan 2014 10:25:40 -0600 Subject: [PATCH 433/800] (PUP-1150) Fix race condition in Puppet::Util::Lockfile. Before this commit, Puppet::Util::Lockfile.lock called the locked? method before opening the @lockfile for write. Checking that the file doesn't exist and creating the file were not an atomic operation, which allowed two processes to detect that the file didn't exist and open it for write simultaneously. This commit adds and used Puppet::FileSystem::exclusive_create wrapper that sets the following options: * File:CREAT - Creates the file if it doesn't exist. * File:EXCL - Fails if the file does exist. * File:WRONLY - Opens the file for write only. File:EXCL is the important option, callers will recieve an Exception if the file exists, meaning another process has acquired the lock. If the file doesn't exist it will be created atomically, so other another caller cannot create the file in the gap. --- lib/puppet/file_system.rb | 11 ++++++ lib/puppet/file_system/file_impl.rb | 5 +++ lib/puppet/util/lockfile.rb | 12 ++++--- spec/unit/file_system/file_spec.rb | 18 ++++++++++ spec/unit/util/lockfile_spec.rb | 52 ++++++++++++++++++++++++++--- 5 files changed, 89 insertions(+), 9 deletions(-) diff --git a/lib/puppet/file_system.rb b/lib/puppet/file_system.rb index cf7583d1b..1a0fe7163 100644 --- a/lib/puppet/file_system.rb +++ b/lib/puppet/file_system.rb @@ -311,4 +311,15 @@ module Puppet::FileSystem @impl.path_string(path) end + # Create and open a file for write only if it doesn't exist. + # + # @see Puppet::FileSystem::open + # + # @raise [Errno::EEXIST] path already exists. + # + # @api public + # + def self.exclusive_create(path, mode, &block) + @impl.exclusive_create(assert_path(path), mode, &block) + end end diff --git a/lib/puppet/file_system/file_impl.rb b/lib/puppet/file_system/file_impl.rb index 8f0f50bad..2fa318e4e 100644 --- a/lib/puppet/file_system/file_impl.rb +++ b/lib/puppet/file_system/file_impl.rb @@ -38,6 +38,11 @@ class Puppet::FileSystem::FileImpl path.size end + def exclusive_create(path, mode, &block) + opt = File::CREAT | File::EXCL | File::WRONLY + self.open(path, mode, opt, &block) + end + def exclusive_open(path, mode, options = 'r', timeout = 300, &block) wait = 0.001 + (Kernel.rand / 1000) written = false diff --git a/lib/puppet/util/lockfile.rb b/lib/puppet/util/lockfile.rb index a2d0d2482..9771f389e 100644 --- a/lib/puppet/util/lockfile.rb +++ b/lib/puppet/util/lockfile.rb @@ -23,10 +23,14 @@ class Puppet::Util::Lockfile # @return [boolean] true if lock is successfully acquired, false otherwise. def lock(lock_data = nil) - return false if locked? - - File.open(@file_path, 'w') { |fd| fd.print(lock_data) } - true + begin + Puppet::FileSystem.exclusive_create(@file_path, nil) do |fd| + fd.print(lock_data) + end + true + rescue Errno::EEXIST + false + end end def unlock diff --git a/spec/unit/file_system/file_spec.rb b/spec/unit/file_system/file_spec.rb index 1d7e2efcb..10f225c76 100644 --- a/spec/unit/file_system/file_spec.rb +++ b/spec/unit/file_system/file_spec.rb @@ -482,5 +482,23 @@ describe "Puppet::FileSystem" do Puppet::FileSystem.exist?(dir).should be_true end end + + describe "exclusive_create" do + it "should create a file that doesn't exist" do + Puppet::FileSystem.exist?(missing_file).should be_false + + Puppet::FileSystem.exclusive_create(missing_file, nil) {} + + Puppet::FileSystem.exist?(missing_file).should be_true + end + + it "should raise Errno::EEXIST creating a file that does exist" do + Puppet::FileSystem.exist?(file).should be_true + + expect do + Puppet::FileSystem.exclusive_create(file, nil) {} + end.to raise_error(Errno::EEXIST) + end + end end end diff --git a/spec/unit/util/lockfile_spec.rb b/spec/unit/util/lockfile_spec.rb index c203f0b06..33755dad2 100644 --- a/spec/unit/util/lockfile_spec.rb +++ b/spec/unit/util/lockfile_spec.rb @@ -3,6 +3,33 @@ require 'spec_helper' require 'puppet/util/lockfile' +module LockfileSpecHelper + def self.run_in_forks(count, &blk) + forks = {} + results = [] + count.times do |i| + forks[i] = {} + forks[i][:read], forks[i][:write] = IO.pipe + + forks[i][:pid] = fork do + forks[i][:read].close + res = yield + Marshal.dump(res, forks[i][:write]) + exit! + end + end + + count.times do |i| + forks[i][:write].close + result = forks[i][:read].read + forks[i][:read].close + Process.wait2(forks[i][:pid]) + results << Marshal.load(result) + end + results + end +end + describe Puppet::Util::Lockfile do require 'puppet_spec/files' include PuppetSpec::Files @@ -13,21 +40,36 @@ describe Puppet::Util::Lockfile do end describe "#lock" do - it "should return false if already locked" do - @lock.stubs(:locked?).returns(true) - @lock.lock.should be_false - end - it "should return true if it successfully locked" do @lock.lock.should be_true end + it "should return false if already locked" do + @lock.lock + @lock.lock.should be_false + end + it "should create a lock file" do @lock.lock Puppet::FileSystem.exist?(@lockfile).should be_true end + # We test simultaneous locks using fork which isn't supported on Windows. + it "should not be acquired by another process", :unless => Puppet.features.microsoft_windows? do + 30.times do + forks = 3 + results = LockfileSpecHelper.run_in_forks(forks) do + @lock.lock(Process.pid) + end + @lock.unlock + + # Confirm one fork returned true and everyone else false. + (results - [true]).size.should == forks - 1 + (results - [false]).size.should == 1 + end + end + it "should create a lock file containing a string" do data = "foofoo barbar" @lock.lock(data) From ad2b5ea54564974eb05e7eefa4a2544c6a797dcb Mon Sep 17 00:00:00 2001 From: Gavin Williams Date: Mon, 9 Dec 2013 09:35:30 +0000 Subject: [PATCH 434/800] (#23316) Update yum package provider support :holdable This commit adds support for the `holdable` package feature to the yum package provider. If the yum versionlock plugin is available packages can specify an ensure value of 'held' and the yum provider will use the versionlock plugin to lock the version. This feature has the caveat that it can only be enabled at provider load time since provider features cannot be enabled based on a code block. --- lib/puppet/provider/package/yum.rb | 34 ++++++++++++++++++++++++++ spec/unit/provider/package/yum_spec.rb | 31 +++++++++++++++++++++-- 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/lib/puppet/provider/package/yum.rb b/lib/puppet/provider/package/yum.rb index c32052d3d..99b7fb198 100644 --- a/lib/puppet/provider/package/yum.rb +++ b/lib/puppet/provider/package/yum.rb @@ -27,6 +27,16 @@ Puppet::Type.type(:package).provide :yum, :parent => :rpm, :source => :rpm do defaultfor :operatingsystem => [:fedora, :centos, :redhat] + if command('yum') + Puppet.debug('Checking if yum supports versionlock.') + begin + yum('versionlock') + has_feature :holdable + rescue Puppet::ExecutionFailure + false + end + end + def self.prefetch(packages) raise Puppet::Error, "The yum provider can only be used as root" if Process.euid != 0 super @@ -74,6 +84,12 @@ Puppet::Type.type(:package).provide :yum, :parent => :rpm, :source => :rpm do end end + # Unhold before installing + if self.class.declared_feature?(:holdable) + Puppet.debug('Provider supports holdable. Unholding package before installing...') + self.unhold + end + yum "-d", "0", "-e", "0", "-y", operation, wanted is = self.query @@ -107,4 +123,22 @@ Puppet::Type.type(:package).provide :yum, :parent => :rpm, :source => :rpm do def purge yum "-y", :erase, @resource[:name] end + + def hold + # Install before locking the version. + self.install + yum('versionlock', @resource[:name]) + end + + def unhold + yum('versionlock', 'delete', "*#{@resource[:name]}*") + rescue Puppet::ExecutionFailure => e + if e.message.match /versionlock delete: no matches/ + # No versionlock present for this package + return true + else + # If it's not a no match failure, then something else went wrong... + raise Puppet::Error, "Failed to unhold package #{@resource[:name]}: #{e.message}", e.backtrace + end + end end diff --git a/spec/unit/provider/package/yum_spec.rb b/spec/unit/provider/package/yum_spec.rb index c5d89ae60..59b2b7fac 100755 --- a/spec/unit/provider/package/yum_spec.rb +++ b/spec/unit/provider/package/yum_spec.rb @@ -17,8 +17,8 @@ describe provider do @provider.stubs(:get).with(:release).returns '1' @provider.stubs(:get).with(:arch).returns 'i386' end - # provider should repond to the following methods - [:install, :latest, :update, :purge].each do |method| + # provider should respond to the following methods + [:install, :latest, :update, :purge, :hold, :unhold].each do |method| it "should have a(n) #{method}" do @provider.should respond_to(method) end @@ -55,6 +55,33 @@ describe provider do @provider.stubs(:query).returns(:ensure => '1.2').then.returns(:ensure => '1.0') @provider.install end + + describe "when the provider is holdable" do + before do + provider.stubs(:declared_feature?).with(:holdable).returns true + end + + it 'can hold packages' do + @resource.stubs(:should).with(:ensure).returns :held + @provider.expects(:yum).with('versionlock', 'mypackage') + @provider.hold + end + + it "installs packages before holding them" do + @resource.stubs(:should).with(:ensure).returns :held + @provider.expects(:yum).with('versionlock', 'mypackage') + @provider.expects(:install) + @provider.hold + end + + it "unholds packages before installing" do + @resource.stubs(:should).with(:ensure).returns :installed + @provider.expects(:yum).with('-d', '0', '-e', '0', '-y', :install, 'mypackage') + @provider.expects(:unhold) + @provider.install + end + + end end describe 'when uninstalling' do From c9b51eb2a18cd4cd6ad3bdb4d499a1d45f1db6a0 Mon Sep 17 00:00:00 2001 From: Guillaume Virlet Date: Wed, 8 Jan 2014 23:59:00 +0100 Subject: [PATCH 435/800] (PUP-1404) execpipe resets LANG and LC_ALL Puppet::Util::Execution.execpipe always run commands with LANG="C" and LC_ALL="C" to avoid localized output. --- lib/puppet/util/execution.rb | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/puppet/util/execution.rb b/lib/puppet/util/execution.rb index 0e8350cbb..411f462c5 100644 --- a/lib/puppet/util/execution.rb +++ b/lib/puppet/util/execution.rb @@ -66,8 +66,15 @@ module Puppet::Util::Execution Puppet.debug "Executing '#{command_str}'" end - output = open("| #{command_str} 2>&1") do |pipe| - yield pipe + # force the run of the command with + # the user/system locale to "C" (via environment variables LANG and LC_*) + # it enables to have non localized output for some commands and therefore + # a predictable output + english_env = ENV.to_hash.merge( {'LANG' => 'C', 'LC_ALL' => 'C'} ) + output = Puppet::Util.withenv(english_env) do + open("| #{command_str} 2>&1") do |pipe| + yield pipe + end end if failonfail From 6c54310e94035dddc9cc3c91105f7a3db212af49 Mon Sep 17 00:00:00 2001 From: Dan Lidral-Porter Date: Wed, 15 Jan 2014 10:25:01 -0800 Subject: [PATCH 436/800] (PUP-1404) Add tests for execpipe locale setting --- spec/integration/util/execution_spec.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 spec/integration/util/execution_spec.rb diff --git a/spec/integration/util/execution_spec.rb b/spec/integration/util/execution_spec.rb new file mode 100644 index 000000000..97807a499 --- /dev/null +++ b/spec/integration/util/execution_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +describe Puppet::Util::Execution do + describe "#execpipe" do + it "should set LANG to C avoid localized output" do + out = "" + Puppet::Util::Execution.execpipe('echo $LANG'){ |line| out << line.read.chomp } + expect(out).to eq("C") + end + + it "should set LC_ALL to C avoid localized output" do + out = "" + Puppet::Util::Execution.execpipe('echo $LANG'){ |line| out << line.read.chomp } + expect(out).to eq("C") + end + end +end From 6ba2a7bd9fb8ae5759ce4243ac78539a6753fcb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Tomulik?= Date: Wed, 6 Nov 2013 00:38:30 +0100 Subject: [PATCH 437/800] (pup-1369) Add package_settings property for package This changeset adds the package_settings property to "package" type. It's intended to be used mainly with FreeBSD ports to control port options on installed packages (the options normally set with make config). The sole implementation of package_settings is delegated to providers. This changeset only introduces package_settings feature and property to package type (this initially defines an interface between package type and provider used to handle package_settings). --- lib/puppet/type/package.rb | 78 +++++++++++++++++++++++++++++++++- spec/unit/type/package_spec.rb | 8 ++++ 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/lib/puppet/type/package.rb b/lib/puppet/type/package.rb index e7ea1125b..dbdfbacab 100644 --- a/lib/puppet/type/package.rb +++ b/lib/puppet/type/package.rb @@ -50,6 +50,11 @@ module Puppet passed to the installer command." feature :uninstall_options, "The provider accepts options to be passed to the uninstaller command." + feature :package_settings, "The provider accepts package_settings to be + ensured for the given package. The meaning and format of these settings is + provider-specific.", + :methods => [:package_settings_insync?, :package_settings, :package_settings=] + ensurable do desc <<-EOT @@ -233,8 +238,79 @@ module Puppet end end + newproperty(:package_settings, :required_features=>:package_settings) do + desc "Package settings. The definition of package settings is provider + specific. In general, these are certain properties which alter contents + of a package being installed. An example of package settings are the + FreeBSD ports options. + + The package_settings attribute is a property. This means that the options + can be enforced during package installation and verified/retrieved + for packages that are already installed. + + For example, ports provider on FreeBSD implements the package settings + as port build options (the ones you normally set with make config). + There is a simple usage example for this particular provider: + + package { 'www/apache22': + package_settings => { 'SUEXEC' => false } + } + + The above manifest ensures, that apache22 is compiled without SUEXEC + module. + + Despite the package_settings are provider specific, the typical + behavior, when you change package's package_settings in your manifest, + is to reinstall package with new settings. + " + + validate do |value| + if provider.respond_to?(:package_settings_validate) + provider.package_settings_validate(value) + else + super + end + end + + munge do |value| + if provider.respond_to?(:package_settings_munge) + provider.package_settings_munge(value) + else + super + end + end + + def insync?(is) + provider.package_settings_insync?(should, is) + end + + def should_to_s(newvalue) + if provider.respond_to?(:package_settings_should_to_s) + provider.package_settings_should_to_s(should, newvalue) + else + super + end + end + + def is_to_s(currentvalue) + if provider.respond_to?(:package_settings_is_to_s) + provider.package_settings_is_to_s(should, currentvalue) + else + super + end + end + + def change_to_s(currentvalue, newvalue) + if provider.respond_to?(:package_settings_change_to_s) + provider.package_settings_change_to_s(currentvalue, newvalue) + else + super + end + end + end + newparam(:source) do - desc "Where to find the actual package. This must be a local file + desc "Where to find the actual package. This must be a local file (or on a network file system) or a URL that your specific packaging type understands; Puppet will not retrieve files for you, although you can manage packages as `file` resources." diff --git a/spec/unit/type/package_spec.rb b/spec/unit/type/package_spec.rb index fefd15805..f665226c2 100755 --- a/spec/unit/type/package_spec.rb +++ b/spec/unit/type/package_spec.rb @@ -26,6 +26,10 @@ describe Puppet::Type.type(:package) do Puppet::Type.type(:package).provider_feature(:versionable).should_not be_nil end + it "should have a :package_settings feature that requires :package_settings_insync?, :package_settings and :package_settings=" do + Puppet::Type.type(:package).provider_feature(:package_settings).methods.should == [:package_settings_insync?, :package_settings, :package_settings=] + end + it "should default to being installed" do pkg = Puppet::Type.type(:package).new(:name => "yay", :provider => :apt) pkg.should(:ensure).should == :present @@ -41,6 +45,10 @@ describe Puppet::Type.type(:package) do it "should have an ensure property" do Puppet::Type.type(:package).attrtype(:ensure).should == :property end + + it "should have a package_settings property" do + Puppet::Type.type(:package).attrtype(:package_settings).should == :property + end end describe "when validating attribute values" do From 6551e50c6d8792a1328e101a62756caca1d56887 Mon Sep 17 00:00:00 2001 From: Dan Lidral-Porter Date: Wed, 15 Jan 2014 11:30:32 -0800 Subject: [PATCH 438/800] (PUP-1404) LC_ALL test to actually tests LC_ALL --- spec/integration/util/execution_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/integration/util/execution_spec.rb b/spec/integration/util/execution_spec.rb index 97807a499..6fac11dc6 100644 --- a/spec/integration/util/execution_spec.rb +++ b/spec/integration/util/execution_spec.rb @@ -10,7 +10,7 @@ describe Puppet::Util::Execution do it "should set LC_ALL to C avoid localized output" do out = "" - Puppet::Util::Execution.execpipe('echo $LANG'){ |line| out << line.read.chomp } + Puppet::Util::Execution.execpipe('echo $LC_ALL'){ |line| out << line.read.chomp } expect(out).to eq("C") end end From 0b1f4bd0b32504e4dd5e94ff5b5c22b9d9ff0e78 Mon Sep 17 00:00:00 2001 From: Dan Lidral-Porter Date: Wed, 15 Jan 2014 15:20:26 -0800 Subject: [PATCH 439/800] Don't run LANG/LC_ALL tests on windows --- spec/integration/util/execution_spec.rb | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/spec/integration/util/execution_spec.rb b/spec/integration/util/execution_spec.rb index 6fac11dc6..fa18d662b 100644 --- a/spec/integration/util/execution_spec.rb +++ b/spec/integration/util/execution_spec.rb @@ -1,17 +1,19 @@ require 'spec_helper' describe Puppet::Util::Execution do - describe "#execpipe" do - it "should set LANG to C avoid localized output" do - out = "" - Puppet::Util::Execution.execpipe('echo $LANG'){ |line| out << line.read.chomp } - expect(out).to eq("C") - end + if not Puppet.features.microsoft_windows? do + describe "#execpipe" do + it "should set LANG to C avoid localized output" do + out = "" + Puppet::Util::Execution.execpipe('echo $LANG'){ |line| out << line.read.chomp } + expect(out).to eq("C") + end - it "should set LC_ALL to C avoid localized output" do - out = "" - Puppet::Util::Execution.execpipe('echo $LC_ALL'){ |line| out << line.read.chomp } - expect(out).to eq("C") + it "should set LC_ALL to C avoid localized output" do + out = "" + Puppet::Util::Execution.execpipe('echo $LC_ALL'){ |line| out << line.read.chomp } + expect(out).to eq("C") + end end end end From fcf482aa85708fe20f38cd96d6d92fc58ee78cf6 Mon Sep 17 00:00:00 2001 From: Dan Lidral-Porter Date: Wed, 15 Jan 2014 15:26:01 -0800 Subject: [PATCH 440/800] (PUP-1404) Don't swallow execpipe tests on windows --- spec/integration/util/execution_spec.rb | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/spec/integration/util/execution_spec.rb b/spec/integration/util/execution_spec.rb index fa18d662b..a5be9ab65 100644 --- a/spec/integration/util/execution_spec.rb +++ b/spec/integration/util/execution_spec.rb @@ -1,19 +1,17 @@ require 'spec_helper' describe Puppet::Util::Execution do - if not Puppet.features.microsoft_windows? do - describe "#execpipe" do - it "should set LANG to C avoid localized output" do - out = "" - Puppet::Util::Execution.execpipe('echo $LANG'){ |line| out << line.read.chomp } - expect(out).to eq("C") - end + describe "#execpipe" do + it "should set LANG to C avoid localized output", :if => !Puppet.features.microsoft_windows do + out = "" + Puppet::Util::Execution.execpipe('echo $LANG'){ |line| out << line.read.chomp } + expect(out).to eq("C") + end - it "should set LC_ALL to C avoid localized output" do - out = "" - Puppet::Util::Execution.execpipe('echo $LC_ALL'){ |line| out << line.read.chomp } - expect(out).to eq("C") - end + it "should set LC_ALL to C avoid localized output" :if => !Puppet.features.microsoft_windows do + out = "" + Puppet::Util::Execution.execpipe('echo $LC_ALL'){ |line| out << line.read.chomp } + expect(out).to eq("C") end end end From 5a5f0d33e6f171b05ddab956054743fceae12550 Mon Sep 17 00:00:00 2001 From: Dan Lidral-Porter Date: Tue, 14 Jan 2014 16:53:22 -0800 Subject: [PATCH 441/800] (PUP-1432) Add V2 API structured errors. This commit introduces error responses, a JSON object as described in the api documentation (with associated schema). It introduces the Puppet::Network::HTTP::Issues module, which collects the known issue_kind values, in order to ensure consistency and provide ability to yardoc them. It adds two HTTPError subclasses for future use: an HTTPBadRequestError (400 response), and an HTTPServerError class. The HTTPServerError allows the API to provide more information about an unexpected error than simply letting the error bubble up to be caught by the generic HTTP handler. --- api/docs/http_api_index.md | 5 +++- api/schemas/error.json | 23 +++++++++++++++ lib/puppet/network/http.rb | 1 + lib/puppet/network/http/api/v1.rb | 13 +++++---- lib/puppet/network/http/api/v2.rb | 2 +- lib/puppet/network/http/error.rb | 39 ++++++++++++++++++-------- lib/puppet/network/http/handler.rb | 14 +++++---- lib/puppet/network/http/issues.rb | 9 ++++++ lib/puppet/network/http/request.rb | 2 +- lib/puppet/network/http/route.rb | 2 +- spec/unit/network/http/handler_spec.rb | 28 ++++++++++++++++-- 11 files changed, 111 insertions(+), 27 deletions(-) create mode 100644 api/schemas/error.json create mode 100644 lib/puppet/network/http/issues.rb diff --git a/api/docs/http_api_index.md b/api/docs/http_api_index.md index e133e3d73..0683ee40f 100644 --- a/api/docs/http_api_index.md +++ b/api/docs/http_api_index.md @@ -91,9 +91,12 @@ All error responses will contain a body, except when it is a HEAD request. The error responses will uniformly be a JSON object with the following properties: * `message`: [String] A human readable message explaining the error. - * `code`: [String] A unique code to identify the error condition. + * `issue_kind`: [String] A unique label to identify the error class. * `stacktrace` (only for 5xx errors): [Array] A stacktrace to where the error occurred. +A {file:api/schemas/error.json JSON schema for the error objects} is also available. + + Serialization Formats --------------------- diff --git a/api/schemas/error.json b/api/schemas/error.json new file mode 100644 index 000000000..730dcf5c7 --- /dev/null +++ b/api/schemas/error.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "HTTP Error Response Object", + "description": "A description of the error encountered when attempting to service an HTTP request.", + "type": "object", + "properties": { + "message": { + "description": "A human-readable message explaining the error", + "type": "string" + }, + "issue_kind": { + "description": "A unique label to identify the error class", + "type": "string" + }, + "stacktrace": { + "description": "For 5xx responses only, a ruby stacktrace to where an error occurred.", + "type": "array", + "items": "string" + } + }, + "required": ["message", "issue_kind"], + "additionalProperties": false +} diff --git a/lib/puppet/network/http.rb b/lib/puppet/network/http.rb index f15e679f2..a2712b8d5 100644 --- a/lib/puppet/network/http.rb +++ b/lib/puppet/network/http.rb @@ -2,6 +2,7 @@ module Puppet::Network::HTTP HEADER_ENABLE_PROFILING = "X-Puppet-Profiling" HEADER_PUPPET_VERSION = "X-Puppet-Version" + require 'puppet/network/http/issues' require 'puppet/network/http/error' require 'puppet/network/http/route' require 'puppet/network/http/api' diff --git a/lib/puppet/network/http/api/v1.rb b/lib/puppet/network/http/api/v1.rb index 926408ac9..9b37ec5b2 100644 --- a/lib/puppet/network/http/api/v1.rb +++ b/lib/puppet/network/http/api/v1.rb @@ -39,7 +39,10 @@ class Puppet::Network::HTTP::API::V1 raise ArgumentError, "Could not find indirection '#{indirection_name}'" unless indirection if !indirection.allow_remote_requests? - raise Puppet::Network::HTTP::Error::HTTPNotFoundError, "No handler for #{indirection.name}" + # TODO: should we tell the user we found an indirection but it doesn't + # allow remote requests, or just pretend there's no handler at all? what + # are the security implications for the former? + raise Puppet::Network::HTTP::Error::HTTPNotFoundError.new("No handler for #{indirection.name}", :NO_INDIRECTION_REMOTE_REQUESTS) end trusted = Puppet::Context::TrustedInformation.remote(params[:authenticated], params[:node], certificate) @@ -93,7 +96,7 @@ class Puppet::Network::HTTP::API::V1 # Execute our find. def do_find(indirection, key, params, request, response) unless result = indirection.find(key, params) - raise Puppet::Network::HTTP::Error::HTTPNotFoundError, "Could not find #{indirection.name} #{key}" + raise Puppet::Network::HTTP::Error::HTTPNotFoundError.new("Could not find #{indirection.name} #{key}", Puppet::Network::HTTP::Issues::RESOURCE_NOT_FOUND) end format = accepted_response_formatter_for(indirection.model, request) @@ -113,7 +116,7 @@ class Puppet::Network::HTTP::API::V1 # Execute our head. def do_head(indirection, key, params, request, response) unless indirection.head(key, params) - raise Puppet::Network::HTTP::Error::HTTPNotFoundError, "Could not find #{indirection.name} #{key}" + raise Puppet::Network::HTTP::Error::HTTPNotFoundError.new("Could not find #{indirection.name} #{key}", Puppet::Network::HTTP::Issues::RESOURCE_NOT_FOUND) end # No need to set a response because no response is expected from a @@ -125,7 +128,7 @@ class Puppet::Network::HTTP::API::V1 result = indirection.search(key, params) if result.nil? - raise Puppet::Network::HTTP::Error::HTTPNotFoundError, "Could not find instances in #{indirection.name} with '#{key}'" + raise Puppet::Network::HTTP::Error::HTTPNotFoundError.new("Could not find instances in #{indirection.name} with '#{key}'", Puppet::Network::HTTP::Issues::RESOURCE_NOT_FOUND) end format = accepted_response_formatter_for(indirection.model, request) @@ -153,7 +156,7 @@ class Puppet::Network::HTTP::API::V1 end def accepted_response_formatter_for(model_class, request) - accepted_formats = request.headers['accept'] or raise Puppet::Network::HTTP::Error::HTTPNotAcceptableError, "Missing required Accept header" + accepted_formats = request.headers['accept'] or raise Puppet::Network::HTTP::Error::HTTPNotAcceptableError.new("Missing required Accept header", Puppet::Network::HTTP::Issues::MISSING_HEADER_FIELD) request.response_formatter_for(model_class.supported_formats, accepted_formats) end diff --git a/lib/puppet/network/http/api/v2.rb b/lib/puppet/network/http/api/v2.rb index 08e2f8efc..ec221093f 100644 --- a/lib/puppet/network/http/api/v2.rb +++ b/lib/puppet/network/http/api/v2.rb @@ -23,7 +23,7 @@ module Puppet::Network::HTTP::API::V2 NOT_FOUND = Puppet::Network::HTTP::Route. path(/.*/). any(lambda do |req, res| - raise Puppet::Network::HTTP::Handler::HTTPNotFoundError, req.path + raise Puppet::Network::HTTP::Handler::HTTPNotFoundError.new(req.path, Puppet::Network::HTTP::Issues::HANDLER_NOT_FOUND) end) ENVIRONMENTS = path(%r{^/environments$}).get(provide do diff --git a/lib/puppet/network/http/error.rb b/lib/puppet/network/http/error.rb index 9722a943d..01b7ecb55 100644 --- a/lib/puppet/network/http/error.rb +++ b/lib/puppet/network/http/error.rb @@ -1,38 +1,55 @@ module Puppet::Network::HTTP::Error - class HTTPError < Exception - attr_reader :status + Issues = Puppet::Network::HTTP::Issues - def initialize(message, status) + class HTTPError < Exception + attr_reader :status, :issue_kind + + def initialize(message, status, issue_kind) super(message) @status = status + @issue_kind = issue_kind end end class HTTPNotAcceptableError < HTTPError CODE = 406 - def initialize(message) - super("Not Acceptable: " + message, CODE) + def initialize(message, issue_kind = Issues::RUNTIME_ERROR) + super("Not Acceptable: " + message, CODE, issue_kind) end end class HTTPNotFoundError < HTTPError CODE = 404 - def initialize(message) - super("Not Found: " + message, CODE) + def initialize(message, issue_kind = Issues::RUNTIME_ERROR) + super("Not Found: " + message, CODE, issue_kind) end end class HTTPNotAuthorizedError < HTTPError CODE = 403 - def initialize(message) - super("Not Authorized: " + message, CODE) + def initialize(message, issue_kind = Issues::RUNTIME_ERROR) + super("Not Authorized: " + message, CODE, issue_kind) + end + end + + class HTTPBadRequestError < HTTPError + CODE = 400 + def initialize(message, issue_kind = Issues::RUNTIME_ERROR) + super("Bad Request: " + message, CODE, issue_kind) end end class HTTPMethodNotAllowedError < HTTPError CODE = 405 - def initialize(message) - super("Method Not Allowed: " + message, CODE) + def initialize(message, issue_kind = Issues::RUNTIME_ERROR) + super("Method Not Allowed: " + message, CODE, issue_kind) + end + end + + class HTTPServerError < HTTPError + CODE = 500 + def initialize(message, issue_kind = Issues::RUNTIME_ERROR) + super("Server Error: " + message, CODE, issue_kind) end end end diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb index e3366f8e8..1f6d9d546 100644 --- a/lib/puppet/network/http/handler.rb +++ b/lib/puppet/network/http/handler.rb @@ -8,9 +8,11 @@ require 'puppet/network/authentication' require 'puppet/network/rights' require 'puppet/util/profiler' require 'resolv' +require 'json' module Puppet::Network::HTTP::Handler include Puppet::Network::Authentication + include Puppet::Network::HTTP::Issues # These shouldn't be allowed to be set by clients # in the query string, for security reasons. @@ -63,7 +65,7 @@ module Puppet::Network::HTTP::Handler if route = @routes.find { |route| route.matches?(new_request) } route.process(new_request, new_response) else - raise Puppet::Network::HTTP::Error::HTTPNotFoundError, "No route for #{new_request.method} #{new_request.path}" + raise Puppet::Network::HTTP::Error::HTTPNotFoundError.new("No route for #{new_request.method} #{new_request.path}", HANDLER_NOT_FOUND) end end end @@ -71,11 +73,13 @@ module Puppet::Network::HTTP::Handler rescue Puppet::Network::HTTP::Error::HTTPError => e msg = e.message Puppet.info(msg) - new_response.respond_with(e.status, "text/plain", msg) + structured_msg = JSON({:message => msg, :issue_kind => e.issue_kind}) + new_response.respond_with(e.status, "application/json", structured_msg) rescue Exception => e - msg = e.message - Puppet.err(msg) - new_response.respond_with(500, "text/plain", msg) + http_e = Puppet::Network::HTTP::Error::HTTPServerError.new(e.message) + Puppet.err(http_e.message) + structured_msg = JSON({:message => http_e.message, :issue_kind => http_e.issue_kind, :stacktrace => e.backtrace}) + new_response.respond_with(500, "application/json", structured_msg) ensure cleanup(request) end diff --git a/lib/puppet/network/http/issues.rb b/lib/puppet/network/http/issues.rb new file mode 100644 index 000000000..9582675a8 --- /dev/null +++ b/lib/puppet/network/http/issues.rb @@ -0,0 +1,9 @@ +module Puppet::Network::HTTP::Issues + NO_INDIRECTION_REMOTE_REQUESTS = :NO_INDIRECTION_REMOTE_REQUESTS + HANDLER_NOT_FOUND = :HANDLER_NOT_FOUND + RESOURCE_NOT_FOUND = :RESOURCE_NOT_FOUND + RUNTIME_ERROR = :RUNTIME_ERROR + MISSING_HEADER_FIELD = :MISSING_HEADER_FIELD + UNSUPPORTED_FORMAT = :UNSUPPORTED_FORMAT + UNSUPPORTED_METHOD = :UNSUPPORTED_METHOD +end diff --git a/lib/puppet/network/http/request.rb b/lib/puppet/network/http/request.rb index 3936508cc..c17673b72 100644 --- a/lib/puppet/network/http/request.rb +++ b/lib/puppet/network/http/request.rb @@ -40,7 +40,7 @@ Puppet::Network::HTTP::Request = Struct.new(:headers, :params, :method, :path, : supported_formats) if formatter.nil? - raise Puppet::Network::HTTP::Error::HTTPNotAcceptableError, "No supported formats are acceptable (Accept: #{accepted_formats})" + raise Puppet::Network::HTTP::Error::HTTPNotAcceptableError.new("No supported formats are acceptable (Accept: #{accepted_formats})", Puppet::Network::HTTP::Issues::UNSUPPORTED_FORMAT) end report_if_deprecated(formatter) diff --git a/lib/puppet/network/http/route.rb b/lib/puppet/network/http/route.rb index 3e8d89a68..e73d5b593 100644 --- a/lib/puppet/network/http/route.rb +++ b/lib/puppet/network/http/route.rb @@ -1,6 +1,6 @@ class Puppet::Network::HTTP::Route MethodNotAllowedHandler = lambda do |req, res| - raise Puppet::Network::HTTP::Error::HTTPMethodNotAllowedError, "method #{req.method} not allowed for route #{req.path}" + raise Puppet::Network::HTTP::Error::HTTPMethodNotAllowedError.new("method #{req.method} not allowed for route #{req.path}", Puppet::Network::HTTP::Issues::UNSUPPORTED_METHOD) end attr_reader :path_matcher diff --git a/spec/unit/network/http/handler_spec.rb b/spec/unit/network/http/handler_spec.rb index 31fc46e11..345818b4a 100755 --- a/spec/unit/network/http/handler_spec.rb +++ b/spec/unit/network/http/handler_spec.rb @@ -64,9 +64,33 @@ describe Puppet::Network::HTTP::Handler do handler.process(req, res) - expect(res[:body]).to eq("Not Found: No route for GET /vtest/foo") + res_body = JSON(res[:body]) + + expect(res[:content_type_header]).to eq("application/json") + expect(res_body["issue_kind"]).to eq("HANDLER_NOT_FOUND") + expect(res_body["message"]).to eq("Not Found: No route for GET /vtest/foo") expect(res[:status]).to eq(404) end + + it "returns a structured error response with a stacktrace when the server encounters an internal error" do + handler = TestingHandler.new( + Puppet::Network::HTTP::Route.path(/.*/).get(lambda { |_, _| raise Exception.new("the sky is falling!")})) + + req = a_request("GET", "/vtest/foo") + res = {} + + handler.process(req, res) + + res_body = JSON(res[:body]) + + expect(res[:content_type_header]).to eq("application/json") + expect(res_body["issue_kind"]).to eq(Puppet::Network::HTTP::Issues::RUNTIME_ERROR.to_s) + expect(res_body["message"]).to eq("Server Error: the sky is falling!") + expect(res_body["stacktrace"].is_a?(Array) && !res_body["stacktrace"].empty?).to be_true + expect(res_body["stacktrace"][0]).to match("spec/unit/network/http/handler_spec.rb") + expect(res[:status]).to eq(500) + end + end describe "when processing a request" do @@ -163,7 +187,7 @@ describe Puppet::Network::HTTP::Handler do end def set_content_type(response, format) - "my_result" + response[:content_type_header] = format end def set_response(response, body, status = 200) From 7ce5d104932df7b7928935c5a966049a684f5d5d Mon Sep 17 00:00:00 2001 From: Dan Lidral-Porter Date: Wed, 15 Jan 2014 14:59:46 -0800 Subject: [PATCH 442/800] (PUP-1432) Move JSON error ser8n to error classes Rather than serializing in the generic HTTP handler. --- lib/puppet/network/http/error.rb | 18 ++++++++++++++++-- lib/puppet/network/http/handler.rb | 12 ++++-------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/lib/puppet/network/http/error.rb b/lib/puppet/network/http/error.rb index 01b7ecb55..4e521d130 100644 --- a/lib/puppet/network/http/error.rb +++ b/lib/puppet/network/http/error.rb @@ -1,3 +1,5 @@ +require 'json' + module Puppet::Network::HTTP::Error Issues = Puppet::Network::HTTP::Issues @@ -9,6 +11,10 @@ module Puppet::Network::HTTP::Error @status = status @issue_kind = issue_kind end + + def to_json + JSON({:message => message, :issue_kind => @issue_kind}) + end end class HTTPNotAcceptableError < HTTPError @@ -48,8 +54,16 @@ module Puppet::Network::HTTP::Error class HTTPServerError < HTTPError CODE = 500 - def initialize(message, issue_kind = Issues::RUNTIME_ERROR) - super("Server Error: " + message, CODE, issue_kind) + + attr_reader :backtrace + + def initialize(original_error, issue_kind = Issues::RUNTIME_ERROR) + super("Server Error: " + original_error.message, CODE, issue_kind) + @backtrace = original_error.backtrace + end + + def to_json + JSON({:message => message, :issue_kind => @issue_kind, :stacktrace => self.backtrace}) end end end diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb index 1f6d9d546..9ca2a3b85 100644 --- a/lib/puppet/network/http/handler.rb +++ b/lib/puppet/network/http/handler.rb @@ -8,7 +8,6 @@ require 'puppet/network/authentication' require 'puppet/network/rights' require 'puppet/util/profiler' require 'resolv' -require 'json' module Puppet::Network::HTTP::Handler include Puppet::Network::Authentication @@ -71,15 +70,12 @@ module Puppet::Network::HTTP::Handler end rescue Puppet::Network::HTTP::Error::HTTPError => e - msg = e.message - Puppet.info(msg) - structured_msg = JSON({:message => msg, :issue_kind => e.issue_kind}) - new_response.respond_with(e.status, "application/json", structured_msg) + Puppet.info(e.message) + new_response.respond_with(e.status, "application/json", e.to_json) rescue Exception => e - http_e = Puppet::Network::HTTP::Error::HTTPServerError.new(e.message) + http_e = Puppet::Network::HTTP::Error::HTTPServerError.new(e) Puppet.err(http_e.message) - structured_msg = JSON({:message => http_e.message, :issue_kind => http_e.issue_kind, :stacktrace => e.backtrace}) - new_response.respond_with(500, "application/json", structured_msg) + new_response.respond_with(http_e.status, "application/json", http_e.to_json) ensure cleanup(request) end From 208f6e98a26252fcecf2e02edd154fab817a0fda Mon Sep 17 00:00:00 2001 From: Dan Lidral-Porter Date: Wed, 15 Jan 2014 16:20:27 -0800 Subject: [PATCH 443/800] (PUP-1432) Fix error JSON schema The type of the items in the stacktrace array was not correctly specified. --- api/schemas/error.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/schemas/error.json b/api/schemas/error.json index 730dcf5c7..7cc570446 100644 --- a/api/schemas/error.json +++ b/api/schemas/error.json @@ -15,7 +15,7 @@ "stacktrace": { "description": "For 5xx responses only, a ruby stacktrace to where an error occurred.", "type": "array", - "items": "string" + "items": { "type": "string" } } }, "required": ["message", "issue_kind"], From a8e93589030766ea576012920b8b4686c75bf4bd Mon Sep 17 00:00:00 2001 From: Dan Lidral-Porter Date: Wed, 15 Jan 2014 16:23:11 -0800 Subject: [PATCH 444/800] (PUP-1432) Add error spec that tests serialization --- spec/unit/network/http/error_spec.rb | 30 ++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 spec/unit/network/http/error_spec.rb diff --git a/spec/unit/network/http/error_spec.rb b/spec/unit/network/http/error_spec.rb new file mode 100644 index 000000000..0df295038 --- /dev/null +++ b/spec/unit/network/http/error_spec.rb @@ -0,0 +1,30 @@ +require 'spec_helper' +require 'matchers/json' + +require 'puppet/network/http' + +describe Puppet::Network::HTTP::Error do + include JSONMatchers + + describe Puppet::Network::HTTP::Error::HTTPError do + it "should serialize to JSON that matches the error schema" do + error = Puppet::Network::HTTP::Error::HTTPError.new("I don't like the looks of you", 400, :SHIFTY_USER) + + expect(error.to_json).to validate_against('api/schemas/error.json') + end + end + + describe Puppet::Network::HTTP::Error::HTTPServerError do + it "should serialize to JSON that matches the error schema and has the optional stacktrace property" do + begin + raise Exception, "a wild Exception appeared!" + rescue Exception => e + culpable = e + end + error = Puppet::Network::HTTP::Error::HTTPServerError.new(culpable) + + expect(error.to_json).to validate_against('api/schemas/error.json') + end + end + +end From f44bca496a1c38ed1cdead4a32d9ae8d8a3c930f Mon Sep 17 00:00:00 2001 From: Dan Lidral-Porter Date: Wed, 15 Jan 2014 16:35:20 -0800 Subject: [PATCH 445/800] (maint) really fix the execution locale test --- spec/integration/util/execution_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/integration/util/execution_spec.rb b/spec/integration/util/execution_spec.rb index a5be9ab65..b14798236 100644 --- a/spec/integration/util/execution_spec.rb +++ b/spec/integration/util/execution_spec.rb @@ -2,13 +2,13 @@ require 'spec_helper' describe Puppet::Util::Execution do describe "#execpipe" do - it "should set LANG to C avoid localized output", :if => !Puppet.features.microsoft_windows do + it "should set LANG to C avoid localized output", :if => !Puppet.features.microsoft_windows? do out = "" Puppet::Util::Execution.execpipe('echo $LANG'){ |line| out << line.read.chomp } expect(out).to eq("C") end - it "should set LC_ALL to C avoid localized output" :if => !Puppet.features.microsoft_windows do + it "should set LC_ALL to C avoid localized output", :if => !Puppet.features.microsoft_windows? do out = "" Puppet::Util::Execution.execpipe('echo $LC_ALL'){ |line| out << line.read.chomp } expect(out).to eq("C") From 40193c53a4a0f497e6aadc41e9a85b07e9265e4e Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 3 Jan 2014 16:29:17 -0800 Subject: [PATCH 446/800] (PUP-1282) Express Windows specific gem dependencies Previously, if a ruby module on windows tried to use puppet as a library, the calling module had to know about puppet's platform specific dependencies. This comes up when trying to develop and test puppet modules on windows. This commit adds the windows specific gems to project_data.yaml, and updates the Gemfile to add these dependencies when during bundle install The build pipeline will automatically create an x86-mingw32 gem in addition to the generic one. --- Gemfile | 26 ++++++++++++-------------- ext/project_data.yaml | 18 ++++++++++++++++++ 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/Gemfile b/Gemfile index 5607f1fc5..824660a6b 100644 --- a/Gemfile +++ b/Gemfile @@ -59,20 +59,18 @@ group(:extra) do gem "msgpack", :require => false end -platforms :mswin, :mingw do - gem "ffi", "1.9.0", :require => false - gem "sys-admin", "1.5.6", :require => false - gem "win32-api", "1.4.8", :require => false - gem "win32-dir", "0.4.3", :require => false - gem "win32-eventlog", "0.5.3", :require => false - gem "win32-process", "0.6.5", :require => false - gem "win32-security", "0.1.4", :require => false - gem "win32-service", "0.7.2", :require => false - gem "win32-taskscheduler", "0.2.2", :require => false - gem "win32console", "1.3.2", :require => false - gem "windows-api", "0.4.2", :require => false - gem "windows-pr", "1.2.2", :require => false - gem "minitar", "0.5.4", :require => false +require 'yaml' +data = YAML.load_file(File.join(File.dirname(__FILE__), 'ext', 'project_data.yaml')) +bundle_platforms = data['bundle_platforms'] +data['gem_platform_dependencies'].each_pair do |gem_platform, info| + if bundle_deps = info['gem_runtime_dependencies'] + bundle_platform = bundle_platforms[gem_platform] or raise "Missing bundle_platform" + platform(bundle_platform.intern) do + bundle_deps.each_pair do |name, version| + gem(name, version, :require => false) + end + end + end end if File.exists? "#{__FILE__}.local" diff --git a/ext/project_data.yaml b/ext/project_data.yaml index f5f74cdbe..cb4106ec7 100644 --- a/ext/project_data.yaml +++ b/ext/project_data.yaml @@ -24,3 +24,21 @@ gem_rdoc_options: - --main - README.md - --line-numbers +gem_platform_dependencies: + x86-mingw32: + gem_runtime_dependencies: + ffi: '1.9.0' + sys-admin: '1.5.6' + win32-api: '1.4.8' + win32-dir: '0.4.3' + win32-eventlog: '0.5.3' + win32-process: '0.6.5' + win32-security: '0.1.4' + win32-service: '0.7.2' + win32-taskscheduler: '0.2.2' + win32console: '1.3.2' + windows-api: '0.4.2' + windows-pr: '1.2.2' + minitar: '0.5.4' +bundle_platforms: + x86-mingw32: mingw From bdce6832a9651abf2ddd9cec6f5bdd63d5777bd0 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Thu, 16 Jan 2014 12:24:52 -0800 Subject: [PATCH 447/800] (PUP-1118) Introduce "create" to make specific environments Environments are more than just a name, they are also a modulepath and a manifest. This commit makes a "create" method for setting up environments with specific modulepaths and manifests rather than relying on the Puppet Settings at the time that methods are called. In order to control environments for module installation they now take advantage of this in order to control the modulepath that things will be installed into. --- lib/puppet.rb | 1 + lib/puppet/environments.rb | 71 ++++++++++++++- lib/puppet/face/module/list.rb | 20 ++-- .../module_tool/applications/installer.rb | 2 +- .../module_tool/applications/uninstaller.rb | 2 +- lib/puppet/network/http/handler.rb | 1 - lib/puppet/node/environment.rb | 91 +++++++++++-------- spec/unit/face/module/list_spec.rb | 66 ++++++++------ .../applications/installer_spec.rb | 31 +++++-- .../applications/uninstaller_spec.rb | 49 +++++----- spec/unit/node/environment_spec.rb | 37 +------- 11 files changed, 230 insertions(+), 141 deletions(-) diff --git a/lib/puppet.rb b/lib/puppet.rb index 05ad5917d..c75000127 100644 --- a/lib/puppet.rb +++ b/lib/puppet.rb @@ -185,3 +185,4 @@ require 'puppet/util/storage' require 'puppet/status' require 'puppet/file_bucket/file' require 'puppet/context' +require 'puppet/environments' diff --git a/lib/puppet/environments.rb b/lib/puppet/environments.rb index f0b6cbca4..8724fe651 100644 --- a/lib/puppet/environments.rb +++ b/lib/puppet/environments.rb @@ -1,11 +1,80 @@ module Puppet::Environments + module EnvironmentCreator + def for(module_path, manifest) + Puppet::Node::Environment.create(:anonymous, + module_path.split(File::PATH_SEPARATOR), + manifest) + end + end + class OnlyProduction def search_paths - [] + ["environments://static/production"] end def list [Puppet::Node::Environment.new(:production)] end end + + class Static + include EnvironmentCreator + + def initialize(*environments) + @environments = environments + end + + def search_paths + ["environments://static/memory"] + end + + def list + @environments + end + + def get(name) + @environments.find do |env| + env.name == name.intern + end + end + end + + class Legacy + include EnvironmentCreator + + def search_paths + ["environments://legacy/cached"] + end + + def list + [] + end + + def get(name) + Puppet::Node::Environment.new(name) + end + end + + class Combined + def initialize(*loaders) + @loaders = loaders + end + + def search_paths + @loaders.collect(&:search_paths).flatten + end + + def list + @loaders.collect(&:list).flatten + end + + def get(name) + @loaders.each do |loader| + if env = loader.get(name) + return env + end + end + nil + end + end end diff --git a/lib/puppet/face/module/list.rb b/lib/puppet/face/module/list.rb index 83523c78b..e4c30ed04 100644 --- a/lib/puppet/face/module/list.rb +++ b/lib/puppet/face/module/list.rb @@ -58,17 +58,12 @@ Puppet::Face.define(:module, '1.0.0') do EOT when_invoked do |options| - Puppet[:modulepath] = options[:modulepath] if options[:modulepath] - environment = Puppet::Node::Environment.new(options[:environment]) - - environment.modules_by_path + environment_from_options(options).modules_by_path end when_rendering :console do |modules_by_path, options| output = '' - - Puppet[:modulepath] = options[:modulepath] if options[:modulepath] - environment = Puppet::Node::Environment.new(options[:environment]) + environment = environment_from_options(options) error_types = { :non_semantic_version => { @@ -171,6 +166,17 @@ Puppet::Face.define(:module, '1.0.0') do end end + def environment_from_options(options) + environments = Puppet::Context.lookup(:environments) + if options[:modulepath] + environments.for(options[:modulepath], '') + elsif options[:environment] + environments.get(options[:environment]) + else + environments.get(Puppet[:environment]) + end + end + # Prepare a list of module objects and their dependencies for print in a # tree view. # diff --git a/lib/puppet/module_tool/applications/installer.rb b/lib/puppet/module_tool/applications/installer.rb index e3805a149..fb7fb78cd 100644 --- a/lib/puppet/module_tool/applications/installer.rb +++ b/lib/puppet/module_tool/applications/installer.rb @@ -18,7 +18,7 @@ module Puppet::ModuleTool def initialize(name, forge, install_dir, options = {}) super(options) @action = :install - @environment = Puppet::Node::Environment.new(Puppet.settings[:environment]) + @environment = Puppet::Context.lookup(:environments).get(Puppet[:environment]) @force = options[:force] @ignore_dependencies = options[:force] || options[:ignore_dependencies] @name = name diff --git a/lib/puppet/module_tool/applications/uninstaller.rb b/lib/puppet/module_tool/applications/uninstaller.rb index f81ff9514..4f88842ea 100644 --- a/lib/puppet/module_tool/applications/uninstaller.rb +++ b/lib/puppet/module_tool/applications/uninstaller.rb @@ -10,7 +10,7 @@ module Puppet::ModuleTool @unfiltered = [] @installed = [] @suggestions = [] - @environment = Puppet::Node::Environment.new(options[:environment]) + @environment = Puppet::Context.lookup(:environments).get(options[:environment]) end def run diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb index e3366f8e8..bcebd81eb 100644 --- a/lib/puppet/network/http/handler.rb +++ b/lib/puppet/network/http/handler.rb @@ -1,7 +1,6 @@ module Puppet::Network::HTTP end -require 'puppet/environments' require 'puppet/network/http' require 'puppet/network/http/api/v1' require 'puppet/network/authentication' diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb index 53b429ad3..615eae43c 100644 --- a/lib/puppet/node/environment.rb +++ b/lib/puppet/node/environment.rb @@ -112,11 +112,42 @@ class Puppet::Node::Environment return seen[symbol] if seen[symbol] - obj = self.allocate - obj.send :initialize, symbol + obj = self.create(symbol, + split_path(Puppet.settings.value(:modulepath, symbol)), + Puppet.settings.value(:manifest, symbol)) seen[symbol] = obj end + # Create a new environment with the given name + # + # @param name [Symbol] the name of the + # @param modulepath [Array] the list of paths from which to load modules + # @param manifest [String] the path to the manifest for the environment + # @return [Puppet::Node::Environment] + # + # @api public + def self.create(name, modulepath, manifest) + obj = self.allocate + obj.send(:initialize, + name, + validate_dirs(extralibs() + modulepath), + manifest) + obj + end + + # Instantiate a new environment + # + # @note {Puppet::Node::Environment.new} is overridden to return memoized + # objects, so this will not be invoked with the normal Ruby initialization + # semantics. + # + # @param name [Symbol] The environment name + def initialize(name, modulepath, manifest) + @name = name + @modulepath = modulepath + @manifest = manifest + end + # Retrieve the environment for the current thread # # @note This should only used when a catalog is being compiled. @@ -153,7 +184,7 @@ class Puppet::Node::Environment # # @api private def self.root - @root ||= new(:'*root*') + @root ||= create(:'*root*', split_path(Puppet[:modulepath]), Puppet[:manifest]) end # Clear all memoized environments and the 'current' environment @@ -170,6 +201,11 @@ class Puppet::Node::Environment # environment identifier attr_reader :name + # @!attribute [r] modulepath + # @api public + # @return [Array] All directories present in the modulepath + attr_reader :modulepath + # @!attribute [r] manifest # @api public # @return [String] path to the manifest file or directory. @@ -185,19 +221,6 @@ class Puppet::Node::Environment Puppet.settings.value(param, self.name) end - # Instantiate a new environment - # - # @note {Puppet::Node::Environment.new} is overridden to return memoized - # objects, so this will not be invoked with the normal Ruby initialization - # semantics. - # - # @param name [Symbol] The environment name - def initialize(name) - @name = name - @modulepath = Puppet.settings.value(:modulepath, name) - @manifest = Puppet.settings.value(:manifest, name) - end - # The current global TypeCollection # # @note The environment is loosely coupled with the {Puppet::Resource::TypeCollection} @@ -275,21 +298,6 @@ class Puppet::Node::Environment nil end - # @!attribute [r] modulepath - # Return all existent directories in the modulepath for this environment - # @note This value is cached so that the filesystem doesn't have to be - # re-enumerated every time this method is invoked, since that - # enumeration could be a costly operation and this method is called - # frequently. The cache expiry is determined by `Puppet[:filetimeout]`. - # @see Puppet::Util::Cacher.cached_attr - # @api public - # @return [Array] All directories present in the modulepath - cached_attr(:modulepath, Puppet[:filetimeout]) do - dirs = @modulepath.split(File::PATH_SEPARATOR) - dirs = ENV["PUPPETLIB"].split(File::PATH_SEPARATOR) + dirs if ENV["PUPPETLIB"] - validate_dirs(dirs) - end - # @!attribute [r] modules # Return all modules for this environment in the order they appear in the # modulepath. @@ -447,13 +455,24 @@ class Puppet::Node::Environment self.to_s.to_zaml(z) end + private + + def self.split_path(path_string) + path_string.split(File::PATH_SEPARATOR) + end + + def self.extralibs() + if ENV["PUPPETLIB"] + split_path(ENV["PUPPETLIB"]) + else + [] + end + end + # Validate a list of file paths and return the paths that are directories on the filesystem - # - # @api private - # # @param dirs [Array] The file paths to validate # @return [Array] All file paths that exist and are directories - def validate_dirs(dirs) + def self.validate_dirs(dirs) dirs.collect do |dir| File.expand_path(dir) end.find_all do |p| @@ -461,8 +480,6 @@ class Puppet::Node::Environment end end - private - # Reparse the manifests for the given environment # # There are two sources that can be used for the initial parse: diff --git a/spec/unit/face/module/list_spec.rb b/spec/unit/face/module/list_spec.rb index 3ffb474dc..ef4cd04a9 100644 --- a/spec/unit/face/module/list_spec.rb +++ b/spec/unit/face/module/list_spec.rb @@ -20,6 +20,12 @@ describe "puppet module list" do FileUtils.mkdir_p(@modpath2) end + around do |example| + Puppet::Context.override(:environments => Puppet::Environments::Legacy.new()) do + example.run + end + end + it "should return an empty list per dir in path if there are no modules" do Puppet.settings[:modulepath] = @modulepath Puppet::Face[:module, :current].list.should == { @@ -48,43 +54,51 @@ describe "puppet module list" do foomod = PuppetSpec::Modules.create('foo', @modpath1) barmod = PuppetSpec::Modules.create('bar', @modpath1) - usedenv = Puppet::Node::Environment.new('useme') - usedenv.modulepath = [@modpath1, @modpath2] + usedenv = Puppet::Node::Environment.create(:useme, [@modpath1, @modpath2], '') - Puppet::Face[:module, :current].list(:environment => 'useme').should == { - @modpath1 => [ - Puppet::Module.new('bar', barmod.path, usedenv), - Puppet::Module.new('foo', foomod.path, usedenv) - ], - @modpath2 => [] - } + Puppet::Context.override(:environments => Puppet::Environments::Static.new(usedenv)) do + Puppet::Face[:module, :current].list(:environment => 'useme').should == { + @modpath1 => [ + Puppet::Module.new('bar', barmod.path, usedenv), + Puppet::Module.new('foo', foomod.path, usedenv) + ], + @modpath2 => [] + } + end end it "should use the specified modulepath" do foomod = PuppetSpec::Modules.create('foo', @modpath1) barmod = PuppetSpec::Modules.create('bar', @modpath2) - Puppet::Face[:module, :current].list(:modulepath => "#{@modpath1}#{File::PATH_SEPARATOR}#{@modpath2}").should == { - @modpath1 => [ Puppet::Module.new('foo', foomod.path, Puppet::Node::Environment.new) ], - @modpath2 => [ Puppet::Module.new('bar', barmod.path, Puppet::Node::Environment.new) ] - } + modules = Puppet::Face[:module, :current].list(:modulepath => "#{@modpath1}#{File::PATH_SEPARATOR}#{@modpath2}") + + expect(modules[@modpath1].first.name).to eq('foo') + expect(modules[@modpath1].first.path).to eq(foomod.path) + expect(modules[@modpath1].first.environment.modulepath).to eq([@modpath1, @modpath2]) + + expect(modules[@modpath2].first.name).to eq('bar') + expect(modules[@modpath2].first.path).to eq(barmod.path) + expect(modules[@modpath2].first.environment.modulepath).to eq([@modpath1, @modpath2]) end - it "should use the specified modulepath over the specified environment in place of the environment's default path" do - foomod1 = PuppetSpec::Modules.create('foo', @modpath1) - barmod2 = PuppetSpec::Modules.create('bar', @modpath2) - env = Puppet::Node::Environment.new('myenv') - env.modulepath = ['/tmp/notused'] + it "prefers a given modulepath over the modulepath from the given environment" do + foomod = PuppetSpec::Modules.create('foo', @modpath1) + barmod = PuppetSpec::Modules.create('bar', @modpath2) + env = Puppet::Node::Environment.create(:myenv, ['/tmp/notused'], '') + Puppet[:modulepath] = "" - list = Puppet::Face[:module, :current].list(:environment => 'myenv', :modulepath => "#{@modpath1}#{File::PATH_SEPARATOR}#{@modpath2}") + modules = Puppet::Face[:module, :current].list(:environment => 'myenv', :modulepath => "#{@modpath1}#{File::PATH_SEPARATOR}#{@modpath2}") - # Changing Puppet[:modulepath] causes Puppet::Node::Environment.new('myenv') - # to have a different object_id than the env above - env = Puppet::Node::Environment.new('myenv') - list.should == { - @modpath1 => [ Puppet::Module.new('foo', foomod1.path, env) ], - @modpath2 => [ Puppet::Module.new('bar', barmod2.path, env) ] - } + expect(modules[@modpath1].first.name).to eq('foo') + expect(modules[@modpath1].first.path).to eq(foomod.path) + expect(modules[@modpath1].first.environment.modulepath).to eq([@modpath1, @modpath2]) + expect(modules[@modpath1].first.environment.name).to_not eq(:myenv) + + expect(modules[@modpath2].first.name).to eq('bar') + expect(modules[@modpath2].first.path).to eq(barmod.path) + expect(modules[@modpath2].first.environment.modulepath).to eq([@modpath1, @modpath2]) + expect(modules[@modpath2].first.environment.name).to_not eq(:myenv) end describe "inline documentation" do diff --git a/spec/unit/module_tool/applications/installer_spec.rb b/spec/unit/module_tool/applications/installer_spec.rb index 3d3461949..30712b7c4 100644 --- a/spec/unit/module_tool/applications/installer_spec.rb +++ b/spec/unit/module_tool/applications/installer_spec.rb @@ -6,20 +6,31 @@ require 'semver' describe Puppet::ModuleTool::Applications::Installer, :unless => Puppet.features.microsoft_windows? do include PuppetSpec::Files - before do - FileUtils.mkdir_p(modpath1) - fake_env.modulepath = [modpath1] - FileUtils.touch(stdlib_pkg) - Puppet.settings[:modulepath] = modpath1 - end - let(:unpacker) { stub(:run) } let(:installer_class) { Puppet::ModuleTool::Applications::Installer } - let(:modpath1) { File.join(tmpdir("installer"), "modpath1") } - let(:stdlib_pkg) { File.join(modpath1, "pmtacceptance-stdlib-0.0.1.tar.gz") } - let(:fake_env) { Puppet::Node::Environment.new('fake_env') } + let(:modpath1) do + path = File.join(tmpdir("installer"), "modpath1") + FileUtils.mkdir_p(path) + path + end + let(:stdlib_pkg) do + mod = File.join(modpath1, "pmtacceptance-stdlib-0.0.1.tar.gz") + FileUtils.touch(mod) + mod + end + let(:env) { Puppet::Node::Environment.create(:env, [modpath1], '') } let(:options) { { :target_dir => modpath1 } } + before do + Puppet[:environment] = :env + end + + around :each do |example| + Puppet::Context.override(:environments => Puppet::Environments::Static.new(env)) do + example.run + end + end + let(:forge) do forge = mock("Puppet::Forge") diff --git a/spec/unit/module_tool/applications/uninstaller_spec.rb b/spec/unit/module_tool/applications/uninstaller_spec.rb index da9ca1224..e1fe6eba5 100644 --- a/spec/unit/module_tool/applications/uninstaller_spec.rb +++ b/spec/unit/module_tool/applications/uninstaller_spec.rb @@ -20,18 +20,24 @@ describe Puppet::ModuleTool::Applications::Uninstaller do end describe "the behavior of the instances" do + let(:working_dir) { tmpdir("uninstaller") } + let(:modpath1) { create_temp_dir("modpath1") } + let(:modpath2) { create_temp_dir("modpath2") } + let(:env) { Puppet::Node::Environment.create(:env, [modpath1, modpath2], '') } + let(:options) { {:environment => "env"} } + let(:uninstaller) { Puppet::ModuleTool::Applications::Uninstaller.new("puppetlabs-foo", options) } - before do - @uninstaller = Puppet::ModuleTool::Applications::Uninstaller - FileUtils.mkdir_p(modpath1) - FileUtils.mkdir_p(modpath2) - fake_env.modulepath = [modpath1, modpath2] + around :each do |example| + Puppet::Context.override(:environments => Puppet::Environments::Static.new(env)) do + example.run + end end - let(:modpath1) { File.join(tmpdir("uninstaller"), "modpath1") } - let(:modpath2) { File.join(tmpdir("uninstaller"), "modpath2") } - let(:fake_env) { Puppet::Node::Environment.new('fake_env') } - let(:options) { {:environment => "fake_env"} } + def create_temp_dir(name) + path = File.join(working_dir, name) + FileUtils.mkdir_p(path) + path + end let(:foo_metadata) do { @@ -57,7 +63,7 @@ describe Puppet::ModuleTool::Applications::Uninstaller do context "when the module is not installed" do it "should fail" do - @uninstaller.new('fakemod_not_installed', options).run[:result].should == :failure + Puppet::ModuleTool::Applications::Uninstaller.new('fakemod_not_installed', options).run[:result].should == :failure end end @@ -66,7 +72,8 @@ describe Puppet::ModuleTool::Applications::Uninstaller do it "should uninstall the module" do PuppetSpec::Modules.create('foo', modpath1, :metadata => foo_metadata) - results = @uninstaller.new("puppetlabs-foo", options).run + results = uninstaller.run + results[:affected_modules].first.forge_name.should == "puppetlabs/foo" end @@ -74,7 +81,7 @@ describe Puppet::ModuleTool::Applications::Uninstaller do PuppetSpec::Modules.create('foo', modpath1, :metadata => foo_metadata) PuppetSpec::Modules.create('bar', modpath1, :metadata => bar_metadata) - results = @uninstaller.new("puppetlabs-foo", options).run + results = uninstaller.run results[:affected_modules].length == 1 results[:affected_modules].first.forge_name.should == "puppetlabs/foo" end @@ -83,7 +90,7 @@ describe Puppet::ModuleTool::Applications::Uninstaller do PuppetSpec::Modules.create('foo', modpath1, :metadata => foo_metadata) PuppetSpec::Modules.create('foo', modpath2, :metadata => foo_metadata) - @uninstaller.new('puppetlabs-foo', options).run[:result].should == :failure + uninstaller.run[:result].should == :failure end context "when options[:version] is specified" do @@ -93,7 +100,7 @@ describe Puppet::ModuleTool::Applications::Uninstaller do options[:version] = "1.0.0" - results = @uninstaller.new("puppetlabs-foo", options).run + results = uninstaller.run results[:affected_modules].length.should == 1 results[:affected_modules].first.forge_name.should == "puppetlabs/foo" results[:affected_modules].first.version.should == "1.0.0" @@ -104,7 +111,7 @@ describe Puppet::ModuleTool::Applications::Uninstaller do options[:version] = "2.0.0" - @uninstaller.new("puppetlabs-foo", options).run[:result].should == :failure + uninstaller.run[:result].should == :failure end end @@ -113,7 +120,7 @@ describe Puppet::ModuleTool::Applications::Uninstaller do it "should not uninstall the module" do PuppetSpec::Modules.create('foo', modpath1) - @uninstaller.new("puppetlabs-foo", options).run[:result].should == :failure + uninstaller.run[:result].should == :failure end end @@ -123,7 +130,7 @@ describe Puppet::ModuleTool::Applications::Uninstaller do mod = PuppetSpec::Modules.create('foo', modpath1, :metadata => foo_metadata) Puppet::ModuleTool::Applications::Checksummer.expects(:run).with(mod.path).returns(['change']) - @uninstaller.new("puppetlabs-foo", options).run[:result].should == :failure + uninstaller.run[:result].should == :failure end end @@ -133,7 +140,7 @@ describe Puppet::ModuleTool::Applications::Uninstaller do it "should uninstall the module" do PuppetSpec::Modules.create('foo', modpath1, :metadata => foo_metadata) - results = @uninstaller.new("puppetlabs-foo", options).run + results = uninstaller.run results[:affected_modules].length.should == 1 results[:affected_modules].first.forge_name.should == "puppetlabs/foo" end @@ -156,7 +163,7 @@ describe Puppet::ModuleTool::Applications::Uninstaller do } ) - @uninstaller.new("puppetlabs-foo", options).run[:result].should == :failure + uninstaller.run[:result].should == :failure end end @@ -174,7 +181,7 @@ describe Puppet::ModuleTool::Applications::Uninstaller do foo = mkmod("foo", modpath1, foo_metadata) options[:force] = true - results = @uninstaller.new("puppetlabs-foo", options).run + results = uninstaller.run results[:affected_modules].length.should == 1 results[:affected_modules].first.forge_name.should == "puppetlabs/foo" end @@ -196,7 +203,7 @@ describe Puppet::ModuleTool::Applications::Uninstaller do ) options[:force] = true - results = @uninstaller.new("puppetlabs-foo", options).run + results = uninstaller.run results[:affected_modules].length.should == 1 results[:affected_modules].first.forge_name.should == "puppetlabs/foo" end diff --git a/spec/unit/node/environment_spec.rb b/spec/unit/node/environment_spec.rb index b77e3bcce..fbe791972 100755 --- a/spec/unit/node/environment_spec.rb +++ b/spec/unit/node/environment_spec.rb @@ -17,10 +17,6 @@ describe Puppet::Node::Environment do end shared_examples_for 'the environment' do - it "should use the filetimeout for the ttl for the modulepath" do - Puppet::Node::Environment.attr_ttl(:modulepath).should == Integer(Puppet[:filetimeout]) - end - it "should use the filetimeout for the ttl for the module list" do Puppet::Node::Environment.attr_ttl(:modules).should == Integer(Puppet[:filetimeout]) end @@ -128,29 +124,6 @@ describe Puppet::Node::Environment do end end - describe "when validating modulepath or manifestdir directories" do - before :each do - @path_one = tmpdir("path_one") - @path_two = tmpdir("path_one") - sep = File::PATH_SEPARATOR - Puppet[:modulepath] = "#{@path_one}#{sep}#{@path_two}" - end - - it "should not return non-directories" do - FileTest.expects(:directory?).with(@path_one).returns true - FileTest.expects(:directory?).with(@path_two).returns false - - env.validate_dirs([@path_one, @path_two]).should == [@path_one] - end - - it "should use the current working directory to fully-qualify unqualified paths" do - FileTest.stubs(:directory?).returns true - two = File.expand_path("two") - - env.validate_dirs([@path_one, 'two']).should == [@path_one, two] - end - end - describe "when modeling a specific environment" do it "should have a method for returning the environment name" do Puppet::Node::Environment.new("testing").name.should == :testing @@ -185,7 +158,7 @@ describe Puppet::Node::Environment do it "should return nil if asked for a module that does not exist in its path" do modpath = tmpdir('modpath') - env.modulepath = [modpath] + env = Puppet::Node::Environment.create(:testing, [modpath], '') env.module("one").should be_nil end @@ -371,14 +344,6 @@ describe Puppet::Node::Environment do end end - - it "should cache the module list" do - env.modulepath = %w{/a} - Dir.expects(:entries).once.with("/a").returns %w{foo} - - env.modules - env.modules - end end describe Puppet::Node::Environment::Helper do From 58a6cc57e8d0ce586e93ea92300c64f58fb06b27 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Thu, 16 Jan 2014 15:08:51 -0800 Subject: [PATCH 448/800] (PUP-1118) Provide context at initialization for environments When puppet is initialized it now also sets up a environments service so that other parts of the code and go about their merry way. This also changes the handling of component to work the same way as other types so that an extra, and confusing, require isn't needed. --- lib/puppet.rb | 1 + lib/puppet/node/environment.rb | 26 +++++++++++++++----------- lib/puppet/resource.rb | 7 ++----- spec/unit/context_spec.rb | 5 ++--- 4 files changed, 20 insertions(+), 19 deletions(-) diff --git a/lib/puppet.rb b/lib/puppet.rb index c75000127..7430e55b9 100644 --- a/lib/puppet.rb +++ b/lib/puppet.rb @@ -149,6 +149,7 @@ module Puppet run_mode = Puppet::Util::RunMode[run_mode] Puppet.settings.initialize_app_defaults(Puppet::Settings.app_defaults_for_run_mode(run_mode)) Puppet::Parser::Functions.reset + Puppet::Context.push(:environments => Puppet::Environments::Legacy.new) end private_class_method :do_initialize_settings_for_run_mode diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb index 615eae43c..e1bb86791 100644 --- a/lib/puppet/node/environment.rb +++ b/lib/puppet/node/environment.rb @@ -130,7 +130,7 @@ class Puppet::Node::Environment obj = self.allocate obj.send(:initialize, name, - validate_dirs(extralibs() + modulepath), + expand_dirs(extralibs() + modulepath), manifest) obj end @@ -201,10 +201,19 @@ class Puppet::Node::Environment # environment identifier attr_reader :name - # @!attribute [r] modulepath - # @api public - # @return [Array] All directories present in the modulepath - attr_reader :modulepath + # @api public + # @return [Array] All directories present on disk in the modulepath + def modulepath + @modulepath.find_all do |p| + FileTest.directory?(p) + end + end + + # @api public + # @return [Array] All directories in the modulepath (even if they are not present on disk) + def full_modulepath + @modulepath + end # @!attribute [r] manifest # @api public @@ -469,14 +478,9 @@ class Puppet::Node::Environment end end - # Validate a list of file paths and return the paths that are directories on the filesystem - # @param dirs [Array] The file paths to validate - # @return [Array] All file paths that exist and are directories - def self.validate_dirs(dirs) + def self.expand_dirs(dirs) dirs.collect do |dir| File.expand_path(dir) - end.find_all do |p| - FileTest.directory?(p) end end diff --git a/lib/puppet/resource.rb b/lib/puppet/resource.rb index 1dd59a0ab..22df43a67 100644 --- a/lib/puppet/resource.rb +++ b/lib/puppet/resource.rb @@ -303,11 +303,8 @@ class Puppet::Resource # Convert our resource to a RAL resource instance. Creates component # instances for resource types that don't exist. def to_ral - if typeklass = Puppet::Type.type(self.type) - return typeklass.new(self) - else - return Puppet::Type::Component.new(self) - end + typeklass = Puppet::Type.type(self.type) || Puppet::Type.type(:component) + typeklass.new(self) end def name diff --git a/spec/unit/context_spec.rb b/spec/unit/context_spec.rb index d7e3bd860..52075443a 100644 --- a/spec/unit/context_spec.rb +++ b/spec/unit/context_spec.rb @@ -18,10 +18,9 @@ describe Puppet::Context do end it "fails if you try to pop off the top of the stack" do - root = Puppet::Context.pop - expect(root).to be_root + while !Puppet::Context.pop.root?; end expect { Puppet::Context.pop }.to raise_error(Puppet::Context::StackUnderflow) - # TestHelper expects to have something to pop in its after_each_test() + # TestHelper expects to have something to pop in its after_each_test() Puppet::Context.push({}) end From 8408ac07970324df0a34ceaeeb50e9dbb08230b1 Mon Sep 17 00:00:00 2001 From: Bart ten Brinke Date: Tue, 15 May 2012 14:53:24 +0200 Subject: [PATCH 449/800] (PUP-1447) Added unless_uid option to User Resource management. --- lib/puppet/type/resources.rb | 39 +++++++- spec/unit/type/resources_spec.rb | 155 +++++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+), 5 deletions(-) diff --git a/lib/puppet/type/resources.rb b/lib/puppet/type/resources.rb index 12bd3cac5..c4da057d7 100644 --- a/lib/puppet/type/resources.rb +++ b/lib/puppet/type/resources.rb @@ -65,6 +65,28 @@ Puppet::Type.newtype(:resources) do } end + newparam(:unless_uid) do + desc "This keeps specific uids or ranges of uids from being purged when purge is true. + Accepts ranges, integers and (mixed) arrays of both." + + munge do |value| + case value + when /^\d+/ + [Integer(value)] + when Integer + [value] + when Range + [value] + when Array + value + when /^\[\d+/ + value.split(',').collect{|x| x.include?('..') ? Integer(x.split('..')[0])..Integer(x.split('..')[1]) : Integer(x) } + else + raise ArgumentError, "Invalid value #{value.inspect}" + end + end + end + def check(resource) @checkmethod ||= "#{self[:name]}_check" @hascheck ||= respond_to?(@checkmethod) @@ -112,18 +134,25 @@ Puppet::Type.newtype(:resources) do @resource_type end - # Make sure we don't purge users below a certain uid, if the check - # is enabled. + # Make sure we don't purge users with specific uids def user_check(resource) return true unless self[:name] == "user" return true unless self[:unless_system_user] - resource[:audit] = :uid + current_values = resource.retrieve_resource + current_uid = current_values[resource.property(:uid)] + unless_uids = self[:unless_uid] return false if system_users.include?(resource[:name]) - current_values = resource.retrieve_resource - current_values[resource.property(:uid)] > self[:unless_system_user] + if unless_uids && unless_uids.length > 0 + unless_uids.each do |unless_uid| + return false if unless_uid == current_uid + return false if unless_uid.respond_to?('include?') && unless_uid.include?(current_uid) + end + end + + current_uid > self[:unless_system_user] end def system_users diff --git a/spec/unit/type/resources_spec.rb b/spec/unit/type/resources_spec.rb index 30b60edf4..f08afd7ae 100755 --- a/spec/unit/type/resources_spec.rb +++ b/spec/unit/type/resources_spec.rb @@ -51,6 +51,161 @@ describe resources do end end + describe "#check_user purge behaviour" do + describe "with unless_system_user => true" do + before do + @res = Puppet::Type.type(:resources).new :name => :user, :purge => true, :unless_system_user => true + @res.catalog = Puppet::Resource::Catalog.new + end + + it "should never purge hardcoded system users" do + %w{root nobody bin noaccess daemon sys}.each do |sys_user| + @res.user_check(Puppet::Type.type(:user).new(:name => sys_user)).should be_false + end + end + + it "should not purge system users if unless_system_user => true" do + user_hash = {:name => 'system_user', :uid => 125, :system => true} + user = Puppet::Type.type(:user).new(user_hash) + user.stubs(:retrieve_resource).returns Puppet::Resource.new("user", user_hash[:name], :parameters => user_hash) + @res.user_check(user).should be_false + end + + it "should purge manual users if unless_system_user => true" do + user_hash = {:name => 'system_user', :uid => 525, :system => true} + user = Puppet::Type.type(:user).new(user_hash) + user.stubs(:retrieve_resource).returns Puppet::Resource.new("user", user_hash[:name], :parameters => user_hash) + @res.user_check(user).should be_true + end + + it "should purge system users over 500 if unless_system_user => 600" do + res = Puppet::Type.type(:resources).new :name => :user, :purge => true, :unless_system_user => 600 + res.catalog = Puppet::Resource::Catalog.new + user_hash = {:name => 'system_user', :uid => 525, :system => true} + user = Puppet::Type.type(:user).new(user_hash) + user.stubs(:retrieve_resource).returns Puppet::Resource.new("user", user_hash[:name], :parameters => user_hash) + res.user_check(user).should be_false + end + end + + describe "with unless_uid" do + describe "with a uid range" do + before do + @res = Puppet::Type.type(:resources).new :name => :user, :purge => true, :unless_uid => 10_000..20_000 + @res.catalog = Puppet::Resource::Catalog.new + end + + it "should purge uids that are not in a specified range" do + user_hash = {:name => 'special_user', :uid => 25_000} + user = Puppet::Type.type(:user).new(user_hash) + user.stubs(:retrieve_resource).returns Puppet::Resource.new("user", user_hash[:name], :parameters => user_hash) + @res.user_check(user).should be_true + end + + it "should not purge uids that are in a specified range" do + user_hash = {:name => 'special_user', :uid => 15_000} + user = Puppet::Type.type(:user).new(user_hash) + user.stubs(:retrieve_resource).returns Puppet::Resource.new("user", user_hash[:name], :parameters => user_hash) + @res.user_check(user).should be_false + end + end + + describe "with a uid range array" do + before do + @res = Puppet::Type.type(:resources).new :name => :user, :purge => true, :unless_uid => [10_000..15_000, 15_000..20_000] + @res.catalog = Puppet::Resource::Catalog.new + end + + it "should purge uids that are not in a specified range array" do + user_hash = {:name => 'special_user', :uid => 25_000} + user = Puppet::Type.type(:user).new(user_hash) + user.stubs(:retrieve_resource).returns Puppet::Resource.new("user", user_hash[:name], :parameters => user_hash) + @res.user_check(user).should be_true + end + + it "should not purge uids that are in a specified range array" do + user_hash = {:name => 'special_user', :uid => 15_000} + user = Puppet::Type.type(:user).new(user_hash) + user.stubs(:retrieve_resource).returns Puppet::Resource.new("user", user_hash[:name], :parameters => user_hash) + @res.user_check(user).should be_false + end + + end + + describe "with a uid array" do + before do + @res = Puppet::Type.type(:resources).new :name => :user, :purge => true, :unless_uid => [15_000, 15_001, 15_002] + @res.catalog = Puppet::Resource::Catalog.new + end + + it "should purge uids that are not in a specified array" do + user_hash = {:name => 'special_user', :uid => 25_000} + user = Puppet::Type.type(:user).new(user_hash) + user.stubs(:retrieve_resource).returns Puppet::Resource.new("user", user_hash[:name], :parameters => user_hash) + @res.user_check(user).should be_true + end + + it "should not purge uids that are in a specified array" do + user_hash = {:name => 'special_user', :uid => 15000} + user = Puppet::Type.type(:user).new(user_hash) + user.stubs(:retrieve_resource).returns Puppet::Resource.new("user", user_hash[:name], :parameters => user_hash) + @res.user_check(user).should be_false + end + + end + + describe "with a single uid" do + before do + @res = Puppet::Type.type(:resources).new :name => :user, :purge => true, :unless_uid => 15_000 + @res.catalog = Puppet::Resource::Catalog.new + end + + it "should purge uids that are not specified" do + user_hash = {:name => 'special_user', :uid => 25_000} + user = Puppet::Type.type(:user).new(user_hash) + user.stubs(:retrieve_resource).returns Puppet::Resource.new("user", user_hash[:name], :parameters => user_hash) + @res.user_check(user).should be_true + end + + it "should not purge uids that are specified" do + user_hash = {:name => 'special_user', :uid => 15_000} + user = Puppet::Type.type(:user).new(user_hash) + user.stubs(:retrieve_resource).returns Puppet::Resource.new("user", user_hash[:name], :parameters => user_hash) + @res.user_check(user).should be_false + end + end + + describe "with a mixed uid array" do + before do + @res = Puppet::Type.type(:resources).new :name => :user, :purge => true, :unless_uid => [10_000..15_000, 16_666] + @res.catalog = Puppet::Resource::Catalog.new + end + + it "should not purge ids in the range" do + user_hash = {:name => 'special_user', :uid => 15_000} + user = Puppet::Type.type(:user).new(user_hash) + user.stubs(:retrieve_resource).returns Puppet::Resource.new("user", user_hash[:name], :parameters => user_hash) + @res.user_check(user).should be_false + end + + it "should not purge specified ids" do + user_hash = {:name => 'special_user', :uid => 16_666} + user = Puppet::Type.type(:user).new(user_hash) + user.stubs(:retrieve_resource).returns Puppet::Resource.new("user", user_hash[:name], :parameters => user_hash) + @res.user_check(user).should be_false + end + + it "should purge unspecified ids" do + user_hash = {:name => 'special_user', :uid => 17_000} + user = Puppet::Type.type(:user).new(user_hash) + user.stubs(:retrieve_resource).returns Puppet::Resource.new("user", user_hash[:name], :parameters => user_hash) + @res.user_check(user).should be_true + end + end + + end + end + describe "#generate" do before do @host1 = Puppet::Type.type(:host).new(:name => 'localhost', :ip => '127.0.0.1') From 73b0c2b0f59db920a14d4d0951b1f1c40d603cb4 Mon Sep 17 00:00:00 2001 From: Alejandro Ramirez Date: Thu, 26 Sep 2013 13:39:37 +0200 Subject: [PATCH 450/800] (PUP-1448) Validates the user type shell property Adds the feature :manages_shell to the user type and asserts that providers manipulating the shell property must have this feature. The useradd provider is then patched to specifically check that the shell property is valid (file exists and is executable). Other providers which use shell (user/aix, user/directory_service, user/ldap, user/pw) have the :manages_shell feature added to ensure that they continue to function, but no additional validation is performed. Rebased to a more recent master from Alejandro Ramirez's PR https://github.com/puppetlabs/puppet/pull/1942 (Commits squashed) --- lib/puppet/provider/user/aix.rb | 2 +- lib/puppet/provider/user/directoryservice.rb | 3 ++ lib/puppet/provider/user/ldap.rb | 2 +- lib/puppet/provider/user/pw.rb | 2 +- lib/puppet/provider/user/useradd.rb | 18 +++++++ lib/puppet/type/user.rb | 5 +- spec/unit/provider/user/useradd_spec.rb | 30 ++++++++++-- spec/unit/type/user_spec.rb | 49 +++++++++++++++++++- 8 files changed, 101 insertions(+), 10 deletions(-) diff --git a/lib/puppet/provider/user/aix.rb b/lib/puppet/provider/user/aix.rb index f48a943ad..06eae6f64 100644 --- a/lib/puppet/provider/user/aix.rb +++ b/lib/puppet/provider/user/aix.rb @@ -34,7 +34,7 @@ Puppet::Type.type(:user).provide :aix, :parent => Puppet::Provider::AixObject do # Provider features has_features :manages_aix_lam - has_features :manages_homedir, :manages_passwords + has_features :manages_homedir, :manages_passwords, :manages_shell has_features :manages_expiry, :manages_password_age # Attribute verification (TODO) diff --git a/lib/puppet/provider/user/directoryservice.rb b/lib/puppet/provider/user/directoryservice.rb index 8cf2ee5d6..a4ae31b8c 100644 --- a/lib/puppet/provider/user/directoryservice.rb +++ b/lib/puppet/provider/user/directoryservice.rb @@ -30,6 +30,9 @@ Puppet::Type.type(:user).provide :directoryservice do # 10.8 Passwords use a PBKDF2 salt value has_features :manages_password_salt + #provider can set the user's shell + has_feature :manages_shell + ## ## ## Class Methods ## ## ## diff --git a/lib/puppet/provider/user/ldap.rb b/lib/puppet/provider/user/ldap.rb index 4de89666c..6dbda5f9b 100644 --- a/lib/puppet/provider/user/ldap.rb +++ b/lib/puppet/provider/user/ldap.rb @@ -14,7 +14,7 @@ Puppet::Type.type(:user).provide :ldap, :parent => Puppet::Provider::Ldap do confine :feature => :ldap, :false => (Puppet[:ldapuser] == "") - has_feature :manages_passwords + has_feature :manages_passwords, :manages_shell manages(:posixAccount, :person).at("ou=People").named_by(:uid).and.maps :name => :uid, :password => :userPassword, diff --git a/lib/puppet/provider/user/pw.rb b/lib/puppet/provider/user/pw.rb index 343fd8a7d..c1e9a08a8 100644 --- a/lib/puppet/provider/user/pw.rb +++ b/lib/puppet/provider/user/pw.rb @@ -5,7 +5,7 @@ Puppet::Type.type(:user).provide :pw, :parent => Puppet::Provider::NameService:: desc "User management via `pw` on FreeBSD and DragonFly BSD." commands :pw => "pw" - has_features :manages_homedir, :allows_duplicates, :manages_passwords, :manages_expiry + has_features :manages_homedir, :allows_duplicates, :manages_passwords, :manages_expiry, :manages_shell defaultfor :operatingsystem => [:freebsd, :dragonfly] diff --git a/lib/puppet/provider/user/useradd.rb b/lib/puppet/provider/user/useradd.rb index 2c6552c73..10c4060a9 100644 --- a/lib/puppet/provider/user/useradd.rb +++ b/lib/puppet/provider/user/useradd.rb @@ -80,6 +80,11 @@ Puppet::Type.type(:user).provide :useradd, :parent => Puppet::Provider::NameServ false end + def shell=(value) + check_valid_shell + set("shell", value) + end + verify :gid, "GID must be an integer" do |value| value.is_a? Integer end @@ -92,6 +97,7 @@ Puppet::Type.type(:user).provide :useradd, :parent => Puppet::Provider::NameServ has_features :system_users unless %w{HP-UX Solaris}.include? Facter.value(:operatingsystem) has_features :manages_passwords, :manages_password_age if Puppet.features.libshadow? + has_features :manages_shell def check_allow_dup # We have to manually check for duplicates when using libuser @@ -108,6 +114,15 @@ Puppet::Type.type(:user).provide :useradd, :parent => Puppet::Provider::NameServ [] end + def check_valid_shell + unless File.exists?(@resource.should(:shell)) + raise(Puppet::Error, "Shell #{@resource.should(:shell)} must exist") + end + unless File.executable?(@resource.should(:shell).to_s) + raise(Puppet::Error, "Shell #{@resource.should(:shell)} must be executable") + end + end + def check_manage_home cmd = [] if @resource.managehome? and not @resource.forcelocal? @@ -202,6 +217,9 @@ Puppet::Type.type(:user).provide :useradd, :parent => Puppet::Provider::NameServ end def create + if @resource[:shell] + check_valid_shell + end super if @resource.forcelocal? and self.groups? set(:groups, @resource[:groups]) diff --git a/lib/puppet/type/user.rb b/lib/puppet/type/user.rb index d0ca0f78c..5240810b4 100644 --- a/lib/puppet/type/user.rb +++ b/lib/puppet/type/user.rb @@ -54,6 +54,9 @@ module Puppet "Allows local users to be managed on systems that also use some other remote NSS method of managing accounts." + feature :manages_shell, + "The provider allows for setting shell and validates if possible" + newproperty(:ensure, :parent => Puppet::Property::Ensure) do newvalue(:present, :event => :user_created) do provider.create @@ -169,7 +172,7 @@ module Puppet end end - newproperty(:shell) do + newproperty(:shell, :required_features => :manages_shell) do desc "The user's login shell. The shell must exist and be executable. diff --git a/spec/unit/provider/user/useradd_spec.rb b/spec/unit/provider/user/useradd_spec.rb index 98183758b..9d42017a7 100755 --- a/spec/unit/provider/user/useradd_spec.rb +++ b/spec/unit/provider/user/useradd_spec.rb @@ -93,9 +93,9 @@ describe Puppet::Type.type(:user).provider(:useradd) do provider.expects(:execute).with(includes('/usr/sbin/luseradd'), has_entry(:custom_environment, has_key('LIBUSER_CONF'))) provider.create end - + it "should NOT use -o when allowdupe=true" do - resource[:allowdupe] = :true + resource[:allowdupe] = :true provider.expects(:execute).with(Not(includes('-o')), has_entry(:custom_environment, has_key('LIBUSER_CONF'))) provider.create end @@ -110,13 +110,13 @@ describe Puppet::Type.type(:user).provider(:useradd) do resource[:groups] = ['group1', 'group2'] provider.expects(:execute).with(Not(includes("-G")), has_entry(:custom_environment, has_key('LIBUSER_CONF'))) provider.expects(:execute).with(includes('/usr/sbin/usermod')) - provider.create + provider.create end it "should not use -m when managehome set" do resource[:managehome] = :true provider.expects(:execute).with(Not(includes('-m')), has_entry(:custom_environment, has_key('LIBUSER_CONF'))) - provider.create + provider.create end it "should not use -e with luseradd, should call usermod with -e after luseradd when expiry is set" do @@ -127,6 +127,15 @@ describe Puppet::Type.type(:user).provider(:useradd) do end end + describe "on systems that allow to set shell" do + it "should trigger shell validation" do + resource[:shell] = '/bin/bash' + provider.expects(:check_valid_shell) + provider.expects(:execute).with(includes('-s'), kind_of(Hash)) + provider.create + end + end + end describe "#uid=" do @@ -399,4 +408,17 @@ describe Puppet::Type.type(:user).provider(:useradd) do provider.passcmd.must == ['/usr/bin/chage','-m',123,'-M',999,'myuser'] end end + + describe "#check_valid_shell" do + it "should raise an error if shell does not exist" do + resource[:shell] = 'foo/bin/bash' + lambda { provider.check_valid_shell }.should raise_error(Puppet::Error, /Shell foo\/bin\/bash must exist/) + end + + it "should raise an error if the shell is not executable" do + resource[:shell] = 'LICENSE' + lambda { provider.check_valid_shell }.should raise_error(Puppet::Error, /Shell LICENSE must be executable/) + end + end + end diff --git a/spec/unit/type/user_spec.rb b/spec/unit/type/user_spec.rb index 2877d25c5..6c33acaf1 100755 --- a/spec/unit/type/user_spec.rb +++ b/spec/unit/type/user_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' describe Puppet::Type.type(:user) do before :each do @provider_class = described_class.provide(:simple) do - has_features :manages_expiry, :manages_password_age, :manages_passwords, :manages_solaris_rbac + has_features :manages_expiry, :manages_password_age, :manages_passwords, :manages_solaris_rbac, :manages_shell mk_resource_methods def create; end def delete; end @@ -47,6 +47,10 @@ describe Puppet::Type.type(:user) do described_class.provider_feature(:system_users).should_not be_nil end + it "should have a manages_shell feature" do + described_class.provider_feature(:manages_shell).should_not be_nil + end + describe :managehome do let (:provider) { @provider_class.new(:name => 'foo', :ensure => :absent) } let (:instance) { described_class.new(:name => 'foo', :provider => provider) } @@ -61,7 +65,7 @@ describe Puppet::Type.type(:user) do it "cannot be set to true for a provider that does not manage homedirs" do provider.class.stubs(:manages_homedir?).returns false - expect { instance[:managehome] = 'yes' }.to raise_error Puppet::Error + expect { instance[:managehome] = 'yes' }.to raise_error(Puppet::Error, /can not manage home directories/) end it "can be set to true for a provider that does manage homedirs" do @@ -369,4 +373,45 @@ describe Puppet::Type.type(:user) do rel.target.ref.should == testuser.ref end end + + describe "when setting shell" do + before :each do + @shell_provider_class = described_class.provide(:shell_manager) do + has_features :manages_shell + mk_resource_methods + def create; check_valid_shell;end + def shell=(value); check_valid_shell; end + def delete; end + def exists?; get(:ensure) != :absent; end + def flush; end + def self.instances; []; end + def check_valid_shell; end + end + + described_class.stubs(:defaultprovider).returns @shell_provider_class + end + + it "should call :check_valid_shell on the provider when changing shell value" do + @provider = @shell_provider_class.new(:name => 'foo', :shell => '/bin/bash', :ensure => :present) + @provider.expects(:check_valid_shell) + resource = described_class.new(:name => 'foo', :shell => '/bin/zsh', :provider => @provider) + Puppet::Util::Storage.stubs(:load) + Puppet::Util::Storage.stubs(:store) + catalog = Puppet::Resource::Catalog.new + catalog.add_resource resource + catalog.apply + end + + it "should call :check_valid_shell on the provider when changing ensure from present to absent" do + @provider = @shell_provider_class.new(:name => 'foo', :shell => '/bin/bash', :ensure => :absent) + @provider.expects(:check_valid_shell) + resource = described_class.new(:name => 'foo', :shell => '/bin/zsh', :provider => @provider) + Puppet::Util::Storage.stubs(:load) + Puppet::Util::Storage.stubs(:store) + catalog = Puppet::Resource::Catalog.new + catalog.add_resource resource + catalog.apply + end + + end end From 23fb77fcf65cd6a33db345c075212b50f8b8e674 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 17 Jan 2014 09:29:14 -0800 Subject: [PATCH 451/800] (Maint) Alias TESTS as TEST Sometimes we type one, sometimes the other. Just make them aliases. --- Rakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rakefile b/Rakefile index b6111380d..3bb695abe 100644 --- a/Rakefile +++ b/Rakefile @@ -64,5 +64,5 @@ task :default do end task :spec do - sh %{rspec #{ENV['TESTS'] || 'spec'}} + sh %{rspec #{ENV['TEST'] || ENV['TESTS'] || 'spec'}} end From abdb04a2765bfeffd38b4ad43f82460127e5a528 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 17 Jan 2014 10:47:07 -0800 Subject: [PATCH 452/800] (PUP-1118) Remove use of Environment.current One does not simply change the current environment! This replaces use of Environment.current= and Environment.current with Puppet::Context.lookup(:current_environment). This uncovered a lot of assumptions about what the current environment is at various times, such as during startup before initialization. There is now a "bootstrap" context that is setup as soon as possible which provides a basic setup, once initialization occures this is overridden with another context that is able to do more. --- lib/puppet.rb | 2 +- lib/puppet/bindings.rb | 12 ++-- lib/puppet/context.rb | 89 +++++++++++++++++++----- lib/puppet/metatype/manager.rb | 2 +- lib/puppet/node/environment.rb | 22 +----- lib/puppet/parser/compiler.rb | 50 +++++++------ lib/puppet/parser/functions.rb | 45 ++++++------ lib/puppet/test/test_helper.rb | 16 +++-- spec/unit/context_spec.rb | 45 +++++------- spec/unit/parser/functions_spec.rb | 108 +++++++---------------------- 10 files changed, 185 insertions(+), 206 deletions(-) diff --git a/lib/puppet.rb b/lib/puppet.rb index 7430e55b9..771af0d08 100644 --- a/lib/puppet.rb +++ b/lib/puppet.rb @@ -148,8 +148,8 @@ module Puppet Puppet.settings.initialize_global_settings(args) run_mode = Puppet::Util::RunMode[run_mode] Puppet.settings.initialize_app_defaults(Puppet::Settings.app_defaults_for_run_mode(run_mode)) + Puppet::Context.push(Puppet::Context.initial_context, "Initial context after settings initialization") Puppet::Parser::Functions.reset - Puppet::Context.push(:environments => Puppet::Environments::Legacy.new) end private_class_method :do_initialize_settings_for_run_mode diff --git a/lib/puppet/bindings.rb b/lib/puppet/bindings.rb index b6a8a3afa..4c59bb6e4 100644 --- a/lib/puppet/bindings.rb +++ b/lib/puppet/bindings.rb @@ -74,7 +74,7 @@ class Puppet::Bindings end def self.register_proc(name, block) - adapter = NamedBindingsAdapter.adapt(Environment.current) + adapter = NamedBindingsAdapter.adapt(Puppet::Context.lookup(:current_environment)) adapter[name] = block end @@ -83,7 +83,7 @@ class Puppet::Bindings # @api public # def self.register(named_bindings) - adapter = NamedBindingsAdapter.adapt(Environment.current) + adapter = NamedBindingsAdapter.adapt(Puppet::Context.lookup(:current_environment)) adapter[named_bindings.name] = named_bindings end @@ -91,7 +91,7 @@ class Puppet::Bindings entry = get(name) return entry unless entry.is_a?(Proc) named_bindings = Puppet::Pops::Binder::BindingsFactory.safe_named_bindings(name, scope, &entry).model - adapter = NamedBindingsAdapter.adapt(Environment.current) + adapter = NamedBindingsAdapter.adapt(Puppet::Context.lookup(:current_environment)) adapter[named_bindings.name] = named_bindings named_bindings end @@ -102,7 +102,7 @@ class Puppet::Bindings # @api public # def self.get(name) - adapter = NamedBindingsAdapter.adapt(Environment.current) + adapter = NamedBindingsAdapter.adapt(Puppet::Context.lookup(:current_environment)) adapter[name] end @@ -112,7 +112,7 @@ class Puppet::Bindings # Supports Enumerable iteration (k,v) over the named bindings hash. def self.each - adapter = NamedBindingsAdapter.adapt(Environment.current) + adapter = NamedBindingsAdapter.adapt(Puppet::Context.lookup(:current_environment)) adapter.each_pair {|k,v| yield k,v } end @@ -144,4 +144,4 @@ class Puppet::Bindings end end -end \ No newline at end of file +end diff --git a/lib/puppet/context.rb b/lib/puppet/context.rb index c6e871880..b9725ac35 100644 --- a/lib/puppet/context.rb +++ b/lib/puppet/context.rb @@ -1,4 +1,10 @@ -module Puppet::Context +# Puppet::Context is a system for tracking services and contextual information +# that puppet needs to be able to run. Values are "bound" in a context when it is created +# and cannot be changed; however a child context can be created, using +# {#override}, that provides a different value. +# +# @api private +class Puppet::Context require 'puppet/context/trusted_information' class UndefinedBindingError < Puppet::Error; end @@ -6,11 +12,12 @@ module Puppet::Context # @api private class Bindings - attr_reader :parent + attr_reader :parent, :description - def initialize(parent, overrides = {}) + def initialize(parent, description, overrides = {}) overrides ||= {} @parent = parent + @description = description @table = parent ? parent.table.merge(overrides) : overrides end @@ -33,34 +40,80 @@ module Puppet::Context attr_reader :table end - @bindings = Bindings.new(nil) - - # @param overrides [Hash] A hash of bindings to be merged with the parent context. # @api private - def self.push(overrides) - @bindings = Bindings.new(@bindings, overrides) + def initialize(initial_bindings) + @bindings = Bindings.new(nil, "root", initial_bindings) end # @api private - def self.pop - raise(StackUnderflow, "Attempted to pop, but lready at root of the context stack.") if @bindings.root? + def push(overrides, description = "") + @bindings = Bindings.new(@bindings, description, overrides) + end + + # @api private + def pop + raise(StackUnderflow, "Attempted to pop, but already at root of the context stack.") if @bindings.root? @bindings = @bindings.parent end - # Lookup a binding by name or return a default value provided by a passed block (if given). - # @api public - def self.lookup(name, &block) + # @api private + def lookup(name, &block) @bindings.lookup(name, block) end - # @param bindings [Hash] A hash of bindings to be merged with the parent context. - # @yield [] A block executed in the context of the temporarily pushed bindings. - # @api public - def self.override(bindings) - push(bindings) + # @api private + def override(bindings, description = "", &block) + push(bindings, description) yield ensure pop end + + # The bindings used for initialization of puppet + # @api private + def self.initial_context + { + :environments => Puppet::Environments::Legacy.new, + :current_environment => Puppet::Node::Environment.root, + } + end + + # A simple set of bindings that is just enough to limp along to + # initialization where the {#initial_context} bindings are put in place + # @api private + def self.bootstrap_context + { :current_environment => Puppet::Node::Environment.create(:'*bootstrap*', [], '') } + end + + # @param overrides [Hash] A hash of bindings to be merged with the parent context. + # @param description [String] A description of the context. + # @api private + def self.push(overrides, description = "") + @instance.push(overrides, description) + end + + # Return to the previous context. + # @raise [StackUnderflow] if the current context is the root + # @api private + def self.pop + @instance.pop + end + + # Lookup a binding by name or return a default value provided by a passed block (if given). + # @api public + def self.lookup(name, &block) + @instance.lookup(name, &block) + end + + # @param bindings [Hash] A hash of bindings to be merged with the parent context. + # @param description [String] A description of the context. + # @yield [] A block executed in the context of the temporarily pushed bindings. + # @api public + def self.override(bindings, description = "", &block) + @instance.override(bindings, description, &block) + end + + # The single instance used for normal operation + @instance = new(bootstrap_context) end diff --git a/lib/puppet/metatype/manager.rb b/lib/puppet/metatype/manager.rb index a0a21b1ca..a565939ab 100644 --- a/lib/puppet/metatype/manager.rb +++ b/lib/puppet/metatype/manager.rb @@ -156,7 +156,7 @@ module Manager end # Try loading the type. - if typeloader.load(name, Puppet::Node::Environment.current) + if typeloader.load(name, Puppet::Context.lookup(:current_environment)) Puppet.warning "Loaded puppet/type/#{name} but no class was created" unless @types.include? name end diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb index e1bb86791..5d9ff0635 100644 --- a/lib/puppet/node/environment.rb +++ b/lib/puppet/node/environment.rb @@ -148,7 +148,7 @@ class Puppet::Node::Environment @manifest = manifest end - # Retrieve the environment for the current thread + # Retrieve the environment for the current process. # # @note This should only used when a catalog is being compiled. # @@ -157,26 +157,10 @@ class Puppet::Node::Environment # @return [Puppet::Node::Environment] the currently set environment if one # has been explicitly set, else it will return the '*root*' environment def self.current - $environment || root + Puppet.deprecation_warning("Remove me.") + Puppet::Context.lookup(:current_environment) end - # Set the environment for the current thread - # - # @note This should only set when a catalog is being compiled. Under normal - # This value is initially set in {Puppet::Parser::Compiler#environment} - # - # @note Setting this affects global state during catalog compilation, and - # changing the current environment during compilation can cause unexpected - # and generally very bad behaviors. - # - # @api private - # - # @param env [Puppet::Node::Environment] - def self.current=(env) - $environment = new(env) - end - - # @return [Puppet::Node::Environment] The `*root*` environment. # # This is only used for handling functions that are not attached to a diff --git a/lib/puppet/parser/compiler.rb b/lib/puppet/parser/compiler.rb index 700d27b70..a6d40bc16 100644 --- a/lib/puppet/parser/compiler.rb +++ b/lib/puppet/parser/compiler.rb @@ -99,42 +99,40 @@ class Puppet::Parser::Compiler # Compiler our catalog. This mostly revolves around finding and evaluating classes. # This is the main entry into our catalog. def compile - # Set the client's parameters into the top scope. - Puppet::Util::Profiler.profile("Compile: Set node parameters") { set_node_parameters } + Puppet::Context.override({ :current_environment => environment }, "For compiling #{node.name}") do + # Set the client's parameters into the top scope. + Puppet::Util::Profiler.profile("Compile: Set node parameters") { set_node_parameters } - Puppet::Util::Profiler.profile("Compile: Created settings scope") { create_settings_scope } + Puppet::Util::Profiler.profile("Compile: Created settings scope") { create_settings_scope } - if is_binder_active? - Puppet::Util::Profiler.profile("Compile: Created injector") { create_injector } + if is_binder_active? + Puppet::Util::Profiler.profile("Compile: Created injector") { create_injector } + end + + Puppet::Util::Profiler.profile("Compile: Evaluated main") { evaluate_main } + + Puppet::Util::Profiler.profile("Compile: Evaluated AST node") { evaluate_ast_node } + + Puppet::Util::Profiler.profile("Compile: Evaluated node classes") { evaluate_node_classes } + + Puppet::Util::Profiler.profile("Compile: Evaluated generators") { evaluate_generators } + + Puppet::Util::Profiler.profile("Compile: Finished catalog") { finish } + + fail_on_unevaluated + + @catalog end - - Puppet::Util::Profiler.profile("Compile: Evaluated main") { evaluate_main } - - Puppet::Util::Profiler.profile("Compile: Evaluated AST node") { evaluate_ast_node } - - Puppet::Util::Profiler.profile("Compile: Evaluated node classes") { evaluate_node_classes } - - Puppet::Util::Profiler.profile("Compile: Evaluated generators") { evaluate_generators } - - Puppet::Util::Profiler.profile("Compile: Finished catalog") { finish } - - fail_on_unevaluated - - @catalog end def_delegator :@collections, :delete, :delete_collection # Return the node's environment. def environment - unless defined?(@environment) - unless node.environment.is_a? Puppet::Node::Environment - raise Puppet::DevError, "node #{node} has an invalid environment!" - end - @environment = node.environment + unless node.environment.is_a? Puppet::Node::Environment + raise Puppet::DevError, "node #{node} has an invalid environment!" end - Puppet::Node::Environment.current = @environment - @environment + node.environment end # Evaluate all of the classes specified by the node. diff --git a/lib/puppet/parser/functions.rb b/lib/puppet/parser/functions.rb index 4e3c93b54..03c345448 100644 --- a/lib/puppet/parser/functions.rb +++ b/lib/puppet/parser/functions.rb @@ -41,11 +41,11 @@ module Puppet::Parser::Functions # environment # # @api private - def self.environment_module(env = nil) + def self.environment_module(env = Puppet::Context.lookup(:current_environment)) if env and ! env.is_a?(Puppet::Node::Environment) env = Puppet::Node::Environment.new(env) end - @modules[ (env || Environment.current).name ] ||= Module.new + @modules[env.name] ||= Module.new end # Create a new Puppet DSL function. @@ -118,8 +118,9 @@ module Puppet::Parser::Functions # @api public def self.newfunction(name, options = {}, &block) name = name.intern + environment = Puppet::Context.lookup(:current_environment) - Puppet.warning "Overwriting previous definition for function #{name}" if get_function(name) + Puppet.warning "Overwriting previous definition for function #{name}" if get_function(name, environment) arity = options[:arity] || -1 ftype = options[:type] || :statement @@ -152,25 +153,26 @@ module Puppet::Parser::Functions func = {:arity => arity, :type => ftype, :name => fname} func[:doc] = options[:doc] if options[:doc] - add_function(name, func) + add_function(name, func, environment) func end # Determine if a function is defined # # @param [Symbol] name the function + # @param [Puppet::Node::Environment] environment the environment to find the function in # # @return [Symbol, false] The name of the function if it's defined, # otherwise false. # # @api public - def self.function(name) + def self.function(name, environment = Puppet::Context.lookup(:current_environment)) name = name.intern func = nil - unless func = get_function(name) - autoloader.load(name, Environment.current) - func = get_function(name) + unless func = get_function(name, environment) + autoloader.load(name, environment) + func = get_function(name, environment) end if func @@ -180,12 +182,12 @@ module Puppet::Parser::Functions end end - def self.functiondocs + def self.functiondocs(environment = Puppet::Context.lookup(:current_environment)) autoloader.loadall ret = "" - merged_functions.sort { |a,b| a[0].to_s <=> b[0].to_s }.each do |name, hash| + merged_functions(environment).sort { |a,b| a[0].to_s <=> b[0].to_s }.each do |name, hash| ret << "#{name}\n#{"-" * name.to_s.length}\n" if hash[:doc] ret << Puppet::Util::Docs.scrub(hash[:doc]) @@ -202,40 +204,43 @@ module Puppet::Parser::Functions # Determine whether a given function returns a value. # # @param [Symbol] name the function + # @param [Puppet::Node::Environment] environment The environment to find the function in + # @return [Boolean] whether it is an rvalue function # # @api public - def self.rvalue?(name) - func = get_function(name) + def self.rvalue?(name, environment = Puppet::Context.lookup(:current_environment)) + func = get_function(name, environment) func ? func[:type] == :rvalue : false end # Return the number of arguments a function expects. # # @param [Symbol] name the function + # @param [Puppet::Node::Environment] environment The environment to find the function in # @return [Integer] The arity of the function. See {newfunction} for # the meaning of negative values. # # @api public - def self.arity(name) - func = get_function(name) + def self.arity(name, environment = Puppet::Context.lookup(:current_environment)) + func = get_function(name, environment) func ? func[:arity] : -1 end class << self private - def merged_functions - @functions[Environment.root].merge(@functions[Environment.current]) + def merged_functions(environment) + @functions[Environment.root].merge(@functions[environment]) end - def get_function(name) + def get_function(name, environment) name = name.intern - merged_functions[name] + merged_functions(environment)[name] end - def add_function(name, func) + def add_function(name, func, environment) name = name.intern - @functions[Environment.current][name] = func + @functions[environment][name] = func end end end diff --git a/lib/puppet/test/test_helper.rb b/lib/puppet/test/test_helper.rb index 2b45b24ef..5e97dc8a5 100644 --- a/lib/puppet/test/test_helper.rb +++ b/lib/puppet/test/test_helper.rb @@ -40,7 +40,9 @@ module Puppet::Test # any individual tests. # @return nil def self.before_all_tests() - Puppet::Parser::Functions.reset + # Make sure that all of the setup is also done for any before(:all) blocks + Puppet::Context.push(Puppet::Context.initial_context, "Initial for specs") + self.before_each_test() end # Call this method once, at the end of a test run, when no more tests @@ -84,17 +86,21 @@ module Puppet::Test initialize_settings_before_each() - Puppet::Node::Environment.clear + Puppet::Context.push( + { + :trusted_information => + Puppet::Context::TrustedInformation.new('local', 'testing', {}), + }, + "Context for specs") + Puppet::Parser::Functions.reset + Puppet::Node::Environment.clear Puppet::Application.clear! Puppet::Util::Profiler.clear Puppet.clear_deprecation_warnings Puppet::DataBinding::Hiera.instance_variable_set("@hiera", nil) - - Puppet::Context.push(:trusted_information => - Puppet::Context::TrustedInformation.new('local', 'testing', {})) end # Call this method once per test, after execution of each individual test. diff --git a/spec/unit/context_spec.rb b/spec/unit/context_spec.rb index 52075443a..3708ebc10 100644 --- a/spec/unit/context_spec.rb +++ b/spec/unit/context_spec.rb @@ -1,52 +1,45 @@ require 'spec_helper' describe Puppet::Context do + let(:context) { Puppet::Context.new({ :testing => "value" }) } context "with the implicit test_helper.rb pushed context" do it "fails to lookup a value that does not exist" do - expect { Puppet::Context.lookup("a") }.to raise_error(Puppet::Context::UndefinedBindingError) + expect { context.lookup("a") }.to raise_error(Puppet::Context::UndefinedBindingError) end it "calls a provided block for a default value when none is found" do - expect(Puppet::Context.lookup("a") { "default" }).to eq("default") + expect(context.lookup("a") { "default" }).to eq("default") end it "behaves as if pushed a {} if you push nil" do - Puppet::Context.push(nil) - expect(Puppet::Context.lookup(:trusted_information)).to_not be_nil - Puppet::Context.pop + context.push(nil) + expect(context.lookup(:testing)).to eq("value") + context.pop end it "fails if you try to pop off the top of the stack" do - while !Puppet::Context.pop.root?; end - expect { Puppet::Context.pop }.to raise_error(Puppet::Context::StackUnderflow) - # TestHelper expects to have something to pop in its after_each_test() - Puppet::Context.push({}) + expect { context.pop }.to raise_error(Puppet::Context::StackUnderflow) end it "protects the bindings table from casual access" do - expect { Puppet::Context.push({}).table }.to raise_error(NoMethodError, /protected/) - Puppet::Context.pop + expect { context.push({}).table }.to raise_error(NoMethodError, /protected/) end end describe "with additional context" do before :each do - Puppet::Context.push("a" => 1) - end - - after :each do - Puppet::Context.pop + context.push("a" => 1) end it "holds values for later lookup" do - expect(Puppet::Context.lookup("a")).to eq(1) + expect(context.lookup("a")).to eq(1) end it "allows rebinding values in a nested context" do inner = nil - Puppet::Context.override("a" => 2) do - inner = Puppet::Context.lookup("a") + context.override("a" => 2) do + inner = context.lookup("a") end expect(inner).to eq(2) @@ -55,9 +48,9 @@ describe Puppet::Context do it "outer bindings are available in an overridden context" do inner_a = nil inner_b = nil - Puppet::Context.override("b" => 2) do - inner_a = Puppet::Context.lookup("a") - inner_b = Puppet::Context.lookup("b") + context.override("b" => 2) do + inner_a = context.lookup("a") + inner_b = context.lookup("b") end expect(inner_a).to eq(1) @@ -65,21 +58,21 @@ describe Puppet::Context do end it "overridden bindings do not exist outside of the override" do - Puppet::Context.override("a" => 2) do + context.override("a" => 2) do end - expect(Puppet::Context.lookup("a")).to eq(1) + expect(context.lookup("a")).to eq(1) end it "overridden bindings do not exist outside of the override even when leaving via an error" do begin - Puppet::Context.override("a" => 2) do + context.override("a" => 2) do raise "this should still cause the bindings to leave" end rescue end - expect(Puppet::Context.lookup("a")).to eq(1) + expect(context.lookup("a")).to eq(1) end end end diff --git a/spec/unit/parser/functions_spec.rb b/spec/unit/parser/functions_spec.rb index 8ad33c874..f169e0a6f 100755 --- a/spec/unit/parser/functions_spec.rb +++ b/spec/unit/parser/functions_spec.rb @@ -6,6 +6,12 @@ describe Puppet::Parser::Functions do Class.new { include mod }.new end + let(:function_module) { Puppet::Parser::Functions.environment_module(Puppet::Context.lookup(:current_environment)) } + + before do + Puppet::Parser::Functions.reset + end + it "should have a method for returning an environment-specific module" do Puppet::Parser::Functions.environment_module(Puppet::Node::Environment.new("myenv")).should be_instance_of(Module) end @@ -19,11 +25,6 @@ describe Puppet::Parser::Functions do end describe "when calling newfunction" do - let(:function_module) { Module.new } - before do - Puppet::Parser::Functions.stubs(:environment_module).returns(function_module) - end - it "should create the function in the environment module" do Puppet::Parser::Functions.newfunction("name", :type => :rvalue) { |args| } @@ -41,7 +42,7 @@ describe Puppet::Parser::Functions do expect { Puppet::Parser::Functions.newfunction("name", :type => :unknown) { |args| } }.to raise_error Puppet::DevError, "Invalid statement type :unknown" end - it "instruments the function to profiles the execution" do + it "instruments the function to profile the execution" do messages = [] Puppet::Util::Profiler.current = Puppet::Util::Profiler::WallClock.new(proc { |msg| messages << msg }, "id") @@ -53,11 +54,6 @@ describe Puppet::Parser::Functions do end describe "when calling function to test function existence" do - let(:function_module) { Module.new } - before do - Puppet::Parser::Functions.stubs(:environment_module).returns(function_module) - end - it "should return false if the function doesn't exist" do Puppet::Parser::Functions.autoloader.stubs(:load) @@ -75,6 +71,23 @@ describe Puppet::Parser::Functions do Puppet::Parser::Functions.function("name") end + + it "combines functions from the root with those from the current environment" do + Puppet::Context.override(:current_environment => Puppet::Node::Environment.root) do + Puppet::Parser::Functions.newfunction("onlyroot", :type => :rvalue) do |args| + end + end + + Puppet::Context.override(:current_environment => Puppet::Node::Environment.create(:other, [''], '')) do + Puppet::Parser::Functions.newfunction("other_env", :type => :rvalue) do |args| + end + + expect(Puppet::Parser::Functions.function("onlyroot")).to eq("function_onlyroot") + expect(Puppet::Parser::Functions.function("other_env")).to eq("function_other_env") + end + + expect(Puppet::Parser::Functions.function("other_env")).to be_false + end end describe "when calling function to test arity" do @@ -125,77 +138,4 @@ describe Puppet::Parser::Functions do Puppet::Parser::Functions.arity(:name).should == -1 end end - - describe "::get_function" do - it "can retrieve a function defined on the *root* environment" do - $environment = nil - function = Puppet::Parser::Functions.newfunction("atest", :type => :rvalue) do - nil - end - - Puppet::Node::Environment.current = "test_env" - Puppet::Parser::Functions.send(:get_function, "atest").should equal(function) - end - - it "can retrieve a function from the current environment" do - Puppet::Node::Environment.current = "test_env" - function = Puppet::Parser::Functions.newfunction("atest", :type => :rvalue) do - nil - end - - Puppet::Parser::Functions.send(:get_function, "atest").should equal(function) - end - - it "takes a function in the current environment over one in the root" do - root = Puppet::Node::Environment.root - env = Puppet::Node::Environment.current = "test_env" - func1 = {:type => :rvalue, :name => :testfunc, :extra => :func1} - func2 = {:type => :rvalue, :name => :testfunc, :extra => :func2} - Puppet::Parser::Functions.instance_eval do - @functions[Puppet::Node::Environment.root][:atest] = func1 - @functions[Puppet::Node::Environment.current][:atest] = func2 - end - - Puppet::Parser::Functions.send(:get_function, "atest").should equal(func2) - end - end - - describe "::merged_functions" do - it "returns functions in both the current and root environment" do - $environment = nil - func_a = Puppet::Parser::Functions.newfunction("test_a", :type => :rvalue) do - nil - end - Puppet::Node::Environment.current = "test_env" - func_b = Puppet::Parser::Functions.newfunction("test_b", :type => :rvalue) do - nil - end - - Puppet::Parser::Functions.send(:merged_functions).should include(:test_a, :test_b) - end - - it "returns functions from the current environment over the root environment" do - root = Puppet::Node::Environment.root - env = Puppet::Node::Environment.current = "test_env" - func1 = {:type => :rvalue, :name => :testfunc, :extra => :func1} - func2 = {:type => :rvalue, :name => :testfunc, :extra => :func2} - Puppet::Parser::Functions.instance_eval do - @functions[Puppet::Node::Environment.root][:atest] = func1 - @functions[Puppet::Node::Environment.current][:atest] = func2 - end - - Puppet::Parser::Functions.send(:merged_functions)[:atest].should equal(func2) - end - end - - describe "::add_function" do - it "adds functions to the current environment" do - func = {:type => :rvalue, :name => :testfunc} - Puppet::Node::Environment.current = "add_function_test" - Puppet::Parser::Functions.send(:add_function, :testfunc, func) - - Puppet::Parser::Functions.instance_variable_get(:@functions)[Puppet::Node::Environment.root].should_not include(:testfunc) - Puppet::Parser::Functions.instance_variable_get(:@functions)[Puppet::Node::Environment.current].should include(:testfunc) - end - end end From 03fea798c2af1a5d266eed585171c343c78f1c6d Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 17 Jan 2014 12:52:45 -0800 Subject: [PATCH 453/800] (PUP-1118) Move global context methods to Puppet This moves the global context handling out of the Puppet::Context class and onto the Puppet module, which allows Puppet::Context to be a more generic, overridable context system. It also allows references to context information to be a little bit shorter. This doesn't tackle making settings information available in the context, and so there might be a little confusion between Puppet[] and Puppet.lookup. --- lib/puppet.rb | 55 ++++++++++++++++++- lib/puppet/bindings.rb | 10 ++-- lib/puppet/context.rb | 47 ---------------- lib/puppet/face/module/list.rb | 2 +- lib/puppet/indirector/catalog/compiler.rb | 2 +- lib/puppet/metatype/manager.rb | 2 +- .../module_tool/applications/installer.rb | 2 +- .../module_tool/applications/uninstaller.rb | 2 +- lib/puppet/network/http/api/v1.rb | 2 +- lib/puppet/network/http/api/v2.rb | 2 +- lib/puppet/network/http/handler.rb | 2 +- lib/puppet/node/environment.rb | 6 +- lib/puppet/parser/compiler.rb | 2 +- lib/puppet/parser/functions.rb | 12 ++-- lib/puppet/test/test_helper.rb | 6 +- spec/unit/face/module/list_spec.rb | 4 +- .../applications/installer_spec.rb | 2 +- .../applications/uninstaller_spec.rb | 2 +- spec/unit/parser/functions_spec.rb | 6 +- 19 files changed, 83 insertions(+), 85 deletions(-) diff --git a/lib/puppet.rb b/lib/puppet.rb index 771af0d08..0fb9fae6c 100644 --- a/lib/puppet.rb +++ b/lib/puppet.rb @@ -28,6 +28,8 @@ require 'puppet/external/pson/pure' # @api public module Puppet require 'puppet/file_system' + require 'puppet/context' + require 'puppet/environments' class << self include Puppet::Util @@ -148,7 +150,7 @@ module Puppet Puppet.settings.initialize_global_settings(args) run_mode = Puppet::Util::RunMode[run_mode] Puppet.settings.initialize_app_defaults(Puppet::Settings.app_defaults_for_run_mode(run_mode)) - Puppet::Context.push(Puppet::Context.initial_context, "Initial context after settings initialization") + Puppet.push_context(Puppet.initial_context, "Initial context after settings initialization") Puppet::Parser::Functions.reset end private_class_method :do_initialize_settings_for_run_mode @@ -168,6 +170,55 @@ module Puppet # Set default for YAML.load to unsafe so we don't affect programs # requiring puppet -- in puppet we will call safe explicitly SafeYAML::OPTIONS[:default_mode] = :unsafe + + # The bindings used for initialization of puppet + # @api private + def self.initial_context + { + :environments => Puppet::Environments::Legacy.new, + :current_environment => Puppet::Node::Environment.root, + } + end + + # A simple set of bindings that is just enough to limp along to + # initialization where the {#initial_context} bindings are put in place + # @api private + def self.bootstrap_context + { :current_environment => Puppet::Node::Environment.create(:'*bootstrap*', [], '') } + end + + # @param overrides [Hash] A hash of bindings to be merged with the parent context. + # @param description [String] A description of the context. + # @api private + def self.push_context(overrides, description = "") + @context.push(overrides, description) + end + + # Return to the previous context. + # @raise [StackUnderflow] if the current context is the root + # @api private + def self.pop_context + @context.pop + end + + # Lookup a binding by name or return a default value provided by a passed block (if given). + # @api public + def self.lookup(name, &block) + @context.lookup(name, &block) + end + + # @param bindings [Hash] A hash of bindings to be merged with the parent context. + # @param description [String] A description of the context. + # @yield [] A block executed in the context of the temporarily pushed bindings. + # @api public + def self.override(bindings, description = "", &block) + @context.override(bindings, description, &block) + end + + require 'puppet/node' + + # The single instance used for normal operation + @context = Puppet::Context.new(bootstrap_context) end # This feels weird to me; I would really like for us to get to a state where there is never a "require" statement @@ -185,5 +236,3 @@ require 'puppet/data_binding' require 'puppet/util/storage' require 'puppet/status' require 'puppet/file_bucket/file' -require 'puppet/context' -require 'puppet/environments' diff --git a/lib/puppet/bindings.rb b/lib/puppet/bindings.rb index 4c59bb6e4..a30cfe2b3 100644 --- a/lib/puppet/bindings.rb +++ b/lib/puppet/bindings.rb @@ -74,7 +74,7 @@ class Puppet::Bindings end def self.register_proc(name, block) - adapter = NamedBindingsAdapter.adapt(Puppet::Context.lookup(:current_environment)) + adapter = NamedBindingsAdapter.adapt(Puppet.lookup(:current_environment)) adapter[name] = block end @@ -83,7 +83,7 @@ class Puppet::Bindings # @api public # def self.register(named_bindings) - adapter = NamedBindingsAdapter.adapt(Puppet::Context.lookup(:current_environment)) + adapter = NamedBindingsAdapter.adapt(Puppet.lookup(:current_environment)) adapter[named_bindings.name] = named_bindings end @@ -91,7 +91,7 @@ class Puppet::Bindings entry = get(name) return entry unless entry.is_a?(Proc) named_bindings = Puppet::Pops::Binder::BindingsFactory.safe_named_bindings(name, scope, &entry).model - adapter = NamedBindingsAdapter.adapt(Puppet::Context.lookup(:current_environment)) + adapter = NamedBindingsAdapter.adapt(Puppet.lookup(:current_environment)) adapter[named_bindings.name] = named_bindings named_bindings end @@ -102,7 +102,7 @@ class Puppet::Bindings # @api public # def self.get(name) - adapter = NamedBindingsAdapter.adapt(Puppet::Context.lookup(:current_environment)) + adapter = NamedBindingsAdapter.adapt(Puppet.lookup(:current_environment)) adapter[name] end @@ -112,7 +112,7 @@ class Puppet::Bindings # Supports Enumerable iteration (k,v) over the named bindings hash. def self.each - adapter = NamedBindingsAdapter.adapt(Puppet::Context.lookup(:current_environment)) + adapter = NamedBindingsAdapter.adapt(Puppet.lookup(:current_environment)) adapter.each_pair {|k,v| yield k,v } end diff --git a/lib/puppet/context.rb b/lib/puppet/context.rb index b9725ac35..43c037109 100644 --- a/lib/puppet/context.rb +++ b/lib/puppet/context.rb @@ -69,51 +69,4 @@ class Puppet::Context ensure pop end - - # The bindings used for initialization of puppet - # @api private - def self.initial_context - { - :environments => Puppet::Environments::Legacy.new, - :current_environment => Puppet::Node::Environment.root, - } - end - - # A simple set of bindings that is just enough to limp along to - # initialization where the {#initial_context} bindings are put in place - # @api private - def self.bootstrap_context - { :current_environment => Puppet::Node::Environment.create(:'*bootstrap*', [], '') } - end - - # @param overrides [Hash] A hash of bindings to be merged with the parent context. - # @param description [String] A description of the context. - # @api private - def self.push(overrides, description = "") - @instance.push(overrides, description) - end - - # Return to the previous context. - # @raise [StackUnderflow] if the current context is the root - # @api private - def self.pop - @instance.pop - end - - # Lookup a binding by name or return a default value provided by a passed block (if given). - # @api public - def self.lookup(name, &block) - @instance.lookup(name, &block) - end - - # @param bindings [Hash] A hash of bindings to be merged with the parent context. - # @param description [String] A description of the context. - # @yield [] A block executed in the context of the temporarily pushed bindings. - # @api public - def self.override(bindings, description = "", &block) - @instance.override(bindings, description, &block) - end - - # The single instance used for normal operation - @instance = new(bootstrap_context) end diff --git a/lib/puppet/face/module/list.rb b/lib/puppet/face/module/list.rb index e4c30ed04..18c76709f 100644 --- a/lib/puppet/face/module/list.rb +++ b/lib/puppet/face/module/list.rb @@ -167,7 +167,7 @@ Puppet::Face.define(:module, '1.0.0') do end def environment_from_options(options) - environments = Puppet::Context.lookup(:environments) + environments = Puppet.lookup(:environments) if options[:modulepath] environments.for(options[:modulepath], '') elsif options[:environment] diff --git a/lib/puppet/indirector/catalog/compiler.rb b/lib/puppet/indirector/catalog/compiler.rb index e3af33e7c..ebce8edb7 100644 --- a/lib/puppet/indirector/catalog/compiler.rb +++ b/lib/puppet/indirector/catalog/compiler.rb @@ -41,7 +41,7 @@ class Puppet::Resource::Catalog::Compiler < Puppet::Indirector::Code extract_facts_from_request(request) node = node_from_request(request) - node.trusted_data = Puppet::Context.lookup(:trusted_information) { Puppet::Context::TrustedInformation.local(node) }.to_h + node.trusted_data = Puppet.lookup(:trusted_information) { Puppet::Context::TrustedInformation.local(node) }.to_h if catalog = compile(node) return catalog diff --git a/lib/puppet/metatype/manager.rb b/lib/puppet/metatype/manager.rb index a565939ab..007d22f97 100644 --- a/lib/puppet/metatype/manager.rb +++ b/lib/puppet/metatype/manager.rb @@ -156,7 +156,7 @@ module Manager end # Try loading the type. - if typeloader.load(name, Puppet::Context.lookup(:current_environment)) + if typeloader.load(name, Puppet.lookup(:current_environment)) Puppet.warning "Loaded puppet/type/#{name} but no class was created" unless @types.include? name end diff --git a/lib/puppet/module_tool/applications/installer.rb b/lib/puppet/module_tool/applications/installer.rb index fb7fb78cd..26e791da5 100644 --- a/lib/puppet/module_tool/applications/installer.rb +++ b/lib/puppet/module_tool/applications/installer.rb @@ -18,7 +18,7 @@ module Puppet::ModuleTool def initialize(name, forge, install_dir, options = {}) super(options) @action = :install - @environment = Puppet::Context.lookup(:environments).get(Puppet[:environment]) + @environment = Puppet.lookup(:environments).get(Puppet[:environment]) @force = options[:force] @ignore_dependencies = options[:force] || options[:ignore_dependencies] @name = name diff --git a/lib/puppet/module_tool/applications/uninstaller.rb b/lib/puppet/module_tool/applications/uninstaller.rb index 4f88842ea..3cd12394e 100644 --- a/lib/puppet/module_tool/applications/uninstaller.rb +++ b/lib/puppet/module_tool/applications/uninstaller.rb @@ -10,7 +10,7 @@ module Puppet::ModuleTool @unfiltered = [] @installed = [] @suggestions = [] - @environment = Puppet::Context.lookup(:environments).get(options[:environment]) + @environment = Puppet.lookup(:environments).get(options[:environment]) end def run diff --git a/lib/puppet/network/http/api/v1.rb b/lib/puppet/network/http/api/v1.rb index 926408ac9..329177a8e 100644 --- a/lib/puppet/network/http/api/v1.rb +++ b/lib/puppet/network/http/api/v1.rb @@ -43,7 +43,7 @@ class Puppet::Network::HTTP::API::V1 end trusted = Puppet::Context::TrustedInformation.remote(params[:authenticated], params[:node], certificate) - Puppet::Context.override(:trusted_information => trusted) do + Puppet.override(:trusted_information => trusted) do send("do_#{method}", indirection, key, params, request, response) end rescue Puppet::Network::HTTP::Error::HTTPError => e diff --git a/lib/puppet/network/http/api/v2.rb b/lib/puppet/network/http/api/v2.rb index 08e2f8efc..adb64a3a9 100644 --- a/lib/puppet/network/http/api/v2.rb +++ b/lib/puppet/network/http/api/v2.rb @@ -27,6 +27,6 @@ module Puppet::Network::HTTP::API::V2 end) ENVIRONMENTS = path(%r{^/environments$}).get(provide do - Environments.new(Puppet::Context.lookup(:environments)) + Environments.new(Puppet.lookup(:environments)) end) end diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb index bcebd81eb..c00cbb441 100644 --- a/lib/puppet/network/http/handler.rb +++ b/lib/puppet/network/http/handler.rb @@ -57,7 +57,7 @@ module Puppet::Network::HTTP::Handler configure_profiler(request_headers, request_params) warn_if_near_expiration(new_request.client_cert) - Puppet::Context.override(request_bindings()) do + Puppet.override(request_bindings()) do Puppet::Util::Profiler.profile("Processed request #{request_method} #{request_path}") do if route = @routes.find { |route| route.matches?(new_request) } route.process(new_request, new_response) diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb index 5d9ff0635..4a40ec0f4 100644 --- a/lib/puppet/node/environment.rb +++ b/lib/puppet/node/environment.rb @@ -47,10 +47,6 @@ end # that are not bound to a specific environment. The main case for this is for # logging functions. Logging functions are attached to the 'root' environment # when {Puppet::Parser::Functions.reset} is called. -# -# The root environment is also used as a fallback environment when the -# current environment has been requested by {Puppet::Node::Environment.current} -# requested and no environment was set by {Puppet::Node::Environment.current=} class Puppet::Node::Environment # This defines a mixin for classes that have an environment. It implements @@ -158,7 +154,7 @@ class Puppet::Node::Environment # has been explicitly set, else it will return the '*root*' environment def self.current Puppet.deprecation_warning("Remove me.") - Puppet::Context.lookup(:current_environment) + Puppet.lookup(:current_environment) end # @return [Puppet::Node::Environment] The `*root*` environment. diff --git a/lib/puppet/parser/compiler.rb b/lib/puppet/parser/compiler.rb index a6d40bc16..7014bf3ad 100644 --- a/lib/puppet/parser/compiler.rb +++ b/lib/puppet/parser/compiler.rb @@ -99,7 +99,7 @@ class Puppet::Parser::Compiler # Compiler our catalog. This mostly revolves around finding and evaluating classes. # This is the main entry into our catalog. def compile - Puppet::Context.override({ :current_environment => environment }, "For compiling #{node.name}") do + Puppet.override({ :current_environment => environment }, "For compiling #{node.name}") do # Set the client's parameters into the top scope. Puppet::Util::Profiler.profile("Compile: Set node parameters") { set_node_parameters } diff --git a/lib/puppet/parser/functions.rb b/lib/puppet/parser/functions.rb index 03c345448..0c422ad38 100644 --- a/lib/puppet/parser/functions.rb +++ b/lib/puppet/parser/functions.rb @@ -41,7 +41,7 @@ module Puppet::Parser::Functions # environment # # @api private - def self.environment_module(env = Puppet::Context.lookup(:current_environment)) + def self.environment_module(env = Puppet.lookup(:current_environment)) if env and ! env.is_a?(Puppet::Node::Environment) env = Puppet::Node::Environment.new(env) end @@ -118,7 +118,7 @@ module Puppet::Parser::Functions # @api public def self.newfunction(name, options = {}, &block) name = name.intern - environment = Puppet::Context.lookup(:current_environment) + environment = Puppet.lookup(:current_environment) Puppet.warning "Overwriting previous definition for function #{name}" if get_function(name, environment) @@ -166,7 +166,7 @@ module Puppet::Parser::Functions # otherwise false. # # @api public - def self.function(name, environment = Puppet::Context.lookup(:current_environment)) + def self.function(name, environment = Puppet.lookup(:current_environment)) name = name.intern func = nil @@ -182,7 +182,7 @@ module Puppet::Parser::Functions end end - def self.functiondocs(environment = Puppet::Context.lookup(:current_environment)) + def self.functiondocs(environment = Puppet.lookup(:current_environment)) autoloader.loadall ret = "" @@ -208,7 +208,7 @@ module Puppet::Parser::Functions # @return [Boolean] whether it is an rvalue function # # @api public - def self.rvalue?(name, environment = Puppet::Context.lookup(:current_environment)) + def self.rvalue?(name, environment = Puppet.lookup(:current_environment)) func = get_function(name, environment) func ? func[:type] == :rvalue : false end @@ -221,7 +221,7 @@ module Puppet::Parser::Functions # the meaning of negative values. # # @api public - def self.arity(name, environment = Puppet::Context.lookup(:current_environment)) + def self.arity(name, environment = Puppet.lookup(:current_environment)) func = get_function(name, environment) func ? func[:arity] : -1 end diff --git a/lib/puppet/test/test_helper.rb b/lib/puppet/test/test_helper.rb index 5e97dc8a5..252b1e2ae 100644 --- a/lib/puppet/test/test_helper.rb +++ b/lib/puppet/test/test_helper.rb @@ -41,7 +41,7 @@ module Puppet::Test # @return nil def self.before_all_tests() # Make sure that all of the setup is also done for any before(:all) blocks - Puppet::Context.push(Puppet::Context.initial_context, "Initial for specs") + Puppet.push_context(Puppet.initial_context, "Initial for specs") self.before_each_test() end @@ -86,7 +86,7 @@ module Puppet::Test initialize_settings_before_each() - Puppet::Context.push( + Puppet.push_context( { :trusted_information => Puppet::Context::TrustedInformation.new('local', 'testing', {}), @@ -147,7 +147,7 @@ module Puppet::Test $LOAD_PATH.clear $old_load_path.each {|x| $LOAD_PATH << x } - Puppet::Context.pop + Puppet.pop_context end diff --git a/spec/unit/face/module/list_spec.rb b/spec/unit/face/module/list_spec.rb index ef4cd04a9..8ac7e8523 100644 --- a/spec/unit/face/module/list_spec.rb +++ b/spec/unit/face/module/list_spec.rb @@ -21,7 +21,7 @@ describe "puppet module list" do end around do |example| - Puppet::Context.override(:environments => Puppet::Environments::Legacy.new()) do + Puppet.override(:environments => Puppet::Environments::Legacy.new()) do example.run end end @@ -56,7 +56,7 @@ describe "puppet module list" do usedenv = Puppet::Node::Environment.create(:useme, [@modpath1, @modpath2], '') - Puppet::Context.override(:environments => Puppet::Environments::Static.new(usedenv)) do + Puppet.override(:environments => Puppet::Environments::Static.new(usedenv)) do Puppet::Face[:module, :current].list(:environment => 'useme').should == { @modpath1 => [ Puppet::Module.new('bar', barmod.path, usedenv), diff --git a/spec/unit/module_tool/applications/installer_spec.rb b/spec/unit/module_tool/applications/installer_spec.rb index 30712b7c4..dd99fadae 100644 --- a/spec/unit/module_tool/applications/installer_spec.rb +++ b/spec/unit/module_tool/applications/installer_spec.rb @@ -26,7 +26,7 @@ describe Puppet::ModuleTool::Applications::Installer, :unless => Puppet.features end around :each do |example| - Puppet::Context.override(:environments => Puppet::Environments::Static.new(env)) do + Puppet.override(:environments => Puppet::Environments::Static.new(env)) do example.run end end diff --git a/spec/unit/module_tool/applications/uninstaller_spec.rb b/spec/unit/module_tool/applications/uninstaller_spec.rb index e1fe6eba5..b80a8f5ba 100644 --- a/spec/unit/module_tool/applications/uninstaller_spec.rb +++ b/spec/unit/module_tool/applications/uninstaller_spec.rb @@ -28,7 +28,7 @@ describe Puppet::ModuleTool::Applications::Uninstaller do let(:uninstaller) { Puppet::ModuleTool::Applications::Uninstaller.new("puppetlabs-foo", options) } around :each do |example| - Puppet::Context.override(:environments => Puppet::Environments::Static.new(env)) do + Puppet.override(:environments => Puppet::Environments::Static.new(env)) do example.run end end diff --git a/spec/unit/parser/functions_spec.rb b/spec/unit/parser/functions_spec.rb index f169e0a6f..35fdca846 100755 --- a/spec/unit/parser/functions_spec.rb +++ b/spec/unit/parser/functions_spec.rb @@ -6,7 +6,7 @@ describe Puppet::Parser::Functions do Class.new { include mod }.new end - let(:function_module) { Puppet::Parser::Functions.environment_module(Puppet::Context.lookup(:current_environment)) } + let(:function_module) { Puppet::Parser::Functions.environment_module(Puppet.lookup(:current_environment)) } before do Puppet::Parser::Functions.reset @@ -73,12 +73,12 @@ describe Puppet::Parser::Functions do end it "combines functions from the root with those from the current environment" do - Puppet::Context.override(:current_environment => Puppet::Node::Environment.root) do + Puppet.override(:current_environment => Puppet::Node::Environment.root) do Puppet::Parser::Functions.newfunction("onlyroot", :type => :rvalue) do |args| end end - Puppet::Context.override(:current_environment => Puppet::Node::Environment.create(:other, [''], '')) do + Puppet.override(:current_environment => Puppet::Node::Environment.create(:other, [''], '')) do Puppet::Parser::Functions.newfunction("other_env", :type => :rvalue) do |args| end From f716cf7dbd4dc2c23dfeb4f84b23de66ad4e0c3b Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 17 Jan 2014 13:09:59 -0800 Subject: [PATCH 454/800] (Maint) Mark new context methods private Let's keep these private a while longer until they have settled down a bit more. --- lib/puppet.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/puppet.rb b/lib/puppet.rb index 0fb9fae6c..10d467fc4 100644 --- a/lib/puppet.rb +++ b/lib/puppet.rb @@ -202,7 +202,7 @@ module Puppet end # Lookup a binding by name or return a default value provided by a passed block (if given). - # @api public + # @api private def self.lookup(name, &block) @context.lookup(name, &block) end @@ -210,7 +210,7 @@ module Puppet # @param bindings [Hash] A hash of bindings to be merged with the parent context. # @param description [String] A description of the context. # @yield [] A block executed in the context of the temporarily pushed bindings. - # @api public + # @api private def self.override(bindings, description = "", &block) @context.override(bindings, description, &block) end From 898b660253ae58974ed96c8cad82fde3be8532bb Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 17 Jan 2014 13:23:56 -0800 Subject: [PATCH 455/800] (Maint) Unravel require of forge code This changes it so that the puppet/forge file handles pulling in all of the forge related code and thereby sidesteps all sorts of load order issues. --- lib/puppet/forge.rb | 7 ++++--- lib/puppet/forge/repository.rb | 1 - lib/puppet/module_tool.rb | 1 - spec/unit/forge/errors_spec.rb | 2 +- spec/unit/forge/repository_spec.rb | 4 +--- 5 files changed, 6 insertions(+), 9 deletions(-) diff --git a/lib/puppet/forge.rb b/lib/puppet/forge.rb index 3c6626328..e5b54d4c5 100644 --- a/lib/puppet/forge.rb +++ b/lib/puppet/forge.rb @@ -2,11 +2,12 @@ require 'net/http' require 'open-uri' require 'pathname' require 'uri' -require 'puppet/forge/cache' -require 'puppet/forge/repository' -require 'puppet/forge/errors' class Puppet::Forge + require 'puppet/forge/errors' + require 'puppet/forge/cache' + require 'puppet/forge/repository' + include Puppet::Forge::Errors # +consumer_name+ is a name to be used for identifying the consumer of the diff --git a/lib/puppet/forge/repository.rb b/lib/puppet/forge/repository.rb index 75b7a9f4c..eb2e490e4 100644 --- a/lib/puppet/forge/repository.rb +++ b/lib/puppet/forge/repository.rb @@ -2,7 +2,6 @@ require 'net/https' require 'digest/sha1' require 'uri' require 'puppet/util/http_proxy' -require 'puppet/forge/errors' if Puppet.features.zlib? && Puppet[:zlib] require 'zlib' diff --git a/lib/puppet/module_tool.rb b/lib/puppet/module_tool.rb index 98876b6dd..eb6d91f9a 100644 --- a/lib/puppet/module_tool.rb +++ b/lib/puppet/module_tool.rb @@ -144,5 +144,4 @@ require 'puppet/module_tool/dependency' require 'puppet/module_tool/metadata' require 'puppet/module_tool/modulefile' require 'puppet/module_tool/skeleton' -require 'puppet/forge/cache' require 'puppet/forge' diff --git a/spec/unit/forge/errors_spec.rb b/spec/unit/forge/errors_spec.rb index 686790e3a..057bf014a 100644 --- a/spec/unit/forge/errors_spec.rb +++ b/spec/unit/forge/errors_spec.rb @@ -1,5 +1,5 @@ require 'spec_helper' -require 'puppet/forge/errors' +require 'puppet/forge' describe Puppet::Forge::Errors do describe 'SSLVerifyError' do diff --git a/spec/unit/forge/repository_spec.rb b/spec/unit/forge/repository_spec.rb index 31d28b5d9..95bf8f9af 100644 --- a/spec/unit/forge/repository_spec.rb +++ b/spec/unit/forge/repository_spec.rb @@ -1,9 +1,7 @@ # encoding: utf-8 require 'spec_helper' require 'net/http' -require 'puppet/forge/repository' -require 'puppet/forge/cache' -require 'puppet/forge/errors' +require 'puppet/forge' describe Puppet::Forge::Repository do let(:consumer_version) { "Test/1.0" } From df859f885d55bb851cdfef3d43c2815a5abcbb53 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 17 Jan 2014 23:04:43 +0100 Subject: [PATCH 456/800] (maint) remove dead code in scope_spec Commented out code was testing removed behavior. Removed comments. --- spec/unit/parser/scope_spec.rb | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/spec/unit/parser/scope_spec.rb b/spec/unit/parser/scope_spec.rb index 4ed6b8ee8..1d41a6aec 100755 --- a/spec/unit/parser/scope_spec.rb +++ b/spec/unit/parser/scope_spec.rb @@ -190,18 +190,6 @@ describe Puppet::Parser::Scope do @scope.should_not be_include("var") end -# it "should support iteration over its variables" do -# @scope["one"] = "two" -# @scope["three"] = "four" -# hash = {} -# @scope.each { |name, value| hash[name] = value } -# hash.should == {"one" => "two", "three" => "four" } -# end - -# it "should include Enumerable" do -# @scope.singleton_class.ancestors.should be_include(Enumerable) -# end - describe "and the variable is qualified" do before :each do @known_resource_types = @scope.known_resource_types @@ -464,19 +452,6 @@ describe Puppet::Parser::Scope do @scope.include?("1").should be_false end -# # TODO: Stupid test - nothing but tests use the remove all ephemeral -# describe "when calling unset_ephemeral_var without a level" do -# it "should remove all the variables values" do -# @scope.setvar("1", :value1, :ephemeral => true) -# @scope.new_ephemeral -# @scope.setvar("1", :value2, :ephemeral => true) -# -# @scope.unset_ephemeral_var -# -# @scope["1"].should be_nil -# end -# end - describe "when calling unset_ephemeral_var with a level" do it "should remove ephemeral scopes up to this level" do @scope.set_match_data({1 => :value1}) From 02878e80ae7b3591cab3cf456015d347283cd106 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 17 Jan 2014 23:38:39 +0100 Subject: [PATCH 457/800] (PUP-1166) Add better error message when turning on strict_variables When turning on --strict_variables without also using --future parser with the future evaluator, the raised error is an uncaught throw. Now the error message is "Undefined Variable". --- lib/puppet/parser/scope.rb | 32 ++++++++++++------- spec/unit/parser/scope_spec.rb | 14 ++++++++ .../pops/evaluator/evaluating_parser_spec.rb | 6 ++++ 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb index ecf5ca94d..95b63d438 100644 --- a/lib/puppet/parser/scope.rb +++ b/lib/puppet/parser/scope.rb @@ -366,14 +366,19 @@ class Puppet::Parser::Scope if next_scope next_scope.lookupvar(name, options) else - variable_not_found() + variable_not_found(name) end end end - def variable_not_found + def variable_not_found(name, reason=nil) if Puppet[:strict_variables] - throw :undefined_variable + if Puppet[:evaluator] == 'future' && Puppet[:parser] == 'future' + throw :undefined_variable + else + reason_msg = reason.nil? ? '' : "; #{reason}" + raise Puppet::ParseError, "Undefined variable #{name.inspect}#{reason_msg}" + end else nil end @@ -443,15 +448,18 @@ class Puppet::Parser::Scope qualified_scope(class_name).lookupvar(variable_name, position) end rescue RuntimeError => e - location = if position[:lineproc] - " at #{position[:lineproc].call}" - elsif position[:file] && position[:line] - " at #{position[:file]}:#{position[:line]}" - else - "" - end - warning "Could not look up qualified variable '#{class_name}::#{variable_name}'; #{e.message}#{location}" - variable_not_found() + unless Puppet[:strict_variables] + # Do not issue warning if strict variables are on, as an error will be raised by variable_not_found + location = if position[:lineproc] + " at #{position[:lineproc].call}" + elsif position[:file] && position[:line] + " at #{position[:file]}:#{position[:line]}" + else + "" + end + warning "Could not look up qualified variable '#{class_name}::#{variable_name}'; #{e.message}#{location}" + end + variable_not_found("#{class_name}::#{variable_name}", e.message) end end diff --git a/spec/unit/parser/scope_spec.rb b/spec/unit/parser/scope_spec.rb index 1d41a6aec..95ca6790b 100755 --- a/spec/unit/parser/scope_spec.rb +++ b/spec/unit/parser/scope_spec.rb @@ -268,6 +268,20 @@ describe Puppet::Parser::Scope do @scope["other::deep::klass::var"].should be_nil end end + + context "and strict_variables is true" do + before(:each) do + Puppet[:strict_variables] = true + end + + it "should raise an error when unknown variable is looked up" do + expect { @scope['john_doe'] }.to raise_error(/Undefined variable/) + end + + it "should raise an error when unknown qualified variable is looked up" do + expect { @scope['nowhere::john_doe'] }.to raise_error(/Undefined variable/) + end + end end describe "when variables are set with append=true" do diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index de5119a26..511efd6a5 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -16,6 +16,12 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do include PuppetSpec::Scope before(:each) do Puppet[:strict_variables] = true + + # These must be set since the is 3x logic that triggers on these even if the tests are explicit + # about selection of parser and evaluator + # + Puppet[:parser] = 'future' + Puppet[:evaluator] = 'future' end let(:parser) { Puppet::Pops::Parser::EvaluatingParser::Transitional.new } From 91ebb1f42c020daba8e46211f631c7fa123a5805 Mon Sep 17 00:00:00 2001 From: Brandon High Date: Fri, 17 Jan 2014 15:24:44 -0800 Subject: [PATCH 458/800] Fixing HTTP API link formatting --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 648e92327..0f0fd0c5e 100644 --- a/README.md +++ b/README.md @@ -50,4 +50,4 @@ is an active #puppet channel on Freenode. HTTP API -------- -{file:api/docs/http_api_index.md HTTP API Index} +[HTTP API Index](api/docs/http_api_index.md) From 9ed39df6438f994fa0c36bc94a2ab0b5420689ff Mon Sep 17 00:00:00 2001 From: Brandon High Date: Fri, 17 Jan 2014 15:26:07 -0800 Subject: [PATCH 459/800] Adding link to LICENCE --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0f0fd0c5e..60723a25a 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Documentation](https://github.com/puppetlabs/puppet/blob/master/README_DEVELOPER License ------- -See LICENSE file. +See [LICENCE](licence.md) file. Support ------- From c1d1060f7eca821d46c194f80fb88e7240d6ac08 Mon Sep 17 00:00:00 2001 From: Brandon High Date: Fri, 17 Jan 2014 15:26:49 -0800 Subject: [PATCH 460/800] LICENCE is not a .md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 60723a25a..434e40ddf 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Documentation](https://github.com/puppetlabs/puppet/blob/master/README_DEVELOPER License ------- -See [LICENCE](licence.md) file. +See [LICENCE](LICENCE) file. Support ------- From 36ef11861ed7b0c87b8f7ddd9052ebd64156020f Mon Sep 17 00:00:00 2001 From: Brandon High Date: Fri, 17 Jan 2014 15:27:36 -0800 Subject: [PATCH 461/800] Spelling LICENSE correctly --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 434e40ddf..274c99039 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Documentation](https://github.com/puppetlabs/puppet/blob/master/README_DEVELOPER License ------- -See [LICENCE](LICENCE) file. +See [LICENSE](LICENSE) file. Support ------- From 3127da4c0f2ba73bf07bd82f3cb5195b0ca2ba84 Mon Sep 17 00:00:00 2001 From: Brandon High Date: Fri, 17 Jan 2014 15:28:14 -0800 Subject: [PATCH 462/800] Updating copyright years in LICENSE --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 934582dfa..e2f64ae04 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ Puppet - Automating Configuration Management. - Copyright (C) 2005-2012 Puppet Labs Inc + Copyright (C) 2005-2014 Puppet Labs Inc Puppet Labs can be contacted at: info@puppetlabs.com From c580cd93ff801668022e296aeaf6be4f3bcc31e0 Mon Sep 17 00:00:00 2001 From: Iristyle Date: Fri, 17 Jan 2014 15:58:42 -0800 Subject: [PATCH 463/800] Revert "Merge branch 'ticket/master/18342-windows-file-setting-owner-group'" This reverts commit cf4501c7a97c106994c2ef81756c2081f56feeef, reversing changes made to fcf482aa85708fe20f38cd96d6d92fc58ee78cf6. --- ...apply_file_metadata_specified_in_config.rb | 3 + lib/puppet/settings/file_setting.rb | 30 ++----- lib/puppet/util/adsi.rb | 15 ---- spec/integration/defaults_spec.rb | 87 ------------------- spec/unit/provider/group/windows_adsi_spec.rb | 1 - spec/unit/provider/user/windows_adsi_spec.rb | 1 - spec/unit/settings/file_setting_spec.rb | 36 ++++++-- spec/unit/settings_spec.rb | 3 +- spec/unit/util/adsi_spec.rb | 31 +------ spec/unit/util/windows/sid_spec.rb | 6 -- 10 files changed, 38 insertions(+), 175 deletions(-) diff --git a/acceptance/tests/config/apply_file_metadata_specified_in_config.rb b/acceptance/tests/config/apply_file_metadata_specified_in_config.rb index de9ccc0f6..932ec74f3 100644 --- a/acceptance/tests/config/apply_file_metadata_specified_in_config.rb +++ b/acceptance/tests/config/apply_file_metadata_specified_in_config.rb @@ -1,5 +1,8 @@ test_name "#17371 file metadata specified in puppet.conf needs to be applied" +# when owner/group works on windows for settings, this confine should be removed. +confine :except, :platform => 'windows' + require 'puppet/acceptance/temp_file_utils' extend Puppet::Acceptance::TempFileUtils initialize_temp_dirs() diff --git a/lib/puppet/settings/file_setting.rb b/lib/puppet/settings/file_setting.rb index 791b66464..e20767374 100644 --- a/lib/puppet/settings/file_setting.rb +++ b/lib/puppet/settings/file_setting.rb @@ -16,11 +16,7 @@ class Puppet::Settings::FileSetting < Puppet::Settings::StringSetting # @api private class Root def value - if Puppet.features.microsoft_windows? - return Win32::Security::SID::BuiltinAdministrators - end - - 'root' + "root" end end @@ -79,7 +75,7 @@ class Puppet::Settings::FileSetting < Puppet::Settings::StringSetting when "service" # Group falls back to `nil` because we cannot assume that a "root" group exists. # Some systems have root group, others have wheel, others have something else. - Service.new(:group, service_group_fallback, @settings, :service_group_available?) + Service.new(:group, nil, @settings, :service_group_available?) else unknown_value(':group', value) end @@ -92,7 +88,7 @@ class Puppet::Settings::FileSetting < Puppet::Settings::StringSetting when "root" Root.new when "service" - Service.new(:user, service_owner_fallback, @settings, :service_user_available?) + Service.new(:user, "root", @settings, :service_user_available?) else unknown_value(':owner', value) end @@ -159,7 +155,8 @@ class Puppet::Settings::FileSetting < Puppet::Settings::StringSetting resource[:mode] = mode end - if Puppet.features.root? + # REMIND fails on Windows because chown/chgrp functionality not supported yet + if Puppet.features.root? and !Puppet.features.microsoft_windows? resource[:owner] = self.owner if self.owner resource[:group] = self.group if self.group end @@ -201,22 +198,7 @@ class Puppet::Settings::FileSetting < Puppet::Settings::StringSetting end end -private - def service_owner_fallback - if Puppet.features.microsoft_windows? - return Win32::Security::SID::BuiltinAdministrators - end - 'root' - end - - def service_group_fallback - # Service group includes all security principals that have logged on as a service. - # Membership is controlled by the operating system. - if Puppet.features.microsoft_windows? - return Win32::Security::SID::Service - end - nil - end + private def file Puppet::FileSystem.pathname(value) diff --git a/lib/puppet/util/adsi.rb b/lib/puppet/util/adsi.rb index 941413ca1..98639eabd 100644 --- a/lib/puppet/util/adsi.rb +++ b/lib/puppet/util/adsi.rb @@ -41,17 +41,6 @@ module Puppet::Util::ADSI "winmgmts:{impersonationLevel=impersonate}!//#{host}/root/cimv2" end - def sid_uri_safe(sid) - return Puppet::Util::ADSI.sid_uri(sid) if sid.kind_of?(Win32::Security::SID) - - begin - sid = Win32::Security::SID.new(Win32::Security::SID.string_to_sid(sid)) - Puppet::Util::ADSI.sid_uri(sid) - rescue - return nil - end - end - def sid_uri(sid) raise Puppet::Error.new( "Must use a valid SID object" ) if !sid.kind_of?(Win32::Security::SID) "WinNT://#{sid.to_s}" @@ -107,8 +96,6 @@ module Puppet::Util::ADSI end def self.uri(name, host = '.') - if sid_uri = Puppet::Util::ADSI.sid_uri_safe(name) then return sid_uri end - host = '.' if ['NT AUTHORITY', 'BUILTIN', Socket.gethostname].include?(host) Puppet::Util::ADSI.uri(name, 'user', host) @@ -253,8 +240,6 @@ module Puppet::Util::ADSI end def self.uri(name, host = '.') - if sid_uri = Puppet::Util::ADSI.sid_uri_safe(name) then return sid_uri end - Puppet::Util::ADSI.uri(name, 'group', host) end diff --git a/spec/integration/defaults_spec.rb b/spec/integration/defaults_spec.rb index 25507f4f2..8c8432b6f 100755 --- a/spec/integration/defaults_spec.rb +++ b/spec/integration/defaults_spec.rb @@ -312,91 +312,4 @@ describe "Puppet defaults" do Puppet.settings.setting(:agent_catalog_run_lockfile).should be_a Puppet::Settings::StringSetting end end - - describe "properly enforces permissions on" do - describe "vardir" do - before :each do - # Puppet will create this dir with requested permissions - @vardir = Puppet::FileSystem.pathname(Puppet.settings[:vardir]) - @path = @vardir.to_s - - # instruct Puppet to load settings, which resets perms - Puppet.settings.use(:main) - end - - describe "on POSIX", :if => Puppet.features.root? && - !Puppet.features.microsoft_windows? do - - it "with a group and owner of service" do - provider = Puppet::Type.type(:file).provider(:posix).new - - stat = Puppet::FileSystem.stat(@path) - provider.uid2name(stat.uid).should == Puppet.settings[:user] - provider.gid2name(stat.gid).should == Puppet.settings[:group] - end - end - - describe "on Windows", :if => Puppet.features.root? && - Puppet.features.microsoft_windows? do - - it "with a group and owner of service" do - sd = Puppet::Util::Windows::Security.get_security_descriptor(@path) - - sd.owner.should == Win32::Security::SID::BuiltinAdministrators - sd.group.should == Win32::Security::SID::Service - end - end - end - - # really, any file that relies on 'root' would do here - # we simply want to verify that a file with 'root' owner and mode works - describe "classfile" do - - before :each do - @classfile = Puppet::FileSystem.pathname(Puppet.settings[:classfile]) - @path = @classfile.to_s - - FileUtils.mkdir_p(@classfile.parent) - FileUtils.touch(@classfile) - end - - describe "on POSIX", :if => Puppet.features.root? && - !Puppet.features.microsoft_windows? do - - it "with an owner of root" do - provider = Puppet::Type.type(:file).provider(:posix).new - - # change owner to nobody - nobody_id = provider.name2uid('nobody') - File.chown(nobody_id, nil, @path) - - # instruct Puppet to load settings, which resets perms - Puppet.settings.use(:agent) - - provider.uid2name(classfile.stat.uid).should == 'root' - provider.mode.should == '640' - end - end - - describe "on Windows", :if => Puppet.features.root? && - Puppet.features.microsoft_windows? do - - it "with an owner of Administrators" do - # change the owner to everyone - sd = Puppet::Util::Windows::Security.get_security_descriptor(@path) - sd.owner = Win32::Security::SID::Everyone - Puppet::Util::Windows::Security.set_security_descriptor(@path, sd) - - # instruct Puppet to load settings, which resets perms - Puppet.settings.use(:agent) - - sd = Puppet::Util::Windows::Security.get_security_descriptor(@path) - sd.owner.should == Win32::Security::SID::BuiltinAdministrators - - mode = Puppet::Util::Windows::Security.get_mode(@path) - mode.to_s(8).should == '640' - end - end - end - end end diff --git a/spec/unit/provider/group/windows_adsi_spec.rb b/spec/unit/provider/group/windows_adsi_spec.rb index a7de859da..d28601172 100644 --- a/spec/unit/provider/group/windows_adsi_spec.rb +++ b/spec/unit/provider/group/windows_adsi_spec.rb @@ -138,7 +138,6 @@ describe Puppet::Type.type(:group).provider(:windows_adsi) do end it "should be able to test whether a group exists" do - Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil) Puppet::Util::ADSI.stubs(:connect).returns stub('connection') provider.should be_exists diff --git a/spec/unit/provider/user/windows_adsi_spec.rb b/spec/unit/provider/user/windows_adsi_spec.rb index 8d3ed1d0a..c25ccaf95 100755 --- a/spec/unit/provider/user/windows_adsi_spec.rb +++ b/spec/unit/provider/user/windows_adsi_spec.rb @@ -122,7 +122,6 @@ describe Puppet::Type.type(:user).provider(:windows_adsi) do end it 'should be able to test whether a user exists' do - Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil) Puppet::Util::ADSI.stubs(:connect).returns stub('connection') provider.should be_exists diff --git a/spec/unit/settings/file_setting_spec.rb b/spec/unit/settings/file_setting_spec.rb index 70c93732a..b31d0ccb3 100755 --- a/spec/unit/settings/file_setting_spec.rb +++ b/spec/unit/settings/file_setting_spec.rb @@ -36,11 +36,10 @@ describe Puppet::Settings::FileSetting do setting = FileSetting.new(:settings => settings, :owner => "root", :desc => "a setting") - setting.owner.should == FileSetting::Root.new.value + setting.owner.should == "root" end - it "is the service user if we are making users", - :unless => Puppet.features.microsoft_windows? do + it "is the service user if we are making users" do settings = settings(:user => "the_service", :mkusers => true, :service_user_available? => false) setting = FileSetting.new(:settings => settings, :owner => "service", :desc => "a setting") @@ -61,7 +60,7 @@ describe Puppet::Settings::FileSetting do setting = FileSetting.new(:settings => settings, :owner => "service", :desc => "a setting") - setting.owner.should == FileSetting::Root.new.value + setting.owner.should == "root" end it "is unspecified when no specific owner is wanted" do @@ -86,7 +85,7 @@ describe Puppet::Settings::FileSetting do setting = FileSetting.new(:settings => settings, :group => "root", :desc => "a setting") - setting.group.should == FileSetting::Root.new.value + setting.group.should == "root" end it "is the service group if we are making users" do @@ -105,14 +104,12 @@ describe Puppet::Settings::FileSetting do setting.group.should == "the_service" end - it "is a fallback default value (unspecified on POSIX, 'Service' on Windows) when the setting specifies service and the group is not available on the system" do + it "is unspecified when the setting specifies service and the group is not available on the system" do settings = settings(:group => "the_service", :mkusers => false, :service_group_available? => false) setting = FileSetting.new(:settings => settings, :group => "service", :desc => "a setting") - group = Puppet.features.microsoft_windows? ? Win32::Security::SID::Service : nil - - setting.group.should == group + setting.group.should be_nil end it "does not allow other groups" do @@ -199,6 +196,7 @@ describe Puppet::Settings::FileSetting do it "should set the owner if running as root and the owner is provided" do Puppet.features.expects(:root?).returns true + Puppet.features.stubs(:microsoft_windows?).returns false @file.stubs(:owner).returns "foo" @file.to_resource[:owner].should == "foo" @@ -214,6 +212,7 @@ describe Puppet::Settings::FileSetting do it "should set the group if running as root and the group is provided" do Puppet.features.expects(:root?).returns true + Puppet.features.stubs(:microsoft_windows?).returns false @file.stubs(:group).returns "foo" @file.to_resource[:group].should == "foo" @@ -227,18 +226,37 @@ describe Puppet::Settings::FileSetting do @file.to_resource[:group].should == nil end + it "should not set owner if not running as root" do Puppet.features.expects(:root?).returns false + Puppet.features.stubs(:microsoft_windows?).returns false @file.stubs(:owner).returns "foo" @file.to_resource[:owner].should be_nil end it "should not set group if not running as root" do Puppet.features.expects(:root?).returns false + Puppet.features.stubs(:microsoft_windows?).returns false @file.stubs(:group).returns "foo" @file.to_resource[:group].should be_nil end + describe "on Microsoft Windows systems" do + before :each do + Puppet.features.stubs(:microsoft_windows?).returns true + end + + it "should not set owner" do + @file.stubs(:owner).returns "foo" + @file.to_resource[:owner].should be_nil + end + + it "should not set group" do + @file.stubs(:group).returns "foo" + @file.to_resource[:group].should be_nil + end + end + it "should set :ensure to the file type" do @file.expects(:type).returns :directory @file.to_resource[:ensure].should == :directory diff --git a/spec/unit/settings_spec.rb b/spec/unit/settings_spec.rb index 6f35f18f9..b201b3eb8 100755 --- a/spec/unit/settings_spec.rb +++ b/spec/unit/settings_spec.rb @@ -1229,8 +1229,7 @@ describe Puppet::Settings do @settings.to_catalog end - describe "on Microsoft Windows", - :if => Puppet.features.microsoft_windows? do + describe "on Microsoft Windows" do before :each do Puppet.features.stubs(:root?).returns true Puppet.features.stubs(:microsoft_windows?).returns true diff --git a/spec/unit/util/adsi_spec.rb b/spec/unit/util/adsi_spec.rb index 5e9eb129d..974aba79c 100755 --- a/spec/unit/util/adsi_spec.rb +++ b/spec/unit/util/adsi_spec.rb @@ -73,12 +73,10 @@ describe Puppet::Util::ADSI do let(:domain_username) { "#{domain}\\#{username}"} it "should generate the correct URI" do - Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil) Puppet::Util::ADSI::User.uri(username).should == "WinNT://./#{username},user" end it "should generate the correct URI for a user with a domain" do - Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil) Puppet::Util::ADSI::User.uri(username, domain).should == "WinNT://#{domain}/#{username},user" end @@ -109,26 +107,15 @@ describe Puppet::Util::ADSI do end it "should be able to check the existence of a user" do - Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil) Puppet::Util::ADSI.expects(:connect).with("WinNT://./#{username},user").returns connection Puppet::Util::ADSI::User.exists?(username).should be_true end it "should be able to check the existence of a domain user" do - Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil) Puppet::Util::ADSI.expects(:connect).with("WinNT://#{domain}/#{username},user").returns connection Puppet::Util::ADSI::User.exists?(domain_username).should be_true end - it "should be able to confirm the existence of a user with a well-known SID", - :if => Puppet.features.microsoft_windows? do - - system_user = Win32::Security::SID::LocalSystem - # ensure that the underlying OS is queried here - Puppet::Util::ADSI.unstub(:connect) - Puppet::Util::ADSI::User.exists?(system_user).should be_true - end - it "should be able to delete a user" do connection.expects(:Delete).with('user', username) @@ -136,8 +123,6 @@ describe Puppet::Util::ADSI do end it "should return an enumeration of IADsUser wrapped objects" do - Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil) - name = 'Administrator' wmi_users = [stub('WMI', :name => name)] Puppet::Util::ADSI.expects(:execquery).with('select name from win32_useraccount where localaccount = "TRUE"').returns(wmi_users) @@ -189,7 +174,7 @@ describe Puppet::Util::ADSI do user.password = 'pwd' end - it "should generate the correct URI", :if => Puppet.features.microsoft_windows? do + it "should generate the correct URI",:if => Puppet.features.microsoft_windows? do Puppet::Util::Windows::Security.stubs(:octet_string_to_sid_object).returns(sid) user.uri.should == "WinNT://testcomputername/#{username},user" end @@ -336,13 +321,11 @@ describe Puppet::Util::ADSI do end it "should generate the correct URI" do - Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil) group.uri.should == "WinNT://./#{groupname},group" end end it "should generate the correct URI" do - Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil) Puppet::Util::ADSI::Group.uri("people").should == "WinNT://./people,group" end @@ -359,21 +342,11 @@ describe Puppet::Util::ADSI do end it "should be able to confirm the existence of a group" do - Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil) Puppet::Util::ADSI.expects(:connect).with("WinNT://./#{groupname},group").returns connection Puppet::Util::ADSI::Group.exists?(groupname).should be_true end - it "should be able to confirm the existence of a group with a well-known SID", - :if => Puppet.features.microsoft_windows? do - - service_group = Win32::Security::SID::Service - # ensure that the underlying OS is queried here - Puppet::Util::ADSI.unstub(:connect) - Puppet::Util::ADSI::Group.exists?(service_group).should be_true - end - it "should be able to delete a group" do connection.expects(:Delete).with('group', groupname) @@ -381,8 +354,6 @@ describe Puppet::Util::ADSI do end it "should return an enumeration of IADsGroup wrapped objects" do - Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil) - name = 'Administrators' wmi_groups = [stub('WMI', :name => name)] Puppet::Util::ADSI.expects(:execquery).with('select name from win32_group where localaccount = "TRUE"').returns(wmi_groups) diff --git a/spec/unit/util/windows/sid_spec.rb b/spec/unit/util/windows/sid_spec.rb index a05aee6aa..770512188 100755 --- a/spec/unit/util/windows/sid_spec.rb +++ b/spec/unit/util/windows/sid_spec.rb @@ -11,7 +11,6 @@ describe "Puppet::Util::Windows::SID", :if => Puppet.features.microsoft_windows? let(:subject) { SIDTester.new } let(:sid) { Win32::Security::SID::LocalSystem } - let(:service_sid) { Win32::Security::SID::Service } let(:invalid_sid) { 'bogus' } let(:unknown_sid) { 'S-0-0-0' } let(:unknown_name) { 'chewbacca' } @@ -79,11 +78,6 @@ describe "Puppet::Util::Windows::SID", :if => Puppet.features.microsoft_windows? it "should be the identity function for any sid" do subject.name_to_sid(sid).should == sid end - - it "should map Puppet 'service' user to service group" do - # this test is only kept around to ensure the mapping never breaks - subject.name_to_sid('service').should == service_sid - end end context "#name_to_sid_object" do From 9c888b66bf812c8501d28fdee9f1750942f04461 Mon Sep 17 00:00:00 2001 From: Matthew Nicholas Bradley Date: Mon, 20 Jan 2014 02:07:53 +0000 Subject: [PATCH 464/800] Correct all misspelled instances of the word occurred --- lib/puppet/indirector/queue.rb | 2 +- spec/unit/indirector/queue_spec.rb | 2 +- spec/unit/transaction/additional_resource_generator_spec.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/puppet/indirector/queue.rb b/lib/puppet/indirector/queue.rb index 8b9d8ab61..337dc1cf1 100644 --- a/lib/puppet/indirector/queue.rb +++ b/lib/puppet/indirector/queue.rb @@ -72,7 +72,7 @@ class Puppet::Indirector::Queue < Puppet::Indirector::Terminus begin yield(self.intern(msg)) rescue => detail - Puppet.log_exception(detail, "Error occured with subscription to queue #{queue} for indirection #{indirection_name}: #{detail}") + Puppet.log_exception(detail, "Error occurred with subscription to queue #{queue} for indirection #{indirection_name}: #{detail}") end end end diff --git a/spec/unit/indirector/queue_spec.rb b/spec/unit/indirector/queue_spec.rb index 3711b1846..b14b4cbb3 100755 --- a/spec/unit/indirector/queue_spec.rb +++ b/spec/unit/indirector/queue_spec.rb @@ -107,7 +107,7 @@ describe Puppet::Indirector::Queue do expect { @store_class.subscribe {|o| o } }.to_not raise_error @logs.length.should == 1 - @logs.first.message.should =~ /Error occured with subscription to queue my_queue for indirection my_queue: ArgumentError/ + @logs.first.message.should =~ /Error occurred with subscription to queue my_queue for indirection my_queue: ArgumentError/ @logs.first.level.should == :err end end diff --git a/spec/unit/transaction/additional_resource_generator_spec.rb b/spec/unit/transaction/additional_resource_generator_spec.rb index d6f99ede3..6983ba354 100644 --- a/spec/unit/transaction/additional_resource_generator_spec.rb +++ b/spec/unit/transaction/additional_resource_generator_spec.rb @@ -139,7 +139,7 @@ describe Puppet::Transaction::AdditionalResourceGenerator do expect(catalog).to contain_resources_equally('Generator[thing]', 'Notify[hello]') end - it "should return false if an error occured when generating resources" do + it "should return false if an error occurred when generating resources" do catalog = compile_to_ral(<<-MANIFEST) generator { thing: code => 'fail("not a good generation")' From f6055775f31d65ee4f485a9c1ea5ca6c5fee3caa Mon Sep 17 00:00:00 2001 From: glennsarti Date: Mon, 16 Dec 2013 23:15:14 +0800 Subject: [PATCH 465/800] (PUP-1048) Windows puppet service should log to the eventlog Initial attempt to add in eventlogging for all log messages New service command line argument (--logtofile). By default the service will log all messages to the Eventlog. This argument will log messages both to the Eventlog and the Puppet log file in Common Application Data. This can be useful if the EventLog logging is failing or another application depends on the the logfile Basic tests show the new code works however more testing should be done --- ext/windows/service/daemon.rb | 57 +++++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/ext/windows/service/daemon.rb b/ext/windows/service/daemon.rb index 8aa6a3ca4..157d12b5f 100755 --- a/ext/windows/service/daemon.rb +++ b/ext/windows/service/daemon.rb @@ -4,6 +4,7 @@ require 'fileutils' require 'win32/daemon' require 'win32/dir' require 'win32/process' +require 'win32/eventlog' require 'windows/synchronize' require 'windows/handle' @@ -13,6 +14,7 @@ class WindowsDaemon < Win32::Daemon include Windows::Handle include Windows::Process + @LOG_TO_FILE = false LOG_FILE = File.expand_path(File.join(Dir::COMMON_APPDATA, 'PuppetLabs', 'puppet', 'var', 'log', 'windows.log')) LEVELS = [:debug, :info, :notice, :err] LEVELS.each do |level| @@ -22,7 +24,6 @@ class WindowsDaemon < Win32::Daemon end def service_init - FileUtils.mkdir_p(File.dirname(LOG_FILE)) end def service_main(*argsv) @@ -30,6 +31,28 @@ class WindowsDaemon < Win32::Daemon args = argsv.join(' ') @loglevel = LEVELS.index(argsv.index('--debug') ? :debug : :notice) + @LOG_TO_FILE = (argv.index('--logtofile') ? true : false) + + if (@LOG_TO_FILE) + FileUtils.mkdir_p(File.dirname(LOG_FILE)) + end + basedir = File.expand_path(File.join(File.dirname(__FILE__), '..')) + + # The puppet installer registers a 'Puppet' event source. For the moment events will be logged with this key, but + # it may be a good idea to split the Service and Puppet evnts later so it's easier to read in the windows Event Log. + # + # Example code to register an event source; + # eventlogdll = File.expand_path(File.join(basedir, 'puppet', 'ext', 'windows', 'eventlog', 'puppetres.dll')) + # if (File.exists?(eventlogdll)) + # Win32::EventLog.add_event_source( + # 'source' => "Application", + # 'key_name' => "Puppet Agent", + # 'category_count' => 3, + # 'event_message_file' => eventlogdll, + # 'category_message_file' => eventlogdll + # ) + # end + log_notice("Starting service: #{args}") while running? do @@ -37,7 +60,6 @@ class WindowsDaemon < Win32::Daemon log_notice('Service running') - basedir = File.expand_path(File.join(File.dirname(__FILE__), '..')) puppet = File.join(basedir, 'bin', 'puppet.bat') unless File.exists?(puppet) log_err("File not found: '#{puppet}'") @@ -81,7 +103,36 @@ class WindowsDaemon < Win32::Daemon def log(msg, level) if LEVELS.index(level) >= @loglevel - File.open(LOG_FILE, 'a') { |f| f.puts("#{Time.now} Puppet (#{level}): #{msg}") } + if (@LOG_TO_FILE) + File.open(LOG_FILE, 'a') { |f| f.puts("#{Time.now} Puppet (#{level}): #{msg}") } + end + + case level + when :debug + raise_windows_event(Win32::EventLog::INFO,0x01,msg.to_s) + when :info + raise_windows_event(Win32::EventLog::INFO,0x01,msg.to_s) + when :notice + raise_windows_event(Win32::EventLog::INFO,0x01,msg.to_s) + when :err + raise_windows_event(Win32::EventLog::ERR,0x03,msg.to_s) + else + raise_windows_event(Win32::EventLog::WARN,0x02,msg.to_s) + end + end + end + + def raise_windows_event(type,id,message) + begin + eventlog = Win32::EventLog.open("Application") + eventlog.report_event( + :source => "Puppet Agent", + :event_type => type, # Win32::EventLog::INFO or WARN, ERROR + :event_id => id, # 0x01 or 0x02, 0x03 etc. + :data => message # "the message" + ) + rescue Exception => e + # Ignore all errors end end end From 0ce0c641dc6ddab50b837c917170747b1139d5a0 Mon Sep 17 00:00:00 2001 From: glennsarti Date: Mon, 16 Dec 2013 23:28:29 +0800 Subject: [PATCH 466/800] (PUP-1048) Windows puppet service should log to the eventlog Cleaned up some whitespace issues Changed the wording on some of the log entries to avoid confusion between 'Service resuming' and a Service Resume control message Added logging for the Pause, Resume and Shutdown service control messages. Previously there was none Noted Bug id 11 in the win32-service ruby code Added a commented out handler for the Interrogation event for debuggin purposes Changed the name of the event log function from raise_windows_event to report_windows_event to avoid connotations with raise Changed the event source to Puppet so that the correct event source DLL is used. However it may be a good idea to split the event source between the service and the actual agent so it's easier to read in the Eventlog. This would require a change to the puppet_for_the_win repo as well. The eventlog.close command is within an ensure block to stop leaked handles if an error occurs The --logtofile argument was working for the service but was causing the puppet agent run to fail as it didn't know what to do with it. Used a simple text substitution to remove it from the args string. Spelling mistake Rebasing broke detecting of the --logtofile argument. Changed the variable used Changed wording of the log message when putting the service into a paused state. Now references a Puppet Issue ticket for tracking The ensure block will make sure that the eventlog handle is not nil before trying to close the handle --- ext/windows/service/daemon.rb | 48 +++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/ext/windows/service/daemon.rb b/ext/windows/service/daemon.rb index 157d12b5f..50431f4f2 100755 --- a/ext/windows/service/daemon.rb +++ b/ext/windows/service/daemon.rb @@ -31,15 +31,16 @@ class WindowsDaemon < Win32::Daemon args = argsv.join(' ') @loglevel = LEVELS.index(argsv.index('--debug') ? :debug : :notice) - @LOG_TO_FILE = (argv.index('--logtofile') ? true : false) + @LOG_TO_FILE = (argsv.index('--logtofile') ? true : false) if (@LOG_TO_FILE) FileUtils.mkdir_p(File.dirname(LOG_FILE)) + args = args.gsub("--logtofile","") end basedir = File.expand_path(File.join(File.dirname(__FILE__), '..')) # The puppet installer registers a 'Puppet' event source. For the moment events will be logged with this key, but - # it may be a good idea to split the Service and Puppet evnts later so it's easier to read in the windows Event Log. + # it may be a good idea to split the Service and Puppet events later so it's easier to read in the windows Event Log. # # Example code to register an event source; # eventlogdll = File.expand_path(File.join(basedir, 'puppet', 'ext', 'windows', 'eventlog', 'puppetres.dll')) @@ -53,7 +54,7 @@ class WindowsDaemon < Win32::Daemon # ) # end - log_notice("Starting service: #{args}") + log_notice("Starting service with arguments: #{args}") while running? do return if state != RUNNING @@ -83,7 +84,7 @@ class WindowsDaemon < Win32::Daemon log_debug("Service waiting for #{runinterval} seconds") sleep(runinterval) - log_debug('Service resuming') + log_debug('Service woken up') end log_notice('Service stopped') @@ -96,6 +97,26 @@ class WindowsDaemon < Win32::Daemon Thread.main.wakeup end + def service_pause + # The service will not stay in a paused stated, instead it will go back into a running state after a short period of time. This is an issue in the Win32-Service ruby code + # Raised bug https://github.com/djberg96/win32-service/issues/11 and is fixed in version 0.8.3. + # Because the Pause feature is so rarely used, there is no point in creating a workaround until puppet uses 0.8.3. + log_notice('Service pausing. The service will not stay paused. See Puppet Issue PUP-1471 for more information') + end + + def service_resume + log_notice('Service resuming') + end + + def service_shutdown + log_notice('Host shutting down') + end + + # Interrogation handler is just for debug. Can be commented out or removed entirely. + # def service_interrogate + # log_debug('Service is being interrogated') + # end + def log_exception(e) log_err(e.message) log_err(e.backtrace.join("\n")) @@ -109,30 +130,35 @@ class WindowsDaemon < Win32::Daemon case level when :debug - raise_windows_event(Win32::EventLog::INFO,0x01,msg.to_s) + report_windows_event(Win32::EventLog::INFO,0x01,msg.to_s) when :info - raise_windows_event(Win32::EventLog::INFO,0x01,msg.to_s) + report_windows_event(Win32::EventLog::INFO,0x01,msg.to_s) when :notice - raise_windows_event(Win32::EventLog::INFO,0x01,msg.to_s) + report_windows_event(Win32::EventLog::INFO,0x01,msg.to_s) when :err - raise_windows_event(Win32::EventLog::ERR,0x03,msg.to_s) + report_windows_event(Win32::EventLog::ERR,0x03,msg.to_s) else - raise_windows_event(Win32::EventLog::WARN,0x02,msg.to_s) + report_windows_event(Win32::EventLog::WARN,0x02,msg.to_s) end end end - def raise_windows_event(type,id,message) + def report_windows_event(type,id,message) begin + eventlog = nil eventlog = Win32::EventLog.open("Application") eventlog.report_event( - :source => "Puppet Agent", + :source => "Puppet", :event_type => type, # Win32::EventLog::INFO or WARN, ERROR :event_id => id, # 0x01 or 0x02, 0x03 etc. :data => message # "the message" ) rescue Exception => e # Ignore all errors + ensure + if (!eventlog.nil?) + eventlog.close + end end end end From c59380409153c65269692b4d110b23132f3f5f55 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Tue, 21 Jan 2014 12:54:19 -0800 Subject: [PATCH 467/800] (maint) Reset Puppet::Parser::Functions early in TestHelper.initialize External tests which rely on being able to simply require 'puppet' and then load a function for test relied on Puppet::Parser::Functions.reset being called during the class loading. Now that that has moved into Puppet initialization, we simulate this in TestHelper by calling it before specs are loaded. --- lib/puppet/test/test_helper.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/puppet/test/test_helper.rb b/lib/puppet/test/test_helper.rb index 252b1e2ae..81933e169 100644 --- a/lib/puppet/test/test_helper.rb +++ b/lib/puppet/test/test_helper.rb @@ -34,6 +34,7 @@ module Puppet::Test # @return nil def self.initialize() initialize_settings_before_each + Puppet::Parser::Functions.reset end # Call this method once, when beginning a test run--prior to running From 652f3b2cc1d45b8dbeda292bc4ddad06350aa8bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Tomulik?= Date: Tue, 21 Jan 2014 22:38:25 +0100 Subject: [PATCH 468/800] (pup-1369) Add more specs for package/package_settings --- .../type/package/package_settings_spec.rb | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100755 spec/unit/type/package/package_settings_spec.rb diff --git a/spec/unit/type/package/package_settings_spec.rb b/spec/unit/type/package/package_settings_spec.rb new file mode 100755 index 000000000..18e7a4b7e --- /dev/null +++ b/spec/unit/type/package/package_settings_spec.rb @@ -0,0 +1,135 @@ +#! /usr/bin/env ruby +require 'spec_helper' + +describe Puppet::Type.type(:package) do + + before do + Puppet::Util::Storage.stubs(:store) + end + + it "should have a :package_settings feature that requires :package_settings_insync?, :package_settings and :package_settings=" do + described_class.provider_feature(:package_settings).methods.should == [:package_settings_insync?, :package_settings, :package_settings=] + end + + context "when validating attributes" do + it "should have a package_settings property" do + described_class.attrtype(:package_settings).should == :property + end + end + + context "when validating attribute values" do + let(:provider) do + stub( 'provider', + :class => described_class.defaultprovider, + :clear => nil, + :validate_source => false ) + end + before do + provider.class.stubs(:supports_parameter?).returns(true) + described_class.defaultprovider.stubs(:new).returns(provider) + end + describe 'package_settings' do + context "with a minimalistic provider supporting package_settings" do + context "and {:package_settings => :settings}" do + let(:resource) do + described_class.new :name => 'foo', :package_settings => :settings + end + it { expect { resource }.to_not raise_error } + it "should set package_settings to :settings" do + resource.value(:package_settings).should be :settings + end + end + end + context "with a provider that supports validation of the package_settings" do + context "and {:package_settings => :valid_value}" do + before do + provider.expects(:package_settings_validate).once.with(:valid_value).returns(true) + end + let(:resource) do + described_class.new :name => 'foo', :package_settings => :valid_value + end + it { expect { resource }.to_not raise_error } + it "should set package_settings to :valid_value" do + resource.value(:package_settings).should == :valid_value + end + end + context "and {:package_settings => :invalid_value}" do + before do + msg = "package_settings must be a Hash, not Symbol" + provider.expects(:package_settings_validate).once. + with(:invalid_value).raises(ArgumentError, msg) + end + let(:resource) do + described_class.new :name => 'foo', :package_settings => :invalid_value + end + it do + expect { resource }.to raise_error Puppet::Error, + /package_settings must be a Hash, not Symbol/ + end + end + end + context "with a provider that supports munging of the package_settings" do + context "and {:package_settings => 'A'}" do + before do + provider.expects(:package_settings_munge).once.with('A').returns(:a) + end + let(:resource) do + described_class.new :name => 'foo', :package_settings => 'A' + end + it do + expect { resource }.to_not raise_error + end + it "should set package_settings to :a" do + resource.value(:package_settings).should be :a + end + end + end + end + end + describe "package_settings property" do + let(:provider) do + stub( 'provider', + :class => described_class.defaultprovider, + :clear => nil, + :validate_source => false ) + end + before do + provider.class.stubs(:supports_parameter?).returns(true) + described_class.defaultprovider.stubs(:new).returns(provider) + end + context "with {package_settings => :should}" do + let(:resource) do + described_class.new :name => 'foo', :package_settings => :should + end + describe "#insync?(:is)" do + it "returns the result of provider.package_settings_insync?(:should,:is)" do + resource.provider.expects(:package_settings_insync?).once.with(:should,:is).returns :ok1 + resource.property(:package_settings).insync?(:is).should be :ok1 + end + end + describe "#should_to_s(:newvalue)" do + it "returns the result of provider.package_settings_should_to_s(:should,:newvalue)" do + resource.provider.expects(:package_settings_should_to_s).once.with(:should,:newvalue).returns :ok2 + resource.property(:package_settings).should_to_s(:newvalue).should be :ok2 + end + end + describe "#is_to_s(:currentvalue)" do + it "returns the result of provider.package_settings_is_to_s(:should,:currentvalue)" do + resource.provider.expects(:package_settings_is_to_s).once.with(:should,:currentvalue).returns :ok3 + resource.property(:package_settings).is_to_s(:currentvalue).should be :ok3 + end + end + end + context "with any non-nil package_settings" do + describe "#change_to_s(:currentvalue,:newvalue)" do + let(:resource) do + described_class.new :name => 'foo', :package_settings => {} + end + it "returns the result of provider.package_settings_change_to_s(:currentvalue,:newvalue)" do + resource.provider.expects(:package_settings_change_to_s).once.with(:currentvalue,:newvalue).returns :ok4 + resource.property(:package_settings).change_to_s(:currentvalue,:newvalue).should be :ok4 + end + end + end + end +end From 4ef4e50ff4bbeca3335673d1101fb6560429099b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Tomulik?= Date: Tue, 21 Jan 2014 22:38:35 +0100 Subject: [PATCH 469/800] (pup-1369) Fixed a bug in package/package_settings Ruby versions >= 1.9.3 (at least) raised the following exceptions when validating the package_settings: implicit argument passing of super from method defined by define_method() is not supported. Specify all arguments explicitly. This commit fixes the bug. --- lib/puppet/type/package.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/puppet/type/package.rb b/lib/puppet/type/package.rb index dbdfbacab..c9951b62f 100644 --- a/lib/puppet/type/package.rb +++ b/lib/puppet/type/package.rb @@ -268,7 +268,7 @@ module Puppet if provider.respond_to?(:package_settings_validate) provider.package_settings_validate(value) else - super + super(value) end end @@ -276,7 +276,7 @@ module Puppet if provider.respond_to?(:package_settings_munge) provider.package_settings_munge(value) else - super + super(value) end end @@ -288,7 +288,7 @@ module Puppet if provider.respond_to?(:package_settings_should_to_s) provider.package_settings_should_to_s(should, newvalue) else - super + super(newvalue) end end @@ -296,7 +296,7 @@ module Puppet if provider.respond_to?(:package_settings_is_to_s) provider.package_settings_is_to_s(should, currentvalue) else - super + super(currentvalue) end end @@ -304,7 +304,7 @@ module Puppet if provider.respond_to?(:package_settings_change_to_s) provider.package_settings_change_to_s(currentvalue, newvalue) else - super + super(currentvalue,newvalue) end end end From 4a89dcf27ed1e78274dfe63130e7dbf48dd7fb90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Dal=C3=A9n?= Date: Wed, 22 Jan 2014 16:34:42 +0100 Subject: [PATCH 470/800] (PUP-1490) Support --test option for puppet apply This allows more parity between puppet agent and puppet apply and also makes it easier to enable a bunch of testing options for apply. --- lib/puppet/application/apply.rb | 14 +++++++++++++ spec/unit/application/apply_spec.rb | 31 +++++++++++++++++++++++++---- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/lib/puppet/application/apply.rb b/lib/puppet/application/apply.rb index e8154b6ca..1d2535f6c 100644 --- a/lib/puppet/application/apply.rb +++ b/lib/puppet/application/apply.rb @@ -8,6 +8,7 @@ class Puppet::Application::Apply < Puppet::Application options[:code] = arg end option("--loadclasses","-L") + option("--test","-t") option("--verbose","-v") option("--use-nodes") option("--detailed-exitcodes") @@ -103,6 +104,10 @@ configuration options can also be generated by running puppet with * --execute: Execute a specific piece of Puppet code +* --test: + Enable the most common options used for testing. These are 'verbose', + 'detailed-exitcodes' and 'show_diff'. + * --verbose: Print extra information. @@ -230,7 +235,16 @@ Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License end end + # Enable all of the most common test options. + def setup_test + Puppet.settings.handlearg("--show_diff") + options[:verbose] = true + options[:detailed_exitcodes] = true + end + def setup + setup_test if options[:test] + exit(Puppet.settings.print_configs ? 0 : 1) if Puppet.settings.print_configs? Puppet::Util::Log.newdestination(:console) unless options[:setdest] diff --git a/spec/unit/application/apply_spec.rb b/spec/unit/application/apply_spec.rb index d3cc54884..69da405fc 100755 --- a/spec/unit/application/apply_spec.rb +++ b/spec/unit/application/apply_spec.rb @@ -21,7 +21,7 @@ describe Puppet::Application::Apply do Puppet::Node.indirection.cache_class = nil end - [:debug,:loadclasses,:verbose,:use_nodes,:detailed_exitcodes,:catalog, :write_catalog_summary].each do |option| + [:debug,:loadclasses,:test,:verbose,:use_nodes,:detailed_exitcodes,:catalog, :write_catalog_summary].each do |option| it "should declare handle_#{option} method" do @apply.should respond_to("handle_#{option}".to_sym) end @@ -58,8 +58,31 @@ describe Puppet::Application::Apply do Puppet::FileBucket::Dipper.stubs(:new) STDIN.stubs(:read) Puppet::Transaction::Report.indirection.stubs(:cache_class=) + end - @apply.options.stubs(:[]).with(any_parameters) + describe "with --test" do + it "should call setup_test" do + @apply.options[:test] = true + @apply.expects(:setup_test) + + @apply.setup + end + + it "should set options[:verbose] to true" do + @apply.setup_test + + @apply.options[:verbose].should == true + end + it "should set options[:show_diff] to true" do + Puppet[:show_diff] = false + @apply.setup_test + Puppet[:show_diff].should == true + end + it "should set options[:detailed_exitcodes] to true" do + @apply.setup_test + + @apply.options[:detailed_exitcodes].should == true + end end it "should set console as the log destination if logdest option wasn't provided" do @@ -75,13 +98,13 @@ describe Puppet::Application::Apply do end it "should set log level to debug if --debug was passed" do - @apply.options.stubs(:[]).with(:debug).returns(true) + @apply.options[:debug] = true @apply.setup Puppet::Log.level.should == :debug end it "should set log level to info if --verbose was passed" do - @apply.options.stubs(:[]).with(:verbose).returns(true) + @apply.options[:verbose] = true @apply.setup Puppet::Log.level.should == :info end From f7f4550a5189ad2c6fb756188f7b0c15f77d600e Mon Sep 17 00:00:00 2001 From: Rob Braden Date: Tue, 21 Jan 2014 16:31:26 -0800 Subject: [PATCH 471/800] (pup-1491)(packaging) Remove Fedora 18 as a default build target Fedora 18 reached end-of-life on January 14, 2014. This removes F18 as a default build target from ext/build_defaults.yaml --- ext/build_defaults.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/build_defaults.yaml b/ext/build_defaults.yaml index 1d117b3d4..1d256182f 100644 --- a/ext/build_defaults.yaml +++ b/ext/build_defaults.yaml @@ -9,7 +9,7 @@ gpg_name: 'info@puppetlabs.com' gpg_key: '4BD6EC30' sign_tar: FALSE # a space separated list of mock configs -final_mocks: 'pl-el-5-i386 pl-el-6-i386 pl-fedora-18-i386 pl-fedora-19-i386 pl-fedora-20-i386' +final_mocks: 'pl-el-5-i386 pl-el-6-i386 pl-fedora-19-i386 pl-fedora-20-i386' yum_host: 'yum.puppetlabs.com' yum_repo_path: '/opt/repository/yum/' build_gem: TRUE From 0bb13a1e082b762fd1cc0ba22fd53dff0dda1036 Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Wed, 22 Jan 2014 11:54:53 -0800 Subject: [PATCH 472/800] (PUP-1492) Fix pacman specs so that they pass when yaourt is installed. Stub out the location of yaourt. Fix spec that assumes pacman is executed when yaourt is installed. --- spec/unit/provider/package/pacman_spec.rb | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/spec/unit/provider/package/pacman_spec.rb b/spec/unit/provider/package/pacman_spec.rb index 21de2af7e..07d114355 100755 --- a/spec/unit/provider/package/pacman_spec.rb +++ b/spec/unit/provider/package/pacman_spec.rb @@ -12,6 +12,8 @@ describe provider do before do resolver.stubs(:which).with('/usr/bin/pacman').returns('/usr/bin/pacman') provider.stubs(:which).with('/usr/bin/pacman').returns('/usr/bin/pacman') + resolver.stubs(:which).with('/usr/bin/yaourt').returns('/usr/bin/yaourt') + provider.stubs(:which).with('/usr/bin/yaourt').returns('/usr/bin/yaourt') @resource = Puppet::Type.type(:package).new(:name => 'package') @provider = provider.new(@resource) end @@ -24,11 +26,18 @@ describe provider do end it "should call pacman to install the right package quietly" do + + if @provider.yaourt? + args = ['/usr/bin/yaourt', '--noconfirm', '-S', @resource[:name]] + else + args = ['/usr/bin/pacman', '--noconfirm', '--noprogressbar', '-Sy', @resource[:name]] + end + executor. expects(:execute). at_least_once. - with(["/usr/bin/pacman", "--noconfirm", "--noprogressbar", "-Sy", @resource[:name]], no_extra_options). - returns "" + with(args, no_extra_options). + returns '' @provider.install end From ce904cf8e1590e9e9940f6509e8ab3000f8e7d53 Mon Sep 17 00:00:00 2001 From: "Ethan J. Brown" Date: Wed, 22 Jan 2014 15:55:07 -0800 Subject: [PATCH 473/800] PUP-1494 - Remove Windows iconv from colors.rb - We no longer support / ship Ruby 1.8 on Windows, so iconv support is not necessary --- lib/puppet/util/colors.rb | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/lib/puppet/util/colors.rb b/lib/puppet/util/colors.rb index a10e240a1..f55dc9fcb 100644 --- a/lib/puppet/util/colors.rb +++ b/lib/puppet/util/colors.rb @@ -112,17 +112,9 @@ module Puppet::Util::Colors WriteConsole.call(@handle, utf16, nChars, written, reserved) end - if String.method_defined?("encode") - def string_encode(str) - wstr = str.encode('UTF-16LE') - [wstr, wstr.length] - end - else - require 'iconv' - def string_encode(str) - wstr = Iconv.conv('UTF-16LE', 'UTF-8', str) - [wstr, wstr.length/2] - end + def string_encode(str) + wstr = str.encode('UTF-16LE') + [wstr, wstr.length] end end From ffecc4e93e660caeb552103e72e885828f9b9e75 Mon Sep 17 00:00:00 2001 From: Iristyle Date: Thu, 23 Jan 2014 10:57:17 -0800 Subject: [PATCH 474/800] (maint) Fix major performance regression in Puppet specs - This one seemingly innocuous change resulted in an increase of spec run time from 20 minutes to 2 hours on Win2003R2 and a series of type conversion errors in specs on Win2008R2 that it has been theorized are a result of memory pressure exerted by the change. This has also caused slowdowns across the boards in other specs, and more diagnostic work is being performed to determine the root cause. - For now, this change is being merged as it clears out a number of CI issues that we're experiencing. --- lib/puppet/test/test_helper.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/puppet/test/test_helper.rb b/lib/puppet/test/test_helper.rb index 81933e169..44cfe42f6 100644 --- a/lib/puppet/test/test_helper.rb +++ b/lib/puppet/test/test_helper.rb @@ -43,7 +43,6 @@ module Puppet::Test def self.before_all_tests() # Make sure that all of the setup is also done for any before(:all) blocks Puppet.push_context(Puppet.initial_context, "Initial for specs") - self.before_each_test() end # Call this method once, at the end of a test run, when no more tests From fe4bcba3ff3dd2c1dcece6bcb9fb580bd6e04501 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Tue, 21 Jan 2014 12:22:38 -0600 Subject: [PATCH 475/800] (PUP-1282) Allow patch version float on windows dependencies This loosens the version dependency of the windows specific gems a bit to allow for floating on the patch part of the version number. This allows better compatibility as future updates are made to dependency versions. --- ext/project_data.yaml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/ext/project_data.yaml b/ext/project_data.yaml index cb4106ec7..fb08d1ec9 100644 --- a/ext/project_data.yaml +++ b/ext/project_data.yaml @@ -27,18 +27,19 @@ gem_rdoc_options: gem_platform_dependencies: x86-mingw32: gem_runtime_dependencies: + # Pinning versions that require native extensions ffi: '1.9.0' sys-admin: '1.5.6' win32-api: '1.4.8' - win32-dir: '0.4.3' - win32-eventlog: '0.5.3' - win32-process: '0.6.5' - win32-security: '0.1.4' + win32-dir: '~> 0.4.3' + win32-eventlog: '~> 0.5.3' + win32-process: '~> 0.6.5' + win32-security: '~> 0.1.4' win32-service: '0.7.2' - win32-taskscheduler: '0.2.2' + win32-taskscheduler: '~> 0.2.2' win32console: '1.3.2' - windows-api: '0.4.2' - windows-pr: '1.2.2' - minitar: '0.5.4' + windows-api: '~> 0.4.2' + windows-pr: '~> 1.2.2' + minitar: '~> 0.5.4' bundle_platforms: x86-mingw32: mingw From 446f39e73a66bdaaab79681fdea32a5e704d03b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Dal=C3=A9n?= Date: Tue, 21 Jan 2014 13:39:21 +0100 Subject: [PATCH 476/800] (PUP-1484) Fix serialization of TagSet The TagSet class didn't include FormatSupport and didn't have a to_data_hash method, so it couldn't be serialized to msgpack. --- lib/puppet/util/tag_set.rb | 11 +++++++++-- spec/unit/network/formats_spec.rb | 23 +++++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/lib/puppet/util/tag_set.rb b/lib/puppet/util/tag_set.rb index bd1029040..d9c9d9695 100644 --- a/lib/puppet/util/tag_set.rb +++ b/lib/puppet/util/tag_set.rb @@ -1,6 +1,9 @@ require 'set' +require 'puppet/network/format_support' class Puppet::Util::TagSet < Set + include Puppet::Network::FormatSupport + def self.from_yaml(yaml) self.new(YAML.load(yaml)) end @@ -13,14 +16,18 @@ class Puppet::Util::TagSet < Set self.new(data) end + def to_data_hash + to_a + end + def to_pson(*args) - to_a.to_pson + to_data_hash.to_pson end # this makes puppet serialize it as an array for backwards # compatibility def to_zaml(z) - to_a.to_zaml(z) + to_data_hash.to_zaml(z) end def join(*args) diff --git a/spec/unit/network/formats_spec.rb b/spec/unit/network/formats_spec.rb index 57ff77795..a64f96e1e 100755 --- a/spec/unit/network/formats_spec.rb +++ b/spec/unit/network/formats_spec.rb @@ -48,6 +48,29 @@ describe "Puppet Network Format" do @msgpack.intern_multiple(Hash, MessagePack.pack(["foo"])) end.to raise_error(NoMethodError) end + + it "should be able to serialize a catalog" do + cat = Puppet::Resource::Catalog.new('foo') + cat.add_resource(Puppet::Resource.new(:file, 'my_file')) + catunpack = MessagePack.unpack(cat.to_msgpack) + catunpack.should include( + "tags"=>[], + "name"=>"foo", + "version"=>nil, + "environment"=>"", + "edges"=>[], + "classes"=>[] + ) + catunpack["resources"][0].should include( + "type"=>"File", + "title"=>"my_file", + "exported"=>false + ) + catunpack["resources"][0]["tags"].should include( + "file", + "my_file" + ) + end end it "should include a yaml format" do From 76c064f2f3f6f7ebb660a03861e4156288af828f Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 17 Jan 2014 13:41:19 -0800 Subject: [PATCH 477/800] (PUP-1118) Add check for getting multiple environments The acceptance test now checks for getting multiple environments listed from the environmentsdir --- .../tests/environment/can_enumerate_environments.rb | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/acceptance/tests/environment/can_enumerate_environments.rb b/acceptance/tests/environment/can_enumerate_environments.rb index 0b30bc3a0..3a8016673 100644 --- a/acceptance/tests/environment/can_enumerate_environments.rb +++ b/acceptance/tests/environment/can_enumerate_environments.rb @@ -25,7 +25,15 @@ def curl_master_from(agent, path, headers = '', &block) on agent, "#{curl_base} '#{url}'", &block end -with_puppet_running_on(master, {}) do +environments_dir = master.tmpdir("environments") +on master, "mkdir -p #{environments_dir}/env1" +on master, "mkdir -p #{environments_dir}/env2" + +with_puppet_running_on(master, { + :master => { + "environmentsdir" => environments_dir + } +}) do agents.each do |agent| step "Ensure that an unauthenticated client cannot access the environments list" do on agent, "curl -ksv https://#{master}:#{master_port(agent)}/v2.0/environments", :acceptable_exit_codes => [0,7] do @@ -36,7 +44,7 @@ with_puppet_running_on(master, {}) do step "Ensure that an authenticated client can retrieve the list of environments" do curl_master_from(agent, '/v2.0/environments') do data = JSON.parse(stdout) - assert_equal(["production"], data["environments"].keys) + assert_equal(["env1", "env2"], data["environments"].keys.sort) end end end From ecb317b111fa2fd49d3c2b61db290913b14f0a80 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Tue, 21 Jan 2014 14:13:43 -0800 Subject: [PATCH 478/800] (PUP-1118) List environments from a directory This starts the process of creating a Puppet::Environments::Directory that will find environments from a particular directory. The directory has sub-directories, where each sub-directory name is the environment name. --- lib/puppet/environments.rb | 30 +++++++++++++++++++ lib/puppet/file_system.rb | 18 +++++++++++ lib/puppet/file_system/file_impl.rb | 10 ++++++- lib/puppet/file_system/memory_file.rb | 30 +++++++++++++------ lib/puppet/file_system/memory_impl.rb | 24 ++++++++++++--- lib/puppet/network/http/api/v1.rb | 2 +- lib/puppet/node/environment.rb | 17 ++++------- spec/lib/matchers/include.rb | 27 +++++++++++++++++ .../file_spec.rb => file_system_spec.rb} | 0 9 files changed, 131 insertions(+), 27 deletions(-) create mode 100644 spec/lib/matchers/include.rb rename spec/unit/{file_system/file_spec.rb => file_system_spec.rb} (100%) diff --git a/lib/puppet/environments.rb b/lib/puppet/environments.rb index 8724fe651..a7e58eab7 100644 --- a/lib/puppet/environments.rb +++ b/lib/puppet/environments.rb @@ -1,4 +1,6 @@ module Puppet::Environments + class NotFoundError < Puppet::Error; end + module EnvironmentCreator def for(module_path, manifest) Puppet::Node::Environment.create(:anonymous, @@ -55,6 +57,34 @@ module Puppet::Environments end end + class Directories + def initialize(environment_dir) + @environment_dir = environment_dir + @environments = [] + end + + def search_paths + ["environments://directories/#{@environment_dir}"] + end + + def list + Puppet::FileSystem.children(@environment_dir).inject(Set.new) do |envs,child| + if Puppet::FileSystem.directory?(child) && + Puppet::Node::Environment.valid_name?(Puppet::FileSystem.basename_string(child)) + + envs << Puppet::Node::Environment.create( + Puppet::FileSystem.path_string(child).intern, + [], '') + end + envs + end + end + + def get(name) + list.find { |env| env.name == name.intern } or raise NotFoundError, "Unable to find environment #{name}" + end + end + class Combined def initialize(*loaders) @loaders = loaders diff --git a/lib/puppet/file_system.rb b/lib/puppet/file_system.rb index 1a0fe7163..c8312a289 100644 --- a/lib/puppet/file_system.rb +++ b/lib/puppet/file_system.rb @@ -148,6 +148,15 @@ module Puppet::FileSystem @impl.exist?(assert_path(path)) end + # Determines if a file is a directory. + # + # @return [Boolean] true if the given file is a directory. + # + # @api public + def self.directory?(path) + @impl.directory?(assert_path(path)) + end + # Determines if a file is executable. # # @todo Should this take into account extensions on the windows platform? @@ -184,6 +193,13 @@ module Puppet::FileSystem @impl.mkpath(assert_path(path)) end + # @return [Array] references to all of the children of the given + # directory path, excluding `.` and `..`. + # @api public + def self.children(path) + @impl.children(assert_path(path)) + end + # Creates a symbolic link dest which points to the current file. # If dest already exists: # @@ -285,6 +301,7 @@ module Puppet::FileSystem # objects. The produced "handle" should be used in all other operations # that take a "path". No operation should be directly invoked on the returned opaque object # + # @param path [String] The string representation of the path # @return [Object] An opaque path handle on which no operations should be directly performed # # @api public @@ -305,6 +322,7 @@ module Puppet::FileSystem # Produces a string representation of the opaque path handle. # + # @param path [Object] a path handle produced by {#pathname} # @return [String] a string representation of the path # def self.path_string(path) diff --git a/lib/puppet/file_system/file_impl.rb b/lib/puppet/file_system/file_impl.rb index 2fa318e4e..9ee7a12f4 100644 --- a/lib/puppet/file_system/file_impl.rb +++ b/lib/puppet/file_system/file_impl.rb @@ -80,7 +80,11 @@ class Puppet::FileSystem::FileImpl end def exist?(path) - File.exist?(path) + ::File.exist?(path) + end + + def directory?(path) + ::File.directory?(path) end def executable?(path) @@ -99,6 +103,10 @@ class Puppet::FileSystem::FileImpl path.mkpath end + def children(path) + path.children + end + def symlink(path, dest, options = {}) FileUtils.symlink(path, dest, options) end diff --git a/lib/puppet/file_system/memory_file.rb b/lib/puppet/file_system/memory_file.rb index 94280bfcc..29909d23c 100644 --- a/lib/puppet/file_system/memory_file.rb +++ b/lib/puppet/file_system/memory_file.rb @@ -1,7 +1,7 @@ # An in-memory file abstraction. Commonly used with Puppet::FileSystem::File#overlay # @api private class Puppet::FileSystem::MemoryFile - attr_reader :path + attr_reader :path, :children def self.a_missing_file(path) new(path, :exist? => false, :executable? => false) @@ -15,17 +15,29 @@ class Puppet::FileSystem::MemoryFile new(path, :exist? => true, :executable? => true) end - def initialize(path, options) - @path = path - @exist = options[:exist?] - @executable = options[:executable?] - @content = options[:content] + def self.a_directory(path, children = []) + new(path, + :exist? => true, + :excutable? => true, + :directory? => true, + :children => children) end - def exist?; @exist; end - def executable?; @executable; end + def initialize(path, properties) + @path = path + @properties = properties + @children = properties[:children] + end + + def directory?; @properties[:directory?]; end + def exist?; @properties[:exist?]; end + def executable?; @properties[:executable?]; end def each_line(&block) - StringIO.new(@content).each_line(&block) + StringIO.new(@properties[:content]).each_line(&block) + end + + def duplicate_as(other_path) + self.class.new(other_path, @properties) end end diff --git a/lib/puppet/file_system/memory_impl.rb b/lib/puppet/file_system/memory_impl.rb index 6114483e5..32275afaf 100644 --- a/lib/puppet/file_system/memory_impl.rb +++ b/lib/puppet/file_system/memory_impl.rb @@ -4,19 +4,35 @@ class Puppet::FileSystem::MemoryImpl end def exist?(path) - find(path).exist? + path.exist? + end + + def directory?(path) + path.directory? end def executable?(path) - find(path).executable? + path.executable? + end + + def children(path) + path.children end def each_line(path, &block) - find(path).each_line(&block) + path.each_line(&block) end def pathname(path) - path.to_s + find(path) + end + + def basename(path) + path.duplicate_as(path_string(path).split(File::PATH_SEPARATOR).last) + end + + def path_string(object) + object.path end def assert_path(path) diff --git a/lib/puppet/network/http/api/v1.rb b/lib/puppet/network/http/api/v1.rb index 45e59b93b..8116e4756 100644 --- a/lib/puppet/network/http/api/v1.rb +++ b/lib/puppet/network/http/api/v1.rb @@ -58,7 +58,7 @@ class Puppet::Network::HTTP::API::V1 def uri2indirection(http_method, uri, params) environment, indirection, key = uri.split("/", 4)[1..-1] # the first field is always nil because of the leading slash - raise ArgumentError, "The environment must be purely alphanumeric, not '#{environment}'" unless environment =~ /^\w+$/ + raise ArgumentError, "The environment must be purely alphanumeric, not '#{environment}'" unless Puppet::Node::Environment.valid_name?(environment) raise ArgumentError, "The indirection name must be purely alphanumeric, not '#{indirection}'" unless indirection =~ /^\w+$/ method = indirection_method(http_method, indirection) diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb index 4a40ec0f4..0000db3d4 100644 --- a/lib/puppet/node/environment.rb +++ b/lib/puppet/node/environment.rb @@ -144,17 +144,11 @@ class Puppet::Node::Environment @manifest = manifest end - # Retrieve the environment for the current process. - # - # @note This should only used when a catalog is being compiled. - # - # @api private - # - # @return [Puppet::Node::Environment] the currently set environment if one - # has been explicitly set, else it will return the '*root*' environment - def self.current - Puppet.deprecation_warning("Remove me.") - Puppet.lookup(:current_environment) + # @param [String] name Environment name to check for valid syntax. + # @return [Boolean] true if name is valid + # @api public + def self.valid_name?(name) + !!name.match(/\A\w+\Z/) end # @return [Puppet::Node::Environment] The `*root*` environment. @@ -172,7 +166,6 @@ class Puppet::Node::Environment # @api private def self.clear seen.clear - $environment = nil end # @!attribute [r] name diff --git a/spec/lib/matchers/include.rb b/spec/lib/matchers/include.rb new file mode 100644 index 000000000..22069ff03 --- /dev/null +++ b/spec/lib/matchers/include.rb @@ -0,0 +1,27 @@ +module Matchers; module Include + extend RSpec::Matchers::DSL + + matcher :include_in_any_order do |*matchers| + match do |enumerable| + @not_matched = [] + matchers.each do |matcher| + if enumerable.empty? + break + end + + if found = enumerable.find { |elem| matcher.matches?(elem) } + enumerable = enumerable.reject { |elem| elem == found } + else + @not_matched << matcher + end + end + + + @not_matched.empty? && enumerable.empty? + end + + failure_message_for_should do |enumerable| + "did not match #{@not_matched.collect(&:description).join(', ')} in #{enumerable.inspect}" + end + end +end; end diff --git a/spec/unit/file_system/file_spec.rb b/spec/unit/file_system_spec.rb similarity index 100% rename from spec/unit/file_system/file_spec.rb rename to spec/unit/file_system_spec.rb From f40365aef8246020ac5d309c7689bc0182e2f3fd Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Tue, 21 Jan 2014 16:42:56 -0800 Subject: [PATCH 479/800] (PUP-1118) Load full environments from a directory This changes the loader used by the code to list environments to be a composition of the legacy environments and the new directory environments. --- .../environment/can_enumerate_environments.rb | 2 +- lib/puppet.rb | 5 +- lib/puppet/defaults.rb | 5 + lib/puppet/environments.rb | 30 +++-- lib/puppet/file_system/file_impl.rb | 3 +- lib/puppet/file_system/memory_file.rb | 4 +- lib/puppet/file_system/memory_impl.rb | 19 ++- lib/puppet/network/http/handler.rb | 18 +-- lib/puppet/node/environment.rb | 11 +- lib/puppet/test/test_helper.rb | 2 +- lib/puppet/util/autoload.rb | 4 +- spec/lib/matchers/include.rb | 4 +- spec/lib/matchers/include_spec.rb | 32 +++++ spec/unit/environments_spec.rb | 126 ++++++++++++++++++ spec/unit/util/autoload_spec.rb | 22 +-- 15 files changed, 232 insertions(+), 55 deletions(-) create mode 100644 spec/lib/matchers/include_spec.rb create mode 100644 spec/unit/environments_spec.rb diff --git a/acceptance/tests/environment/can_enumerate_environments.rb b/acceptance/tests/environment/can_enumerate_environments.rb index 3a8016673..6e6d7fd58 100644 --- a/acceptance/tests/environment/can_enumerate_environments.rb +++ b/acceptance/tests/environment/can_enumerate_environments.rb @@ -31,7 +31,7 @@ on master, "mkdir -p #{environments_dir}/env2" with_puppet_running_on(master, { :master => { - "environmentsdir" => environments_dir + "environmentdir" => environments_dir } }) do agents.each do |agent| diff --git a/lib/puppet.rb b/lib/puppet.rb index 10d467fc4..51ed2192e 100644 --- a/lib/puppet.rb +++ b/lib/puppet.rb @@ -175,7 +175,10 @@ module Puppet # @api private def self.initial_context { - :environments => Puppet::Environments::Legacy.new, + :environments => Puppet::Environments::Combined.new( + Puppet::Environments::Directories.new(Puppet[:environmentdir], Puppet::Node::Environment.split_path(Puppet[:modulepath])), + Puppet::Environments::Legacy.new + ), :current_environment => Puppet::Node::Environment.root, } end diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index 8881ef9b5..dfac70309 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -196,6 +196,11 @@ module Puppet is used to find modules and much more. For servers (i.e., `puppet master`) this provides the default environment for nodes we know nothing about." }, + :environmentdir => { + :default => "$confdir/environments", + :desc => "A directory of environments", + :type => :directory + }, :diff_args => { :default => default_diffargs, :desc => "Which arguments to pass to the diff command when printing differences between diff --git a/lib/puppet/environments.rb b/lib/puppet/environments.rb index a7e58eab7..0935d6ad2 100644 --- a/lib/puppet/environments.rb +++ b/lib/puppet/environments.rb @@ -54,13 +54,18 @@ module Puppet::Environments def get(name) Puppet::Node::Environment.new(name) + #symbol = name.to_sym + #Puppet::Node::Environment.create( + # symbol, + # Puppet::Node::Environment.split_path(Puppet.settings.value(:modulepath, symbol)), + # Puppet.settings.value(:manifest, symbol)) end end class Directories - def initialize(environment_dir) + def initialize(environment_dir, global_module_path) @environment_dir = environment_dir - @environments = [] + @global_module_path = global_module_path end def search_paths @@ -68,20 +73,23 @@ module Puppet::Environments end def list - Puppet::FileSystem.children(@environment_dir).inject(Set.new) do |envs,child| - if Puppet::FileSystem.directory?(child) && - Puppet::Node::Environment.valid_name?(Puppet::FileSystem.basename_string(child)) + base = Puppet::FileSystem.path_string(@environment_dir) - envs << Puppet::Node::Environment.create( - Puppet::FileSystem.path_string(child).intern, - [], '') - end - envs + Puppet::FileSystem.children(@environment_dir).select do |child| + name = Puppet::FileSystem.basename_string(child) + Puppet::FileSystem.directory?(child) && + Puppet::Node::Environment.valid_name?(name) + end.collect do |child| + name = Puppet::FileSystem.basename_string(child) + Puppet::Node::Environment.create( + name.intern, + [File.join(base, name, "modules")] + @global_module_path, + File.join(base, name, "manifests")) end end def get(name) - list.find { |env| env.name == name.intern } or raise NotFoundError, "Unable to find environment #{name}" + list.find { |env| env.name == name.intern } end end diff --git a/lib/puppet/file_system/file_impl.rb b/lib/puppet/file_system/file_impl.rb index 9ee7a12f4..1c945ae31 100644 --- a/lib/puppet/file_system/file_impl.rb +++ b/lib/puppet/file_system/file_impl.rb @@ -12,7 +12,8 @@ class Puppet::FileSystem::FileImpl # (sigh). # unless path.is_a?(String) || path.respond_to?(:to_str) - raise ArgumentError, "FileSystem implementation expected Pathname, got: '#{path.class}" + Puppet.warning(caller) + raise ArgumentError, "FileSystem implementation expected Pathname, got: '#{path.class}'" end # converts String and #to_str to Pathname Pathname.new(path) diff --git a/lib/puppet/file_system/memory_file.rb b/lib/puppet/file_system/memory_file.rb index 29909d23c..d2715c00a 100644 --- a/lib/puppet/file_system/memory_file.rb +++ b/lib/puppet/file_system/memory_file.rb @@ -26,7 +26,9 @@ class Puppet::FileSystem::MemoryFile def initialize(path, properties) @path = path @properties = properties - @children = properties[:children] + @children = (properties[:children] || []).collect do |child| + child.duplicate_as(File.join(@path, child.path)) + end end def directory?; @properties[:directory?]; end diff --git a/lib/puppet/file_system/memory_impl.rb b/lib/puppet/file_system/memory_impl.rb index 32275afaf..314d2a5f1 100644 --- a/lib/puppet/file_system/memory_impl.rb +++ b/lib/puppet/file_system/memory_impl.rb @@ -1,6 +1,6 @@ class Puppet::FileSystem::MemoryImpl def initialize(*files) - @files = files + @files = files + all_children_of(files) end def exist?(path) @@ -28,7 +28,7 @@ class Puppet::FileSystem::MemoryImpl end def basename(path) - path.duplicate_as(path_string(path).split(File::PATH_SEPARATOR).last) + path.duplicate_as(File.basename(path_string(path))) end def path_string(object) @@ -36,7 +36,11 @@ class Puppet::FileSystem::MemoryImpl end def assert_path(path) - path + if path.is_a?(Puppet::FileSystem::MemoryFile) + path + else + find(path) or raise ArgumentError, "Unable to find registered object for #{path.inspect}" + end end private @@ -44,4 +48,13 @@ class Puppet::FileSystem::MemoryImpl def find(path) @files.find { |file| file.path == path } end + + def all_children_of(files) + children = files.collect(&:children).flatten + if children.empty? + [] + else + children + all_children_of(children) + end + end end diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb index cd67eefea..82e873ea0 100644 --- a/lib/puppet/network/http/handler.rb +++ b/lib/puppet/network/http/handler.rb @@ -58,13 +58,11 @@ module Puppet::Network::HTTP::Handler configure_profiler(request_headers, request_params) warn_if_near_expiration(new_request.client_cert) - Puppet.override(request_bindings()) do - Puppet::Util::Profiler.profile("Processed request #{request_method} #{request_path}") do - if route = @routes.find { |route| route.matches?(new_request) } - route.process(new_request, new_response) - else - raise Puppet::Network::HTTP::Error::HTTPNotFoundError.new("No route for #{new_request.method} #{new_request.path}", HANDLER_NOT_FOUND) - end + Puppet::Util::Profiler.profile("Processed request #{request_method} #{request_path}") do + if route = @routes.find { |route| route.matches?(new_request) } + route.process(new_request, new_response) + else + raise Puppet::Network::HTTP::Error::HTTPNotFoundError.new("No route for #{new_request.method} #{new_request.path}", HANDLER_NOT_FOUND) end end @@ -179,10 +177,4 @@ module Puppet::Network::HTTP::Handler Puppet::Util::Profiler.current = Puppet::Util::Profiler::NONE end end - - def request_bindings - { - :environments => Puppet::Environments::OnlyProduction.new - } - end end diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb index 0000db3d4..d62fce664 100644 --- a/lib/puppet/node/environment.rb +++ b/lib/puppet/node/environment.rb @@ -14,11 +14,6 @@ end # # The Puppet::Node::Environment uses a number of global variables. # -# ### `$environment` -# -# The 'environment' global variable represents the current environment that's -# being used in the compiler. -# # ### `$known_resource_types` # # The 'known_resource_types' global variable represents a singleton instance @@ -178,7 +173,7 @@ class Puppet::Node::Environment # @return [Array] All directories present on disk in the modulepath def modulepath @modulepath.find_all do |p| - FileTest.directory?(p) + Puppet::FileSystem.directory?(p) end end @@ -437,12 +432,12 @@ class Puppet::Node::Environment self.to_s.to_zaml(z) end - private - def self.split_path(path_string) path_string.split(File::PATH_SEPARATOR) end + private + def self.extralibs() if ENV["PUPPETLIB"] split_path(ENV["PUPPETLIB"]) diff --git a/lib/puppet/test/test_helper.rb b/lib/puppet/test/test_helper.rb index 44cfe42f6..2263b9f0a 100644 --- a/lib/puppet/test/test_helper.rb +++ b/lib/puppet/test/test_helper.rb @@ -49,7 +49,7 @@ module Puppet::Test # will be run. # @return nil def self.after_all_tests() - + Puppet.pop_context end # Call this method once per test, prior to execution of each invididual test. diff --git a/lib/puppet/util/autoload.rb b/lib/puppet/util/autoload.rb index 166dab51f..e595dcf38 100644 --- a/lib/puppet/util/autoload.rb +++ b/lib/puppet/util/autoload.rb @@ -137,8 +137,8 @@ class Puppet::Util::Autoload # if the app defaults have been initialized then it should be safe to access the module path setting. $env_module_directories[real_env] ||= real_env.modulepath.collect do |dir| - Dir.entries(dir).reject { |f| f =~ /^\./ }.collect { |f| File.join(dir, f) } - end.flatten.collect { |d| File.join(d, "lib") }.find_all do |d| + Dir.entries(dir).reject { |f| f =~ /^\./ }.collect { |f| File.join(dir, f, "lib") } + end.flatten.find_all do |d| FileTest.directory?(d) end else diff --git a/spec/lib/matchers/include.rb b/spec/lib/matchers/include.rb index 22069ff03..c34725856 100644 --- a/spec/lib/matchers/include.rb +++ b/spec/lib/matchers/include.rb @@ -4,7 +4,7 @@ module Matchers; module Include matcher :include_in_any_order do |*matchers| match do |enumerable| @not_matched = [] - matchers.each do |matcher| + expected.each do |matcher| if enumerable.empty? break end @@ -21,7 +21,7 @@ module Matchers; module Include end failure_message_for_should do |enumerable| - "did not match #{@not_matched.collect(&:description).join(', ')} in #{enumerable.inspect}" + "did not match #{@not_matched.collect(&:description).join(', ')} in #{enumerable.inspect}: <#{@not_matched.collect(&:failure_message_for_should).join('>, <')}>" end end end; end diff --git a/spec/lib/matchers/include_spec.rb b/spec/lib/matchers/include_spec.rb new file mode 100644 index 000000000..7a55e90f8 --- /dev/null +++ b/spec/lib/matchers/include_spec.rb @@ -0,0 +1,32 @@ +require 'spec_helper' +require 'matchers/include' + +describe "include matchers" do + include Matchers::Include + + context :include_in_any_order do + it "matches an empty list" do + expect([]).to include_in_any_order() + end + + it "matches a list with a single element" do + expect([1]).to include_in_any_order(eq(1)) + end + + it "does not match when an expected element is missing" do + expect([1]).to_not include_in_any_order(eq(2)) + end + + it "matches a list with 2 elements in a different order from the expectation" do + expect([1, 2]).to include_in_any_order(eq(2), eq(1)) + end + + it "does not match when there are more than just the expected elements" do + expect([1, 2]).to_not include_in_any_order(eq(1)) + end + + it "matches multiple, equal elements when there are multiple, equal exepectations" do + expect([1, 1]).to include_in_any_order(eq(1), eq(1)) + end + end +end diff --git a/spec/unit/environments_spec.rb b/spec/unit/environments_spec.rb new file mode 100644 index 000000000..3a1f81628 --- /dev/null +++ b/spec/unit/environments_spec.rb @@ -0,0 +1,126 @@ +require 'spec_helper' +require 'puppet/environments' +require 'puppet/file_system' +require 'matchers/include' + +describe Puppet::Environments do + include Matchers::Include + include PuppetSpec::Files + + FS = Puppet::FileSystem + + describe "directories loader" do + + RSpec::Matchers.define :environment do |name| + match do |env| + env.name == name && + (!@manifest || @manifest == env.manifest) && + (!@modulepath || @modulepath == env.modulepath) + end + + chain :with_manifest do |manifest| + @manifest = manifest + end + + chain :with_modulepath do |modulepath| + @modulepath = modulepath + end + + description do + "environment #{expected}" + + (@manifest ? " with manifest #{@manifest}" : "") + + (@modulepath ? " with modulepath [#{@modulepath.join(', ')}]" : "") + end + + failure_message_for_should do |env| + "expected <#{env.name}: modulepath = [#{env.modulepath.join(', ')}], manifest = #{env.manifest}> to be #{description}" + end + end + + def loader_from(options, &block) + FS.overlay(*options[:filesystem]) do + yield Puppet::Environments::Directories.new( + options[:directory], + options[:modulepath] || [] + ) + end + end + + it "lists environments" do + global_path_1_location = File.expand_path("global_path_1") + global_path_2_location = File.expand_path("global_path_2") + global_path_1 = FS::MemoryFile.a_directory(global_path_1_location) + global_path_2 = FS::MemoryFile.a_directory(global_path_2_location) + + envdir = FS::MemoryFile.a_directory(File.expand_path("envdir"), [ + FS::MemoryFile.a_directory("env1", [ + FS::MemoryFile.a_directory("modules"), + FS::MemoryFile.a_directory("manifests"), + ]), + FS::MemoryFile.a_directory("env2") + ]) + + loader_from(:filesystem => [envdir, global_path_1, global_path_2], + :directory => envdir, + :modulepath => [global_path_1_location, global_path_2_location]) do |loader| + expect(loader.list).to include_in_any_order( + environment(:env1). + with_manifest("#{FS.path_string(envdir)}/env1/manifests"). + with_modulepath(["#{FS.path_string(envdir)}/env1/modules", + global_path_1_location, + global_path_2_location]), + environment(:env2)) + end + end + + it "does not list files" do + envdir = FS::MemoryFile.a_directory("envdir", [ + FS::MemoryFile.a_regular_file_containing("foo", ''), + FS::MemoryFile.a_directory("env1"), + FS::MemoryFile.a_directory("env2"), + ]) + + loader_from(:filesystem => [envdir], + :directory => envdir) do |loader| + expect(loader.list).to include_in_any_order(environment(:env1), environment(:env2)) + end + end + + it "it ignores directories that are not valid env names (alphanumeric and _)" do + envdir = FS::MemoryFile.a_directory("envdir", [ + FS::MemoryFile.a_directory(".foo"), + FS::MemoryFile.a_directory("bar-thing"), + FS::MemoryFile.a_directory("with spaces"), + FS::MemoryFile.a_directory("some.thing"), + FS::MemoryFile.a_directory("env1"), + FS::MemoryFile.a_directory("env2"), + ]) + + loader_from(:filesystem => [envdir], + :directory => envdir) do |loader| + expect(loader.list).to include_in_any_order(environment(:env1), environment(:env2)) + end + end + + it "gets a particular environment" do + directory_tree = FS::MemoryFile.a_directory("envdir", [ + FS::MemoryFile.a_directory("env1"), + FS::MemoryFile.a_directory("env2"), + ]) + + loader_from(:filesystem => [directory_tree], + :directory => directory_tree) do |loader| + expect(loader.get("env1")).to environment(:env1) + end + end + + it "returns nil if an environment can't be found" do + directory_tree = FS::MemoryFile.a_directory("envdir", []) + + loader_from(:filesystem => [directory_tree], + :directory => directory_tree) do |loader| + expect(loader.get("env_not_in_this_list")).to be_nil + end + end + end +end diff --git a/spec/unit/util/autoload_spec.rb b/spec/unit/util/autoload_spec.rb index 68f4e2e61..3592a63c9 100755 --- a/spec/unit/util/autoload_spec.rb +++ b/spec/unit/util/autoload_spec.rb @@ -33,12 +33,12 @@ describe Puppet::Util::Autoload do Dir.expects(:entries).with(@dira).returns %w{one two} Dir.expects(:entries).with(@dirb).returns %w{one two} - FileTest.stubs(:directory?).returns false - FileTest.expects(:directory?).with(@dira).returns true - FileTest.expects(:directory?).with(@dirb).returns true - ["#{@dira}/two/lib", "#{@dirb}/two/lib"].each do |d| - FileTest.expects(:directory?).with(d).returns true - end + Puppet::FileSystem.expects(:directory?).with(@dira).returns true + Puppet::FileSystem.expects(:directory?).with(@dirb).returns true + Puppet::FileSystem.expects(:directory?).with(@dirc).returns false + + FileTest.expects(:directory?).with(regexp_matches(%r{two/lib})).times(2).returns true + FileTest.expects(:directory?).with(regexp_matches(%r{one/lib})).times(2).returns false @autoload.class.module_directories.should == ["#{@dira}/two/lib", "#{@dirb}/two/lib"] end @@ -52,11 +52,11 @@ describe Puppet::Util::Autoload do Puppet[:environment] = "foo" Dir.expects(:entries).with(@dira).returns %w{. ..} - FileTest.expects(:directory?).with(@dira).returns true - FileTest.expects(:directory?).with("#{@dira}/./lib").never - FileTest.expects(:directory?).with("#{@dira}/./plugins").never - FileTest.expects(:directory?).with("#{@dira}/../lib").never - FileTest.expects(:directory?).with("#{@dira}/../plugins").never + Puppet::FileSystem.expects(:directory?).with(@dira).returns true + Puppet::FileSystem.expects(:directory?).with("#{@dira}/./lib").never + Puppet::FileSystem.expects(:directory?).with("#{@dira}/./plugins").never + Puppet::FileSystem.expects(:directory?).with("#{@dira}/../lib").never + Puppet::FileSystem.expects(:directory?).with("#{@dira}/../plugins").never @autoload.class.module_directories end From ba6e5ab9330c25d6e9c1b7694f1a65c1f5af8e6a Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Wed, 22 Jan 2014 15:10:17 -0800 Subject: [PATCH 480/800] (PUP-1118) Simplify Puppet::Context Puppet::Context can reduce the number of objects that it uses for tracking contexts by keeping everything internal to it. This actually results in much less code, but a few more instance variables to track. Overall is seems like an easier to understand system. --- lib/puppet/context.rb | 53 +++++++++++++-------------------------- spec/unit/context_spec.rb | 4 --- 2 files changed, 18 insertions(+), 39 deletions(-) diff --git a/lib/puppet/context.rb b/lib/puppet/context.rb index 43c037109..94ffab925 100644 --- a/lib/puppet/context.rb +++ b/lib/puppet/context.rb @@ -10,55 +10,38 @@ class Puppet::Context class UndefinedBindingError < Puppet::Error; end class StackUnderflow < Puppet::Error; end - # @api private - class Bindings - attr_reader :parent, :description - - def initialize(parent, description, overrides = {}) - overrides ||= {} - @parent = parent - @description = description - @table = parent ? parent.table.merge(overrides) : overrides - end - - def lookup(name, default_proc) - if @table.include?(name) - @table[name] - elsif default_proc - default_proc.call - else - raise UndefinedBindingError, name - end - end - - def root? - @parent.nil? - end - - protected - - attr_reader :table - end - # @api private def initialize(initial_bindings) - @bindings = Bindings.new(nil, "root", initial_bindings) + @stack = [] + @table = initial_bindings + @description = "root" end # @api private def push(overrides, description = "") - @bindings = Bindings.new(@bindings, description, overrides) + @stack.push([@table, @description]) + @table = @table.merge(overrides || {}) + @description = description end # @api private def pop - raise(StackUnderflow, "Attempted to pop, but already at root of the context stack.") if @bindings.root? - @bindings = @bindings.parent + if @stack.empty? + raise(StackUnderflow, "Attempted to pop, but already at root of the context stack.") + else + (@table, @description) = @stack.pop + end end # @api private def lookup(name, &block) - @bindings.lookup(name, block) + if @table.include?(name) + @table[name] + elsif block + block.call + else + raise UndefinedBindingError, "lookup of #{name} in #{@table.inspect} at top of #{@stack.inspect}" + end end # @api private diff --git a/spec/unit/context_spec.rb b/spec/unit/context_spec.rb index 3708ebc10..505192f18 100644 --- a/spec/unit/context_spec.rb +++ b/spec/unit/context_spec.rb @@ -21,10 +21,6 @@ describe Puppet::Context do it "fails if you try to pop off the top of the stack" do expect { context.pop }.to raise_error(Puppet::Context::StackUnderflow) end - - it "protects the bindings table from casual access" do - expect { context.push({}).table }.to raise_error(NoMethodError, /protected/) - end end describe "with additional context" do From 757505b99e94e7a09a0846f010acd16b794a9ffa Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Wed, 22 Jan 2014 15:41:22 -0800 Subject: [PATCH 481/800] (PUP-1118) Create a new context after app initialization There end up being 3 steps of initialization in puppet: * bootstrap: after code starts loading, but before initialize_settings is called * initial: after initialize_settings is called, but before an application has been loaded and sets up the true run mode * application: after an application has started running, set the run mode, and reinitialized the app default settings. This last one had been missed in previous changes to have puppet setup the right contexts. This now setups another context override in the Puppet::Application run method to control the application context. --- .../environment/can_enumerate_environments.rb | 4 ++-- lib/puppet.rb | 10 ++++++---- lib/puppet/application.rb | 20 +++++++++++-------- lib/puppet/test/test_helper.rb | 2 +- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/acceptance/tests/environment/can_enumerate_environments.rb b/acceptance/tests/environment/can_enumerate_environments.rb index 6e6d7fd58..600d3b9e8 100644 --- a/acceptance/tests/environment/can_enumerate_environments.rb +++ b/acceptance/tests/environment/can_enumerate_environments.rb @@ -20,7 +20,7 @@ def curl_master_from(agent, path, headers = '', &block) url = "https://#{master}:#{master_port(agent)}#{path}" cert_path = full_path(agent, setting_on(agent, "agent", "hostcert")) key_path = full_path(agent, setting_on(agent, "agent", "hostprivkey")) - curl_base = "curl -g --cert \"#{cert_path}\" --key \"#{key_path}\" -k -H '#{headers}'" + curl_base = "curl -sg --cert \"#{cert_path}\" --key \"#{key_path}\" -k -H '#{headers}'" on agent, "#{curl_base} '#{url}'", &block end @@ -31,7 +31,7 @@ on master, "mkdir -p #{environments_dir}/env2" with_puppet_running_on(master, { :master => { - "environmentdir" => environments_dir + :environmentdir => environments_dir } }) do agents.each do |agent| diff --git a/lib/puppet.rb b/lib/puppet.rb index 51ed2192e..edbc76e09 100644 --- a/lib/puppet.rb +++ b/lib/puppet.rb @@ -150,7 +150,7 @@ module Puppet Puppet.settings.initialize_global_settings(args) run_mode = Puppet::Util::RunMode[run_mode] Puppet.settings.initialize_app_defaults(Puppet::Settings.app_defaults_for_run_mode(run_mode)) - Puppet.push_context(Puppet.initial_context, "Initial context after settings initialization") + Puppet.push_context(Puppet.base_context(Puppet.settings), "Initial context after settings initialization") Puppet::Parser::Functions.reset end private_class_method :do_initialize_settings_for_run_mode @@ -173,10 +173,12 @@ module Puppet # The bindings used for initialization of puppet # @api private - def self.initial_context + def self.base_context(settings) { :environments => Puppet::Environments::Combined.new( - Puppet::Environments::Directories.new(Puppet[:environmentdir], Puppet::Node::Environment.split_path(Puppet[:modulepath])), + Puppet::Environments::Directories.new( + settings[:environmentdir], + Puppet::Node::Environment.split_path(settings[:modulepath])), Puppet::Environments::Legacy.new ), :current_environment => Puppet::Node::Environment.root, @@ -184,7 +186,7 @@ module Puppet end # A simple set of bindings that is just enough to limp along to - # initialization where the {#initial_context} bindings are put in place + # initialization where the {#base_context} bindings are put in place # @api private def self.bootstrap_context { :current_environment => Puppet::Node::Environment.create(:'*bootstrap*', [], '') } diff --git a/lib/puppet/application.rb b/lib/puppet/application.rb index 0cb3c9a3f..71e4d62f1 100644 --- a/lib/puppet/application.rb +++ b/lib/puppet/application.rb @@ -353,15 +353,19 @@ class Application plugin_hook('initialize_app_defaults') { initialize_app_defaults } end - require 'puppet' - require 'puppet/util/instrumentation' - Puppet::Util::Instrumentation.init + # Setup a new context using the app's configuration + Puppet.override(Puppet.base_context(Puppet.settings), + "Base context from application's configuration") do + require 'puppet' + require 'puppet/util/instrumentation' + Puppet::Util::Instrumentation.init - exit_on_fail("initialize") { plugin_hook('preinit') { preinit } } - exit_on_fail("parse application options") { plugin_hook('parse_options') { parse_options } } - exit_on_fail("prepare for execution") { plugin_hook('setup') { setup } } - exit_on_fail("configure routes from #{Puppet[:route_file]}") { configure_indirector_routes } - exit_on_fail("run") { plugin_hook('run_command') { run_command } } + exit_on_fail("initialize") { plugin_hook('preinit') { preinit } } + exit_on_fail("parse application options") { plugin_hook('parse_options') { parse_options } } + exit_on_fail("prepare for execution") { plugin_hook('setup') { setup } } + exit_on_fail("configure routes from #{Puppet[:route_file]}") { configure_indirector_routes } + exit_on_fail("run") { plugin_hook('run_command') { run_command } } + end end def main diff --git a/lib/puppet/test/test_helper.rb b/lib/puppet/test/test_helper.rb index 2263b9f0a..18b4fa1d3 100644 --- a/lib/puppet/test/test_helper.rb +++ b/lib/puppet/test/test_helper.rb @@ -42,7 +42,7 @@ module Puppet::Test # @return nil def self.before_all_tests() # Make sure that all of the setup is also done for any before(:all) blocks - Puppet.push_context(Puppet.initial_context, "Initial for specs") + Puppet.push_context(Puppet.base_context(Puppet.settings), "Initial for specs") end # Call this method once, at the end of a test run, when no more tests From 4b30d1645185b523655af4b7c22045afab13168b Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Thu, 23 Jan 2014 14:09:05 -0800 Subject: [PATCH 482/800] (PUP-1118) Make environments readable by puppet master The directories that were made for the environment listing tests were not actually readable by the master. This changes them to be readable. --- .../environment/can_enumerate_environments.rb | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/acceptance/tests/environment/can_enumerate_environments.rb b/acceptance/tests/environment/can_enumerate_environments.rb index 600d3b9e8..307e8cbb2 100644 --- a/acceptance/tests/environment/can_enumerate_environments.rb +++ b/acceptance/tests/environment/can_enumerate_environments.rb @@ -26,8 +26,19 @@ def curl_master_from(agent, path, headers = '', &block) end environments_dir = master.tmpdir("environments") -on master, "mkdir -p #{environments_dir}/env1" -on master, "mkdir -p #{environments_dir}/env2" +apply_manifest_on(master, <<-MANIFEST) +File { + ensure => directory, + owner => puppet, + mode => 0700, +} + +file { + "#{environments_dir}":; + "#{environments_dir}/env1":; + "#{environments_dir}/env2":; +} +MANIFEST with_puppet_running_on(master, { :master => { From 47316e9788997a5616890d98b1adb94c0b4cc676 Mon Sep 17 00:00:00 2001 From: Ryan McKern Date: Wed, 22 Jan 2014 17:15:52 -0800 Subject: [PATCH 483/800] Add initial support for building in a RHEL 7 mock Building in an x86_64 mock, since RHEL has not provided a 32-bit release. --- ext/build_defaults.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/build_defaults.yaml b/ext/build_defaults.yaml index 1d256182f..1b755499d 100644 --- a/ext/build_defaults.yaml +++ b/ext/build_defaults.yaml @@ -9,7 +9,7 @@ gpg_name: 'info@puppetlabs.com' gpg_key: '4BD6EC30' sign_tar: FALSE # a space separated list of mock configs -final_mocks: 'pl-el-5-i386 pl-el-6-i386 pl-fedora-19-i386 pl-fedora-20-i386' +final_mocks: 'pl-el-5-i386 pl-el-6-i386 pl-el-7-x86_64 pl-fedora-19-i386 pl-fedora-20-i386' yum_host: 'yum.puppetlabs.com' yum_repo_path: '/opt/repository/yum/' build_gem: TRUE From 9fa4a39a684cba8ccb0902060a2557357c05f39e Mon Sep 17 00:00:00 2001 From: "Ethan J. Brown" Date: Thu, 23 Jan 2014 12:26:27 -0800 Subject: [PATCH 484/800] (maint) Puppet fails to properly surface backtraces - In many cases, exceptions are handled with Ruby and rethrown in a manner that loses the original exceptions backtrace. This often masks the real backtrace associated with an error, and can be the source of misleading errors or fishing expeditions. - Where appropriate, pass along the backtrace, or use the initializer of the raised Error to wrap / extract the backtrace from the existing exception. - Puppet::Util::Windows::Error is given a new initializer that allows passing in the original Exception - Puppet::Util::Errors.fail adds a new overload that allows for the original Exception to be passed in - To summarize, calls structured like this preserve backtraces: rescue ArgumentError => e raise raise e, e.message, e.backtrace raise e rescue raise raise $! raise $!, "bad arg yo -- part 2", $!.backtrace raise $!, $!.message, $!.backtrace raise Puppet::Error.new("new msg / ex type", detail) - To summarize, calls structured like this will eat backtraces: rescue raise $!, "Just smoked the backtrace on existing exception" raise "Just smoked the backtrace and changed to RuntimeError" raise RuntimeError.new($!) --- lib/puppet/application/apply.rb | 2 +- lib/puppet/configurer.rb | 2 +- lib/puppet/configurer/fact_handler.rb | 2 +- lib/puppet/external/pson/common.rb | 2 +- lib/puppet/external/pson/pure/generator.rb | 2 +- lib/puppet/external/pson/pure/parser.rb | 2 +- lib/puppet/face/help.rb | 3 ++- lib/puppet/file_bucket/dipper.rb | 2 +- lib/puppet/file_serving/fileset.rb | 2 +- lib/puppet/indirector/catalog/compiler.rb | 2 +- lib/puppet/indirector/exec.rb | 2 +- lib/puppet/indirector/face.rb | 5 +++-- lib/puppet/indirector/json.rb | 6 +++--- lib/puppet/indirector/key/file.rb | 4 ++-- lib/puppet/indirector/ldap.rb | 2 +- lib/puppet/indirector/node/exec.rb | 2 +- lib/puppet/indirector/queue.rb | 3 ++- lib/puppet/indirector/request.rb | 2 +- lib/puppet/indirector/resource_type/parser.rb | 2 +- lib/puppet/indirector/ssl_file.rb | 4 ++-- lib/puppet/indirector/yaml.rb | 2 +- lib/puppet/module_tool/applications/application.rb | 2 +- lib/puppet/module_tool/applications/generator.rb | 3 ++- lib/puppet/module_tool/applications/unpacker.rb | 2 +- lib/puppet/module_tool/applications/upgrader.rb | 4 ++-- lib/puppet/module_tool/shared_behaviors.rb | 2 +- lib/puppet/network/auth_config_parser.rb | 2 +- lib/puppet/network/http/api/v2/authorization.rb | 2 +- lib/puppet/network/http/connection.rb | 5 +++-- lib/puppet/parameter.rb | 2 +- lib/puppet/parser/functions/generate.rb | 2 +- lib/puppet/parser/resource.rb | 2 +- lib/puppet/property.rb | 2 +- lib/puppet/property/ensure.rb | 2 +- lib/puppet/provider/aixobject.rb | 6 +++--- lib/puppet/provider/exec.rb | 2 +- lib/puppet/provider/file/posix.rb | 4 ++-- lib/puppet/provider/file/windows.rb | 4 ++-- lib/puppet/provider/group/aix.rb | 2 +- .../provider/macauthorization/macauthorization.rb | 4 ++-- lib/puppet/provider/naginator.rb | 2 +- lib/puppet/provider/nameservice.rb | 6 +++--- lib/puppet/provider/package/aix.rb | 2 +- lib/puppet/provider/package/gem.rb | 4 ++-- lib/puppet/provider/package/pacman.rb | 2 +- lib/puppet/provider/package/pip.rb | 4 ++-- lib/puppet/provider/package/ports.rb | 2 +- lib/puppet/provider/package/portupgrade.rb | 14 +++++++------- lib/puppet/provider/package/rpm.rb | 2 +- lib/puppet/provider/selboolean/getsetsebool.rb | 2 +- lib/puppet/provider/selmodule/semodule.rb | 10 +++++----- lib/puppet/provider/service/base.rb | 2 +- lib/puppet/provider/service/daemontools.rb | 8 ++++---- lib/puppet/provider/service/gentoo.rb | 4 ++-- lib/puppet/provider/service/launchd.rb | 8 ++++---- lib/puppet/provider/service/redhat.rb | 4 ++-- lib/puppet/provider/service/runit.rb | 2 +- lib/puppet/provider/service/service.rb | 2 +- lib/puppet/provider/service/smf.rb | 2 +- lib/puppet/provider/service/src.rb | 6 +++--- lib/puppet/provider/service/systemd.rb | 4 ++-- lib/puppet/provider/service/windows.rb | 14 +++++++------- lib/puppet/provider/user/aix.rb | 4 ++-- lib/puppet/provider/user/directoryservice.rb | 8 ++++---- lib/puppet/provider/user/user_role_add.rb | 4 ++-- lib/puppet/provider/zone/solaris.rb | 6 +++--- lib/puppet/rails.rb | 6 +++--- lib/puppet/reports/tagmail.rb | 4 ++-- lib/puppet/resource/type_collection.rb | 2 +- lib/puppet/settings.rb | 6 +++--- lib/puppet/ssl/certificate_authority.rb | 4 ++-- lib/puppet/ssl/certificate_request.rb | 4 ++-- lib/puppet/type/exec.rb | 4 ++-- lib/puppet/type/file/content.rb | 4 ++-- lib/puppet/type/file/source.rb | 4 ++-- lib/puppet/type/package.rb | 4 ++-- lib/puppet/type/zone.rb | 2 +- lib/puppet/util.rb | 2 +- lib/puppet/util/adsi.rb | 6 +++--- lib/puppet/util/autoload.rb | 2 +- lib/puppet/util/backups.rb | 4 ++-- lib/puppet/util/command_line/trollop.rb | 4 ++-- lib/puppet/util/errors.rb | 9 ++++++++- lib/puppet/util/execution.rb | 2 +- lib/puppet/util/filetype.rb | 4 ++-- lib/puppet/util/ldap/connection.rb | 2 +- lib/puppet/util/log/destinations.rb | 2 +- lib/puppet/util/metric.rb | 4 ++-- lib/puppet/util/network_device.rb | 2 +- lib/puppet/util/network_device/transport/ssh.rb | 6 +++--- lib/puppet/util/queue/stomp.rb | 4 ++-- lib/puppet/util/rdoc/parser/puppet_parser_core.rb | 8 ++++---- lib/puppet/util/retryaction.rb | 2 +- lib/puppet/util/storage.rb | 2 +- lib/puppet/util/symbolic_file_mode.rb | 2 +- lib/puppet/util/windows/error.rb | 4 ++-- lib/puppet/util/windows/registry.rb | 4 ++-- 97 files changed, 184 insertions(+), 172 deletions(-) diff --git a/lib/puppet/application/apply.rb b/lib/puppet/application/apply.rb index 1d2535f6c..111eafcd4 100644 --- a/lib/puppet/application/apply.rb +++ b/lib/puppet/application/apply.rb @@ -271,7 +271,7 @@ Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License catalog = Puppet::Resource::Catalog.convert_from(Puppet::Resource::Catalog.default_format,text) catalog = Puppet::Resource::Catalog.pson_create(catalog) unless catalog.is_a?(Puppet::Resource::Catalog) rescue => detail - raise Puppet::Error, "Could not deserialize catalog from pson: #{detail}" + raise Puppet::Error, "Could not deserialize catalog from pson: #{detail}", detail.backtrace end catalog.to_ral diff --git a/lib/puppet/configurer.rb b/lib/puppet/configurer.rb index c5a77752f..68252268f 100644 --- a/lib/puppet/configurer.rb +++ b/lib/puppet/configurer.rb @@ -47,7 +47,7 @@ class Puppet::Configurer Puppet::FileSystem.unlink(Puppet[:statefile]) retry rescue => detail - raise Puppet::Error.new("Cannot remove #{Puppet[:statefile]}: #{detail}") + raise Puppet::Error.new("Cannot remove #{Puppet[:statefile]}: #{detail}", detail) end end diff --git a/lib/puppet/configurer/fact_handler.rb b/lib/puppet/configurer/fact_handler.rb index bb76146cc..5d20a5b91 100644 --- a/lib/puppet/configurer/fact_handler.rb +++ b/lib/puppet/configurer/fact_handler.rb @@ -23,7 +23,7 @@ module Puppet::Configurer::FactHandler rescue Exception => detail message = "Could not retrieve local facts: #{detail}" Puppet.log_exception(detail, message) - raise Puppet::Error, message + raise Puppet::Error, message, detail.backtrace end end diff --git a/lib/puppet/external/pson/common.rb b/lib/puppet/external/pson/common.rb index 2ea2b0e49..832d3f7ee 100644 --- a/lib/puppet/external/pson/common.rb +++ b/lib/puppet/external/pson/common.rb @@ -319,7 +319,7 @@ module PSON result end rescue PSON::NestingError - raise ArgumentError, "exceed depth limit" + raise ArgumentError, "exceed depth limit", $!.backtrace end diff --git a/lib/puppet/external/pson/pure/generator.rb b/lib/puppet/external/pson/pure/generator.rb index e0eabffc3..bcf2fde2a 100644 --- a/lib/puppet/external/pson/pure/generator.rb +++ b/lib/puppet/external/pson/pure/generator.rb @@ -46,7 +46,7 @@ module PSON string.gsub!(/["\\\x0-\x1f]/) { MAP[$MATCH] } string rescue => e - raise GeneratorError, "Caught #{e.class}: #{e}" + raise GeneratorError, "Caught #{e.class}: #{e}", e.backtrace end else def utf8_to_pson(string) # :nodoc: diff --git a/lib/puppet/external/pson/pure/parser.rb b/lib/puppet/external/pson/pure/parser.rb index bcdaf7b64..43c6c5ffb 100644 --- a/lib/puppet/external/pson/pure/parser.rb +++ b/lib/puppet/external/pson/pure/parser.rb @@ -204,7 +204,7 @@ module PSON UNPARSED end rescue => e - raise GeneratorError, "Caught #{e.class}: #{e}" + raise GeneratorError, "Caught #{e.class}: #{e}", e.backtrace end def parse_value diff --git a/lib/puppet/face/help.rb b/lib/puppet/face/help.rb index 44380ab30..e9dfe9037 100644 --- a/lib/puppet/face/help.rb +++ b/lib/puppet/face/help.rb @@ -88,12 +88,13 @@ Puppet::Face.define(:help, '0.0.1') do begin face = Puppet::Face[facename.to_sym, version] rescue Puppet::Error => detail - fail ArgumentError, <<-MSG + msg = <<-MSG Could not load help for the face #{facename}. Please check the error logs for more information. Detail: "#{detail.message}" MSG + fail ArgumentError, msg, detail.backtrace end if actionname action = face.get_action(actionname.to_sym) diff --git a/lib/puppet/file_bucket/dipper.rb b/lib/puppet/file_bucket/dipper.rb index f58ea7761..d1852f74f 100644 --- a/lib/puppet/file_bucket/dipper.rb +++ b/lib/puppet/file_bucket/dipper.rb @@ -50,7 +50,7 @@ class Puppet::FileBucket::Dipper rescue => detail message = "Could not back up #{file}: #{detail}" Puppet.log_exception(detail, message) - raise Puppet::Error, message + raise Puppet::Error, message, detail.backtrace end end diff --git a/lib/puppet/file_serving/fileset.rb b/lib/puppet/file_serving/fileset.rb index 89ed86985..6e982708b 100644 --- a/lib/puppet/file_serving/fileset.rb +++ b/lib/puppet/file_serving/fileset.rb @@ -88,7 +88,7 @@ class Puppet::FileServing::Fileset begin send(method, value) rescue NoMethodError - raise ArgumentError, "Invalid option '#{option}'" + raise ArgumentError, "Invalid option '#{option}'", $!.backtrace end end end diff --git a/lib/puppet/indirector/catalog/compiler.rb b/lib/puppet/indirector/catalog/compiler.rb index ebce8edb7..a796fd4fe 100644 --- a/lib/puppet/indirector/catalog/compiler.rb +++ b/lib/puppet/indirector/catalog/compiler.rb @@ -106,7 +106,7 @@ class Puppet::Resource::Catalog::Compiler < Puppet::Indirector::Code rescue => detail message = "Failed when searching for node #{name}: #{detail}" Puppet.log_exception(detail, message) - raise Puppet::Error, message + raise Puppet::Error, message, detail.backtrace end diff --git a/lib/puppet/indirector/exec.rb b/lib/puppet/indirector/exec.rb index 19f028340..4a65ebeb4 100644 --- a/lib/puppet/indirector/exec.rb +++ b/lib/puppet/indirector/exec.rb @@ -18,7 +18,7 @@ class Puppet::Indirector::Exec < Puppet::Indirector::Terminus begin output = execute(external_command, :failonfail => true, :combine => false) rescue Puppet::ExecutionFailure => detail - raise Puppet::Error, "Failed to find #{name} via exec: #{detail}" + raise Puppet::Error, "Failed to find #{name} via exec: #{detail}", detail.backtrace end if output =~ /\A\s*\Z/ # all whitespace diff --git a/lib/puppet/indirector/face.rb b/lib/puppet/indirector/face.rb index 2bf1174b6..10fa27ef0 100644 --- a/lib/puppet/indirector/face.rb +++ b/lib/puppet/indirector/face.rb @@ -43,7 +43,7 @@ class Puppet::Indirector::Face < Puppet::Face rescue => detail message = "Could not call '#{method}' on '#{indirection_name}': #{detail}" Puppet.log_exception(detail, message) - raise message + raise RuntimeError, message, detail.backtrace end return result @@ -132,7 +132,8 @@ class Puppet::Indirector::Face < Puppet::Face begin indirection.terminus_class = from rescue => detail - raise "Could not set '#{indirection.name}' terminus to '#{from}' (#{detail}); valid terminus types are #{self.class.terminus_classes(indirection.name).join(", ") }" + msg = "Could not set '#{indirection.name}' terminus to '#{from}' (#{detail}); valid terminus types are #{self.class.terminus_classes(indirection.name).join(", ") }" + raise detail, msg, detail.backtrace end end end diff --git a/lib/puppet/indirector/json.rb b/lib/puppet/indirector/json.rb index 8c8f26e40..3bfd83112 100644 --- a/lib/puppet/indirector/json.rb +++ b/lib/puppet/indirector/json.rb @@ -24,7 +24,7 @@ class Puppet::Indirector::JSON < Puppet::Indirector::Terminus Puppet::FileSystem.unlink(path(request.key)) rescue => detail unless detail.is_a? Errno::ENOENT - raise Puppet::Error, "Could not destroy #{self.name} #{request.key}: #{detail}" + raise Puppet::Error, "Could not destroy #{self.name} #{request.key}: #{detail}", detail.backtrace end 1 # emulate success... end @@ -56,13 +56,13 @@ class Puppet::Indirector::JSON < Puppet::Indirector::Terminus rescue Errno::ENOENT return nil rescue => detail - raise Puppet::Error, "Could not read JSON data for #{indirection.name} #{key}: #{detail}" + raise Puppet::Error, "Could not read JSON data for #{indirection.name} #{key}: #{detail}", detail.backtrace end begin return from_json(json) rescue => detail - raise Puppet::Error, "Could not parse JSON data for #{indirection.name} #{key}: #{detail}" + raise Puppet::Error, "Could not parse JSON data for #{indirection.name} #{key}: #{detail}", detail.backtrace end end diff --git a/lib/puppet/indirector/key/file.rb b/lib/puppet/indirector/key/file.rb index a1886514d..81c428236 100644 --- a/lib/puppet/indirector/key/file.rb +++ b/lib/puppet/indirector/key/file.rb @@ -30,7 +30,7 @@ class Puppet::SSL::Key::File < Puppet::Indirector::SslFile begin Puppet::FileSystem.unlink(key_path) rescue => detail - raise Puppet::Error, "Could not remove #{request.key} public key: #{detail}" + raise Puppet::Error, "Could not remove #{request.key} public key: #{detail}", detail.backtrace end end @@ -43,7 +43,7 @@ class Puppet::SSL::Key::File < Puppet::Indirector::SslFile f.print request.instance.content.public_key.to_pem end rescue => detail - raise Puppet::Error, "Could not write #{request.key}: #{detail}" + raise Puppet::Error, "Could not write #{request.key}: #{detail}", detail.backtrace end end end diff --git a/lib/puppet/indirector/ldap.rb b/lib/puppet/indirector/ldap.rb index 696cb4091..293c05e2b 100644 --- a/lib/puppet/indirector/ldap.rb +++ b/lib/puppet/indirector/ldap.rb @@ -70,7 +70,7 @@ class Puppet::Indirector::Ldap < Puppet::Indirector::Terminus rescue => detail message = "Could not connect to LDAP: #{detail}" Puppet.log_exception(detail, message) - raise Puppet::Error, message + raise Puppet::Error, message, detail.backtrace end end diff --git a/lib/puppet/indirector/node/exec.rb b/lib/puppet/indirector/node/exec.rb index d74a76204..d4faf74f4 100644 --- a/lib/puppet/indirector/node/exec.rb +++ b/lib/puppet/indirector/node/exec.rb @@ -64,6 +64,6 @@ class Puppet::Node::Exec < Puppet::Indirector::Exec end rescue => detail - raise Puppet::Error, "Could not load external node results for #{name}: #{detail}" + raise Puppet::Error, "Could not load external node results for #{name}: #{detail}", detail.backtrace end end diff --git a/lib/puppet/indirector/queue.rb b/lib/puppet/indirector/queue.rb index 337dc1cf1..1d5776bdb 100644 --- a/lib/puppet/indirector/queue.rb +++ b/lib/puppet/indirector/queue.rb @@ -39,7 +39,8 @@ class Puppet::Indirector::Queue < Puppet::Indirector::Terminus end result rescue => detail - raise Puppet::Error, "Could not write #{request.key} to queue: #{detail}\nInstance::#{request.instance}\n client : #{client}" + msg = "Could not write #{request.key} to queue: #{detail}\nInstance::#{request.instance}\n client : #{client}" + raise Puppet::Error, msg, detail.backtrace end def self.queue diff --git a/lib/puppet/indirector/request.rb b/lib/puppet/indirector/request.rb index 142b753bd..fa40d3a03 100644 --- a/lib/puppet/indirector/request.rb +++ b/lib/puppet/indirector/request.rb @@ -274,7 +274,7 @@ class Puppet::Indirector::Request begin uri = URI.parse(URI.escape(key)) rescue => detail - raise ArgumentError, "Could not understand URL #{key}: #{detail}" + raise ArgumentError, "Could not understand URL #{key}: #{detail}", detail.backtrace end # Just short-circuit these to full paths diff --git a/lib/puppet/indirector/resource_type/parser.rb b/lib/puppet/indirector/resource_type/parser.rb index 4af03d0f8..0583da840 100644 --- a/lib/puppet/indirector/resource_type/parser.rb +++ b/lib/puppet/indirector/resource_type/parser.rb @@ -81,7 +81,7 @@ class Puppet::Indirector::ResourceType::Parser < Puppet::Indirector::Code begin regex = Regexp.new(key) rescue => detail - raise ArgumentError, "Invalid regex '#{request.key}': #{detail}" + raise ArgumentError, "Invalid regex '#{request.key}': #{detail}", detail.backtrace end result.reject! { |t| t.name.to_s !~ regex } diff --git a/lib/puppet/indirector/ssl_file.rb b/lib/puppet/indirector/ssl_file.rb index 8b823eb9c..695dc421d 100644 --- a/lib/puppet/indirector/ssl_file.rb +++ b/lib/puppet/indirector/ssl_file.rb @@ -76,7 +76,7 @@ class Puppet::Indirector::SslFile < Puppet::Indirector::Terminus begin Puppet::FileSystem.unlink(path) rescue => detail - raise Puppet::Error, "Could not remove #{request.key}: #{detail}" + raise Puppet::Error, "Could not remove #{request.key}: #{detail}", detail.backtrace end end @@ -166,7 +166,7 @@ class Puppet::Indirector::SslFile < Puppet::Indirector::Terminus begin Puppet.settings.setting(setting).open_file(path, 'w') { |f| yield f } rescue => detail - raise Puppet::Error, "Could not write #{path} to #{setting}: #{detail}" + raise Puppet::Error, "Could not write #{path} to #{setting}: #{detail}", detail.backtrace end else raise Puppet::DevError, "You must provide a setting to determine where the files are stored" diff --git a/lib/puppet/indirector/yaml.rb b/lib/puppet/indirector/yaml.rb index 0f2816f01..dae477e76 100644 --- a/lib/puppet/indirector/yaml.rb +++ b/lib/puppet/indirector/yaml.rb @@ -11,7 +11,7 @@ class Puppet::Indirector::Yaml < Puppet::Indirector::Terminus begin 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}" + raise Puppet::Error, "Could not parse YAML data for #{indirection.name} #{request.key}: #{detail}", detail.backtrace end end diff --git a/lib/puppet/module_tool/applications/application.rb b/lib/puppet/module_tool/applications/application.rb index 93338be53..f2fb3389f 100644 --- a/lib/puppet/module_tool/applications/application.rb +++ b/lib/puppet/module_tool/applications/application.rb @@ -53,7 +53,7 @@ module Puppet::ModuleTool begin @metadata.extra_metadata = PSON.load(f) rescue PSON::ParserError - raise ArgumentError, "Could not parse JSON #{extra_metadata_path}" + raise ArgumentError, "Could not parse JSON #{extra_metadata_path}", $!.backtrace end end end diff --git a/lib/puppet/module_tool/applications/generator.rb b/lib/puppet/module_tool/applications/generator.rb index 6b5bda98f..a27d5722c 100644 --- a/lib/puppet/module_tool/applications/generator.rb +++ b/lib/puppet/module_tool/applications/generator.rb @@ -10,7 +10,8 @@ module Puppet::ModuleTool begin @metadata = Metadata.new(:full_module_name => full_module_name) rescue ArgumentError - raise "Could not generate directory #{full_module_name.inspect}, you must specify a dash-separated username and module name." + msg = "Could not generate directory #{full_module_name.inspect}, you must specify a dash-separated username and module name." + raise $!, msg, $!.backtrace end super(options) end diff --git a/lib/puppet/module_tool/applications/unpacker.rb b/lib/puppet/module_tool/applications/unpacker.rb index 8fd5431b2..f141610b0 100644 --- a/lib/puppet/module_tool/applications/unpacker.rb +++ b/lib/puppet/module_tool/applications/unpacker.rb @@ -40,7 +40,7 @@ module Puppet::ModuleTool begin Puppet::ModuleTool::Tar.instance(@module_name).unpack(@filename.to_s, build_dir.to_s, [@module_path.stat.uid, @module_path.stat.gid].join(':')) rescue Puppet::ExecutionFailure => e - raise RuntimeError, "Could not extract contents of module archive: #{e.message}" + raise RuntimeError, "Could not extract contents of module archive: #{e.message}", e.backtrace end # grab the first directory diff --git a/lib/puppet/module_tool/applications/upgrader.rb b/lib/puppet/module_tool/applications/upgrader.rb index 8f6999ff2..b45f6cf61 100644 --- a/lib/puppet/module_tool/applications/upgrader.rb +++ b/lib/puppet/module_tool/applications/upgrader.rb @@ -52,9 +52,9 @@ module Puppet::ModuleTool begin get_remote_constraints(@forge) rescue => e - raise UnknownModuleError, results.merge(:repository => @forge.uri) + raise UnknownModuleError, results.merge(:repository => @forge.uri), e.backtrace else - raise UnknownVersionError, results.merge(:repository => @forge.uri) if @remote.empty? + raise UnknownVersionError, results.merge(:repository => @forge.uri), e.backtrace if @remote.empty? end if !@options[:force] && @versions["#{@module_name}"].last[:vstring].sub(/^(?=\d)/, 'v') == (@module.version || '0.0.0').sub(/^(?=\d)/, 'v') diff --git a/lib/puppet/module_tool/shared_behaviors.rb b/lib/puppet/module_tool/shared_behaviors.rb index 2b79b4ed4..afae12523 100644 --- a/lib/puppet/module_tool/shared_behaviors.rb +++ b/lib/puppet/module_tool/shared_behaviors.rb @@ -151,7 +151,7 @@ module Puppet::ModuleTool::Shared cache_path = forge.retrieve(release[:file]) end rescue OpenURI::HTTPError => e - raise RuntimeError, "Could not download module: #{e.message}" + raise RuntimeError, "Could not download module: #{e.message}", e.backtrace end [ diff --git a/lib/puppet/network/auth_config_parser.rb b/lib/puppet/network/auth_config_parser.rb index 2af06051c..cb7199ee3 100644 --- a/lib/puppet/network/auth_config_parser.rb +++ b/lib/puppet/network/auth_config_parser.rb @@ -76,7 +76,7 @@ class AuthConfigParser right.info msg % val right.send(method, val) rescue Puppet::AuthStoreError => detail - raise Puppet::ConfigurationError, "#{detail} at line #{count} of #{@file}" + raise Puppet::ConfigurationError, "#{detail} at line #{count} of #{@file}", detail.backtrace end end end diff --git a/lib/puppet/network/http/api/v2/authorization.rb b/lib/puppet/network/http/api/v2/authorization.rb index ceb7cda54..010376a06 100644 --- a/lib/puppet/network/http/api/v2/authorization.rb +++ b/lib/puppet/network/http/api/v2/authorization.rb @@ -7,7 +7,7 @@ class Puppet::Network::HTTP::API::V2::Authorization begin check_authorization(:find, request.path, request.params) rescue Puppet::Network::AuthorizationError => e - raise Puppet::Network::HTTP::Error::HTTPNotAuthorizedError, e.message + raise Puppet::Network::HTTP::Error::HTTPNotAuthorizedError, e.message, e.backtrace end end end diff --git a/lib/puppet/network/http/connection.rb b/lib/puppet/network/http/connection.rb index 3b1780e3c..eb0d7664c 100644 --- a/lib/puppet/network/http/connection.rb +++ b/lib/puppet/network/http/connection.rb @@ -194,14 +194,15 @@ module Puppet::Network::HTTP if error.message.include? "certificate verify failed" msg = error.message msg << ": [" + @verify.verify_errors.join('; ') + "]" - raise Puppet::Error, msg + raise Puppet::Error, msg, error.backtrace elsif error.message =~ /hostname (\w+ )?not match/ leaf_ssl_cert = @verify.peer_certs.last valid_certnames = [leaf_ssl_cert.name, *leaf_ssl_cert.subject_alt_names].uniq msg = valid_certnames.length > 1 ? "one of #{valid_certnames.join(', ')}" : valid_certnames.first + msg = "Server hostname '#{connection.address}' did not match server certificate; expected #{msg}" - raise Puppet::Error, "Server hostname '#{connection.address}' did not match server certificate; expected #{msg}" + raise Puppet::Error, msg, error.backtrace else raise end diff --git a/lib/puppet/parameter.rb b/lib/puppet/parameter.rb index f265a99ae..1b849a6e2 100644 --- a/lib/puppet/parameter.rb +++ b/lib/puppet/parameter.rb @@ -464,7 +464,7 @@ class Puppet::Parameter begin unsafe_validate(value) rescue ArgumentError => detail - fail detail.to_s + fail detail, detail.to_s, detail.backtrace rescue Puppet::Error, TypeError raise rescue => detail diff --git a/lib/puppet/parser/functions/generate.rb b/lib/puppet/parser/functions/generate.rb index 71bd0b19f..35c752c0b 100644 --- a/lib/puppet/parser/functions/generate.rb +++ b/lib/puppet/parser/functions/generate.rb @@ -32,6 +32,6 @@ Puppet::Parser::Functions::newfunction(:generate, :arity => -2, :type => :rvalue begin Dir.chdir(File.dirname(args[0])) { Puppet::Util::Execution.execute(args) } rescue Puppet::ExecutionFailure => detail - raise Puppet::ParseError, "Failed to execute generator #{args[0]}: #{detail}" + raise Puppet::ParseError, "Failed to execute generator #{args[0]}: #{detail}", detail.backtrace end end diff --git a/lib/puppet/parser/resource.rb b/lib/puppet/parser/resource.rb index 0b4c6677b..cddb5b542 100644 --- a/lib/puppet/parser/resource.rb +++ b/lib/puppet/parser/resource.rb @@ -253,7 +253,7 @@ class Puppet::Parser::Resource < Puppet::Resource validate_parameter(name) end rescue => detail - fail Puppet::ParseError, detail.to_s + fail Puppet::ParseError, detail.to_s, detail.backtrace end def extract_parameters(params) diff --git a/lib/puppet/property.rb b/lib/puppet/property.rb index 74c6e0292..841f8675a 100644 --- a/lib/puppet/property.rb +++ b/lib/puppet/property.rb @@ -231,7 +231,7 @@ class Puppet::Property < Puppet::Parameter rescue => detail message = "Could not convert change '#{name}' to string: #{detail}" Puppet.log_exception(detail, message) - raise Puppet::DevError, message + raise Puppet::DevError, message, detail.backtrace end end diff --git a/lib/puppet/property/ensure.rb b/lib/puppet/property/ensure.rb index 22ba211e3..c87c3fa53 100644 --- a/lib/puppet/property/ensure.rb +++ b/lib/puppet/property/ensure.rb @@ -59,7 +59,7 @@ class Puppet::Property::Ensure < Puppet::Property rescue Puppet::Error, Puppet::DevError raise rescue => detail - raise Puppet::DevError, "Could not convert change #{self.name} to string: #{detail}" + raise Puppet::DevError, "Could not convert change #{self.name} to string: #{detail}", detail.backtrace end end diff --git a/lib/puppet/provider/aixobject.rb b/lib/puppet/provider/aixobject.rb index ed27b4e52..53132a42e 100644 --- a/lib/puppet/provider/aixobject.rb +++ b/lib/puppet/provider/aixobject.rb @@ -317,7 +317,7 @@ class Puppet::Provider::AixObject < Puppet::Provider begin execute(self.addcmd) rescue Puppet::ExecutionFailure => detail - raise Puppet::Error, "Could not create #{@resource.class.name} #{@resource.name}: #{detail}" + raise Puppet::Error, "Could not create #{@resource.class.name} #{@resource.name}: #{detail}", detail.backtrace end end @@ -332,7 +332,7 @@ class Puppet::Provider::AixObject < Puppet::Provider begin execute(self.deletecmd) rescue Puppet::ExecutionFailure => detail - raise Puppet::Error, "Could not delete #{@resource.class.name} #{@resource.name}: #{detail}" + raise Puppet::Error, "Could not delete #{@resource.class.name} #{@resource.name}: #{detail}", detail.backtrace end end @@ -376,7 +376,7 @@ class Puppet::Provider::AixObject < Puppet::Provider begin execute(cmd) rescue Puppet::ExecutionFailure => detail - raise Puppet::Error, "Could not set #{param} on #{@resource.class.name}[#{@resource.name}]: #{detail}" + raise Puppet::Error, "Could not set #{param} on #{@resource.class.name}[#{@resource.name}]: #{detail}", detail.backtrace end end diff --git a/lib/puppet/provider/exec.rb b/lib/puppet/provider/exec.rb index b0db9ff94..cc43a0d37 100644 --- a/lib/puppet/provider/exec.rb +++ b/lib/puppet/provider/exec.rb @@ -63,7 +63,7 @@ class Puppet::Provider::Exec < Puppet::Provider end rescue Errno::ENOENT => detail - self.fail detail.to_s + self.fail Puppet::Error, detail.to_s, detail.backtrace end # Return output twice as processstatus was returned before, but only exitstatus was ever called. diff --git a/lib/puppet/provider/file/posix.rb b/lib/puppet/provider/file/posix.rb index 629a380dd..0ce7d223e 100644 --- a/lib/puppet/provider/file/posix.rb +++ b/lib/puppet/provider/file/posix.rb @@ -81,7 +81,7 @@ Puppet::Type.type(:file).provide :posix do begin File.send(method, should, nil, resource[:path]) rescue => detail - raise Puppet::Error, "Failed to set owner to '#{should}': #{detail}" + raise Puppet::Error, "Failed to set owner to '#{should}': #{detail}", detail.backtrace end end @@ -112,7 +112,7 @@ Puppet::Type.type(:file).provide :posix do begin File.send(method, nil, should, resource[:path]) rescue => detail - raise Puppet::Error, "Failed to set group to '#{should}': #{detail}" + raise Puppet::Error, "Failed to set group to '#{should}': #{detail}", detail.backtrace end end diff --git a/lib/puppet/provider/file/windows.rb b/lib/puppet/provider/file/windows.rb index f77f67be4..5c8038db1 100644 --- a/lib/puppet/provider/file/windows.rb +++ b/lib/puppet/provider/file/windows.rb @@ -44,7 +44,7 @@ Puppet::Type.type(:file).provide :windows do begin set_owner(should, resolved_path) rescue => detail - raise Puppet::Error, "Failed to set owner to '#{should}': #{detail}" + raise Puppet::Error, "Failed to set owner to '#{should}': #{detail}", detail.backtrace end end @@ -57,7 +57,7 @@ Puppet::Type.type(:file).provide :windows do begin set_group(should, resolved_path) rescue => detail - raise Puppet::Error, "Failed to set group to '#{should}': #{detail}" + raise Puppet::Error, "Failed to set group to '#{should}': #{detail}", detail.backtrace end end diff --git a/lib/puppet/provider/group/aix.rb b/lib/puppet/provider/group/aix.rb index 666748378..0eeb49c8b 100644 --- a/lib/puppet/provider/group/aix.rb +++ b/lib/puppet/provider/group/aix.rb @@ -127,7 +127,7 @@ Puppet::Type.type(:group).provide :aix, :parent => Puppet::Provider::AixObject d begin execute(cmd) rescue Puppet::ExecutionFailure => detail - raise Puppet::Error, "Could not set #{param} on #{@resource.class.name}[#{@resource.name}]: #{detail}" + raise Puppet::Error, "Could not set #{param} on #{@resource.class.name}[#{@resource.name}]: #{detail}", detail.backtrace end end end diff --git a/lib/puppet/provider/macauthorization/macauthorization.rb b/lib/puppet/provider/macauthorization/macauthorization.rb index 64eaf0574..ad431d22d 100644 --- a/lib/puppet/provider/macauthorization/macauthorization.rb +++ b/lib/puppet/provider/macauthorization/macauthorization.rb @@ -143,7 +143,7 @@ Puppet::Type.type(:macauthorization).provide :macauthorization, :parent => Puppe authdb["rules"].delete(resource[:name]) Plist::Emit.save_plist(authdb, AuthDB) rescue Errno::EACCES => e - raise Puppet::Error.new("Error saving #{AuthDB}: #{e}") + raise Puppet::Error.new("Error saving #{AuthDB}: #{e}", e) end end end @@ -188,7 +188,7 @@ Puppet::Type.type(:macauthorization).provide :macauthorization, :parent => Puppe cmds << :security << "authorizationdb" << "write" << name execute(cmds, :failonfail => false, :combine => false, :stdinfile => tmp.path.to_s) rescue Errno::EACCES => e - raise Puppet::Error.new("Cannot save right to #{tmp.path}: #{e}") + raise Puppet::Error.new("Cannot save right to #{tmp.path}: #{e}", e) ensure tmp.close tmp.unlink diff --git a/lib/puppet/provider/naginator.rb b/lib/puppet/provider/naginator.rb index c84f75c98..731c55539 100644 --- a/lib/puppet/provider/naginator.rb +++ b/lib/puppet/provider/naginator.rb @@ -23,7 +23,7 @@ class Puppet::Provider::Naginator < Puppet::Provider::ParsedFile def self.parse(text) Nagios::Parser.new.parse(text.gsub(NAME_STRING, "_naginator_name")) rescue => detail - raise Puppet::Error, "Could not parse configuration for #{resource_type.name}: #{detail}" + raise Puppet::Error, "Could not parse configuration for #{resource_type.name}: #{detail}", detail.backtrace end def self.to_file(records) diff --git a/lib/puppet/provider/nameservice.rb b/lib/puppet/provider/nameservice.rb index 8e76b67af..5bf1f4dce 100644 --- a/lib/puppet/provider/nameservice.rb +++ b/lib/puppet/provider/nameservice.rb @@ -169,7 +169,7 @@ class Puppet::Provider::NameService < Puppet::Provider execute(cmd) end rescue Puppet::ExecutionFailure => detail - raise Puppet::Error, "Could not create #{@resource.class.name} #{@resource.name}: #{detail}" + raise Puppet::Error, "Could not create #{@resource.class.name} #{@resource.name}: #{detail}", detail.backtrace end end @@ -183,7 +183,7 @@ class Puppet::Provider::NameService < Puppet::Provider begin execute(self.deletecmd) rescue Puppet::ExecutionFailure => detail - raise Puppet::Error, "Could not delete #{@resource.class.name} #{@resource.name}: #{detail}" + raise Puppet::Error, "Could not delete #{@resource.class.name} #{@resource.name}: #{detail}", detail.backtrace end end @@ -285,7 +285,7 @@ class Puppet::Provider::NameService < Puppet::Provider begin execute(cmd) rescue Puppet::ExecutionFailure => detail - raise Puppet::Error, "Could not set #{param} on #{@resource.class.name}[#{@resource.name}]: #{detail}" + raise Puppet::Error, "Could not set #{param} on #{@resource.class.name}[#{@resource.name}]: #{detail}", detail.backtrace end end end diff --git a/lib/puppet/provider/package/aix.rb b/lib/puppet/provider/package/aix.rb index 026a982f0..ae54962bc 100644 --- a/lib/puppet/provider/package/aix.rb +++ b/lib/puppet/provider/package/aix.rb @@ -113,7 +113,7 @@ Puppet::Type.type(:package).provide :aix, :parent => Puppet::Provider::Package d if hash[:pkgname] return nil else - raise Puppet::Error, "Could not list installed Packages: #{detail}" + raise Puppet::Error, "Could not list installed Packages: #{detail}", detail.backtrace end end diff --git a/lib/puppet/provider/package/gem.rb b/lib/puppet/provider/package/gem.rb index af85fc720..729f4c1c1 100644 --- a/lib/puppet/provider/package/gem.rb +++ b/lib/puppet/provider/package/gem.rb @@ -32,7 +32,7 @@ Puppet::Type.type(:package).provide :gem, :parent => Puppet::Provider::Package d map {|set| gemsplit(set) }. reject {|x| x.nil? } rescue Puppet::ExecutionFailure => detail - raise Puppet::Error, "Could not list gems: #{detail}" + raise Puppet::Error, "Could not list gems: #{detail}", detail.backtrace end if options[:justme] @@ -77,7 +77,7 @@ Puppet::Type.type(:package).provide :gem, :parent => Puppet::Provider::Package d begin uri = URI.parse(source) rescue => detail - fail "Invalid source '#{uri}': #{detail}" + fail detail, "Invalid source '#{uri}': #{detail}", detail.backtrace end case uri.scheme diff --git a/lib/puppet/provider/package/pacman.rb b/lib/puppet/provider/package/pacman.rb index d3fcdea66..9187da872 100644 --- a/lib/puppet/provider/package/pacman.rb +++ b/lib/puppet/provider/package/pacman.rb @@ -46,7 +46,7 @@ Puppet::Type.type(:package).provide :pacman, :parent => Puppet::Provider::Packag begin source_uri = URI.parse source rescue => detail - fail "Invalid source '#{source}': #{detail}" + fail detail, "Invalid source '#{source}': #{detail}", detail.backtrace end source = case source_uri.scheme diff --git a/lib/puppet/provider/package/pip.rb b/lib/puppet/provider/package/pip.rb index d91753fa5..ddbbacdd0 100644 --- a/lib/puppet/provider/package/pip.rb +++ b/lib/puppet/provider/package/pip.rb @@ -63,7 +63,7 @@ Puppet::Type.type(:package).provide :pip, result = client.call("package_releases", @resource[:name]) result.first rescue Timeout::Error => detail - raise Puppet::Error, "Timeout while contacting pypi.python.org: #{detail}"; + raise Puppet::Error, "Timeout while contacting pypi.python.org: #{detail}", detail.backtrace end # Install a package. The ensure parameter may specify installed, @@ -113,7 +113,7 @@ Puppet::Type.type(:package).provide :pip, self.class.commands :pip => pathname pip *args else - raise e, 'Could not locate the pip command.' + raise e, 'Could not locate the pip command.', e.backtrace end end end diff --git a/lib/puppet/provider/package/ports.rb b/lib/puppet/provider/package/ports.rb index 9141e30f5..ffc79d2df 100644 --- a/lib/puppet/provider/package/ports.rb +++ b/lib/puppet/provider/package/ports.rb @@ -31,7 +31,7 @@ Puppet::Type.type(:package).provide :ports, :parent => :freebsd, :source => :fre begin output = portversion(*cmd) rescue Puppet::ExecutionFailure - raise Puppet::Error.new(output) + raise Puppet::Error.new(output, $!) end line = output.split("\n").pop diff --git a/lib/puppet/provider/package/portupgrade.rb b/lib/puppet/provider/package/portupgrade.rb index 2d9f2aef1..5347a7981 100644 --- a/lib/puppet/provider/package/portupgrade.rb +++ b/lib/puppet/provider/package/portupgrade.rb @@ -45,7 +45,7 @@ Puppet::Type.type(:package).provide :portupgrade, :parent => Puppet::Provider::P begin output = portinfo(*cmdline) rescue Puppet::ExecutionFailure - raise Puppet::Error.new(output) + raise Puppet::Error.new(output, $!) return nil end @@ -87,7 +87,7 @@ Puppet::Type.type(:package).provide :portupgrade, :parent => Puppet::Provider::P begin output = portinstall(*cmdline) rescue Puppet::ExecutionFailure - raise Puppet::Error.new(output) + raise Puppet::Error.new(output, $!) end if output =~ /\*\* No such / @@ -110,7 +110,7 @@ Puppet::Type.type(:package).provide :portupgrade, :parent => Puppet::Provider::P begin output = portversion(*cmdline) rescue Puppet::ExecutionFailure - raise Puppet::Error.new(output) + raise Puppet::Error.new(output, $!) end # Check: output format. @@ -175,7 +175,7 @@ Puppet::Type.type(:package).provide :portupgrade, :parent => Puppet::Provider::P begin output = portinfo(*cmdline) rescue Puppet::ExecutionFailure - raise Puppet::Error.new(output) + raise Puppet::Error.new(output, $!) end # Check: if output isn't in the right format, return nil @@ -205,7 +205,7 @@ Puppet::Type.type(:package).provide :portupgrade, :parent => Puppet::Provider::P begin output = portinfo(*cmdline) rescue Puppet::ExecutionFailure - raise Puppet::Error.new(output) + raise Puppet::Error.new(output, $!) end if output =~ /^(\S+)/ @@ -223,7 +223,7 @@ Puppet::Type.type(:package).provide :portupgrade, :parent => Puppet::Provider::P begin output = portinfo(*cmdline) rescue Puppet::ExecutionFailure - raise Puppet::Error.new(output) + raise Puppet::Error.new(output, $!) end if output =~ /^(\S+)/ @@ -232,7 +232,7 @@ Puppet::Type.type(:package).provide :portupgrade, :parent => Puppet::Provider::P begin output = portupgrade(*cmdline) rescue Puppet::ExecutionFailure - raise Puppet::Error.new(output) + raise Puppet::Error.new(output, $!) end end end diff --git a/lib/puppet/provider/package/rpm.rb b/lib/puppet/provider/package/rpm.rb index c97ba6f44..2a3a69785 100644 --- a/lib/puppet/provider/package/rpm.rb +++ b/lib/puppet/provider/package/rpm.rb @@ -62,7 +62,7 @@ Puppet::Type.type(:package).provide :rpm, :source => :rpm, :parent => Puppet::Pr } } rescue Puppet::ExecutionFailure - raise Puppet::Error, "Failed to list packages" + raise Puppet::Error, "Failed to list packages", $!.backtrace end packages diff --git a/lib/puppet/provider/selboolean/getsetsebool.rb b/lib/puppet/provider/selboolean/getsetsebool.rb index cacc41386..d8823fe14 100644 --- a/lib/puppet/provider/selboolean/getsetsebool.rb +++ b/lib/puppet/provider/selboolean/getsetsebool.rb @@ -40,7 +40,7 @@ Puppet::Type.type(:selboolean).provide(:getsetsebool) do output = out.readlines.join('').chomp! end rescue Puppet::ExecutionFailure - raise Puppet::ExecutionFailure, output.split("\n")[0] + raise Puppet::ExecutionFailure, output.split("\n")[0], $!.backtrace end output end diff --git a/lib/puppet/provider/selmodule/semodule.rb b/lib/puppet/provider/selmodule/semodule.rb index ba864b783..dc9a77d25 100644 --- a/lib/puppet/provider/selmodule/semodule.rb +++ b/lib/puppet/provider/selmodule/semodule.rb @@ -7,7 +7,7 @@ Puppet::Type.type(:selmodule).provide(:semodule) do begin execoutput("#{command(:semodule)} --install #{selmod_name_to_filename}") rescue Puppet::ExecutionFailure => detail - raise Puppet::Error, "Could not load policy module: #{detail}"; + raise Puppet::Error, "Could not load policy module: #{detail}", detail.backtrace end :true end @@ -15,7 +15,7 @@ Puppet::Type.type(:selmodule).provide(:semodule) do def destroy execoutput("#{command(:semodule)} --remove #{@resource[:name]}") rescue Puppet::ExecutionFailure => detail - raise Puppet::Error, "Could not remove policy module: #{detail}"; + raise Puppet::Error, "Could not remove policy module: #{detail}", detail.backtrace end def exists? @@ -47,7 +47,7 @@ Puppet::Type.type(:selmodule).provide(:semodule) do def syncversion= (dosync) execoutput("#{command(:semodule)} --upgrade #{selmod_name_to_filename}") rescue Puppet::ExecutionFailure => detail - raise Puppet::Error, "Could not upgrade policy module: #{detail}"; + raise Puppet::Error, "Could not upgrade policy module: #{detail}", detail.backtrace end # Helper functions @@ -59,7 +59,7 @@ Puppet::Type.type(:selmodule).provide(:semodule) do output = out.readlines.join('').chomp! end rescue Puppet::ExecutionFailure - raise Puppet::ExecutionFailure, output.split("\n")[0] + raise Puppet::ExecutionFailure, output.split("\n")[0], $!.backtrace end output end @@ -127,7 +127,7 @@ Puppet::Type.type(:selmodule).provide(:semodule) do end end rescue Puppet::ExecutionFailure - raise Puppet::ExecutionFailure, "Could not list policy modules: #{lines.join(' ').chomp!}" + raise Puppet::ExecutionFailure, "Could not list policy modules: #{lines.join(' ').chomp!}", $!.backtrace end nil end diff --git a/lib/puppet/provider/service/base.rb b/lib/puppet/provider/service/base.rb index 0d960854f..9e37d1f61 100644 --- a/lib/puppet/provider/service/base.rb +++ b/lib/puppet/provider/service/base.rb @@ -93,7 +93,7 @@ Puppet::Type.type(:service).provide :base, :parent => :service do begin output = kill pid rescue Puppet::ExecutionFailure - @resource.fail "Could not kill #{self.name}, PID #{pid}: #{output}" + @resource.fail Puppet::Error, "Could not kill #{self.name}, PID #{pid}: #{output}", $! end return true end diff --git a/lib/puppet/provider/service/daemontools.rb b/lib/puppet/provider/service/daemontools.rb index 033b1ea46..b510f2535 100644 --- a/lib/puppet/provider/service/daemontools.rb +++ b/lib/puppet/provider/service/daemontools.rb @@ -119,7 +119,7 @@ Puppet::Type.type(:service).provide :daemontools, :parent => :base do return :running end rescue Puppet::ExecutionFailure => detail - raise Puppet::Error.new( "Could not get status for service #{resource.ref}: #{detail}" ) + raise Puppet::Error.new( "Could not get status for service #{resource.ref}: #{detail}", detail) end :stopped end @@ -132,7 +132,7 @@ Puppet::Type.type(:service).provide :daemontools, :parent => :base do system("#{command}") end rescue Puppet::ExecutionFailure => detail - raise Puppet::Error.new( "Cannot config #{self.service} to enable it: #{detail}" ) + raise Puppet::Error.new( "Cannot config #{self.service} to enable it: #{detail}", detail) end def enabled? @@ -158,7 +158,7 @@ Puppet::Type.type(:service).provide :daemontools, :parent => :base do end end rescue Puppet::ExecutionFailure - raise Puppet::Error.new( "No daemon directory found for #{self.service}") + raise Puppet::Error.new( "No daemon directory found for #{self.service}", $!) end def disable @@ -174,7 +174,7 @@ rescue Puppet::ExecutionFailure end end rescue Puppet::ExecutionFailure - raise Puppet::Error.new( "No daemon directory found for #{self.service}") + raise Puppet::Error.new( "No daemon directory found for #{self.service}", $!) end self.stop end diff --git a/lib/puppet/provider/service/gentoo.rb b/lib/puppet/provider/service/gentoo.rb index f4750eb71..4fecbde9d 100644 --- a/lib/puppet/provider/service/gentoo.rb +++ b/lib/puppet/provider/service/gentoo.rb @@ -15,7 +15,7 @@ Puppet::Type.type(:service).provide :gentoo, :parent => :init do def disable output = update :del, @resource[:name], :default rescue Puppet::ExecutionFailure - raise Puppet::Error, "Could not disable #{self.name}: #{output}" + raise Puppet::Error, "Could not disable #{self.name}: #{output}", $!.backtrace end def enabled? @@ -40,6 +40,6 @@ Puppet::Type.type(:service).provide :gentoo, :parent => :init do def enable output = update :add, @resource[:name], :default rescue Puppet::ExecutionFailure - raise Puppet::Error, "Could not enable #{self.name}: #{output}" + raise Puppet::Error, "Could not enable #{self.name}: #{output}", $!.backtrace end end diff --git a/lib/puppet/provider/service/launchd.rb b/lib/puppet/provider/service/launchd.rb index 316fdbf46..2f8afacba 100644 --- a/lib/puppet/provider/service/launchd.rb +++ b/lib/puppet/provider/service/launchd.rb @@ -171,7 +171,7 @@ Puppet::Type.type(:service).provide :launchd, :parent => :base do @job_list[line.split(/\s/).last] = :running end rescue Puppet::ExecutionFailure - raise Puppet::Error.new("Unable to determine status of #{resource[:name]}") + raise Puppet::Error.new("Unable to determine status of #{resource[:name]}", $!) end @job_list end @@ -222,7 +222,7 @@ Puppet::Type.type(:service).provide :launchd, :parent => :base do @macosx_version_major = product_version_major return @macosx_version_major rescue Puppet::ExecutionFailure => detail - fail("Could not determine OS X version: #{detail}") + fail(detail, "Could not determine OS X version: #{detail}", detail.backtrace) end end @@ -257,7 +257,7 @@ Puppet::Type.type(:service).provide :launchd, :parent => :base do begin execute(cmds) rescue Puppet::ExecutionFailure - raise Puppet::Error.new("Unable to start service: #{resource[:name]} at path: #{job_path}") + raise Puppet::Error.new("Unable to start service: #{resource[:name]} at path: #{job_path}", $!) end # As load -w clears the Disabled flag, we need to add it in after self.disable if did_enable_job and resource[:enable] == :false @@ -278,7 +278,7 @@ Puppet::Type.type(:service).provide :launchd, :parent => :base do begin execute(cmds) rescue Puppet::ExecutionFailure - raise Puppet::Error.new("Unable to stop service: #{resource[:name]} at path: #{job_path}") + raise Puppet::Error.new("Unable to stop service: #{resource[:name]} at path: #{job_path}", $!) end # As unload -w sets the Disabled flag, we need to add it in after self.enable if did_disable_job and resource[:enable] == :true diff --git a/lib/puppet/provider/service/redhat.rb b/lib/puppet/provider/service/redhat.rb index c1c6401c1..7d07b9289 100644 --- a/lib/puppet/provider/service/redhat.rb +++ b/lib/puppet/provider/service/redhat.rb @@ -17,7 +17,7 @@ Puppet::Type.type(:service).provide :redhat, :parent => :init, :source => :init # service in run levels 0, 1 and/or 6 output = chkconfig("--level", "0123456", @resource[:name], :off) rescue Puppet::ExecutionFailure - raise Puppet::Error, "Could not disable #{self.name}: #{output}" + raise Puppet::Error, "Could not disable #{self.name}: #{output}", $!.backtrace end def enabled? @@ -38,7 +38,7 @@ Puppet::Type.type(:service).provide :redhat, :parent => :init, :source => :init def enable chkconfig(@resource[:name], :on) rescue Puppet::ExecutionFailure => detail - raise Puppet::Error, "Could not enable #{self.name}: #{detail}" + raise Puppet::Error, "Could not enable #{self.name}: #{detail}", detail.backtrace end def initscript diff --git a/lib/puppet/provider/service/runit.rb b/lib/puppet/provider/service/runit.rb index 8395c6da3..f73c70929 100644 --- a/lib/puppet/provider/service/runit.rb +++ b/lib/puppet/provider/service/runit.rb @@ -73,7 +73,7 @@ Puppet::Type.type(:service).provide :runit, :parent => :daemontools do return :running if output =~ /^run: / rescue Puppet::ExecutionFailure => detail unless detail.message =~ /(warning: |runsv not running$)/ - raise Puppet::Error.new( "Could not get status for service #{resource.ref}: #{detail}" ) + raise Puppet::Error.new( "Could not get status for service #{resource.ref}: #{detail}", detail ) end end :stopped diff --git a/lib/puppet/provider/service/service.rb b/lib/puppet/provider/service/service.rb index 27f9b6373..27081a878 100644 --- a/lib/puppet/provider/service/service.rb +++ b/lib/puppet/provider/service/service.rb @@ -25,7 +25,7 @@ Puppet::Type.type(:service).provide :service do # #565: Services generally produce no output, so squelch them. execute(command, :failonfail => fof, :override_locale => false, :squelch => true) rescue Puppet::ExecutionFailure => detail - @resource.fail "Could not #{type} #{@resource.ref}: #{detail}" + @resource.fail Puppet::Error, "Could not #{type} #{@resource.ref}: #{detail}", detail end nil end diff --git a/lib/puppet/provider/service/smf.rb b/lib/puppet/provider/service/smf.rb index 25ae551de..fd7793f4e 100644 --- a/lib/puppet/provider/service/smf.rb +++ b/lib/puppet/provider/service/smf.rb @@ -28,7 +28,7 @@ Puppet::Type.type(:service).provide :smf, :parent => :base do end end rescue Puppet::ExecutionFailure => detail - raise Puppet::Error.new( "Cannot config #{self.name} to enable it: #{detail}" ) + raise Puppet::Error.new( "Cannot config #{self.name} to enable it: #{detail}", detail ) end def self.instances diff --git a/lib/puppet/provider/service/src.rb b/lib/puppet/provider/service/src.rb index 8028751ff..e4bcbd9b6 100644 --- a/lib/puppet/provider/service/src.rb +++ b/lib/puppet/provider/service/src.rb @@ -87,12 +87,12 @@ Puppet::Type.type(:service).provide :src, :parent => :base do end return :true rescue Puppet::ExecutionFailure => detail - raise Puppet::Error.new("Unable to restart service #{@resource[:name]}, error was: #{detail}" ) + raise Puppet::Error.new("Unable to restart service #{@resource[:name]}, error was: #{detail}", detail ) end end self.fail("No such service found") rescue Puppet::ExecutionFailure => detail - raise Puppet::Error.new("Cannot get status of #{@resource[:name]}, error was: #{detail}" ) + raise Puppet::Error.new("Cannot get status of #{@resource[:name]}, error was: #{detail}", detail ) end def status @@ -113,7 +113,7 @@ Puppet::Type.type(:service).provide :src, :parent => :base do end self.fail("No such service found") rescue Puppet::ExecutionFailure => detail - raise Puppet::Error.new("Cannot get status of #{@resource[:name]}, error was: #{detail}" ) + raise Puppet::Error.new("Cannot get status of #{@resource[:name]}, error was: #{detail}", detail ) end end diff --git a/lib/puppet/provider/service/systemd.rb b/lib/puppet/provider/service/systemd.rb index 1ff070b86..1c51236a4 100644 --- a/lib/puppet/provider/service/systemd.rb +++ b/lib/puppet/provider/service/systemd.rb @@ -22,7 +22,7 @@ Puppet::Type.type(:service).provide :systemd, :parent => :base do def disable output = systemctl(:disable, @resource[:name]) rescue Puppet::ExecutionFailure - raise Puppet::Error, "Could not disable #{self.name}: #{output}" + raise Puppet::Error, "Could not disable #{self.name}: #{output}", $!.backtrace end def enabled? @@ -47,7 +47,7 @@ Puppet::Type.type(:service).provide :systemd, :parent => :base do def enable output = systemctl("enable", @resource[:name]) rescue Puppet::ExecutionFailure - raise Puppet::Error, "Could not enable #{self.name}: #{output}" + raise Puppet::Error, "Could not enable #{self.name}: #{output}", $!.backtrace end def restartcmd diff --git a/lib/puppet/provider/service/windows.rb b/lib/puppet/provider/service/windows.rb index 1e58843bf..c084ffbc9 100644 --- a/lib/puppet/provider/service/windows.rb +++ b/lib/puppet/provider/service/windows.rb @@ -22,21 +22,21 @@ Puppet::Type.type(:service).provide :windows, :parent => :service do w32ss = Win32::Service.configure( 'service_name' => @resource[:name], 'start_type' => Win32::Service::SERVICE_AUTO_START ) raise Puppet::Error.new("Win32 service enable of #{@resource[:name]} failed" ) if( w32ss.nil? ) rescue Win32::Service::Error => detail - raise Puppet::Error.new("Cannot enable #{@resource[:name]}, error was: #{detail}" ) + raise Puppet::Error.new("Cannot enable #{@resource[:name]}, error was: #{detail}", detail ) end def disable w32ss = Win32::Service.configure( 'service_name' => @resource[:name], 'start_type' => Win32::Service::SERVICE_DISABLED ) raise Puppet::Error.new("Win32 service disable of #{@resource[:name]} failed" ) if( w32ss.nil? ) rescue Win32::Service::Error => detail - raise Puppet::Error.new("Cannot disable #{@resource[:name]}, error was: #{detail}" ) + raise Puppet::Error.new("Cannot disable #{@resource[:name]}, error was: #{detail}", detail ) end def manual_start w32ss = Win32::Service.configure( 'service_name' => @resource[:name], 'start_type' => Win32::Service::SERVICE_DEMAND_START ) raise Puppet::Error.new("Win32 service manual enable of #{@resource[:name]} failed" ) if( w32ss.nil? ) rescue Win32::Service::Error => detail - raise Puppet::Error.new("Cannot enable #{@resource[:name]} for manual start, error was: #{detail}" ) + raise Puppet::Error.new("Cannot enable #{@resource[:name]} for manual start, error was: #{detail}", detail ) end def enabled? @@ -56,7 +56,7 @@ Puppet::Type.type(:service).provide :windows, :parent => :service do raise Puppet::Error.new("Unknown start type: #{w32ss.start_type}") end rescue Win32::Service::Error => detail - raise Puppet::Error.new("Cannot get start type for #{@resource[:name]}, error was: #{detail}" ) + raise Puppet::Error.new("Cannot get start type for #{@resource[:name]}, error was: #{detail}", detail ) end def start @@ -75,13 +75,13 @@ Puppet::Type.type(:service).provide :windows, :parent => :service do net(:start, @resource[:name]) rescue Puppet::ExecutionFailure => detail - raise Puppet::Error.new("Cannot start #{@resource[:name]}, error was: #{detail}" ) + raise Puppet::Error.new("Cannot start #{@resource[:name]}, error was: #{detail}", detail ) end def stop net(:stop, @resource[:name]) rescue Puppet::ExecutionFailure => detail - raise Puppet::Error.new("Cannot stop #{@resource[:name]}, error was: #{detail}" ) + raise Puppet::Error.new("Cannot stop #{@resource[:name]}, error was: #{detail}", detail ) end def status @@ -96,7 +96,7 @@ Puppet::Type.type(:service).provide :windows, :parent => :service do debug("Service #{@resource[:name]} is #{w32ss.current_state}") return state rescue Win32::Service::Error => detail - raise Puppet::Error.new("Cannot get status of #{@resource[:name]}, error was: #{detail}" ) + raise Puppet::Error.new("Cannot get status of #{@resource[:name]}, error was: #{detail}", detail ) end # returns all providers for all existing services and startup state diff --git a/lib/puppet/provider/user/aix.rb b/lib/puppet/provider/user/aix.rb index 06eae6f64..1fc25deab 100644 --- a/lib/puppet/provider/user/aix.rb +++ b/lib/puppet/provider/user/aix.rb @@ -271,7 +271,7 @@ Puppet::Type.type(:user).provide :aix, :parent => Puppet::Provider::AixObject do raise Puppet::ExecutionFailure, "chpasswd said #{output}" end rescue Puppet::ExecutionFailure => detail - raise Puppet::Error, "Could not set #{param} on #{@resource.class.name}[#{@resource.name}]: #{detail}" + raise Puppet::Error, "Could not set #{param} on #{@resource.class.name}[#{@resource.name}]: #{detail}", detail.backtrace ensure tmpfile.delete() end @@ -311,7 +311,7 @@ Puppet::Type.type(:user).provide :aix, :parent => Puppet::Provider::AixObject do begin execute(cmd) rescue Puppet::ExecutionFailure => detail - raise Puppet::Error, "Could not set #{param} on #{@resource.class.name}[#{@resource.name}]: #{detail}" + raise Puppet::Error, "Could not set #{param} on #{@resource.class.name}[#{@resource.name}]: #{detail}", detail.backtrace end end end diff --git a/lib/puppet/provider/user/directoryservice.rb b/lib/puppet/provider/user/directoryservice.rb index a4ae31b8c..e93663fca 100644 --- a/lib/puppet/provider/user/directoryservice.rb +++ b/lib/puppet/provider/user/directoryservice.rb @@ -455,14 +455,14 @@ Puppet::Type.type(:user).provide :directoryservice do dscl '.', '-change', "/Users/#{resource.name}", self.class.ns_to_ds_attribute_map[setter_method.intern], @property_hash[setter_method.intern], value rescue Puppet::ExecutionFailure => e raise Puppet::Error, "Cannot set the #{setter_method} value of '#{value}' for user " + - "#{@resource.name} due to the following error: #{e.inspect}" + "#{@resource.name} due to the following error: #{e.inspect}", e.backtrace end else begin dscl '.', '-merge', "/Users/#{resource.name}", self.class.ns_to_ds_attribute_map[setter_method.intern], value rescue Puppet::ExecutionFailure => e raise Puppet::Error, "Cannot set the #{setter_method} value of '#{value}' for user " + - "#{@resource.name} due to the following error: #{e.inspect}" + "#{@resource.name} due to the following error: #{e.inspect}", e.backtrace end end end @@ -486,7 +486,7 @@ Puppet::Type.type(:user).provide :directoryservice do begin dscl '.', '-merge', "/#{path}/#{username}", keyname, value rescue Puppet::ExecutionFailure => detail - raise Puppet::Error, "Could not set the dscl #{keyname} key with value: #{value} - #{detail.inspect}" + raise Puppet::Error, "Could not set the dscl #{keyname} key with value: #{value} - #{detail.inspect}", detail.backtrace end end @@ -643,7 +643,7 @@ Puppet::Type.type(:user).provide :directoryservice do begin File.open(filename, 'w') { |f| f.write(value)} rescue Errno::EACCES => detail - raise Puppet::Error, "Could not write to file #{filename}: #{detail}" + raise Puppet::Error, "Could not write to file #{filename}: #{detail}", detail.backtrace end end diff --git a/lib/puppet/provider/user/user_role_add.rb b/lib/puppet/provider/user/user_role_add.rb index 3ec49507d..4d240b964 100644 --- a/lib/puppet/provider/user/user_role_add.rb +++ b/lib/puppet/provider/user/user_role_add.rb @@ -68,7 +68,7 @@ Puppet::Type.type(:user).provide :user_role_add, :parent => :useradd, :source => def run(cmd, msg) execute(cmd) rescue Puppet::ExecutionFailure => detail - raise Puppet::Error, "Could not #{msg} #{@resource.class.name} #{@resource.name}: #{detail}" + raise Puppet::Error, "Could not #{msg} #{@resource.class.name} #{@resource.name}: #{detail}", detail.backtrace end def transition(type) @@ -203,7 +203,7 @@ Puppet::Type.type(:user).provide :user_role_add, :parent => :useradd, :source => end end rescue => detail - fail "Could not write replace #{target_file_path}: #{detail}" + fail detail, "Could not write replace #{target_file_path}: #{detail}", detail.backtrace end end end diff --git a/lib/puppet/provider/zone/solaris.rb b/lib/puppet/provider/zone/solaris.rb index 935495252..e681aca75 100644 --- a/lib/puppet/provider/zone/solaris.rb +++ b/lib/puppet/provider/zone/solaris.rb @@ -268,7 +268,7 @@ Puppet::Type.type(:zone).provide(:solaris) do end rescue => detail puts detail.stacktrace if Puppet[:debug] - raise Puppet::Error, "Could not create sysidcfg: #{detail}" + raise Puppet::Error, "Could not create sysidcfg: #{detail}", detail.backtrace end end end @@ -345,7 +345,7 @@ Puppet::Type.type(:zone).provide(:solaris) do def zoneadm(*cmd) adm("-z", @resource[:name], *cmd) rescue Puppet::ExecutionFailure => detail - self.fail "Could not #{cmd[0]} zone: #{detail}" + self.fail Puppet::Error, "Could not #{cmd[0]} zone: #{detail}", detail end def zonecfg(*cmd) @@ -354,7 +354,7 @@ Puppet::Type.type(:zone).provide(:solaris) do begin cfg("-z", self.name, *cmd) rescue Puppet::ExecutionFailure => detail - self.fail "Could not #{cmd[0]} zone: #{detail}" + self.fail Puppet::Error, "Could not #{cmd[0]} zone: #{detail}", detail end end end diff --git a/lib/puppet/rails.rb b/lib/puppet/rails.rb index 1ea064dd2..2c97c02c6 100644 --- a/lib/puppet/rails.rb +++ b/lib/puppet/rails.rb @@ -35,7 +35,7 @@ module Puppet::Rails rescue => detail message = "Could not connect to database: #{detail}" Puppet.log_exception(detail, message) - raise Puppet::Error, message + raise Puppet::Error, message, detail.backtrace end end @@ -107,7 +107,7 @@ module Puppet::Rails rescue => detail message = "Could not migrate database: #{detail}" Puppet.log_exception(detail, message) - raise Puppet::Error, "Could not migrate database: #{detail}" + raise Puppet::Error, "Could not migrate database: #{detail}", detail.backtrace end end @@ -122,7 +122,7 @@ module Puppet::Rails ActiveRecord::Base.establish_connection(database_arguments) rescue => detail Puppet.log_exception(detail) - raise Puppet::Error, "Could not connect to database: #{detail}" + raise Puppet::Error, "Could not connect to database: #{detail}", detail.backtrace end ActiveRecord::Base.connection.tables.each do |t| diff --git a/lib/puppet/reports/tagmail.rb b/lib/puppet/reports/tagmail.rb index 1feab4879..eb9888edc 100644 --- a/lib/puppet/reports/tagmail.rb +++ b/lib/puppet/reports/tagmail.rb @@ -148,7 +148,7 @@ Puppet::Reports.register_report(:tagmail) do rescue => detail message = "Could not send report emails through smtp: #{detail}" Puppet.log_exception(detail, message) - raise Puppet::Error, message + raise Puppet::Error, message, detail.backtrace end elsif Puppet[:sendmail] != "" begin @@ -165,7 +165,7 @@ Puppet::Reports.register_report(:tagmail) do rescue => detail message = "Could not send report emails via sendmail: #{detail}" Puppet.log_exception(detail, message) - raise Puppet::Error, message + raise Puppet::Error, message, detail.backtrace end else raise Puppet::Error, "SMTP server is unset and could not find sendmail" diff --git a/lib/puppet/resource/type_collection.rb b/lib/puppet/resource/type_collection.rb index 4ca8a9689..d212199dc 100644 --- a/lib/puppet/resource/type_collection.rb +++ b/lib/puppet/resource/type_collection.rb @@ -148,7 +148,7 @@ class Puppet::Resource::TypeCollection @version rescue Puppet::ExecutionFailure => e - raise Puppet::ParseError, "Execution of config_version command `#{environment[:config_version]}` failed: #{e.message}" + raise Puppet::ParseError, "Execution of config_version command `#{environment[:config_version]}` failed: #{e.message}", e.backtrace end def watch_file(filename) diff --git a/lib/puppet/settings.rb b/lib/puppet/settings.rb index 36347b756..c37a7010f 100644 --- a/lib/puppet/settings.rb +++ b/lib/puppet/settings.rb @@ -297,7 +297,7 @@ class Puppet::Settings begin setting.handle(self.value(setting.name)) rescue InterpolationError => err - raise err unless options[:ignore_interpolation_dependency_errors] + raise InterpolationError, err, err.backtrace unless options[:ignore_interpolation_dependency_errors] #swallow. We're not concerned if we can't call hooks because dependencies don't exist yet #we'll get another chance after application defaults are initialized end @@ -1079,9 +1079,9 @@ Generated on #{Time.now}. begin return File.read(file) rescue Errno::ENOENT - raise ArgumentError, "No such file #{file}" + raise ArgumentError, "No such file #{file}", $!.backtrace rescue Errno::EACCES - raise ArgumentError, "Permission denied to file #{file}" + raise ArgumentError, "Permission denied to file #{file}", $!.backtrace end end diff --git a/lib/puppet/ssl/certificate_authority.rb b/lib/puppet/ssl/certificate_authority.rb index 2738c2262..5453e7b66 100644 --- a/lib/puppet/ssl/certificate_authority.rb +++ b/lib/puppet/ssl/certificate_authority.rb @@ -174,7 +174,7 @@ class Puppet::SSL::CertificateAuthority begin Puppet.settings.setting(:capass).open('w') { |f| f.print pass } rescue Errno::EACCES => detail - raise Puppet::Error, "Could not write CA password: #{detail}" + raise Puppet::Error, "Could not write CA password: #{detail}", detail.backtrace end @password = pass @@ -408,7 +408,7 @@ class Puppet::SSL::CertificateAuthority private :create_x509_store # Utility method which is API for PE license checking. - # This is used rather than `verify` because + # This is used rather than `verify` because # 1) We have already read the certificate from disk into memory. # To read the certificate from disk again is just wasteful. # 2) Because we're checking a large number of certificates against diff --git a/lib/puppet/ssl/certificate_request.rb b/lib/puppet/ssl/certificate_request.rb index cd68eb34a..4afe01b0c 100644 --- a/lib/puppet/ssl/certificate_request.rb +++ b/lib/puppet/ssl/certificate_request.rb @@ -205,7 +205,7 @@ DOC csr.add_attribute(OpenSSL::X509::Attribute.new(oid, attr_set)) Puppet.debug("Added csr attribute: #{oid} => #{attr_set.inspect}") rescue OpenSSL::X509::AttributeError => e - raise Puppet::Error, "Cannot create CSR with attribute #{oid}: #{e.message}" + raise Puppet::Error, "Cannot create CSR with attribute #{oid}: #{e.message}", e.backtrace end end end @@ -230,7 +230,7 @@ DOC ext = OpenSSL::X509::Extension.new(oid, value.to_s, false) extensions << ext rescue OpenSSL::X509::ExtensionError => e - raise Puppet::Error, "Cannot create CSR with extension request #{oid}: #{e.message}" + raise Puppet::Error, "Cannot create CSR with extension request #{oid}: #{e.message}", e.backtrace end end end diff --git a/lib/puppet/type/exec.rb b/lib/puppet/type/exec.rb index 81730848e..f42104245 100644 --- a/lib/puppet/type/exec.rb +++ b/lib/puppet/type/exec.rb @@ -117,7 +117,7 @@ module Puppet end end rescue Timeout::Error - self.fail "Command exceeded timeout" % value.inspect + self.fail Puppet::Error, "Command exceeded timeout" % value.inspect, $!.backtrace end if log = @resource[:logoutput] @@ -265,7 +265,7 @@ module Puppet begin value = Float(value) rescue ArgumentError - raise ArgumentError, "The timeout must be a number." + raise ArgumentError, "The timeout must be a number.", $!.backtrace end [value, 0.0].max end diff --git a/lib/puppet/type/file/content.rb b/lib/puppet/type/file/content.rb index 8a2d3c423..73f921403 100644 --- a/lib/puppet/type/file/content.rb +++ b/lib/puppet/type/file/content.rb @@ -126,7 +126,7 @@ module Puppet begin resource.parameter(:checksum).sum_file(resource[:path]) rescue => detail - raise Puppet::Error, "Could not read #{ftype} #{@resource.title}: #{detail}" + raise Puppet::Error, "Could not read #{ftype} #{@resource.title}: #{detail}", detail.backtrace end end @@ -233,7 +233,7 @@ module Puppet dipper.getfile(sum) rescue => detail - fail "Could not retrieve content for #{should} from filebucket: #{detail}" + fail detail, "Could not retrieve content for #{should} from filebucket: #{detail}", detail.backtrace end end end diff --git a/lib/puppet/type/file/source.rb b/lib/puppet/type/file/source.rb index 180ec0172..30941efbe 100644 --- a/lib/puppet/type/file/source.rb +++ b/lib/puppet/type/file/source.rb @@ -58,7 +58,7 @@ module Puppet begin uri = URI.parse(URI.escape(source)) rescue => detail - self.fail "Could not understand source #{source}: #{detail}" + self.fail Puppet::Error, "Could not understand source #{source}: #{detail}", detail.backtrace end self.fail "Cannot use relative URLs '#{source}'" unless uri.absolute? @@ -175,7 +175,7 @@ module Puppet break end rescue => detail - fail detail, "Could not retrieve file metadata for #{source}: #{detail}" + fail detail, "Could not retrieve file metadata for #{source}: #{detail}", detail.backtrace end end fail "Could not retrieve information from environment #{resource.catalog.environment} source(s) #{value.join(", ")}" unless @metadata diff --git a/lib/puppet/type/package.rb b/lib/puppet/type/package.rb index c9951b62f..943871608 100644 --- a/lib/puppet/type/package.rb +++ b/lib/puppet/type/package.rb @@ -95,7 +95,7 @@ module Puppet begin provider.update rescue => detail - self.fail "Could not update: #{detail}" + self.fail Puppet::Error, "Could not update: #{detail}", detail.backtrace end if current == :absent @@ -109,7 +109,7 @@ module Puppet begin provider.install rescue => detail - self.fail "Could not update: #{detail}" + self.fail Puppet::Error, "Could not update: #{detail}", detail.backtrace end if self.retrieve == :absent diff --git a/lib/puppet/type/zone.rb b/lib/puppet/type/zone.rb index 6c010f249..7226623da 100644 --- a/lib/puppet/type/zone.rb +++ b/lib/puppet/type/zone.rb @@ -330,7 +330,7 @@ end def validate_ip(ip, name) IPAddr.new(ip) if ip rescue ArgumentError - self.fail "'#{ip}' is an invalid #{name}" + self.fail Puppet::Error, "'#{ip}' is an invalid #{name}", $!.backtrace end def validate_exclusive(interface, address, router) diff --git a/lib/puppet/util.rb b/lib/puppet/util.rb index 628de3b2d..a8b78da7c 100644 --- a/lib/puppet/util.rb +++ b/lib/puppet/util.rb @@ -263,7 +263,7 @@ module Util begin URI::Generic.build(params) rescue => detail - raise Puppet::Error, "Failed to convert '#{path}' to URI: #{detail}" + raise Puppet::Error, "Failed to convert '#{path}' to URI: #{detail}", detail.backtrace end end module_function :path_to_uri diff --git a/lib/puppet/util/adsi.rb b/lib/puppet/util/adsi.rb index 98639eabd..91debc756 100644 --- a/lib/puppet/util/adsi.rb +++ b/lib/puppet/util/adsi.rb @@ -12,7 +12,7 @@ module Puppet::Util::ADSI begin WIN32OLE.connect(uri) rescue Exception => e - raise Puppet::Error.new( "ADSI connection error: #{e}" ) + raise Puppet::Error.new( "ADSI connection error: #{e}", e ) end end @@ -121,7 +121,7 @@ module Puppet::Util::ADSI begin native_user.SetInfo unless native_user.nil? rescue Exception => e - raise Puppet::Error.new( "User update failed: #{e}" ) + raise Puppet::Error.new( "User update failed: #{e}", e ) end self end @@ -251,7 +251,7 @@ module Puppet::Util::ADSI begin native_group.SetInfo unless native_group.nil? rescue Exception => e - raise Puppet::Error.new( "Group update failed: #{e}" ) + raise Puppet::Error.new( "Group update failed: #{e}", e ) end self end diff --git a/lib/puppet/util/autoload.rb b/lib/puppet/util/autoload.rb index 166dab51f..0662704b5 100644 --- a/lib/puppet/util/autoload.rb +++ b/lib/puppet/util/autoload.rb @@ -65,7 +65,7 @@ class Puppet::Util::Autoload rescue Exception => detail message = "Could not autoload #{name}: #{detail}" Puppet.log_exception(detail, message) - raise Puppet::Error, message + raise Puppet::Error, message, detail.backtrace end end diff --git a/lib/puppet/util/backups.rb b/lib/puppet/util/backups.rb index 24161ff15..461563569 100644 --- a/lib/puppet/util/backups.rb +++ b/lib/puppet/util/backups.rb @@ -46,7 +46,7 @@ module Puppet::Util::Backups rescue => detail # since they said they want a backup, let's error out # if we couldn't make one - self.fail "Could not back #{file} up: #{detail.message}" + self.fail Puppet::Error, "Could not back #{file} up: #{detail.message}", detail.backtrace end end @@ -74,7 +74,7 @@ module Puppet::Util::Backups rescue => detail message = "Could not remove old backup: #{detail}" self.log_exception(detail, message) - self.fail message + self.fail Puppet::Error, message, detail.backtrace end end diff --git a/lib/puppet/util/command_line/trollop.rb b/lib/puppet/util/command_line/trollop.rb index ed1246c0e..bce1ea436 100644 --- a/lib/puppet/util/command_line/trollop.rb +++ b/lib/puppet/util/command_line/trollop.rb @@ -455,7 +455,7 @@ class Parser end time ? Date.new(time.year, time.month, time.day) : Date.parse(param) rescue ArgumentError - raise CommandlineError, "option '#{arg}' needs a date" + raise CommandlineError, "option '#{arg}' needs a date", $!.backtrace end end @@ -651,7 +651,7 @@ private begin open param rescue SystemCallError => e - raise CommandlineError, "file or url for option '#{arg}' cannot be opened: #{e.message}" + raise CommandlineError, "file or url for option '#{arg}' cannot be opened: #{e.message}", e.backtrace end end end diff --git a/lib/puppet/util/errors.rb b/lib/puppet/util/errors.rb index a03e3a8bb..d10336dc7 100644 --- a/lib/puppet/util/errors.rb +++ b/lib/puppet/util/errors.rb @@ -85,6 +85,12 @@ module Puppet::Util::Errors # the given arguments. # @param [Class] type of error # @param [String] message error message(s) + # @overload fail(error_klass, message, ..) + # Throw an exception of type error_klass with a message concatenated from + # the given arguments. + # @param [Class] type of error + # @param [String] message error message(s) + # @param [Exception] original exception, source of backtrace info def fail(*args) if args[0].is_a?(Class) type = args.shift @@ -92,7 +98,8 @@ module Puppet::Util::Errors type = Puppet::Error end - error = adderrorcontext(type.new(args.join(" "))) + other = args.count > 1 ? args.pop : nil + error = adderrorcontext(type.new(args.join(" ")), other) raise error end diff --git a/lib/puppet/util/execution.rb b/lib/puppet/util/execution.rb index 411f462c5..0fc4733e2 100644 --- a/lib/puppet/util/execution.rb +++ b/lib/puppet/util/execution.rb @@ -94,7 +94,7 @@ module Puppet::Util::Execution output = execute(command) return output rescue Puppet::ExecutionFailure - raise exception, output + raise exception, output, exception.backtrace end # Default empty options for {execute} diff --git a/lib/puppet/util/filetype.rb b/lib/puppet/util/filetype.rb index 28900420a..9fc3b289a 100644 --- a/lib/puppet/util/filetype.rb +++ b/lib/puppet/util/filetype.rb @@ -47,7 +47,7 @@ class Puppet::Util::FileType rescue => detail message = "#{self.class} could not read #{@path}: #{detail}" Puppet.log_exception(detail, message) - raise Puppet::Error, message + raise Puppet::Error, message, detail.backtrace end end @@ -63,7 +63,7 @@ class Puppet::Util::FileType rescue => detail message = "#{self.class} could not write #{@path}: #{detail}" Puppet.log_exception(detail, message) - raise Puppet::Error, message + raise Puppet::Error, message, detail.backtrace end end end diff --git a/lib/puppet/util/ldap/connection.rb b/lib/puppet/util/ldap/connection.rb index 9f951ce10..c5110921c 100644 --- a/lib/puppet/util/ldap/connection.rb +++ b/lib/puppet/util/ldap/connection.rb @@ -66,6 +66,6 @@ class Puppet::Util::Ldap::Connection @connection.set_option(LDAP::LDAP_OPT_REFERRALS, LDAP::LDAP_OPT_ON) @connection.simple_bind(user, password) rescue => detail - raise Puppet::Error, "Could not connect to LDAP: #{detail}" + raise Puppet::Error, "Could not connect to LDAP: #{detail}", detail.backtrace end end diff --git a/lib/puppet/util/log/destinations.rb b/lib/puppet/util/log/destinations.rb index 32041066a..932007099 100644 --- a/lib/puppet/util/log/destinations.rb +++ b/lib/puppet/util/log/destinations.rb @@ -18,7 +18,7 @@ Puppet::Util::Log.newdesttype :syslog do begin facility = Syslog.const_get("LOG_#{str.upcase}") rescue NameError - raise Puppet::Error, "Invalid syslog facility #{str}" + raise Puppet::Error, "Invalid syslog facility #{str}", $!.backtrace end @syslog = Syslog.open(name, options, facility) diff --git a/lib/puppet/util/metric.rb b/lib/puppet/util/metric.rb index 86df95c13..7b07a181d 100644 --- a/lib/puppet/util/metric.rb +++ b/lib/puppet/util/metric.rb @@ -71,7 +71,7 @@ class Puppet::Util::Metric RRD.create( self.path, '-s', Puppet[:rrdinterval].to_s, '-b', start.to_i.to_s, *args) end rescue => detail - raise "Could not create RRD file #{path}: #{detail}" + raise detail, "Could not create RRD file #{path}: #{detail}", detail.backtrace end end @@ -185,7 +185,7 @@ class Puppet::Util::Metric end #system("rrdtool updatev #{self.path} '#{arg}'") rescue => detail - raise Puppet::Error, "Failed to update #{self.name}: #{detail}" + raise Puppet::Error, "Failed to update #{self.name}: #{detail}", detail.backtrace end end diff --git a/lib/puppet/util/network_device.rb b/lib/puppet/util/network_device.rb index 2d0f94f4f..60da0363a 100644 --- a/lib/puppet/util/network_device.rb +++ b/lib/puppet/util/network_device.rb @@ -7,7 +7,7 @@ class Puppet::Util::NetworkDevice require "puppet/util/network_device/#{device.provider}/device" @current = Puppet::Util::NetworkDevice.const_get(device.provider.capitalize).const_get(:Device).new(device.url, device.options) rescue => detail - raise "Can't load #{device.provider} for #{device.name}: #{detail}" + raise detail, "Can't load #{device.provider} for #{device.name}: #{detail}", detail.backtrace end # Should only be used in tests diff --git a/lib/puppet/util/network_device/transport/ssh.rb b/lib/puppet/util/network_device/transport/ssh.rb index cc613bda0..1c20eb822 100644 --- a/lib/puppet/util/network_device/transport/ssh.rb +++ b/lib/puppet/util/network_device/transport/ssh.rb @@ -33,11 +33,11 @@ class Puppet::Util::NetworkDevice::Transport::Ssh < Puppet::Util::NetworkDevice: Puppet.debug("connecting to #{host} as #{user}") @ssh = Net::SSH.start(host, user, :port => port, :password => password, :timeout => timeout) rescue TimeoutError - raise TimeoutError, "timed out while opening an ssh connection to the host" + raise TimeoutError, "timed out while opening an ssh connection to the host", $!.backtrace rescue Net::SSH::AuthenticationFailed - raise Puppet::Error, "SSH authentication failure connecting to #{host} as #{user}" + raise Puppet::Error, "SSH authentication failure connecting to #{host} as #{user}", $!.backtrace rescue Net::SSH::Exception - raise Puppet::Error, "SSH connection failure to #{host}" + raise Puppet::Error, "SSH connection failure to #{host}", $!.backtrace end @buf = "" diff --git a/lib/puppet/util/queue/stomp.rb b/lib/puppet/util/queue/stomp.rb index 4a7081bc7..4ed91e1e9 100644 --- a/lib/puppet/util/queue/stomp.rb +++ b/lib/puppet/util/queue/stomp.rb @@ -17,7 +17,7 @@ class Puppet::Util::Queue::Stomp begin uri = URI.parse(Puppet[:queue_source]) rescue => detail - raise ArgumentError, "Could not create Stomp client instance - queue source #{Puppet[:queue_source]} is invalid: #{detail}" + raise ArgumentError, "Could not create Stomp client instance - queue source #{Puppet[:queue_source]} is invalid: #{detail}", detail.backtrace end unless uri.scheme == "stomp" raise ArgumentError, "Could not create Stomp client instance - queue source #{Puppet[:queue_source]} is not a Stomp URL: #{detail}" @@ -26,7 +26,7 @@ class Puppet::Util::Queue::Stomp begin self.stomp_client = Stomp::Client.new(uri.user, uri.password, uri.host, uri.port, true) rescue => detail - raise ArgumentError, "Could not create Stomp client instance with queue source #{Puppet[:queue_source]}: got internal Stomp client error #{detail}" + raise ArgumentError, "Could not create Stomp client instance with queue source #{Puppet[:queue_source]}: got internal Stomp client error #{detail}", detail.backtrace end # Identify the supported method for sending messages. diff --git a/lib/puppet/util/rdoc/parser/puppet_parser_core.rb b/lib/puppet/util/rdoc/parser/puppet_parser_core.rb index dd7d03caf..0cd1ee5cc 100644 --- a/lib/puppet/util/rdoc/parser/puppet_parser_core.rb +++ b/lib/puppet/util/rdoc/parser/puppet_parser_core.rb @@ -218,7 +218,7 @@ module RDoc::PuppetParserCore container.add_resource(RDoc::PuppetResource.new(type, title, stmt.doc, param)) end rescue => detail - raise Puppet::ParseError, "impossible to parse resource in #{stmt.file} at line #{stmt.line}: #{detail}" + raise Puppet::ParseError, "impossible to parse resource in #{stmt.file} at line #{stmt.line}: #{detail}", detail.backtrace end end end @@ -252,7 +252,7 @@ module RDoc::PuppetParserCore cls.add_comment(comment, klass.file) rescue => detail - raise Puppet::ParseError, "impossible to parse class '#{name}' in #{klass.file} at line #{klass.line}: #{detail}" + raise Puppet::ParseError, "impossible to parse class '#{name}' in #{klass.file} at line #{klass.line}: #{detail}", detail.backtrace end # create documentation for a node @@ -277,7 +277,7 @@ module RDoc::PuppetParserCore n.add_comment(comment, node.file) rescue => detail - raise Puppet::ParseError, "impossible to parse node '#{name}' in #{node.file} at line #{node.line}: #{detail}" + raise Puppet::ParseError, "impossible to parse node '#{name}' in #{node.file} at line #{node.line}: #{detail}", detail.backtrace end # create documentation for a define @@ -318,7 +318,7 @@ module RDoc::PuppetParserCore meth.document_self = true meth.singleton = false rescue => detail - raise Puppet::ParseError, "impossible to parse definition '#{name}' in #{define.file} at line #{define.line}: #{detail}" + raise Puppet::ParseError, "impossible to parse definition '#{name}' in #{define.file} at line #{define.line}: #{detail}", detail.backtrace end # Traverse the AST tree and produce code-objects node diff --git a/lib/puppet/util/retryaction.rb b/lib/puppet/util/retryaction.rb index bd578c147..a50ceb784 100644 --- a/lib/puppet/util/retryaction.rb +++ b/lib/puppet/util/retryaction.rb @@ -23,7 +23,7 @@ module Puppet::Util::RetryAction # catch the excptions we care about and retry. # All others fail hard - raise RetryException::RetriesExceeded if parameters[:retries] == 0 + raise RetryException::RetriesExceeded, "#{parameters[:retries]} exceeded", e.backtrace if parameters[:retries] == 0 if (not parameters[:retry_exceptions].keys.empty?) and parameters[:retry_exceptions].keys.include?(e.class) Puppet.info("Caught exception #{e.class}:#{e}") diff --git a/lib/puppet/util/storage.rb b/lib/puppet/util/storage.rb index d01bcc3f4..0568243b9 100644 --- a/lib/puppet/util/storage.rb +++ b/lib/puppet/util/storage.rb @@ -62,7 +62,7 @@ class Puppet::Util::Storage begin File.rename(filename, filename + ".bad") rescue - raise Puppet::Error, "Could not rename corrupt #{filename}; remove manually" + raise Puppet::Error, "Could not rename corrupt #{filename}; remove manually", detail.backtrace end end end diff --git a/lib/puppet/util/symbolic_file_mode.rb b/lib/puppet/util/symbolic_file_mode.rb index 970dededc..69248190c 100644 --- a/lib/puppet/util/symbolic_file_mode.rb +++ b/lib/puppet/util/symbolic_file_mode.rb @@ -128,7 +128,7 @@ module SymbolicFileMode rest = '' end - raise Puppet::Error, "#{e}#{rest} in symbolic mode #{modification.inspect}" + raise Puppet::Error, "#{e}#{rest} in symbolic mode #{modification.inspect}", e.backtrace end end diff --git a/lib/puppet/util/windows/error.rb b/lib/puppet/util/windows/error.rb index 2893b6a42..c6088fa7a 100644 --- a/lib/puppet/util/windows/error.rb +++ b/lib/puppet/util/windows/error.rb @@ -7,8 +7,8 @@ class Puppet::Util::Windows::Error < Puppet::Error attr_reader :code - def initialize(message, code = GetLastError.call) - super(message + ": #{get_last_error(code)}") + def initialize(message, code = GetLastError.call, original = nil) + super(message + ": #{get_last_error(code)}", original) @code = code end diff --git a/lib/puppet/util/windows/registry.rb b/lib/puppet/util/windows/registry.rb index a8feb3dce..13b931ec0 100644 --- a/lib/puppet/util/windows/registry.rb +++ b/lib/puppet/util/windows/registry.rb @@ -13,7 +13,7 @@ module Puppet::Util::Windows def root(name) Win32::Registry.const_get(name) rescue NameError - raise Puppet::Error, "Invalid registry key '#{name}'" + raise Puppet::Error, "Invalid registry key '#{name}'", $!.backtrace end def open(name, path, mode = KEY_READ | KEY64, &block) @@ -23,7 +23,7 @@ module Puppet::Util::Windows return yield subkey end rescue Win32::Registry::Error => error - raise Puppet::Util::Windows::Error.new("Failed to open registry key '#{hkey.keyname}\\#{path}'", error.code) + raise Puppet::Util::Windows::Error.new("Failed to open registry key '#{hkey.keyname}\\#{path}'", error.code, error) end end From 1b9cf6f9c5cc4092a3996f88ff4a3b3ebeff4f53 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Thu, 23 Jan 2014 15:37:38 -0800 Subject: [PATCH 485/800] (PUP-1118) Document Puppet::Environments This also creates two new rake tasks for generating the documentation: doc:api and doc:all. The doc:api is to generate the published docs, which omits portions of the code that hasn't been tagged. doc:all just outputs everything. --- lib/puppet.rb | 6 +-- lib/puppet/environments.rb | 87 ++++++++++++++++++++++++++++++-------- tasks/yard.rake | 53 +++++++++++++++++++++++ 3 files changed, 126 insertions(+), 20 deletions(-) create mode 100644 tasks/yard.rake diff --git a/lib/puppet.rb b/lib/puppet.rb index edbc76e09..cc1ea6b5f 100644 --- a/lib/puppet.rb +++ b/lib/puppet.rb @@ -118,7 +118,7 @@ module Puppet end # Parse the config file for this process. - # @deprecated Use {#initialize_settings} + # @deprecated Use {initialize_settings} def self.parse_config() Puppet.deprecation_warning("Puppet.parse_config is deprecated; please use Faces API (which will handle settings and state management for you), or (less desirable) call Puppet.initialize_settings") Puppet.initialize_settings @@ -138,7 +138,7 @@ module Puppet # Initialize puppet's settings for a specified run_mode. # - # @deprecated Use {#initialize_settings} + # @deprecated Use {initialize_settings} def self.initialize_settings_for_run_mode(run_mode) Puppet.deprecation_warning("initialize_settings_for_run_mode may be removed in a future release, as may run_mode itself") do_initialize_settings_for_run_mode(run_mode, []) @@ -186,7 +186,7 @@ module Puppet end # A simple set of bindings that is just enough to limp along to - # initialization where the {#base_context} bindings are put in place + # initialization where the {base_context} bindings are put in place # @api private def self.bootstrap_context { :current_environment => Puppet::Node::Environment.create(:'*bootstrap*', [], '') } diff --git a/lib/puppet/environments.rb b/lib/puppet/environments.rb index 0935d6ad2..3b805381b 100644 --- a/lib/puppet/environments.rb +++ b/lib/puppet/environments.rb @@ -1,7 +1,15 @@ +# @api private module Puppet::Environments - class NotFoundError < Puppet::Error; end - + # @api private module EnvironmentCreator + # Create an anonymous environment. + # + # @param module_path [String] A list of module directories separated by the + # PATH_SEPARATOR + # @param manifest [String] The path to the manifest + # @return A new environment with the `name` `:anonymous` + # + # @api private def for(module_path, manifest) Puppet::Node::Environment.create(:anonymous, module_path.split(File::PATH_SEPARATOR), @@ -9,16 +17,24 @@ module Puppet::Environments end end - class OnlyProduction - def search_paths - ["environments://static/production"] - end - - def list - [Puppet::Node::Environment.new(:production)] - end - end + # @!macro [new] loader_search_paths + # A list of indicators of where the loader is getting its environments from. + # @return [Array] The URIs of the load locations + # + # @!macro [new] loader_list + # @return [Array] All of the environments known + # to the loader + # + # @!macro [new] loader_get + # Find a named environment + # + # @param name [String,Symbol] The name of environment to find + # @return [Puppet::Node::Environment, nil] the requested environment or nil + # if it wasn't found + # A source of pre-defined environments. + # + # @api private class Static include EnvironmentCreator @@ -26,14 +42,17 @@ module Puppet::Environments @environments = environments end + # @!macro loader_search_paths def search_paths ["environments://static/memory"] end + # @!macro loader_list def list @environments end + # @!macro loader_get def get(name) @environments.find do |env| env.name == name.intern @@ -41,37 +60,65 @@ module Puppet::Environments end end + # Old-style environments that come either from explicit stanzas in + # puppet.conf or from dynamic environments created from use of `$environment` + # in puppet.conf. + # + # @example Explicit Stanza + # [environment_name] + # modulepath=/var/my_env/modules + # + # @example Dynamic Environments + # [master] + # modulepath=/var/$environment/modules + # + # @api private class Legacy include EnvironmentCreator + # @!macro loader_search_paths def search_paths - ["environments://legacy/cached"] + ["environments://legacy"] end + # @note The list of environments for the Legacy environments is always + # empty. + # + # @!macro loader_list def list [] end + # @note Because the Legacy system cannot list out all of its environments, + # get is able to return environments that are not returned by a call to + # {#list}. + # + # @!macro loader_get def get(name) Puppet::Node::Environment.new(name) - #symbol = name.to_sym - #Puppet::Node::Environment.create( - # symbol, - # Puppet::Node::Environment.split_path(Puppet.settings.value(:modulepath, symbol)), - # Puppet.settings.value(:manifest, symbol)) end end + # Reads environments from a directory on disk. Each environment is + # represented as a sub-directory. The environment's manifest setting is the + # `manifest` directory of the environment directory. The environment's + # modulepath setting is the global modulepath (from the `[master]` section + # for the master) prepended with the `modules` directory of the environment + # directory. + # + # @api private class Directories def initialize(environment_dir, global_module_path) @environment_dir = environment_dir @global_module_path = global_module_path end + # @!macro loader_search_paths def search_paths ["environments://directories/#{@environment_dir}"] end + # @!macro loader_list def list base = Puppet::FileSystem.path_string(@environment_dir) @@ -88,24 +135,30 @@ module Puppet::Environments end end + # @!macro loader_get def get(name) list.find { |env| env.name == name.intern } end end + # Combine together multiple loaders to act as one. + # @api private class Combined def initialize(*loaders) @loaders = loaders end + # @!macro loader_search_paths def search_paths @loaders.collect(&:search_paths).flatten end + # @!macro loader_list def list @loaders.collect(&:list).flatten end + # @!macro loader_get def get(name) @loaders.each do |loader| if env = loader.get(name) diff --git a/tasks/yard.rake b/tasks/yard.rake new file mode 100644 index 000000000..36a2e3b5f --- /dev/null +++ b/tasks/yard.rake @@ -0,0 +1,53 @@ +require 'yard' + +namespace :doc do + desc "Clean up generated documentation" + task :clean do + rm_rf "doc" + end + + desc "Generate public documentation pages for the API" + YARD::Rake::YardocTask.new(:api) do |t| + t.files = ['lib/**/*.rb'] + t.options = %w{ + --protected + --private + --verbose + --markup markdown + --readme README.md + --tag status + --transitive-tag status + --tag comment + --hide-tag comment + --tag dsl:"DSL" + --no-transitive-tag api + --template-path yardoc/templates + --files README_DEVELOPER.md,CO*.md,api/**/*.md + --api public + --api private + --hide-void-return + } + end + + desc "Generate documentation pages for all of the code" + YARD::Rake::YardocTask.new(:all) do |t| + t.files = ['lib/**/*.rb'] + t.options = %w{ + --verbose + --markup markdown + --readme README.md + --tag status + --transitive-tag status + --tag comment + --hide-tag comment + --tag dsl:"DSL" + --no-transitive-tag api + --template-path yardoc/templates + --files README_DEVELOPER.md,CO*.md,api/**/*.md + --api public + --api private + --no-api + --hide-void-return + } + end +end From 2f3165366036c0404e4eed225159e945a033b440 Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Thu, 23 Jan 2014 21:46:34 -0800 Subject: [PATCH 486/800] (pup-1505) Switch to using facter.search_external for setting search paths for external facts --- lib/puppet/feature/external_facts.rb | 2 +- lib/puppet/indirector/facts/facter.rb | 2 +- spec/unit/indirector/facts/facter_spec.rb | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/puppet/feature/external_facts.rb b/lib/puppet/feature/external_facts.rb index 1e2ad575e..ff55e9eee 100644 --- a/lib/puppet/feature/external_facts.rb +++ b/lib/puppet/feature/external_facts.rb @@ -1,5 +1,5 @@ require 'facter/util/config' Puppet.features.add(:external_facts) { - Facter::Util::Config.respond_to?(:external_facts_dirs=) + Facter.respond_to?(:search_external) } diff --git a/lib/puppet/indirector/facts/facter.rb b/lib/puppet/indirector/facts/facter.rb index 4b44b5812..9f70a7c46 100644 --- a/lib/puppet/indirector/facts/facter.rb +++ b/lib/puppet/indirector/facts/facter.rb @@ -42,7 +42,7 @@ class Puppet::Node::Facts::Facter < Puppet::Indirector::Code end # Add to facter config - Facter::Util::Config.external_facts_dirs += external_facts_dirs + Facter.search_external external_facts_dirs end diff --git a/spec/unit/indirector/facts/facter_spec.rb b/spec/unit/indirector/facts/facter_spec.rb index 4c55ec6c3..6d842d2c1 100755 --- a/spec/unit/indirector/facts/facter_spec.rb +++ b/spec/unit/indirector/facts/facter_spec.rb @@ -147,14 +147,14 @@ describe Puppet::Node::Facts::Facter do :if => Puppet.features.external_facts?, :unless => Puppet.features.microsoft_windows? do Puppet[:pluginfactdest] = "/plugin/dest" @facter.find(@request) - Facter::Util::Config.external_facts_dirs.include?("/plugin/dest") + Facter.search_external_path.include?("/plugin/dest") end it "should include pluginfactdest when loading external facts", :if => Puppet.features.external_facts?, :if => Puppet.features.microsoft_windows? do Puppet[:pluginfactdest] = "/plugin/dest" @facter.find(@request) - Facter::Util::Config.external_facts_dirs.include?("C:/plugin/dest") + Facter.search_external_path.include?("C:/plugin/dest") end describe "when loading fact plugins from disk" do @@ -191,7 +191,7 @@ describe Puppet::Node::Facts::Facter do mod = Puppet::Module.new("mymodule", "#{one}/mymodule", @request.environment) @request.environment.stubs(:modules).returns([mod]) @facter.find(@request) - Facter::Util::Config.external_facts_dirs.include?("#{one}/mymodule/facts.d") + Facter.search_external_path.include?("#{one}/mymodule/facts.d") end end end From 8781d0da2fe6ad2fef954c8c2da54d573181d561 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Thu, 23 Jan 2014 16:26:12 -0800 Subject: [PATCH 487/800] (PUP-1118) Move root environment to Context And adjust TestHelper initialization to preserve base context setup without requiring settings initialization in before_all_tests hook. Initializing settings in the before_all_tests was causing a slowdown somewhere in call_hooks_deferred_to_application_initialization. The issue came from the root environment needing to have the modulepath, which interpolates in the confdir to produce the value. However, after an initial run, the after each hook tears down the settings, which makes them unavailable in the second before all call. This problem had been masked previously because of the way the root environment was cached and not reset between tests. --- lib/puppet.rb | 12 ++++++++---- lib/puppet/node/environment.rb | 12 +----------- lib/puppet/parser/functions.rb | 2 +- lib/puppet/parser/scope.rb | 5 +++-- lib/puppet/test/test_helper.rb | 8 +++++--- spec/unit/parser/functions_spec.rb | 4 ++-- spec/unit/parser/scope_spec.rb | 6 +++--- 7 files changed, 23 insertions(+), 26 deletions(-) diff --git a/lib/puppet.rb b/lib/puppet.rb index cc1ea6b5f..efaea6bfa 100644 --- a/lib/puppet.rb +++ b/lib/puppet.rb @@ -174,14 +174,18 @@ module Puppet # The bindings used for initialization of puppet # @api private def self.base_context(settings) + environments = settings[:environmentdir] + modulepath = Puppet::Node::Environment.split_path(settings[:modulepath]) + manifest = settings[:manifest] + root_environment = Puppet::Node::Environment.create(:'*root*', modulepath, manifest) + { :environments => Puppet::Environments::Combined.new( - Puppet::Environments::Directories.new( - settings[:environmentdir], - Puppet::Node::Environment.split_path(settings[:modulepath])), + Puppet::Environments::Directories.new(environments, modulepath), Puppet::Environments::Legacy.new ), - :current_environment => Puppet::Node::Environment.root, + :current_environment => root_environment, + :root_environment => root_environment, } end diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb index d62fce664..151ee7dca 100644 --- a/lib/puppet/node/environment.rb +++ b/lib/puppet/node/environment.rb @@ -36,7 +36,7 @@ end # In addition to normal environments that are defined by the user,there is a # special 'root' environment. It is defined as an instance variable on the # Puppet::Node::Environment metaclass. The environment name is `*root*` and can -# be accessed by calling {Puppet::Node::Environment.root}. +# be accessed by looking up the `:root_environment` using {Puppet.lookup}. # # The primary purpose of the root environment is to contain parser functions # that are not bound to a specific environment. The main case for this is for @@ -146,16 +146,6 @@ class Puppet::Node::Environment !!name.match(/\A\w+\Z/) end - # @return [Puppet::Node::Environment] The `*root*` environment. - # - # This is only used for handling functions that are not attached to a - # specific environment. - # - # @api private - def self.root - @root ||= create(:'*root*', split_path(Puppet[:modulepath]), Puppet[:manifest]) - end - # Clear all memoized environments and the 'current' environment # # @api private diff --git a/lib/puppet/parser/functions.rb b/lib/puppet/parser/functions.rb index 0c422ad38..2ceb2d4f0 100644 --- a/lib/puppet/parser/functions.rb +++ b/lib/puppet/parser/functions.rb @@ -230,7 +230,7 @@ module Puppet::Parser::Functions private def merged_functions(environment) - @functions[Environment.root].merge(@functions[environment]) + @functions[Puppet.lookup(:root_environment)].merge(@functions[environment]) end def get_function(name, environment) diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb index 95b63d438..0a9f9b14d 100644 --- a/lib/puppet/parser/scope.rb +++ b/lib/puppet/parser/scope.rb @@ -766,7 +766,8 @@ class Puppet::Parser::Scope private def extend_with_functions_module - extend Puppet::Parser::Functions.environment_module(Puppet::Node::Environment.root) - extend Puppet::Parser::Functions.environment_module(environment) if environment != Puppet::Node::Environment.root + root = Puppet.lookup(:root_environment) + extend Puppet::Parser::Functions.environment_module(root) + extend Puppet::Parser::Functions.environment_module(environment) if environment != root end end diff --git a/lib/puppet/test/test_helper.rb b/lib/puppet/test/test_helper.rb index 18b4fa1d3..545bae5d9 100644 --- a/lib/puppet/test/test_helper.rb +++ b/lib/puppet/test/test_helper.rb @@ -33,7 +33,11 @@ module Puppet::Test # that call Puppet. # @return nil def self.initialize() - initialize_settings_before_each + Puppet.push_context(Puppet.base_context({ + :environmentdir => "/dev/null", + :modulepath => "", + :manifest => "/dev/null" + }), "Initial for specs") Puppet::Parser::Functions.reset end @@ -42,14 +46,12 @@ module Puppet::Test # @return nil def self.before_all_tests() # Make sure that all of the setup is also done for any before(:all) blocks - Puppet.push_context(Puppet.base_context(Puppet.settings), "Initial for specs") 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() - Puppet.pop_context end # Call this method once per test, prior to execution of each invididual test. diff --git a/spec/unit/parser/functions_spec.rb b/spec/unit/parser/functions_spec.rb index 35fdca846..8e3f52616 100755 --- a/spec/unit/parser/functions_spec.rb +++ b/spec/unit/parser/functions_spec.rb @@ -73,12 +73,12 @@ describe Puppet::Parser::Functions do end it "combines functions from the root with those from the current environment" do - Puppet.override(:current_environment => Puppet::Node::Environment.root) do + Puppet.override(:current_environment => Puppet.lookup(:root_environment)) do Puppet::Parser::Functions.newfunction("onlyroot", :type => :rvalue) do |args| end end - Puppet.override(:current_environment => Puppet::Node::Environment.create(:other, [''], '')) do + Puppet.override(:current_environment => Puppet::Node::Environment.create(:other, [], '')) do Puppet::Parser::Functions.newfunction("other_env", :type => :rvalue) do |args| end diff --git a/spec/unit/parser/scope_spec.rb b/spec/unit/parser/scope_spec.rb index 95ca6790b..2267294a3 100755 --- a/spec/unit/parser/scope_spec.rb +++ b/spec/unit/parser/scope_spec.rb @@ -120,8 +120,8 @@ describe Puppet::Parser::Scope do describe "when initializing" do it "should extend itself with its environment's Functions module as well as the default" do - env = Puppet::Node::Environment.new("myenv") - root = Puppet::Node::Environment.root + env = Puppet::Node::Environment.create(:myenv, [], '') + root = Puppet.lookup(:root_environment) compiler = stub 'compiler', :environment => env, :is_a? => true scope = Puppet::Parser::Scope.new(compiler) @@ -130,7 +130,7 @@ describe Puppet::Parser::Scope do end it "should extend itself with the default Functions module if its environment is the default" do - root = Puppet::Node::Environment.root + root = Puppet.lookup(:root_environment) node = Puppet::Node.new('localhost') compiler = Puppet::Parser::Compiler.new(node) scope = Puppet::Parser::Scope.new(compiler) From 5d6f48d7921246971f1e3aaee96af78ac0a024cd Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 24 Jan 2014 13:45:46 -0800 Subject: [PATCH 488/800] (maint) Only load yard tasks if yard is available --- tasks/yard.rake | 104 +++++++++++++++++++++++++----------------------- 1 file changed, 55 insertions(+), 49 deletions(-) diff --git a/tasks/yard.rake b/tasks/yard.rake index 36a2e3b5f..d513be771 100644 --- a/tasks/yard.rake +++ b/tasks/yard.rake @@ -1,53 +1,59 @@ -require 'yard' +begin + require 'yard' -namespace :doc do - desc "Clean up generated documentation" - task :clean do - rm_rf "doc" + namespace :doc do + desc "Clean up generated documentation" + task :clean do + rm_rf "doc" + end + + desc "Generate public documentation pages for the API" + YARD::Rake::YardocTask.new(:api) do |t| + t.files = ['lib/**/*.rb'] + t.options = %w{ + --protected + --private + --verbose + --markup markdown + --readme README.md + --tag status + --transitive-tag status + --tag comment + --hide-tag comment + --tag dsl:"DSL" + --no-transitive-tag api + --template-path yardoc/templates + --files README_DEVELOPER.md,CO*.md,api/**/*.md + --api public + --api private + --hide-void-return + } + end + + desc "Generate documentation pages for all of the code" + YARD::Rake::YardocTask.new(:all) do |t| + t.files = ['lib/**/*.rb'] + t.options = %w{ + --verbose + --markup markdown + --readme README.md + --tag status + --transitive-tag status + --tag comment + --hide-tag comment + --tag dsl:"DSL" + --no-transitive-tag api + --template-path yardoc/templates + --files README_DEVELOPER.md,CO*.md,api/**/*.md + --api public + --api private + --no-api + --hide-void-return + } + end end - - desc "Generate public documentation pages for the API" - YARD::Rake::YardocTask.new(:api) do |t| - t.files = ['lib/**/*.rb'] - t.options = %w{ - --protected - --private - --verbose - --markup markdown - --readme README.md - --tag status - --transitive-tag status - --tag comment - --hide-tag comment - --tag dsl:"DSL" - --no-transitive-tag api - --template-path yardoc/templates - --files README_DEVELOPER.md,CO*.md,api/**/*.md - --api public - --api private - --hide-void-return - } - end - - desc "Generate documentation pages for all of the code" - YARD::Rake::YardocTask.new(:all) do |t| - t.files = ['lib/**/*.rb'] - t.options = %w{ - --verbose - --markup markdown - --readme README.md - --tag status - --transitive-tag status - --tag comment - --hide-tag comment - --tag dsl:"DSL" - --no-transitive-tag api - --template-path yardoc/templates - --files README_DEVELOPER.md,CO*.md,api/**/*.md - --api public - --api private - --no-api - --hide-void-return - } +rescue LoadError => e + if verbose + puts "Document generation not available without yard. #{e.message}" end end From 255d38145649c5ee6956e2d47a729df069d9efee Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 24 Jan 2014 12:01:18 -0800 Subject: [PATCH 489/800] (PUP-1151) Respond with 404 when path not known The v2.0 api implementation had a type that was not caught by any tests. This fixes that issue (a typo) and adds a test that would have shown the problem. --- lib/puppet/network/http/api/v1.rb | 2 +- lib/puppet/network/http/api/v2.rb | 6 +++--- lib/puppet/network/http/rack/rest.rb | 2 +- lib/puppet/network/http/webrick/rest.rb | 2 +- spec/unit/module_tool/tar_spec.rb | 2 +- spec/unit/network/authorization_spec.rb | 12 +++++++++++- spec/unit/network/http/api/v2_spec.rb | 14 ++++++++++++++ 7 files changed, 32 insertions(+), 8 deletions(-) create mode 100644 spec/unit/network/http/api/v2_spec.rb diff --git a/lib/puppet/network/http/api/v1.rb b/lib/puppet/network/http/api/v1.rb index 45e59b93b..701a4549f 100644 --- a/lib/puppet/network/http/api/v1.rb +++ b/lib/puppet/network/http/api/v1.rb @@ -25,7 +25,7 @@ class Puppet::Network::HTTP::API::V1 } def self.routes - [Puppet::Network::HTTP::Route.path(/.*/).any(new)] + Puppet::Network::HTTP::Route.path(/.*/).any(new) end # handle an HTTP request diff --git a/lib/puppet/network/http/api/v2.rb b/lib/puppet/network/http/api/v2.rb index aaa2e4fcf..ef9672df4 100644 --- a/lib/puppet/network/http/api/v2.rb +++ b/lib/puppet/network/http/api/v2.rb @@ -3,9 +3,9 @@ module Puppet::Network::HTTP::API::V2 require 'puppet/network/http/api/v2/authorization' def self.routes - [path(%r{^/v2\.0}). + path(%r{^/v2\.0}). get(Authorization.new). - chain(ENVIRONMENTS, NOT_FOUND)] + chain(ENVIRONMENTS, NOT_FOUND) end private @@ -23,7 +23,7 @@ module Puppet::Network::HTTP::API::V2 NOT_FOUND = Puppet::Network::HTTP::Route. path(/.*/). any(lambda do |req, res| - raise Puppet::Network::HTTP::Handler::HTTPNotFoundError.new(req.path, Puppet::Network::HTTP::Issues::HANDLER_NOT_FOUND) + raise Puppet::Network::HTTP::Error::HTTPNotFoundError.new(req.path, Puppet::Network::HTTP::Issues::HANDLER_NOT_FOUND) end) ENVIRONMENTS = path(%r{^/environments$}).get(provide do diff --git a/lib/puppet/network/http/rack/rest.rb b/lib/puppet/network/http/rack/rest.rb index 6179895d6..23d73bf93 100644 --- a/lib/puppet/network/http/rack/rest.rb +++ b/lib/puppet/network/http/rack/rest.rb @@ -28,7 +28,7 @@ class Puppet::Network::HTTP::RackREST def initialize(args={}) super() - register(Puppet::Network::HTTP::API::V2.routes + Puppet::Network::HTTP::API::V1.routes) + register([Puppet::Network::HTTP::API::V2.routes, Puppet::Network::HTTP::API::V1.routes]) end def set_content_type(response, format) diff --git a/lib/puppet/network/http/webrick/rest.rb b/lib/puppet/network/http/webrick/rest.rb index 625740506..42e4d9ea3 100644 --- a/lib/puppet/network/http/webrick/rest.rb +++ b/lib/puppet/network/http/webrick/rest.rb @@ -9,7 +9,7 @@ class Puppet::Network::HTTP::WEBrickREST < WEBrick::HTTPServlet::AbstractServlet def initialize(server) raise ArgumentError, "server is required" unless server - register(Puppet::Network::HTTP::API::V2.routes + Puppet::Network::HTTP::API::V1.routes) + register([Puppet::Network::HTTP::API::V2.routes, Puppet::Network::HTTP::API::V1.routes]) super(server) end diff --git a/spec/unit/module_tool/tar_spec.rb b/spec/unit/module_tool/tar_spec.rb index 3de0dda48..75034e5bd 100644 --- a/spec/unit/module_tool/tar_spec.rb +++ b/spec/unit/module_tool/tar_spec.rb @@ -1,5 +1,5 @@ require 'spec_helper' -require 'puppet/module_tool/tar' +require 'puppet/module_tool' describe Puppet::ModuleTool::Tar do diff --git a/spec/unit/network/authorization_spec.rb b/spec/unit/network/authorization_spec.rb index 397f55dcc..10b9d1def 100644 --- a/spec/unit/network/authorization_spec.rb +++ b/spec/unit/network/authorization_spec.rb @@ -10,11 +10,21 @@ describe Puppet::Network::Authorization do subject { AuthTest.new } describe "when creating an authconfig object" do - it "creates default ACL entries if no file has been read" do + before :each do # Other tests may have created an authconfig, so we have to undo that. + @orig_auth_config = Puppet::Network::AuthConfigLoader.instance_variable_get(:@auth_config) + @orig_auth_config_file = Puppet::Network::AuthConfigLoader.instance_variable_get(:@auth_config_file) + Puppet::Network::AuthConfigLoader.instance_variable_set(:@auth_config, nil) Puppet::Network::AuthConfigLoader.instance_variable_set(:@auth_config_file, nil) + end + after :each do + Puppet::Network::AuthConfigLoader.instance_variable_set(:@auth_config, @orig_auth_config) + Puppet::Network::AuthConfigLoader.instance_variable_set(:@auth_config_file, @orig_auth_config_file) + end + + it "creates default ACL entries if no file has been read" do Puppet::Network::AuthConfigParser.expects(:new_from_file).raises Errno::ENOENT Puppet::Network::AuthConfig.any_instance.expects(:insert_default_acl) diff --git a/spec/unit/network/http/api/v2_spec.rb b/spec/unit/network/http/api/v2_spec.rb new file mode 100644 index 000000000..30cbaaac0 --- /dev/null +++ b/spec/unit/network/http/api/v2_spec.rb @@ -0,0 +1,14 @@ +require 'spec_helper' + +require 'puppet/network/http' + +describe Puppet::Network::HTTP::API::V2 do + it "responds to unknown paths with a 404" do + response = Puppet::Network::HTTP::MemoryResponse.new + request = Puppet::Network::HTTP::Request.from_hash(:path => "/v2.0/unknown") + + expect do + Puppet::Network::HTTP::API::V2.routes.process(request, response) + end.to raise_error(Puppet::Network::HTTP::Error::HTTPNotFoundError) + end +end From d45930c9d648fd15ce6c1c0d458c5c6a77cc95e7 Mon Sep 17 00:00:00 2001 From: jrussek Date: Thu, 9 Jan 2014 13:00:59 +0100 Subject: [PATCH 490/800] (PUP-1387) fixes wrong hash in subjectKeyIdentifier this fixes the regression that appeared when the arguments to ExtensionFactory where accidentally swapped. Also added a test case to check if the subjectKeyIdentifier is present. --- lib/puppet/ssl/certificate_factory.rb | 2 +- spec/unit/ssl/certificate_factory_spec.rb | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/puppet/ssl/certificate_factory.rb b/lib/puppet/ssl/certificate_factory.rb index c6d01026c..5642bff05 100644 --- a/lib/puppet/ssl/certificate_factory.rb +++ b/lib/puppet/ssl/certificate_factory.rb @@ -37,7 +37,7 @@ module Puppet::SSL::CertificateFactory def self.add_extensions_to(cert, csr, issuer, extensions) ef = OpenSSL::X509::ExtensionFactory. - new(cert, issuer.is_a?(OpenSSL::X509::Request) ? cert : issuer) + new(issuer.is_a?(OpenSSL::X509::Request) ? cert : issuer, cert) # Extract the requested extensions from the CSR. requested_exts = csr.request_extensions.inject({}) do |hash, re| diff --git a/spec/unit/ssl/certificate_factory_spec.rb b/spec/unit/ssl/certificate_factory_spec.rb index fa436edcf..db5d05039 100755 --- a/spec/unit/ssl/certificate_factory_spec.rb +++ b/spec/unit/ssl/certificate_factory_spec.rb @@ -84,6 +84,13 @@ describe Puppet::SSL::CertificateFactory do "critical" => false } end + it "should add an extension for the subjectKeyIdentifer" do + cert = subject.build(:server, csr, issuer, serial) + ef = OpenSSL::X509::ExtensionFactory.new(issuer, cert) + cert.extensions.map { |x| x.to_h }.find {|x| x["oid"] == "subjectKeyIdentifier" }.should == + ef.create_extension("subjectKeyIdentifier", "hash", false).to_h + end + # See #2848 for why we are doing this: we need to make sure that # subjectAltName is set if the CSR has it, but *not* if it is set when the # certificate is built! From bda42f2fdbd7a0fa8fc31d8f599a973c97274911 Mon Sep 17 00:00:00 2001 From: Adrien Thebo Date: Fri, 24 Jan 2014 10:51:52 -0800 Subject: [PATCH 491/800] (maint) expand ExtensionFactory creation for clarity The code compression in 94345ebac introduced a regression where the issuer certificate and subject certificate were transposed, which would cause erroneous extensions to be created. db5fb8a fixed this issue but it's still pretty unclear why the given values are set. This commit removes the code compression so that we specifically call out the behavior of adding extensions to a self signed cert. --- lib/puppet/ssl/certificate_factory.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/puppet/ssl/certificate_factory.rb b/lib/puppet/ssl/certificate_factory.rb index 5642bff05..cbae6455d 100644 --- a/lib/puppet/ssl/certificate_factory.rb +++ b/lib/puppet/ssl/certificate_factory.rb @@ -36,8 +36,9 @@ module Puppet::SSL::CertificateFactory private def self.add_extensions_to(cert, csr, issuer, extensions) - ef = OpenSSL::X509::ExtensionFactory. - new(issuer.is_a?(OpenSSL::X509::Request) ? cert : issuer, cert) + ef = OpenSSL::X509::ExtensionFactory.new + ef.subject_certificate = cert + ef.issuer_certificate = issuer.is_a?(OpenSSL::X509::Request) ? cert : issuer # Extract the requested extensions from the CSR. requested_exts = csr.request_extensions.inject({}) do |hash, re| From b4235bc64e85374a17ca5961ba773bf2b23beb11 Mon Sep 17 00:00:00 2001 From: Adrien Thebo Date: Fri, 24 Jan 2014 11:24:19 -0800 Subject: [PATCH 492/800] (doc) Document certificate_factory methods Any code dealing with X509 is rather difficult to understand by default, and methods that have no documentation don't help. This commit expands method documentation for future generations that are brave enough to delve into this code. --- lib/puppet/ssl/certificate_factory.rb | 42 +++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/lib/puppet/ssl/certificate_factory.rb b/lib/puppet/ssl/certificate_factory.rb index cbae6455d..9fdf89fcd 100644 --- a/lib/puppet/ssl/certificate_factory.rb +++ b/lib/puppet/ssl/certificate_factory.rb @@ -1,8 +1,29 @@ require 'puppet/ssl' -# The tedious class that does all the manipulations to the -# certificate to correctly sign it. Yay. +# This class encapsulates the logic of creating and adding extensions to X509 +# certificates. +# +# @api private module Puppet::SSL::CertificateFactory + + # Create, add extensions to, and sign a new X509 certificate. + # + # @param cert_type [Symbol] The certificate type to create, which specifies + # what extensions are added to the certificate. + # One of (:ca, :terminalsubca, :server, :ocsp, :client) + # @param csr [OpenSSL::X509::Request] The signing request associated with + # the certificate being created. + # @param issuer [OpenSSL::X509::Certificate, OpenSSL::X509::Request] An X509 CSR + # if this is a self signed certificate, or the X509 certificate of the CA if + # this is a CA signed certificate. + # @param serial [Integer] The serial number for the given certificate, which + # MUST be unique for the given CA. + # @param ttl [String] The duration of the validity for the given certificate. + # defaults to Puppet[:ca_ttl] + # + # @api public + # + # @return [OpenSSL::X509::Certificate] def self.build(cert_type, csr, issuer, serial, ttl = nil) # Work out if we can even build the requested type of certificate. build_extensions = "build_#{cert_type.to_s}_extensions" @@ -35,6 +56,23 @@ module Puppet::SSL::CertificateFactory private + # Add X509v3 extensions to the given certificate. + # + # @param cert [OpenSSL::X509::Certificate] The certificate to add the + # extensions to. + # @param csr [OpenSSL::X509::Request] The CSR associated with the given + # certificate, which may specify requested extensions for the given cert. + # See http://tools.ietf.org/html/rfc2985 Section 5.4.2 Extension request + # @param issuer [OpenSSL::X509::Certificate, OpenSSL::X509::Request] An X509 CSR + # if this is a self signed certificate, or the X509 certificate of the CA if + # this is a CA signed certificate. + # @param extensions [Hash | String>] The extensions to + # add to the certificate, based on the certificate type being created (CA, + # server, client, etc) + # + # @api private + # + # @return [void] def self.add_extensions_to(cert, csr, issuer, extensions) ef = OpenSSL::X509::ExtensionFactory.new ef.subject_certificate = cert From e70300f1562066d7947fa72d75f222d560c3339c Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 24 Jan 2014 14:40:19 -0800 Subject: [PATCH 493/800] (PUP-1118) Ensure there is only one *root* If there are multiple root environments ever created, then functions that belong on the root do not get registered correctly. This stops that by making sure that the root is only created during the bootstrap phase. --- lib/puppet.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/puppet.rb b/lib/puppet.rb index efaea6bfa..51c5451e8 100644 --- a/lib/puppet.rb +++ b/lib/puppet.rb @@ -176,16 +176,12 @@ module Puppet def self.base_context(settings) environments = settings[:environmentdir] modulepath = Puppet::Node::Environment.split_path(settings[:modulepath]) - manifest = settings[:manifest] - root_environment = Puppet::Node::Environment.create(:'*root*', modulepath, manifest) { :environments => Puppet::Environments::Combined.new( Puppet::Environments::Directories.new(environments, modulepath), Puppet::Environments::Legacy.new - ), - :current_environment => root_environment, - :root_environment => root_environment, + ) } end @@ -193,7 +189,11 @@ module Puppet # initialization where the {base_context} bindings are put in place # @api private def self.bootstrap_context - { :current_environment => Puppet::Node::Environment.create(:'*bootstrap*', [], '') } + root_environment = Puppet::Node::Environment.create(:'*root*', [], '') + { + :current_environment => root_environment, + :root_environment => root_environment + } end # @param overrides [Hash] A hash of bindings to be merged with the parent context. From 2cfc3231b3ac4e14fa7093f641fc01de4368e260 Mon Sep 17 00:00:00 2001 From: "glenn.sarti" Date: Sat, 25 Jan 2014 13:22:04 +0800 Subject: [PATCH 494/800] (PUP-1278) Windows Puppet Agent Service gracefully terminates after succesfully being put into a Paused state Removed redundant return statement Added in logic to only start the Puppet Agent if the service is in a Running or Idle state --- ext/windows/service/daemon.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ext/windows/service/daemon.rb b/ext/windows/service/daemon.rb index 50431f4f2..3f05ee95f 100755 --- a/ext/windows/service/daemon.rb +++ b/ext/windows/service/daemon.rb @@ -57,8 +57,6 @@ class WindowsDaemon < Win32::Daemon log_notice("Starting service with arguments: #{args}") while running? do - return if state != RUNNING - log_notice('Service running') puppet = File.join(basedir, 'bin', 'puppet.bat') @@ -79,8 +77,12 @@ class WindowsDaemon < Win32::Daemon runinterval = 1800 end - pid = Process.create(:command_line => "\"#{puppet}\" agent --onetime #{args}", :creation_flags => Process::CREATE_NEW_CONSOLE).process_id - log_debug("Process created: #{pid}") + if state == RUNNING or state == IDLE + pid = Process.create(:command_line => "\"#{puppet}\" agent --onetime #{args}", :creation_flags => Process::CREATE_NEW_CONSOLE).process_id + log_debug("Process created: #{pid}") + else + log_debug("Service is paused. Not invoking Puppet agent") + end log_debug("Service waiting for #{runinterval} seconds") sleep(runinterval) From 50a9eb34cc7e722997b236e920102c96ed99592f Mon Sep 17 00:00:00 2001 From: Peter Souter Date: Tue, 21 Jan 2014 13:43:30 +0000 Subject: [PATCH 495/800] (PUP-1318) Make service squelch false This will allow us to get failure output from commands... --- lib/puppet/provider/service/service.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/puppet/provider/service/service.rb b/lib/puppet/provider/service/service.rb index 27f9b6373..35e9eb706 100644 --- a/lib/puppet/provider/service/service.rb +++ b/lib/puppet/provider/service/service.rb @@ -20,10 +20,10 @@ Puppet::Type.type(:service).provide :service do end # A simple wrapper so execution failures are a bit more informative. - def texecute(type, command, fof = true) + def texecute(type, command, fof = true, squelch = false) begin # #565: Services generally produce no output, so squelch them. - execute(command, :failonfail => fof, :override_locale => false, :squelch => true) + execute(command, :failonfail => fof, :override_locale => false, :squelch => squelch) rescue Puppet::ExecutionFailure => detail @resource.fail "Could not #{type} #{@resource.ref}: #{detail}" end From 4c38910f2d7dc252531596955ffc5f7731c0b9fd Mon Sep 17 00:00:00 2001 From: Peter Souter Date: Tue, 21 Jan 2014 15:51:54 +0000 Subject: [PATCH 496/800] (PUP-1318) Service now outputs full errorlog on fail --- lib/puppet/provider/service/service.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/puppet/provider/service/service.rb b/lib/puppet/provider/service/service.rb index 35e9eb706..f61202627 100644 --- a/lib/puppet/provider/service/service.rb +++ b/lib/puppet/provider/service/service.rb @@ -20,10 +20,10 @@ Puppet::Type.type(:service).provide :service do end # A simple wrapper so execution failures are a bit more informative. - def texecute(type, command, fof = true, squelch = false) + def texecute(type, command, fof = true, squelch = false, combine = true) begin # #565: Services generally produce no output, so squelch them. - execute(command, :failonfail => fof, :override_locale => false, :squelch => squelch) + execute(command, :failonfail => fof, :override_locale => false, :squelch => squelch, :combine => combine) rescue Puppet::ExecutionFailure => detail @resource.fail "Could not #{type} #{@resource.ref}: #{detail}" end From 295467ba13df66c50837c03b1c65482650ab3f60 Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Mon, 27 Jan 2014 11:50:18 -0800 Subject: [PATCH 497/800] (PUP-1318) Fixing expected parameters in service provider specs now that service output is no longer squelched by default. Redmine 565, a seven year old issue referenced in a comment in lib/puppet/provider/service/service.rb, is no longer applicable to `Puppet::Util::Execution#execute`. It was squelching service output because of a bug in ruby where `#read` on a pipe never returned when the spawned process is SIGTERM'd by one of its children. The current implementation of `Puppet::Util::Execution#execute` redirects to a temporary file instead of reading from a pipe. We should no longer be squelching service output so that users can easily diagnose service failures. --- lib/puppet/provider/service/service.rb | 1 - lib/puppet/util/execution.rb | 2 +- spec/unit/provider/service/gentoo_spec.rb | 40 +++++++++++----------- spec/unit/provider/service/openbsd_spec.rb | 32 ++++++++--------- spec/unit/provider/service/openrc_spec.rb | 40 +++++++++++----------- spec/unit/provider/service/src_spec.rb | 8 ++--- spec/unit/provider/service/systemd_spec.rb | 14 ++++---- spec/unit/provider/service/windows_spec.rb | 2 +- 8 files changed, 69 insertions(+), 70 deletions(-) diff --git a/lib/puppet/provider/service/service.rb b/lib/puppet/provider/service/service.rb index f61202627..8afd24946 100644 --- a/lib/puppet/provider/service/service.rb +++ b/lib/puppet/provider/service/service.rb @@ -22,7 +22,6 @@ Puppet::Type.type(:service).provide :service do # A simple wrapper so execution failures are a bit more informative. def texecute(type, command, fof = true, squelch = false, combine = true) begin - # #565: Services generally produce no output, so squelch them. execute(command, :failonfail => fof, :override_locale => false, :squelch => squelch, :combine => combine) rescue Puppet::ExecutionFailure => detail @resource.fail "Could not #{type} #{@resource.ref}: #{detail}" diff --git a/lib/puppet/util/execution.rb b/lib/puppet/util/execution.rb index 411f462c5..6ad37510f 100644 --- a/lib/puppet/util/execution.rb +++ b/lib/puppet/util/execution.rb @@ -192,7 +192,7 @@ module Puppet::Util::Execution end if options[:failonfail] and exit_status != 0 - raise Puppet::ExecutionFailure, "Execution of '#{str}' returned #{exit_status}: #{output}" + raise Puppet::ExecutionFailure, "Execution of '#{str}' returned #{exit_status}: #{output.strip}" end Puppet::Util::Execution::ProcessOutput.new(output || '', exit_status) diff --git a/spec/unit/provider/service/gentoo_spec.rb b/spec/unit/provider/service/gentoo_spec.rb index c68832a5b..2bca67937 100755 --- a/spec/unit/provider/service/gentoo_spec.rb +++ b/spec/unit/provider/service/gentoo_spec.rb @@ -75,12 +75,12 @@ describe Puppet::Type.type(:service).provider(:gentoo) do describe "#start" do it "should use the supplied start command if specified" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :start => '/bin/foo')) - provider.expects(:execute).with(['/bin/foo'], :failonfail => true, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/bin/foo'], :failonfail => true, :override_locale => false, :squelch => false, :combine => true) provider.start end it "should start the service with start otherwise" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd')) - provider.expects(:execute).with(['/etc/init.d/sshd',:start], :failonfail => true, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/etc/init.d/sshd',:start], :failonfail => true, :override_locale => false, :squelch => false, :combine => true) provider.expects(:search).with('sshd').returns('/etc/init.d/sshd') provider.start end @@ -89,12 +89,12 @@ describe Puppet::Type.type(:service).provider(:gentoo) do describe "#stop" do it "should use the supplied stop command if specified" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :stop => '/bin/foo')) - provider.expects(:execute).with(['/bin/foo'], :failonfail => true, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/bin/foo'], :failonfail => true, :override_locale => false, :squelch => false, :combine => true) provider.stop end it "should stop the service with stop otherwise" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd')) - provider.expects(:execute).with(['/etc/init.d/sshd',:stop], :failonfail => true, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/etc/init.d/sshd',:stop], :failonfail => true, :override_locale => false, :squelch => false, :combine => true) provider.expects(:search).with('sshd').returns('/etc/init.d/sshd') provider.stop end @@ -156,23 +156,23 @@ describe Puppet::Type.type(:service).provider(:gentoo) do describe "when a special status command is specified" do it "should use the status command from the resource" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :status => '/bin/foo')) - provider.expects(:execute).with(['/etc/init.d/sshd',:status], :failonfail => false, :override_locale => false, :squelch => true).never - provider.expects(:execute).with(['/bin/foo'], :failonfail => false, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/etc/init.d/sshd',:status], :failonfail => false, :override_locale => false, :squelch => false, :combine => true).never + provider.expects(:execute).with(['/bin/foo'], :failonfail => false, :override_locale => false, :squelch => false, :combine => true) provider.status end it "should return :stopped when the status command returns with a non-zero exitcode" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :status => '/bin/foo')) - provider.expects(:execute).with(['/etc/init.d/sshd',:status], :failonfail => false, :override_locale => false, :squelch => true).never - provider.expects(:execute).with(['/bin/foo'], :failonfail => false, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/etc/init.d/sshd',:status], :failonfail => false, :override_locale => false, :squelch => false, :combine => true).never + provider.expects(:execute).with(['/bin/foo'], :failonfail => false, :override_locale => false, :squelch => false, :combine => true) $CHILD_STATUS.stubs(:exitstatus).returns 3 provider.status.should == :stopped end it "should return :running when the status command returns with a zero exitcode" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :status => '/bin/foo')) - provider.expects(:execute).with(['/etc/init.d/sshd',:status], :failonfail => false, :override_locale => false, :squelch => true).never - provider.expects(:execute).with(['/bin/foo'], :failonfail => false, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/etc/init.d/sshd',:status], :failonfail => false, :override_locale => false, :squelch => false, :combine => true).never + provider.expects(:execute).with(['/bin/foo'], :failonfail => false, :override_locale => false, :squelch => false, :combine => true) $CHILD_STATUS.stubs(:exitstatus).returns 0 provider.status.should == :running end @@ -181,14 +181,14 @@ describe Puppet::Type.type(:service).provider(:gentoo) do describe "when hasstatus is false" do it "should return running if a pid can be found" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasstatus => false)) - provider.expects(:execute).with(['/etc/init.d/sshd',:status], :failonfail => false, :override_locale => false, :squelch => true).never + provider.expects(:execute).with(['/etc/init.d/sshd',:status], :failonfail => false, :override_locale => false, :squelch => false, :combine => true).never provider.expects(:getpid).returns 1000 provider.status.should == :running end it "should return stopped if no pid can be found" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasstatus => false)) - provider.expects(:execute).with(['/etc/init.d/sshd',:status], :failonfail => false, :override_locale => false, :squelch => true).never + provider.expects(:execute).with(['/etc/init.d/sshd',:status], :failonfail => false, :override_locale => false, :squelch => false, :combine => true).never provider.expects(:getpid).returns nil provider.status.should == :stopped end @@ -198,7 +198,7 @@ describe Puppet::Type.type(:service).provider(:gentoo) do it "should return running if status exits with a zero exitcode" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasstatus => true)) provider.expects(:search).with('sshd').returns('/etc/init.d/sshd') - provider.expects(:execute).with(['/etc/init.d/sshd',:status], :failonfail => false, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/etc/init.d/sshd',:status], :failonfail => false, :override_locale => false, :squelch => false, :combine => true) $CHILD_STATUS.stubs(:exitstatus).returns 0 provider.status.should == :running end @@ -206,7 +206,7 @@ describe Puppet::Type.type(:service).provider(:gentoo) do it "should return stopped if status exits with a non-zero exitcode" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasstatus => true)) provider.expects(:search).with('sshd').returns('/etc/init.d/sshd') - provider.expects(:execute).with(['/etc/init.d/sshd',:status], :failonfail => false, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/etc/init.d/sshd',:status], :failonfail => false, :override_locale => false, :squelch => false, :combine => true) $CHILD_STATUS.stubs(:exitstatus).returns 3 provider.status.should == :stopped end @@ -216,24 +216,24 @@ describe Puppet::Type.type(:service).provider(:gentoo) do describe "#restart" do it "should use the supplied restart command if specified" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :restart => '/bin/foo')) - provider.expects(:execute).with(['/etc/init.d/sshd',:restart], :failonfail => true, :override_locale => false, :squelch => true).never - provider.expects(:execute).with(['/bin/foo'], :failonfail => true, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/etc/init.d/sshd',:restart], :failonfail => true, :override_locale => false, :squelch => false, :combine => true).never + provider.expects(:execute).with(['/bin/foo'], :failonfail => true, :override_locale => false, :squelch => false, :combine => true) provider.restart end it "should restart the service with restart if hasrestart is true" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasrestart => true)) provider.expects(:search).with('sshd').returns('/etc/init.d/sshd') - provider.expects(:execute).with(['/etc/init.d/sshd',:restart], :failonfail => true, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/etc/init.d/sshd',:restart], :failonfail => true, :override_locale => false, :squelch => false, :combine => true) provider.restart end it "should restart the service with stop/start if hasrestart is false" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasrestart => false)) provider.expects(:search).with('sshd').returns('/etc/init.d/sshd') - provider.expects(:execute).with(['/etc/init.d/sshd',:restart], :failonfail => true, :override_locale => false, :squelch => true).never - provider.expects(:execute).with(['/etc/init.d/sshd',:stop], :failonfail => true, :override_locale => false, :squelch => true) - provider.expects(:execute).with(['/etc/init.d/sshd',:start], :failonfail => true, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/etc/init.d/sshd',:restart], :failonfail => true, :override_locale => false, :squelch => false, :combine => true).never + provider.expects(:execute).with(['/etc/init.d/sshd',:stop], :failonfail => true, :override_locale => false, :squelch => false, :combine => true) + provider.expects(:execute).with(['/etc/init.d/sshd',:start], :failonfail => true, :override_locale => false, :squelch => false, :combine => true) provider.restart end end diff --git a/spec/unit/provider/service/openbsd_spec.rb b/spec/unit/provider/service/openbsd_spec.rb index 8c417986d..5bed8862a 100644 --- a/spec/unit/provider/service/openbsd_spec.rb +++ b/spec/unit/provider/service/openbsd_spec.rb @@ -46,13 +46,13 @@ describe provider_class do describe "#start" do it "should use the supplied start command if specified" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :start => '/bin/foo')) - provider.expects(:execute).with(['/bin/foo'], :failonfail => true, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/bin/foo'], :failonfail => true, :override_locale => false, :squelch => false, :combine => true) provider.start end it "should start the service otherwise" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd')) - provider.expects(:execute).with(['/etc/rc.d/sshd', '-f', :start], :failonfail => true, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/etc/rc.d/sshd', '-f', :start], :failonfail => true, :override_locale => false, :squelch => false, :combine => true) provider.expects(:search).with('sshd').returns('/etc/rc.d/sshd') provider.start end @@ -61,13 +61,13 @@ describe provider_class do describe "#stop" do it "should use the supplied stop command if specified" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :stop => '/bin/foo')) - provider.expects(:execute).with(['/bin/foo'], :failonfail => true, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/bin/foo'], :failonfail => true, :override_locale => false, :squelch => false, :combine => true) provider.stop end it "should stop the service otherwise" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd')) - provider.expects(:execute).with(['/etc/rc.d/sshd', :stop], :failonfail => true, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/etc/rc.d/sshd', :stop], :failonfail => true, :override_locale => false, :squelch => false, :combine => true) provider.expects(:search).with('sshd').returns('/etc/rc.d/sshd') provider.stop end @@ -76,23 +76,23 @@ describe provider_class do describe "#status" do it "should use the status command from the resource" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :status => '/bin/foo')) - provider.expects(:execute).with(['/etc/rc.d/sshd', :status], :failonfail => false, :override_locale => false, :squelch => true).never - provider.expects(:execute).with(['/bin/foo'], :failonfail => false, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/etc/rc.d/sshd', :status], :failonfail => false, :override_locale => false, :squelch => false, :combine => true).never + provider.expects(:execute).with(['/bin/foo'], :failonfail => false, :override_locale => false, :squelch => false, :combine => true) provider.status end it "should return :stopped when status command returns with a non-zero exitcode" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :status => '/bin/foo')) - provider.expects(:execute).with(['/etc/rc.d/sshd', :status], :failonfail => false, :override_locale => false, :squelch => true).never - provider.expects(:execute).with(['/bin/foo'], :failonfail => false, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/etc/rc.d/sshd', :status], :failonfail => false, :override_locale => false, :squelch => false, :combine => true).never + provider.expects(:execute).with(['/bin/foo'], :failonfail => false, :override_locale => false, :squelch => false, :combine => true) $CHILD_STATUS.stubs(:exitstatus).returns 3 provider.status.should == :stopped end it "should return :running when status command returns with a zero exitcode" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :status => '/bin/foo')) - provider.expects(:execute).with(['/etc/rc.d/sshd', :status], :failonfail => false, :override_locale => false, :squelch => true).never - provider.expects(:execute).with(['/bin/foo'], :failonfail => false, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/etc/rc.d/sshd', :status], :failonfail => false, :override_locale => false, :squelch => false, :combine => true).never + provider.expects(:execute).with(['/bin/foo'], :failonfail => false, :override_locale => false, :squelch => false, :combine => true) $CHILD_STATUS.stubs(:exitstatus).returns 0 provider.status.should == :running end @@ -101,23 +101,23 @@ describe provider_class do describe "#restart" do it "should use the supplied restart command if specified" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :restart => '/bin/foo')) - provider.expects(:execute).with(['/etc/rc.d/sshd', '-f', :restart], :failonfail => true, :override_locale => false, :squelch => true).never - provider.expects(:execute).with(['/bin/foo'], :failonfail => true, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/etc/rc.d/sshd', '-f', :restart], :failonfail => true, :override_locale => false, :squelch => false, :combine => true).never + provider.expects(:execute).with(['/bin/foo'], :failonfail => true, :override_locale => false, :squelch => false, :combine => true) provider.restart end it "should restart the service with rc-service restart if hasrestart is true" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasrestart => true)) - provider.expects(:execute).with(['/etc/rc.d/sshd', '-f', :restart], :failonfail => true, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/etc/rc.d/sshd', '-f', :restart], :failonfail => true, :override_locale => false, :squelch => false, :combine => true) provider.expects(:search).with('sshd').returns('/etc/rc.d/sshd') provider.restart end it "should restart the service with rc-service stop/start if hasrestart is false" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasrestart => false)) - provider.expects(:execute).with(['/etc/rc.d/sshd', '-f', :restart], :failonfail => true, :override_locale => false, :squelch => true).never - provider.expects(:execute).with(['/etc/rc.d/sshd', :stop], :failonfail => true, :override_locale => false, :squelch => true) - provider.expects(:execute).with(['/etc/rc.d/sshd', '-f', :start], :failonfail => true, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/etc/rc.d/sshd', '-f', :restart], :failonfail => true, :override_locale => false, :squelch => false, :combine => true).never + provider.expects(:execute).with(['/etc/rc.d/sshd', :stop], :failonfail => true, :override_locale => false, :squelch => false, :combine => true) + provider.expects(:execute).with(['/etc/rc.d/sshd', '-f', :start], :failonfail => true, :override_locale => false, :squelch => false, :combine => true) provider.expects(:search).with('sshd').returns('/etc/rc.d/sshd') provider.restart end diff --git a/spec/unit/provider/service/openrc_spec.rb b/spec/unit/provider/service/openrc_spec.rb index 1949d3328..038340ed6 100755 --- a/spec/unit/provider/service/openrc_spec.rb +++ b/spec/unit/provider/service/openrc_spec.rb @@ -38,12 +38,12 @@ describe Puppet::Type.type(:service).provider(:openrc) do describe "#start" do it "should use the supplied start command if specified" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :start => '/bin/foo')) - provider.expects(:execute).with(['/bin/foo'], :failonfail => true, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/bin/foo'], :failonfail => true, :override_locale => false, :squelch => false, :combine => true) provider.start end it "should start the service with rc-service start otherwise" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd')) - provider.expects(:execute).with(['/sbin/rc-service','sshd',:start], :failonfail => true, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/sbin/rc-service','sshd',:start], :failonfail => true, :override_locale => false, :squelch => false, :combine => true) provider.start end end @@ -51,12 +51,12 @@ describe Puppet::Type.type(:service).provider(:openrc) do describe "#stop" do it "should use the supplied stop command if specified" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :stop => '/bin/foo')) - provider.expects(:execute).with(['/bin/foo'], :failonfail => true, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/bin/foo'], :failonfail => true, :override_locale => false, :squelch => false, :combine => true) provider.stop end it "should stop the service with rc-service stop otherwise" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd')) - provider.expects(:execute).with(['/sbin/rc-service','sshd',:stop], :failonfail => true, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/sbin/rc-service','sshd',:stop], :failonfail => true, :override_locale => false, :squelch => false, :combine => true) provider.stop end end @@ -144,23 +144,23 @@ describe Puppet::Type.type(:service).provider(:openrc) do describe "when a special status command if specified" do it "should use the status command from the resource" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :status => '/bin/foo')) - provider.expects(:execute).with(['/sbin/rc-service','sshd',:status], :failonfail => false, :override_locale => false, :squelch => true).never - provider.expects(:execute).with(['/bin/foo'], :failonfail => false, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/sbin/rc-service','sshd',:status], :failonfail => false, :override_locale => false, :squelch => false, :combine => true).never + provider.expects(:execute).with(['/bin/foo'], :failonfail => false, :override_locale => false, :squelch => false, :combine => true) provider.status end it "should return :stopped when status command returns with a non-zero exitcode" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :status => '/bin/foo')) - provider.expects(:execute).with(['/sbin/rc-service','sshd',:status], :failonfail => false, :override_locale => false, :squelch => true).never - provider.expects(:execute).with(['/bin/foo'], :failonfail => false, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/sbin/rc-service','sshd',:status], :failonfail => false, :override_locale => false, :squelch => false, :combine => true).never + provider.expects(:execute).with(['/bin/foo'], :failonfail => false, :override_locale => false, :squelch => false, :combine => true) $CHILD_STATUS.stubs(:exitstatus).returns 3 provider.status.should == :stopped end it "should return :running when status command returns with a zero exitcode" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :status => '/bin/foo')) - provider.expects(:execute).with(['/sbin/rc-service','sshd',:status], :failonfail => false, :override_locale => false, :squelch => true).never - provider.expects(:execute).with(['/bin/foo'], :failonfail => false, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/sbin/rc-service','sshd',:status], :failonfail => false, :override_locale => false, :squelch => false, :combine => true).never + provider.expects(:execute).with(['/bin/foo'], :failonfail => false, :override_locale => false, :squelch => false, :combine => true) $CHILD_STATUS.stubs(:exitstatus).returns 0 provider.status.should == :running end @@ -169,14 +169,14 @@ describe Puppet::Type.type(:service).provider(:openrc) do describe "when hasstatus is false" do it "should return running if a pid can be found" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasstatus => false)) - provider.expects(:execute).with(['/sbin/rc-service','sshd',:status], :failonfail => false, :override_locale => false, :squelch => true).never + provider.expects(:execute).with(['/sbin/rc-service','sshd',:status], :failonfail => false, :override_locale => false, :squelch => false, :combine => true).never provider.expects(:getpid).returns 1000 provider.status.should == :running end it "should return stopped if no pid can be found" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasstatus => false)) - provider.expects(:execute).with(['/sbin/rc-service','sshd',:status], :failonfail => false, :override_locale => false, :squelch => true).never + provider.expects(:execute).with(['/sbin/rc-service','sshd',:status], :failonfail => false, :override_locale => false, :squelch => false, :combine => true).never provider.expects(:getpid).returns nil provider.status.should == :stopped end @@ -185,14 +185,14 @@ describe Puppet::Type.type(:service).provider(:openrc) do describe "when hasstatus is true" do it "should return running if rc-service status exits with a zero exitcode" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasstatus => true)) - provider.expects(:execute).with(['/sbin/rc-service','sshd',:status], :failonfail => false, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/sbin/rc-service','sshd',:status], :failonfail => false, :override_locale => false, :squelch => false, :combine => true) $CHILD_STATUS.stubs(:exitstatus).returns 0 provider.status.should == :running end it "should return stopped if rc-service status exits with a non-zero exitcode" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasstatus => true)) - provider.expects(:execute).with(['/sbin/rc-service','sshd',:status], :failonfail => false, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/sbin/rc-service','sshd',:status], :failonfail => false, :override_locale => false, :squelch => false, :combine => true) $CHILD_STATUS.stubs(:exitstatus).returns 3 provider.status.should == :stopped end @@ -202,22 +202,22 @@ describe Puppet::Type.type(:service).provider(:openrc) do describe "#restart" do it "should use the supplied restart command if specified" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :restart => '/bin/foo')) - provider.expects(:execute).with(['/sbin/rc-service','sshd',:restart], :failonfail => true, :override_locale => false, :squelch => true).never - provider.expects(:execute).with(['/bin/foo'], :failonfail => true, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/sbin/rc-service','sshd',:restart], :failonfail => true, :override_locale => false, :squelch => false, :combine => true).never + provider.expects(:execute).with(['/bin/foo'], :failonfail => true, :override_locale => false, :squelch => false, :combine => true) provider.restart end it "should restart the service with rc-service restart if hasrestart is true" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasrestart => true)) - provider.expects(:execute).with(['/sbin/rc-service','sshd',:restart], :failonfail => true, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/sbin/rc-service','sshd',:restart], :failonfail => true, :override_locale => false, :squelch => false, :combine => true) provider.restart end it "should restart the service with rc-service stop/start if hasrestart is false" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasrestart => false)) - provider.expects(:execute).with(['/sbin/rc-service','sshd',:restart], :failonfail => true, :override_locale => false, :squelch => true).never - provider.expects(:execute).with(['/sbin/rc-service','sshd',:stop], :failonfail => true, :override_locale => false, :squelch => true) - provider.expects(:execute).with(['/sbin/rc-service','sshd',:start], :failonfail => true, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/sbin/rc-service','sshd',:restart], :failonfail => true, :override_locale => false, :squelch => false, :combine => true).never + provider.expects(:execute).with(['/sbin/rc-service','sshd',:stop], :failonfail => true, :override_locale => false, :squelch => false, :combine => true) + provider.expects(:execute).with(['/sbin/rc-service','sshd',:start], :failonfail => true, :override_locale => false, :squelch => false, :combine => true) provider.restart end end diff --git a/spec/unit/provider/service/src_spec.rb b/spec/unit/provider/service/src_spec.rb index 1f0b928fd..f168e3844 100755 --- a/spec/unit/provider/service/src_spec.rb +++ b/spec/unit/provider/service/src_spec.rb @@ -62,14 +62,14 @@ _EOF_ describe "when starting a service" do it "should execute the startsrc command" do - @provider.expects(:execute).with(['/usr/bin/startsrc', '-s', "myservice"], {:override_locale => false, :squelch => true, :failonfail => true}) + @provider.expects(:execute).with(['/usr/bin/startsrc', '-s', "myservice"], {:override_locale => false, :squelch => false, :combine => true, :failonfail => true}) @provider.start end end describe "when stopping a service" do it "should execute the stopsrc command" do - @provider.expects(:execute).with(['/usr/bin/stopsrc', '-s', "myservice"], {:override_locale => false, :squelch => true, :failonfail => true}) + @provider.expects(:execute).with(['/usr/bin/stopsrc', '-s', "myservice"], {:override_locale => false, :squelch => false, :combine => true, :failonfail => true}) @provider.stop end end @@ -165,8 +165,8 @@ _EOF_ myservice::--no-daemonize:/usr/sbin/puppetd:0:0:/dev/null:/var/log/puppet.log:/var/log/puppet.log:-O:-Q:-S:0:0:20:15:9:-d:20::" _EOF_ @provider.expects(:execute).with(['/usr/bin/lssrc', '-Ss', "myservice"]).returns sample_output - @provider.expects(:execute).with(['/usr/bin/stopsrc', '-s', "myservice"], {:override_locale => false, :squelch => true, :failonfail => true}) - @provider.expects(:execute).with(['/usr/bin/startsrc', '-s', "myservice"], {:override_locale => false, :squelch => true, :failonfail => true}) + @provider.expects(:execute).with(['/usr/bin/stopsrc', '-s', "myservice"], {:override_locale => false, :squelch => false, :combine => true, :failonfail => true}) + @provider.expects(:execute).with(['/usr/bin/startsrc', '-s', "myservice"], {:override_locale => false, :squelch => false, :combine => true, :failonfail => true}) @provider.restart end end diff --git a/spec/unit/provider/service/systemd_spec.rb b/spec/unit/provider/service/systemd_spec.rb index 6ed8658f9..df46be4d8 100755 --- a/spec/unit/provider/service/systemd_spec.rb +++ b/spec/unit/provider/service/systemd_spec.rb @@ -56,13 +56,13 @@ describe Puppet::Type.type(:service).provider(:systemd) do describe "#start" do it "should use the supplied start command if specified" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service', :start => '/bin/foo')) - provider.expects(:execute).with(['/bin/foo'], :failonfail => true, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/bin/foo'], :failonfail => true, :override_locale => false, :squelch => false, :combine => true) provider.start end it "should start the service with systemctl start otherwise" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service')) - provider.expects(:execute).with(['/bin/systemctl','start','sshd.service'], :failonfail => true, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/bin/systemctl','start','sshd.service'], :failonfail => true, :override_locale => false, :squelch => false, :combine => true) provider.start end end @@ -70,13 +70,13 @@ describe Puppet::Type.type(:service).provider(:systemd) do describe "#stop" do it "should use the supplied stop command if specified" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service', :stop => '/bin/foo')) - provider.expects(:execute).with(['/bin/foo'], :failonfail => true, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/bin/foo'], :failonfail => true, :override_locale => false, :squelch => false, :combine => true) provider.stop end it "should stop the service with systemctl stop otherwise" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service')) - provider.expects(:execute).with(['/bin/systemctl','stop','sshd.service'], :failonfail => true, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/bin/systemctl','stop','sshd.service'], :failonfail => true, :override_locale => false, :squelch => false, :combine => true) provider.stop end end @@ -132,14 +132,14 @@ describe Puppet::Type.type(:service).provider(:systemd) do describe "#restart" do it "should use the supplied restart command if specified" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :restart => '/bin/foo')) - provider.expects(:execute).with(['/bin/systemctl','restart','sshd.service'], :failonfail => true, :override_locale => false, :squelch => true).never - provider.expects(:execute).with(['/bin/foo'], :failonfail => true, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/bin/systemctl','restart','sshd.service'], :failonfail => true, :override_locale => false, :squelch => false, :combine => true).never + provider.expects(:execute).with(['/bin/foo'], :failonfail => true, :override_locale => false, :squelch => false, :combine => true) provider.restart end it "should restart the service with systemctl restart" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service')) - provider.expects(:execute).with(['/bin/systemctl','restart','sshd.service'], :failonfail => true, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['/bin/systemctl','restart','sshd.service'], :failonfail => true, :override_locale => false, :squelch => false, :combine => true) provider.restart end end diff --git a/spec/unit/provider/service/windows_spec.rb b/spec/unit/provider/service/windows_spec.rb index 86ab266f4..bb883bdf5 100755 --- a/spec/unit/provider/service/windows_spec.rb +++ b/spec/unit/provider/service/windows_spec.rb @@ -119,7 +119,7 @@ describe Puppet::Type.type(:service).provider(:windows), :if => Puppet.features. resource[:restart] = 'c:/bin/foo' provider.expects(:execute).never - provider.expects(:execute).with(['c:/bin/foo'], :failonfail => true, :override_locale => false, :squelch => true) + provider.expects(:execute).with(['c:/bin/foo'], :failonfail => true, :override_locale => false, :squelch => false, :combine => true) provider.restart end From b7c10f5d045af5890f83159b74ab5d5a23e0abc9 Mon Sep 17 00:00:00 2001 From: "Ethan J. Brown" Date: Wed, 22 Jan 2014 16:05:07 -0800 Subject: [PATCH 498/800] PUP-1494 Guard against Win32 memory corruption - Ruby's .encode(UTF-16LE) call on String may generate a string that not properly terminated with 2 NULLs, which may result in garbage bytes read from the memory pointer passed into the Win32 API. This can cause memory instability which results in failures in later Win32 calls. These calls have been replaced with an internal helper that properly NULl terminates the string. Note that this bug is fixed in Ruby 2.1: http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?revision=41920&view=revision - Don't include the wide NULL character when calculating number of characters to write to the console to properly indent Puppets stdout output (without the -1 calculation, it's indented with two spaces). - Includes a test to ensure that we can detect when `string_encode` is updated in the future, when we move to a version of Ruby where the String#encode bug is fixed for encodings with multi-byte terminators Paired-with Josh Cooper --- lib/puppet/util/colors.rb | 6 +++--- spec/unit/util/colors_spec.rb | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/puppet/util/colors.rb b/lib/puppet/util/colors.rb index f55dc9fcb..622c3b583 100644 --- a/lib/puppet/util/colors.rb +++ b/lib/puppet/util/colors.rb @@ -84,7 +84,7 @@ module Puppet::Util::Colors begin require 'Win32API' require 'win32console' - require 'windows/wide_string' + require 'puppet/util/windows/string' # The win32console gem uses ANSI functions for writing to the console # which doesn't work for unicode strings, e.g. module tool. Ruby 1.9 @@ -113,8 +113,8 @@ module Puppet::Util::Colors end def string_encode(str) - wstr = str.encode('UTF-16LE') - [wstr, wstr.length] + wstr = Puppet::Util::Windows::String.wide_string(str) + [wstr, wstr.length - 1] end end diff --git a/spec/unit/util/colors_spec.rb b/spec/unit/util/colors_spec.rb index f114894da..7407b628b 100755 --- a/spec/unit/util/colors_spec.rb +++ b/spec/unit/util/colors_spec.rb @@ -66,4 +66,18 @@ describe Puppet::Util::Colors do end end end + + describe "on Windows", :if => Puppet.features.microsoft_windows? do + it "expects a trailing embedded NULL character in the wide string" do + message = "hello" + + console = Puppet::Util::Colors::WideConsole.new + wstr, nchars = console.string_encode(message) + + expect(nchars).to eq(message.length) + + expect(wstr.length).to eq(nchars + 1) + expect(wstr[-1].ord).to be_zero + end + end end From 3156ca14905ae0f59a6267c9146352ccb5d82fe1 Mon Sep 17 00:00:00 2001 From: Adrien Thebo Date: Tue, 28 Jan 2014 10:56:21 -0800 Subject: [PATCH 499/800] (maint) Don't duplicate hash keys in rspec tests RSpec examples can be restricted by using `:if`: and `:unless` values in the example metadata. However these are hash keys, and defining two `:if` statements will cause the latter definition to overwrite the first. This commit resolves this issue by defining a single expression evaluated by `:if` instead of having two separate clauses. --- spec/unit/indirector/facts/facter_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/unit/indirector/facts/facter_spec.rb b/spec/unit/indirector/facts/facter_spec.rb index 6d842d2c1..925830d32 100755 --- a/spec/unit/indirector/facts/facter_spec.rb +++ b/spec/unit/indirector/facts/facter_spec.rb @@ -144,14 +144,14 @@ describe Puppet::Node::Facts::Facter do end it "should include pluginfactdest when loading external facts", - :if => Puppet.features.external_facts?, :unless => Puppet.features.microsoft_windows? do + :if => (Puppet.features.external_facts? and not Puppet.features.microsoft_windows?) do Puppet[:pluginfactdest] = "/plugin/dest" @facter.find(@request) Facter.search_external_path.include?("/plugin/dest") end it "should include pluginfactdest when loading external facts", - :if => Puppet.features.external_facts?, :if => Puppet.features.microsoft_windows? do + :if => (Puppet.features.external_facts? and Puppet.features.microsoft_windows?) do Puppet[:pluginfactdest] = "/plugin/dest" @facter.find(@request) Facter.search_external_path.include?("C:/plugin/dest") From c5162bc16827155c107141a4d7c2d5947d575000 Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Tue, 28 Jan 2014 12:38:01 -0800 Subject: [PATCH 500/800] (maint) Fix another broken rspec restriction --- spec/integration/application/doc_spec.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/integration/application/doc_spec.rb b/spec/integration/application/doc_spec.rb index 312c787b8..77fc38625 100755 --- a/spec/integration/application/doc_spec.rb +++ b/spec/integration/application/doc_spec.rb @@ -6,7 +6,8 @@ require 'puppet/application/doc' describe Puppet::Application::Doc do include PuppetSpec::Files - it "should not generate an error when module dir overlaps parent of site.pp (#4798)", :if => Puppet.features.rdoc1?, :unless => Puppet.features.microsoft_windows? do + it "should not generate an error when module dir overlaps parent of site.pp (#4798)", + :if => (Puppet.features.rdoc1? and not Puppet.features.microsoft_windows?) do begin # Note: the directory structure below is more complex than it # needs to be, but it's representative of the directory structure From 273f2d6537f0568de857daedebfe0c2f4ee8d6e1 Mon Sep 17 00:00:00 2001 From: Adrien Thebo Date: Tue, 28 Jan 2014 14:54:45 -0800 Subject: [PATCH 501/800] Revert "Merge branch 'pull-2137'" This reverts commit a20b16bb70446d43c51340f72c821fc3a93c7cb6, reversing changes made to 00579976264da4b2b8492bb664291b9ba0d5987c. Unfortunately this change introduced a number of problems that need to be addressed before this code can be released. 1) The yum provider is always holdable The package type detects the ability to hold packages based on the presence of the #hold method. Even though there is a guarding clause around the `has_feature :holdable` in the provider it's bypassed when it fails since the provider defines the #hold method. This ties into: 2) Holding a package without the yum-plugin-versionlock raises errors Since the plugin isn't available but the provider looks like it's holdable, applying a resource like: package { 'bash': ensure => held, } results in a backtrace since `yum versionlock` returns 1, and a Puppet::ExecutionFailure is raised. 3) Held packages are not idempotent When the yum versionlock plugin is installed, the resulting resource is not idempotent because the ensure value is always being changed from $version to 'held': [root@localhost puppet]# puppet resource package bash ensure=held Notice: /Package[bash]/ensure: ensure changed '4.1.2-15.el6_4' to 'held' package { 'bash': ensure => '4.1.2-15.el6_4', } [root@localhost puppet]# puppet resource package bash ensure=held Notice: /Package[bash]/ensure: ensure changed '4.1.2-15.el6_4' to 'held' package { 'bash': ensure => '4.1.2-15.el6_4', } --- lib/puppet/provider/package/yum.rb | 34 -------------------------- spec/unit/provider/package/yum_spec.rb | 31 ++--------------------- 2 files changed, 2 insertions(+), 63 deletions(-) diff --git a/lib/puppet/provider/package/yum.rb b/lib/puppet/provider/package/yum.rb index 99b7fb198..c32052d3d 100644 --- a/lib/puppet/provider/package/yum.rb +++ b/lib/puppet/provider/package/yum.rb @@ -27,16 +27,6 @@ Puppet::Type.type(:package).provide :yum, :parent => :rpm, :source => :rpm do defaultfor :operatingsystem => [:fedora, :centos, :redhat] - if command('yum') - Puppet.debug('Checking if yum supports versionlock.') - begin - yum('versionlock') - has_feature :holdable - rescue Puppet::ExecutionFailure - false - end - end - def self.prefetch(packages) raise Puppet::Error, "The yum provider can only be used as root" if Process.euid != 0 super @@ -84,12 +74,6 @@ Puppet::Type.type(:package).provide :yum, :parent => :rpm, :source => :rpm do end end - # Unhold before installing - if self.class.declared_feature?(:holdable) - Puppet.debug('Provider supports holdable. Unholding package before installing...') - self.unhold - end - yum "-d", "0", "-e", "0", "-y", operation, wanted is = self.query @@ -123,22 +107,4 @@ Puppet::Type.type(:package).provide :yum, :parent => :rpm, :source => :rpm do def purge yum "-y", :erase, @resource[:name] end - - def hold - # Install before locking the version. - self.install - yum('versionlock', @resource[:name]) - end - - def unhold - yum('versionlock', 'delete', "*#{@resource[:name]}*") - rescue Puppet::ExecutionFailure => e - if e.message.match /versionlock delete: no matches/ - # No versionlock present for this package - return true - else - # If it's not a no match failure, then something else went wrong... - raise Puppet::Error, "Failed to unhold package #{@resource[:name]}: #{e.message}", e.backtrace - end - end end diff --git a/spec/unit/provider/package/yum_spec.rb b/spec/unit/provider/package/yum_spec.rb index 1b795a97b..23d49cab4 100755 --- a/spec/unit/provider/package/yum_spec.rb +++ b/spec/unit/provider/package/yum_spec.rb @@ -17,8 +17,8 @@ describe provider do @provider.stubs(:get).with(:release).returns '1' @provider.stubs(:get).with(:arch).returns 'i386' end - # provider should respond to the following methods - [:install, :latest, :update, :purge, :hold, :unhold].each do |method| + # provider should repond to the following methods + [:install, :latest, :update, :purge].each do |method| it "should have a(n) #{method}" do @provider.should respond_to(method) end @@ -55,33 +55,6 @@ describe provider do @provider.stubs(:query).returns(:ensure => '1.2').then.returns(:ensure => '1.0') @provider.install end - - describe "when the provider is holdable" do - before do - provider.stubs(:declared_feature?).with(:holdable).returns true - end - - it 'can hold packages' do - @resource.stubs(:should).with(:ensure).returns :held - @provider.expects(:yum).with('versionlock', 'mypackage') - @provider.hold - end - - it "installs packages before holding them" do - @resource.stubs(:should).with(:ensure).returns :held - @provider.expects(:yum).with('versionlock', 'mypackage') - @provider.expects(:install) - @provider.hold - end - - it "unholds packages before installing" do - @resource.stubs(:should).with(:ensure).returns :installed - @provider.expects(:yum).with('-d', '0', '-e', '0', '-y', :install, 'mypackage') - @provider.expects(:unhold) - @provider.install - end - - end end describe 'when uninstalling' do From 21ddae171291baddb15e14e635678e4836c9191f Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Mon, 27 Jan 2014 11:46:52 -0800 Subject: [PATCH 502/800] (maint) Adding .ruby-version and .ruby-gemset to .gitignore for RVM users. --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 7466a53aa..873839586 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,5 @@ Gemfile.local puppet-acceptance/ /.project .idea/ +.ruby-version +.ruby-gemset From da5bc0ed3dbc3c33e5893b9602e28fb017b83e86 Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Tue, 28 Jan 2014 16:24:03 -0800 Subject: [PATCH 503/800] (maint) Fix acceptance test failure caused by backtrace preservation change (GH-2293). We should not be accessing the exception object from the else clause of the begin/rescue/else. --- lib/puppet/module_tool/applications/upgrader.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/module_tool/applications/upgrader.rb b/lib/puppet/module_tool/applications/upgrader.rb index b45f6cf61..36ccb8021 100644 --- a/lib/puppet/module_tool/applications/upgrader.rb +++ b/lib/puppet/module_tool/applications/upgrader.rb @@ -54,7 +54,7 @@ module Puppet::ModuleTool rescue => e raise UnknownModuleError, results.merge(:repository => @forge.uri), e.backtrace else - raise UnknownVersionError, results.merge(:repository => @forge.uri), e.backtrace if @remote.empty? + raise UnknownVersionError, results.merge(:repository => @forge.uri) if @remote.empty? end if !@options[:force] && @versions["#{@module_name}"].last[:vstring].sub(/^(?=\d)/, 'v') == (@module.version || '0.0.0').sub(/^(?=\d)/, 'v') From f2801cae6c897df263181dd2e03a6b03deaa26d9 Mon Sep 17 00:00:00 2001 From: Charlie Sharpsteen Date: Tue, 28 Jan 2014 17:47:36 -0800 Subject: [PATCH 504/800] (DOCUMENT-22) Clarify matching of crontab entries This updates the documentation to be consistent with behavior introduced in commit da9162c. --- lib/puppet/type/cron.rb | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/puppet/type/cron.rb b/lib/puppet/type/cron.rb index c6d6c3a36..7aa1989ce 100644 --- a/lib/puppet/type/cron.rb +++ b/lib/puppet/type/cron.rb @@ -7,15 +7,14 @@ Puppet::Type.newtype(:cron) do Installs and manages cron jobs. Every cron resource requires a command and user attribute, as well as at least one periodic attribute (hour, minute, month, monthday, weekday, or special). While the name of the cron - job is not part of the actual job, it is used by Puppet to store and - retrieve it. + job is not part of the actual job, the name is stored in a comment beginning with + `# Puppet Name: `. These comments are used to match crontab entries created by + Puppet with cron resources. - If you specify a cron resource that duplicates the scheduling and command - used by an existing crontab entry, then Puppet will take no action and - defers to the existing crontab entry. If the duplicate cron resource - specifies `ensure => absent`, all existing duplicated crontab entries will - be removed. Specifying multiple duplicate cron resources with different - `ensure` states will result in undefined behavior. + If an existing crontab entry happens to match the scheduling and command of a + cron resource that has never been synched, Puppet will defer to the existing + crontab entry and will not create a new entry tagged with the `# Puppet Name: ` + comment. Example: From 2418ebd59c3f2573becd086531ee5061dbc2babc Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Tue, 28 Jan 2014 22:13:03 -0800 Subject: [PATCH 505/800] (pup-640) Update report schema for error events Previously, special handling was added to synthesize a report event even when the property application failed early. However, when this change was made the report schema wasn't updated, so the schema did not allow for several fields in the event to be null (as they are for one of these synthesized events). This change updates the report schema to reflect that certain fields can be null. It also adds a spec test including an error event, which requires the schema change in order to pass. --- api/schemas/report.json | 20 +++++++++++++------- spec/unit/transaction/report_spec.rb | 19 +++++++++++++++++++ 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/api/schemas/report.json b/api/schemas/report.json index 26f1988d4..cf76bfbbc 100644 --- a/api/schemas/report.json +++ b/api/schemas/report.json @@ -266,7 +266,7 @@ "changed", "total", "skipped", - "failed_to_restart", + "failed_to_restart", "restarted", "scheduled" ] @@ -279,7 +279,7 @@ "Changed", "Total", "Skipped", - "Failed to restart", + "Failed to restart", "Restarted", "Scheduled" ] @@ -496,7 +496,7 @@ "required": [ "resource_type", "title", - "change_count", + "change_count", "out_of_sync_count", "tags", "events", @@ -517,7 +517,10 @@ "property": { "description": "The property for which the event occurred.", - "type": "string" + "oneOf": [ + { "type": "string" }, + { "type": "null" } + ] }, "previous_value": { @@ -525,7 +528,8 @@ "oneOf": [ { "type": "string" }, { "type": "array" }, - { "type": "object" } + { "type": "object" }, + { "type": "null" } ] }, @@ -534,7 +538,8 @@ "oneOf": [ { "type": "string" }, { "type": "array" }, - { "type": "object" } + { "type": "object" }, + { "type": "null" } ] }, @@ -543,7 +548,8 @@ "oneOf": [ { "type": "string" }, { "type": "array" }, - { "type": "object" } + { "type": "object" }, + { "type": "null" } ] }, diff --git a/spec/unit/transaction/report_spec.rb b/spec/unit/transaction/report_spec.rb index 32dd280fb..1eafe5ae3 100755 --- a/spec/unit/transaction/report_spec.rb +++ b/spec/unit/transaction/report_spec.rb @@ -401,6 +401,12 @@ describe Puppet::Transaction::Report do expect(report.render).to validate_against('api/schemas/report.json') end + it "generates pson for error report which validates against the report schema" do + Puppet[:report_serialization_format] = "pson" + error_report = generate_report_with_error + expect(error_report.render).to validate_against('api/schemas/report.json') + end + it "can make a round trip through yaml" do Puppet[:report_serialization_format] = "yaml" report = generate_report @@ -475,4 +481,17 @@ describe Puppet::Transaction::Report do report end + def generate_report_with_error + status = Puppet::Resource::Status.new(Puppet::Type.type(:notify).new(:title => "a resource")) + status.changed = true + status.failed_because("bad stuff happened") + + report = Puppet::Transaction::Report.new('apply', 1357986, 'test_environment', "df34516e-4050-402d-a166-05b03b940749") + report << Puppet::Util::Log.new(:level => :warning, :message => "log message") + report.add_times("timing", 4) + report.add_resource_status(status) + report.finalize_report + report + end + end From d474c2eebbaf642d21a4b14102b6b5b5d85f1194 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Mon, 27 Jan 2014 10:00:12 -0800 Subject: [PATCH 506/800] (PUP-1118) Add acceptance test for using directory envs This checks that an environment that comes from the environmentdir setting is usable as an environment. --- .../use_environment_from_environmentdir.rb | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 acceptance/tests/environment/use_environment_from_environmentdir.rb diff --git a/acceptance/tests/environment/use_environment_from_environmentdir.rb b/acceptance/tests/environment/use_environment_from_environmentdir.rb new file mode 100644 index 000000000..dcf12e40c --- /dev/null +++ b/acceptance/tests/environment/use_environment_from_environmentdir.rb @@ -0,0 +1,68 @@ +test_name "Use environments from the environmentdir" + +testdir = master.tmpdir('use_environmentdir') + +apply_manifest_on(master, <<-MANIFEST) +File { + ensure => directory, + owner => puppet, + mode => 0700, +} + +file { + "#{testdir}/environments":; + "#{testdir}/environments/special":; + "#{testdir}/environments/special/manifest":; + "#{testdir}/environments/special/modules":; + "#{testdir}/environments/special/modules/amod":; + "#{testdir}/environments/special/modules/amod/manifests":; + "#{testdir}/environments/special/modules/amod/files":; + "#{testdir}/environments/special/modules/amod/templates":; + "#{testdir}/environments/special/modules/amod/lib":; + "#{testdir}/environments/special/modules/amod/lib/facter":; + + "#{testdir}/environments/special/modules/amod/manifests/init.pp": + ensure => file, + content => 'class amod { notify { template: message => template("amod/our_template.erb") } file { "$agent_file_location/file": source => "puppet:///modules/amod/data" }' + ; + "#{testdir}/environments/special/modules/amod/lib/facter/environment_fact.rb": + ensure => file, + content => "Facter.add(:environment_fact) { setcode { 'environment fact' } }" + ; + "#{testdir}/environments/special/modules/amod/files/data": + ensure => file, + content => "data file" + ; + "#{testdir}/environments/special/modules/amod/templates/our_template.erb": + ensure => file, + content => "<%= @environment_fact %>" + ; + "#{testdir}/environments/special/manifests/site.pp": + ensure => file, + content => "include amod" + ; +} +MANIFEST + +master_opts = { + 'master' => { + 'environmentdir' => "#{testdir}/environments" + } +} + +with_puppet_running_on master, master_opts, testdir do + agents.each do |agent| + atmp = agent.tmpdir('use_environmentdir') + on agent, puppet("agent", "--environment", "special", "-t", "--server", master, "--trace"), :environment => { + "FACTER_agent_file_location" => atmp + } do |result| + assert_match(/environment fact/, result.stdout) + end + + on agent, "cat #{atmp}/file" do |result| + assert_match(/data file/, result.stdout) + end + + on agent, "rm -rf #{atmp}" + end +end From 5c988b988840eddd4081c60f48e924dffe56d6d7 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Mon, 27 Jan 2014 14:23:20 -0800 Subject: [PATCH 507/800] (PUP-1118) Stop using Environment.new Instead of creating environments directly, we need to go through the configured environment loaders, otherwise environments can't come from other places. --- .../use_environment_from_environmentdir.rb | 20 +++-- api/schemas/environments.json | 2 +- lib/puppet/application/doc.rb | 2 +- lib/puppet/context.rb | 2 +- lib/puppet/defaults.rb | 4 +- lib/puppet/environments.rb | 24 +++--- lib/puppet/face/node/clean.rb | 2 +- lib/puppet/face/parser.rb | 2 +- lib/puppet/indirector/node/ldap.rb | 4 +- lib/puppet/indirector/request.rb | 4 +- lib/puppet/indirector/rest.rb | 2 +- lib/puppet/module.rb | 3 +- .../module_tool/applications/upgrader.rb | 2 +- lib/puppet/network/http/api/v1.rb | 3 +- .../network/http/api/v2/environments.rb | 2 +- lib/puppet/network/rights.rb | 4 +- lib/puppet/node.rb | 22 +++--- lib/puppet/node/environment.rb | 20 ----- lib/puppet/parser/files.rb | 17 ++-- lib/puppet/parser/functions.rb | 9 +-- lib/puppet/parser/parser_support.rb | 3 +- lib/puppet/parser/templatewrapper.rb | 2 +- lib/puppet/parser/type_loader.rb | 13 +++- lib/puppet/resource.rb | 34 +++----- lib/puppet/resource/type_collection.rb | 2 +- lib/puppet/test/test_helper.rb | 20 ++++- lib/puppet/util/autoload.rb | 29 +++---- lib/puppet/util/command_line.rb | 2 +- lib/puppet/util/instance_loader.rb | 1 + lib/puppet/util/rdoc.rb | 2 +- .../util/rdoc/parser/puppet_parser_core.rb | 30 +++---- spec/integration/agent/logging_spec.rb | 2 +- .../file_content/file_server_spec.rb | 5 +- spec/integration/node/environment_spec.rb | 28 ++++--- .../resource/type_collection_spec.rb | 7 +- spec/lib/puppet_spec/modules.rb | 2 +- spec/spec_helper.rb | 6 +- spec/unit/face/module/list_spec.rb | 18 +++-- .../indirector/node/active_record_spec.rb | 2 +- spec/unit/indirector/node/ldap_spec.rb | 23 ++++-- spec/unit/indirector/node/plain_spec.rb | 2 +- spec/unit/indirector/request_spec.rb | 23 ++++-- spec/unit/module_spec.rb | 78 +++++++++++-------- .../network/http/api/v2/environments_spec.rb | 19 ++--- spec/unit/node/environment_spec.rb | 22 ------ spec/unit/node_spec.rb | 29 ++++--- spec/unit/parser/ast/collection_spec.rb | 2 +- spec/unit/parser/ast/resource_spec.rb | 2 +- spec/unit/parser/compiler_spec.rb | 8 +- spec/unit/parser/files_spec.rb | 70 +++++++---------- spec/unit/parser/functions_spec.rb | 12 +-- spec/unit/parser/parser_spec.rb | 12 +-- spec/unit/parser/resource_spec.rb | 12 +-- spec/unit/parser/scope_spec.rb | 4 +- spec/unit/parser/type_loader_spec.rb | 4 - spec/unit/rails/host_spec.rb | 2 +- spec/unit/resource/type_collection_spec.rb | 75 +++++++----------- spec/unit/resource/type_spec.rb | 5 +- spec/unit/resource_spec.rb | 70 ++++++++--------- spec/unit/util/autoload_spec.rb | 33 ++------ 60 files changed, 406 insertions(+), 459 deletions(-) diff --git a/acceptance/tests/environment/use_environment_from_environmentdir.rb b/acceptance/tests/environment/use_environment_from_environmentdir.rb index dcf12e40c..4f2a95c40 100644 --- a/acceptance/tests/environment/use_environment_from_environmentdir.rb +++ b/acceptance/tests/environment/use_environment_from_environmentdir.rb @@ -2,7 +2,7 @@ test_name "Use environments from the environmentdir" testdir = master.tmpdir('use_environmentdir') -apply_manifest_on(master, <<-MANIFEST) +apply_manifest_on(master, <<-MANIFEST, :catch_failures => true) File { ensure => directory, owner => puppet, @@ -10,9 +10,10 @@ File { } file { + "#{testdir}":; "#{testdir}/environments":; "#{testdir}/environments/special":; - "#{testdir}/environments/special/manifest":; + "#{testdir}/environments/special/manifests":; "#{testdir}/environments/special/modules":; "#{testdir}/environments/special/modules/amod":; "#{testdir}/environments/special/modules/amod/manifests":; @@ -23,7 +24,10 @@ file { "#{testdir}/environments/special/modules/amod/manifests/init.pp": ensure => file, - content => 'class amod { notify { template: message => template("amod/our_template.erb") } file { "$agent_file_location/file": source => "puppet:///modules/amod/data" }' + content => 'class amod { + notify { template: message => template("amod/our_template.erb") } + file { "$agent_file_location/file": source => "puppet:///modules/amod/data" } + }' ; "#{testdir}/environments/special/modules/amod/lib/facter/environment_fact.rb": ensure => file, @@ -53,9 +57,13 @@ master_opts = { with_puppet_running_on master, master_opts, testdir do agents.each do |agent| atmp = agent.tmpdir('use_environmentdir') - on agent, puppet("agent", "--environment", "special", "-t", "--server", master, "--trace"), :environment => { - "FACTER_agent_file_location" => atmp - } do |result| + on agent, puppet("agent", + "--environment", "special", + "-t", + "--server", master, + "--trace", + 'ENV' => { "FACTER_agent_file_location" => atmp }), + :acceptable_exit_codes => [2] do |result| assert_match(/environment fact/, result.stdout) end diff --git a/api/schemas/environments.json b/api/schemas/environments.json index b9b1f15b7..01cfc4293 100644 --- a/api/schemas/environments.json +++ b/api/schemas/environments.json @@ -36,5 +36,5 @@ } } }, - "required": ["search_path", "environments"] + "required": ["search_paths", "environments"] } diff --git a/lib/puppet/application/doc.rb b/lib/puppet/application/doc.rb index 32a378e6a..99a2ad346 100644 --- a/lib/puppet/application/doc.rb +++ b/lib/puppet/application/doc.rb @@ -171,7 +171,7 @@ HELP exit_code = 0 files = [] unless @manifest - env = Puppet::Node::Environment.new + env = Puppet.lookup(:environments).get(Puppet[:environment]) files += env.modulepath files << ::File.dirname(env[:manifest]) end diff --git a/lib/puppet/context.rb b/lib/puppet/context.rb index 94ffab925..369027599 100644 --- a/lib/puppet/context.rb +++ b/lib/puppet/context.rb @@ -40,7 +40,7 @@ class Puppet::Context elsif block block.call else - raise UndefinedBindingError, "lookup of #{name} in #{@table.inspect} at top of #{@stack.inspect}" + raise UndefinedBindingError, "no '#{name}' in #{@table.inspect} at top of #{@stack.inspect}" end end diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index dfac70309..cb7c3f10a 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -199,7 +199,9 @@ module Puppet :environmentdir => { :default => "$confdir/environments", :desc => "A directory of environments", - :type => :directory + :type => :directory, + :owner => "service", + :group => "service", }, :diff_args => { :default => default_diffargs, diff --git a/lib/puppet/environments.rb b/lib/puppet/environments.rb index 3b805381b..80dec6d69 100644 --- a/lib/puppet/environments.rb +++ b/lib/puppet/environments.rb @@ -122,16 +122,20 @@ module Puppet::Environments def list base = Puppet::FileSystem.path_string(@environment_dir) - Puppet::FileSystem.children(@environment_dir).select do |child| - name = Puppet::FileSystem.basename_string(child) - Puppet::FileSystem.directory?(child) && - Puppet::Node::Environment.valid_name?(name) - end.collect do |child| - name = Puppet::FileSystem.basename_string(child) - Puppet::Node::Environment.create( - name.intern, - [File.join(base, name, "modules")] + @global_module_path, - File.join(base, name, "manifests")) + if Puppet::FileSystem.directory?(@environment_dir) + Puppet::FileSystem.children(@environment_dir).select do |child| + name = Puppet::FileSystem.basename_string(child) + Puppet::FileSystem.directory?(child) && + Puppet::Node::Environment.valid_name?(name) + end.collect do |child| + name = Puppet::FileSystem.basename_string(child) + Puppet::Node::Environment.create( + name.intern, + [File.join(base, name, "modules")] + @global_module_path, + File.join(base, name, "manifests")) + end + else + [] end end diff --git a/lib/puppet/face/node/clean.rb b/lib/puppet/face/node/clean.rb index 20d454e8a..dc1da367e 100644 --- a/lib/puppet/face/node/clean.rb +++ b/lib/puppet/face/node/clean.rb @@ -144,7 +144,7 @@ Puppet::Face.define(:node, '0.0.1') do end def environment - @environment ||= Puppet::Node::Environment.new + @environment ||= Puppet.lookup(:environments).get(Puppet[:environment]) end def type_is_ensurable(resource) diff --git a/lib/puppet/face/parser.rb b/lib/puppet/face/parser.rb index 4ba044f68..cb7aa229b 100644 --- a/lib/puppet/face/parser.rb +++ b/lib/puppet/face/parser.rb @@ -53,7 +53,7 @@ Puppet::Face.define(:parser, '0.0.1') do end def validate_manifest - Puppet::Node::Environment.new(Puppet[:environment]).known_resource_types.clear + Puppet.lookup(:environments).get(Puppet[:environment]).known_resource_types.clear rescue => detail Puppet.log_exception(detail) exit(1) diff --git a/lib/puppet/indirector/node/ldap.rb b/lib/puppet/indirector/node/ldap.rb index 0075b5b4b..37f9c22dd 100644 --- a/lib/puppet/indirector/node/ldap.rb +++ b/lib/puppet/indirector/node/ldap.rb @@ -35,7 +35,7 @@ class Puppet::Node::Ldap < Puppet::Indirector::Ldap next unless info = name2hash(name) merge_parent(info) if info[:parent] - info[:environment] ||= request.environment.to_s + info[:environment] ||= request.environment node = info2node(request.key, info) break end @@ -59,7 +59,7 @@ class Puppet::Node::Ldap < Puppet::Indirector::Ldap return infos.collect do |info| merge_parent(info) if info[:parent] - info[:environment] ||= request.environment.to_s + info[:environment] ||= request.environment info2node(info[:name], info) end end diff --git a/lib/puppet/indirector/request.rb b/lib/puppet/indirector/request.rb index fa40d3a03..cd451745c 100644 --- a/lib/puppet/indirector/request.rb +++ b/lib/puppet/indirector/request.rb @@ -78,14 +78,14 @@ class Puppet::Indirector::Request end def environment - @environment ||= Puppet::Node::Environment.new + @environment ||= Puppet.lookup(:environments).get(Puppet[:environment]) end def environment=(env) @environment = if env.is_a?(Puppet::Node::Environment) env else - Puppet::Node::Environment.new(env) + Puppet.lookup(:environments).get(env) end end diff --git a/lib/puppet/indirector/rest.rb b/lib/puppet/indirector/rest.rb index ea2eaadcd..fbae86344 100644 --- a/lib/puppet/indirector/rest.rb +++ b/lib/puppet/indirector/rest.rb @@ -241,6 +241,6 @@ class Puppet::Indirector::REST < Puppet::Indirector::Terminus end def environment - Puppet::Node::Environment.new + Puppet.lookup(:environments).get(Puppet[:environment]) end end diff --git a/lib/puppet/module.rb b/lib/puppet/module.rb index f562fb414..09661a85b 100644 --- a/lib/puppet/module.rb +++ b/lib/puppet/module.rb @@ -27,7 +27,8 @@ class Puppet::Module # of +path+, return +nil+ def self.find(modname, environment = nil) return nil unless modname - Puppet::Node::Environment.new(environment).module(modname) + env = Puppet.lookup(:environments).get(environment || Puppet[:environment]) + env.module(modname) end attr_reader :name, :environment, :path diff --git a/lib/puppet/module_tool/applications/upgrader.rb b/lib/puppet/module_tool/applications/upgrader.rb index 36ccb8021..cb9d0fa54 100644 --- a/lib/puppet/module_tool/applications/upgrader.rb +++ b/lib/puppet/module_tool/applications/upgrader.rb @@ -6,7 +6,7 @@ module Puppet::ModuleTool def initialize(name, forge, options) @action = :upgrade - @environment = Puppet::Node::Environment.new(Puppet.settings[:environment]) + @environment = Puppet.lookup(:environments).get(Puppet[:environment]) @module_name = name @options = options @force = options[:force] diff --git a/lib/puppet/network/http/api/v1.rb b/lib/puppet/network/http/api/v1.rb index 28540f0f2..79a7ddcbe 100644 --- a/lib/puppet/network/http/api/v1.rb +++ b/lib/puppet/network/http/api/v1.rb @@ -63,7 +63,8 @@ class Puppet::Network::HTTP::API::V1 method = indirection_method(http_method, indirection) - params[:environment] = Puppet::Node::Environment.new(environment) + params[:environment] = Puppet.lookup(:environments).get(environment) + params.delete(:bucket_path) raise ArgumentError, "No request key specified in #{uri}" if key == "" or key.nil? diff --git a/lib/puppet/network/http/api/v2/environments.rb b/lib/puppet/network/http/api/v2/environments.rb index 2be83bb72..de31a8e8d 100644 --- a/lib/puppet/network/http/api/v2/environments.rb +++ b/lib/puppet/network/http/api/v2/environments.rb @@ -7,7 +7,7 @@ class Puppet::Network::HTTP::API::V2::Environments def call(request, response) response.respond_with(200, "application/json", JSON.dump({ - "search_path" => @env_loader.search_paths, + "search_paths" => @env_loader.search_paths, "environments" => Hash[@env_loader.list.collect do |env| [env.name, { "modules" => Hash[env.modules.collect do |mod| diff --git a/lib/puppet/network/rights.rb b/lib/puppet/network/rights.rb index d81473bd1..d7275babf 100644 --- a/lib/puppet/network/rights.rb +++ b/lib/puppet/network/rights.rb @@ -187,8 +187,8 @@ class Rights @methods << m end - def restrict_environment(env) - env = Puppet::Node::Environment.new(env) + def restrict_environment(environment) + env = Puppet.lookup(:environments).get(environment) raise ArgumentError, "'#{env}' is already in the '#{name}' ACL" if @environment.include?(env) @environment << env diff --git a/lib/puppet/node.rb b/lib/puppet/node.rb index 09b307927..4577ea7e3 100644 --- a/lib/puppet/node.rb +++ b/lib/puppet/node.rb @@ -9,9 +9,6 @@ class Puppet::Node # the node sources. extend Puppet::Indirector - # Adds the environment getter and setter, with some instance/string conversion - include Puppet::Node::Environment::Helper - # Use the node source as the indirection terminus. indirects :node, :terminus_setting => :node_terminus, :doc => "Where to find node information. A node is composed of its name, its facts, and its environment." @@ -53,15 +50,22 @@ class Puppet::Node end def environment - return super if @environment - - if env = parameters["environment"] + if @environment + @environment + elsif env = parameters["environment"] self.environment = env - return super + @environment + else + Puppet.lookup(:environments).get(Puppet[:environment]) end + end - # Else, return the default - Puppet::Node::Environment.new + def environment=(env) + if env.is_a?(String) or env.is_a?(Symbol) + @environment = Puppet.lookup(:environments).get(env) + else + @environment = env + end end def initialize(name, options = {}) diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb index 151ee7dca..163d0d9a4 100644 --- a/lib/puppet/node/environment.rb +++ b/lib/puppet/node/environment.rb @@ -44,26 +44,6 @@ end # when {Puppet::Parser::Functions.reset} is called. class Puppet::Node::Environment - # This defines a mixin for classes that have an environment. It implements - # `environment` and `environment=` that respects the semantics of the - # Puppet::Node::Environment class - # - # @api public - module Helper - - def environment - Puppet::Node::Environment.new(@environment) - end - - def environment=(env) - if env.is_a?(String) or env.is_a?(Symbol) - @environment = env - else - @environment = env.name - end - end - end - include Puppet::Util::Cacher # @api private diff --git a/lib/puppet/parser/files.rb b/lib/puppet/parser/files.rb index 809968547..605bbeb69 100644 --- a/lib/puppet/parser/files.rb +++ b/lib/puppet/parser/files.rb @@ -15,7 +15,7 @@ module Puppet; module Parser; module Files def find_manifests_in_modules(pattern, environment) module_name, file_pattern = split_file_path(pattern) begin - if mod = Puppet::Module.find(module_name, environment) + if mod = environment.module(module_name) return [mod.name, mod.match_manifests(file_pattern)] end rescue Puppet::Module::InvalidName @@ -31,7 +31,9 @@ module Puppet; module Parser; module Files # module. # In all cases, an absolute path is returned, which does not # necessarily refer to an existing file - def find_template(template, environment = nil) + # + # @api private + def find_template(template, environment) if template == File.expand_path(template) return template end @@ -53,7 +55,8 @@ module Puppet; module Parser; module Files nil end - def find_template_in_module(template, environment = nil) + # @api private + def find_template_in_module(template, environment) path, file = split_file_path(template) # Because templates don't have an assumed template name, like manifests do, @@ -61,7 +64,7 @@ module Puppet; module Parser; module Files # directory. return nil unless file - if mod = Puppet::Module.find(path, environment) and t = mod.template(file) + if mod = environment.module(path) and t = mod.template(file) return t end nil @@ -69,8 +72,9 @@ module Puppet; module Parser; module Files # Return an array of paths by splitting the +templatedir+ config # parameter. - def templatepath(environment = nil) - dirs = Puppet.settings.value(:templatedir, environment).split(File::PATH_SEPARATOR) + # @api private + def templatepath(environment) + dirs = Puppet.settings.value(:templatedir, environment.to_s).split(File::PATH_SEPARATOR) dirs.select do |p| File::directory?(p) end @@ -78,6 +82,7 @@ module Puppet; module Parser; module Files # Split the path into the module and the rest of the path, or return # nil if the path is empty or absolute (starts with a /). + # @api private def split_file_path(path) if path == "" or Puppet::Util.absolute_path?(path) nil diff --git a/lib/puppet/parser/functions.rb b/lib/puppet/parser/functions.rb index 2ceb2d4f0..45928d193 100644 --- a/lib/puppet/parser/functions.rb +++ b/lib/puppet/parser/functions.rb @@ -41,10 +41,7 @@ module Puppet::Parser::Functions # environment # # @api private - def self.environment_module(env = Puppet.lookup(:current_environment)) - if env and ! env.is_a?(Puppet::Node::Environment) - env = Puppet::Node::Environment.new(env) - end + def self.environment_module(env) @modules[env.name] ||= Module.new end @@ -132,10 +129,10 @@ module Puppet::Parser::Functions # the block must be installed as a method because it may use "return", # which is not allowed from procs. real_fname = "real_function_#{name}" - environment_module.send(:define_method, real_fname, &block) + environment_module(environment).send(:define_method, real_fname, &block) fname = "function_#{name}" - environment_module.send(:define_method, fname) do |*args| + environment_module(environment).send(:define_method, fname) do |*args| Puppet::Util::Profiler.profile("Called #{name}") do if args[0].is_a? Array if arity >= 0 and args[0].size != arity diff --git a/lib/puppet/parser/parser_support.rb b/lib/puppet/parser/parser_support.rb index dedae1bab..963e4d106 100644 --- a/lib/puppet/parser/parser_support.rb +++ b/lib/puppet/parser/parser_support.rb @@ -121,8 +121,7 @@ class Puppet::Parser::Parser end def initialize(env) - # The environment is needed to know how to find the resource type collection. - @environment = env.is_a?(String) ? Puppet::Node::Environment.new(env) : env + @environment = env initvars end diff --git a/lib/puppet/parser/templatewrapper.rb b/lib/puppet/parser/templatewrapper.rb index fc5721626..e4426cdf9 100644 --- a/lib/puppet/parser/templatewrapper.rb +++ b/lib/puppet/parser/templatewrapper.rb @@ -84,7 +84,7 @@ class Puppet::Parser::TemplateWrapper # @api private def file=(filename) - unless @__file__ = Puppet::Parser::Files.find_template(filename, scope.compiler.environment.to_s) + unless @__file__ = Puppet::Parser::Files.find_template(filename, scope.compiler.environment) raise Puppet::ParseError, "Could not find template '#{filename}'" end diff --git a/lib/puppet/parser/type_loader.rb b/lib/puppet/parser/type_loader.rb index 9e03fd8af..398944996 100644 --- a/lib/puppet/parser/type_loader.rb +++ b/lib/puppet/parser/type_loader.rb @@ -5,7 +5,6 @@ require 'puppet/parser/parser_factory' class Puppet::Parser::TypeLoader extend Forwardable - include Puppet::Node::Environment::Helper # Import manifest files that match a given file glob pattern. # @@ -50,6 +49,18 @@ class Puppet::Parser::TypeLoader self.environment = env end + def environment + @environment + end + + def environment=(env) + if env.is_a?(String) or env.is_a?(Symbol) + @environment = Puppet.lookup(:environments).get(env) + else + @environment = env + end + end + # Try to load the object with the given fully qualified name. def try_load_fqname(type, fqname) return nil if fqname == "" # special-case main. diff --git a/lib/puppet/resource.rb b/lib/puppet/resource.rb index 22df43a67..bc8625750 100644 --- a/lib/puppet/resource.rb +++ b/lib/puppet/resource.rb @@ -15,9 +15,6 @@ class Puppet::Resource include Puppet::Util::Tagging - require 'puppet/resource/type_collection_helper' - include Puppet::Resource::TypeCollectionHelper - extend Puppet::Util::Pson include Enumerable attr_accessor :file, :line, :catalog, :exported, :virtual, :validate_parameters, :strict @@ -170,23 +167,6 @@ class Puppet::Resource super || parameters.keys.include?( parameter_name(parameter) ) end - # These two methods are extracted into a Helper - # module, but file load order prevents me - # from including them in the class, and I had weird - # behaviour (i.e., sometimes it didn't work) when - # I directly extended each resource with the helper. - def environment - Puppet::Node::Environment.new(@environment) - end - - def environment=(env) - if env.is_a?(String) or env.is_a?(Symbol) - @environment = env - else - @environment = env.name - end - end - %w{exported virtual strict}.each do |m| define_method(m+"?") do self.send(m) @@ -248,13 +228,21 @@ class Puppet::Resource def resource_type @rstype ||= case type - when "Class"; known_resource_types.hostclass(title == :main ? "" : title) - when "Node"; known_resource_types.node(title) + when "Class"; environment.known_resource_types.hostclass(title == :main ? "" : title) + when "Node"; environment.known_resource_types.node(title) else - Puppet::Type.type(type) || known_resource_types.definition(type) + Puppet::Type.type(type) || environment.known_resource_types.definition(type) end end + def environment + @environment ||= Puppet.lookup(:environments).get(Puppet[:environment]) + end + + def environment=(environment) + @environment = environment + end + # Produce a simple hash of our parameters. def to_hash parse_title.merge parameters diff --git a/lib/puppet/resource/type_collection.rb b/lib/puppet/resource/type_collection.rb index d212199dc..21a953eee 100644 --- a/lib/puppet/resource/type_collection.rb +++ b/lib/puppet/resource/type_collection.rb @@ -17,7 +17,7 @@ class Puppet::Resource::TypeCollection end def initialize(env) - @environment = env.is_a?(String) ? Puppet::Node::Environment.new(env) : env + @environment = env @hostclasses = {} @definitions = {} @nodes = {} diff --git a/lib/puppet/test/test_helper.rb b/lib/puppet/test/test_helper.rb index 545bae5d9..55b952c33 100644 --- a/lib/puppet/test/test_helper.rb +++ b/lib/puppet/test/test_helper.rb @@ -1,5 +1,8 @@ require 'puppet/indirector/data_binding/hiera' +require 'tmpdir' +require 'fileutils' + 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 @@ -33,12 +36,20 @@ module Puppet::Test # that call Puppet. # @return nil def self.initialize() + owner = Process.pid + @environmentdir = Dir.mktmpdir('environments') Puppet.push_context(Puppet.base_context({ - :environmentdir => "/dev/null", + :environmentdir => @environmentdir, :modulepath => "", :manifest => "/dev/null" }), "Initial for specs") Puppet::Parser::Functions.reset + + ObjectSpace.define_finalizer(Puppet.lookup(:environments), proc { + if Process.pid == owner + FileUtils.rm_rf(@environmentdir) + end + }) end # Call this method once, when beginning a test run--prior to running @@ -192,6 +203,13 @@ module Puppet::Test # below 512 bits. Sad, really, because a 0 bit key would be just fine. Puppet[:req_bits] = 512 Puppet[:keylength] = 512 + + # Although we setup a testing context during initialization, some tests + # will end up creating their own context using the real context objects + # and use the setting for the environments. In order to avoid those tests + # having to deal with a missing environmentdir we can just set it right + # here. + Puppet[:environmentdir] = @environmentdir end private_class_method :initialize_settings_before_each end diff --git a/lib/puppet/util/autoload.rb b/lib/puppet/util/autoload.rb index 04e2af7bc..c0429599b 100644 --- a/lib/puppet/util/autoload.rb +++ b/lib/puppet/util/autoload.rb @@ -43,7 +43,8 @@ class Puppet::Util::Autoload name = cleanpath(name).chomp('.rb') return true unless loaded.include?(name) file, old_mtime = loaded[name] - return true unless file == get_file(name) + environment = Puppet.lookup(:environments).get(Puppet[:environment]) + return true unless file == get_file(name, environment) begin old_mtime.to_i != File.mtime(file).to_i rescue Errno::ENOENT @@ -53,7 +54,7 @@ class Puppet::Util::Autoload # Load a single plugin by name. We use 'load' here so we can reload a # given plugin. - def load_file(name, env=nil) + def load_file(name, env) file = get_file(name.to_s, env) return false unless file begin @@ -73,24 +74,24 @@ class Puppet::Util::Autoload # Load every instance of everything we can find. files_to_load(path).each do |file| name = file.chomp(".rb") - load_file(name) unless loaded?(name) + load_file(name, nil) unless loaded?(name) end end def reload_changed - loaded.keys.each { |file| load_file(file) if changed?(file) } + loaded.keys.each { |file| load_file(file, nil) if changed?(file) } end # Get the correct file to load for a given path # returns nil if no file is found - def get_file(name, env=nil) + def get_file(name, env) name = name + '.rb' unless name =~ /\.rb$/ path = search_directories(env).find { |dir| Puppet::FileSystem.exist?(File.join(dir, name)) } path and File.join(path, name) end def files_to_load(path) - search_directories.map {|dir| files_in_dir(dir, path) }.flatten.uniq + search_directories(nil).map {|dir| files_in_dir(dir, path) }.flatten.uniq end def files_in_dir(dir, path) @@ -100,13 +101,7 @@ class Puppet::Util::Autoload end end - def module_directories(env=nil) - # We have to require this late in the process because otherwise we might - # have load order issues. Since require is much slower than defined?, we - # can skip that - and save some 2,155 invocations of require in my real - # world testing. --daniel 2012-07-10 - require 'puppet/node/environment' unless defined?(Puppet::Node::Environment) - + def module_directories(env) # We're using a per-thread cache of module directories so that we don't # scan the filesystem each time we try to load something. This is reset # at the beginning of compilation and at the end of an agent run. @@ -133,10 +128,10 @@ class Puppet::Util::Autoload # "app_defaults_initialized?" method on the main puppet Settings object. # --cprice 2012-03-16 if Puppet.settings.app_defaults_initialized? - real_env = Puppet::Node::Environment.new(env) + env ||= Puppet.lookup(:environments).get(Puppet[:environment]) # if the app defaults have been initialized then it should be safe to access the module path setting. - $env_module_directories[real_env] ||= real_env.modulepath.collect do |dir| + $env_module_directories[env] ||= env.modulepath.collect do |dir| Dir.entries(dir).reject { |f| f =~ /^\./ }.collect { |f| File.join(dir, f, "lib") } end.flatten.find_all do |d| FileTest.directory?(d) @@ -161,7 +156,7 @@ class Puppet::Util::Autoload gem_source.directories end - def search_directories(env=nil) + def search_directories(env) [gem_directories, module_directories(env), libdirs(), $LOAD_PATH].flatten end @@ -196,7 +191,7 @@ class Puppet::Util::Autoload @wrap = true unless defined?(@wrap) end - def load(name, env=nil) + def load(name, env = nil) self.class.load_file(expand(name), env) end diff --git a/lib/puppet/util/command_line.rb b/lib/puppet/util/command_line.rb index 5713cc7df..35a38f5c3 100644 --- a/lib/puppet/util/command_line.rb +++ b/lib/puppet/util/command_line.rb @@ -126,7 +126,7 @@ module Puppet # we try to restrict to only code that can be autoloaded from the node's # environment. if @subcommand_name != 'master' and @subcommand_name != 'agent' - Puppet::Node::Environment.new.each_plugin_directory do |dir| + Puppet.lookup(:environments).get(Puppet[:environment]).each_plugin_directory do |dir| $LOAD_PATH << dir unless $LOAD_PATH.include?(dir) end end diff --git a/lib/puppet/util/instance_loader.rb b/lib/puppet/util/instance_loader.rb index c6d6b71d4..fe0036b82 100644 --- a/lib/puppet/util/instance_loader.rb +++ b/lib/puppet/util/instance_loader.rb @@ -65,6 +65,7 @@ module Puppet::Util::InstanceLoader name = name.intern return nil unless instances = instance_hash(type) unless instances.include? name +# require 'debugger'; debugger if instance_loader(type).load(name) unless instances.include? name Puppet.warning( diff --git a/lib/puppet/util/rdoc.rb b/lib/puppet/util/rdoc.rb index 49784956b..9119784e7 100644 --- a/lib/puppet/util/rdoc.rb +++ b/lib/puppet/util/rdoc.rb @@ -47,7 +47,7 @@ module Puppet::Util::RDoc def manifestdoc(files) Puppet[:ignoreimport] = true files.select { |f| FileTest.file?(f) }.each do |f| - parser = Puppet::Parser::Parser.new(Puppet::Node::Environment.new(Puppet[:environment])) + parser = Puppet::Parser::Parser.new(Puppet.lookup(:environments).get(Puppet[:environment])) parser.file = f ast = parser.parse output(f, ast) diff --git a/lib/puppet/util/rdoc/parser/puppet_parser_core.rb b/lib/puppet/util/rdoc/parser/puppet_parser_core.rb index 0cd1ee5cc..baff91e98 100644 --- a/lib/puppet/util/rdoc/parser/puppet_parser_core.rb +++ b/lib/puppet/util/rdoc/parser/puppet_parser_core.rb @@ -24,20 +24,20 @@ module RDoc::PuppetParserCore # main entry point def scan - environment = Puppet::Node::Environment.new - @known_resource_types = environment.known_resource_types - unless environment.known_resource_types.watching_file?(@input_file_name) + environment = Puppet.lookup(:environments).get(Puppet[:environment]) + known_resource_types = environment.known_resource_types + unless known_resource_types.watching_file?(@input_file_name) Puppet.info "rdoc: scanning #{@input_file_name}" if @input_file_name =~ /\.pp$/ @parser = Puppet::Parser::Parser.new(environment) @parser.file = @input_file_name @parser.parse.instantiate('').each do |type| - @known_resource_types.add type + known_resource_types.add type end end end - scan_top_level(@top_level) + scan_top_level(@top_level, environment) @top_level end @@ -76,7 +76,7 @@ module RDoc::PuppetParserCore # if it does, it returns the module name, otherwise if we are sure # it is part of the global manifest path, "__site__" is returned. # And finally if this path couldn't be mapped anywhere, nil is returned. - def split_module(path) + def split_module(path, environment) # find a module fullpath = File.expand_path(path) Puppet.debug "rdoc: testing #{fullpath}" @@ -84,7 +84,7 @@ module RDoc::PuppetParserCore modpath = $1 name = $2 Puppet.debug "rdoc: module #{name} into #{modpath} ?" - Puppet::Node::Environment.new.modulepath.each do |mp| + environment.modulepath.each do |mp| if File.identical?(modpath,mp) Puppet.debug "rdoc: found module #{name}" return name @@ -95,7 +95,7 @@ module RDoc::PuppetParserCore # there can be paths we don't want to scan under modules # imagine a ruby or manifest that would be distributed as part as a module # but we don't want those to be hosted under - Puppet::Node::Environment.new.modulepath.each do |mp| + environment.modulepath.each do |mp| # check that fullpath is a descendant of mp dirname = fullpath previous = dirname @@ -111,7 +111,7 @@ module RDoc::PuppetParserCore end # create documentation for the top level +container+ - def scan_top_level(container) + def scan_top_level(container, environment) # use the module README as documentation for the module comment = "" %w{README README.rdoc}.each do |rfile| @@ -121,7 +121,7 @@ module RDoc::PuppetParserCore look_for_directives_in(container, comment) unless comment.empty? # infer module name from directory - name = split_module(@input_file_name) + name = split_module(@input_file_name, environment) if name.nil? # skip .pp files that are not in manifests directories as we can't guarantee they're part # of a module or the global configuration. @@ -140,7 +140,7 @@ module RDoc::PuppetParserCore mod.add_comment(comment, @input_file_name) if @input_file_name =~ /\.pp$/ - parse_elements(mod) + parse_elements(mod, environment.known_resource_types) elsif @input_file_name =~ /\.rb$/ parse_plugins(mod) end @@ -323,10 +323,10 @@ module RDoc::PuppetParserCore # Traverse the AST tree and produce code-objects node # that contains the documentation - def parse_elements(container) + def parse_elements(container, known_resource_types) Puppet.debug "rdoc: scanning manifest" - @known_resource_types.hostclasses.values.sort { |a,b| a.name <=> b.name }.each do |klass| + known_resource_types.hostclasses.values.sort { |a,b| a.name <=> b.name }.each do |klass| name = klass.name if klass.file == @input_file_name unless name.empty? @@ -339,13 +339,13 @@ module RDoc::PuppetParserCore end end - @known_resource_types.definitions.each do |name, define| + known_resource_types.definitions.each do |name, define| if define.file == @input_file_name document_define(name,define,container) end end - @known_resource_types.nodes.each do |name, node| + known_resource_types.nodes.each do |name, node| if node.file == @input_file_name document_node(name.to_s,node,container) end diff --git a/spec/integration/agent/logging_spec.rb b/spec/integration/agent/logging_spec.rb index 284928ed7..c686397c8 100755 --- a/spec/integration/agent/logging_spec.rb +++ b/spec/integration/agent/logging_spec.rb @@ -94,7 +94,7 @@ describe 'agent logging' do # # It's not something we are specifically testing here since it occurs # regardless of user flags. - Puppet::Util::Log.expects(:newdestination).with(instance_of(Puppet::Transaction::Report)).once + Puppet::Util::Log.expects(:newdestination).with(instance_of(Puppet::Transaction::Report)).at_least_once expected[:loggers].each do |logclass| Puppet::Util::Log.expects(:newdestination).with(logclass).at_least_once end diff --git a/spec/integration/indirector/file_content/file_server_spec.rb b/spec/integration/indirector/file_content/file_server_spec.rb index bfa2f2017..d35d60b44 100755 --- a/spec/integration/indirector/file_content/file_server_spec.rb +++ b/spec/integration/indirector/file_content/file_server_spec.rb @@ -28,10 +28,9 @@ describe Puppet::Indirector::FileContent::FileServer, " when finding files" do Puppet.settings[:modulepath] = "/no/such/file" - env = Puppet::Node::Environment.new("foo") - env.stubs(:modulepath).returns [path] + env = Puppet::Node::Environment.create(:foo, [path], '') - result = Puppet::FileServing::Content.indirection.search("plugins", :environment => "foo", :recurse => true) + result = Puppet::FileServing::Content.indirection.search("plugins", :environment => env, :recurse => true) result.should_not be_nil result.length.should == 2 diff --git a/spec/integration/node/environment_spec.rb b/spec/integration/node/environment_spec.rb index c7d9c91a1..105c70944 100755 --- a/spec/integration/node/environment_spec.rb +++ b/spec/integration/node/environment_spec.rb @@ -7,6 +7,13 @@ require 'puppet_spec/scope' describe Puppet::Node::Environment do include PuppetSpec::Files + def a_module_in(name, dir) + Dir.mkdir(dir) + moddir = File.join(dir, name) + Dir.mkdir(moddir) + moddir + end + it "should be able to return each module from its environment with the environment, name, and path set correctly" do base = tmpfile("env_modules") Dir.mkdir(base) @@ -16,15 +23,11 @@ describe Puppet::Node::Environment do %w{1 2}.each do |num| dir = File.join(base, "dir#{num}") dirs << dir - Dir.mkdir(dir) - mod = "mod#{num}" - moddir = File.join(dir, mod) - mods[mod] = moddir - Dir.mkdir(moddir) + + mods["mod#{num}"] = a_module_in("mod#{num}", dir) end - environment = Puppet::Node::Environment.new("foo") - environment.stubs(:modulepath).returns dirs + environment = Puppet::Node::Environment.create(:foo, dirs, '') environment.modules.each do |mod| mod.environment.should == environment @@ -37,19 +40,14 @@ describe Puppet::Node::Environment do Dir.mkdir(base) dirs = [] - mods = {} %w{1 2}.each do |num| dir = File.join(base, "dir#{num}") dirs << dir - Dir.mkdir(dir) - mod = "mod" - moddir = File.join(dir, mod) - mods[mod] = moddir - Dir.mkdir(moddir) + + a_module_in("mod", dir) end - environment = Puppet::Node::Environment.new("foo") - environment.stubs(:modulepath).returns dirs + environment = Puppet::Node::Environment.create(:foo, dirs, '') mods = environment.modules mods.length.should == 1 diff --git a/spec/integration/resource/type_collection_spec.rb b/spec/integration/resource/type_collection_spec.rb index db2612ed0..6349460be 100755 --- a/spec/integration/resource/type_collection_spec.rb +++ b/spec/integration/resource/type_collection_spec.rb @@ -10,11 +10,10 @@ describe Puppet::Resource::TypeCollection do before do @dir = tmpfile("autoload_testing") - Puppet[:modulepath] = @dir - FileUtils.mkdir_p @dir - @code = Puppet::Resource::TypeCollection.new("env") - Puppet::Node::Environment.new("env").stubs(:known_resource_types).returns @code + + environment = Puppet::Node::Environment.create(:env, [@dir], '') + @code = environment.known_resource_types end # Setup a module. diff --git a/spec/lib/puppet_spec/modules.rb b/spec/lib/puppet_spec/modules.rb index 1b75bb23a..6835e4434 100644 --- a/spec/lib/puppet_spec/modules.rb +++ b/spec/lib/puppet_spec/modules.rb @@ -4,7 +4,7 @@ module PuppetSpec::Modules module_dir = File.join(dir, name) FileUtils.mkdir_p(module_dir) - environment = Puppet::Node::Environment.new(options[:environment]) + environment = options[:environment] if metadata = options[:metadata] metadata[:source] ||= 'github' diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 9b7af8552..ee7b37d8a 100755 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -164,10 +164,10 @@ RSpec.configure do |config| config.instance_variable_get(:@files_to_run).each { |f| logfile.puts f } end end - # Clean up switch of TMPDIR, don't know if needed after this, so needs to reset it - # to old before removing it + + # return to original tmpdir ENV['TMPDIR'] = oldtmpdir - FileUtils.rm_rf(tmpdir) if Puppet::FileSystem.exist?(tmpdir) && tmpdir.to_s.start_with?(oldtmpdir) + FileUtils.rm_rf(tmpdir) end if ENV['PROFILE'] diff --git a/spec/unit/face/module/list_spec.rb b/spec/unit/face/module/list_spec.rb index 8ac7e8523..9fc4e7121 100644 --- a/spec/unit/face/module/list_spec.rb +++ b/spec/unit/face/module/list_spec.rb @@ -39,15 +39,17 @@ describe "puppet module list" do barmod1 = PuppetSpec::Modules.create('bar', @modpath1) foomod2 = PuppetSpec::Modules.create('foo', @modpath2) - env = Puppet::Node::Environment.new + usedenv = Puppet::Node::Environment.create(:useme, [@modpath1, @modpath2], '') - Puppet::Face[:module, :current].list.should == { - @modpath1 => [ - Puppet::Module.new('bar', barmod1.path, env), - Puppet::Module.new('foo', foomod1.path, env) - ], - @modpath2 => [Puppet::Module.new('foo', foomod2.path, env)] - } + Puppet.override(:environments => Puppet::Environments::Static.new(usedenv)) do + Puppet::Face[:module, :current].list(:environment => 'useme').should == { + @modpath1 => [ + Puppet::Module.new('bar', barmod1.path, usedenv), + Puppet::Module.new('foo', foomod1.path, usedenv) + ], + @modpath2 => [Puppet::Module.new('foo', foomod2.path, usedenv)] + } + end end it "should use the specified environment" do diff --git a/spec/unit/indirector/node/active_record_spec.rb b/spec/unit/indirector/node/active_record_spec.rb index 2365dcb3b..ac3c98bc4 100755 --- a/spec/unit/indirector/node/active_record_spec.rb +++ b/spec/unit/indirector/node/active_record_spec.rb @@ -9,7 +9,7 @@ describe "Puppet::Node::ActiveRecord", :if => Puppet.features.rails? && Puppet.f let(:nodename) { "mynode" } let(:fact_values) { {:afact => "a value"} } let(:facts) { Puppet::Node::Facts.new(nodename, fact_values) } - let(:environment) { Puppet::Node::Environment.new("myenv") } + let(:environment) { Puppet::Node::Environment.create(:myenv, [], '') } let(:request) { Puppet::Indirector::Request.new(:node, :find, nodename, nil, :environment => environment) } let(:node_indirection) { Puppet::Node::ActiveRecord.new } diff --git a/spec/unit/indirector/node/ldap_spec.rb b/spec/unit/indirector/node/ldap_spec.rb index 42c08f72b..c19a30150 100755 --- a/spec/unit/indirector/node/ldap_spec.rb +++ b/spec/unit/indirector/node/ldap_spec.rb @@ -6,7 +6,7 @@ require 'puppet/indirector/node/ldap' describe Puppet::Node::Ldap do let(:nodename) { "mynode.domain.com" } let(:node_indirection) { Puppet::Node::Ldap.new } - let(:environment) { Puppet::Node::Environment.new("myenv") } + let(:environment) { Puppet::Node::Environment.create(:myenv, [], '') } let(:fact_values) { {:afact => "a value", "one" => "boo"} } let(:facts) { Puppet::Node::Facts.new(nodename, fact_values) } @@ -176,10 +176,13 @@ describe Puppet::Node::Ldap do end it "should set the node's environment to the environment of the results" do - result_env = Puppet::Node::Environment.new("local_test") + result_env = Puppet::Node::Environment.create(:local_test, [], '') Puppet::Node::Facts.indirection.stubs(:find).with(nodename, :environment => result_env).returns(facts) @result[:environment] = "local_test" - node_indirection.find(request).environment.should == result_env + + Puppet.override(:environments => Puppet::Environments::Static.new(result_env)) do + node_indirection.find(request).environment.should == result_env + end end it "should retain false parameter values" do @@ -256,17 +259,20 @@ describe Puppet::Node::Ldap do end it "should use the parent's environment if the node has none" do - env = Puppet::Node::Environment.new("parent") + env = Puppet::Node::Environment.create(:parent, [], '') @entry[:parent] = "parent" @parent[:environment] = "parent" Puppet::Node::Facts.indirection.stubs(:find).with(nodename, :environment => env).returns(facts) - node_indirection.find(request).environment.should == env + + Puppet.override(:environments => Puppet::Environments::Static.new(env)) do + node_indirection.find(request).environment.should == env + end end it "should prefer the node's environment to the parent's" do - child_env = Puppet::Node::Environment.new("child") + child_env = Puppet::Node::Environment.create(:child, [], '') @entry[:parent] = "parent" @entry[:environment] = "child" @@ -274,7 +280,10 @@ describe Puppet::Node::Ldap do Puppet::Node::Facts.indirection.stubs(:find).with(nodename, :environment => child_env).returns(facts) - node_indirection.find(request).environment.should == child_env + Puppet.override(:environments => Puppet::Environments::Static.new(child_env)) do + + node_indirection.find(request).environment.should == child_env + end end it "should recursively look up parent information" do diff --git a/spec/unit/indirector/node/plain_spec.rb b/spec/unit/indirector/node/plain_spec.rb index 15cef217d..8e1d0decf 100755 --- a/spec/unit/indirector/node/plain_spec.rb +++ b/spec/unit/indirector/node/plain_spec.rb @@ -7,7 +7,7 @@ describe Puppet::Node::Plain do let(:nodename) { "mynode" } let(:fact_values) { {:afact => "a value"} } let(:facts) { Puppet::Node::Facts.new(nodename, fact_values) } - let(:environment) { Puppet::Node::Environment.new("myenv") } + let(:environment) { Puppet::Node::Environment.create(:myenv, [], '') } let(:request) { Puppet::Indirector::Request.new(:node, :find, nodename, nil, :environment => environment) } let(:node_indirection) { Puppet::Node::Plain.new } diff --git a/spec/unit/indirector/request_spec.rb b/spec/unit/indirector/request_spec.rb index 859d5ba0b..e0d9df339 100755 --- a/spec/unit/indirector/request_spec.rb +++ b/spec/unit/indirector/request_spec.rb @@ -218,21 +218,28 @@ describe Puppet::Indirector::Request do Puppet::Indirector::Request.new(:myind, :find, "my key", nil).escaped_key.should == URI.escape("my key") end - it "should have an environment accessor" do - Puppet::Indirector::Request.new(:myind, :find, "my key", nil, :environment => "foo").should respond_to(:environment) - end - it "should set its environment to an environment instance when a string is specified as its environment" do - Puppet::Indirector::Request.new(:myind, :find, "my key", nil, :environment => "foo").environment.should == Puppet::Node::Environment.new("foo") + env = Puppet::Node::Environment.create(:foo, [], '') + + Puppet.override(:environments => Puppet::Environments::Static.new(env)) do + Puppet::Indirector::Request.new(:myind, :find, "my key", nil, :environment => "foo").environment.should == env + end end it "should use any passed in environment instances as its environment" do - env = Puppet::Node::Environment.new("foo") + env = Puppet::Node::Environment.create(:foo, [], '') + Puppet::Indirector::Request.new(:myind, :find, "my key", nil, :environment => env).environment.should equal(env) end - it "should use the default environment when none is provided" do - Puppet::Indirector::Request.new(:myind, :find, "my key", nil ).environment.should equal(Puppet::Node::Environment.new) + it "should use the configured environment when none is provided" do + configured = Puppet::Node::Environment.create(:foo, [], '') + + Puppet[:environment] = "foo" + + Puppet.override(:environments => Puppet::Environments::Static.new(configured)) do + Puppet::Indirector::Request.new(:myind, :find, "my key", nil).environment.should == configured + end end it "should support converting its options to a hash" do diff --git a/spec/unit/module_spec.rb b/spec/unit/module_spec.rb index 2a0945468..8e3c46989 100755 --- a/spec/unit/module_spec.rb +++ b/spec/unit/module_spec.rb @@ -97,7 +97,6 @@ describe Puppet::Module do it "should list modules that are missing" do metadata_file = "#{@modpath}/needy/metadata.json" - Puppet::FileSystem.expects(:exist?).twice.with(metadata_file).returns true mod = PuppetSpec::Modules.create( 'needy', @modpath, @@ -119,7 +118,6 @@ describe Puppet::Module do it "should list modules that are missing and have invalid names" do metadata_file = "#{@modpath}/needy/metadata.json" - Puppet::FileSystem.expects(:exist?).with(metadata_file).twice.returns true mod = PuppetSpec::Modules.create( 'needy', @modpath, @@ -140,42 +138,43 @@ describe Puppet::Module do end it "should list modules with unmet version requirement" do - ['foobar', 'foobaz'].each do |mod_name| - metadata_file = "#{@modpath}/#{mod_name}/metadata.json" - Puppet::FileSystem.stubs(:exist?).with(metadata_file).returns true - end + env = Puppet::Node::Environment.create(:testing, [@modpath], '') + mod = PuppetSpec::Modules.create( - 'foobar', + 'test_gte_req', @modpath, :metadata => { :dependencies => [{ "version_requirement" => ">= 2.2.0", "name" => "baz/foobar" }] - } + }, + :environment => env ) mod2 = PuppetSpec::Modules.create( - 'foobaz', + 'test_specific_req', @modpath, :metadata => { :dependencies => [{ "version_requirement" => "1.0.0", "name" => "baz/foobar" }] - } + }, + :environment => env ) PuppetSpec::Modules.create( 'foobar', @modpath, - :metadata => { :version => '2.0.0', :author => 'baz' } + :metadata => { :version => '2.0.0', :author => 'baz' }, + :environment => env ) mod.unmet_dependencies.should == [{ :reason => :version_mismatch, :name => "baz/foobar", :version_constraint => ">= 2.2.0", - :parent => { :version => "v9.9.9", :name => "puppetlabs/foobar" }, + :parent => { :version => "v9.9.9", :name => "puppetlabs/test_gte_req" }, :mod_details => { :installed_version => "2.0.0" } }] @@ -183,13 +182,15 @@ describe Puppet::Module do :reason => :version_mismatch, :name => "baz/foobar", :version_constraint => "v1.0.0", - :parent => { :version => "v9.9.9", :name => "puppetlabs/foobaz" }, + :parent => { :version => "v9.9.9", :name => "puppetlabs/test_specific_req" }, :mod_details => { :installed_version => "2.0.0" } }] end it "should consider a dependency without a version requirement to be satisfied" do + env = Puppet::Node::Environment.create(:testing, [@modpath], '') + mod = PuppetSpec::Modules.create( 'foobar', @modpath, @@ -197,7 +198,8 @@ describe Puppet::Module do :dependencies => [{ "name" => "baz/foobar" }] - } + }, + :environment => env ) PuppetSpec::Modules.create( 'foobar', @@ -205,15 +207,16 @@ describe Puppet::Module do :metadata => { :version => '2.0.0', :author => 'baz' - } + }, + :environment => env ) mod.unmet_dependencies.should be_empty end it "should consider a dependency without a semantic version to be unmet" do - metadata_file = "#{@modpath}/foobar/metadata.json" - Puppet::FileSystem.expects(:exist?).with(metadata_file).times(3).returns true + env = Puppet::Node::Environment.create(:testing, [@modpath], '') + mod = PuppetSpec::Modules.create( 'foobar', @modpath, @@ -221,7 +224,8 @@ describe Puppet::Module do :dependencies => [{ "name" => "baz/foobar" }] - } + }, + :environment => env ) PuppetSpec::Modules.create( 'foobar', @@ -229,7 +233,8 @@ describe Puppet::Module do :metadata => { :version => '5.1', :author => 'baz' - } + }, + :environment => env ) mod.unmet_dependencies.should == [{ @@ -254,10 +259,8 @@ describe Puppet::Module do end it "should only list unmet dependencies" do - [name, 'satisfied'].each do |mod_name| - metadata_file = "#{@modpath}/#{mod_name}/metadata.json" - Puppet::FileSystem.expects(:exist?).with(metadata_file).twice.returns true - end + env = Puppet::Node::Environment.create(:testing, [@modpath], '') + mod = PuppetSpec::Modules.create( name, @modpath, @@ -272,7 +275,8 @@ describe Puppet::Module do "name" => "baz/notsatisfied" } ] - } + }, + :environment => env ) PuppetSpec::Modules.create( 'satisfied', @@ -280,7 +284,8 @@ describe Puppet::Module do :metadata => { :version => '3.3.0', :author => 'baz' - } + }, + :environment => env ) mod.unmet_dependencies.should == [{ @@ -293,6 +298,8 @@ describe Puppet::Module do end it "should be empty when all dependencies are met" do + env = Puppet::Node::Environment.create(:testing, [@modpath], '') + mod = PuppetSpec::Modules.create( 'mymod2', @modpath, @@ -307,7 +314,8 @@ describe Puppet::Module do "name" => "baz/alsosatisfied" } ] - } + }, + :environment => env ) PuppetSpec::Modules.create( 'satisfied', @@ -315,7 +323,8 @@ describe Puppet::Module do :metadata => { :version => '3.3.0', :author => 'baz' - } + }, + :environment => env ) PuppetSpec::Modules.create( 'alsosatisfied', @@ -323,7 +332,8 @@ describe Puppet::Module do :metadata => { :version => '2.1.0', :author => 'baz' - } + }, + :environment => env ) mod.unmet_dependencies.should be_empty @@ -652,11 +662,13 @@ describe Puppet::Module do end it "should know what other modules require it" do - Puppet.settings[:modulepath] = @modpath + env = Puppet::Node::Environment.create(:testing, [@modpath], '') + dependable = PuppetSpec::Modules.create( 'dependable', @modpath, - :metadata => {:author => 'puppetlabs'} + :metadata => {:author => 'puppetlabs'}, + :environment => env ) PuppetSpec::Modules.create( 'needy', @@ -667,7 +679,8 @@ describe Puppet::Module do "version_requirement" => ">= 2.2.0", "name" => "puppetlabs/dependable" }] - } + }, + :environment => env ) PuppetSpec::Modules.create( 'wantit', @@ -678,7 +691,8 @@ describe Puppet::Module do "version_requirement" => "< 5.0.0", "name" => "puppetlabs/dependable" }] - } + }, + :environment => env ) dependable.required_by.should =~ [ { diff --git a/spec/unit/network/http/api/v2/environments_spec.rb b/spec/unit/network/http/api/v2/environments_spec.rb index adeea57be..394587238 100644 --- a/spec/unit/network/http/api/v2/environments_spec.rb +++ b/spec/unit/network/http/api/v2/environments_spec.rb @@ -8,7 +8,9 @@ describe Puppet::Network::HTTP::API::V2::Environments do include JSONMatchers it "responds with all of the available environments environments" do - handler = Puppet::Network::HTTP::API::V2::Environments.new(TestingEnvironmentLoader.new) + environment = FakeEnvironment.create(:production, [], '') + loader = Puppet::Environments::Static.new(environment) + handler = Puppet::Network::HTTP::API::V2::Environments.new(loader) response = Puppet::Network::HTTP::MemoryResponse.new handler.call(Puppet::Network::HTTP::Request.from_hash(:headers => { 'accept' => 'application/json' }), response) @@ -16,7 +18,7 @@ describe Puppet::Network::HTTP::API::V2::Environments do expect(response.code).to eq(200) expect(response.type).to eq("application/json") expect(JSON.parse(response.body)).to eq({ - "search_path" => ["file:///fake"], + "search_paths" => loader.search_paths, "environments" => { "production" => { "modules" => { @@ -30,7 +32,8 @@ describe Puppet::Network::HTTP::API::V2::Environments do end it "the response conforms to the environments schema" do - handler = Puppet::Network::HTTP::API::V2::Environments.new(TestingEnvironmentLoader.new) + environment = FakeEnvironment.create(:production, [], '') + handler = Puppet::Network::HTTP::API::V2::Environments.new(Puppet::Environments::Static.new(environment)) response = Puppet::Network::HTTP::MemoryResponse.new handler.call(Puppet::Network::HTTP::Request.from_hash(:headers => { 'accept' => 'application/json' }), response) @@ -38,16 +41,6 @@ describe Puppet::Network::HTTP::API::V2::Environments do expect(response.body).to validate_against('api/schemas/environments.json') end - class TestingEnvironmentLoader - def search_paths - ["file:///fake"] - end - - def list - [FakeEnvironment.new(:production)] - end - end - class FakeEnvironment < Puppet::Node::Environment def modules fake_module = Puppet::Module.new('testing', '/somewhere/on/disk', self) diff --git a/spec/unit/node/environment_spec.rb b/spec/unit/node/environment_spec.rb index fbe791972..0953e0349 100755 --- a/spec/unit/node/environment_spec.rb +++ b/spec/unit/node/environment_spec.rb @@ -346,28 +346,6 @@ describe Puppet::Node::Environment do end end - describe Puppet::Node::Environment::Helper do - before do - @helper = Object.new - @helper.extend(Puppet::Node::Environment::Helper) - end - - it "should be able to set and retrieve the environment as a symbol" do - @helper.environment = :foo - @helper.environment.name.should == :foo - end - - it "should accept an environment directly" do - @helper.environment = Puppet::Node::Environment.new(:foo) - @helper.environment.name.should == :foo - end - - it "should accept an environment as a string" do - @helper.environment = 'foo' - @helper.environment.name.should == :foo - end - end - describe "when performing initial import" do def parser_and_environment(name) env = Puppet::Node::Environment.new(name) diff --git a/spec/unit/node_spec.rb b/spec/unit/node_spec.rb index ae0a33f62..11f24729e 100755 --- a/spec/unit/node_spec.rb +++ b/spec/unit/node_spec.rb @@ -5,29 +5,36 @@ require 'matchers/json' describe Puppet::Node do include JSONMatchers + let(:environment) { Puppet::Node::Environment.create(:bar, [], '') } + let(:env_loader) { Puppet::Environments::Static.new(environment) } + it "should register its document type as Node" do PSON.registered_document_types["Node"].should equal(Puppet::Node) end describe "when managing its environment" do it "should use any set environment" do - Puppet::Node.new("foo", :environment => "bar").environment.name.should == :bar + Puppet.override(:environments => env_loader) do + Puppet::Node.new("foo", :environment => "bar").environment.should == environment + end end it "should support providing an actual environment instance" do - Puppet::Node.new("foo", :environment => Puppet::Node::Environment.new(:bar)).environment.name.should == :bar + Puppet::Node.new("foo", :environment => environment).environment.name.should == :bar end it "should determine its environment from its parameters if no environment is set" do - Puppet::Node.new("foo", :parameters => {"environment" => :bar}).environment.name.should == :bar + Puppet.override(:environments => env_loader) do + Puppet::Node.new("foo", :parameters => {"environment" => :bar}).environment.should == environment + end end - it "should use the default environment if no environment is provided" do - Puppet::Node.new("foo").environment.name.should == Puppet::Node::Environment.new.name - end + it "should use the configured environment if no environment is provided" do + Puppet[:environment] = environment.name.to_s - it "should always return an environment instance rather than a string" do - Puppet::Node.new("foo").environment.should be_instance_of(Puppet::Node::Environment) + Puppet.override(:environments => env_loader) do + Puppet::Node.new("foo").environment.should == environment + end end it "should allow the environment to be set after initialization" do @@ -151,8 +158,10 @@ describe Puppet::Node do end it "should include the environment" do - @node.environment = "production" - Puppet::Node.should read_json_attribute('environment').from(@node.to_pson).as(Puppet::Node::Environment.new(:production)) + Puppet.override(:environments => env_loader) do + @node.environment = environment + Puppet::Node.should read_json_attribute('environment').from(@node.to_pson).as(environment) + end end end end diff --git a/spec/unit/parser/ast/collection_spec.rb b/spec/unit/parser/ast/collection_spec.rb index bd4f5f82f..a5e40b2c3 100755 --- a/spec/unit/parser/ast/collection_spec.rb +++ b/spec/unit/parser/ast/collection_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' describe Puppet::Parser::AST::Collection do before :each do @mytype = Puppet::Resource::Type.new(:definition, "mytype") - @environment = Puppet::Node::Environment.new + @environment = Puppet::Node::Environment.create(:testing, [], '') @environment.known_resource_types.add @mytype @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("foonode", :environment => @environment)) diff --git a/spec/unit/parser/ast/resource_spec.rb b/spec/unit/parser/ast/resource_spec.rb index abf815c39..00aa263ff 100755 --- a/spec/unit/parser/ast/resource_spec.rb +++ b/spec/unit/parser/ast/resource_spec.rb @@ -97,7 +97,7 @@ describe Puppet::Parser::AST::Resource do describe "when generating qualified resources" do before do @scope = Puppet::Parser::Scope.new Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) - @parser = Puppet::Parser::Parser.new(Puppet::Node::Environment.new) + @parser = Puppet::Parser::Parser.new(@scope.environment) ["one", "one::two", "three"].each do |name| @parser.environment.known_resource_types.add(Puppet::Resource::Type.new(:definition, name, {})) end diff --git a/spec/unit/parser/compiler_spec.rb b/spec/unit/parser/compiler_spec.rb index 68210a4df..8a6621520 100755 --- a/spec/unit/parser/compiler_spec.rb +++ b/spec/unit/parser/compiler_spec.rb @@ -66,13 +66,15 @@ describe Puppet::Parser::Compiler do now = Time.now Time.stubs(:now).returns(now) - @node = Puppet::Node.new("testnode", :facts => Puppet::Node::Facts.new("facts", {})) - @known_resource_types = Puppet::Resource::TypeCollection.new "development" + environment = Puppet::Node::Environment.create(:testing, [], '') + @node = Puppet::Node.new("testnode", + :facts => Puppet::Node::Facts.new("facts", {}), + :environment => environment) + @known_resource_types = environment.known_resource_types @compiler = Puppet::Parser::Compiler.new(@node) @scope = Puppet::Parser::Scope.new(@compiler, :source => stub('source')) @scope_resource = Puppet::Parser::Resource.new(:file, "/my/file", :scope => @scope) @scope.resource = @scope_resource - @compiler.environment.stubs(:known_resource_types).returns @known_resource_types end it "should have a class method that compiles, converts, and returns a catalog" do diff --git a/spec/unit/parser/files_spec.rb b/spec/unit/parser/files_spec.rb index 3813260b4..02ef3d5cf 100755 --- a/spec/unit/parser/files_spec.rb +++ b/spec/unit/parser/files_spec.rb @@ -6,6 +6,8 @@ require 'puppet/parser/files' describe Puppet::Parser::Files do include PuppetSpec::Files + let(:environment) { Puppet::Node::Environment.create(:testing, [], '') } + before do @basepath = make_absolute("/somepath") end @@ -13,15 +15,15 @@ describe Puppet::Parser::Files do describe "when searching for templates" do it "should return fully-qualified templates directly" do Puppet::Parser::Files.expects(:modulepath).never - Puppet::Parser::Files.find_template(@basepath + "/my/template").should == @basepath + "/my/template" + Puppet::Parser::Files.find_template(@basepath + "/my/template", environment).should == @basepath + "/my/template" end it "should return the template from the first found module" do mod = mock 'module' - Puppet::Node::Environment.new.expects(:module).with("mymod").returns mod - mod.expects(:template).returns("/one/mymod/templates/mytemplate") - Puppet::Parser::Files.find_template("mymod/mytemplate").should == "/one/mymod/templates/mytemplate" + environment.expects(:module).with("mymod").returns mod + + Puppet::Parser::Files.find_template("mymod/mytemplate", environment).should == "/one/mymod/templates/mytemplate" end it "should return the file in the templatedir if it exists" do @@ -29,101 +31,91 @@ describe Puppet::Parser::Files do Puppet[:modulepath] = "/one:/two" File.stubs(:directory?).returns(true) Puppet::FileSystem.stubs(:exist?).returns(true) - Puppet::Parser::Files.find_template("mymod/mytemplate").should == File.join(Puppet[:templatedir], "mymod/mytemplate") + Puppet::Parser::Files.find_template("mymod/mytemplate", environment).should == File.join(Puppet[:templatedir], "mymod/mytemplate") end it "should not raise an error if no valid templatedir exists and the template exists in a module" do mod = mock 'module' - Puppet::Node::Environment.new.expects(:module).with("mymod").returns mod - mod.expects(:template).returns("/one/mymod/templates/mytemplate") - Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(nil) + environment.expects(:module).with("mymod").returns mod + Puppet::Parser::Files.stubs(:templatepath).with(environment).returns(nil) - Puppet::Parser::Files.find_template("mymod/mytemplate").should == "/one/mymod/templates/mytemplate" + Puppet::Parser::Files.find_template("mymod/mytemplate", environment).should == "/one/mymod/templates/mytemplate" end it "should return unqualified templates if they exist in the template dir" do Puppet::FileSystem.stubs(:exist?).returns true - Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(["/my/templates"]) - Puppet::Parser::Files.find_template("mytemplate").should == "/my/templates/mytemplate" + Puppet::Parser::Files.stubs(:templatepath).with(environment).returns(["/my/templates"]) + + Puppet::Parser::Files.find_template("mytemplate", environment).should == "/my/templates/mytemplate" end it "should only return templates if they actually exist" do Puppet::FileSystem.expects(:exist?).with("/my/templates/mytemplate").returns true - Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(["/my/templates"]) - Puppet::Parser::Files.find_template("mytemplate").should == "/my/templates/mytemplate" + Puppet::Parser::Files.stubs(:templatepath).with(environment).returns(["/my/templates"]) + Puppet::Parser::Files.find_template("mytemplate", environment).should == "/my/templates/mytemplate" end it "should return nil when asked for a template that doesn't exist" do Puppet::FileSystem.expects(:exist?).with("/my/templates/mytemplate").returns false - Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(["/my/templates"]) - Puppet::Parser::Files.find_template("mytemplate").should be_nil - end - - it "should search in the template directories before modules" do - Puppet::FileSystem.stubs(:exist?).returns true - Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(["/my/templates"]) - Puppet::Module.expects(:find).never - Puppet::Parser::Files.find_template("mytemplate") + Puppet::Parser::Files.stubs(:templatepath).with(environment).returns(["/my/templates"]) + Puppet::Parser::Files.find_template("mytemplate", environment).should be_nil end it "should accept relative templatedirs" do Puppet::FileSystem.stubs(:exist?).returns true Puppet[:templatedir] = "my/templates" File.expects(:directory?).with(File.expand_path("my/templates")).returns(true) - Puppet::Parser::Files.find_template("mytemplate").should == File.expand_path("my/templates/mytemplate") + Puppet::Parser::Files.find_template("mytemplate", environment).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 Puppet::FileSystem.stubs(:exist?).returns true - Puppet::Parser::Files.stubs(:templatepath).with("myenv").returns(["/myenv/templates"]) - Puppet::Parser::Files.find_template("mymod/mytemplate", "myenv").should == "/myenv/templates/mymod/mytemplate" + Puppet::Parser::Files.stubs(:templatepath).with(environment).returns(["/myenv/templates"]) + Puppet::Parser::Files.find_template("mymod/mytemplate", environment).should == "/myenv/templates/mymod/mytemplate" end it "should use first dir from environment templatedir if no module is found and an environment is specified" do Puppet::FileSystem.stubs(:exist?).returns true - Puppet::Parser::Files.stubs(:templatepath).with("myenv").returns(["/myenv/templates", "/two/templates"]) - Puppet::Parser::Files.find_template("mymod/mytemplate", "myenv").should == "/myenv/templates/mymod/mytemplate" + Puppet::Parser::Files.stubs(:templatepath).with(environment).returns(["/myenv/templates", "/two/templates"]) + Puppet::Parser::Files.find_template("mymod/mytemplate", environment).should == "/myenv/templates/mymod/mytemplate" end it "should use a valid dir when templatedir is a path for unqualified templates and the first dir contains template" do Puppet::Parser::Files.stubs(:templatepath).returns(["/one/templates", "/two/templates"]) Puppet::FileSystem.expects(:exist?).with("/one/templates/mytemplate").returns(true) - Puppet::Parser::Files.find_template("mytemplate").should == "/one/templates/mytemplate" + Puppet::Parser::Files.find_template("mytemplate", environment).should == "/one/templates/mytemplate" end it "should use a valid dir when templatedir is a path for unqualified templates and only second dir contains template" do Puppet::Parser::Files.stubs(:templatepath).returns(["/one/templates", "/two/templates"]) Puppet::FileSystem.expects(:exist?).with("/one/templates/mytemplate").returns(false) Puppet::FileSystem.expects(:exist?).with("/two/templates/mytemplate").returns(true) - Puppet::Parser::Files.find_template("mytemplate").should == "/two/templates/mytemplate" + Puppet::Parser::Files.find_template("mytemplate", environment).should == "/two/templates/mytemplate" end it "should use the node environment if specified" do mod = mock 'module' - Puppet::Node::Environment.new("myenv").expects(:module).with("mymod").returns mod + environment.expects(:module).with("mymod").returns mod mod.expects(:template).returns("/my/modules/mymod/templates/envtemplate") - Puppet::Parser::Files.find_template("mymod/envtemplate", "myenv").should == "/my/modules/mymod/templates/envtemplate" + Puppet::Parser::Files.find_template("mymod/envtemplate", environment).should == "/my/modules/mymod/templates/envtemplate" end it "should return nil if no template can be found" do - Puppet::Parser::Files.find_template("foomod/envtemplate", "myenv").should be_nil + Puppet::Parser::Files.find_template("foomod/envtemplate", environment).should be_nil end - - after { Puppet.settings.clear } end describe "when searching for manifests" do it "should ignore invalid modules" do mod = mock 'module' - env = Puppet::Node::Environment.new - env.expects(:module).with("mymod").raises(Puppet::Module::InvalidName, "name is invalid") + environment.expects(:module).with("mymod").raises(Puppet::Module::InvalidName, "name is invalid") Puppet.expects(:value).with(:modulepath).never Dir.stubs(:glob).returns %w{foo} - Puppet::Parser::Files.find_manifests_in_modules("mymod/init.pp", env) + Puppet::Parser::Files.find_manifests_in_modules("mymod/init.pp", environment) end end @@ -134,8 +126,6 @@ describe Puppet::Parser::Files do mod.stubs(:match_manifests).with("init.pp").returns(["/one/#{name}/manifests/init.pp"]) end - let(:environment) { Puppet::Node::Environment.new } - it "returns no files when no module is found" do module_name, files = Puppet::Parser::Files.find_manifests_in_modules("not_here_module/foo", environment) expect(files).to be_empty @@ -150,7 +140,7 @@ describe Puppet::Parser::Files do end it "does not find the module when it is a different environment" do - different_env = Puppet::Node::Environment.new("different") + different_env = Puppet::Node::Environment.create(:different, [], '') a_module_in_environment(environment, "mymod") Puppet::Parser::Files.find_manifests_in_modules("mymod/init.pp", different_env).should_not include("mymod") diff --git a/spec/unit/parser/functions_spec.rb b/spec/unit/parser/functions_spec.rb index 8e3f52616..91013586c 100755 --- a/spec/unit/parser/functions_spec.rb +++ b/spec/unit/parser/functions_spec.rb @@ -8,20 +8,14 @@ describe Puppet::Parser::Functions do let(:function_module) { Puppet::Parser::Functions.environment_module(Puppet.lookup(:current_environment)) } + let(:environment) { Puppet::Node::Environment.create(:myenv, [], '') } + before do Puppet::Parser::Functions.reset end it "should have a method for returning an environment-specific module" do - Puppet::Parser::Functions.environment_module(Puppet::Node::Environment.new("myenv")).should be_instance_of(Module) - end - - it "should use the current default environment if no environment is provided" do - Puppet::Parser::Functions.environment_module.should be_instance_of(Module) - end - - it "should be able to retrieve environment modules asked for by name rather than instance" do - Puppet::Parser::Functions.environment_module(Puppet::Node::Environment.new("myenv")).should equal(Puppet::Parser::Functions.environment_module("myenv")) + Puppet::Parser::Functions.environment_module(environment).should be_instance_of(Module) end describe "when calling newfunction" do diff --git a/spec/unit/parser/parser_spec.rb b/spec/unit/parser/parser_spec.rb index 466bfea13..56062d47e 100755 --- a/spec/unit/parser/parser_spec.rb +++ b/spec/unit/parser/parser_spec.rb @@ -19,18 +19,14 @@ describe Puppet::Parser do end it "should set the environment" do - env = Puppet::Node::Environment.new + env = Puppet::Node::Environment.create(:testing, [], '') Puppet::Parser::Parser.new(env).environment.should == env end - it "should convert the environment into an environment instance if a string is provided" do - env = Puppet::Node::Environment.new("testing") - Puppet::Parser::Parser.new("testing").environment.should == env - end - it "should be able to look up the environment-specific resource type collection" do - rtc = Puppet::Node::Environment.new("development").known_resource_types - parser = Puppet::Parser::Parser.new "development" + env = Puppet::Node::Environment.create(:development, [], '') + rtc = env.known_resource_types + parser = Puppet::Parser::Parser.new env parser.known_resource_types.should equal(rtc) end diff --git a/spec/unit/parser/resource_spec.rb b/spec/unit/parser/resource_spec.rb index 74a66d1c1..f78f83982 100755 --- a/spec/unit/parser/resource_spec.rb +++ b/spec/unit/parser/resource_spec.rb @@ -1,15 +1,11 @@ -#! /usr/bin/env ruby require 'spec_helper' -# LAK: FIXME This is just new tests for resources; I have -# not moved all tests over yet. - describe Puppet::Parser::Resource do before do - @node = Puppet::Node.new("yaynode") - @known_resource_types = Puppet::Resource::TypeCollection.new("env") + environment = Puppet::Node::Environment.create(:testing, [], '') + @node = Puppet::Node.new("yaynode", :environment => environment) + @known_resource_types = environment.known_resource_types @compiler = Puppet::Parser::Compiler.new(@node) - @compiler.environment.stubs(:known_resource_types).returns @known_resource_types @source = newclass "" @scope = @compiler.topscope end @@ -131,8 +127,6 @@ describe Puppet::Parser::Resource do describe "when evaluating" do before do - @node = Puppet::Node.new "test-node" - @compiler = Puppet::Parser::Compiler.new @node @catalog = Puppet::Resource::Catalog.new source = stub('source') source.stubs(:module_name) diff --git a/spec/unit/parser/scope_spec.rb b/spec/unit/parser/scope_spec.rb index 2267294a3..b9b10618f 100755 --- a/spec/unit/parser/scope_spec.rb +++ b/spec/unit/parser/scope_spec.rb @@ -64,7 +64,7 @@ describe Puppet::Parser::Scope do end it "should get its environment from its compiler" do - env = Puppet::Node::Environment.new + env = Puppet::Node::Environment.create(:testing, [], '') compiler = stub 'compiler', :environment => env, :is_a? => true scope = Puppet::Parser::Scope.new(compiler) scope.environment.should equal(env) @@ -87,7 +87,7 @@ describe Puppet::Parser::Scope do end describe "when custom functions are called" do - let(:env) { Puppet::Node::Environment.new('testing') } + let(:env) { Puppet::Node::Environment.create(:testing, [], '') } let(:compiler) { Puppet::Parser::Compiler.new(Puppet::Node.new('foo', :environment => env)) } let(:scope) { Puppet::Parser::Scope.new(compiler) } diff --git a/spec/unit/parser/type_loader_spec.rb b/spec/unit/parser/type_loader_spec.rb index c735db7c2..659ffa942 100755 --- a/spec/unit/parser/type_loader_spec.rb +++ b/spec/unit/parser/type_loader_spec.rb @@ -19,10 +19,6 @@ describe Puppet::Parser::TypeLoader do loader.environment.name.should == :myenv end - it "should include the Environment Helper" do - loader.class.ancestors.should be_include(Puppet::Node::Environment::Helper) - end - it "should delegate its known resource types to its environment" do loader.known_resource_types.should be_instance_of(Puppet::Resource::TypeCollection) end diff --git a/spec/unit/rails/host_spec.rb b/spec/unit/rails/host_spec.rb index 81d048ef1..746c3a075 100755 --- a/spec/unit/rails/host_spec.rb +++ b/spec/unit/rails/host_spec.rb @@ -40,7 +40,7 @@ describe "Puppet::Rails::Host", :if => can_use_scratch_database? do it "should stringify the environment" do host = Puppet::Rails::Host.new - host.environment = Puppet::Node::Environment.new("production") + host.environment = Puppet::Node::Environment.create(:production, [], '') host.environment.class.should == String end diff --git a/spec/unit/resource/type_collection_spec.rb b/spec/unit/resource/type_collection_spec.rb index c3a335ce2..4cd5b339e 100755 --- a/spec/unit/resource/type_collection_spec.rb +++ b/spec/unit/resource/type_collection_spec.rb @@ -7,39 +7,20 @@ require 'puppet/resource/type' describe Puppet::Resource::TypeCollection do include PuppetSpec::Files + let(:environment) { Puppet::Node::Environment.create(:testing, [], '') } + before do @instance = Puppet::Resource::Type.new(:hostclass, "foo") - @code = Puppet::Resource::TypeCollection.new("env") - end - - it "should require an environment at initialization" do - env = Puppet::Node::Environment.new("testing") - Puppet::Resource::TypeCollection.new(env).environment.should equal(env) - end - - it "should convert the environment into an environment instance if a string is provided" do - env = Puppet::Node::Environment.new("testing") - Puppet::Resource::TypeCollection.new("testing").environment.should equal(env) - end - - it "should create a 'loader' at initialization" do - Puppet::Resource::TypeCollection.new("testing").loader.should be_instance_of(Puppet::Parser::TypeLoader) - end - - it "should be able to add a resource type" do - Puppet::Resource::TypeCollection.new("env").should respond_to(:add) + @code = Puppet::Resource::TypeCollection.new(environment) end it "should consider '<<' to be an alias to 'add' but should return self" do - loader = Puppet::Resource::TypeCollection.new("env") - loader.expects(:add).with "foo" - loader.expects(:add).with "bar" - loader << "foo" << "bar" + @code.expects(:add).with "foo" + @code.expects(:add).with "bar" + @code << "foo" << "bar" end it "should set itself as the code collection for added resource types" do - loader = Puppet::Resource::TypeCollection.new("env") - node = Puppet::Resource::Type.new(:node, "foo") @code.add(node) @@ -96,7 +77,7 @@ describe Puppet::Resource::TypeCollection do end it "should remove all nodes, classes, and definitions when cleared" do - loader = Puppet::Resource::TypeCollection.new("env") + loader = Puppet::Resource::TypeCollection.new(environment) loader.add Puppet::Resource::Type.new(:hostclass, "class") loader.add Puppet::Resource::Type.new(:definition, "define") loader.add Puppet::Resource::Type.new(:node, "node") @@ -200,14 +181,14 @@ describe Puppet::Resource::TypeCollection do describe "behavior of add for #{data}" do it "should return the added #{data}" do - loader = Puppet::Resource::TypeCollection.new("env") + loader = Puppet::Resource::TypeCollection.new(environment) instance = Puppet::Resource::Type.new(data, "foo") loader.add(instance).should equal(instance) end it "should retrieve #{data} insensitive to case" do - loader = Puppet::Resource::TypeCollection.new("env") + loader = Puppet::Resource::TypeCollection.new(environment) instance = Puppet::Resource::Type.new(data, "Bar") loader.add instance @@ -216,75 +197,75 @@ describe Puppet::Resource::TypeCollection do end it "should return nil when asked for a #{data} that has not been added" do - Puppet::Resource::TypeCollection.new("env").send(data, "foo").should be_nil + Puppet::Resource::TypeCollection.new(environment).send(data, "foo").should be_nil end end end describe "when finding a qualified instance" do it "should return any found instance if the instance name is fully qualified" do - loader = Puppet::Resource::TypeCollection.new("env") + loader = Puppet::Resource::TypeCollection.new(environment) instance = Puppet::Resource::Type.new(:hostclass, "foo::bar") loader.add instance loader.find_hostclass("namespace", "::foo::bar").should equal(instance) end it "should return nil if the instance name is fully qualified and no such instance exists" do - loader = Puppet::Resource::TypeCollection.new("env") + loader = Puppet::Resource::TypeCollection.new(environment) loader.find_hostclass("namespace", "::foo::bar").should be_nil end it "should be able to find classes in the base namespace" do - loader = Puppet::Resource::TypeCollection.new("env") + loader = Puppet::Resource::TypeCollection.new(environment) instance = Puppet::Resource::Type.new(:hostclass, "foo") loader.add instance loader.find_hostclass("", "foo").should equal(instance) end it "should return the partially qualified object if it exists in a provided namespace" do - loader = Puppet::Resource::TypeCollection.new("env") + loader = Puppet::Resource::TypeCollection.new(environment) instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") loader.add instance loader.find_hostclass("foo", "bar::baz").should equal(instance) end it "should be able to find partially qualified objects in any of the provided namespaces" do - loader = Puppet::Resource::TypeCollection.new("env") + loader = Puppet::Resource::TypeCollection.new(environment) instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") loader.add instance loader.find_hostclass(["nons", "foo", "otherns"], "bar::baz").should equal(instance) end it "should return the unqualified object if it exists in a provided namespace" do - loader = Puppet::Resource::TypeCollection.new("env") + loader = Puppet::Resource::TypeCollection.new(environment) instance = Puppet::Resource::Type.new(:hostclass, "foo::bar") loader.add instance loader.find_hostclass("foo", "bar").should equal(instance) end it "should return the unqualified object if it exists in the parent namespace" do - loader = Puppet::Resource::TypeCollection.new("env") + loader = Puppet::Resource::TypeCollection.new(environment) instance = Puppet::Resource::Type.new(:hostclass, "foo::bar") loader.add instance loader.find_hostclass("foo::bar::baz", "bar").should equal(instance) end it "should should return the partially qualified object if it exists in the parent namespace" do - loader = Puppet::Resource::TypeCollection.new("env") + loader = Puppet::Resource::TypeCollection.new(environment) instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") loader.add instance loader.find_hostclass("foo::bar", "bar::baz").should equal(instance) end it "should return the qualified object if it exists in the root namespace" do - loader = Puppet::Resource::TypeCollection.new("env") + loader = Puppet::Resource::TypeCollection.new(environment) instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") loader.add instance loader.find_hostclass("foo::bar", "foo::bar::baz").should equal(instance) end it "should return nil if the object cannot be found" do - loader = Puppet::Resource::TypeCollection.new("env") + loader = Puppet::Resource::TypeCollection.new(environment) instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") loader.add instance loader.find_hostclass("foo::bar", "eh").should be_nil @@ -292,7 +273,7 @@ describe Puppet::Resource::TypeCollection do describe "when topscope has a class that has the same name as a local class" do before do - @loader = Puppet::Resource::TypeCollection.new("env") + @loader = Puppet::Resource::TypeCollection.new(environment) [ "foo::bar", "bar" ].each do |name| @loader.add Puppet::Resource::Type.new(:hostclass, name) end @@ -312,7 +293,7 @@ describe Puppet::Resource::TypeCollection do end it "should not look in the local scope for classes when the name is qualified" do - @loader = Puppet::Resource::TypeCollection.new("env") + @loader = Puppet::Resource::TypeCollection.new(environment) @loader.add Puppet::Resource::Type.new(:hostclass, "foo::bar") @loader.find_hostclass("foo", "::bar").should == nil @@ -322,24 +303,24 @@ describe Puppet::Resource::TypeCollection do it "should be able to find nodes" do node = Puppet::Resource::Type.new(:node, "bar") - loader = Puppet::Resource::TypeCollection.new("env") + loader = Puppet::Resource::TypeCollection.new(environment) loader.add(node) loader.find_node(stub("ignored"), "bar").should == node end it "should indicate whether any nodes are defined" do - loader = Puppet::Resource::TypeCollection.new("env") + loader = Puppet::Resource::TypeCollection.new(environment) loader.add_node(Puppet::Resource::Type.new(:node, "foo")) loader.should be_nodes end it "should indicate whether no nodes are defined" do - Puppet::Resource::TypeCollection.new("env").should_not be_nodes + Puppet::Resource::TypeCollection.new(environment).should_not be_nodes end describe "when finding nodes" do before :each do - @loader = Puppet::Resource::TypeCollection.new("env") + @loader = Puppet::Resource::TypeCollection.new(environment) end it "should return any node whose name exactly matches the provided node name" do @@ -368,7 +349,7 @@ describe Puppet::Resource::TypeCollection do describe "when managing files" do before do - @loader = Puppet::Resource::TypeCollection.new("env") + @loader = Puppet::Resource::TypeCollection.new(environment) Puppet::Util::WatchedFile.stubs(:new).returns stub("watched_file") end @@ -409,7 +390,7 @@ describe Puppet::Resource::TypeCollection do describe "when determining the configuration version" do before do - @code = Puppet::Resource::TypeCollection.new("env") + @code = Puppet::Resource::TypeCollection.new(environment) end it "should default to the current time" do diff --git a/spec/unit/resource/type_spec.rb b/spec/unit/resource/type_spec.rb index 1ed1ebcd6..1dd0c41db 100755 --- a/spec/unit/resource/type_spec.rb +++ b/spec/unit/resource/type_spec.rb @@ -347,14 +347,15 @@ describe Puppet::Resource::Type do describe "when describing and managing parent classes" do before do - @krt = Puppet::Node::Environment.new.known_resource_types + environment = Puppet::Node::Environment.create(:testing, [], '') + @krt = environment.known_resource_types @parent = Puppet::Resource::Type.new(:hostclass, "bar") @krt.add @parent @child = Puppet::Resource::Type.new(:hostclass, "foo", :parent => "bar") @krt.add @child - @scope = Puppet::Parser::Scope.new(Puppet::Parser::Compiler.new(Puppet::Node.new("foo"))) + @scope = Puppet::Parser::Scope.new(Puppet::Parser::Compiler.new(Puppet::Node.new("foo", :environment => environment))) end it "should be able to define a parent" do diff --git a/spec/unit/resource_spec.rb b/spec/unit/resource_spec.rb index b6443865a..1ac2decf1 100755 --- a/spec/unit/resource_spec.rb +++ b/spec/unit/resource_spec.rb @@ -5,7 +5,8 @@ require 'puppet/resource' describe Puppet::Resource do include PuppetSpec::Files - let :basepath do make_absolute("/somepath") end + let(:basepath) { make_absolute("/somepath") } + let(:environment) { Puppet::Node::Environment.create(:testing, [], '') } [:catalog, :file, :line].each do |attr| it "should have an #{attr} attribute" do @@ -120,10 +121,6 @@ describe Puppet::Resource do resource.should be_exported end - it "should support an environment attribute" do - Puppet::Resource.new("file", "/my/file", :environment => :foo).environment.name.should == :foo - end - describe "and munging its type and title" do describe "when modeling a builtin resource" do it "should be able to find the resource type" do @@ -139,19 +136,19 @@ describe Puppet::Resource do describe "that exists" do before do @type = Puppet::Resource::Type.new(:definition, "foo::bar") - Puppet::Node::Environment.new.known_resource_types.add @type + environment.known_resource_types.add @type end it "should set its type to the capitalized type name" do - Puppet::Resource.new("foo::bar", "/my/file").type.should == "Foo::Bar" + Puppet::Resource.new("foo::bar", "/my/file", :environment => environment).type.should == "Foo::Bar" end it "should be able to find the resource type" do - Puppet::Resource.new("foo::bar", "/my/file").resource_type.should equal(@type) + Puppet::Resource.new("foo::bar", "/my/file", :environment => environment).resource_type.should equal(@type) end it "should set its title to the provided title" do - Puppet::Resource.new("foo::bar", "/my/file").title.should == "/my/file" + Puppet::Resource.new("foo::bar", "/my/file", :environment => environment).title.should == "/my/file" end end @@ -179,15 +176,15 @@ describe Puppet::Resource do describe "that exists" do before do @type = Puppet::Resource::Type.new(:hostclass, "foo::bar") - Puppet::Node::Environment.new.known_resource_types.add @type + environment.known_resource_types.add @type end it "should set its title to the capitalized, fully qualified resource type" do - Puppet::Resource.new("class", "foo::bar").title.should == "Foo::Bar" + Puppet::Resource.new("class", "foo::bar", :environment => environment).title.should == "Foo::Bar" end it "should be able to find the resource type" do - Puppet::Resource.new("class", "foo::bar").resource_type.should equal(@type) + Puppet::Resource.new("class", "foo::bar", :environment => environment).resource_type.should equal(@type) end end @@ -207,9 +204,9 @@ describe Puppet::Resource do describe "and a class exists whose name is the empty string" do # this was a bit tough to track down it "should set its title to :main" do @type = Puppet::Resource::Type.new(:hostclass, "") - Puppet::Node::Environment.new.known_resource_types.add @type + environment.known_resource_types.add @type - Puppet::Resource.new("class", "").title.should == :main + Puppet::Resource.new("class", "", :environment => environment).title.should == :main end end end @@ -222,9 +219,9 @@ describe Puppet::Resource do describe "and a class exists whose name is the empty string" do # this was a bit tough to track down it "should set its title to :main" do @type = Puppet::Resource::Type.new(:hostclass, "") - Puppet::Node::Environment.new.known_resource_types.add @type + environment.known_resource_types.add @type - Puppet::Resource.new("class", :main).title.should == :main + Puppet::Resource.new("class", :main, :environment => environment).title.should == :main end end end @@ -237,8 +234,8 @@ describe Puppet::Resource do it "should not fail when an invalid parameter is used and strict mode is disabled" do type = Puppet::Resource::Type.new(:definition, "foobar") - Puppet::Node::Environment.new.known_resource_types.add type - resource = Puppet::Resource.new("foobar", "/my/file") + environment.known_resource_types.add type + resource = Puppet::Resource.new("foobar", "/my/file", :environment => environment) resource[:yay] = true end @@ -267,7 +264,7 @@ describe Puppet::Resource do end describe "when setting default parameters" do - let(:foo_node) { Puppet::Node.new('foo') } + let(:foo_node) { Puppet::Node.new('foo', :environment => environment) } let(:compiler) { Puppet::Parser::Compiler.new(foo_node) } let(:scope) { Puppet::Parser::Scope.new(compiler) } @@ -276,15 +273,15 @@ describe Puppet::Resource do end it "should fail when asked to set default values and it is not a parser resource" do - Puppet::Node::Environment.new.known_resource_types.add( + environment.known_resource_types.add( Puppet::Resource::Type.new(:definition, "default_param", :arguments => {"a" => ast_string("default")}) ) - resource = Puppet::Resource.new("default_param", "name") + resource = Puppet::Resource.new("default_param", "name", :environment => environment) lambda { resource.set_default_parameters(scope) }.should raise_error(Puppet::DevError) end it "should evaluate and set any default values when no value is provided" do - Puppet::Node::Environment.new.known_resource_types.add( + environment.known_resource_types.add( Puppet::Resource::Type.new(:definition, "default_param", :arguments => {"a" => ast_string("a_default_value")}) ) resource = Puppet::Parser::Resource.new("default_param", "name", :scope => scope) @@ -293,7 +290,7 @@ describe Puppet::Resource do end it "should skip attributes with no default value" do - Puppet::Node::Environment.new.known_resource_types.add( + environment.known_resource_types.add( Puppet::Resource::Type.new(:definition, "no_default_param", :arguments => {"a" => ast_string("a_default_value")}) ) resource = Puppet::Parser::Resource.new("no_default_param", "name", :scope => scope) @@ -301,7 +298,7 @@ describe Puppet::Resource do end it "should return the list of default parameters set" do - Puppet::Node::Environment.new.known_resource_types.add( + environment.known_resource_types.add( Puppet::Resource::Type.new(:definition, "default_param", :arguments => {"a" => ast_string("a_default_value")}) ) resource = Puppet::Parser::Resource.new("default_param", "name", :scope => scope) @@ -315,11 +312,10 @@ describe Puppet::Resource do let(:apache) { Puppet::Resource::Type.new(:hostclass, 'apache', :arguments => { 'port' => port }) } before do - environment = Puppet::Node::Environment.new(environment_name) environment.known_resource_types.add(apache) scope.stubs(:host).returns('host') - scope.stubs(:environment).returns(Puppet::Node::Environment.new(environment_name)) + scope.stubs(:environment).returns(environment) scope.stubs(:facts).returns(Puppet::Node::Facts.new("facts", fact_values)) end @@ -398,17 +394,17 @@ describe Puppet::Resource do describe "when validating all required parameters are present" do it "should be able to validate that all required parameters are present" do - Puppet::Node::Environment.new.known_resource_types.add( + environment.known_resource_types.add( Puppet::Resource::Type.new(:definition, "required_param", :arguments => {"a" => nil}) ) - lambda { Puppet::Resource.new("required_param", "name").validate_complete }.should raise_error(Puppet::ParseError) + lambda { Puppet::Resource.new("required_param", "name", :environment => environment).validate_complete }.should raise_error(Puppet::ParseError) end it "should not fail when all required parameters are present" do - Puppet::Node::Environment.new.known_resource_types.add( + environment.known_resource_types.add( Puppet::Resource::Type.new(:definition, "no_required_param") ) - resource = Puppet::Resource.new("no_required_param", "name") + resource = Puppet::Resource.new("no_required_param", "name", :environment => environment) resource["a"] = "meh" lambda { resource.validate_complete }.should_not raise_error end @@ -455,14 +451,14 @@ describe Puppet::Resource do it "should correctly detect when provided parameters are not valid for defined resource types" do type = Puppet::Resource::Type.new(:definition, "foobar") - Puppet::Node::Environment.new.known_resource_types.add type - Puppet::Resource.new("foobar", "/my/file").should_not be_valid_parameter("myparam") + environment.known_resource_types.add type + Puppet::Resource.new("foobar", "/my/file", :environment => environment).should_not be_valid_parameter("myparam") end it "should correctly detect when provided parameters are valid for defined resource types" do type = Puppet::Resource::Type.new(:definition, "foobar", :arguments => {"myparam" => nil}) - Puppet::Node::Environment.new.known_resource_types.add type - Puppet::Resource.new("foobar", "/my/file").should be_valid_parameter("myparam") + environment.known_resource_types.add type + Puppet::Resource.new("foobar", "/my/file", :environment => environment).should be_valid_parameter("myparam") end it "should allow setting and retrieving of parameters" do @@ -603,11 +599,9 @@ describe Puppet::Resource do describe "when serializing a defined type" do before do type = Puppet::Resource::Type.new(:definition, "foo::bar") - Puppet::Node::Environment.new.known_resource_types.add type - end + environment.known_resource_types.add type - before :each do - @resource = Puppet::Resource.new('foo::bar', 'xyzzy') + @resource = Puppet::Resource.new('foo::bar', 'xyzzy', :environment => environment) @resource['one'] = 'test' @resource['two'] = 'other' @resource.resource_type diff --git a/spec/unit/util/autoload_spec.rb b/spec/unit/util/autoload_spec.rb index 3592a63c9..1800dd2dc 100755 --- a/spec/unit/util/autoload_spec.rb +++ b/spec/unit/util/autoload_spec.rb @@ -24,14 +24,9 @@ describe Puppet::Util::Autoload do end it "should collect all of the lib directories that exist in the current environment's module path" do - Puppet.settings.parse_config(<<-CONF) - [foo] - modulepath = #{@dira}#{File::PATH_SEPARATOR}#{@dirb}#{File::PATH_SEPARATOR}#{@dirc} - CONF - - Puppet[:environment] = "foo" - Dir.expects(:entries).with(@dira).returns %w{one two} - Dir.expects(:entries).with(@dirb).returns %w{one two} + environment = Puppet::Node::Environment.create(:foo, [@dira, @dirb, @dirc], '') + Dir.expects(:entries).with(@dira).returns %w{. .. one two} + Dir.expects(:entries).with(@dirb).returns %w{. .. one two} Puppet::FileSystem.expects(:directory?).with(@dira).returns true Puppet::FileSystem.expects(:directory?).with(@dirb).returns true @@ -40,32 +35,14 @@ describe Puppet::Util::Autoload do FileTest.expects(:directory?).with(regexp_matches(%r{two/lib})).times(2).returns true FileTest.expects(:directory?).with(regexp_matches(%r{one/lib})).times(2).returns false - @autoload.class.module_directories.should == ["#{@dira}/two/lib", "#{@dirb}/two/lib"] - end - - it "should not look for lib directories in directories starting with '.'" do - Puppet.settings.parse_config(<<-CONF) - [foo] - modulepath = #{@dira} - CONF - - Puppet[:environment] = "foo" - Dir.expects(:entries).with(@dira).returns %w{. ..} - - Puppet::FileSystem.expects(:directory?).with(@dira).returns true - Puppet::FileSystem.expects(:directory?).with("#{@dira}/./lib").never - Puppet::FileSystem.expects(:directory?).with("#{@dira}/./plugins").never - Puppet::FileSystem.expects(:directory?).with("#{@dira}/../lib").never - Puppet::FileSystem.expects(:directory?).with("#{@dira}/../plugins").never - - @autoload.class.module_directories + @autoload.class.module_directories(environment).should == ["#{@dira}/two/lib", "#{@dirb}/two/lib"] end it "should include the module directories, the Puppet libdir, and all of the Ruby load directories" do Puppet[:libdir] = %w{/libdir1 /lib/dir/two /third/lib/dir}.join(File::PATH_SEPARATOR) @autoload.class.expects(:gem_directories).returns %w{/one /two} @autoload.class.expects(:module_directories).returns %w{/three /four} - @autoload.class.search_directories.should == %w{/one /two /three /four} + Puppet[:libdir].split(File::PATH_SEPARATOR) + $LOAD_PATH + @autoload.class.search_directories(nil).should == %w{/one /two /three /four} + Puppet[:libdir].split(File::PATH_SEPARATOR) + $LOAD_PATH end end From 78b54c77de7bed5f4065976f2de0417da8925670 Mon Sep 17 00:00:00 2001 From: Sam Kottler Date: Fri, 17 Jan 2014 12:44:54 +0100 Subject: [PATCH 508/800] (PUP-1463) Enable puppet.service during upgrade if puppetagent.service was previously enabled --- ext/redhat/puppet.spec.erb | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/ext/redhat/puppet.spec.erb b/ext/redhat/puppet.spec.erb index dd934f1cb..eb48e811b 100644 --- a/ext/redhat/puppet.spec.erb +++ b/ext/redhat/puppet.spec.erb @@ -20,6 +20,8 @@ %global rpmversion <%= @rpmversion %> %global confdir ext/redhat +%global pending_upgrade_path %{_localstatedir}/lib/rpm-state/puppet +%global pending_upgrade_file %{pending_upgrade_path}/upgrade_pending Name: puppet Version: %{rpmversion} @@ -359,6 +361,23 @@ if [ "$1" -eq 0 ] ; then /bin/systemctl stop puppet.service > /dev/null 2>&1 || : /bin/systemctl daemon-reload >/dev/null 2>&1 || : fi + +if [ "$1" == "1" ]; then + /bin/systemctl is-enabled puppetagent.service > /dev/null 2>&1 + if [ "$?" == "0" ]; then + /bin/systemctl --no-reload disable puppetagent.service > /dev/null 2>&1 ||: + /bin/systemctl stop puppetagent.service > /dev/null 2>&1 ||: + /bin/systemctl daemon-reload >/dev/null 2>&1 ||: + if [ ! -d %{pending_upgrade_path} ]; then + mkdir -p %{pending_upgrade_path} + fi + + if [ ! -e %{pending_upgrade_file} ]; then + touch %{pending_upgrade_file} + fi + fi +fi + %else if [ "$1" = 0 ] ; then /sbin/service puppet stop > /dev/null 2>&1 @@ -384,6 +403,12 @@ fi %postun %if 0%{?_with_systemd} if [ $1 -ge 1 ] ; then + if [ -e %{pending_upgrade_file} ]; then + /bin/systemctl --no-reload enable puppet.service > /dev/null 2>&1 ||: + /bin/systemctl start puppet.service > /dev/null 2>&1 ||: + /bin/systemctl daemon-reload >/dev/null 2>&1 ||: + rm %{pending_upgrade_file} + fi # Package upgrade, not uninstall /bin/systemctl try-restart puppetagent.service >/dev/null 2>&1 || : fi From f381f2c758c770e6e3e8ec4e3cb2b6ed3670f423 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Wed, 29 Jan 2014 10:52:10 -0800 Subject: [PATCH 509/800] (PUP-1118) Fix rdoc1 specs to match changes to rdoc puppet_parser_core PuppetParserCore was updated to pass environment, known_resource_types; this just catches up the spec, which runs only on 1.8.7, to match the changes. --- spec/unit/util/rdoc/parser_spec.rb | 55 +++++++++++++++--------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/spec/unit/util/rdoc/parser_spec.rb b/spec/unit/util/rdoc/parser_spec.rb index 72f7ba926..fa536703a 100755 --- a/spec/unit/util/rdoc/parser_spec.rb +++ b/spec/unit/util/rdoc/parser_spec.rb @@ -68,9 +68,11 @@ describe "RDoc::Parser", :if => Puppet.features.rdoc1? do end describe "when scanning top level entities" do + let(:environment) { Puppet::Node::Environment.create(:env, [], '') } + before :each do @resource_type_collection = resource_type_collection = stub_everything('resource_type_collection') - @parser.instance_eval { @known_resource_types = resource_type_collection } + environment.stubs(:known_resource_types).returns(@resource_type_collection) @parser.stubs(:split_module).returns("module") @topcontainer = stub_everything 'topcontainer' @@ -88,7 +90,7 @@ describe "RDoc::Parser", :if => Puppet.features.rdoc1? do @module.expects(:add_comment).with("readme", "module/manifests/init.pp") - @parser.scan_top_level(@topcontainer) + @parser.scan_top_level(@topcontainer, environment) end it "should read any present README.rdoc as module documentation" do @@ -99,7 +101,7 @@ describe "RDoc::Parser", :if => Puppet.features.rdoc1? do @module.expects(:add_comment).with("readme", "module/manifests/init.pp") - @parser.scan_top_level(@topcontainer) + @parser.scan_top_level(@topcontainer, environment) end it "should prefer README.rdoc over README as module documentation" do @@ -111,7 +113,7 @@ describe "RDoc::Parser", :if => Puppet.features.rdoc1? do @module.expects(:add_comment).with("readme.rdoc", "module/manifests/init.pp") - @parser.scan_top_level(@topcontainer) + @parser.scan_top_level(@topcontainer, environment) end it "should tell the container its module name" do @@ -119,7 +121,7 @@ describe "RDoc::Parser", :if => Puppet.features.rdoc1? do @topcontainer.expects(:module_name=).with("module") - @parser.scan_top_level(@topcontainer) + @parser.scan_top_level(@topcontainer, environment) end it "should not document our toplevel if it isn't a valid module" do @@ -128,7 +130,7 @@ describe "RDoc::Parser", :if => Puppet.features.rdoc1? do @topcontainer.expects(:document_self=).with(false) @parser.expects(:parse_elements).never - @parser.scan_top_level(@topcontainer) + @parser.scan_top_level(@topcontainer, environment) end it "should set the module as global if we parse the global manifests (ie __site__ module)" do @@ -137,7 +139,7 @@ describe "RDoc::Parser", :if => Puppet.features.rdoc1? do @topcontainer.expects(:global=).with(true) - @parser.scan_top_level(@topcontainer) + @parser.scan_top_level(@topcontainer, environment) end it "should attach this module container to the toplevel container" do @@ -145,13 +147,13 @@ describe "RDoc::Parser", :if => Puppet.features.rdoc1? do @container.expects(:add_module).with(RDoc::PuppetModule, "module").returns(@module) - @parser.scan_top_level(@topcontainer) + @parser.scan_top_level(@topcontainer, environment) end it "should defer ast parsing to parse_elements for this module" do - @parser.expects(:parse_elements).with(@module) + @parser.expects(:parse_elements).with(@module, @resource_type_collection) - @parser.scan_top_level(@topcontainer) + @parser.scan_top_level(@topcontainer, environment) end it "should defer plugins parsing to parse_plugins for this module" do @@ -159,29 +161,28 @@ describe "RDoc::Parser", :if => Puppet.features.rdoc1? do @parser.expects(:parse_plugins).with(@module) - @parser.scan_top_level(@topcontainer) + @parser.scan_top_level(@topcontainer, environment) end end describe "when finding modules from filepath" do - before :each do - Puppet::Node::Environment.any_instance.stubs(:modulepath).returns("/path/to/modules") - end + let(:environment) { + Puppet::FileSystem.expects(:directory?).with("/path/to/modules").at_least_once.returns(true) + Puppet::Node::Environment.create(:env, ["/path/to/modules"], '') + } it "should return the module name for modulized puppet manifests" do - File.stubs(:expand_path).returns("/path/to/module/manifests/init.pp") - File.stubs(:identical?).with("/path/to", "/path/to/modules").returns(true) - @parser.split_module("/path/to/modules/mymodule/manifests/init.pp").should == "module" + File.stubs(:identical?).with("/path/to/modules", "/path/to/modules").returns(true) + @parser.split_module("/path/to/modules/mymodule/manifests/init.pp", environment).should == "mymodule" end it "should return for manifests not under module path" do - File.stubs(:expand_path).returns("/path/to/manifests/init.pp") File.stubs(:identical?).returns(false) - @parser.split_module("/path/to/manifests/init.pp").should == RDoc::Parser::SITE + @parser.split_module("/path/to/manifests/init.pp", environment).should == RDoc::Parser::SITE end it "should handle windows paths with drive letters", :if => Puppet.features.microsoft_windows? && Puppet.features.rdoc1? do - @parser.split_module("C:/temp/init.pp").should == RDoc::Parser::SITE + @parser.split_module("C:/temp/init.pp", environment).should == RDoc::Parser::SITE end end @@ -202,7 +203,7 @@ describe "RDoc::Parser", :if => Puppet.features.rdoc1? do @parser.expects(:document_class).with("myclass", @klass, @container) - @parser.parse_elements(@container) + @parser.parse_elements(@container, @resource_type_collection) end it "should not document class parsed in an other file" do @@ -211,7 +212,7 @@ describe "RDoc::Parser", :if => Puppet.features.rdoc1? do @parser.expects(:document_class).with("myclass", @klass, @container).never - @parser.parse_elements(@container) + @parser.parse_elements(@container, @resource_type_collection) end it "should document vardefs for the main class" do @@ -224,7 +225,7 @@ describe "RDoc::Parser", :if => Puppet.features.rdoc1? do @parser.expects(:scan_for_vardef).with(@container, code) - @parser.parse_elements(@container) + @parser.parse_elements(@container, @resource_type_collection) end it "should document definitions in the parsed file" do @@ -232,7 +233,7 @@ describe "RDoc::Parser", :if => Puppet.features.rdoc1? do @parser.expects(:document_define).with("mydef", @definition, @container) - @parser.parse_elements(@container) + @parser.parse_elements(@container, @resource_type_collection) end it "should not document definitions parsed in an other file" do @@ -241,7 +242,7 @@ describe "RDoc::Parser", :if => Puppet.features.rdoc1? do @parser.expects(:document_define).with("mydef", @definition, @container).never - @parser.parse_elements(@container) + @parser.parse_elements(@container, @resource_type_collection) end it "should document nodes in the parsed file" do @@ -249,7 +250,7 @@ describe "RDoc::Parser", :if => Puppet.features.rdoc1? do @parser.expects(:document_node).with("mynode", @node, @container) - @parser.parse_elements(@container) + @parser.parse_elements(@container, @resource_type_collection) end it "should not document node parsed in an other file" do @@ -258,7 +259,7 @@ describe "RDoc::Parser", :if => Puppet.features.rdoc1? do @parser.expects(:document_node).with("mynode", @node, @container).never - @parser.parse_elements(@container) + @parser.parse_elements(@container, @resource_type_collection) end end From b439bb8abea49a731e4f28162e2ddd6e97384199 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Wed, 29 Jan 2014 11:45:30 -0800 Subject: [PATCH 510/800] (Maint) Remove dead port code The port code has been commented out and unmaintained since 5d2f95 (~7 years ago). --- lib/puppet/provider/port/parsed.rb | 173 ----------------------------- lib/puppet/type/port.rb | 119 -------------------- 2 files changed, 292 deletions(-) delete mode 100644 lib/puppet/provider/port/parsed.rb delete mode 100644 lib/puppet/type/port.rb diff --git a/lib/puppet/provider/port/parsed.rb b/lib/puppet/provider/port/parsed.rb deleted file mode 100644 index 5c973b6af..000000000 --- a/lib/puppet/provider/port/parsed.rb +++ /dev/null @@ -1,173 +0,0 @@ -require 'puppet/provider/parsedfile' - -#services = nil -#case Facter.value(:operatingsystem) -#when "Solaris"; services = "/etc/inet/services" -#else -# services = "/etc/services" -#end -# -#Puppet::Type.type(:port).provide(:parsed, -# :parent => Puppet::Provider::ParsedFile, -# :default_target => services, -# :filetype => :flat -#) do -# text_line :comment, :match => /^\s*#/ -# text_line :blank, :match => /^\s*$/ -# -# # We're cheating horribly here -- we don't support ddp, because it assigns -# # the same number to already-used names, and the same name to different -# # numbers. -# text_line :ddp, :match => /^\S+\s+\d+\/ddp/ -# -# # Also, just ignore the lines on OS X that don't have service names. -# text_line :funky_darwin, :match => /^\s+\d+\// -# -# # We have to manually parse the line, since it's so darn complicated. -# record_line :parsed, :fields => %w{name port protocols alias description}, -# :optional => %w{alias description} do |line| -# if line =~ /\/ddp/ -# raise "missed ddp in #{line}" -# end -# # The record might contain multiple port lines separated by \n. -# hashes = line.split("\n").collect { |l| parse_port(l) } -# -# # It's easy if there's just one hash. -# if hashes.length == 1 -# return hashes.shift -# end -# -# # Else, merge the two records into one. -# return port_merge(*hashes) -# end -# -# # Override how we split into lines, so that we always treat both protocol -# # lines as a single line. This drastically simplifies merging the two lines -# # into one record. -# def self.lines(text) -# names = {} -# lines = [] -# -# # We organize by number, because that's apparently how the ports work. -# # You'll never be able to use Puppet to manage multiple entries -# # with the same name but different numbers, though. -# text.split("\n").each do |line| -# if line =~ /^([-\w]+)\s+(\d+)\/[^d]/ # We want to skip ddp proto stuff -# names[$1] ||= [] -# names[$1] << line -# lines << [:special, $1] -# else -# lines << line -# end -# end -# -# # Now, return each line in order, but join the ones with the same name -# lines.collect do |line| -# if line.is_a?(Array) -# name = line[1] -# if names[name] -# t = names[name].join("\n") -# names.delete(name) -# t -# end -# else -# line -# end -# end.reject { |l| l.nil? } -# end -# -# # Parse a single port line, returning a hash. -# def self.parse_port(line) -# hash = {} -# if line.sub!(/^(\S+)\s+(\d+)\/(\w+)\s*/, '') -# hash[:name] = $1 -# hash[:number] = $2 -# hash[:protocols] = [$3] -# -# unless line == "" -# line.sub!(/^([^#]+)\s*/) do |value| -# aliases = $1 -# -# # Remove any trailing whitespace -# aliases.strip! -# unless aliases =~ /^\s*$/ -# hash[:alias] = aliases.split(/\s+/) -# end -# -# "" -# end -# -# line.sub!(/^\s*#\s*(.+)$/) do |value| -# desc = $1 -# unless desc =~ /^\s*$/ -# hash[:description] = desc.sub(/\s*$/, '') -# end -# -# "" -# end -# end -# else -# if line =~ /^\s+\d+/ and -# Facter["operatingsystem"].value == "Darwin" -# #Puppet.notice "Skipping wonky OS X port entry %s" % -# # line.inspect -# next -# end -# Puppet.notice "Ignoring unparseable line '#{line}' in #{self.target}" -# end -# -# if hash.empty? -# return nil -# else -# return hash -# end -# end -# -# # Merge two records into one. -# def self.port_merge(one, two) -# keys = [one.keys, two.keys].flatten.uniq -# -# # We'll be returning the 'one' hash. so make any necessary modifications -# # to it. -# keys.each do |key| -# # The easy case -# if one[key] == two[key] -# next -# elsif one[key] and ! two[key] -# next -# elsif ! one[key] and two[key] -# one[key] = two[key] -# elsif one[key].is_a?(Array) and two[key].is_a?(Array) -# one[key] = [one[key], two[key]].flatten.uniq -# else -# # Keep the info from the first hash, so don't do anything -# #Puppet.notice "Cannot merge %s in %s with %s" % -# # [key, one.inspect, two.inspect] -# end -# end -# -# return one -# end -# -# # Convert the current object into one or more services entry. -# def self.to_line(hash) -# unless hash[:record_type] == :parsed -# return super -# end -# -# # Strangely, most sites seem to use tabs as separators. -# hash[:protocols].collect { |proto| -# str = "#{hash[:name]}\t\t#{hash[:number]}/#{proto}" -# -# if value = hash[:alias] and value != :absent -# str += "\t\t#{value.join(" ")}" -# end -# -# if value = hash[:description] and value != :absent -# str += "\t# #{value}" -# end -# str -# }.join("\n") -# end -#end - diff --git a/lib/puppet/type/port.rb b/lib/puppet/type/port.rb deleted file mode 100644 index e19988515..000000000 --- a/lib/puppet/type/port.rb +++ /dev/null @@ -1,119 +0,0 @@ -#module Puppet -# newtype(:port) do -# @doc = "Installs and manages port entries. For most systems, these -# entries will just be in /etc/services, but some systems (notably OS X) -# will have different solutions." -# -# ensurable -# -# newproperty(:protocols) do -# desc "The protocols the port uses. Valid values are *udp* and *tcp*. -# Most services have both protocols, but not all. If you want -# both protocols, you must specify that; Puppet replaces the -# current values, it does not merge with them. If you specify -# multiple protocols they must be as an array." -# -# def is=(value) -# case value -# when String -# @is = value.split(/\s+/) -# else -# @is = value -# end -# end -# -# def is -# @is -# end -# -# # We actually want to return the whole array here, not just the first -# # value. -# def should -# if defined?(@should) -# if @should[0] == :absent -# return :absent -# else -# return @should -# end -# else -# return nil -# end -# end -# -# validate do |value| -# valids = ["udp", "tcp", "ddp", :absent] -# unless valids.include? value -# raise Puppet::Error, -# "Protocols can be either 'udp' or 'tcp', not #{value}" -# end -# end -# end -# -# newproperty(:number) do -# desc "The port number." -# end -# -# newproperty(:description) do -# desc "The port description." -# end -# -# newproperty(:port_aliases) do -# desc 'Any aliases the port might have. Multiple values must be -# specified as an array. Note that this property is not the same as -# the "alias" metaparam; use this property to add aliases to a port -# in the services file, and "alias" to aliases for use in your Puppet -# scripts.' -# -# # We actually want to return the whole array here, not just the first -# # value. -# def should -# if defined?(@should) -# if @should[0] == :absent -# return :absent -# else -# return @should -# end -# else -# return nil -# end -# end -# -# validate do |value| -# if value.is_a? String and value =~ /\s/ -# raise Puppet::Error, -# "Aliases cannot have whitespace in them: %s" % -# value.inspect -# end -# end -# -# munge do |value| -# unless value == "absent" or value == :absent -# # Add the :alias metaparam in addition to the property -# @resource.newmetaparam( -# @resource.class.metaparamclass(:alias), value -# ) -# end -# value -# end -# end -# -# newproperty(:target) do -# desc "The file in which to store service information. Only used by -# those providers that write to disk." -# -# defaultto { if @resource.class.defaultprovider.ancestors.include?(Puppet::Provider::ParsedFile) -# @resource.class.defaultprovider.default_target -# else -# nil -# end -# } -# end -# -# newparam(:name) do -# desc "The port name." -# -# isnamevar -# end -# end -#end - From 29509c34d451420e29201096c327dfbe6e01c92f Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Wed, 29 Jan 2014 13:00:25 -0800 Subject: [PATCH 511/800] (Maint) Fix load order dependency Running bundle exec rspec spec/unit/module_tool/install_directory_spec.rb would fail because the Puppet::ModuleTool::InstallDirectory classes referenced the Errors module without ensuring it was required. This commit ensures that all of the module_tool leaf code requires its parent first, and any code that they directly reference, e.g. open-uri --- lib/puppet/module_tool/contents_description.rb | 2 ++ lib/puppet/module_tool/dependency.rb | 2 ++ lib/puppet/module_tool/errors.rb | 2 ++ lib/puppet/module_tool/install_directory.rb | 3 +++ lib/puppet/module_tool/metadata.rb | 1 + lib/puppet/module_tool/modulefile.rb | 3 +++ lib/puppet/module_tool/shared_behaviors.rb | 5 +++++ lib/puppet/module_tool/skeleton.rb | 3 +++ lib/puppet/module_tool/tar.rb | 3 +++ 9 files changed, 24 insertions(+) diff --git a/lib/puppet/module_tool/contents_description.rb b/lib/puppet/module_tool/contents_description.rb index 0d19cd3ef..7fd6505f4 100644 --- a/lib/puppet/module_tool/contents_description.rb +++ b/lib/puppet/module_tool/contents_description.rb @@ -1,3 +1,5 @@ +require 'puppet/module_tool' + module Puppet::ModuleTool # = ContentsDescription diff --git a/lib/puppet/module_tool/dependency.rb b/lib/puppet/module_tool/dependency.rb index 222e714c8..777d9cdc4 100644 --- a/lib/puppet/module_tool/dependency.rb +++ b/lib/puppet/module_tool/dependency.rb @@ -1,3 +1,5 @@ +require 'puppet/module_tool' + module Puppet::ModuleTool class Dependency diff --git a/lib/puppet/module_tool/errors.rb b/lib/puppet/module_tool/errors.rb index b2b36ada6..3f917bd35 100644 --- a/lib/puppet/module_tool/errors.rb +++ b/lib/puppet/module_tool/errors.rb @@ -1,3 +1,5 @@ +require 'puppet/module_tool' + module Puppet::ModuleTool module Errors require 'puppet/module_tool/errors/base' diff --git a/lib/puppet/module_tool/install_directory.rb b/lib/puppet/module_tool/install_directory.rb index d52ceaa87..a53a542fa 100644 --- a/lib/puppet/module_tool/install_directory.rb +++ b/lib/puppet/module_tool/install_directory.rb @@ -1,3 +1,6 @@ +require 'puppet/module_tool' +require 'puppet/module_tool/errors' + module Puppet module ModuleTool # Control the install location for modules. diff --git a/lib/puppet/module_tool/metadata.rb b/lib/puppet/module_tool/metadata.rb index 650043802..b58038213 100644 --- a/lib/puppet/module_tool/metadata.rb +++ b/lib/puppet/module_tool/metadata.rb @@ -1,4 +1,5 @@ require 'puppet/util/methodhelper' +require 'puppet/module_tool' module Puppet::ModuleTool # = Metadata diff --git a/lib/puppet/module_tool/modulefile.rb b/lib/puppet/module_tool/modulefile.rb index 321fcac37..46abc0d90 100644 --- a/lib/puppet/module_tool/modulefile.rb +++ b/lib/puppet/module_tool/modulefile.rb @@ -1,3 +1,6 @@ +require 'puppet/module_tool' +require 'puppet/module_tool/dependency' + module Puppet::ModuleTool # = Modulefile diff --git a/lib/puppet/module_tool/shared_behaviors.rb b/lib/puppet/module_tool/shared_behaviors.rb index afae12523..99ce52900 100644 --- a/lib/puppet/module_tool/shared_behaviors.rb +++ b/lib/puppet/module_tool/shared_behaviors.rb @@ -1,3 +1,8 @@ +require 'open-uri' + +require 'puppet/module_tool' +require 'puppet/module_tool/errors' + module Puppet::ModuleTool::Shared include Puppet::ModuleTool::Errors diff --git a/lib/puppet/module_tool/skeleton.rb b/lib/puppet/module_tool/skeleton.rb index c5e26e35f..4eaf4446f 100644 --- a/lib/puppet/module_tool/skeleton.rb +++ b/lib/puppet/module_tool/skeleton.rb @@ -1,3 +1,6 @@ +require 'pathname' +require 'puppet/module_tool' + module Puppet::ModuleTool # = Skeleton diff --git a/lib/puppet/module_tool/tar.rb b/lib/puppet/module_tool/tar.rb index 6b3257cf4..6b844545a 100644 --- a/lib/puppet/module_tool/tar.rb +++ b/lib/puppet/module_tool/tar.rb @@ -1,3 +1,6 @@ +require 'puppet/module_tool' +require 'puppet/util' + module Puppet::ModuleTool::Tar require 'puppet/module_tool/tar/gnu' require 'puppet/module_tool/tar/solaris' From 8e835d5f73834d503197e325cd4f0052b7f4c4f8 Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Wed, 29 Jan 2014 13:18:38 -0800 Subject: [PATCH 512/800] (maint) Fixing acceptance test failure caused by GH-2293. We have our own version of "fail" that takes different parameters than Kernel::fail. This lead to accidentally passing in a backtrace array (what Kernel#fail expects) instead of the original exception object (what Puppet::Util::Errors#fail expects). This causes a failure when attempting to format the nested exception because it is not the correct type. --- lib/puppet/parameter.rb | 2 +- lib/puppet/parser/resource.rb | 2 +- lib/puppet/provider/exec.rb | 2 +- lib/puppet/provider/package/gem.rb | 2 +- lib/puppet/provider/package/pacman.rb | 2 +- lib/puppet/provider/service/launchd.rb | 2 +- lib/puppet/provider/user/user_role_add.rb | 2 +- lib/puppet/type/exec.rb | 2 +- lib/puppet/type/file/content.rb | 2 +- lib/puppet/type/file/source.rb | 8 ++++---- lib/puppet/type/package.rb | 4 ++-- lib/puppet/type/zone.rb | 2 +- lib/puppet/util/backups.rb | 4 ++-- 13 files changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/puppet/parameter.rb b/lib/puppet/parameter.rb index 1b849a6e2..59e18de8e 100644 --- a/lib/puppet/parameter.rb +++ b/lib/puppet/parameter.rb @@ -464,7 +464,7 @@ class Puppet::Parameter begin unsafe_validate(value) rescue ArgumentError => detail - fail detail, detail.to_s, detail.backtrace + self.fail Puppet::Error, detail.to_s, detail rescue Puppet::Error, TypeError raise rescue => detail diff --git a/lib/puppet/parser/resource.rb b/lib/puppet/parser/resource.rb index cddb5b542..58e806c6e 100644 --- a/lib/puppet/parser/resource.rb +++ b/lib/puppet/parser/resource.rb @@ -253,7 +253,7 @@ class Puppet::Parser::Resource < Puppet::Resource validate_parameter(name) end rescue => detail - fail Puppet::ParseError, detail.to_s, detail.backtrace + self.fail Puppet::ParseError, detail.to_s, detail end def extract_parameters(params) diff --git a/lib/puppet/provider/exec.rb b/lib/puppet/provider/exec.rb index cc43a0d37..e5b90b6e2 100644 --- a/lib/puppet/provider/exec.rb +++ b/lib/puppet/provider/exec.rb @@ -63,7 +63,7 @@ class Puppet::Provider::Exec < Puppet::Provider end rescue Errno::ENOENT => detail - self.fail Puppet::Error, detail.to_s, detail.backtrace + self.fail Puppet::Error, detail.to_s, detail end # Return output twice as processstatus was returned before, but only exitstatus was ever called. diff --git a/lib/puppet/provider/package/gem.rb b/lib/puppet/provider/package/gem.rb index 729f4c1c1..518fac289 100644 --- a/lib/puppet/provider/package/gem.rb +++ b/lib/puppet/provider/package/gem.rb @@ -77,7 +77,7 @@ Puppet::Type.type(:package).provide :gem, :parent => Puppet::Provider::Package d begin uri = URI.parse(source) rescue => detail - fail detail, "Invalid source '#{uri}': #{detail}", detail.backtrace + self.fail Puppet::Error, "Invalid source '#{uri}': #{detail}", detail end case uri.scheme diff --git a/lib/puppet/provider/package/pacman.rb b/lib/puppet/provider/package/pacman.rb index 9187da872..49a009718 100644 --- a/lib/puppet/provider/package/pacman.rb +++ b/lib/puppet/provider/package/pacman.rb @@ -46,7 +46,7 @@ Puppet::Type.type(:package).provide :pacman, :parent => Puppet::Provider::Packag begin source_uri = URI.parse source rescue => detail - fail detail, "Invalid source '#{source}': #{detail}", detail.backtrace + self.fail Puppet::Error, "Invalid source '#{source}': #{detail}", detail end source = case source_uri.scheme diff --git a/lib/puppet/provider/service/launchd.rb b/lib/puppet/provider/service/launchd.rb index 2f8afacba..49f14d619 100644 --- a/lib/puppet/provider/service/launchd.rb +++ b/lib/puppet/provider/service/launchd.rb @@ -222,7 +222,7 @@ Puppet::Type.type(:service).provide :launchd, :parent => :base do @macosx_version_major = product_version_major return @macosx_version_major rescue Puppet::ExecutionFailure => detail - fail(detail, "Could not determine OS X version: #{detail}", detail.backtrace) + self.fail Puppet::Error, "Could not determine OS X version: #{detail}", detail end end diff --git a/lib/puppet/provider/user/user_role_add.rb b/lib/puppet/provider/user/user_role_add.rb index 4d240b964..96b4928fd 100644 --- a/lib/puppet/provider/user/user_role_add.rb +++ b/lib/puppet/provider/user/user_role_add.rb @@ -203,7 +203,7 @@ Puppet::Type.type(:user).provide :user_role_add, :parent => :useradd, :source => end end rescue => detail - fail detail, "Could not write replace #{target_file_path}: #{detail}", detail.backtrace + self.fail Puppet::Error, "Could not write replace #{target_file_path}: #{detail}", detail end end end diff --git a/lib/puppet/type/exec.rb b/lib/puppet/type/exec.rb index f42104245..6091baafa 100644 --- a/lib/puppet/type/exec.rb +++ b/lib/puppet/type/exec.rb @@ -117,7 +117,7 @@ module Puppet end end rescue Timeout::Error - self.fail Puppet::Error, "Command exceeded timeout" % value.inspect, $!.backtrace + self.fail Puppet::Error, "Command exceeded timeout", $! end if log = @resource[:logoutput] diff --git a/lib/puppet/type/file/content.rb b/lib/puppet/type/file/content.rb index 73f921403..2ee29bd5b 100644 --- a/lib/puppet/type/file/content.rb +++ b/lib/puppet/type/file/content.rb @@ -233,7 +233,7 @@ module Puppet dipper.getfile(sum) rescue => detail - fail detail, "Could not retrieve content for #{should} from filebucket: #{detail}", detail.backtrace + self.fail Puppet::Error, "Could not retrieve content for #{should} from filebucket: #{detail}", detail end end end diff --git a/lib/puppet/type/file/source.rb b/lib/puppet/type/file/source.rb index 30941efbe..e0cbf606f 100644 --- a/lib/puppet/type/file/source.rb +++ b/lib/puppet/type/file/source.rb @@ -58,7 +58,7 @@ module Puppet begin uri = URI.parse(URI.escape(source)) rescue => detail - self.fail Puppet::Error, "Could not understand source #{source}: #{detail}", detail.backtrace + self.fail Puppet::Error, "Could not understand source #{source}: #{detail}", detail end self.fail "Cannot use relative URLs '#{source}'" unless uri.absolute? @@ -101,7 +101,7 @@ module Puppet raise Puppet::DevError, "No source for content was stored with the metadata" unless metadata.source unless tmp = Puppet::FileServing::Content.indirection.find(metadata.source, :environment => resource.catalog.environment, :links => resource[:links]) - fail "Could not find any content at %s" % metadata.source + self.fail "Could not find any content at %s" % metadata.source end @content = tmp.content end @@ -175,10 +175,10 @@ module Puppet break end rescue => detail - fail detail, "Could not retrieve file metadata for #{source}: #{detail}", detail.backtrace + self.fail Puppet::Error, "Could not retrieve file metadata for #{source}: #{detail}", detail end end - fail "Could not retrieve information from environment #{resource.catalog.environment} source(s) #{value.join(", ")}" unless @metadata + self.fail "Could not retrieve information from environment #{resource.catalog.environment} source(s) #{value.join(", ")}" unless @metadata @metadata end diff --git a/lib/puppet/type/package.rb b/lib/puppet/type/package.rb index 943871608..6534d45f9 100644 --- a/lib/puppet/type/package.rb +++ b/lib/puppet/type/package.rb @@ -95,7 +95,7 @@ module Puppet begin provider.update rescue => detail - self.fail Puppet::Error, "Could not update: #{detail}", detail.backtrace + self.fail Puppet::Error, "Could not update: #{detail}", detail end if current == :absent @@ -109,7 +109,7 @@ module Puppet begin provider.install rescue => detail - self.fail Puppet::Error, "Could not update: #{detail}", detail.backtrace + self.fail Puppet::Error, "Could not update: #{detail}", detail end if self.retrieve == :absent diff --git a/lib/puppet/type/zone.rb b/lib/puppet/type/zone.rb index 7226623da..09572bdb6 100644 --- a/lib/puppet/type/zone.rb +++ b/lib/puppet/type/zone.rb @@ -330,7 +330,7 @@ end def validate_ip(ip, name) IPAddr.new(ip) if ip rescue ArgumentError - self.fail Puppet::Error, "'#{ip}' is an invalid #{name}", $!.backtrace + self.fail Puppet::Error, "'#{ip}' is an invalid #{name}", $! end def validate_exclusive(interface, address, router) diff --git a/lib/puppet/util/backups.rb b/lib/puppet/util/backups.rb index 461563569..249d27861 100644 --- a/lib/puppet/util/backups.rb +++ b/lib/puppet/util/backups.rb @@ -46,7 +46,7 @@ module Puppet::Util::Backups rescue => detail # since they said they want a backup, let's error out # if we couldn't make one - self.fail Puppet::Error, "Could not back #{file} up: #{detail.message}", detail.backtrace + self.fail Puppet::Error, "Could not back #{file} up: #{detail.message}", detail end end @@ -74,7 +74,7 @@ module Puppet::Util::Backups rescue => detail message = "Could not remove old backup: #{detail}" self.log_exception(detail, message) - self.fail Puppet::Error, message, detail.backtrace + self.fail Puppet::Error, message, detail end end From f4b6cc2c2ceef7b1402a76b624e872dae3b98857 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Wed, 29 Jan 2014 15:19:22 -0800 Subject: [PATCH 513/800] (PUP-576) Handle Ruby 2.0 OpenSSL lib error message The error message returned by Ruby's OpenSSL library when the host name does not match the server certificate changes between 1.8, 1.9 and 2.x. We handle this message and re-wrap it in a more informative form, but were failing to catch the 2.x variation. 1.8: 'hostname was not match with the server certificate' 1.9: "hostname does not match the server certificate" 2.x: "hostname \"#{hostname}\" does not match the server certificate" (2.0 and 2.1 as of the time of this commit) This change just alters the regex to catch all three variants. --- lib/puppet/network/http/connection.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/network/http/connection.rb b/lib/puppet/network/http/connection.rb index e7468562e..a2ce6eef3 100644 --- a/lib/puppet/network/http/connection.rb +++ b/lib/puppet/network/http/connection.rb @@ -197,7 +197,7 @@ module Puppet::Network::HTTP msg = error.message msg << ": [" + @verify.verify_errors.join('; ') + "]" raise Puppet::Error, msg, error.backtrace - elsif error.message =~ /hostname (\w+ )?not match/ + elsif error.message =~ /hostname.*not match.*server certificate/ leaf_ssl_cert = @verify.peer_certs.last valid_certnames = [leaf_ssl_cert.name, *leaf_ssl_cert.subject_alt_names].uniq From abcfc8d8d02cd4da0f94f856b7b7d2e0724d5ca5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Dal=C3=A9n?= Date: Thu, 30 Jan 2014 22:27:31 +0100 Subject: [PATCH 514/800] (maint) Fix error message in reduce function If you had 3 parameters this error message would print the type of the wrong parameter. --- lib/puppet/parser/functions/reduce.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/parser/functions/reduce.rb b/lib/puppet/parser/functions/reduce.rb index ddd2d8537..5dd454a3e 100644 --- a/lib/puppet/parser/functions/reduce.rb +++ b/lib/puppet/parser/functions/reduce.rb @@ -78,7 +78,7 @@ Puppet::Parser::Functions::newfunction( raise ArgumentError, ("reduce(): wrong number of arguments (#{args.length}; must be 2 or 3)") end unless pblock.respond_to?(:puppet_lambda) - raise ArgumentError, ("reduce(): wrong argument type (#{args[1].class}; must be a parameterized block.") + raise ArgumentError, ("reduce(): wrong argument type (#{pblock.class}; must be a parameterized block.") end receiver = args[0] case receiver From efa9eefce26e2ff81e55aa4c6e78ddd99ef30f62 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 30 Jan 2014 22:36:23 +0100 Subject: [PATCH 515/800] (PUP-1529) Make squelch of parse error use context instead of global This fixes up the earlier fix for 3.4.x where a global variable was used to control if parse errors should be raised or not. Now the context system is used instead of a global variable. (It is still a boolean value since there is no other immediate use for a strategy that either raises the error or logs the problem). --- lib/puppet/indirector/resource_type/parser.rb | 14 ++------------ lib/puppet/parser/type_loader.rb | 2 +- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/lib/puppet/indirector/resource_type/parser.rb b/lib/puppet/indirector/resource_type/parser.rb index 9b9194727..6632f3d8a 100644 --- a/lib/puppet/indirector/resource_type/parser.rb +++ b/lib/puppet/indirector/resource_type/parser.rb @@ -23,10 +23,7 @@ class Puppet::Indirector::ResourceType::Parser < Puppet::Indirector::Code # @return [Puppet::Resource::Type, nil] # @api public def find(request) - begin - # This is a fix in 3.x that will be replaced with the use of a context - # (That is not available until 3.5). - $squelsh_parse_errors = true + Puppet.override(:squelch_parse_errors => true) do krt = request.environment.known_resource_types # This is a bit ugly. @@ -39,8 +36,6 @@ class Puppet::Indirector::ResourceType::Parser < Puppet::Indirector::Code end end nil - ensure - $squelsh_parse_errors = false end end @@ -60,10 +55,7 @@ class Puppet::Indirector::ResourceType::Parser < Puppet::Indirector::Code # # @api public def search(request) - begin - # This is a fix in 3.x that will be replaced with the use of a context - # (That is not available until 3.5). - $squelsh_parse_errors = true + Puppet.override(:squelch_parse_errors => true) do krt = request.environment.known_resource_types # Make sure we've got all of the types loaded. @@ -100,7 +92,5 @@ class Puppet::Indirector::ResourceType::Parser < Puppet::Indirector::Code return nil if result.empty? result end - ensure - $squelsh_parse_errors = false end end diff --git a/lib/puppet/parser/type_loader.rb b/lib/puppet/parser/type_loader.rb index 36e2b259d..1357f948d 100644 --- a/lib/puppet/parser/type_loader.rb +++ b/lib/puppet/parser/type_loader.rb @@ -112,7 +112,7 @@ class Puppet::Parser::TypeLoader # The use case is that parsing for the purpose of searching for information # should not abort. There is currently one such use case in indirector/resourcetype/parser # - if $squelsh_parse_errors + if Puppet.lookup(:squelch_parse_errors) {|| false } begin loaded_asts << parse_file(file) rescue => e From 284bbfcdc9de9f40e54ecc01907f8cda156999e6 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Wed, 29 Jan 2014 18:09:52 -0800 Subject: [PATCH 516/800] (PUP-576,PUP-1064) Update to activerecord 3.2.x to work around a Ruby 2.x issue tests/store_configs/enc_provides_node_when_storeconfigs_enabled.rb fails running under Ruby 2.0 because of a bug between activerecord 3.0 and Ruby 2.0. This fails with: 'undefined method `insert_record' for ' when Puppet::Rails::Host attempts to save with merged facts. The root cause is tangled up in ActiveRecord's association proxies. ActiveRecord 3.2 has patches which allow it to work under Ruby 2.0 However making this change unearthed another Rails issue which Andrew Parker recalled was in fact PUP-1064. This issue tracks a general failure in Puppet on any Ruby version working with ActiveRecord > 3.0.10. Fortunately there was a patch attached to PUP-1064 which guards lookup of the activerecord id property on the puppet/rails/resource.rb activerecord object, and this does appear to fix the issue. --- Gemfile | 2 +- .../enc_provides_node_when_storeconfigs_enabled.rb | 4 ++-- lib/puppet/rails/resource.rb | 6 +++++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index 824660a6b..94120da85 100644 --- a/Gemfile +++ b/Gemfile @@ -49,7 +49,7 @@ end group(:extra) do gem "rack", "~> 1.4", :require => false - gem "activerecord", '~> 3.0.7', :require => false + gem "activerecord", '~> 3.2', :require => false gem "couchrest", '~> 1.0', :require => false gem "net-ssh", '~> 2.1', :require => false gem "puppetlabs_spec_helper", :require => false diff --git a/acceptance/tests/store_configs/enc_provides_node_when_storeconfigs_enabled.rb b/acceptance/tests/store_configs/enc_provides_node_when_storeconfigs_enabled.rb index daf283f51..0d8e16785 100644 --- a/acceptance/tests/store_configs/enc_provides_node_when_storeconfigs_enabled.rb +++ b/acceptance/tests/store_configs/enc_provides_node_when_storeconfigs_enabled.rb @@ -29,9 +29,9 @@ create_remote_file master, "#{testdir}/setup.pp", < $lsbmajdistrelease ? { 5 => '2.2.3', - default => '3.0.20', + default => '3.2.16', }, - default => '3.0.20', + default => '3.2.16', } package { diff --git a/lib/puppet/rails/resource.rb b/lib/puppet/rails/resource.rb index cded3f3c1..a2f732613 100644 --- a/lib/puppet/rails/resource.rb +++ b/lib/puppet/rails/resource.rb @@ -84,7 +84,11 @@ class Puppet::Rails::Resource < ActiveRecord::Base end def [](param) - super || parameter(param) + if param == 'id' + super + else + super || parameter(param) + end end # Make sure this resource is equivalent to the provided Parser resource. From b349e7dae5134b6f2c0fb0558b276c222db79e32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Dal=C3=A9n?= Date: Thu, 30 Jan 2014 23:07:30 +0100 Subject: [PATCH 517/800] (maint) Add optional_parameter_count method to Closure Add a method to return number of optional parameters on Puppet::Pops::Evaluator::Closure just like Puppet::Parser::AST::Lambda has. --- lib/puppet/pops/evaluator/closure.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/puppet/pops/evaluator/closure.rb b/lib/puppet/pops/evaluator/closure.rb index 980f3d139..05a4fd55a 100644 --- a/lib/puppet/pops/evaluator/closure.rb +++ b/lib/puppet/pops/evaluator/closure.rb @@ -39,4 +39,10 @@ class Puppet::Pops::Evaluator::Closure (@model.parameters || []).size end + # Returns the number of optional parameters. + # @return [Integer] the number of optional accepted parameters + def optional_parameter_count + @model.parameters.count { |p| !p.value.nil? } + end + end From 3c4669dc042baa6aaf45835b1e94e275fce1668f Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Thu, 30 Jan 2014 15:09:35 -0800 Subject: [PATCH 518/800] (PUP-1118) Convert environment for nodes on load The environment for a Puppet::Node instance is turned into simply the name of the environment when the Puppet::Node is serialized. Since we then load the Puppet::Node back in using YAML, and YAML uses the magic of type annotations to re-create the Puppet::Node instance, the environment that ends up being set on the Puppet::Node instance isn't an instance of Puppet::Node::Environment, but is a String. Since the Puppet::Node doesn't get a callback during deserialization, we added a fixup method to the Puppet::Indirector::YAML implementation so that the node object can be fixed up before being handed out to the big, bad world. The other choice was to use safe_yaml to load the file and then do the conversion, but safe_yaml is significantly slower than just plain yaml and we decided to avoid the performance hit. --- lib/puppet/indirector/node/yaml.rb | 15 +++++++++++++++ lib/puppet/indirector/yaml.rb | 14 +++++--------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/lib/puppet/indirector/node/yaml.rb b/lib/puppet/indirector/node/yaml.rb index 5a316b62e..b72abab24 100644 --- a/lib/puppet/indirector/node/yaml.rb +++ b/lib/puppet/indirector/node/yaml.rb @@ -4,4 +4,19 @@ require 'puppet/indirector/yaml' class Puppet::Node::Yaml < Puppet::Indirector::Yaml desc "Store node information as flat files, serialized using YAML, or deserialize stored YAML nodes." + + protected + + def fix(object) + # This looks very strange because when the object is read from disk the + # environment is a string and by assigning it back onto the object it gets + # converted to a Puppet::Node::Environment. + # + # The Puppet::Node class can't handle this itself because we are loading + # with just straight YAML, which doesn't give the object a chance to modify + # things as it is loaded. Instead YAML simply sets the instance variable + # and leaves it at that. + object.environment = object.environment + object + end end diff --git a/lib/puppet/indirector/yaml.rb b/lib/puppet/indirector/yaml.rb index dae477e76..9a6be8895 100644 --- a/lib/puppet/indirector/yaml.rb +++ b/lib/puppet/indirector/yaml.rb @@ -9,7 +9,7 @@ class Puppet::Indirector::Yaml < Puppet::Indirector::Terminus return nil unless Puppet::FileSystem.exist?(file) begin - return Puppet::Util::Yaml.load_file(file) + return fix(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}", detail.backtrace end @@ -51,17 +51,13 @@ class Puppet::Indirector::Yaml < Puppet::Indirector::Terminus def search(request) Dir.glob(path(request.key,'')).collect do |file| - YAML.load_file(file) + fix(Puppet::Util::Yaml.load_file(file)) end end - private + protected - def from_yaml(text) - YAML.load(text) - end - - def to_yaml(object) - YAML.dump(object) + def fix(object) + object end end From ab168ce94d0ce7db57757b2705394ec5bc02cf27 Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Thu, 30 Jan 2014 15:12:12 -0800 Subject: [PATCH 519/800] (PUP-1278) Updating Windows service event messages. Changing the service startup message to not include the agent parameters. This will now only be printed when actually doing the agent run. Removing the "service running" message from the loop as this prints out repeatedly even if the service is paused. Moving determining the path to puppet to outside the run loop. --- ext/windows/service/daemon.rb | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/ext/windows/service/daemon.rb b/ext/windows/service/daemon.rb index 3f05ee95f..84e4a283d 100755 --- a/ext/windows/service/daemon.rb +++ b/ext/windows/service/daemon.rb @@ -54,18 +54,16 @@ class WindowsDaemon < Win32::Daemon # ) # end - log_notice("Starting service with arguments: #{args}") + puppet = File.join(basedir, 'bin', 'puppet.bat') + unless File.exists?(puppet) + log_err("File not found: '#{puppet}'") + return + end + log_debug("Using '#{puppet}'") + + log_notice('Service started') while running? do - log_notice('Service running') - - puppet = File.join(basedir, 'bin', 'puppet.bat') - unless File.exists?(puppet) - log_err("File not found: '#{puppet}'") - return - end - - log_debug("Using '#{puppet}'") begin runinterval = %x{ "#{puppet}" agent --configprint runinterval }.to_i if runinterval == 0 @@ -78,6 +76,7 @@ class WindowsDaemon < Win32::Daemon end if state == RUNNING or state == IDLE + log_notice("Executing agent with arguments: #{args}") pid = Process.create(:command_line => "\"#{puppet}\" agent --onetime #{args}", :creation_flags => Process::CREATE_NEW_CONSOLE).process_id log_debug("Process created: #{pid}") else From 1ca7c2c0473547e1e7766c316f6001248b3525e9 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 31 Jan 2014 08:48:37 -0800 Subject: [PATCH 520/800] (maint) Remove warning added during debugging --- lib/puppet/file_system/file_impl.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/puppet/file_system/file_impl.rb b/lib/puppet/file_system/file_impl.rb index 1c945ae31..bf8ff31e5 100644 --- a/lib/puppet/file_system/file_impl.rb +++ b/lib/puppet/file_system/file_impl.rb @@ -12,7 +12,6 @@ class Puppet::FileSystem::FileImpl # (sigh). # unless path.is_a?(String) || path.respond_to?(:to_str) - Puppet.warning(caller) raise ArgumentError, "FileSystem implementation expected Pathname, got: '#{path.class}'" end # converts String and #to_str to Pathname From 923a76620e199daf42f3e29348a56223a1d8e7a6 Mon Sep 17 00:00:00 2001 From: "Ethan J. Brown" Date: Thu, 23 Jan 2014 12:25:47 -0800 Subject: [PATCH 521/800] PUP-1450 Windows file resource Invalid DACL errors - When the source of a file resource is referred to through a symlink it's possible the volume for the real file does not support ACLs. Unfortunately it's not possible to simply readlink the path to determine the real path / volume, so the best thing to do is attempt to read the DACL of the file, catch the exception when it's invalid and fallback to default owner / group / mode on Windows. - Furthermore, under VirtualBox, the standard ERROR_INVALID_DACL is not the error that propagates, but rather ERROR_INVALID_FUNCTION, so there is special handling for that scenario. - Note that this particular issue also affects paths like the Puppet module path puppet:///modules that may be symlinked in environments like Vagrant. --- lib/puppet/file_serving/metadata.rb | 37 +++++++++++++- spec/unit/file_serving/metadata_spec.rb | 65 +++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 1 deletion(-) diff --git a/lib/puppet/file_serving/metadata.rb b/lib/puppet/file_serving/metadata.rb index ae9376180..623c57bb8 100644 --- a/lib/puppet/file_serving/metadata.rb +++ b/lib/puppet/file_serving/metadata.rb @@ -49,7 +49,42 @@ class Puppet::FileServing::Metadata < Puppet::FileServing::Base :mode => 0644 }.each do |method, default_value| define_method method do - Puppet::Util::Windows::Security.send("get_#{method}", @path) || default_value + begin + Puppet::Util::Windows::Security.send("get_#{method}", @path) || default_value + rescue Puppet::Util::Windows::Error => detail + # Very carefully catch only this specific error that result from + # trying to read permissions on a symlinked file that is on a volume + # that does not support ACLs. + # + # Unfortunately readlink method will not return the target path when + # the given path is not the symlink. + # + # For instance, consider: + # symlink c:\link points to c:\target + # FileSystem.readlink('c:/link') returns 'c:/target' + # FileSystem.readlink('c:/link/file') will NOT return 'c:/target/file' + # + # Since detecting this up front is costly, since the path in question + # needs to be recursively split and tested at each depth in the path, + # we catch the standard error that will result from trying to read a + # file that doesn't have a DACL - 1336 is ERROR_INVALID_DACL + # + # Note that this affects any manually created symlinks as well as + # paths like puppet:///modules + return default_value if detail.code == 1336 + + # Also handle a VirtualBox bug where ERROR_INVALID_FUNCTION is + # returned when following a symlink to a volume that is not NTFS. + # It appears that the VirtualBox file system is not propagating + # the standard Win32 error code above like it should. + # + # Apologies to all who enter this code path at a later date + if detail.code == 1 && Facter.value(:virtual) == 'virtualbox' + return default_value + end + + raise + end end end end diff --git a/spec/unit/file_serving/metadata_spec.rb b/spec/unit/file_serving/metadata_spec.rb index 4dccb780c..51161c4fe 100755 --- a/spec/unit/file_serving/metadata_spec.rb +++ b/spec/unit/file_serving/metadata_spec.rb @@ -173,6 +173,46 @@ describe Puppet::FileServing::Metadata do end end + describe "WindowsStat", :if => Puppet.features.microsoft_windows? do + include PuppetSpec::Files + + it "should return default owner, group and mode when the given path has an invalid DACL (such as a non-NTFS volume)" do + invalid_error = Puppet::Util::Windows::Error.new('Invalid DACL', 1336) + path = tmpfile('foo') + FileUtils.touch(path) + + Puppet::Util::Windows::Security.stubs(:get_owner).with(path).raises(invalid_error) + Puppet::Util::Windows::Security.stubs(:get_group).with(path).raises(invalid_error) + Puppet::Util::Windows::Security.stubs(:get_mode).with(path).raises(invalid_error) + + stat = Puppet::FileSystem.stat(path) + + win_stat = Puppet::FileServing::Metadata::WindowsStat.new(stat, path) + + win_stat.owner.should == 'S-1-5-32-544' + win_stat.group.should == 'S-1-0-0' + win_stat.mode.should == 0644 + end + + it "should still raise errors that are not the result of an 'Invalid DACL'" do + invalid_error = ArgumentError.new('bar') + path = tmpfile('bar') + FileUtils.touch(path) + + Puppet::Util::Windows::Security.stubs(:get_owner).with(path).raises(invalid_error) + Puppet::Util::Windows::Security.stubs(:get_group).with(path).raises(invalid_error) + Puppet::Util::Windows::Security.stubs(:get_mode).with(path).raises(invalid_error) + + stat = Puppet::FileSystem.stat(path) + + win_stat = Puppet::FileServing::Metadata::WindowsStat.new(stat, path) + + expect { win_stat.owner }.to raise_error(ArgumentError) + expect { win_stat.group }.to raise_error(ArgumentError) + expect { win_stat.mode }.to raise_error(ArgumentError) + end + end + shared_examples_for "metadata collector symlinks" do let(:metadata) do @@ -277,6 +317,9 @@ describe Puppet::FileServing::Metadata do data.collect data end + let (:invalid_dacl_error) do + Puppet::Util::Windows::Error.new('Invalid DACL', 1336) + end it "should default owner" do Puppet::Util::Windows::Security.stubs(:get_owner).returns nil @@ -295,6 +338,28 @@ describe Puppet::FileServing::Metadata do metadata.mode.should == 0644 end + + describe "when the path raises an Invalid ACL error" do + # these simulate the behavior of a symlink file whose target does not support ACLs + it "should default owner" do + Puppet::Util::Windows::Security.stubs(:get_owner).raises(invalid_dacl_error) + + metadata.owner.should == 'S-1-5-32-544' + end + + it "should default group" do + Puppet::Util::Windows::Security.stubs(:get_group).raises(invalid_dacl_error) + + metadata.group.should == 'S-1-0-0' + end + + it "should default mode" do + Puppet::Util::Windows::Security.stubs(:get_mode).raises(invalid_dacl_error) + + metadata.mode.should == 0644 + end + end + end def set_mode(mode, path) From ee34bf9036ffd6dd5f9fa9f398c64b7be07ef213 Mon Sep 17 00:00:00 2001 From: "Ethan J. Brown" Date: Thu, 30 Jan 2014 16:36:57 -0800 Subject: [PATCH 522/800] PUP-1450 Honor source_permissions_ignore - In further debugging this failure, it was discovered that despite the :source_permissions parameter being set to :ignore on the file resource type, that we're still attempting to stat the source file and extract group / owner / mode. So instead of trying to stat the file, catching errors, and falling back to defaults, we properly ignore the group / owner / mode of the source file and provide reasonable defaults on both Windows and Linux. - In Windows, the defaults continue to be :owner => 'S-1-5-32-544' (BuiltinAdministrators) :group => 'S-1-0-0' (Nobody) :mode => 0644 - On other operating systems, the defaults are now set to be :owner => Process.euid :group => Process.egid :mode => 0644 - Several tests were refactored to be less fragile with respect to the :source_permissions parameter --- lib/puppet/file_serving/content.rb | 2 +- lib/puppet/file_serving/metadata.rb | 36 +++++++++++++------ lib/puppet/indirector/file_metadata/file.rb | 2 +- lib/puppet/indirector/file_server.rb | 2 +- lib/puppet/type/file/source.rb | 8 ++++- .../catalog/static_compiler_spec.rb | 10 +++--- spec/unit/type/file/source_spec.rb | 35 ++++++++++++++---- 7 files changed, 70 insertions(+), 25 deletions(-) diff --git a/lib/puppet/file_serving/content.rb b/lib/puppet/file_serving/content.rb index 189e899ca..00e9dc8ce 100644 --- a/lib/puppet/file_serving/content.rb +++ b/lib/puppet/file_serving/content.rb @@ -23,7 +23,7 @@ class Puppet::FileServing::Content < Puppet::FileServing::Base # BF: we used to fetch the file content here, but this is counter-productive # for puppetmaster streaming of file content. So collect just returns itself - def collect + def collect(source_permissions = nil) return if stat.ftype == "directory" self end diff --git a/lib/puppet/file_serving/metadata.rb b/lib/puppet/file_serving/metadata.rb index 623c57bb8..880c95993 100644 --- a/lib/puppet/file_serving/metadata.rb +++ b/lib/puppet/file_serving/metadata.rb @@ -25,13 +25,24 @@ class Puppet::FileServing::Metadata < Puppet::FileServing::Base class MetaStat extend Forwardable - def initialize(stat) + def initialize(stat, source_permissions = nil) @stat = stat + @source_permissions_ignore = source_permissions == :ignore end - def_delegator :@stat, :uid, :owner - def_delegator :@stat, :gid, :group - def_delegators :@stat, :mode, :ftype + def owner + @source_permissions_ignore ? Process.euid : @stat.uid + end + + def group + @source_permissions_ignore ? Process.egid : @stat.gid + end + + def mode + @source_permissions_ignore ? 0644 : @stat.mode + end + + def_delegators :@stat, :ftype end class WindowsStat < MetaStat @@ -39,8 +50,8 @@ class Puppet::FileServing::Metadata < Puppet::FileServing::Base require 'puppet/util/windows/security' end - def initialize(stat, path) - super(stat) + def initialize(stat, path, source_permissions = nil) + super(stat, source_permissions) @path = path end @@ -49,6 +60,9 @@ class Puppet::FileServing::Metadata < Puppet::FileServing::Base :mode => 0644 }.each do |method, default_value| define_method method do + return default_value if @source_permissions_ignore + + # this code remains for when source_permissions is not set to :ignore begin Puppet::Util::Windows::Security.send("get_#{method}", @path) || default_value rescue Puppet::Util::Windows::Error => detail @@ -89,23 +103,23 @@ class Puppet::FileServing::Metadata < Puppet::FileServing::Base end end - def collect_stat(path) + def collect_stat(path, source_permissions) stat = stat() if Puppet.features.microsoft_windows? - WindowsStat.new(stat, path) + WindowsStat.new(stat, path, source_permissions) else - MetaStat.new(stat) + MetaStat.new(stat, source_permissions) end end # Retrieve the attributes for this file, relative to a base directory. # Note that Puppet::FileSystem.stat(path) raises Errno::ENOENT # if the file is absent and this method does not catch that exception. - def collect + def collect(source_permissions = nil) real_path = full_path - stat = collect_stat(real_path) + stat = collect_stat(real_path, source_permissions) @owner = stat.owner @group = stat.group @ftype = stat.ftype diff --git a/lib/puppet/indirector/file_metadata/file.rb b/lib/puppet/indirector/file_metadata/file.rb index 9d8f839b3..78dfe5786 100644 --- a/lib/puppet/indirector/file_metadata/file.rb +++ b/lib/puppet/indirector/file_metadata/file.rb @@ -7,7 +7,7 @@ class Puppet::Indirector::FileMetadata::File < Puppet::Indirector::DirectFileSer def find(request) return unless data = super - data.collect + data.collect(request.options[:source_permissions]) data end diff --git a/lib/puppet/indirector/file_server.rb b/lib/puppet/indirector/file_server.rb index 9516a404c..389951dc3 100644 --- a/lib/puppet/indirector/file_server.rb +++ b/lib/puppet/indirector/file_server.rb @@ -29,7 +29,7 @@ class Puppet::Indirector::FileServer < Puppet::Indirector::Terminus return nil unless path = mount.find(relative_path, request) result = model.new(path) result.links = request.options[:links] if request.options[:links] - result.collect + result.collect(request.options[:source_permissions]) result end diff --git a/lib/puppet/type/file/source.rb b/lib/puppet/type/file/source.rb index e0cbf606f..7826ddf3d 100644 --- a/lib/puppet/type/file/source.rb +++ b/lib/puppet/type/file/source.rb @@ -169,7 +169,13 @@ module Puppet return nil unless value value.each do |source| begin - if data = Puppet::FileServing::Metadata.indirection.find(source, :environment => resource.catalog.environment, :links => resource[:links]) + options = { + :environment => resource.catalog.environment, + :links => resource[:links], + :source_permissions => resource[:source_permissions] + } + + if data = Puppet::FileServing::Metadata.indirection.find(source, options) @metadata = data @metadata.source = source break diff --git a/spec/unit/indirector/catalog/static_compiler_spec.rb b/spec/unit/indirector/catalog/static_compiler_spec.rb index e42ea777f..7f5aa997e 100644 --- a/spec/unit/indirector/catalog/static_compiler_spec.rb +++ b/spec/unit/indirector/catalog/static_compiler_spec.rb @@ -136,9 +136,11 @@ describe Puppet::Resource::Catalog::StaticCompiler do # Stub the call to the FileServer metadata API so we don't have to have # a real fileserver initialized for testing. Puppet::FileServing::Metadata. - indirection.stubs(:find). - with() { |*args| args[0] == options[:source].sub('puppet:///','') and args[1] == {:links => :manage, :environment => nil}}. - returns(fake_fileserver_metadata) + indirection.stubs(:find).with do |uri, opts| + expect(uri).to eq options[:source].sub('puppet:///','') + expect(opts[:links]).to eq :manage + expect(opts[:environment]).to eq nil + end.returns(fake_fileserver_metadata) # I want a resource that all the file resources require and another # that requires them. @@ -205,7 +207,7 @@ describe Puppet::Resource::Catalog::StaticCompiler do --- !ruby/object:Puppet::FileServing::Metadata checksum: "{md5}361fadf1c712e812d198c4cab5712a79" checksum_type: md5 - destination: + destination: expiration: #{Time.now + 1800} ftype: file group: 0 diff --git a/spec/unit/type/file/source_spec.rb b/spec/unit/type/file/source_spec.rb index 70e9a07ab..b6e97cd7a 100755 --- a/spec/unit/type/file/source_spec.rb +++ b/spec/unit/type/file/source_spec.rb @@ -94,6 +94,7 @@ describe Puppet::Type.type(:file).attrclass(:source) do before do @metadata = stub 'metadata', :source= => nil @resource.stubs(:[]).with(:links).returns :manage + @resource.stubs(:[]).with(:source_permissions) end it "should return already-available metadata" do @@ -109,22 +110,36 @@ describe Puppet::Type.type(:file).attrclass(:source) do it "should collect its metadata using the Metadata class if it is not already set" do @source = source.new(:resource => @resource, :value => @foobar) - Puppet::FileServing::Metadata.indirection.expects(:find).with(@foobar_uri, :environment => @environment, :links => :manage).returns @metadata + Puppet::FileServing::Metadata.indirection.expects(:find).with do |uri, options| + expect(uri).to eq @foobar_uri + expect(options[:environment]).to eq @environment + expect(options[:links]).to eq :manage + end.returns @metadata + @source.metadata end it "should use the metadata from the first found source" do metadata = stub 'metadata', :source= => nil @source = source.new(:resource => @resource, :value => [@foobar, @feebooz]) - Puppet::FileServing::Metadata.indirection.expects(:find).with(@foobar_uri, :environment => @environment, :links => :manage).returns nil - Puppet::FileServing::Metadata.indirection.expects(:find).with(@feebooz_uri, :environment => @environment, :links => :manage).returns metadata + options = { + :environment => @environment, + :links => :manage, + :source_permissions => nil + } + Puppet::FileServing::Metadata.indirection.expects(:find).with(@foobar_uri, options).returns nil + Puppet::FileServing::Metadata.indirection.expects(:find).with(@feebooz_uri, options).returns metadata @source.metadata.should equal(metadata) end it "should store the found source as the metadata's source" do metadata = mock 'metadata' @source = source.new(:resource => @resource, :value => @foobar) - Puppet::FileServing::Metadata.indirection.expects(:find).with(@foobar_uri, :environment => @environment, :links => :manage).returns metadata + Puppet::FileServing::Metadata.indirection.expects(:find).with do |uri, options| + expect(uri).to eq @foobar_uri + expect(options[:environment]).to eq @environment + expect(options[:links]).to eq :manage + end.returns metadata metadata.expects(:source=).with(@foobar_uri) @source.metadata @@ -132,7 +147,11 @@ describe Puppet::Type.type(:file).attrclass(:source) do it "should fail intelligently if an exception is encountered while querying for metadata" do @source = source.new(:resource => @resource, :value => @foobar) - Puppet::FileServing::Metadata.indirection.expects(:find).with(@foobar_uri, :environment => @environment, :links => :manage).raises RuntimeError + Puppet::FileServing::Metadata.indirection.expects(:find).with do |uri, options| + expect(uri).to eq @foobar_uri + expect(options[:environment]).to eq @environment + expect(options[:links]).to eq :manage + end.raises RuntimeError @source.expects(:fail).raises ArgumentError lambda { @source.metadata }.should raise_error(ArgumentError) @@ -140,7 +159,11 @@ describe Puppet::Type.type(:file).attrclass(:source) do it "should fail if no specified sources can be found" do @source = source.new(:resource => @resource, :value => @foobar) - Puppet::FileServing::Metadata.indirection.expects(:find).with(@foobar_uri, :environment => @environment, :links => :manage).returns nil + Puppet::FileServing::Metadata.indirection.expects(:find).with do |uri, options| + expect(uri).to eq @foobar_uri + expect(options[:environment]).to eq @environment + expect(options[:links]).to eq :manage + end.returns nil @source.expects(:fail).raises RuntimeError From 181d56cb8cc15a126b470d0f919b0c9b8ca31eac Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Thu, 30 Jan 2014 17:10:58 -0800 Subject: [PATCH 523/800] (PUP-1549) Use standard reason phrases on webrick The webrick interface was using the body of the response for the reason phrase for non-2xx responses. In the v1 api, this meant that we got bizarre response headers. In the v2 api, it meant that we got json formatted body data in the first header. Not only was that state of affairs odd, it was also inconsistent with the rack implementation, which didn't ever set the reason phrase. I am also a little concerned that some clients might have choked on large reason phrases (header too long issues). This removes the setting of the reason phrase on webrick, which give us back the standard reason phrases. --- lib/puppet/network/http/webrick/rest.rb | 1 - spec/unit/network/http/webrick/rest_spec.rb | 31 +++++++-------------- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/lib/puppet/network/http/webrick/rest.rb b/lib/puppet/network/http/webrick/rest.rb index 42e4d9ea3..66987151a 100644 --- a/lib/puppet/network/http/webrick/rest.rb +++ b/lib/puppet/network/http/webrick/rest.rb @@ -70,7 +70,6 @@ class Puppet::Network::HTTP::WEBrickREST < WEBrick::HTTPServlet::AbstractServlet response.body = result response["content-length"] = result.stat.size if result.is_a?(File) end - response.reason_phrase = result if status < 200 or status >= 300 end # Retrieve node/cert/ip information from the request object. diff --git a/spec/unit/network/http/webrick/rest_spec.rb b/spec/unit/network/http/webrick/rest_spec.rb index c2e6d2a5f..36b2bcff9 100755 --- a/spec/unit/network/http/webrick/rest_spec.rb +++ b/spec/unit/network/http/webrick/rest_spec.rb @@ -12,7 +12,7 @@ describe Puppet::Network::HTTP::WEBrickREST do describe "when receiving a request" do before do @request = stub('webrick http request', :query => {}, :peeraddr => %w{eh boo host ip}, :client_cert => nil) - @response = stub('webrick http response', :status= => true, :body= => true) + @response = mock('webrick http response') @model_class = stub('indirected model class') @webrick = stub('webrick http server', :mount => true, :[] => {}) Puppet::Indirector::Indirection.stubs(:model).with(:foo).returns(@model_class) @@ -66,32 +66,21 @@ describe Puppet::Network::HTTP::WEBrickREST do @handler.set_response(@response, "mybody", 200) end - describe "when the result is a File" do - before(:each) do - stat = stub 'stat', :size => 100 - @file = stub 'file', :stat => stat, :path => "/tmp/path" - @file.stubs(:is_a?).with(File).returns(true) - end + it "serves a file" do + stat = stub 'stat', :size => 100 + @file = stub 'file', :stat => stat, :path => "/tmp/path" + @file.stubs(:is_a?).with(File).returns(true) - it "should serve it" do - @response.stubs(:[]=) + @response.expects(:[]=).with('content-length', 100) + @response.expects(:status=).with 200 + @response.expects(:body=).with @file - @response.expects(:status=).with 200 - @response.expects(:body=).with @file - - @handler.set_response(@response, @file, 200) - end - - it "should set the Content-Length header" do - @response.expects(:[]=).with('content-length', 100) - - @handler.set_response(@response, @file, 200) - end + @handler.set_response(@response, @file, 200) end it "should set the status and message on the response when setting the response for a failed query" do @response.expects(:status=).with 400 - @response.expects(:reason_phrase=).with "mybody" + @response.expects(:body=).with "mybody" @handler.set_response(@response, "mybody", 400) end From 655ace5858c71843c6998cd343892a2a1a08349e Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Thu, 30 Jan 2014 17:20:11 -0800 Subject: [PATCH 524/800] (PUP-1549) Update documentation showing reason phrase All of the error responses that we had used as examples in the API documentation had been taken from using webrick. Now that webrick is consistent with rack, the docs need to be updated to show what the responses should really look like. --- api/docs/http_certificate.md | 6 +++--- api/docs/http_certificate_request.md | 4 ++-- api/docs/http_certificate_revocation_list.md | 2 +- api/docs/http_file_bucket_file.md | 4 +++- api/docs/http_file_content.md | 8 ++++++-- api/docs/http_file_metadata.md | 4 +++- api/docs/http_resource_type.md | 8 ++++---- api/docs/pson.md | 4 +++- 8 files changed, 25 insertions(+), 15 deletions(-) diff --git a/api/docs/http_certificate.md b/api/docs/http_certificate.md index b57949155..9b60eb034 100644 --- a/api/docs/http_certificate.md +++ b/api/docs/http_certificate.md @@ -77,7 +77,7 @@ The environment field is ignored. GET /env/certificate/certificate_does_not_exist - HTTP 404 Not Found: Could not find certificate certificate_does_not_exist + HTTP 404 Not Found Content-Type: text/plain Not Found: Could not find certificate certificate_does_not_exist @@ -86,7 +86,7 @@ The environment field is ignored. GET /env/certificate/ - HTTP/1.1 400 No request key specified in /env/certificate/ + HTTP/1.1 400 Bad Request Content-Type: text/plain No request key specified in /env/certificate/ @@ -95,7 +95,7 @@ The environment field is ignored. GET /env/certificate/valid_certificate - HTTP/1.1 400 this master is not a CA + HTTP/1.1 400 Bad Request Content-Type: text/plain this master is not a CA diff --git a/api/docs/http_certificate_request.md b/api/docs/http_certificate_request.md index 3b3b500fb..f84883169 100644 --- a/api/docs/http_certificate_request.md +++ b/api/docs/http_certificate_request.md @@ -89,7 +89,7 @@ None GET /env/certificate_request/does_not_exist - HTTP/1.1 404 Not Found: Could not find certificate_request does_not_exist + HTTP/1.1 404 Not Found Content-Type: text/plain Not Found: Could not find certificate_request does_not_exist @@ -98,7 +98,7 @@ None GET /env/certificate_request/ - HTTP/1.1 400 No request key specified in /env/certificate_request/ + HTTP/1.1 400 Bad Request Content-Type: text/plain No request key specified in /env/certificate_request/ diff --git a/api/docs/http_certificate_revocation_list.md b/api/docs/http_certificate_revocation_list.md index 0fcf481fb..fa7468be4 100644 --- a/api/docs/http_certificate_revocation_list.md +++ b/api/docs/http_certificate_revocation_list.md @@ -175,7 +175,7 @@ decoding of the CRL PEM file. GET /env/certificate_revocation_list - HTTP/1.1 400 No request key specified in /env/certificate_revocation_list + HTTP/1.1 400 Bad Request Content-Type: text/plain No request key specified in /env/certificate_revocation_list diff --git a/api/docs/http_file_bucket_file.md b/api/docs/http_file_bucket_file.md index 7b5ade372..3adf17b43 100644 --- a/api/docs/http_file_bucket_file.md +++ b/api/docs/http_file_bucket_file.md @@ -90,7 +90,9 @@ None > Accept: s - < HTTP/1.1 404 Not Found: Could not find file_bucket_file md5/4949e56d376cc80ce5387e8e89a75396/home/user/wrong_name + < HTTP/1.1 404 Not Found + < + < Not Found: Could not find file_bucket_file md5/4949e56d376cc80ce5387e8e89a75396/home/user/wrong_name Schema ------ diff --git a/api/docs/http_file_content.md b/api/docs/http_file_content.md index 8720a4cec..8b13417c7 100644 --- a/api/docs/http_file_content.md +++ b/api/docs/http_file_content.md @@ -49,16 +49,20 @@ None GET /env/file_content/modules/example/not_found Accept: raw - HTTP/1.1 404 Not Found: Could not find file_content modules/example/not_found + HTTP/1.1 404 Not Found Content-Type: text/plain + Not Found: Could not find file_content modules/example/not_found + #### No file name given GET /env/file_content/ - HTTP/1.1 400 No request key specified in /env/file_content/ + HTTP/1.1 400 Bad Request Content-Type: text/plain + No request key specified in /env/file_content/ + Schema ------ diff --git a/api/docs/http_file_metadata.md b/api/docs/http_file_metadata.md index 41b8077ce..e84c1fe14 100644 --- a/api/docs/http_file_metadata.md +++ b/api/docs/http_file_metadata.md @@ -130,7 +130,9 @@ None GET /env/file_metadata/modules/example/does_not_exist - HTTP/1.1 404 Not Found: Could not find file_metadata modules/example/does_not_exist + HTTP/1.1 404 Not Found + + Not Found: Could not find file_metadata modules/example/does_not_exist Search ------ diff --git a/api/docs/http_resource_type.md b/api/docs/http_resource_type.md index 6c1f6eee1..413b60cb8 100644 --- a/api/docs/http_resource_type.md +++ b/api/docs/http_resource_type.md @@ -66,7 +66,7 @@ None GET /env/resource_type/resource_type_does_not_exist - HTTP 404 Not Found: Could not find resource_type resource_type_does_not_exist + HTTP 404 Not Found Content-Type: text/plain Not Found: Could not find resource_type resource_type_does_not_exist @@ -75,7 +75,7 @@ None GET /env/resource_type/ - HTTP/1.1 400 No request key specified in /env/resource_type/ + HTTP/1.1 400 Bad Request Content-Type: text/plain No request key specified in /env/resource_type/ @@ -168,7 +168,7 @@ Accept: pson, text/pson GET /env/resource_types/ - HTTP/1.1 400 No request key specified in /env/resource_types/ + HTTP/1.1 400 Bad Request Content-Type: text/plain No request key specified in /env/resource_types/ @@ -179,7 +179,7 @@ Searching on `[-` for instance. GET /env/resource_types/%5b- - HTTP/1.1 400 Invalid regex '[-': premature end of char-class: /[-/ + HTTP/1.1 400 Bad Request Content-Type: text/plain Invalid regex '[-': premature end of char-class: /[-/ diff --git a/api/docs/pson.md b/api/docs/pson.md index 5695b9b31..23108d1d3 100644 --- a/api/docs/pson.md +++ b/api/docs/pson.md @@ -11,7 +11,9 @@ Puppet uses the MIME types "pson" and "text/pson" to refer to PSON. Differences from JSON --------------------- -PSON does *not differ* from JSON in its representation of objects, arrays, numbers, booleans, and null values. PSON *does* serialize strings differently from JSON. +PSON does *not differ* from JSON in its representation of objects, arrays, +numbers, booleans, and null values. PSON *does* serialize strings differently +from JSON. A PSON string is a sequence of 8-bit ASCII encoded data. It must start and end with " (ASCII 0x22) characters. Between these characters it may contain any From 7534bf73ac4b463d02b5b29acc6ba55290655eb4 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Fri, 31 Jan 2014 11:41:43 -0800 Subject: [PATCH 525/800] (maint) Acceptance install_utils uses release rpm symlinks The Puppet::Acceptance::InstallUtils was fiddling with extraneous parsing trying to pick the current release rpm version from deep in hierarchy of yum.puppetlabs.com. Now we make use of the root symlinks. --- .../lib/puppet/acceptance/install_utils.rb | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/acceptance/lib/puppet/acceptance/install_utils.rb b/acceptance/lib/puppet/acceptance/install_utils.rb index 904c74c6d..85ac84b01 100644 --- a/acceptance/lib/puppet/acceptance/install_utils.rb +++ b/acceptance/lib/puppet/acceptance/install_utils.rb @@ -94,15 +94,9 @@ module Puppet version = $2 arch = $3 - package_version = version == '19' ? '19-2' : "#{version}-7" - rpm = fetch( - "http://yum.puppetlabs.com/%s/%s%s/products/i386/" % [ - variant, - fedora_prefix, - version, - ], - "puppetlabs-release-%s.noarch.rpm" % package_version, + "http://yum.puppetlabs.com", + "puppetlabs-release-%s-%s.noarch.rpm" % [variant, version], platform_configs_dir ) @@ -114,13 +108,11 @@ module Puppet version, arch ] - begin - repo = fetch( - "http://builds.puppetlabs.lan/puppet/%s/repo_configs/rpm/" % sha, - repo_filename, - platform_configs_dir - ) - end + repo = fetch( + "http://builds.puppetlabs.lan/puppet/%s/repo_configs/rpm/" % sha, + repo_filename, + platform_configs_dir + ) on host, "rm -rf /root/*.repo; rm -rf /root/*.rpm" From 3ebdccffe65396d5b87f6f1dc5e3454a0e59ea64 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Fri, 31 Jan 2014 11:42:37 -0800 Subject: [PATCH 526/800] (PUP-576) Update store_configs test for Debian The store_configs/enc_provides_node_when_storeconfigs_enabled.rb test has been updated to use Activerecord 3.2.x, but this version of Activerecord no longer supports the sqlite3-ruby gem package available on Debian via apt. So we are installing the newer sqlite3 gem. As an added wrinkle, this is the same name as the core sqlite3 package on Debian, running us afoul of PUP-1073 (duplicate names for packages handled by different providers). We are working around that with a separate puppet apply for the sqlite3 installation. --- ...provides_node_when_storeconfigs_enabled.rb | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/acceptance/tests/store_configs/enc_provides_node_when_storeconfigs_enabled.rb b/acceptance/tests/store_configs/enc_provides_node_when_storeconfigs_enabled.rb index 0d8e16785..c6da0354d 100644 --- a/acceptance/tests/store_configs/enc_provides_node_when_storeconfigs_enabled.rb +++ b/acceptance/tests/store_configs/enc_provides_node_when_storeconfigs_enabled.rb @@ -41,17 +41,19 @@ package { activerecord: ensure => $active_record_version, provider => 'gem', - require => Package[rubygems] + require => Package[rubygems]; } if $osfamily == "Debian" { package { + # This is the deb sqlite3 package sqlite3: ensure => present; - libsqlite3-ruby: + libsqlite3-dev: ensure => present, - require => Package[sqlite3] + require => Package[sqlite3]; + } } elsif $osfamily == "RedHat" { $sqlite_gem_pkg_name = $operatingsystem ? { @@ -72,7 +74,23 @@ if $osfamily == "Debian" { } END +# This is a brute force hack around PUP-1073 because the deb for the core +# sqlite3 package and the rubygem for the sqlite3 driver are both named +# 'sqlite3'. So we just run a second puppet apply. +create_remote_file master, "#{testdir}/setup_sqlite_gem.pp", < 'sqlite3', + ensure => present, + provider => 'gem', + } +} +END + on master, puppet_apply("#{testdir}/setup.pp") +on master, puppet_apply("#{testdir}/setup_sqlite_gem.pp") master_opts = { 'master' => { From 658f1237c1596a6f735b7b707130fc0897a1a113 Mon Sep 17 00:00:00 2001 From: Peter Meier Date: Sun, 2 Feb 2014 14:35:07 +0100 Subject: [PATCH 527/800] Fix PUP-1568 - no success variable anymore In the refactoring within 000b8fef the success variables was removed. So the error message shouldn't anymore use it. --- lib/puppet/provider/augeas/augeas.rb | 8 ++++---- spec/unit/provider/augeas/augeas_spec.rb | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/lib/puppet/provider/augeas/augeas.rb b/lib/puppet/provider/augeas/augeas.rb index 47c0acb1a..f828b8de0 100644 --- a/lib/puppet/provider/augeas/augeas.rb +++ b/lib/puppet/provider/augeas/augeas.rb @@ -425,10 +425,10 @@ Puppet::Type.type(:augeas).provide(:augeas) do set_augeas_save_mode(SAVE_OVERWRITE) if versioncmp(get_augeas_version, "0.3.6") >= 0 @aug.load do_execute_changes - unless @aug.save - print_put_errors - fail("Save failed with return code #{success}, see debug") - end + unless @aug.save + print_put_errors + fail("Save failed, see debug") + end :executed ensure diff --git a/spec/unit/provider/augeas/augeas_spec.rb b/spec/unit/provider/augeas/augeas_spec.rb index 13c0412b1..191bfbd0d 100755 --- a/spec/unit/provider/augeas/augeas_spec.rb +++ b/spec/unit/provider/augeas/augeas_spec.rb @@ -663,6 +663,20 @@ describe provider_class do @augeas.expects(:set).with("/foo/test[2]/Jar/Jar", "Bar").returns(true) expect { @provider.execute_changes }.to raise_error(RuntimeError, /command 'clearm' not supported/) end + + it "should throw error if saving failed" do + @resource[:changes] = ["set test[1]/Jar/Jar Foo","set test[2]/Jar/Jar Bar","clearm test Jar/Jar"] + @resource[:context] = "/foo/" + @augeas.expects(:respond_to?).with("clearm").returns(true) + @augeas.expects(:set).with("/foo/test[1]/Jar/Jar", "Foo").returns(true) + @augeas.expects(:set).with("/foo/test[2]/Jar/Jar", "Bar").returns(true) + @augeas.expects(:clearm).with("/foo/test", "Jar/Jar").returns(true) + @augeas.expects(:save).returns(false) + @provider.expects(:print_put_errors) + @augeas.expects(:match).returns([]) + expect { @provider.execute_changes }.to raise_error(Puppet::Error) + end + end describe "when making changes", :if => Puppet.features.augeas? do From 49412e0c4a6c39c16ad5bc8af3d3395abdbd9214 Mon Sep 17 00:00:00 2001 From: Peter Meier Date: Sun, 2 Feb 2014 14:47:53 +0100 Subject: [PATCH 528/800] Fix broken spec & wording of error message. While working on 658f1237c1596a6f735b7b707130fc0897a1a113 I stumbled accross another messages that would say 'with error code false', which doesn't make a lot of sense. Also the plain 'expect raise_error' was masking another problem with tests. One should always only look for the exact expected error and not just general for any kind of error. --- lib/puppet/provider/augeas/augeas.rb | 2 +- spec/unit/provider/augeas/augeas_spec.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/puppet/provider/augeas/augeas.rb b/lib/puppet/provider/augeas/augeas.rb index f828b8de0..e3203c81e 100644 --- a/lib/puppet/provider/augeas/augeas.rb +++ b/lib/puppet/provider/augeas/augeas.rb @@ -385,7 +385,7 @@ Puppet::Type.type(:augeas).provide(:augeas) do save_result = @aug.save unless save_result print_put_errors - fail("Save failed with return code #{save_result}, see debug") + fail("Saving failed, see debug") end saved_files = @aug.match("/augeas/events/saved") diff --git a/spec/unit/provider/augeas/augeas_spec.rb b/spec/unit/provider/augeas/augeas_spec.rb index 191bfbd0d..3d3499626 100755 --- a/spec/unit/provider/augeas/augeas_spec.rb +++ b/spec/unit/provider/augeas/augeas_spec.rb @@ -499,7 +499,8 @@ describe provider_class do @augeas.expects(:close) @provider.expects(:diff).never() - lambda { @provider.need_to_run? }.should raise_error + @provider.expects(:print_put_errors) + lambda { @provider.need_to_run? }.should raise_error(Puppet::Error) end end end @@ -676,7 +677,6 @@ describe provider_class do @augeas.expects(:match).returns([]) expect { @provider.execute_changes }.to raise_error(Puppet::Error) end - end describe "when making changes", :if => Puppet.features.augeas? do From ce83c664bc87b904c45a3f192cf5fb00a385932a Mon Sep 17 00:00:00 2001 From: Ashley Penney Date: Sun, 2 Feb 2014 15:18:57 -0500 Subject: [PATCH 529/800] (PUP-1066) Add the ability to delete files. Add a .destory and .destroy? in order to flag files for deletion. We also sneak in a .entries reader in order to let us read the entries directly. This should fix PUP-1066 and #12687. --- lib/puppet/util/inifile.rb | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/lib/puppet/util/inifile.rb b/lib/puppet/util/inifile.rb index 82f074d53..df3c59850 100644 --- a/lib/puppet/util/inifile.rb +++ b/lib/puppet/util/inifile.rb @@ -13,13 +13,15 @@ require 'puppet/util/filetype' module Puppet::Util::IniConfig # A section in a .ini file class Section - attr_reader :name, :file + attr_reader :name, :file, :entries + attr_writer :destroy def initialize(name, file) @name = name @file = file @dirty = false @entries = [] + @destroy = false end # Has this section been modified since it's been read in @@ -33,6 +35,11 @@ module Puppet::Util::IniConfig @dirty = false end + # Should the file be destroyed? + def destroy? + @destroy + end + # Add a line of text (e.g., a comment) Such lines # will be written back out in exactly the same # place they were read in @@ -142,8 +149,10 @@ module Puppet::Util::IniConfig @files.each do |file, lines| text = "" dirty = false + destroy = false lines.each do |l| if l.is_a?(Section) + destroy ||= l.destroy? dirty ||= l.dirty? text << l.format l.mark_clean @@ -151,9 +160,15 @@ module Puppet::Util::IniConfig text << l end end - if dirty - Puppet::Util::FileType.filetype(:flat).new(file).write(text) - return file + # We delete the file and then remove it from the list of files. + if destroy + ::File.unlink(file) + @files.delete(file) + else + if dirty + Puppet::Util::FileType.filetype(:flat).new(file).write(text) + return file + end end end end From 8bab12e9c9e2a18b023cac2b46c96e54739f96c0 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Mon, 3 Feb 2014 11:13:54 -0800 Subject: [PATCH 530/800] (Maint) Extract re-usable resource matcher Checking that catalogs have particular resources is useful. Let's make it easier. This also adds parameter matching to the resource matcher. --- spec/lib/matchers/resource.rb | 35 +++++++++++++++++++ .../additional_resource_generator_spec.rb | 12 ++----- 2 files changed, 37 insertions(+), 10 deletions(-) create mode 100644 spec/lib/matchers/resource.rb diff --git a/spec/lib/matchers/resource.rb b/spec/lib/matchers/resource.rb new file mode 100644 index 000000000..3964a3e9e --- /dev/null +++ b/spec/lib/matchers/resource.rb @@ -0,0 +1,35 @@ +module Matchers; module Resource + extend RSpec::Matchers::DSL + + matcher :have_resource do |expected_resource| + @params = {} + + match do |actual_catalog| + @mismatch = "" + if resource = actual_catalog.resource(expected_resource) + matched = true + failures = [] + @params.each do |name, value| + if resource[name] != value + matched = false + failures << "expected #{name} to be '#{value}' but was '#{resource[name]}'" + end + end + @mismatch = failures.join("\n") + + matched + else + @mismatch = "expected #{@actual.to_dot} to include #{@expected[0]}" + false + end + end + + chain :with_parameter do |name, value| + @params[name] = value + end + + def failure_message_for_should + @mismatch + end + end +end; end diff --git a/spec/unit/transaction/additional_resource_generator_spec.rb b/spec/unit/transaction/additional_resource_generator_spec.rb index 6983ba354..27dc3cbeb 100644 --- a/spec/unit/transaction/additional_resource_generator_spec.rb +++ b/spec/unit/transaction/additional_resource_generator_spec.rb @@ -3,11 +3,13 @@ require 'puppet/transaction' require 'puppet_spec/compiler' require 'matchers/relationship_graph_matchers' require 'matchers/include_in_order' +require 'matchers/resource' describe Puppet::Transaction::AdditionalResourceGenerator do include PuppetSpec::Compiler include PuppetSpec::Files include RelationshipGraphMatchers + include Matchers::Resource let(:prioritizer) { Puppet::Graph::SequentialPrioritizer.new } @@ -407,13 +409,3 @@ describe Puppet::Transaction::AdditionalResourceGenerator do end end end - -RSpec::Matchers.define :have_resource do |expected_resource| - match do |actual_catalog| - actual_catalog.resource(expected_resource) - end - - def failure_message_for_should - "expected #{@actual.to_dot} to include #{@expected[0]}" - end -end From 6c982a95f21031de0a2fcb02dae11410875587fb Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Mon, 3 Feb 2014 13:26:59 -0800 Subject: [PATCH 531/800] (maint) Add how-to for destroying individual hosts --- acceptance/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/acceptance/README.md b/acceptance/README.md index ae4411a04..aa30ddb35 100644 --- a/acceptance/README.md +++ b/acceptance/README.md @@ -112,6 +112,11 @@ If you run a number of jobs with --preserve_hosts or vi ci:test_and_preserve_hos to clean them up sooner and free resources. +There also may be scenarios where you want to specify the host(s) to destroy. E.g. you may want to destroy a subset of the hosts you've created. Or, if a test run terminates early, ci:destroy_preserved_hosts may not be able to derive the name of the vm to delete. In such cases you can specify host(s) to be deleted using the HOST_NAMES environment variable. E.g. + + HOST_NAMES=lvwwr9tdplg351u bundle exec rake ci:destroy_preserved_hosts + HOST_NAMES=lvwwr9tdplg351u,ylrqjh5l6xvym4t bundle exec rake ci:destroy_preserved_hosts + Running Tests on Vagrant Boxen ------------------------------ From 83c0eff03996e5003f8386c816515f1651729246 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Mon, 3 Feb 2014 14:27:49 -0800 Subject: [PATCH 532/800] (PUP-865) Allow BlockExpression to evaluate everything Previously BlockExpression would skip evaluating any AST nodes that implement an instantiate method. The assumption was that instiate only existed on Hostclass, Node, and Definition AST nodes and those same classes also did not implement an evaluate method. With the introduction of the PopsBridge::Program class, this entire house of assumption cards came falling down, since it impelements both instantiate and evaluate. The only thing that truly prevented BlockExpression from calling evaluate was that the default evaluate implementation on Parser::AST raised a Puppet::DevError to try to catch the case of evaluate not being implemented for an AST class. Since Parser::AST is entirely internal and missing an evaluate should show up very quickly in tests, we can just as easily make evaluate a noop by default, which then simplifies BlockExpression. This change fixes the problem where the body of pp files were loaded, but not evaluated when using the future evaluator and a directory of manifest files. The problem was triggered by the BlockExpression logic interacting with the PopsBridge::Program object and the structure created by the Puppet::Pops::Parser::CodeMerger. --- lib/puppet/parser/ast.rb | 1 - lib/puppet/parser/ast/block_expression.rb | 7 +---- lib/puppet/parser/code_merger.rb | 2 +- lib/puppet/pops/parser/code_merger.rb | 4 +-- .../node/environment/sitedir/01_b.pp | 6 +++- .../node/environment/sitedir/04_include.pp | 2 ++ spec/integration/node/environment_spec.rb | 31 ++++++++----------- 7 files changed, 24 insertions(+), 29 deletions(-) create mode 100644 spec/fixtures/integration/node/environment/sitedir/04_include.pp diff --git a/lib/puppet/parser/ast.rb b/lib/puppet/parser/ast.rb index bf60c75a0..b7e324687 100644 --- a/lib/puppet/parser/ast.rb +++ b/lib/puppet/parser/ast.rb @@ -36,7 +36,6 @@ class Puppet::Parser::AST # Evaluate the current object. Just a stub method, since the subclass # should override this method. def evaluate(*options) - raise Puppet::DevError, "Did not override #evaluate in #{self.class}" end # Throw a parse error. diff --git a/lib/puppet/parser/ast/block_expression.rb b/lib/puppet/parser/ast/block_expression.rb index ac6173c39..b6b7e66b4 100644 --- a/lib/puppet/parser/ast/block_expression.rb +++ b/lib/puppet/parser/ast/block_expression.rb @@ -8,12 +8,7 @@ class Puppet::Parser::AST def evaluate(scope) result = nil @children.each do |child| - # Skip things that respond to :instantiate (classes, nodes, - # and definitions), because they have already been - # instantiated. - if !child.respond_to?(:instantiate) - result = child.safeevaluate(scope) - end + result = child.safeevaluate(scope) end result end diff --git a/lib/puppet/parser/code_merger.rb b/lib/puppet/parser/code_merger.rb index 62d72dd05..e8913b88b 100644 --- a/lib/puppet/parser/code_merger.rb +++ b/lib/puppet/parser/code_merger.rb @@ -10,4 +10,4 @@ class Puppet::Parser::CodeMerger end Puppet::Parser::AST::BlockExpression.new(:children => children) end -end \ No newline at end of file +end diff --git a/lib/puppet/pops/parser/code_merger.rb b/lib/puppet/pops/parser/code_merger.rb index 8634b329d..79e1328cd 100644 --- a/lib/puppet/pops/parser/code_merger.rb +++ b/lib/puppet/pops/parser/code_merger.rb @@ -12,6 +12,6 @@ class Puppet::Pops::Parser::CodeMerger children = parse_results.select {|x| !x.nil? && x.code}.reduce([]) do |memo, parsed_class| memo << parsed_class.code end - main = Puppet::Parser::AST::BlockExpression.new(:children => children) + Puppet::Parser::AST::BlockExpression.new(:children => children) end -end \ No newline at end of file +end diff --git a/spec/fixtures/integration/node/environment/sitedir/01_b.pp b/spec/fixtures/integration/node/environment/sitedir/01_b.pp index 4dce5eb83..25e339b4c 100644 --- a/spec/fixtures/integration/node/environment/sitedir/01_b.pp +++ b/spec/fixtures/integration/node/environment/sitedir/01_b.pp @@ -1,2 +1,6 @@ class b {} -$b = $a # error if $a not set in strict mode \ No newline at end of file + +# if the files are evaluated in the wrong order, the file 'b' has a reference +# to $a (set in file 'a') and with strict variable lookup should raise an error +# and fail this test. +$b = $a # error if $a not set in strict mode diff --git a/spec/fixtures/integration/node/environment/sitedir/04_include.pp b/spec/fixtures/integration/node/environment/sitedir/04_include.pp new file mode 100644 index 000000000..293067a7f --- /dev/null +++ b/spec/fixtures/integration/node/environment/sitedir/04_include.pp @@ -0,0 +1,2 @@ +include a, b +notify { "variables": message => "a: $a, b: $b" } diff --git a/spec/integration/node/environment_spec.rb b/spec/integration/node/environment_spec.rb index 105c70944..b99337ed5 100755 --- a/spec/integration/node/environment_spec.rb +++ b/spec/integration/node/environment_spec.rb @@ -3,9 +3,11 @@ require 'spec_helper' require 'puppet_spec/files' require 'puppet_spec/scope' +require 'matchers/resource' describe Puppet::Node::Environment do include PuppetSpec::Files + include Matchers::Resource def a_module_in(name, dir) Dir.mkdir(dir) @@ -55,29 +57,22 @@ describe Puppet::Node::Environment do end shared_examples_for "the environment's initial import" do - include PuppetSpec::Scope - - let(:node) { Puppet::Node.new('testnode') } - it "a manifest referring to a directory invokes parsing of all its files in sorted order" do - # fixture has three files 00_a.pp, 01_b.pp, and 02_c.pp. The 'b' file depends on 'a' - # being evaluated first. The 'c' file is empty (to ensure empty things do not break the directory import). - # if the files are evaluated in the wrong order, the file 'b' has a reference to $a (set in file 'a') - # and with strict variable lookup should raise an error and fail this test. + # fixture has three files 00_a.pp, 01_b.pp, and 02_c.pp. The 'b' file + # depends on 'a' being evaluated first. The 'c' file is empty (to ensure + # empty things do not break the directory import). # dirname = my_fixture('sitedir') + # Set the manifest to the directory to make it parse and combine them when compiling - Puppet[:manifest] = dirname + node = Puppet::Node.new('testnode', + :environment => Puppet::Node::Environment.create(:testing, [], dirname)) - # include the classes that are in the fixture files - node.stubs(:classes).returns(['a', 'b']) - - # compile, to make the initial_import in the environment take place the correct way catalog = Puppet::Parser::Compiler.compile(node) - class_a = catalog.resource('Class[a]') - class_b = catalog.resource('Class[b]') - expect(class_a).to_not be_nil - expect(class_b).to_not be_nil + + expect(catalog).to have_resource('Class[a]') + expect(catalog).to have_resource('Class[b]') + expect(catalog).to have_resource('Notify[variables]').with_parameter(:message, "a: 10, b: 10") end end @@ -101,6 +96,6 @@ describe Puppet::Node::Environment do end it_behaves_like "the environment's initial import" do end -end + end end From dae56db2a224610ad4af76067185eb4e30b1a5c6 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Mon, 3 Feb 2014 15:18:09 -0800 Subject: [PATCH 533/800] (PUP-865) Only use the pops merge for the future evaluator The future evaluator is only really in play when both the parser is future and the evaluator is future. The old check left out the evaluator portion of that check and so ended up using the wrong CodeMerger when using the current evaluator with the future parser. --- lib/puppet/parser/parser_factory.rb | 3 +-- spec/integration/node/environment_spec.rb | 8 ++++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/puppet/parser/parser_factory.rb b/lib/puppet/parser/parser_factory.rb index d4239afe6..576bc8755 100644 --- a/lib/puppet/parser/parser_factory.rb +++ b/lib/puppet/parser/parser_factory.rb @@ -76,8 +76,7 @@ module Puppet::Parser end def self.code_merger - case Puppet[:parser] - when 'future' + if Puppet[:parser] == 'future' && Puppet[:evaluator] == 'future' Puppet::Pops::Parser::CodeMerger.new else Puppet::Parser::CodeMerger.new diff --git a/spec/integration/node/environment_spec.rb b/spec/integration/node/environment_spec.rb index b99337ed5..c94e43e54 100755 --- a/spec/integration/node/environment_spec.rb +++ b/spec/integration/node/environment_spec.rb @@ -96,6 +96,14 @@ describe Puppet::Node::Environment do end it_behaves_like "the environment's initial import" do end + + context 'using evaluator current' do + before do + Puppet[:evaluator] = 'current' + end + + it_behaves_like "the environment's initial import" + end end end From 4d408df0de3ac100eedd83f304f3697b03d8a315 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Mon, 3 Feb 2014 15:25:45 -0800 Subject: [PATCH 534/800] (Maint) Clean up tests --- spec/integration/node/environment_spec.rb | 50 +++++++++++------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/spec/integration/node/environment_spec.rb b/spec/integration/node/environment_spec.rb index c94e43e54..d193367ee 100755 --- a/spec/integration/node/environment_spec.rb +++ b/spec/integration/node/environment_spec.rb @@ -56,8 +56,12 @@ describe Puppet::Node::Environment do mods[0].path.should == File.join(base, "dir1", "mod") end - shared_examples_for "the environment's initial import" do + shared_examples_for "the environment's initial import" do |settings| it "a manifest referring to a directory invokes parsing of all its files in sorted order" do + settings.each do |name, value| + Puppet[name] = value + end + # fixture has three files 00_a.pp, 01_b.pp, and 02_c.pp. The 'b' file # depends on 'a' being evaluated first. The 'c' file is empty (to ensure # empty things do not break the directory import). @@ -77,33 +81,29 @@ describe Puppet::Node::Environment do end describe 'using classic parser' do - before :each do - Puppet[:parser] = 'current' - # fixture uses variables that are set in a particular order (this ensures that files are parsed - # and combined in the right order or an error will be raised if 'b' is evaluated before 'a'). - Puppet[:strict_variables] = true - end - it_behaves_like "the environment's initial import" do - end + it_behaves_like "the environment's initial import", + :parser => 'current', + # fixture uses variables that are set in a particular order (this ensures + # that files are parsed and combined in the right order or an error will + # be raised if 'b' is evaluated before 'a'). + :strict_variables => true end + describe 'using future parser' do - before :each do - Puppet[:parser] = 'future' - # Turned off because currently future parser turns on the binder which causes lookup of facts - # that are uninitialized and it will fail with errors for 'osfamily' etc. - # This can be turned back on when the binder is taken out of the equation. - # Puppet[:strict_variables] = true - end - it_behaves_like "the environment's initial import" do - end + it_behaves_like "the environment's initial import", + :parser => 'future', + :evaluator => 'future', + # Turned off because currently future parser turns on the binder which + # causes lookup of facts that are uninitialized and it will fail with + # errors for 'osfamily' etc. This can be turned back on when the binder + # is taken out of the equation. + :strict_variables => false - context 'using evaluator current' do - before do - Puppet[:evaluator] = 'current' - end - - it_behaves_like "the environment's initial import" + context 'and evaluator current' do + it_behaves_like "the environment's initial import", + :parser => 'future', + :evaluator => 'current', + :strict_variables => false end end - end From 04a657cc81905d82b03f434c2f544ac4f2a4a8a3 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Thu, 30 Jan 2014 16:57:38 -0800 Subject: [PATCH 535/800] (PUP-1551) Change environmentdir to environmentpath In order to provide more flexibility we are changing the environmentdir setting to an environmentpath setting so that multiple environment directories can be set as loaders in an established path sequence. --- .../use_environment_from_environmentdir.rb | 184 +++++++++++++----- lib/puppet.rb | 10 +- lib/puppet/defaults.rb | 8 +- lib/puppet/environments.rb | 12 ++ lib/puppet/test/test_helper.rb | 6 +- 5 files changed, 159 insertions(+), 61 deletions(-) diff --git a/acceptance/tests/environment/use_environment_from_environmentdir.rb b/acceptance/tests/environment/use_environment_from_environmentdir.rb index 4f2a95c40..a4a703503 100644 --- a/acceptance/tests/environment/use_environment_from_environmentdir.rb +++ b/acceptance/tests/environment/use_environment_from_environmentdir.rb @@ -1,6 +1,62 @@ -test_name "Use environments from the environmentdir" +test_name "Use environments from the environmentpath" -testdir = master.tmpdir('use_environmentdir') +testdir = master.tmpdir('use_environmentpath') + +def generate_environment(path_to_env, environment) + env_content = <<-EOS + "#{path_to_env}/#{environment}":; + "#{path_to_env}/#{environment}/manifests":; + "#{path_to_env}/#{environment}/modules":; + EOS +end + +def generate_module_content(module_name, options = {}) + base_path = options[:base_path] + environment = options[:environment] + env_path = options[:env_path] + + path_to_module = [base_path, env_path, environment, "modules"].compact.join("/") + module_info = "module-#{module_name}" + module_info << "-from-#{environment}" if environment + + module_content = <<-EOS + "#{path_to_module}/#{module_name}":; + "#{path_to_module}/#{module_name}/manifests":; + "#{path_to_module}/#{module_name}/files":; + "#{path_to_module}/#{module_name}/templates":; + "#{path_to_module}/#{module_name}/lib":; + "#{path_to_module}/#{module_name}/lib/facter":; + + "#{path_to_module}/#{module_name}/manifests/init.pp": + ensure => file, + content => 'class #{module_name} { + notify { template-#{module_name}: message => template("#{module_name}/our_template.erb") } + file { "$agent_file_location/file-#{module_info}": source => "puppet:///modules/#{module_name}/data" } + }' + ; + "#{path_to_module}/#{module_name}/lib/facter/environment_fact_#{module_name}.rb": + ensure => file, + content => "Facter.add(:environment_fact_#{module_name}) { setcode { 'environment fact from #{module_info}' } }" + ; + "#{path_to_module}/#{module_name}/files/data": + ensure => file, + content => "data file from #{module_info}" + ; + "#{path_to_module}/#{module_name}/templates/our_template.erb": + ensure => file, + content => "<%= @environment_fact_#{module_name} %>" + ; + EOS +end + +def generate_site_manifest(path_to_manifest, *modules_to_include) + manifest_content = <<-EOS + "#{path_to_manifest}/site.pp": + ensure => file, + content => "#{modules_to_include.map { |m| "include #{m}" }.join("\n")}" + ; + EOS +end apply_manifest_on(master, <<-MANIFEST, :catch_failures => true) File { @@ -11,66 +67,98 @@ File { file { "#{testdir}":; - "#{testdir}/environments":; - "#{testdir}/environments/special":; - "#{testdir}/environments/special/manifests":; - "#{testdir}/environments/special/modules":; - "#{testdir}/environments/special/modules/amod":; - "#{testdir}/environments/special/modules/amod/manifests":; - "#{testdir}/environments/special/modules/amod/files":; - "#{testdir}/environments/special/modules/amod/templates":; - "#{testdir}/environments/special/modules/amod/lib":; - "#{testdir}/environments/special/modules/amod/lib/facter":; + "#{testdir}/base":; + "#{testdir}/additional":; + "#{testdir}/modules":; +#{generate_environment("#{testdir}/base", "shadowed")} +#{generate_environment("#{testdir}/base", "onlybase")} +#{generate_environment("#{testdir}/additional", "shadowed")} - "#{testdir}/environments/special/modules/amod/manifests/init.pp": - ensure => file, - content => 'class amod { - notify { template: message => template("amod/our_template.erb") } - file { "$agent_file_location/file": source => "puppet:///modules/amod/data" } - }' - ; - "#{testdir}/environments/special/modules/amod/lib/facter/environment_fact.rb": - ensure => file, - content => "Facter.add(:environment_fact) { setcode { 'environment fact' } }" - ; - "#{testdir}/environments/special/modules/amod/files/data": - ensure => file, - content => "data file" - ; - "#{testdir}/environments/special/modules/amod/templates/our_template.erb": - ensure => file, - content => "<%= @environment_fact %>" - ; - "#{testdir}/environments/special/manifests/site.pp": - ensure => file, - content => "include amod" - ; +#{generate_module_content("atmp", + :base_path => testdir, + :env_path => 'base', + :environment => 'shadowed')} +#{generate_site_manifest("#{testdir}/base/shadowed/manifests", "atmp", "globalmod")} + +#{generate_module_content("atmp", + :base_path => testdir, + :env_path => 'base', + :environment => 'onlybase')} +#{generate_site_manifest("#{testdir}/base/onlybase/manifests", "atmp", "globalmod")} + +#{generate_module_content("atmp", + :base_path => testdir, + :env_path => 'additional', + :environment => 'shadowed')} +#{generate_site_manifest("#{testdir}/additional/shadowed/manifests", "atmp", "globalmod")} + +# And one global module (--modulepath setting) +#{generate_module_content("globalmod", :base_path => testdir)} } MANIFEST +def run_with_environment(agent, environment, options = {}) + expected_exit_code = options[:expected_exit_code] || 2 + expected_strings = options[:expected_strings] + + step "running an agent in environment '#{environment}'" + atmp = agent.tmpdir("use_environmentpath_#{environment}") + + agent_config = [ + "-t", + "--server", master, + ] + agent_config << '--environment' << environment if environment + agent_config << { + 'ENV' => { "FACTER_agent_file_location" => atmp }, + } + + on(agent, + puppet("agent", *agent_config), + :acceptable_exit_codes => [expected_exit_code]) do |result| + + yield atmp, result + end + + on agent, "rm -rf #{atmp}" +end + master_opts = { 'master' => { - 'environmentdir' => "#{testdir}/environments" + 'environmentpath' => "#{testdir}/additional:#{testdir}/base", + 'modulepath' => "#{testdir}/modules", } } with_puppet_running_on master, master_opts, testdir do agents.each do |agent| - atmp = agent.tmpdir('use_environmentdir') - on agent, puppet("agent", - "--environment", "special", - "-t", - "--server", master, - "--trace", - 'ENV' => { "FACTER_agent_file_location" => atmp }), - :acceptable_exit_codes => [2] do |result| - assert_match(/environment fact/, result.stdout) + run_with_environment(agent, "shadowed") do |tmpdir,catalog_result| + ["module-atmp-from-shadowed", "module-globalmod"].each do |expected| + assert_match(/environment fact from #{expected}/, catalog_result.stdout) + end + + ["module-atmp-from-shadowed", "module-globalmod"].each do |expected| + on agent, "cat #{tmpdir}/file-#{expected}" do |file_result| + assert_match(/data file from #{expected}/, file_result.stdout) + end + end end - on agent, "cat #{atmp}/file" do |result| - assert_match(/data file/, result.stdout) + run_with_environment(agent, "onlybase") do |tmpdir,catalog_result| + ["module-atmp-from-onlybase", "module-globalmod"].each do |expected| + assert_match(/environment fact from #{expected}/, catalog_result.stdout) + end + + ["module-atmp-from-onlybase", "module-globalmod"].each do |expected| + on agent, "cat #{tmpdir}/file-#{expected}" do |file_result| + assert_match(/data file from #{expected}/, file_result.stdout) + end + end end - on agent, "rm -rf #{atmp}" + run_with_environment(agent, nil, :expected_exit_code => 0) do |tmpdir, result| + assert_not_match(/module-atmp/, result.stdout) + assert_match(/Loading facts.*globalmod/, result.stdout) + end end end diff --git a/lib/puppet.rb b/lib/puppet.rb index 51c5451e8..ece55c6ab 100644 --- a/lib/puppet.rb +++ b/lib/puppet.rb @@ -174,14 +174,14 @@ module Puppet # The bindings used for initialization of puppet # @api private def self.base_context(settings) - environments = settings[:environmentdir] + environments = settings[:environmentpath] modulepath = Puppet::Node::Environment.split_path(settings[:modulepath]) + loaders = Puppet::Environments::Directories.from_path(environments, modulepath) + loaders << Puppet::Environments::Legacy.new + { - :environments => Puppet::Environments::Combined.new( - Puppet::Environments::Directories.new(environments, modulepath), - Puppet::Environments::Legacy.new - ) + :environments => Puppet::Environments::Combined.new(*loaders) } end diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index cb7c3f10a..41e79881a 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -196,12 +196,10 @@ module Puppet is used to find modules and much more. For servers (i.e., `puppet master`) this provides the default environment for nodes we know nothing about." }, - :environmentdir => { + :environmentpath => { :default => "$confdir/environments", - :desc => "A directory of environments", - :type => :directory, - :owner => "service", - :group => "service", + :desc => "A path of environment directoriess", + :type => :path, }, :diff_args => { :default => default_diffargs, diff --git a/lib/puppet/environments.rb b/lib/puppet/environments.rb index 80dec6d69..7ce9637b1 100644 --- a/lib/puppet/environments.rb +++ b/lib/puppet/environments.rb @@ -113,6 +113,18 @@ module Puppet::Environments @global_module_path = global_module_path end + # Generate an array of directory loaders from a path string. + # @param path [String] path to environment directories + # @param global_module_path [String] the global modulepath setting + # @return [Array] An array + # of configured directory loaders. + def self.from_path(path, global_module_path) + environments = path.split(File::PATH_SEPARATOR) + environments.map do |dir| + Puppet::Environments::Directories.new(dir, global_module_path) + end + end + # @!macro loader_search_paths def search_paths ["environments://directories/#{@environment_dir}"] diff --git a/lib/puppet/test/test_helper.rb b/lib/puppet/test/test_helper.rb index 55b952c33..c9c428c25 100644 --- a/lib/puppet/test/test_helper.rb +++ b/lib/puppet/test/test_helper.rb @@ -39,7 +39,7 @@ module Puppet::Test owner = Process.pid @environmentdir = Dir.mktmpdir('environments') Puppet.push_context(Puppet.base_context({ - :environmentdir => @environmentdir, + :environmentpath => @environmentdir, :modulepath => "", :manifest => "/dev/null" }), "Initial for specs") @@ -207,9 +207,9 @@ module Puppet::Test # Although we setup a testing context during initialization, some tests # will end up creating their own context using the real context objects # and use the setting for the environments. In order to avoid those tests - # having to deal with a missing environmentdir we can just set it right + # having to deal with a missing environmentpath we can just set it right # here. - Puppet[:environmentdir] = @environmentdir + Puppet[:environmentpath] = @environmentpath end private_class_method :initialize_settings_before_each end From 424ce15ece1eca7acb87038d838290111d151577 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 4 Feb 2014 12:03:28 +0100 Subject: [PATCH 536/800] (PUP-1579) Rename the Literal type to Scalar Calling the type "Literal" was bad because it is used not only for literal values entered in source code, but naturally also for computed values. A commonly used name for these types is "Scalar". This commit changes "Literal" to "Scalar" everywhere: * class name PLiteralType => PScalarType * its label * factory methods "literal" => "scalar" --- lib/puppet/pops/binder/bindings_factory.rb | 8 +- lib/puppet/pops/types/type_calculator.rb | 40 ++++----- lib/puppet/pops/types/type_factory.rb | 12 +-- lib/puppet/pops/types/type_parser.rb | 6 +- lib/puppet/pops/types/types.rb | 16 ++-- spec/unit/pops/evaluator/access_ops_spec.rb | 10 +-- .../pops/evaluator/evaluating_parser_spec.rb | 6 +- spec/unit/pops/types/type_calculator_spec.rb | 82 +++++++++---------- spec/unit/pops/types/type_factory_spec.rb | 8 +- spec/unit/pops/types/type_parser_spec.rb | 6 +- 10 files changed, 97 insertions(+), 97 deletions(-) diff --git a/lib/puppet/pops/binder/bindings_factory.rb b/lib/puppet/pops/binder/bindings_factory.rb index 3f85df48a..c7716c52b 100644 --- a/lib/puppet/pops/binder/bindings_factory.rb +++ b/lib/puppet/pops/binder/bindings_factory.rb @@ -262,11 +262,11 @@ module Puppet::Pops::Binder::BindingsFactory type(T.pattern()) end - # Sets the type of the binding to the abstract type Literal. - # @return [Puppet::Pops::Types::PLiteralType] the type + # Sets the type of the binding to the abstract type Scalar. + # @return [Puppet::Pops::Types::PScalarType] the type # @api public - def literal() - type(T.literal()) + def scalar() + type(T.scalar()) end # Sets the type of the binding to the abstract type Data. diff --git a/lib/puppet/pops/types/type_calculator.rb b/lib/puppet/pops/types/type_calculator.rb index acc05a942..bcf19a130 100644 --- a/lib/puppet/pops/types/type_calculator.rb +++ b/lib/puppet/pops/types/type_calculator.rb @@ -3,19 +3,19 @@ # The Puppet type system is primarily based on sub-classing. When asking the type calculator to infer types from Ruby in general, it # may not provide the wanted answer; it does not for instance take module inclusions and extensions into account. In general the type # system should be unsurprising for anyone being exposed to the notion of type. The type `Data` may require a bit more explanation; this -# is an abstract type that includes all literal types, as well as Array with an element type compatible with Data, and Hash with key -# compatible with Literal and elements compatible with Data. Expressed differently; Data is what you typically express using JSON (with -# the exception that the Puppet type system also includes Pattern (regular expression) as a literal. +# is an abstract type that includes all scalar types, as well as Array with an element type compatible with Data, and Hash with key +# compatible with scalar and elements compatible with Data. Expressed differently; Data is what you typically express using JSON (with +# the exception that the Puppet type system also includes Pattern (regular expression) as a scalar. # # Inference # --------- -# The `infer(o)` method infers a Puppet type for literal Ruby objects, and for Arrays and Hashes. +# The `infer(o)` method infers a Puppet type for scalar Ruby objects, and for Arrays and Hashes. # The inference result is instance specific for single typed collections # and allows answering questions about its embedded type. It does not however preserve multiple types in # a collection, and can thus not answer questions like `[1,a].infer() =~ Array[Integer, String]` since the inference -# computes the common type Literal when combining Integer and String. +# computes the common type Scalar when combining Integer and String. # -# The `infer_generic(o)` method infers a generic Puppet type for literal Ruby object, Arrays and Hashes. +# The `infer_generic(o)` method infers a generic Puppet type for scalar Ruby object, Arrays and Hashes. # This inference result does not contain instance specific information; e.g. Array[Integer] where the integer # range is the generic default. Just `infer` it also combines types into a common type. # @@ -63,7 +63,7 @@ # In general, the type calculator should be used to answer questions if a type is a subtype of another (using {#assignable?}, or # {#instance?} if the question is if a given object is an instance of a given type (or is a subtype thereof). # Many of the types also have a Ruby subtype relationship; e.g. PHashType and PArrayType are both subtypes of PCollectionType, and -# PIntegerType, PFloatType, PStringType,... are subtypes of PLiteralType. Even if it is possible to answer certain questions about +# PIntegerType, PFloatType, PStringType,... are subtypes of PScalarType. Even if it is possible to answer certain questions about # type by looking at the Ruby class of the types this is considered an implementation detail, and such checks should in general # be performed by the type_calculator which implements the type system semantics. # @@ -156,11 +156,11 @@ class Puppet::Pops::Types::TypeCalculator h = Types::PHashType.new() h.element_type = Types::PDataType.new() - h.key_type = Types::PLiteralType.new() + h.key_type = Types::PScalarType.new() @data_hash = h @data_t = Types::PDataType.new() - @literal_t = Types::PLiteralType.new() + @scalar_t = Types::PScalarType.new() @numeric_t = Types::PNumericType.new() @t = Types::PObjectType.new() @@ -168,7 +168,7 @@ class Puppet::Pops::Types::TypeCalculator data_variant = Types::PVariantType.new() data_variant.addTypes(@data_hash.copy) data_variant.addTypes(@data_array.copy) - data_variant.addTypes(Types::PLiteralType.new) + data_variant.addTypes(Types::PScalarType.new) data_variant.addTypes(Types::PNilType.new) @data_variant_t = data_variant @@ -273,9 +273,9 @@ class Puppet::Pops::Types::TypeCalculator type = Types::PArrayType.new() type.element_type = Types::PDataType.new() when c == Hash - # Assume hash with literal keys and data values + # Assume hash with scalar keys and data values type = Types::PHashType.new() - type.key_type = Types::PLiteralType.new() + type.key_type = Types::PScalarType.new() type.element_type = Types::PDataType.new() else type = Types::PRubyType.new() @@ -521,8 +521,8 @@ class Puppet::Pops::Types::TypeCalculator return Types::PNumericType.new() end - if common_literal?(t1, t2) - return Types::PLiteralType.new() + if common_scalar?(t1, t2) + return Types::PScalarType.new() end if common_data?(t1,t2) @@ -794,8 +794,8 @@ class Puppet::Pops::Types::TypeCalculator end # @api private - def assignable_PLiteralType(t, t2) - t2.is_a?(Types::PLiteralType) + def assignable_PScalarType(t, t2) + t2.is_a?(Types::PScalarType) end # @api private @@ -994,7 +994,7 @@ class Puppet::Pops::Types::TypeCalculator return t1.title == t2.title end - # Data is assignable by other Data and by Array[Data] and Hash[Literal, Data] + # Data is assignable by other Data and by Array[Data] and Hash[Scalar, Data] # @api private def assignable_PDataType(t, t2) t2.is_a?(Types::PDataType) || assignable?(@data_variant_t, t2) @@ -1042,7 +1042,7 @@ class Puppet::Pops::Types::TypeCalculator def string_PBooleanType(t) ; "Boolean" ; end # @api private - def string_PLiteralType(t) ; "Literal" ; end + def string_PScalarType(t) ; "Scalar" ; end # @api private def string_PDataType(t) ; "Data" ; end @@ -1200,8 +1200,8 @@ class Puppet::Pops::Types::TypeCalculator assignable?(@data_t, t1) && assignable?(@data_t, t2) end - def common_literal?(t1, t2) - assignable?(@literal_t, t1) && assignable?(@literal_t, t2) + def common_scalar?(t1, t2) + assignable?(@scalar_t, t1) && assignable?(@scalar_t, t2) end def common_numeric?(t1, t2) diff --git a/lib/puppet/pops/types/type_factory.rb b/lib/puppet/pops/types/type_factory.rb index ee702f6de..6208bda08 100644 --- a/lib/puppet/pops/types/type_factory.rb +++ b/lib/puppet/pops/types/type_factory.rb @@ -149,8 +149,8 @@ module Puppet::Pops::Types::TypeFactory # Produces the Literal type # @api public # - def self.literal() - Types::PLiteralType.new() + def self.scalar() + Types::PScalarType.new() end # Produces the abstract type Collection @@ -210,10 +210,10 @@ module Puppet::Pops::Types::TypeFactory type end - # Produces a type for Hash[Literal, o] where o is either a type, or an instance for which a type is inferred. + # Produces a type for Hash[Scalar, o] where o is either a type, or an instance for which a type is inferred. # @api public # - def self.hash_of(value, key = literal()) + def self.hash_of(value, key = scalar()) type = Types::PHashType.new() type.key_type = type_of(key) type.element_type = type_of(value) @@ -229,12 +229,12 @@ module Puppet::Pops::Types::TypeFactory type end - # Produces a type for Hash[Literal, Data] + # Produces a type for Hash[Scalar, Data] # @api public # def self.hash_of_data() type = Types::PHashType.new() - type.key_type = literal() + type.key_type = scalar() type.element_type = data() type end diff --git a/lib/puppet/pops/types/type_parser.rb b/lib/puppet/pops/types/type_parser.rb index f351ffe14..bb9d0267f 100644 --- a/lib/puppet/pops/types/type_parser.rb +++ b/lib/puppet/pops/types/type_parser.rb @@ -139,8 +139,8 @@ class Puppet::Pops::Types::TypeParser when "collection" TYPES.collection() - when "literal" - TYPES.literal() + when "scalar" + TYPES.scalar() when "catalogentry" TYPES.catalog_entry() @@ -345,7 +345,7 @@ class Puppet::Pops::Types::TypeParser assert_type(parameters[0]) TYPES.optional(parameters[0]) - when "object", "data", "catalogentry", "boolean", "literal", "undef", "numeric" + when "object", "data", "catalogentry", "boolean", "scalar", "undef", "numeric" raise_unparameterized_type_error(parameterized_ast.left_expr) when "type" diff --git a/lib/puppet/pops/types/types.rb b/lib/puppet/pops/types/types.rb index e6d848648..4d3ad1cb1 100644 --- a/lib/puppet/pops/types/types.rb +++ b/lib/puppet/pops/types/types.rb @@ -3,7 +3,7 @@ require 'rgen/metamodel_builder' # The Types model is a model of Puppet Language types. # # The exact relationship between types is not visible in this model wrt. the PDataType which is an abstraction -# of Literal, Array[Data], and Hash[Literal, Data] nested to any depth. This means it is not possible to +# of Scalar, Array[Data], and Hash[Scalar, Data] nested to any depth. This means it is not possible to # infer the type by simply looking at the inheritance hierarchy. The {Puppet::Pops::Types::TypeCalculator} should # be used to answer questions about types. The {Puppet::Pops::Types::TypeFactory} should be used to create an instance # of a type whenever one is needed. @@ -100,12 +100,12 @@ module Puppet::Pops::Types # Type that is PDataType compatible, but is not a PCollectionType. # @api public - class PLiteralType < PObjectType + class PScalarType < PObjectType end # A string type describing the set of strings having one of the given values # - class PEnumType < PLiteralType + class PEnumType < PScalarType has_many_attr 'values', String, :lowerBound => 1 module ClassModule @@ -120,7 +120,7 @@ module Puppet::Pops::Types end # @api public - class PNumericType < PLiteralType + class PNumericType < PScalarType end # @api public @@ -177,7 +177,7 @@ module Puppet::Pops::Types end # @api public - class PStringType < PLiteralType + class PStringType < PScalarType has_many_attr 'values', String, :lowerBound => 0, :upperBound => -1, :unique => true contains_one_uni 'size_type', PIntegerType @@ -194,7 +194,7 @@ module Puppet::Pops::Types end # @api public - class PRegexpType < PLiteralType + class PRegexpType < PScalarType has_attr 'pattern', String, :lowerBound => 1 has_attr 'regexp', Object, :derived => true @@ -218,7 +218,7 @@ module Puppet::Pops::Types # If specified without a pattern it is basically the same as the String type. # # @api public - class PPatternType < PLiteralType + class PPatternType < PScalarType contains_many_uni 'patterns', PRegexpType module ClassModule @@ -234,7 +234,7 @@ module Puppet::Pops::Types end # @api public - class PBooleanType < PLiteralType + class PBooleanType < PScalarType end # @api public diff --git a/spec/unit/pops/evaluator/access_ops_spec.rb b/spec/unit/pops/evaluator/access_ops_spec.rb index 1f19b929d..aaecbab4d 100644 --- a/spec/unit/pops/evaluator/access_ops_spec.rb +++ b/spec/unit/pops/evaluator/access_ops_spec.rb @@ -166,13 +166,13 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do # Hash Type # - it 'produces a Hash[Literal,String] from the expression Hash[String]' do + it 'produces a Hash[Scalar,String] from the expression Hash[String]' do expr = fqr('Hash')[fqr('String')] - expect(evaluate(expr)).to be_the_type(types.hash_of(types.string, types.literal)) + expect(evaluate(expr)).to be_the_type(types.hash_of(types.string, types.scalar)) # arguments are flattened expr = fqr('Hash')[[fqr('String')]] - expect(evaluate(expr)).to be_the_type(types.hash_of(types.string, types.literal)) + expect(evaluate(expr)).to be_the_type(types.hash_of(types.string, types.scalar)) end it 'produces a Hash[String,String] from the expression Hash[String, String]' do @@ -180,9 +180,9 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do expect(evaluate(expr)).to be_the_type(types.hash_of(types.string, types.string)) end - it 'produces a Hash[Literal,String] from the expression Hash[Integer][String]' do + it 'produces a Hash[Scalar,String] from the expression Hash[Integer][String]' do expr = fqr('Hash')[fqr('Integer')][fqr('String')] - expect(evaluate(expr)).to be_the_type(types.hash_of(types.string, types.literal)) + expect(evaluate(expr)).to be_the_type(types.hash_of(types.string, types.scalar)) end it "gives an error if parameter is not a type" do diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index 511efd6a5..43cfb7737 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -276,12 +276,12 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do end { - 'Object' => ['Data', 'Literal', 'Numeric', 'Integer', 'Float', 'Boolean', 'String', 'Pattern', 'Collection', + 'Object' => ['Data', 'Scalar', 'Numeric', 'Integer', 'Float', 'Boolean', 'String', 'Pattern', 'Collection', 'Array', 'Hash', 'CatalogEntry', 'Resource', 'Class', 'Undef', 'File', 'NotYetKnownResourceType'], # Note, Data > Collection is false (so not included) - 'Data' => ['Literal', 'Numeric', 'Integer', 'Float', 'Boolean', 'String', 'Pattern', 'Array', 'Hash',], - 'Literal' => ['Numeric', 'Integer', 'Float', 'Boolean', 'String', 'Pattern'], + 'Data' => ['Scalar', 'Numeric', 'Integer', 'Float', 'Boolean', 'String', 'Pattern', 'Array', 'Hash',], + 'Scalar' => ['Numeric', 'Integer', 'Float', 'Boolean', 'String', 'Pattern'], 'Numeric' => ['Integer', 'Float'], 'CatalogEntry' => ['Class', 'Resource', 'File', 'NotYetKnownResourceType'], 'Integer[1,10]' => ['Integer[2,3]'], diff --git a/spec/unit/pops/types/type_calculator_spec.rb b/spec/unit/pops/types/type_calculator_spec.rb index b50d5da3a..a9393ed55 100644 --- a/spec/unit/pops/types/type_calculator_spec.rb +++ b/spec/unit/pops/types/type_calculator_spec.rb @@ -65,7 +65,7 @@ describe 'The type calculator' do [ Puppet::Pops::Types::PObjectType, Puppet::Pops::Types::PNilType, Puppet::Pops::Types::PDataType, - Puppet::Pops::Types::PLiteralType, + Puppet::Pops::Types::PScalarType, Puppet::Pops::Types::PStringType, Puppet::Pops::Types::PNumericType, Puppet::Pops::Types::PIntegerType, @@ -84,10 +84,10 @@ describe 'The type calculator' do ] end - def literal_types - # PVariantType is also literal, if its types are all Literal + def scalar_types + # PVariantType is also scalar, if its types are all Scalar [ - Puppet::Pops::Types::PLiteralType, + Puppet::Pops::Types::PScalarType, Puppet::Pops::Types::PStringType, Puppet::Pops::Types::PNumericType, Puppet::Pops::Types::PIntegerType, @@ -127,7 +127,7 @@ describe 'The type calculator' do end def data_compatible_types - result = literal_types + result = scalar_types result << Puppet::Pops::Types::PDataType result << array_t(types::PDataType.new) result << types::TypeFactory.hash_of_data @@ -223,24 +223,24 @@ describe 'The type calculator' do calculator.infer([1,2.0]).element_type.class.should == Puppet::Pops::Types::PNumericType end - it 'with fixnum and string values translates to PArrayType[PLiteralType]' do - calculator.infer([1,'two']).element_type.class.should == Puppet::Pops::Types::PLiteralType + it 'with fixnum and string values translates to PArrayType[PScalarType]' do + calculator.infer([1,'two']).element_type.class.should == Puppet::Pops::Types::PScalarType end - it 'with float and string values translates to PArrayType[PLiteralType]' do - calculator.infer([1.0,'two']).element_type.class.should == Puppet::Pops::Types::PLiteralType + it 'with float and string values translates to PArrayType[PScalarType]' do + calculator.infer([1.0,'two']).element_type.class.should == Puppet::Pops::Types::PScalarType end - it 'with fixnum, float, and string values translates to PArrayType[PLiteralType]' do - calculator.infer([1, 2.0,'two']).element_type.class.should == Puppet::Pops::Types::PLiteralType + it 'with fixnum, float, and string values translates to PArrayType[PScalarType]' do + calculator.infer([1, 2.0,'two']).element_type.class.should == Puppet::Pops::Types::PScalarType end - it 'with fixnum and regexp values translates to PArrayType[PLiteralType]' do - calculator.infer([1, /two/]).element_type.class.should == Puppet::Pops::Types::PLiteralType + it 'with fixnum and regexp values translates to PArrayType[PScalarType]' do + calculator.infer([1, /two/]).element_type.class.should == Puppet::Pops::Types::PScalarType end - it 'with string and regexp values translates to PArrayType[PLiteralType]' do - calculator.infer(['one', /two/]).element_type.class.should == Puppet::Pops::Types::PLiteralType + it 'with string and regexp values translates to PArrayType[PScalarType]' do + calculator.infer(['one', /two/]).element_type.class.should == Puppet::Pops::Types::PScalarType end it 'with string and symbol values translates to PArrayType[PObjectType]' do @@ -260,13 +260,13 @@ describe 'The type calculator' do et.class.should == Puppet::Pops::Types::PStringType end - it 'with array of string values and array of fixnums translates to PArrayType[PArrayType[PLiteralType]]' do + it 'with array of string values and array of fixnums translates to PArrayType[PArrayType[PScalarType]]' do et = calculator.infer([['first' 'array'], [1,2]]) et.class.should == Puppet::Pops::Types::PArrayType et = et.element_type et.class.should == Puppet::Pops::Types::PArrayType et = et.element_type - et.class.should == Puppet::Pops::Types::PLiteralType + et.class.should == Puppet::Pops::Types::PScalarType end it 'with hashes of string values translates to PArrayType[PHashType[PStringType]]' do @@ -278,13 +278,13 @@ describe 'The type calculator' do et.class.should == Puppet::Pops::Types::PStringType end - it 'with hash of string values and hash of fixnums translates to PArrayType[PHashType[PLiteralType]]' do + it 'with hash of string values and hash of fixnums translates to PArrayType[PHashType[PScalarType]]' do et = calculator.infer([{:first => 'first', :second => 'second' }, {:first => 1, :second => 2 }]) et.class.should == Puppet::Pops::Types::PArrayType et = et.element_type et.class.should == Puppet::Pops::Types::PHashType et = et.element_type - et.class.should == Puppet::Pops::Types::PLiteralType + et.class.should == Puppet::Pops::Types::PScalarType end end @@ -430,12 +430,12 @@ describe 'The type calculator' do end context "for Data, such that" do - it 'all literals + array and hash are assignable to Data' do + it 'all scalars + array and hash are assignable to Data' do t = Puppet::Pops::Types::PDataType.new() data_compatible_types.each { |t2| type_from_class(t2).should be_assignable_to(t) } end - it 'a Variant of literal, hash, or array is assignable to Data' do + it 'a Variant of scalar, hash, or array is assignable to Data' do t = Puppet::Pops::Types::PDataType.new() data_compatible_types.each { |t2| variant_t(type_from_class(t2)).should be_assignable_to(t) } end @@ -453,27 +453,27 @@ describe 'The type calculator' do end it 'Data is not assignable to any disjunct type' do - tested_types = all_types - [Puppet::Pops::Types::PObjectType, Puppet::Pops::Types::PDataType] - literal_types + tested_types = all_types - [Puppet::Pops::Types::PObjectType, Puppet::Pops::Types::PDataType] - scalar_types t = Puppet::Pops::Types::PDataType.new() tested_types.each {|t2| t.should_not be_assignable_to(t2.new) } end end - context "for Literal, such that" do - it "all literals are assignable to Literal" do - t = Puppet::Pops::Types::PLiteralType.new() - literal_types.each {|t2| t2.new.should be_assignable_to(t) } + context "for Scalar, such that" do + it "all scalars are assignable to Scalar" do + t = Puppet::Pops::Types::PScalarType.new() + scalar_types.each {|t2| t2.new.should be_assignable_to(t) } end - it 'Literal is not assignable to any of its subtypes' do - t = Puppet::Pops::Types::PLiteralType.new() - types_to_test = literal_types - [Puppet::Pops::Types::PLiteralType] + it 'Scalar is not assignable to any of its subtypes' do + t = Puppet::Pops::Types::PScalarType.new() + types_to_test = scalar_types - [Puppet::Pops::Types::PScalarType] types_to_test.each {|t2| t.should_not be_assignable_to(t2.new) } end - it 'Literal is not assignable to any disjunct type' do - tested_types = all_types - [Puppet::Pops::Types::PObjectType, Puppet::Pops::Types::PDataType] - literal_types - t = Puppet::Pops::Types::PLiteralType.new() + it 'Scalar is not assignable to any disjunct type' do + tested_types = all_types - [Puppet::Pops::Types::PObjectType, Puppet::Pops::Types::PDataType] - scalar_types + t = Puppet::Pops::Types::PScalarType.new() tested_types.each {|t2| t.should_not be_assignable_to(t2.new) } end end @@ -494,7 +494,7 @@ describe 'The type calculator' do tested_types = all_types - [ Puppet::Pops::Types::PObjectType, Puppet::Pops::Types::PDataType, - Puppet::Pops::Types::PLiteralType, + Puppet::Pops::Types::PScalarType, ] - numeric_types t = Puppet::Pops::Types::PNumericType.new() tested_types.each {|t2| t.should_not be_assignable_to(t2.new) } @@ -915,10 +915,10 @@ describe 'The type calculator' do t.element_type.class.should == Puppet::Pops::Types::PDataType end - it 'should yield \'PHashType[PLiteralType,PDataType]\' for Hash' do + it 'should yield \'PHashType[PScalarType,PDataType]\' for Hash' do t = calculator.type(Hash) t.class.should == Puppet::Pops::Types::PHashType - t.key_type.class.should == Puppet::Pops::Types::PLiteralType + t.key_type.class.should == Puppet::Pops::Types::PScalarType t.element_type.class.should == Puppet::Pops::Types::PDataType end end @@ -932,8 +932,8 @@ describe 'The type calculator' do calculator.string(Puppet::Pops::Types::PObjectType.new()).should == 'Object' end - it 'should yield \'Literal\' for PLiteralType' do - calculator.string(Puppet::Pops::Types::PLiteralType.new()).should == 'Literal' + it 'should yield \'Scalar\' for PScalarType' do + calculator.string(Puppet::Pops::Types::PScalarType.new()).should == 'Scalar' end it 'should yield \'Boolean\' for PBooleanType' do @@ -1096,7 +1096,7 @@ describe 'The type calculator' do ptype = Puppet::Pops::Types::PType calculator.infer(Puppet::Pops::Types::PNilType.new() ).is_a?(ptype).should() == true calculator.infer(Puppet::Pops::Types::PDataType.new() ).is_a?(ptype).should() == true - calculator.infer(Puppet::Pops::Types::PLiteralType.new() ).is_a?(ptype).should() == true + calculator.infer(Puppet::Pops::Types::PScalarType.new() ).is_a?(ptype).should() == true calculator.infer(Puppet::Pops::Types::PStringType.new() ).is_a?(ptype).should() == true calculator.infer(Puppet::Pops::Types::PNumericType.new() ).is_a?(ptype).should() == true calculator.infer(Puppet::Pops::Types::PIntegerType.new() ).is_a?(ptype).should() == true @@ -1118,7 +1118,7 @@ describe 'The type calculator' do ptype = Puppet::Pops::Types::PType calculator.string(calculator.infer(Puppet::Pops::Types::PNilType.new() )).should == "Type[Undef]" calculator.string(calculator.infer(Puppet::Pops::Types::PDataType.new() )).should == "Type[Data]" - calculator.string(calculator.infer(Puppet::Pops::Types::PLiteralType.new() )).should == "Type[Literal]" + calculator.string(calculator.infer(Puppet::Pops::Types::PScalarType.new() )).should == "Type[Scalar]" calculator.string(calculator.infer(Puppet::Pops::Types::PStringType.new() )).should == "Type[String]" calculator.string(calculator.infer(Puppet::Pops::Types::PNumericType.new() )).should == "Type[Numeric]" calculator.string(calculator.infer(Puppet::Pops::Types::PIntegerType.new() )).should == "Type[Integer]" @@ -1140,7 +1140,7 @@ describe 'The type calculator' do int_t = Puppet::Pops::Types::PIntegerType.new() string_t = Puppet::Pops::Types::PStringType.new() calculator.string(calculator.infer([int_t])).should == "Array[Type[Integer], 1, 1]" - calculator.string(calculator.infer([int_t, string_t])).should == "Array[Type[Literal], 2, 2]" + calculator.string(calculator.infer([int_t, string_t])).should == "Array[Type[Scalar], 2, 2]" end it 'should infer PType as the type of ruby classes' do @@ -1235,7 +1235,7 @@ describe 'The type calculator' do it "does not reduce by combining types when using infer_set" do element_type = calculator.infer(['a','b',1,2]).element_type - element_type.class.should == Puppet::Pops::Types::PLiteralType + element_type.class.should == Puppet::Pops::Types::PScalarType element_type = calculator.infer_set(['a','b',1,2]).element_type element_type.class.should == Puppet::Pops::Types::PVariantType element_type.types[0].class.should == Puppet::Pops::Types::PStringType diff --git a/spec/unit/pops/types/type_factory_spec.rb b/spec/unit/pops/types/type_factory_spec.rb index 5ace7e771..16db10880 100644 --- a/spec/unit/pops/types/type_factory_spec.rb +++ b/spec/unit/pops/types/type_factory_spec.rb @@ -35,8 +35,8 @@ describe 'The type factory' do Puppet::Pops::Types::TypeFactory.variant().class().should == Puppet::Pops::Types::PVariantType end - it 'literal() returns PLiteralType' do - Puppet::Pops::Types::TypeFactory.literal().class().should == Puppet::Pops::Types::PLiteralType + it 'scalar() returns PScalarType' do + Puppet::Pops::Types::TypeFactory.scalar().class().should == Puppet::Pops::Types::PScalarType end it 'data() returns PDataType' do @@ -129,10 +129,10 @@ describe 'The type factory' do at.element_type.class.should == Puppet::Pops::Types::PDataType end - it 'hash_of_data returns PHashType[PLiteralType,PDataType]' do + it 'hash_of_data returns PHashType[PScalarType,PDataType]' do ht = Puppet::Pops::Types::TypeFactory.hash_of_data ht.class().should == Puppet::Pops::Types::PHashType - ht.key_type.class.should == Puppet::Pops::Types::PLiteralType + ht.key_type.class.should == Puppet::Pops::Types::PScalarType ht.element_type.class.should == Puppet::Pops::Types::PDataType end diff --git a/spec/unit/pops/types/type_parser_spec.rb b/spec/unit/pops/types/type_parser_spec.rb index 691d7ff81..247d3701d 100644 --- a/spec/unit/pops/types/type_parser_spec.rb +++ b/spec/unit/pops/types/type_parser_spec.rb @@ -30,7 +30,7 @@ describe Puppet::Pops::Types::TypeParser do end [ - 'Object', 'Data', 'CatalogEntry', 'Boolean', 'Literal', 'Undef', 'Numeric', + 'Object', 'Data', 'CatalogEntry', 'Boolean', 'Scalar', 'Undef', 'Numeric', ].each do |name| it "does not support parameterizing unparameterized type <#{name}>" do expect { parser.parse("#{name}[Integer]") }.to raise_unparameterized_error_for(name) @@ -53,11 +53,11 @@ describe Puppet::Pops::Types::TypeParser do expect(parser.parse("Array")).to be_the_type(types.array_of_data) end - it "interprets an unparameterized Hash as a Hash of Literal to Data" do + it "interprets an unparameterized Hash as a Hash of Scalar to Data" do expect(parser.parse("Hash")).to be_the_type(types.hash_of_data) end - it "interprets a parameterized Hash[t] as a Hash of Literal to t" do + it "interprets a parameterized Hash[t] as a Hash of Scalar to t" do expect(parser.parse("Hash[Integer]")).to be_the_type(types.hash_of(types.integer)) end From f8ceaa0b4dfae377bd038e34a0603d6962ca39ab Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Tue, 4 Feb 2014 09:00:40 -0800 Subject: [PATCH 537/800] (Maint) Remove documentation rspec output The -fd flag makes the spec output very verbose, but doesn't really provide any benefit for us. It does, however, cause builds to take a significant amount of extra space. --- tasks/ci.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks/ci.rake b/tasks/ci.rake index ea4b95383..716e1a83e 100644 --- a/tasks/ci.rake +++ b/tasks/ci.rake @@ -4,7 +4,7 @@ require 'time' namespace "ci" do task :spec do ENV["LOG_SPEC_ORDER"] = "true" - sh %{rspec -r yarjuf -f JUnit -o result.xml -fd spec} + sh %{rspec -r yarjuf -f JUnit -o result.xml spec} end desc "Tar up the acceptance/ directory so that package test runs have tests to run against." From a690fefdfd00f65e8f26654e49207553e6ce66f1 Mon Sep 17 00:00:00 2001 From: Daniele Sluijters Date: Sun, 19 Jan 2014 13:20:29 +0100 Subject: [PATCH 538/800] (PUP-1470) Make mk_resource_method with false. Previously the method generated by mk_resource_methods create getters that would convert any "falsey" values to `:absent`. This has the unfortunate effect of making `false` turn into `:absent`, rendering these generated methods difficult to use for boolean parameters. This change allows boolean values to be preserved. It technically changes the behavior of the method in an incompatible manner, but the correctness of the new behavior was deemed to outweigh any problems. --- lib/puppet/provider.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/puppet/provider.rb b/lib/puppet/provider.rb index d88eebea3..6d90f91da 100644 --- a/lib/puppet/provider.rb +++ b/lib/puppet/provider.rb @@ -419,7 +419,11 @@ class Puppet::Provider attr = attr.intern next if attr == :name define_method(attr) do - @property_hash[attr] || :absent + if @property_hash[attr].nil? + :absent + else + @property_hash[attr] + end end define_method(attr.to_s + "=") do |val| From 8384c500a9a2d0a1ba68c11a126fc7844f1a5a9f Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Tue, 4 Feb 2014 09:58:23 -0800 Subject: [PATCH 539/800] (PUP-1470) Add test for new behavior This adds a test for the new (correct) behavior for `false` values on generated property/parameter methods. It also cleans up the tests to be much more straightforward. --- spec/unit/provider_spec.rb | 62 ++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/spec/unit/provider_spec.rb b/spec/unit/provider_spec.rb index 2435895a9..56987a91b 100755 --- a/spec/unit/provider_spec.rb +++ b/spec/unit/provider_spec.rb @@ -440,45 +440,43 @@ describe Puppet::Provider do context "mk_resource_methods" do before :each do - type.newproperty(:prop1) - type.newproperty(:prop2) - type.newparam(:param1) - type.newparam(:param2) + type.newproperty(:prop) + type.newparam(:param) + provider.mk_resource_methods end - fields = %w{prop1 prop2 param1 param2} + let(:instance) { provider.new(nil) } - fields.each do |name| - it "should add getter methods for #{name}" do - expect { subject.mk_resource_methods }. - to change { subject.method_defined?(name) }. - from(false).to(true) - end - - it "should add setter methods for #{name}" do - method = name + '=' - expect { subject.mk_resource_methods }. - to change { subject.method_defined?(name) }. - from(false).to(true) - end + it "defaults to :absent" do + expect(instance.prop).to eq(:absent) + expect(instance.param).to eq(:absent) end - context "with an instance" do - subject { provider.mk_resource_methods; provider.new(nil) } + it "should update when set" do + instance.prop = 'hello' + instance.param = 'goodbye' - fields.each do |name| - context name do - it "should default to :absent" do - subject.send(name).should == :absent - end + expect(instance.prop).to eq('hello') + expect(instance.param).to eq('goodbye') + end - it "should update when set" do - expect { subject.send(name + '=', "hello") }. - to change { subject.send(name) }. - from(:absent).to("hello") - end - end - end + it "treats nil the same as absent" do + instance.prop = "value" + instance.param = "value" + + instance.prop = nil + instance.param = nil + + expect(instance.prop).to eq(:absent) + expect(instance.param).to eq(:absent) + end + + it "preserves false as false" do + instance.prop = false + instance.param = false + + expect(instance.prop).to eq(false) + expect(instance.param).to eq(false) end end From 370d5bcf44af76476dd9dd197b3dc884dc2e1567 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Fri, 31 Jan 2014 16:04:26 -0800 Subject: [PATCH 540/800] (PUP-1551) Rename environmentdir acceptance test to environmentpath And repair some whitespace damage, and a doc typo. --- ...> use_environment_from_environmentpath.rb} | 26 +++++++++---------- lib/puppet/defaults.rb | 2 +- lib/puppet/environments.rb | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) rename acceptance/tests/environment/{use_environment_from_environmentdir.rb => use_environment_from_environmentpath.rb} (91%) diff --git a/acceptance/tests/environment/use_environment_from_environmentdir.rb b/acceptance/tests/environment/use_environment_from_environmentpath.rb similarity index 91% rename from acceptance/tests/environment/use_environment_from_environmentdir.rb rename to acceptance/tests/environment/use_environment_from_environmentpath.rb index a4a703503..432932741 100644 --- a/acceptance/tests/environment/use_environment_from_environmentdir.rb +++ b/acceptance/tests/environment/use_environment_from_environmentpath.rb @@ -30,7 +30,7 @@ def generate_module_content(module_name, options = {}) "#{path_to_module}/#{module_name}/manifests/init.pp": ensure => file, content => 'class #{module_name} { - notify { template-#{module_name}: message => template("#{module_name}/our_template.erb") } + notify { "template-#{module_name}": message => template("#{module_name}/our_template.erb") } file { "$agent_file_location/file-#{module_info}": source => "puppet:///modules/#{module_name}/data" } }' ; @@ -50,7 +50,7 @@ def generate_module_content(module_name, options = {}) end def generate_site_manifest(path_to_manifest, *modules_to_include) - manifest_content = <<-EOS + manifest_content = <<-EOS "#{path_to_manifest}/site.pp": ensure => file, content => "#{modules_to_include.map { |m| "include #{m}" }.join("\n")}" @@ -74,21 +74,21 @@ file { #{generate_environment("#{testdir}/base", "onlybase")} #{generate_environment("#{testdir}/additional", "shadowed")} -#{generate_module_content("atmp", - :base_path => testdir, - :env_path => 'base', +#{generate_module_content("atmp", + :base_path => testdir, + :env_path => 'base', :environment => 'shadowed')} #{generate_site_manifest("#{testdir}/base/shadowed/manifests", "atmp", "globalmod")} #{generate_module_content("atmp", - :base_path => testdir, - :env_path => 'base', + :base_path => testdir, + :env_path => 'base', :environment => 'onlybase')} #{generate_site_manifest("#{testdir}/base/onlybase/manifests", "atmp", "globalmod")} #{generate_module_content("atmp", - :base_path => testdir, - :env_path => 'additional', + :base_path => testdir, + :env_path => 'additional', :environment => 'shadowed')} #{generate_site_manifest("#{testdir}/additional/shadowed/manifests", "atmp", "globalmod")} @@ -101,7 +101,7 @@ def run_with_environment(agent, environment, options = {}) expected_exit_code = options[:expected_exit_code] || 2 expected_strings = options[:expected_strings] - step "running an agent in environment '#{environment}'" + step "running an agent in environment '#{environment}'" atmp = agent.tmpdir("use_environmentpath_#{environment}") agent_config = [ @@ -113,8 +113,8 @@ def run_with_environment(agent, environment, options = {}) 'ENV' => { "FACTER_agent_file_location" => atmp }, } - on(agent, - puppet("agent", *agent_config), + on(agent, + puppet("agent", *agent_config), :acceptable_exit_codes => [expected_exit_code]) do |result| yield atmp, result @@ -135,7 +135,7 @@ with_puppet_running_on master, master_opts, testdir do run_with_environment(agent, "shadowed") do |tmpdir,catalog_result| ["module-atmp-from-shadowed", "module-globalmod"].each do |expected| assert_match(/environment fact from #{expected}/, catalog_result.stdout) - end + end ["module-atmp-from-shadowed", "module-globalmod"].each do |expected| on agent, "cat #{tmpdir}/file-#{expected}" do |file_result| diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index 41e79881a..195a06fc8 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -198,7 +198,7 @@ module Puppet }, :environmentpath => { :default => "$confdir/environments", - :desc => "A path of environment directoriess", + :desc => "A path of environment directories", :type => :path, }, :diff_args => { diff --git a/lib/puppet/environments.rb b/lib/puppet/environments.rb index 7ce9637b1..dd8fa9341 100644 --- a/lib/puppet/environments.rb +++ b/lib/puppet/environments.rb @@ -114,7 +114,7 @@ module Puppet::Environments end # Generate an array of directory loaders from a path string. - # @param path [String] path to environment directories + # @param path [String] path to environment directories # @param global_module_path [String] the global modulepath setting # @return [Array] An array # of configured directory loaders. From e17268b8a39b28052ca8a325bd04e3494142a061 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Tue, 4 Feb 2014 15:24:56 -0800 Subject: [PATCH 541/800] (PUP-1551) Use environmentpath in can_enumerate_environments test Forgot to update the puppet.conf environmentdir setting to environmentpath. Also change assert_not_match to assert_no_match call in environmentpath test, as the later is the correct call. Not entirely certain why this wasn't failing before. --- acceptance/tests/environment/can_enumerate_environments.rb | 2 +- .../tests/environment/use_environment_from_environmentpath.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/acceptance/tests/environment/can_enumerate_environments.rb b/acceptance/tests/environment/can_enumerate_environments.rb index 307e8cbb2..e09718f52 100644 --- a/acceptance/tests/environment/can_enumerate_environments.rb +++ b/acceptance/tests/environment/can_enumerate_environments.rb @@ -42,7 +42,7 @@ MANIFEST with_puppet_running_on(master, { :master => { - :environmentdir => environments_dir + :environmentpath => environments_dir } }) do agents.each do |agent| diff --git a/acceptance/tests/environment/use_environment_from_environmentpath.rb b/acceptance/tests/environment/use_environment_from_environmentpath.rb index 432932741..2a1c3db45 100644 --- a/acceptance/tests/environment/use_environment_from_environmentpath.rb +++ b/acceptance/tests/environment/use_environment_from_environmentpath.rb @@ -157,7 +157,7 @@ with_puppet_running_on master, master_opts, testdir do end run_with_environment(agent, nil, :expected_exit_code => 0) do |tmpdir, result| - assert_not_match(/module-atmp/, result.stdout) + assert_no_match(/module-atmp/, result.stdout, "module-atmp was included despite no environment being loaded") assert_match(/Loading facts.*globalmod/, result.stdout) end end From ae242995ce3021680740c65dfe1adb52aaaf0676 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Tue, 4 Feb 2014 16:19:40 -0800 Subject: [PATCH 542/800] (PUP-753) Benchmark puppet compiling w/many modules This sets up a scenario in which we are benchmarking the puppet compiler with a large number of modules, but very simple manifests. The generated modules have 2 manifests each an public and an internal. The site.pp for the environment simply includes all of the modules' public classes. The public classes in turn include their own internal class. The internal class then places a single "notify" resource in the catalog. This also ends up laying down a general structure for benchmark scenarios. Each scenario has support files under `bencmarks/` and has a rake namespace under the `benchmark` namespace. Each namespace should have at least tasks for `generate` and `run`. --- benchmarks/many_modules/module/init.pp.erb | 3 + .../many_modules/module/internal.pp.erb | 3 + benchmarks/many_modules/puppet.conf.erb | 3 + benchmarks/many_modules/site.pp.erb | 3 + tasks/benchmark.rake | 87 +++++++++++++++++++ 5 files changed, 99 insertions(+) create mode 100644 benchmarks/many_modules/module/init.pp.erb create mode 100644 benchmarks/many_modules/module/internal.pp.erb create mode 100644 benchmarks/many_modules/puppet.conf.erb create mode 100644 benchmarks/many_modules/site.pp.erb create mode 100644 tasks/benchmark.rake diff --git a/benchmarks/many_modules/module/init.pp.erb b/benchmarks/many_modules/module/init.pp.erb new file mode 100644 index 000000000..49fca07ba --- /dev/null +++ b/benchmarks/many_modules/module/init.pp.erb @@ -0,0 +1,3 @@ +class <%= name %> { + class { "<%= name %>::internal": } +} diff --git a/benchmarks/many_modules/module/internal.pp.erb b/benchmarks/many_modules/module/internal.pp.erb new file mode 100644 index 000000000..83a36947d --- /dev/null +++ b/benchmarks/many_modules/module/internal.pp.erb @@ -0,0 +1,3 @@ +class <%= name %>::internal { + notify { "<%= name %>::internal": } +} diff --git a/benchmarks/many_modules/puppet.conf.erb b/benchmarks/many_modules/puppet.conf.erb new file mode 100644 index 000000000..e0c5d8588 --- /dev/null +++ b/benchmarks/many_modules/puppet.conf.erb @@ -0,0 +1,3 @@ +confdir = <%= location %> +vardir = <%= location %> +environmentpath = <%= File.join(location, 'environments') %> diff --git a/benchmarks/many_modules/site.pp.erb b/benchmarks/many_modules/site.pp.erb new file mode 100644 index 000000000..9f6c9668a --- /dev/null +++ b/benchmarks/many_modules/site.pp.erb @@ -0,0 +1,3 @@ +<% size.times do |i| %> + include module<%= i %> +<% end %> diff --git a/tasks/benchmark.rake b/tasks/benchmark.rake new file mode 100644 index 000000000..cbb8bcd0f --- /dev/null +++ b/tasks/benchmark.rake @@ -0,0 +1,87 @@ +require 'benchmark' +require 'tmpdir' +require 'erb' +require 'ostruct' +require 'open3' + +desc "Execute all puppet benchmarks." +task :benchmark => ["benchmark:many_modules"] + +namespace :benchmark do + desc "Benchmark scenario: many manifests spread across many modules. +Benchmark target: catalog compilation." + task :many_modules => "many_modules:run" + + namespace :many_modules do + task :setup do + ENV['SIZE'] ||= '100' + ENV['TARGET'] ||= Dir.mktmpdir("many_modules") + ENV['TARGET'] = File.expand_path(ENV['TARGET']) + + mkdir_p(ENV['TARGET']) + end + + desc "Generate the scenario" + task :generate => :setup do + size = ENV['SIZE'].to_i + environment = File.join(ENV['TARGET'], 'environments', 'benchmarking') + templates = File.join('benchmarks', 'many_modules') + + mkdir_p(File.join(environment, 'modules')) + mkdir_p(File.join(environment, 'manifests')) + + render(File.join(templates, 'site.pp.erb'), + File.join(environment, 'manifests', 'site.pp'), + :size => size) + + size.times do |i| + module_name = "module#{i}" + manifests = File.join(environment, 'modules', module_name, 'manifests') + + mkdir_p(manifests) + + render(File.join(templates, 'module', 'init.pp.erb'), + File.join(manifests, 'init.pp'), + :name => module_name) + + render(File.join(templates, 'module', 'internal.pp.erb'), + File.join(manifests, 'internal.pp'), + :name => module_name) + end + + render(File.join(templates, 'puppet.conf.erb'), + File.join(ENV['TARGET'], 'puppet.conf'), + :location => ENV['TARGET']) + end + + task :run => :generate do + require 'puppet' + include Benchmark + + config = File.join(ENV['TARGET'], 'puppet.conf') + Puppet.initialize_settings(['--config', config]) + + Benchmark.benchmark(CAPTION, 10, FORMAT, "> total:", "> avg:") do |b| + times = [] + 10.times do |i| + times << b.report("Run #{i + 1}") do + env = Puppet.lookup(:environments).get('benchmarking') + node = Puppet::Node.new("testing", :environment => env) + Puppet::Resource::Catalog.indirection.find("testing", :use_node => node) + end + end + + sum = times.inject(Benchmark::Tms.new, &:+) + + [sum, sum / times.length] + end + end + end + + def render(erb_file, output_file, bindings) + site = ERB.new(File.read(erb_file)) + File.open(output_file, 'w') do |fh| + fh.write(site.result(OpenStruct.new(bindings).instance_eval { binding })) + end + end +end From f1d66838c5f417bd774e565898e859154d86562c Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Tue, 4 Feb 2014 17:19:58 -0800 Subject: [PATCH 543/800] (PUP-753) Profile the many_modules benchmark scenario This adds a new rake task "benchmark:many_modules:profile" that will run a single case of the many_modules benchmark and profile it using ruby-prof. The profile data is written to a file in the PWD that can then be viewed using kcachegrind (or qcachegrind on OSX). --- Gemfile | 3 +++ tasks/benchmark.rake | 27 ++++++++++++++++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 94120da85..4fb5fe2ca 100644 --- a/Gemfile +++ b/Gemfile @@ -45,6 +45,9 @@ group(:development, :test) do gem "json-schema", "2.1.1", :require => false end + platforms :ruby_19, :ruby_20 do + gem 'ruby-prof' + end end group(:extra) do diff --git a/tasks/benchmark.rake b/tasks/benchmark.rake index cbb8bcd0f..d95cd552d 100644 --- a/tasks/benchmark.rake +++ b/tasks/benchmark.rake @@ -65,9 +65,7 @@ Benchmark target: catalog compilation." times = [] 10.times do |i| times << b.report("Run #{i + 1}") do - env = Puppet.lookup(:environments).get('benchmarking') - node = Puppet::Node.new("testing", :environment => env) - Puppet::Resource::Catalog.indirection.find("testing", :use_node => node) + run end end @@ -76,6 +74,29 @@ Benchmark target: catalog compilation." [sum, sum / times.length] end end + + task :profile => :generate do + require 'puppet' + require 'ruby-prof' + + RubyProf.start + config = File.join(ENV['TARGET'], 'puppet.conf') + Puppet.initialize_settings(['--config', config]) + run + result = RubyProf.stop + + printer = RubyProf::CallTreePrinter.new(result) + File.open(File.join("callgrind.many_modules.#{Time.now.to_i}.trace"), "w") do |f| + printer.print(f) + end + + end + + def run + env = Puppet.lookup(:environments).get('benchmarking') + node = Puppet::Node.new("testing", :environment => env) + Puppet::Resource::Catalog.indirection.find("testing", :use_node => node) + end end def render(erb_file, output_file, bindings) From 90351dce4b24e48ef813938c26fe3704a72c9d1b Mon Sep 17 00:00:00 2001 From: Adrien Thebo Date: Tue, 4 Feb 2014 17:24:54 -0800 Subject: [PATCH 544/800] (maint) Pass Facter.search arrays instead of strings The implementation of Facter.search expects `*args` while Puppet was defaulting to a colon separated string. This isn't a valid path but until Facter commit 6c6c394 warnings were almost always squelched which prevented this from being exposed. This commit ensures that we split up our (semi)colon delimited paths into an array and passes them to Facter in the manner that it expects. --- lib/puppet/defaults.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index 195a06fc8..4fd36278e 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -1474,7 +1474,13 @@ EOT separator is ':', and the Windows path separator is ';'.)", :call_hook => :on_initialize_and_write, # Call our hook with the default value, so we always get the value added to facter. - :hook => proc { |value| Facter.search(value) if Facter.respond_to?(:search) }} + :hook => proc do |value| + if Facter.respond_to?(:search) + paths = value.split(File::PATH_SEPARATOR) + Facter.search(*paths) + end + end + } ) From 85c0894562f9d6addf44c71daf8e0b83e9502902 Mon Sep 17 00:00:00 2001 From: Adrien Thebo Date: Tue, 4 Feb 2014 17:31:48 -0800 Subject: [PATCH 545/800] (maint) Don't check for Facter.search Facter has defined `Facter.search` since Facter 1.5, which was released in 2008. Since we depend on Facter >= 1.6.2 we can assume that method will be present and directly call it. --- lib/puppet/defaults.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index 4fd36278e..8ee73d27b 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -1475,10 +1475,8 @@ EOT :call_hook => :on_initialize_and_write, # Call our hook with the default value, so we always get the value added to facter. :hook => proc do |value| - if Facter.respond_to?(:search) - paths = value.split(File::PATH_SEPARATOR) - Facter.search(*paths) - end + paths = value.split(File::PATH_SEPARATOR) + Facter.search(*paths) end } ) From 61cffab3be4a8069d17b2c6053df036b07be92ae Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Tue, 4 Feb 2014 17:41:39 -0800 Subject: [PATCH 546/800] (PUP-753) Extract benchmark impl into file The code for setting up and running the benchmark is very specific to this particular scenario and also too large to keep in a rake file. This extracts it out into a file that lives inside the scenario. This should turn into a common interface that all benchmark scenarios can implement and should allow the maintenance of scenarios to remain separated and the creation of new ones to be vastly simplified. --- benchmarks/many_modules/benchmark.rb | 62 +++++++++++++++++++++++++ tasks/benchmark.rake | 68 +++++----------------------- 2 files changed, 74 insertions(+), 56 deletions(-) create mode 100644 benchmarks/many_modules/benchmark.rb diff --git a/benchmarks/many_modules/benchmark.rb b/benchmarks/many_modules/benchmark.rb new file mode 100644 index 000000000..fd60c4a01 --- /dev/null +++ b/benchmarks/many_modules/benchmark.rb @@ -0,0 +1,62 @@ +require 'erb' +require 'ostruct' +require 'fileutils' + +class ManyModules + include FileUtils + + def initialize(target, size) + @target = target + @size = size + end + + def setup + require 'puppet' + config = File.join(@target, 'puppet.conf') + Puppet.initialize_settings(['--config', config]) + end + + def run + env = Puppet.lookup(:environments).get('benchmarking') + node = Puppet::Node.new("testing", :environment => env) + Puppet::Resource::Catalog.indirection.find("testing", :use_node => node) + end + + def generate + environment = File.join(@target, 'environments', 'benchmarking') + templates = File.join('benchmarks', 'many_modules') + + mkdir_p(File.join(environment, 'modules')) + mkdir_p(File.join(environment, 'manifests')) + + render(File.join(templates, 'site.pp.erb'), + File.join(environment, 'manifests', 'site.pp'), + :size => @size) + + @size.times do |i| + module_name = "module#{i}" + manifests = File.join(environment, 'modules', module_name, 'manifests') + + mkdir_p(manifests) + + render(File.join(templates, 'module', 'init.pp.erb'), + File.join(manifests, 'init.pp'), + :name => module_name) + + render(File.join(templates, 'module', 'internal.pp.erb'), + File.join(manifests, 'internal.pp'), + :name => module_name) + end + + render(File.join(templates, 'puppet.conf.erb'), + File.join(@target, 'puppet.conf'), + :location => @target) + end + + def render(erb_file, output_file, bindings) + site = ERB.new(File.read(erb_file)) + File.open(output_file, 'w') do |fh| + fh.write(site.result(OpenStruct.new(bindings).instance_eval { binding })) + end + end +end diff --git a/tasks/benchmark.rake b/tasks/benchmark.rake index d95cd552d..00569c23f 100644 --- a/tasks/benchmark.rake +++ b/tasks/benchmark.rake @@ -19,53 +19,24 @@ Benchmark target: catalog compilation." ENV['TARGET'] = File.expand_path(ENV['TARGET']) mkdir_p(ENV['TARGET']) + + require File.expand_path(File.join('benchmarks', 'many_modules', 'benchmark.rb')) + + @benchmark = ManyModules.new(ENV['TARGET'], ENV['SIZE'].to_i) end desc "Generate the scenario" task :generate => :setup do - size = ENV['SIZE'].to_i - environment = File.join(ENV['TARGET'], 'environments', 'benchmarking') - templates = File.join('benchmarks', 'many_modules') - - mkdir_p(File.join(environment, 'modules')) - mkdir_p(File.join(environment, 'manifests')) - - render(File.join(templates, 'site.pp.erb'), - File.join(environment, 'manifests', 'site.pp'), - :size => size) - - size.times do |i| - module_name = "module#{i}" - manifests = File.join(environment, 'modules', module_name, 'manifests') - - mkdir_p(manifests) - - render(File.join(templates, 'module', 'init.pp.erb'), - File.join(manifests, 'init.pp'), - :name => module_name) - - render(File.join(templates, 'module', 'internal.pp.erb'), - File.join(manifests, 'internal.pp'), - :name => module_name) - end - - render(File.join(templates, 'puppet.conf.erb'), - File.join(ENV['TARGET'], 'puppet.conf'), - :location => ENV['TARGET']) + @benchmark.generate end task :run => :generate do - require 'puppet' - include Benchmark - - config = File.join(ENV['TARGET'], 'puppet.conf') - Puppet.initialize_settings(['--config', config]) - - Benchmark.benchmark(CAPTION, 10, FORMAT, "> total:", "> avg:") do |b| + @benchmark.setup + Benchmark.benchmark(Benchmark::CAPTION, 10, Benchmark::FORMAT, "> total:", "> avg:") do |b| times = [] 10.times do |i| times << b.report("Run #{i + 1}") do - run + @benchmark.run end end @@ -76,14 +47,12 @@ Benchmark target: catalog compilation." end task :profile => :generate do - require 'puppet' require 'ruby-prof' - RubyProf.start - config = File.join(ENV['TARGET'], 'puppet.conf') - Puppet.initialize_settings(['--config', config]) - run - result = RubyProf.stop + @benchmark.setup + result = RubyProf.profile do + @benchmark.run + end printer = RubyProf::CallTreePrinter.new(result) File.open(File.join("callgrind.many_modules.#{Time.now.to_i}.trace"), "w") do |f| @@ -91,18 +60,5 @@ Benchmark target: catalog compilation." end end - - def run - env = Puppet.lookup(:environments).get('benchmarking') - node = Puppet::Node.new("testing", :environment => env) - Puppet::Resource::Catalog.indirection.find("testing", :use_node => node) - end - end - - def render(erb_file, output_file, bindings) - site = ERB.new(File.read(erb_file)) - File.open(output_file, 'w') do |fh| - fh.write(site.result(OpenStruct.new(bindings).instance_eval { binding })) - end end end From 2a6b1c7a70e72df4ad0acd987d8535b4b766f53b Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Tue, 4 Feb 2014 17:56:51 -0800 Subject: [PATCH 547/800] (PUP-753) Generate scenario tasks from filesystem Since the scenario execution is just defined by a ruby class that lives in the scenario directory, the scenario tasks can now be generated. This extracts the description of a scenario into a file (`description`) and generates a set of rake tasks for each scenario that is in the benchmarks directory. This also removes the top-level benchmark task because right now all scenarios will be using the same class name (Benchmarker), which would cause name collisions when there are multiple scenarios being run in the process. --- .../{benchmark.rb => benchmarker.rb} | 2 +- benchmarks/many_modules/description | 3 + tasks/benchmark.rake | 92 ++++++++++--------- 3 files changed, 52 insertions(+), 45 deletions(-) rename benchmarks/many_modules/{benchmark.rb => benchmarker.rb} (98%) create mode 100644 benchmarks/many_modules/description diff --git a/benchmarks/many_modules/benchmark.rb b/benchmarks/many_modules/benchmarker.rb similarity index 98% rename from benchmarks/many_modules/benchmark.rb rename to benchmarks/many_modules/benchmarker.rb index fd60c4a01..10a32b93a 100644 --- a/benchmarks/many_modules/benchmark.rb +++ b/benchmarks/many_modules/benchmarker.rb @@ -2,7 +2,7 @@ require 'erb' require 'ostruct' require 'fileutils' -class ManyModules +class Benchmarker include FileUtils def initialize(target, size) diff --git a/benchmarks/many_modules/description b/benchmarks/many_modules/description new file mode 100644 index 000000000..a2b58ebe7 --- /dev/null +++ b/benchmarks/many_modules/description @@ -0,0 +1,3 @@ +Benchmark scenario: many manifests spread across many modules. +Benchmark target: catalog compilation. + diff --git a/tasks/benchmark.rake b/tasks/benchmark.rake index 00569c23f..afee2766e 100644 --- a/tasks/benchmark.rake +++ b/tasks/benchmark.rake @@ -4,61 +4,65 @@ require 'erb' require 'ostruct' require 'open3' -desc "Execute all puppet benchmarks." -task :benchmark => ["benchmark:many_modules"] - namespace :benchmark do - desc "Benchmark scenario: many manifests spread across many modules. -Benchmark target: catalog compilation." - task :many_modules => "many_modules:run" + def generate_scenario_tasks(location, name) + desc File.read(File.join(location, 'description')) + task name => "#{name}:run" - namespace :many_modules do - task :setup do - ENV['SIZE'] ||= '100' - ENV['TARGET'] ||= Dir.mktmpdir("many_modules") - ENV['TARGET'] = File.expand_path(ENV['TARGET']) + namespace name do + task :setup do + ENV['ITERATIONS'] ||= '10' + ENV['SIZE'] ||= '100' + ENV['TARGET'] ||= Dir.mktmpdir(name) + ENV['TARGET'] = File.expand_path(ENV['TARGET']) - mkdir_p(ENV['TARGET']) + mkdir_p(ENV['TARGET']) - require File.expand_path(File.join('benchmarks', 'many_modules', 'benchmark.rb')) + require File.expand_path(File.join(location, 'benchmarker.rb')) - @benchmark = ManyModules.new(ENV['TARGET'], ENV['SIZE'].to_i) - end + @benchmark = Benchmarker.new(ENV['TARGET'], ENV['SIZE'].to_i) + end - desc "Generate the scenario" - task :generate => :setup do - @benchmark.generate - end + desc "Generate the #{name} scenario." + task :generate => :setup do + @benchmark.generate + end - task :run => :generate do - @benchmark.setup - Benchmark.benchmark(Benchmark::CAPTION, 10, Benchmark::FORMAT, "> total:", "> avg:") do |b| - times = [] - 10.times do |i| - times << b.report("Run #{i + 1}") do - @benchmark.run + desc "Run the #{name} scenario." + task :run => :generate do + @benchmark.setup + Benchmark.benchmark(Benchmark::CAPTION, 10, Benchmark::FORMAT, "> total:", "> avg:") do |b| + times = [] + ENV['ITERATIONS'].to_i.times do |i| + times << b.report("Run #{i + 1}") do + @benchmark.run + end end + + sum = times.inject(Benchmark::Tms.new, &:+) + + [sum, sum / times.length] + end + end + + desc "Profile a single run of the #{name} scenario." + task :profile => :generate do + require 'ruby-prof' + + @benchmark.setup + result = RubyProf.profile do + @benchmark.run end - sum = times.inject(Benchmark::Tms.new, &:+) - - [sum, sum / times.length] + printer = RubyProf::CallTreePrinter.new(result) + File.open(File.join("callgrind.#{name}.#{Time.now.to_i}.trace"), "w") do |f| + printer.print(f) + end end end - - task :profile => :generate do - require 'ruby-prof' - - @benchmark.setup - result = RubyProf.profile do - @benchmark.run - end - - printer = RubyProf::CallTreePrinter.new(result) - File.open(File.join("callgrind.many_modules.#{Time.now.to_i}.trace"), "w") do |f| - printer.print(f) - end - - end + end + + Dir.glob('benchmarks/*') do |location| + generate_scenario_tasks(location, File.basename(location)) end end From cac434a38969d32816b23e9eb9272cece45cbb0a Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Wed, 5 Feb 2014 11:30:27 -0800 Subject: [PATCH 548/800] (PUP-753) Include a minimal metadata in each module This includes a basic, minimal metadata in each of the generated modules. There is just enough information in the metadata.json file to cause the module to be accepted by puppet as a valid and usable module. --- benchmarks/many_modules/benchmarker.rb | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/benchmarks/many_modules/benchmarker.rb b/benchmarks/many_modules/benchmarker.rb index 10a32b93a..8540f85b1 100644 --- a/benchmarks/many_modules/benchmarker.rb +++ b/benchmarks/many_modules/benchmarker.rb @@ -1,6 +1,7 @@ require 'erb' require 'ostruct' require 'fileutils' +require 'json' class Benchmarker include FileUtils @@ -35,10 +36,24 @@ class Benchmarker @size.times do |i| module_name = "module#{i}" - manifests = File.join(environment, 'modules', module_name, 'manifests') + module_base = File.join(environment, 'modules', module_name) + manifests = File.join(module_base, 'manifests') mkdir_p(manifests) + File.open(File.join(module_base, 'metadata.json'), 'w') do |f| + JSON.dump({ + "types" => [], + "source" => "", + "author" => "ManyModules Benchmark", + "license" => "Apache 2.0", + "version" => "1.0.0", + "description" => "Many Modules benchmark module #{i}", + "summary" => "Just this benchmark module, you know?", + "dependencies" => [], + }, f) + end + render(File.join(templates, 'module', 'init.pp.erb'), File.join(manifests, 'init.pp'), :name => module_name) From 5b2f99c3be362ad56235f6319de303d6d6b6a58f Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Wed, 5 Feb 2014 11:55:35 -0800 Subject: [PATCH 549/800] (Maint) Remove unused dependencies There were a few require statements that got left behind when the benchmark implementation was moved from the rake tasks to its own file. There was also a vestige of an attempt at using Open3 for executing some commands. This removes those unneeded require statements. --- tasks/benchmark.rake | 3 --- 1 file changed, 3 deletions(-) diff --git a/tasks/benchmark.rake b/tasks/benchmark.rake index afee2766e..5c69f3c0f 100644 --- a/tasks/benchmark.rake +++ b/tasks/benchmark.rake @@ -1,8 +1,5 @@ require 'benchmark' require 'tmpdir' -require 'erb' -require 'ostruct' -require 'open3' namespace :benchmark do def generate_scenario_tasks(location, name) From b3c5ca8f0b55649d1332930592527b68c870f3aa Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Wed, 5 Feb 2014 12:30:09 -0800 Subject: [PATCH 550/800] (PUP-753) Use correct format constant on 1.8.7 The constant for the default format string changed between 1.8.7 and 1.9.3. This adds a check for the ruby version so that it can get the correct constant. --- tasks/benchmark.rake | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tasks/benchmark.rake b/tasks/benchmark.rake index 5c69f3c0f..78240c1d6 100644 --- a/tasks/benchmark.rake +++ b/tasks/benchmark.rake @@ -27,8 +27,13 @@ namespace :benchmark do desc "Run the #{name} scenario." task :run => :generate do + format = if RUBY_VERSION =~ /^1\.8/ + Benchmark::FMTSTR + else + Benchmark::FORMAT + end @benchmark.setup - Benchmark.benchmark(Benchmark::CAPTION, 10, Benchmark::FORMAT, "> total:", "> avg:") do |b| + Benchmark.benchmark(Benchmark::CAPTION, 10, format, "> total:", "> avg:") do |b| times = [] ENV['ITERATIONS'].to_i.times do |i| times << b.report("Run #{i + 1}") do From a36d369675746974a0c509a81b9520312b71920b Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Wed, 5 Feb 2014 12:36:30 -0800 Subject: [PATCH 551/800] (PUP-753) Remove duplicate calls to setup There were two different places calling setup for the benchmarker. This consolidates it down to one. Unfortunately, the setup method depends on the generate method having been run already and so it not analogous to the `setup` task. This meant that setup is actually called in the `generate` task. --- tasks/benchmark.rake | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tasks/benchmark.rake b/tasks/benchmark.rake index 78240c1d6..c59a3213a 100644 --- a/tasks/benchmark.rake +++ b/tasks/benchmark.rake @@ -23,6 +23,7 @@ namespace :benchmark do desc "Generate the #{name} scenario." task :generate => :setup do @benchmark.generate + @benchmark.setup end desc "Run the #{name} scenario." @@ -32,7 +33,6 @@ namespace :benchmark do else Benchmark::FORMAT end - @benchmark.setup Benchmark.benchmark(Benchmark::CAPTION, 10, format, "> total:", "> avg:") do |b| times = [] ENV['ITERATIONS'].to_i.times do |i| @@ -51,7 +51,6 @@ namespace :benchmark do task :profile => :generate do require 'ruby-prof' - @benchmark.setup result = RubyProf.profile do @benchmark.run end From 580a1ce7c21868448a52381d6344756bb51c0427 Mon Sep 17 00:00:00 2001 From: "Ethan J. Brown" Date: Mon, 13 Jan 2014 15:36:31 -0800 Subject: [PATCH 552/800] (PUP-1559) Ensure ADSI Group SIDs may be looked up - Previously, calling Puppet::Util::ADSI::Group.exists? for a well-known group SID would return false, because the WinNT:// style URI used to find the group would not examine / lookup the SID, but would instead create a broken Uri that the underlying OS could not understand - Now, the given group name is checked to see if it's a SID before the Uri is constructed, and a SID style Uri is created where appropriate Paired-with: Joshua Partlow --- lib/puppet/util/adsi.rb | 16 ++++++ spec/unit/provider/group/windows_adsi_spec.rb | 1 + spec/unit/provider/user/windows_adsi_spec.rb | 1 + spec/unit/util/adsi_spec.rb | 49 ++++++++++++++++++- 4 files changed, 66 insertions(+), 1 deletion(-) diff --git a/lib/puppet/util/adsi.rb b/lib/puppet/util/adsi.rb index 91debc756..4c30e18c2 100644 --- a/lib/puppet/util/adsi.rb +++ b/lib/puppet/util/adsi.rb @@ -41,6 +41,18 @@ module Puppet::Util::ADSI "winmgmts:{impersonationLevel=impersonate}!//#{host}/root/cimv2" end + # @api private + def sid_uri_safe(sid) + return sid_uri(sid) if sid.kind_of?(Win32::Security::SID) + + begin + sid = Win32::Security::SID.new(Win32::Security::SID.string_to_sid(sid)) + sid_uri(sid) + rescue Win32::Security::SID::Error + return nil + end + end + def sid_uri(sid) raise Puppet::Error.new( "Must use a valid SID object" ) if !sid.kind_of?(Win32::Security::SID) "WinNT://#{sid.to_s}" @@ -96,6 +108,8 @@ module Puppet::Util::ADSI end def self.uri(name, host = '.') + if sid_uri = Puppet::Util::ADSI.sid_uri_safe(name) then return sid_uri end + host = '.' if ['NT AUTHORITY', 'BUILTIN', Socket.gethostname].include?(host) Puppet::Util::ADSI.uri(name, 'user', host) @@ -240,6 +254,8 @@ module Puppet::Util::ADSI end def self.uri(name, host = '.') + if sid_uri = Puppet::Util::ADSI.sid_uri_safe(name) then return sid_uri end + Puppet::Util::ADSI.uri(name, 'group', host) end diff --git a/spec/unit/provider/group/windows_adsi_spec.rb b/spec/unit/provider/group/windows_adsi_spec.rb index d28601172..a7de859da 100644 --- a/spec/unit/provider/group/windows_adsi_spec.rb +++ b/spec/unit/provider/group/windows_adsi_spec.rb @@ -138,6 +138,7 @@ describe Puppet::Type.type(:group).provider(:windows_adsi) do end it "should be able to test whether a group exists" do + Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil) Puppet::Util::ADSI.stubs(:connect).returns stub('connection') provider.should be_exists diff --git a/spec/unit/provider/user/windows_adsi_spec.rb b/spec/unit/provider/user/windows_adsi_spec.rb index c25ccaf95..8d3ed1d0a 100755 --- a/spec/unit/provider/user/windows_adsi_spec.rb +++ b/spec/unit/provider/user/windows_adsi_spec.rb @@ -122,6 +122,7 @@ describe Puppet::Type.type(:user).provider(:windows_adsi) do end it 'should be able to test whether a user exists' do + Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil) Puppet::Util::ADSI.stubs(:connect).returns stub('connection') provider.should be_exists diff --git a/spec/unit/util/adsi_spec.rb b/spec/unit/util/adsi_spec.rb index 974aba79c..491c4374b 100755 --- a/spec/unit/util/adsi_spec.rb +++ b/spec/unit/util/adsi_spec.rb @@ -73,10 +73,12 @@ describe Puppet::Util::ADSI do let(:domain_username) { "#{domain}\\#{username}"} it "should generate the correct URI" do + Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil) Puppet::Util::ADSI::User.uri(username).should == "WinNT://./#{username},user" end it "should generate the correct URI for a user with a domain" do + Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil) Puppet::Util::ADSI::User.uri(username, domain).should == "WinNT://#{domain}/#{username},user" end @@ -107,15 +109,35 @@ describe Puppet::Util::ADSI do end it "should be able to check the existence of a user" do + Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil) Puppet::Util::ADSI.expects(:connect).with("WinNT://./#{username},user").returns connection Puppet::Util::ADSI::User.exists?(username).should be_true end it "should be able to check the existence of a domain user" do + Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil) Puppet::Util::ADSI.expects(:connect).with("WinNT://#{domain}/#{username},user").returns connection Puppet::Util::ADSI::User.exists?(domain_username).should be_true end + it "should be able to confirm the existence of a user with a well-known SID", + :if => Puppet.features.microsoft_windows? do + + system_user = Win32::Security::SID::LocalSystem + # ensure that the underlying OS is queried here + Puppet::Util::ADSI.unstub(:connect) + Puppet::Util::ADSI::User.exists?(system_user).should be_true + end + + it "should return nil with an unknown SID", + :if => Puppet.features.microsoft_windows? do + + bogus_sid = 'S-1-2-3-4' + # ensure that the underlying OS is queried here + Puppet::Util::ADSI.unstub(:connect) + Puppet::Util::ADSI::User.exists?(bogus_sid).should be_false + end + it "should be able to delete a user" do connection.expects(:Delete).with('user', username) @@ -123,6 +145,8 @@ describe Puppet::Util::ADSI do end it "should return an enumeration of IADsUser wrapped objects" do + Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil) + name = 'Administrator' wmi_users = [stub('WMI', :name => name)] Puppet::Util::ADSI.expects(:execquery).with('select name from win32_useraccount where localaccount = "TRUE"').returns(wmi_users) @@ -174,7 +198,7 @@ describe Puppet::Util::ADSI do user.password = 'pwd' end - it "should generate the correct URI",:if => Puppet.features.microsoft_windows? do + it "should generate the correct URI", :if => Puppet.features.microsoft_windows? do Puppet::Util::Windows::Security.stubs(:octet_string_to_sid_object).returns(sid) user.uri.should == "WinNT://testcomputername/#{username},user" end @@ -321,11 +345,13 @@ describe Puppet::Util::ADSI do end it "should generate the correct URI" do + Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil) group.uri.should == "WinNT://./#{groupname},group" end end it "should generate the correct URI" do + Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil) Puppet::Util::ADSI::Group.uri("people").should == "WinNT://./people,group" end @@ -342,11 +368,30 @@ describe Puppet::Util::ADSI do end it "should be able to confirm the existence of a group" do + Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil) Puppet::Util::ADSI.expects(:connect).with("WinNT://./#{groupname},group").returns connection Puppet::Util::ADSI::Group.exists?(groupname).should be_true end + it "should be able to confirm the existence of a group with a well-known SID", + :if => Puppet.features.microsoft_windows? do + + service_group = Win32::Security::SID::Service + # ensure that the underlying OS is queried here + Puppet::Util::ADSI.unstub(:connect) + Puppet::Util::ADSI::Group.exists?(service_group).should be_true + end + + it "should return nil with an unknown SID", + :if => Puppet.features.microsoft_windows? do + + bogus_sid = 'S-1-2-3-4' + # ensure that the underlying OS is queried here + Puppet::Util::ADSI.unstub(:connect) + Puppet::Util::ADSI::Group.exists?(bogus_sid).should be_false + end + it "should be able to delete a group" do connection.expects(:Delete).with('group', groupname) @@ -354,6 +399,8 @@ describe Puppet::Util::ADSI do end it "should return an enumeration of IADsGroup wrapped objects" do + Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil) + name = 'Administrators' wmi_groups = [stub('WMI', :name => name)] Puppet::Util::ADSI.expects(:execquery).with('select name from win32_group where localaccount = "TRUE"').returns(wmi_groups) From 9fbcbf0a7db3bd7ee5d8e6147b76e8aa3e7a6ac2 Mon Sep 17 00:00:00 2001 From: jrussek Date: Fri, 10 Jan 2014 12:04:41 +0100 Subject: [PATCH 553/800] (PUP-1409) add authorityKeyIdentifier to client certificates As discussed in the ticket this will add the authorityKeyIdentifier that is not required, but recommended to add to certs signed by a CA conforming to RFC 5280. This extension takes the subjectKeyIdentifier of the CA certificate and adds it as keyid to authorityKeyIdentifier. This PR also adds a test to make sure the extension is correctly added to the resulting certificate. --- lib/puppet/ssl/certificate_factory.rb | 8 +++++++- spec/unit/ssl/certificate_factory_spec.rb | 16 ++++++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/lib/puppet/ssl/certificate_factory.rb b/lib/puppet/ssl/certificate_factory.rb index 9fdf89fcd..124ce4b62 100644 --- a/lib/puppet/ssl/certificate_factory.rb +++ b/lib/puppet/ssl/certificate_factory.rb @@ -99,7 +99,13 @@ module Puppet::SSL::CertificateFactory # certificate through where the CA constraint was true, though, if # something went wrong up there. --daniel 2011-10-11 defaults = { "nsComment" => "Puppet Ruby/OpenSSL Internal Certificate" } - override = { "subjectKeyIdentifier" => "hash" } + + # See http://www.openssl.org/docs/apps/x509v3_config.html + # for information about the special meanings of 'hash', 'keyid', 'issuer' + override = { + "subjectKeyIdentifier" => "hash", + "authorityKeyIdentifier" => "keyid,issuer" + } exts = [defaults, requested_exts, extensions, override]. inject({}) {|ret, val| ret.merge(val) } diff --git a/spec/unit/ssl/certificate_factory_spec.rb b/spec/unit/ssl/certificate_factory_spec.rb index db5d05039..59d0e0170 100755 --- a/spec/unit/ssl/certificate_factory_spec.rb +++ b/spec/unit/ssl/certificate_factory_spec.rb @@ -14,9 +14,9 @@ describe Puppet::SSL::CertificateFactory do csr end let :issuer do - cert = OpenSSL::X509::Certificate.new - cert.subject = OpenSSL::X509::Name.new([["CN", 'issuer.local']]) - cert + cert = Puppet::SSL::CertificateAuthority.new + cert.generate_ca_certificate + cert.host.certificate.content end describe "when generating the certificate" do @@ -76,7 +76,7 @@ describe Puppet::SSL::CertificateFactory do cert.not_after.to_i.should == now.to_i + 12 end - it "should build extensions for the certificate" do + it "should adds an extension for the nsComment" do cert = subject.build(:server, csr, issuer, serial) cert.extensions.map {|x| x.to_h }.find {|x| x["oid"] == "nsComment" }.should == { "oid" => "nsComment", @@ -91,6 +91,14 @@ describe Puppet::SSL::CertificateFactory do ef.create_extension("subjectKeyIdentifier", "hash", false).to_h end + + it "should add an extension for the authorityKeyIdentifer" do + cert = subject.build(:server, csr, issuer, serial) + ef = OpenSSL::X509::ExtensionFactory.new(issuer, cert) + cert.extensions.map { |x| x.to_h }.find {|x| x["oid"] == "authorityKeyIdentifier" }.should == + ef.create_extension("authorityKeyIdentifier", "keyid:always", false).to_h + end + # See #2848 for why we are doing this: we need to make sure that # subjectAltName is set if the CSR has it, but *not* if it is set when the # certificate is built! From 379c5afcec3d8f0c63a78669195b240a1a4f9a89 Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Wed, 5 Feb 2014 16:05:52 -0800 Subject: [PATCH 554/800] (maint) Switch from -fd to -fp for specs to reduce disk space usage --- tasks/ci.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks/ci.rake b/tasks/ci.rake index 716e1a83e..b082a1b49 100644 --- a/tasks/ci.rake +++ b/tasks/ci.rake @@ -4,7 +4,7 @@ require 'time' namespace "ci" do task :spec do ENV["LOG_SPEC_ORDER"] = "true" - sh %{rspec -r yarjuf -f JUnit -o result.xml spec} + sh %{rspec -r yarjuf -f JUnit -o result.xml -fp spec} end desc "Tar up the acceptance/ directory so that package test runs have tests to run against." From 6b9b19ccf43d7252193777e6ec584232b0175760 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Wed, 5 Feb 2014 16:31:10 -0800 Subject: [PATCH 555/800] (PUP-753) Use an earlier version of ruby-prof on 1.8.7 ruby-prof > 0.13.1 requires Ruby 1.9.3 or greater, but we can install from the 0.13.x line for Ruby 1.8.7 compatibility. Could not use two platforms commands here because you can't duplicate gem calls for the same gem. --- Gemfile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 4fb5fe2ca..394919fd6 100644 --- a/Gemfile +++ b/Gemfile @@ -45,7 +45,10 @@ group(:development, :test) do gem "json-schema", "2.1.1", :require => false end - platforms :ruby_19, :ruby_20 do + case RUBY_VERSION + when /^1.8/ + gem 'ruby-prof', "~> 0.13.1" + else gem 'ruby-prof' end end From 85ffdfb98d1dfd9970c562b96b4658591531fa16 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Wed, 5 Feb 2014 17:22:19 -0800 Subject: [PATCH 556/800] (PUP-753) Constrain ruby-prof solely to the development group Excludes ruby-prof from the test group so that we can run specs on test machines without requiring it. In particular, ruby-prof makes running specs on Windows difficult unless devkit is present or a ruby installation for windows has been pre-built with the ruby-prof gem in place. We may need to revisit this when we get a ci setup for benchmarking. --- Gemfile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index 394919fd6..c5f446704 100644 --- a/Gemfile +++ b/Gemfile @@ -44,12 +44,14 @@ group(:development, :test) do gem "multi_json", "1.7.7", :require => false gem "json-schema", "2.1.1", :require => false end +end +group(:development) do case RUBY_VERSION when /^1.8/ - gem 'ruby-prof', "~> 0.13.1" + gem 'ruby-prof', "~> 0.13.1", :require => false else - gem 'ruby-prof' + gem 'ruby-prof', :require => false end end From 6d2eeaaee141319bbbb49808e7ba8e54fda0c536 Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Wed, 5 Feb 2014 18:46:40 -0800 Subject: [PATCH 557/800] (maint) Unset the defaultprovider after caching it Prior to this change the defaultprovider for package was implicitly cached in both package_spec.rb and msi_spec.rb (just by checking it). This represented spec "leakage" which could result in downstream spec failures on subsequent tests which accessed defaultprovider. E.g. this sequence failed: be rspec spec/unit/provider/package/msi_spec.rb spec/unit/type/package_spec.rb This change clears the defaultprovider for package after caching it for both these two spec tests. However, there are a lot of uses of defaultprovider in spec tests, so we may want a more general solution. --- spec/unit/provider/package/msi_spec.rb | 4 ++++ spec/unit/type/package_spec.rb | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/spec/unit/provider/package/msi_spec.rb b/spec/unit/provider/package/msi_spec.rb index 192b0a749..9c325bb49 100755 --- a/spec/unit/provider/package/msi_spec.rb +++ b/spec/unit/provider/package/msi_spec.rb @@ -36,6 +36,10 @@ describe Puppet::Type.type(:package).provider(:msi) do end describe 'on Windows', :as_platform => :windows do + after :each do + Puppet::Type.type(:package).defaultprovider = nil + end + it 'should not be the default provider' do # provider.expects(:execute).never Puppet::Type.type(:package).defaultprovider.should_not == subject.class diff --git a/spec/unit/type/package_spec.rb b/spec/unit/type/package_spec.rb index f665226c2..512007576 100755 --- a/spec/unit/type/package_spec.rb +++ b/spec/unit/type/package_spec.rb @@ -52,7 +52,7 @@ describe Puppet::Type.type(:package) do end describe "when validating attribute values" do - before do + before :each do @provider = stub( 'provider', :class => Puppet::Type.type(:package).defaultprovider, @@ -62,6 +62,10 @@ describe Puppet::Type.type(:package) do Puppet::Type.type(:package).defaultprovider.stubs(:new).returns(@provider) end + after :each do + Puppet:git :Type.type(:package).defaultprovider = nil + end + it "should support :present as a value to :ensure" do Puppet::Type.type(:package).new(:name => "yay", :ensure => :present) end From c87f1fe9c639323202a40c8c2a915ebf1c08971f Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Wed, 5 Feb 2014 19:39:49 -0800 Subject: [PATCH 558/800] (maint) Fix typo in spec --- spec/unit/type/package_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/unit/type/package_spec.rb b/spec/unit/type/package_spec.rb index 512007576..3e9ceb8c6 100755 --- a/spec/unit/type/package_spec.rb +++ b/spec/unit/type/package_spec.rb @@ -63,7 +63,7 @@ describe Puppet::Type.type(:package) do end after :each do - Puppet:git :Type.type(:package).defaultprovider = nil + Puppet::Type.type(:package).defaultprovider = nil end it "should support :present as a value to :ensure" do From 4acaafb0ecd4594c4cfb2a1ab2cbf66580d2e1e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Dal=C3=A9n?= Date: Thu, 6 Feb 2014 14:44:30 +0100 Subject: [PATCH 559/800] (maint) Fix syntax error in example code --- lib/puppet/application.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/application.rb b/lib/puppet/application.rb index 71e4d62f1..7d79fa778 100644 --- a/lib/puppet/application.rb +++ b/lib/puppet/application.rb @@ -21,7 +21,7 @@ module Puppet # Puppet::Application::Example.new.run # # -# class Puppet::Application::Example << Puppet::Application +# class Puppet::Application::Example < Puppet::Application # # def preinit # # perform some pre initialization From 39c2fdffa9d6e6ed8ff932cc742c1b71e7b9d051 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Dal=C3=A9n?= Date: Mon, 27 Jan 2014 15:03:59 +0100 Subject: [PATCH 560/800] (maint) Change name from from_pson to from_data_hash This is because these methods are also used for deserialization from other formats than PSON. The method for serializing a object is called to_data_hash, so makes sense to call this from_data_hash. --- ext/puppet-test | 2 +- lib/puppet/file_bucket/file.rb | 14 ++++--- lib/puppet/file_serving/metadata.rb | 6 ++- lib/puppet/indirector/request.rb | 18 ++++---- lib/puppet/indirector/resource/rest.rb | 2 +- lib/puppet/network/formats.rb | 12 +++--- lib/puppet/node.rb | 14 ++++--- lib/puppet/node/facts.rb | 6 ++- lib/puppet/relationship.rb | 14 ++++--- lib/puppet/resource.rb | 16 ++++--- lib/puppet/resource/catalog.rb | 37 ++++++++-------- lib/puppet/resource/status.rb | 8 +++- lib/puppet/resource/type.rb | 6 ++- lib/puppet/run.rb | 12 ++++-- lib/puppet/ssl/host.rb | 12 ++++-- lib/puppet/status.rb | 12 ++++-- lib/puppet/transaction/event.rb | 6 ++- lib/puppet/transaction/report.rb | 12 ++++-- lib/puppet/util/instrumentation/data.rb | 4 ++ .../util/instrumentation/indirection_probe.rb | 6 ++- lib/puppet/util/instrumentation/listener.rb | 6 ++- lib/puppet/util/log.rb | 6 ++- lib/puppet/util/metric.rb | 6 ++- lib/puppet/util/pson.rb | 2 +- lib/puppet/util/tag_set.rb | 6 ++- spec/integration/network/formats_spec.rb | 2 +- spec/lib/puppet/indirector_testing.rb | 8 +++- spec/unit/file_serving/metadata_spec.rb | 4 +- spec/unit/network/formats_spec.rb | 18 ++++---- spec/unit/relationship_spec.rb | 8 ++-- spec/unit/resource/status_spec.rb | 2 +- spec/unit/resource/type_spec.rb | 4 +- spec/unit/resource_spec.rb | 42 +++++++++---------- spec/unit/run_spec.rb | 6 +-- spec/unit/ssl/host_spec.rb | 2 +- spec/unit/transaction/event_spec.rb | 4 +- spec/unit/util/instrumentation/data_spec.rb | 2 +- .../util/instrumentation/listener_spec.rb | 4 +- spec/unit/util/log_spec.rb | 2 +- spec/unit/util/metric_spec.rb | 2 +- spec/unit/util/pson_spec.rb | 4 +- spec/unit/util/tag_set_spec.rb | 2 +- 42 files changed, 221 insertions(+), 140 deletions(-) diff --git a/ext/puppet-test b/ext/puppet-test index c2264da8c..2078806e7 100755 --- a/ext/puppet-test +++ b/ext/puppet-test @@ -241,7 +241,7 @@ Suite.new :resource_type, "Managing resource types" do ARGV.each do |name| json = Puppet::Resource::Type.find(name).to_pson data = PSON.parse(json) - p Puppet::Resource::Type.from_pson(data) + p Puppet::Resource::Type.from_data_hash(data) end end diff --git a/lib/puppet/file_bucket/file.rb b/lib/puppet/file_bucket/file.rb index 50e5d4d92..3445753cb 100644 --- a/lib/puppet/file_bucket/file.rb +++ b/lib/puppet/file_bucket/file.rb @@ -69,20 +69,24 @@ class Puppet::FileBucket::File self.new(contents) end + def to_data_hash + { "contents" => contents } + end + + def self.from_data_hash(data) + self.new(data["contents"]) + end + def to_pson Puppet.deprecation_warning("Serializing Puppet::FileBucket::File objects to pson is deprecated.") to_data_hash.to_pson end - def to_data_hash - { "contents" => contents } - 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) Puppet.deprecation_warning("Deserializing Puppet::FileBucket::File objects from pson is deprecated. Upgrade to a newer version.") - self.new(pson["contents"]) + self.from_data_hash(pson) end end diff --git a/lib/puppet/file_serving/metadata.rb b/lib/puppet/file_serving/metadata.rb index 880c95993..1fd6e6506 100644 --- a/lib/puppet/file_serving/metadata.rb +++ b/lib/puppet/file_serving/metadata.rb @@ -172,6 +172,10 @@ class Puppet::FileServing::Metadata < Puppet::FileServing::Base ) end + def self.from_data_hash(data) + new(data.delete('path'), data) + end + PSON.register_document_type('FileMetadata',self) def to_pson_data_hash { @@ -188,7 +192,7 @@ class Puppet::FileServing::Metadata < Puppet::FileServing::Base end def self.from_pson(data) - new(data.delete('path'), data) + self.from_data_hash(data) end end diff --git a/lib/puppet/indirector/request.rb b/lib/puppet/indirector/request.rb index cd451745c..b4c6b7380 100644 --- a/lib/puppet/indirector/request.rb +++ b/lib/puppet/indirector/request.rb @@ -20,25 +20,29 @@ class Puppet::Indirector::Request ::PSON.register_document_type('IndirectorRequest',self) - def self.from_pson(json) - raise ArgumentError, "No indirection name provided in json data" unless indirection_name = json['type'] - raise ArgumentError, "No method name provided in json data" unless method = json['method'] - raise ArgumentError, "No key provided in json data" unless key = json['key'] + def self.from_data_hash(data) + raise ArgumentError, "No indirection name provided in data" unless indirection_name = data['type'] + raise ArgumentError, "No method name provided in data" unless method = data['method'] + raise ArgumentError, "No key provided in data" unless key = data['key'] - request = new(indirection_name, method, key, nil, json['attributes']) + request = new(indirection_name, method, key, nil, data['attributes']) - if instance = json['instance'] + if instance = data['instance'] klass = Puppet::Indirector::Indirection.instance(request.indirection_name).model if instance.is_a?(klass) request.instance = instance else - request.instance = klass.from_pson(instance) + request.instance = klass.from_data_hash(instance) end end request end + def self.from_pson(json) + self.from_data_hash(json) + end + def to_data_hash result = { 'type' => indirection_name, diff --git a/lib/puppet/indirector/resource/rest.rb b/lib/puppet/indirector/resource/rest.rb index 9992fc057..66384796b 100644 --- a/lib/puppet/indirector/resource/rest.rb +++ b/lib/puppet/indirector/resource/rest.rb @@ -12,6 +12,6 @@ class Puppet::Resource::Rest < Puppet::Indirector::REST # Body is [ral_res.to_resource, transaction.report] format = Puppet::Network::FormatHandler.format_for(content_type) ary = format.intern(Array, body) - [Puppet::Resource.from_pson(ary[0]), Puppet::Transaction::Report.from_pson(ary[1])] + [Puppet::Resource.from_data_hash(ary[0]), Puppet::Transaction::Report.from_data_hash(ary[1])] end end diff --git a/lib/puppet/network/formats.rb b/lib/puppet/network/formats.rb index 62e40d376..5b218e611 100644 --- a/lib/puppet/network/formats.rb +++ b/lib/puppet/network/formats.rb @@ -4,12 +4,12 @@ Puppet::Network::FormatHandler.create_serialized_formats(:msgpack, :weight => 20 def intern(klass, text) data = MessagePack.unpack(text) return data if data.is_a?(klass) - klass.from_pson(data) + klass.from_data_hash(data) end def intern_multiple(klass, text) MessagePack.unpack(text).collect do |data| - klass.from_pson(data) + klass.from_data_hash(data) end end @@ -22,7 +22,7 @@ Puppet::Network::FormatHandler.create_serialized_formats(:msgpack, :weight => 20 end def supported?(klass) - Puppet.features.msgpack? && klass.method_defined?(:to_msgpack) + Puppet.features.msgpack? && klass.method_defined?(:to_msgpack) && klass.method_defined?(:from_data_hash) end end @@ -50,7 +50,7 @@ Puppet::Network::FormatHandler.create_serialized_formats(:yaml) do raise Puppet::Network::FormatHandler::FormatError, "Serialized YAML did not contain a valid instance of #{klass}" end - klass.from_pson(data) + klass.from_data_hash(data) end def render(instance) @@ -143,7 +143,7 @@ Puppet::Network::FormatHandler.create(:raw, :mime => "application/x-raw", :weigh end end -Puppet::Network::FormatHandler.create_serialized_formats(:pson, :weight => 10, :required_methods => [:render_method, :intern_method]) do +Puppet::Network::FormatHandler.create_serialized_formats(:pson, :weight => 10, :required_methods => [:render_method, :intern_method], :intern_method => :from_data_hash) do def intern(klass, text) data_to_instance(klass, PSON.parse(text)) end @@ -168,7 +168,7 @@ Puppet::Network::FormatHandler.create_serialized_formats(:pson, :weight => 10, : data = d end return data if data.is_a?(klass) - klass.from_pson(data) + klass.from_data_hash(data) end end diff --git a/lib/puppet/node.rb b/lib/puppet/node.rb index 4577ea7e3..468f8e764 100644 --- a/lib/puppet/node.rb +++ b/lib/puppet/node.rb @@ -18,16 +18,20 @@ class Puppet::Node ::PSON.register_document_type('Node',self) - def self.from_pson(pson) - raise ArgumentError, "No name provided in serialized data" unless name = pson['name'] + def self.from_data_hash(data) + raise ArgumentError, "No name provided in serialized data" unless name = data['name'] node = new(name) - node.classes = pson['classes'] - node.parameters = pson['parameters'] - node.environment = pson['environment'] + node.classes = data['classes'] + node.parameters = data['parameters'] + node.environment = data['environment'] node end + def self.from_pson(pson) + self.from_data_hash(pson) + end + def to_data_hash result = { 'name' => name, diff --git a/lib/puppet/node/facts.rb b/lib/puppet/node/facts.rb index 2be4e68b9..ac1b4ec76 100644 --- a/lib/puppet/node/facts.rb +++ b/lib/puppet/node/facts.rb @@ -78,12 +78,16 @@ class Puppet::Node::Facts strip_internal == other.send(:strip_internal) end - def self.from_pson(data) + def self.from_data_hash(data) new_facts = allocate new_facts.initialize_from_hash(data) new_facts end + def self.from_pson(data) + self.from_data_hash(data) + end + def to_data_hash result = { 'name' => name, diff --git a/lib/puppet/relationship.rb b/lib/puppet/relationship.rb index ebac97e7e..8655ae882 100644 --- a/lib/puppet/relationship.rb +++ b/lib/puppet/relationship.rb @@ -12,21 +12,25 @@ class Puppet::Relationship attr_reader :event - def self.from_pson(pson) - source = pson["source"] - target = pson["target"] + def self.from_data_hash(data) + source = data["source"] + target = data["target"] args = {} - if event = pson["event"] + if event = data["event"] args[:event] = event end - if callback = pson["callback"] + if callback = data["callback"] args[:callback] = callback end new(source, target, args) end + def self.from_pson(pson) + self.from_data_hash(pson) + end + def event=(event) raise ArgumentError, "You must pass a callback for non-NONE events" if event != :NONE and ! callback @event = event diff --git a/lib/puppet/resource.rb b/lib/puppet/resource.rb index bc8625750..198703beb 100644 --- a/lib/puppet/resource.rb +++ b/lib/puppet/resource.rb @@ -26,22 +26,22 @@ class Puppet::Resource ATTRIBUTES = [:file, :line, :exported] - def self.from_pson(pson) - raise ArgumentError, "No resource type provided in serialized data" unless type = pson['type'] - raise ArgumentError, "No resource title provided in serialized data" unless title = pson['title'] + def self.from_data_hash(data) + raise ArgumentError, "No resource type provided in serialized data" unless type = data['type'] + raise ArgumentError, "No resource title provided in serialized data" unless title = data['title'] resource = new(type, title) - if params = pson['parameters'] + if params = data['parameters'] params.each { |param, value| resource[param] = value } end - if tags = pson['tags'] + if tags = data['tags'] tags.each { |tag| resource.tag(tag) } end ATTRIBUTES.each do |a| - if value = pson[a.to_s] + if value = data[a.to_s] resource.send(a.to_s + "=", value) end end @@ -49,6 +49,10 @@ class Puppet::Resource resource end + def self.from_pson(pson) + self.from_data_hash(pson) + end + def inspect "#{@type}[#{@title}]#{to_hash.inspect}" end diff --git a/lib/puppet/resource/catalog.rb b/lib/puppet/resource/catalog.rb index 61b4f0b81..c31b3490c 100644 --- a/lib/puppet/resource/catalog.rb +++ b/lib/puppet/resource/catalog.rb @@ -313,7 +313,7 @@ class Puppet::Resource::Catalog < Puppet::Graph::SimpleGraph end end - def self.from_pson(data) + def self.from_data_hash(data) result = new(data['name']) if tags = data['tags'] @@ -330,14 +330,24 @@ class Puppet::Resource::Catalog < Puppet::Graph::SimpleGraph if resources = data['resources'] result.add_resource(*resources.collect do |res| - Puppet::Resource.from_pson(res) + Puppet::Resource.from_data_hash(res) end) end if edges = data['edges'] - edges = PSON.parse(edges) if edges.is_a?(String) - edges.each do |edge| - edge_from_pson(result, edge) + edges.each do |edge_hash| + edge = Puppet::Relationship.from_data_hash(edge_hash) + unless source = result.resource(edge.source) + raise ArgumentError, "Could not intern from data: Could not find relationship source #{edge.source.inspect}" + end + edge.source = source + + unless target = result.resource(edge.target) + raise ArgumentError, "Could not intern from data: Could not find relationship target #{edge.target.inspect}" + end + edge.target = target + + result.add_edge(edge) end end @@ -348,21 +358,8 @@ class Puppet::Resource::Catalog < Puppet::Graph::SimpleGraph result end - def self.edge_from_pson(result, edge) - # If no type information was presented, we manually find - # the class. - edge = Puppet::Relationship.from_pson(edge) if edge.is_a?(Hash) - unless source = result.resource(edge.source) - raise ArgumentError, "Could not convert from pson: Could not find relationship source #{edge.source.inspect}" - end - edge.source = source - - unless target = result.resource(edge.target) - raise ArgumentError, "Could not convert from pson: Could not find relationship target #{edge.target.inspect}" - end - edge.target = target - - result.add_edge(edge) + def self.from_pson(data) + self.from_data_hash(data) end def to_data_hash diff --git a/lib/puppet/resource/status.rb b/lib/puppet/resource/status.rb index eae5aeed9..3d0273516 100644 --- a/lib/puppet/resource/status.rb +++ b/lib/puppet/resource/status.rb @@ -24,12 +24,16 @@ module Puppet map(&:to_sym) - def self.from_pson(data) + def self.from_data_hash(data) obj = self.allocate obj.initialize_from_hash(data) obj end + def self.from_pson(data) + self.from_data_hash(data) + end + # Provide a boolean method for each of the states. STATES.each do |attr| define_method("#{attr}?") do @@ -111,7 +115,7 @@ module Puppet @failed = data['failed'] @events = data['events'].map do |event| - Puppet::Transaction::Event.from_pson(event) + Puppet::Transaction::Event.from_data_hash(event) end end diff --git a/lib/puppet/resource/type.rb b/lib/puppet/resource/type.rb index cc09ecbba..63727b429 100644 --- a/lib/puppet/resource/type.rb +++ b/lib/puppet/resource/type.rb @@ -46,7 +46,7 @@ class Puppet::Resource::Type extend Puppet::Indirector indirects :resource_type, :terminus_class => :parser - def self.from_pson(data) + def self.from_data_hash(data) name = data.delete('name') or raise ArgumentError, "Resource Type names must be specified" kind = data.delete('kind') || "definition" @@ -63,6 +63,10 @@ class Puppet::Resource::Type new(type, name, data) end + def self.from_pson(data) + self.from_data_hash(data) + end + def to_pson(*args) to_data_hash.to_pson(*args) end diff --git a/lib/puppet/run.rb b/lib/puppet/run.rb index 1d89304be..c5512d37a 100644 --- a/lib/puppet/run.rb +++ b/lib/puppet/run.rb @@ -80,20 +80,24 @@ class Puppet::Run obj end - def self.from_pson(hash) - if hash['options'] - return from_hash(hash) + def self.from_data_hash(data) + if data['options'] + return from_hash(data) end options = { :pluginsync => Puppet[:pluginsync] } - hash.each do |key, value| + data.each do |key, value| options[key.to_sym] = value end new(options) end + def self.from_pson(hash) + self.from_data_hash(hash) + end + def to_data_hash { :options => @options, diff --git a/lib/puppet/ssl/host.rb b/lib/puppet/ssl/host.rb index f30b4dee7..d66e0985c 100644 --- a/lib/puppet/ssl/host.rb +++ b/lib/puppet/ssl/host.rb @@ -118,14 +118,18 @@ DOC indirection.destroy(name) end - def self.from_pson(pson) - instance = new(pson["name"]) - if pson["desired_state"] - instance.desired_state = pson["desired_state"] + def self.from_data_hash(data) + instance = new(data["name"]) + if data["desired_state"] + instance.desired_state = data["desired_state"] end instance end + def self.from_pson(pson) + self.from_data_hash(pson) + end + # Puppet::SSL::Host is actually indirected now so the original implementation # has been moved into the certificate_status indirector. This method does not # appear to be in use in `puppet cert -l`. diff --git a/lib/puppet/status.rb b/lib/puppet/status.rb index 0b26ae22e..22ad5504f 100644 --- a/lib/puppet/status.rb +++ b/lib/puppet/status.rb @@ -18,14 +18,18 @@ class Puppet::Status @status.to_pson end - def self.from_pson(pson) - if pson.include?('status') - self.new(pson['status']) + def self.from_data_hash(data) + if data.include?('status') + self.new(data['status']) else - self.new(pson) + self.new(data) end end + def self.from_pson(pson) + self.from_data_hash(pson) + end + def name "status" end diff --git a/lib/puppet/transaction/event.rb b/lib/puppet/transaction/event.rb index ca4255937..1ea53e4e0 100644 --- a/lib/puppet/transaction/event.rb +++ b/lib/puppet/transaction/event.rb @@ -19,12 +19,16 @@ class Puppet::Transaction::Event EVENT_STATUSES = %w{noop success failure audit} - def self.from_pson(data) + def self.from_data_hash(data) obj = self.allocate obj.initialize_from_hash(data) obj end + def self.from_pson(data) + self.from_data_hash(data) + end + def initialize(options = {}) @audited = false diff --git a/lib/puppet/transaction/report.rb b/lib/puppet/transaction/report.rb index 25144ef68..8acc14512 100644 --- a/lib/puppet/transaction/report.rb +++ b/lib/puppet/transaction/report.rb @@ -97,12 +97,16 @@ class Puppet::Transaction::Report # attr_reader :report_format - def self.from_pson(data) + def self.from_data_hash(data) obj = self.allocate obj.initialize_from_hash(data) obj end + def self.from_pson(data) + self.from_data_hash(data) + end + def as_logging_destination(&block) Puppet::Util::Log.with_destination(self, &block) end @@ -197,11 +201,11 @@ class Puppet::Transaction::Report @metrics = {} data['metrics'].each do |name, hash| - @metrics[name] = Puppet::Util::Metric.from_pson(hash) + @metrics[name] = Puppet::Util::Metric.from_data_hash(hash) end @logs = data['logs'].map do |record| - Puppet::Util::Log.from_pson(record) + Puppet::Util::Log.from_data_hash(record) end @resource_statuses = {} @@ -209,7 +213,7 @@ class Puppet::Transaction::Report if record[1] == {} status = nil else - status = Puppet::Resource::Status.from_pson(record[1]) + status = Puppet::Resource::Status.from_data_hash(record[1]) end @resource_statuses[record[0]] = status end diff --git a/lib/puppet/util/instrumentation/data.rb b/lib/puppet/util/instrumentation/data.rb index 48e595432..5d06b4be2 100644 --- a/lib/puppet/util/instrumentation/data.rb +++ b/lib/puppet/util/instrumentation/data.rb @@ -35,6 +35,10 @@ class Puppet::Util::Instrumentation::Data to_pson_data_hash.to_pson(*args) end + def self.from_data_hash(data) + data + end + def self.from_pson(data) data end diff --git a/lib/puppet/util/instrumentation/indirection_probe.rb b/lib/puppet/util/instrumentation/indirection_probe.rb index 237d8dbc8..c4817a1b6 100644 --- a/lib/puppet/util/instrumentation/indirection_probe.rb +++ b/lib/puppet/util/instrumentation/indirection_probe.rb @@ -30,7 +30,11 @@ class Puppet::Util::Instrumentation::IndirectionProbe to_pson_data_hash.to_pson(*args) end - def self.from_pson(data) + def self.from_data_hash(data) self.new(data["name"]) end + + def self.from_pson(data) + self.from_data_hash(data) + end end diff --git a/lib/puppet/util/instrumentation/listener.rb b/lib/puppet/util/instrumentation/listener.rb index b965e976f..9baa15288 100644 --- a/lib/puppet/util/instrumentation/listener.rb +++ b/lib/puppet/util/instrumentation/listener.rb @@ -60,8 +60,12 @@ class Puppet::Util::Instrumentation::Listener to_pson_data_hash.to_pson(*args) end - def self.from_pson(data) + def self.from_data_hash(data) result = Puppet::Util::Instrumentation[data["name"]] self.new(result.listener, result.pattern, data["enabled"]) end + + def self.from_pson(data) + self.from_data_hash(data) + end end diff --git a/lib/puppet/util/log.rb b/lib/puppet/util/log.rb index 808e0631d..79414a8f0 100644 --- a/lib/puppet/util/log.rb +++ b/lib/puppet/util/log.rb @@ -233,12 +233,16 @@ class Puppet::Util::Log @levels.include?(level) end - def self.from_pson(data) + def self.from_data_hash(data) obj = allocate obj.initialize_from_hash(data) obj end + def self.from_pson(data) + self.from_data_hash(data) + end + attr_accessor :time, :remote, :file, :line, :source attr_reader :level, :message diff --git a/lib/puppet/util/metric.rb b/lib/puppet/util/metric.rb index 7b07a181d..6e64ee098 100644 --- a/lib/puppet/util/metric.rb +++ b/lib/puppet/util/metric.rb @@ -11,12 +11,16 @@ class Puppet::Util::Metric attr_writer :basedir - def self.from_pson(data) + def self.from_data_hash(data) metric = new(data['name'], data['label']) metric.values = data['values'] metric end + def self.from_pson(data) + self.from_data_hash(data) + end + def to_data_hash { 'name' => @name, diff --git a/lib/puppet/util/pson.rb b/lib/puppet/util/pson.rb index 1441069c0..b796cb111 100644 --- a/lib/puppet/util/pson.rb +++ b/lib/puppet/util/pson.rb @@ -8,6 +8,6 @@ module Puppet::Util::Pson def pson_create(pson) raise ArgumentError, "No data provided in pson data" unless pson['data'] - from_pson(pson['data']) + from_data_hash(pson['data']) end end diff --git a/lib/puppet/util/tag_set.rb b/lib/puppet/util/tag_set.rb index d9c9d9695..7c92cd5dc 100644 --- a/lib/puppet/util/tag_set.rb +++ b/lib/puppet/util/tag_set.rb @@ -12,10 +12,14 @@ class Puppet::Util::TagSet < Set @hash.keys.to_yaml end - def self.from_pson(data) + def self.from_data_hash(data) self.new(data) end + def self.from_pson(data) + self.from_data_hash(data) + end + def to_data_hash to_a end diff --git a/spec/integration/network/formats_spec.rb b/spec/integration/network/formats_spec.rb index 2834e33f0..686961338 100755 --- a/spec/integration/network/formats_spec.rb +++ b/spec/integration/network/formats_spec.rb @@ -9,7 +9,7 @@ class PsonIntTest other.class == self.class and string == other.string end - def self.from_pson(data) + def self.from_data_hash(data) new(data[0]) end diff --git a/spec/lib/puppet/indirector_testing.rb b/spec/lib/puppet/indirector_testing.rb index e6b88ddcd..0fd4ef044 100644 --- a/spec/lib/puppet/indirector_testing.rb +++ b/spec/lib/puppet/indirector_testing.rb @@ -19,14 +19,18 @@ class Puppet::IndirectorTesting end PSON.register_document_type('IndirectorTesting',self) - def self.from_pson(data) + def self.from_data_hash(data) new(data['value']) end + def to_data_hash + { 'value' => value } + end + def to_pson { 'document_type' => 'IndirectorTesting', - 'data' => { 'value' => value }, + 'data' => self.to_data_hash, 'metadata' => { 'api_version' => 1 } }.to_pson end diff --git a/spec/unit/file_serving/metadata_spec.rb b/spec/unit/file_serving/metadata_spec.rb index 51161c4fe..f3b0c4347 100755 --- a/spec/unit/file_serving/metadata_spec.rb +++ b/spec/unit/file_serving/metadata_spec.rb @@ -26,8 +26,8 @@ describe Puppet::FileServing::Metadata do Puppet::FileServing::Metadata.new(foobar).should respond_to(:to_pson_data_hash) end - it "should support pson deserialization" do - Puppet::FileServing::Metadata.should respond_to(:from_pson) + it "should support deserialization" do + Puppet::FileServing::Metadata.should respond_to(:from_data_hash) end describe "when serializing" do diff --git a/spec/unit/network/formats_spec.rb b/spec/unit/network/formats_spec.rb index a64f96e1e..99535ff1e 100755 --- a/spec/unit/network/formats_spec.rb +++ b/spec/unit/network/formats_spec.rb @@ -9,7 +9,7 @@ class PsonTest string == other.string end - def self.from_pson(data) + def self.from_data_hash(data) new(data) end @@ -43,7 +43,7 @@ describe "Puppet Network Format" do @msgpack.weight.should == 20 end - it "should fail when one element does not have a from_pson" do + it "should fail when one element does not have a from_data_hash" do expect do @msgpack.intern_multiple(Hash, MessagePack.pack(["foo"])) end.to raise_error(NoMethodError) @@ -316,10 +316,10 @@ describe "Puppet Network Format" do @pson.render_multiple(instances).should == "foo" end - it "should intern by calling 'PSON.parse' on the text and then using from_pson to convert the data into an instance" do + it "should intern by calling 'PSON.parse' on the text and then using from_data_hash to convert the data into an instance" do text = "foo" PSON.expects(:parse).with("foo").returns("type" => "PsonTest", "data" => "foo") - PsonTest.expects(:from_pson).with("foo").returns "parsed_pson" + PsonTest.expects(:from_data_hash).with("foo").returns "parsed_pson" @pson.intern(PsonTest, text).should == "parsed_pson" end @@ -327,22 +327,22 @@ describe "Puppet Network Format" do text = "foo" instance = PsonTest.new("foo") PSON.expects(:parse).with("foo").returns(instance) - PsonTest.expects(:from_pson).never + PsonTest.expects(:from_data_hash).never @pson.intern(PsonTest, text).should equal(instance) end - it "should intern by calling 'PSON.parse' on the text and then using from_pson to convert the actual into an instance if the pson has no class/data separation" do + it "should intern by calling 'PSON.parse' on the text and then using from_data_hash to convert the actual into an instance if the pson has no class/data separation" do text = "foo" PSON.expects(:parse).with("foo").returns("foo") - PsonTest.expects(:from_pson).with("foo").returns "parsed_pson" + PsonTest.expects(:from_data_hash).with("foo").returns "parsed_pson" @pson.intern(PsonTest, text).should == "parsed_pson" end it "should intern multiples by parsing the text and using 'class.intern' on each resulting data structure" do text = "foo" PSON.expects(:parse).with("foo").returns ["bar", "baz"] - PsonTest.expects(:from_pson).with("bar").returns "BAR" - PsonTest.expects(:from_pson).with("baz").returns "BAZ" + PsonTest.expects(:from_data_hash).with("bar").returns "BAR" + PsonTest.expects(:from_data_hash).with("baz").returns "BAZ" @pson.intern_multiple(PsonTest, text).should == %w{BAR BAZ} end diff --git a/spec/unit/relationship_spec.rb b/spec/unit/relationship_spec.rb index a6aeffc4f..66cd38183 100755 --- a/spec/unit/relationship_spec.rb +++ b/spec/unit/relationship_spec.rb @@ -211,18 +211,18 @@ describe Puppet::Relationship, "when converting from pson" do # LAK:NOTE For all of these tests, we convert back to the edge so we can # trap the actual data structure then. it "should pass the source in as the first argument" do - Puppet::Relationship.from_pson("source" => "mysource", "target" => "mytarget").source.should == "mysource" + Puppet::Relationship.from_data_hash("source" => "mysource", "target" => "mytarget").source.should == "mysource" end it "should pass the target in as the second argument" do - Puppet::Relationship.from_pson("source" => "mysource", "target" => "mytarget").target.should == "mytarget" + Puppet::Relationship.from_data_hash("source" => "mysource", "target" => "mytarget").target.should == "mytarget" end it "should pass the event as an argument if it's provided" do - Puppet::Relationship.from_pson("source" => "mysource", "target" => "mytarget", "event" => "myevent", "callback" => "eh").event.should == "myevent" + Puppet::Relationship.from_data_hash("source" => "mysource", "target" => "mytarget", "event" => "myevent", "callback" => "eh").event.should == "myevent" end it "should pass the callback as an argument if it's provided" do - Puppet::Relationship.from_pson("source" => "mysource", "target" => "mytarget", "callback" => "mycallback").callback.should == "mycallback" + Puppet::Relationship.from_data_hash("source" => "mysource", "target" => "mytarget", "callback" => "mycallback").callback.should == "mycallback" end end diff --git a/spec/unit/resource/status_spec.rb b/spec/unit/resource/status_spec.rb index d50abdce3..271554325 100755 --- a/spec/unit/resource/status_spec.rb +++ b/spec/unit/resource/status_spec.rb @@ -181,7 +181,7 @@ describe Puppet::Resource::Status do @status.containment_path.should == @containment_path - tripped = Puppet::Resource::Status.from_pson(PSON.parse(@status.to_pson)) + tripped = Puppet::Resource::Status.from_data_hash(PSON.parse(@status.to_pson)) tripped.title.should == @status.title tripped.containment_path.should == @status.containment_path diff --git a/spec/unit/resource/type_spec.rb b/spec/unit/resource/type_spec.rb index 1dd0c41db..b3a4aab78 100755 --- a/spec/unit/resource/type_spec.rb +++ b/spec/unit/resource/type_spec.rb @@ -39,11 +39,11 @@ describe Puppet::Resource::Type do end def from_json(json) - Puppet::Resource::Type.from_pson(json) + Puppet::Resource::Type.from_data_hash(json) end def double_convert - Puppet::Resource::Type.from_pson(PSON.parse(@type.to_pson)) + Puppet::Resource::Type.from_data_hash(PSON.parse(@type.to_pson)) end it "should include the name and type" do diff --git a/spec/unit/resource_spec.rb b/spec/unit/resource_spec.rb index 1ac2decf1..b7a95aaba 100755 --- a/spec/unit/resource_spec.rb +++ b/spec/unit/resource_spec.rb @@ -671,45 +671,45 @@ describe Puppet::Resource do # trap the actual data structure then. it "should set its type to the provided type" do - Puppet::Resource.from_pson(PSON.parse(Puppet::Resource.new("File", "/foo").to_pson)).type.should == "File" + Puppet::Resource.from_data_hash(PSON.parse(Puppet::Resource.new("File", "/foo").to_pson)).type.should == "File" end it "should set its title to the provided title" do - Puppet::Resource.from_pson(PSON.parse(Puppet::Resource.new("File", "/foo").to_pson)).title.should == "/foo" + Puppet::Resource.from_data_hash(PSON.parse(Puppet::Resource.new("File", "/foo").to_pson)).title.should == "/foo" end it "should include all tags from the resource" do resource = Puppet::Resource.new("File", "/foo") resource.tag("yay") - Puppet::Resource.from_pson(PSON.parse(resource.to_pson)).tags.should == resource.tags + Puppet::Resource.from_data_hash(PSON.parse(resource.to_pson)).tags.should == resource.tags end it "should include the file if one is set" do resource = Puppet::Resource.new("File", "/foo") resource.file = "/my/file" - Puppet::Resource.from_pson(PSON.parse(resource.to_pson)).file.should == "/my/file" + Puppet::Resource.from_data_hash(PSON.parse(resource.to_pson)).file.should == "/my/file" end it "should include the line if one is set" do resource = Puppet::Resource.new("File", "/foo") resource.line = 50 - Puppet::Resource.from_pson(PSON.parse(resource.to_pson)).line.should == 50 + Puppet::Resource.from_data_hash(PSON.parse(resource.to_pson)).line.should == 50 end it "should include the 'exported' value if one is set" do resource = Puppet::Resource.new("File", "/foo") resource.exported = true - Puppet::Resource.from_pson(PSON.parse(resource.to_pson)).exported?.should be_true + Puppet::Resource.from_data_hash(PSON.parse(resource.to_pson)).exported?.should be_true end it "should set 'exported' to false if no value is set" do resource = Puppet::Resource.new("File", "/foo") - Puppet::Resource.from_pson(PSON.parse(resource.to_pson)).exported?.should be_false + Puppet::Resource.from_data_hash(PSON.parse(resource.to_pson)).exported?.should be_false end it "should set all of its parameters as the 'parameters' entry" do @@ -717,7 +717,7 @@ describe Puppet::Resource do resource[:foo] = %w{bar eh} resource[:fee] = %w{baz} - result = Puppet::Resource.from_pson(PSON.parse(resource.to_pson)) + result = Puppet::Resource.from_data_hash(PSON.parse(resource.to_pson)) result["foo"].should == %w{bar eh} result["fee"].should == %w{baz} end @@ -725,14 +725,14 @@ describe Puppet::Resource do it "should serialize relationships as reference strings" do resource = Puppet::Resource.new("File", "/foo") resource[:requires] = Puppet::Resource.new("File", "/bar") - result = Puppet::Resource.from_pson(PSON.parse(resource.to_pson)) + result = Puppet::Resource.from_data_hash(PSON.parse(resource.to_pson)) result[:requires].should == "File[/bar]" end it "should serialize multiple relationships as arrays of reference strings" do resource = Puppet::Resource.new("File", "/foo") resource[:requires] = [Puppet::Resource.new("File", "/bar"), Puppet::Resource.new("File", "/baz")] - result = Puppet::Resource.from_pson(PSON.parse(resource.to_pson)) + result = Puppet::Resource.from_data_hash(PSON.parse(resource.to_pson)) result[:requires].should == [ "File[/bar]", "File[/baz]" ] end end @@ -750,59 +750,59 @@ describe Puppet::Resource do end it "should set its type to the provided type" do - Puppet::Resource.from_pson(@data).type.should == "File" + Puppet::Resource.from_data_hash(@data).type.should == "File" end it "should set its title to the provided title" do - Puppet::Resource.from_pson(@data).title.should == basepath+"/yay" + Puppet::Resource.from_data_hash(@data).title.should == basepath+"/yay" end it "should tag the resource with any provided tags" do @data['tags'] = %w{foo bar} - resource = Puppet::Resource.from_pson(@data) + resource = Puppet::Resource.from_data_hash(@data) resource.tags.should be_include("foo") resource.tags.should be_include("bar") end it "should set its file to the provided file" do @data['file'] = "/foo/bar" - Puppet::Resource.from_pson(@data).file.should == "/foo/bar" + Puppet::Resource.from_data_hash(@data).file.should == "/foo/bar" end it "should set its line to the provided line" do @data['line'] = 50 - Puppet::Resource.from_pson(@data).line.should == 50 + Puppet::Resource.from_data_hash(@data).line.should == 50 end it "should 'exported' to true if set in the pson data" do @data['exported'] = true - Puppet::Resource.from_pson(@data).exported.should be_true + Puppet::Resource.from_data_hash(@data).exported.should be_true end it "should 'exported' to false if not set in the pson data" do - Puppet::Resource.from_pson(@data).exported.should be_false + Puppet::Resource.from_data_hash(@data).exported.should be_false end it "should fail if no title is provided" do @data.delete('title') - expect { Puppet::Resource.from_pson(@data) }.to raise_error(ArgumentError) + expect { Puppet::Resource.from_data_hash(@data) }.to raise_error(ArgumentError) end it "should fail if no type is provided" do @data.delete('type') - expect { Puppet::Resource.from_pson(@data) }.to raise_error(ArgumentError) + expect { Puppet::Resource.from_data_hash(@data) }.to raise_error(ArgumentError) end it "should set each of the provided parameters" do @data['parameters'] = {'foo' => %w{one two}, 'fee' => %w{three four}} - resource = Puppet::Resource.from_pson(@data) + resource = Puppet::Resource.from_data_hash(@data) resource['foo'].should == %w{one two} resource['fee'].should == %w{three four} end it "should convert single-value array parameters to normal values" do @data['parameters'] = {'foo' => %w{one}} - resource = Puppet::Resource.from_pson(@data) + resource = Puppet::Resource.from_data_hash(@data) resource['foo'].should == %w{one} end end diff --git a/spec/unit/run_spec.rb b/spec/unit/run_spec.rb index 266f65334..c1b1d8a93 100755 --- a/spec/unit/run_spec.rb +++ b/spec/unit/run_spec.rb @@ -121,14 +121,14 @@ describe Puppet::Run do end end - describe ".from_pson" do + describe ".from_data_hash" do it "should read from a hash that represents the 'options' to initialize" do options = { "tags" => "whatever", "background" => true, "ignoreschedules" => false, } - run = Puppet::Run.from_pson(options) + run = Puppet::Run.from_data_hash(options) run.options.should == { :tags => "whatever", @@ -145,7 +145,7 @@ describe Puppet::Run do "tags" => [], "ignoreschedules" => false}, "status" => "success"} - run = Puppet::Run.from_pson(hash) + run = Puppet::Run.from_data_hash(hash) run.options.should == { :pluginsync => true, diff --git a/spec/unit/ssl/host_spec.rb b/spec/unit/ssl/host_spec.rb index a9ab9b51e..a80fe9205 100755 --- a/spec/unit/ssl/host_spec.rb +++ b/spec/unit/ssl/host_spec.rb @@ -931,7 +931,7 @@ describe Puppet::SSL::Host do "name" => host.name, "desired_state" => host.desired_state, } - generated_host = Puppet::SSL::Host.from_pson(pson_hash) + generated_host = Puppet::SSL::Host.from_data_hash(pson_hash) generated_host.desired_state.should == host.desired_state generated_host.name.should == host.name end diff --git a/spec/unit/transaction/event_spec.rb b/spec/unit/transaction/event_spec.rb index 8e62e02f6..4781cbca1 100755 --- a/spec/unit/transaction/event_spec.rb +++ b/spec/unit/transaction/event_spec.rb @@ -149,7 +149,7 @@ describe Puppet::Transaction::Event do :property => :mode, :status => 'success') - tripped = Puppet::Transaction::Event.from_pson(PSON.parse(event.to_pson)) + tripped = Puppet::Transaction::Event.from_data_hash(PSON.parse(event.to_pson)) tripped.audited.should == event.audited tripped.property.should == event.property @@ -176,7 +176,7 @@ describe Puppet::Transaction::Event do :property => :mode, :status => 'success') - tripped = Puppet::Transaction::Event.from_pson(PSON.parse(event.to_pson)) + tripped = Puppet::Transaction::Event.from_data_hash(PSON.parse(event.to_pson)) tripped.desired_value.should be_nil tripped.historical_value.should be_nil diff --git a/spec/unit/util/instrumentation/data_spec.rb b/spec/unit/util/instrumentation/data_spec.rb index d8a6b32d0..418d89724 100755 --- a/spec/unit/util/instrumentation/data_spec.rb +++ b/spec/unit/util/instrumentation/data_spec.rb @@ -41,6 +41,6 @@ describe Puppet::Util::Instrumentation::Data do end it "should return a hash containing data when unserializing from pson" do - Puppet::Util::Instrumentation::Data.from_pson({:name => "name"}).should == {:name => "name"} + Puppet::Util::Instrumentation::Data.from_data_hash({:name => "name"}).should == {:name => "name"} end end diff --git a/spec/unit/util/instrumentation/listener_spec.rb b/spec/unit/util/instrumentation/listener_spec.rb index 90c76978f..344934908 100755 --- a/spec/unit/util/instrumentation/listener_spec.rb +++ b/spec/unit/util/instrumentation/listener_spec.rb @@ -88,14 +88,14 @@ describe Puppet::Util::Instrumentation::Listener do describe "when deserializing from pson" do it "should lookup the archetype listener from the instrumentation layer" do Puppet::Util::Instrumentation.expects(:[]).with("listener").returns(@listener) - Puppet::Util::Instrumentation::Listener.from_pson({"name" => "listener"}) + Puppet::Util::Instrumentation::Listener.from_data_hash({"name" => "listener"}) end it "should create a new listener shell instance delegating to the archetypal listener" do Puppet::Util::Instrumentation.expects(:[]).with("listener").returns(@listener) @listener.stubs(:listener).returns(@delegate) Puppet::Util::Instrumentation::Listener.expects(:new).with(@delegate, nil, true) - Puppet::Util::Instrumentation::Listener.from_pson({"name" => "listener", "enabled" => true}) + Puppet::Util::Instrumentation::Listener.from_data_hash({"name" => "listener", "enabled" => true}) end end end diff --git a/spec/unit/util/log_spec.rb b/spec/unit/util/log_spec.rb index 99984246a..bc6ecfcf0 100755 --- a/spec/unit/util/log_spec.rb +++ b/spec/unit/util/log_spec.rb @@ -365,7 +365,7 @@ describe Puppet::Util::Log do it "should round trip through pson" do log = Puppet::Util::Log.new(:level => 'notice', :message => 'hooray', :file => 'thefile', :line => 1729, :source => 'specs', :tags => ['a', 'b', 'c']) - tripped = Puppet::Util::Log.from_pson(PSON.parse(log.to_pson)) + tripped = Puppet::Util::Log.from_data_hash(PSON.parse(log.to_pson)) tripped.file.should == log.file tripped.line.should == log.line diff --git a/spec/unit/util/metric_spec.rb b/spec/unit/util/metric_spec.rb index e8ffe00dd..2f0d99b6a 100755 --- a/spec/unit/util/metric_spec.rb +++ b/spec/unit/util/metric_spec.rb @@ -89,7 +89,7 @@ describe Puppet::Util::Metric do metric.newvalue("v1", 10.1, "something") metric.newvalue("v2", 20, "something else") - tripped = Puppet::Util::Metric.from_pson(PSON.parse(metric.to_pson)) + tripped = Puppet::Util::Metric.from_data_hash(PSON.parse(metric.to_pson)) tripped.name.should == metric.name tripped.label.should == metric.label diff --git a/spec/unit/util/pson_spec.rb b/spec/unit/util/pson_spec.rb index e0d79cda6..c2307498f 100755 --- a/spec/unit/util/pson_spec.rb +++ b/spec/unit/util/pson_spec.rb @@ -15,9 +15,9 @@ describe Puppet::Util::Pson do }.to raise_error(ArgumentError, /No data provided in pson data/) end - it "should call 'from_pson' with the provided data" do + it "should call 'from_data_hash' with the provided data" do pson = PsonUtil.new - pson.expects(:from_pson).with("mydata") + pson.expects(:from_data_hash).with("mydata") pson.pson_create("type" => "foo", "data" => "mydata") end diff --git a/spec/unit/util/tag_set_spec.rb b/spec/unit/util/tag_set_spec.rb index c59674fcb..5d6d36dfe 100644 --- a/spec/unit/util/tag_set_spec.rb +++ b/spec/unit/util/tag_set_spec.rb @@ -33,7 +33,7 @@ describe Puppet::Util::TagSet do array = ['a', 'b', 1, 5.4] set.merge(array) - tes = Puppet::Util::TagSet.from_pson(PSON.parse(set.to_pson)) + tes = Puppet::Util::TagSet.from_data_hash(PSON.parse(set.to_pson)) tes.should == set end From 3c892bf5f7ec8dbab47409d4650a75a39d9fc9c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Dal=C3=A9n?= Date: Thu, 30 Jan 2014 15:25:49 +0100 Subject: [PATCH 561/800] (maint) Deprecate from_pson methods They have all been replaced with from_data_hash methods instead. --- lib/puppet/file_serving/metadata.rb | 1 + lib/puppet/indirector/request.rb | 1 + lib/puppet/node.rb | 1 + lib/puppet/node/facts.rb | 1 + lib/puppet/relationship.rb | 1 + lib/puppet/resource.rb | 1 + lib/puppet/resource/catalog.rb | 1 + lib/puppet/resource/status.rb | 1 + lib/puppet/resource/type.rb | 1 + lib/puppet/run.rb | 1 + lib/puppet/ssl/host.rb | 1 + lib/puppet/status.rb | 1 + lib/puppet/transaction/event.rb | 1 + lib/puppet/transaction/report.rb | 1 + lib/puppet/util/instrumentation/data.rb | 1 + lib/puppet/util/instrumentation/indirection_probe.rb | 1 + lib/puppet/util/instrumentation/listener.rb | 1 + lib/puppet/util/log.rb | 1 + lib/puppet/util/metric.rb | 1 + lib/puppet/util/tag_set.rb | 1 + 20 files changed, 20 insertions(+) diff --git a/lib/puppet/file_serving/metadata.rb b/lib/puppet/file_serving/metadata.rb index 1fd6e6506..e88da85cf 100644 --- a/lib/puppet/file_serving/metadata.rb +++ b/lib/puppet/file_serving/metadata.rb @@ -192,6 +192,7 @@ class Puppet::FileServing::Metadata < Puppet::FileServing::Base end def self.from_pson(data) + Puppet.deprecation_warning("from_pson is being removed in favour of from_data_hash.") self.from_data_hash(data) end diff --git a/lib/puppet/indirector/request.rb b/lib/puppet/indirector/request.rb index b4c6b7380..a67753f68 100644 --- a/lib/puppet/indirector/request.rb +++ b/lib/puppet/indirector/request.rb @@ -40,6 +40,7 @@ class Puppet::Indirector::Request end def self.from_pson(json) + Puppet.deprecation_warning("from_pson is being removed in favour of from_data_hash.") self.from_data_hash(json) end diff --git a/lib/puppet/node.rb b/lib/puppet/node.rb index 468f8e764..fbb57b2dd 100644 --- a/lib/puppet/node.rb +++ b/lib/puppet/node.rb @@ -29,6 +29,7 @@ class Puppet::Node end def self.from_pson(pson) + Puppet.deprecation_warning("from_pson is being removed in favour of from_data_hash.") self.from_data_hash(pson) end diff --git a/lib/puppet/node/facts.rb b/lib/puppet/node/facts.rb index ac1b4ec76..a978a5c88 100644 --- a/lib/puppet/node/facts.rb +++ b/lib/puppet/node/facts.rb @@ -85,6 +85,7 @@ class Puppet::Node::Facts end def self.from_pson(data) + Puppet.deprecation_warning("from_pson is being removed in favour of from_data_hash.") self.from_data_hash(data) end diff --git a/lib/puppet/relationship.rb b/lib/puppet/relationship.rb index 8655ae882..d0a3e2455 100644 --- a/lib/puppet/relationship.rb +++ b/lib/puppet/relationship.rb @@ -28,6 +28,7 @@ class Puppet::Relationship end def self.from_pson(pson) + Puppet.deprecation_warning("from_pson is being removed in favour of from_data_hash.") self.from_data_hash(pson) end diff --git a/lib/puppet/resource.rb b/lib/puppet/resource.rb index 198703beb..5c088f932 100644 --- a/lib/puppet/resource.rb +++ b/lib/puppet/resource.rb @@ -50,6 +50,7 @@ class Puppet::Resource end def self.from_pson(pson) + Puppet.deprecation_warning("from_pson is being removed in favour of from_data_hash.") self.from_data_hash(pson) end diff --git a/lib/puppet/resource/catalog.rb b/lib/puppet/resource/catalog.rb index c31b3490c..47872032f 100644 --- a/lib/puppet/resource/catalog.rb +++ b/lib/puppet/resource/catalog.rb @@ -359,6 +359,7 @@ class Puppet::Resource::Catalog < Puppet::Graph::SimpleGraph end def self.from_pson(data) + Puppet.deprecation_warning("from_pson is being removed in favour of from_data_hash.") self.from_data_hash(data) end diff --git a/lib/puppet/resource/status.rb b/lib/puppet/resource/status.rb index 3d0273516..930a195a2 100644 --- a/lib/puppet/resource/status.rb +++ b/lib/puppet/resource/status.rb @@ -31,6 +31,7 @@ module Puppet end def self.from_pson(data) + Puppet.deprecation_warning("from_pson is being removed in favour of from_data_hash.") self.from_data_hash(data) end diff --git a/lib/puppet/resource/type.rb b/lib/puppet/resource/type.rb index 63727b429..c7cf50090 100644 --- a/lib/puppet/resource/type.rb +++ b/lib/puppet/resource/type.rb @@ -64,6 +64,7 @@ class Puppet::Resource::Type end def self.from_pson(data) + Puppet.deprecation_warning("from_pson is being removed in favour of from_data_hash.") self.from_data_hash(data) end diff --git a/lib/puppet/run.rb b/lib/puppet/run.rb index c5512d37a..8f7bdaa12 100644 --- a/lib/puppet/run.rb +++ b/lib/puppet/run.rb @@ -95,6 +95,7 @@ class Puppet::Run end def self.from_pson(hash) + Puppet.deprecation_warning("from_pson is being removed in favour of from_data_hash.") self.from_data_hash(hash) end diff --git a/lib/puppet/ssl/host.rb b/lib/puppet/ssl/host.rb index d66e0985c..f93522eaf 100644 --- a/lib/puppet/ssl/host.rb +++ b/lib/puppet/ssl/host.rb @@ -127,6 +127,7 @@ DOC end def self.from_pson(pson) + Puppet.deprecation_warning("from_pson is being removed in favour of from_data_hash.") self.from_data_hash(pson) end diff --git a/lib/puppet/status.rb b/lib/puppet/status.rb index 22ad5504f..f1dc0fbf6 100644 --- a/lib/puppet/status.rb +++ b/lib/puppet/status.rb @@ -27,6 +27,7 @@ class Puppet::Status end def self.from_pson(pson) + Puppet.deprecation_warning("from_pson is being removed in favour of from_data_hash.") self.from_data_hash(pson) end diff --git a/lib/puppet/transaction/event.rb b/lib/puppet/transaction/event.rb index 1ea53e4e0..dee2831ff 100644 --- a/lib/puppet/transaction/event.rb +++ b/lib/puppet/transaction/event.rb @@ -26,6 +26,7 @@ class Puppet::Transaction::Event end def self.from_pson(data) + Puppet.deprecation_warning("from_pson is being removed in favour of from_data_hash.") self.from_data_hash(data) end diff --git a/lib/puppet/transaction/report.rb b/lib/puppet/transaction/report.rb index 8acc14512..4e02ba2d6 100644 --- a/lib/puppet/transaction/report.rb +++ b/lib/puppet/transaction/report.rb @@ -104,6 +104,7 @@ class Puppet::Transaction::Report end def self.from_pson(data) + Puppet.deprecation_warning("from_pson is being removed in favour of from_data_hash.") self.from_data_hash(data) end diff --git a/lib/puppet/util/instrumentation/data.rb b/lib/puppet/util/instrumentation/data.rb index 5d06b4be2..34d978cec 100644 --- a/lib/puppet/util/instrumentation/data.rb +++ b/lib/puppet/util/instrumentation/data.rb @@ -40,6 +40,7 @@ class Puppet::Util::Instrumentation::Data end def self.from_pson(data) + Puppet.deprecation_warning("from_pson is being removed in favour of from_data_hash.") data end end diff --git a/lib/puppet/util/instrumentation/indirection_probe.rb b/lib/puppet/util/instrumentation/indirection_probe.rb index c4817a1b6..afaccd303 100644 --- a/lib/puppet/util/instrumentation/indirection_probe.rb +++ b/lib/puppet/util/instrumentation/indirection_probe.rb @@ -35,6 +35,7 @@ class Puppet::Util::Instrumentation::IndirectionProbe end def self.from_pson(data) + Puppet.deprecation_warning("from_pson is being removed in favour of from_data_hash.") self.from_data_hash(data) end end diff --git a/lib/puppet/util/instrumentation/listener.rb b/lib/puppet/util/instrumentation/listener.rb index 9baa15288..cb25b2925 100644 --- a/lib/puppet/util/instrumentation/listener.rb +++ b/lib/puppet/util/instrumentation/listener.rb @@ -66,6 +66,7 @@ class Puppet::Util::Instrumentation::Listener end def self.from_pson(data) + Puppet.deprecation_warning("from_pson is being removed in favour of from_data_hash.") self.from_data_hash(data) end end diff --git a/lib/puppet/util/log.rb b/lib/puppet/util/log.rb index 79414a8f0..05f0b3fad 100644 --- a/lib/puppet/util/log.rb +++ b/lib/puppet/util/log.rb @@ -240,6 +240,7 @@ class Puppet::Util::Log end def self.from_pson(data) + Puppet.deprecation_warning("from_pson is being removed in favour of from_data_hash.") self.from_data_hash(data) end diff --git a/lib/puppet/util/metric.rb b/lib/puppet/util/metric.rb index 6e64ee098..4173fded6 100644 --- a/lib/puppet/util/metric.rb +++ b/lib/puppet/util/metric.rb @@ -18,6 +18,7 @@ class Puppet::Util::Metric end def self.from_pson(data) + Puppet.deprecation_warning("from_pson is being removed in favour of from_data_hash.") self.from_data_hash(data) end diff --git a/lib/puppet/util/tag_set.rb b/lib/puppet/util/tag_set.rb index 7c92cd5dc..6f83ff870 100644 --- a/lib/puppet/util/tag_set.rb +++ b/lib/puppet/util/tag_set.rb @@ -17,6 +17,7 @@ class Puppet::Util::TagSet < Set end def self.from_pson(data) + Puppet.deprecation_warning("from_pson is being removed in favour of from_data_hash.") self.from_data_hash(data) end From 346bebe994219a865e09792f1ca8733302a71c9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Dal=C3=A9n?= Date: Thu, 30 Jan 2014 15:34:51 +0100 Subject: [PATCH 562/800] (maint) Unify to_pson in FormatSupport module, just like with to_msgpack This removes all unnecessary to_pson definitions and creates a common one in the Puppet::Network::FormatSupport module instead just like with to_msgpack. There are still some to_pson definitions left but that is because they also add a document type structure. That cannot be removed until the next major version as it would change the on wire format. --- lib/puppet/module_tool/dependency.rb | 7 ++----- lib/puppet/module_tool/metadata.rb | 7 ++----- lib/puppet/network/format_support.rb | 4 ++++ lib/puppet/node/facts.rb | 4 ---- lib/puppet/resource/status.rb | 4 ---- lib/puppet/resource/type.rb | 4 ---- lib/puppet/run.rb | 4 ---- lib/puppet/ssl/host.rb | 4 ---- lib/puppet/status.rb | 4 ---- lib/puppet/transaction/event.rb | 4 ---- lib/puppet/transaction/report.rb | 4 ---- 11 files changed, 8 insertions(+), 42 deletions(-) diff --git a/lib/puppet/module_tool/dependency.rb b/lib/puppet/module_tool/dependency.rb index 777d9cdc4..7535a17d9 100644 --- a/lib/puppet/module_tool/dependency.rb +++ b/lib/puppet/module_tool/dependency.rb @@ -1,8 +1,10 @@ require 'puppet/module_tool' +require 'puppet/network/format_support' module Puppet::ModuleTool class Dependency + include Puppet::Network::FormatSupport attr_reader :full_module_name, :username, :name, :version_requirement, :repository @@ -23,10 +25,5 @@ module Puppet::ModuleTool result[:repository] = @repository.to_s if @repository && ! @repository.nil? result end - - # Return PSON representation of this data. - def to_pson(*args) - to_data_hash.to_pson(*args) - end end end diff --git a/lib/puppet/module_tool/metadata.rb b/lib/puppet/module_tool/metadata.rb index b58038213..bc3ccb376 100644 --- a/lib/puppet/module_tool/metadata.rb +++ b/lib/puppet/module_tool/metadata.rb @@ -1,5 +1,6 @@ require 'puppet/util/methodhelper' require 'puppet/module_tool' +require 'puppet/network/format_support' module Puppet::ModuleTool # = Metadata @@ -9,6 +10,7 @@ module Puppet::ModuleTool # +annotate+ methods in other classes. class Metadata include Puppet::Util::MethodHelper + include Puppet::Network::FormatSupport # The full name of the module, which is a dash-separated combination of the # +username+ and module +name+. @@ -149,10 +151,5 @@ module Puppet::ModuleTool def to_hash() to_data_hash end - - # Return the PSON record representing this instance. - def to_pson(*args) - return to_data_hash.to_pson(*args) - end end end diff --git a/lib/puppet/network/format_support.rb b/lib/puppet/network/format_support.rb index 79f7fe665..6a42fd4d7 100644 --- a/lib/puppet/network/format_support.rb +++ b/lib/puppet/network/format_support.rb @@ -88,6 +88,10 @@ module Puppet::Network::FormatSupport to_data_hash.to_msgpack(*args) end + def to_pson(*args) + to_data_hash.to_pson(*args) + end + def render(format = nil) format ||= self.class.default_format diff --git a/lib/puppet/node/facts.rb b/lib/puppet/node/facts.rb index a978a5c88..be9871377 100644 --- a/lib/puppet/node/facts.rb +++ b/lib/puppet/node/facts.rb @@ -114,10 +114,6 @@ class Puppet::Node::Facts result end - def to_pson(*args) - to_data_hash.to_pson(*args) - end - # Add internal data to the facts for storage. def add_timestamp self.timestamp = Time.now diff --git a/lib/puppet/resource/status.rb b/lib/puppet/resource/status.rb index 930a195a2..c923c6fad 100644 --- a/lib/puppet/resource/status.rb +++ b/lib/puppet/resource/status.rb @@ -141,10 +141,6 @@ module Puppet } end - def to_pson(*args) - to_data_hash.to_pson(*args) - end - def to_yaml_properties YAML_ATTRIBUTES & super end diff --git a/lib/puppet/resource/type.rb b/lib/puppet/resource/type.rb index c7cf50090..27884df9f 100644 --- a/lib/puppet/resource/type.rb +++ b/lib/puppet/resource/type.rb @@ -68,10 +68,6 @@ class Puppet::Resource::Type self.from_data_hash(data) end - def to_pson(*args) - to_data_hash.to_pson(*args) - end - def to_data_hash data = [:doc, :line, :file, :parent].inject({}) do |hash, param| next hash unless (value = self.send(param)) and (value != "") diff --git a/lib/puppet/run.rb b/lib/puppet/run.rb index 8f7bdaa12..b94fb7756 100644 --- a/lib/puppet/run.rb +++ b/lib/puppet/run.rb @@ -106,8 +106,4 @@ class Puppet::Run :status => @status } end - - def to_pson(*args) - to_data_hash.to_pson(*args) - end end diff --git a/lib/puppet/ssl/host.rb b/lib/puppet/ssl/host.rb index f93522eaf..e78ca9a61 100644 --- a/lib/puppet/ssl/host.rb +++ b/lib/puppet/ssl/host.rb @@ -312,10 +312,6 @@ ERROR_STRING result end - def to_pson(*args) - to_data_hash.to_pson(*args) - end - # eventually we'll probably want to move this somewhere else or make it # configurable # --jeffweiss 29 aug 2012 diff --git a/lib/puppet/status.rb b/lib/puppet/status.rb index f1dc0fbf6..7fe048146 100644 --- a/lib/puppet/status.rb +++ b/lib/puppet/status.rb @@ -14,10 +14,6 @@ class Puppet::Status @status end - def to_pson(*args) - @status.to_pson - end - def self.from_data_hash(data) if data.include?('status') self.new(data['status']) diff --git a/lib/puppet/transaction/event.rb b/lib/puppet/transaction/event.rb index dee2831ff..fc635a708 100644 --- a/lib/puppet/transaction/event.rb +++ b/lib/puppet/transaction/event.rb @@ -64,10 +64,6 @@ class Puppet::Transaction::Event } end - def to_pson(*args) - to_data_hash.to_pson(*args) - end - def property=(prop) @property = prop.to_s end diff --git a/lib/puppet/transaction/report.rb b/lib/puppet/transaction/report.rb index 4e02ba2d6..05306547e 100644 --- a/lib/puppet/transaction/report.rb +++ b/lib/puppet/transaction/report.rb @@ -238,10 +238,6 @@ class Puppet::Transaction::Report } end - def to_pson - to_data_hash.to_pson - end - # @return [String] the host name # @api public # From faa3978932a8b5c8e652ab48b4277a80751168aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Dal=C3=A9n?= Date: Thu, 30 Jan 2014 16:04:16 +0100 Subject: [PATCH 563/800] (maint) Simplify msgpack format code --- lib/puppet/network/formats.rb | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/lib/puppet/network/formats.rb b/lib/puppet/network/formats.rb index 5b218e611..e636b30e8 100644 --- a/lib/puppet/network/formats.rb +++ b/lib/puppet/network/formats.rb @@ -1,6 +1,9 @@ require 'puppet/network/format_handler' -Puppet::Network::FormatHandler.create_serialized_formats(:msgpack, :weight => 20, :mime => "application/x-msgpack", :required_methods => [:render_method, :intern_method]) do +Puppet::Network::FormatHandler.create_serialized_formats(:msgpack, :weight => 20, :mime => "application/x-msgpack", :required_methods => [:render_method, :intern_method], :intern_method => :from_data_hash) do + + confine :feature => :msgpack + def intern(klass, text) data = MessagePack.unpack(text) return data if data.is_a?(klass) @@ -13,17 +16,9 @@ Puppet::Network::FormatHandler.create_serialized_formats(:msgpack, :weight => 20 end end - def render(instance) - instance.to_msgpack - end - def render_multiple(instances) instances.to_msgpack end - - def supported?(klass) - Puppet.features.msgpack? && klass.method_defined?(:to_msgpack) && klass.method_defined?(:from_data_hash) - end end Puppet::Network::FormatHandler.create_serialized_formats(:yaml) do From df159e243d09f3e92a4793c44ea53affe78104ed Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 6 Feb 2014 17:09:25 +0100 Subject: [PATCH 564/800] (PUP-1029) Add Enumeration - common place to produce an Enumerator. The support for creating an Enumerator was in different places (in each of the iterative functions, and in the type calculator). This commit break out the behavior to a common class; Enumeration. --- lib/puppet/pops.rb | 1 + lib/puppet/pops/types/enumeration.rb | 32 +++++++++++++++ spec/unit/pops/types/enumeration_spec.rb | 52 ++++++++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 lib/puppet/pops/types/enumeration.rb create mode 100644 spec/unit/pops/types/enumeration_spec.rb diff --git a/lib/puppet/pops.rb b/lib/puppet/pops.rb index 54387d6a2..c075be013 100644 --- a/lib/puppet/pops.rb +++ b/lib/puppet/pops.rb @@ -25,6 +25,7 @@ module Puppet require 'puppet/pops/types/type_factory' require 'puppet/pops/types/type_parser' require 'puppet/pops/types/class_loader' + require 'puppet/pops/types/enumeration' end module Model diff --git a/lib/puppet/pops/types/enumeration.rb b/lib/puppet/pops/types/enumeration.rb new file mode 100644 index 000000000..183594fe7 --- /dev/null +++ b/lib/puppet/pops/types/enumeration.rb @@ -0,0 +1,32 @@ +# The Enumeration class provides default Enumerable::Enumerator creation for Puppet Programming Language +# runtime objects that supports the concept of enumeration. +# +class Puppet::Pops::Types::Enumeration + # Produces an Enumerable::Enumerator for Array, Hash, Integer, Integer Range, and String. + # + def self.enumerator(o) + @@singleton ||= new + @@singleton.enumerator(o) + end + + # Produces an Enumerable::Enumerator for Array, Hash, Integer, Integer Range, and String. + # + def enumerator(o) + case o + when String + o.chars + when Integer + o.times + when Array + o.each + when Hash + o.each + when Puppet::Pops::Types::PIntegerType + # Not enumerable if representing an infinite range + return nil if o.to.nil? || o.from.nil? + o.each + else + nil + end + end +end \ No newline at end of file diff --git a/spec/unit/pops/types/enumeration_spec.rb b/spec/unit/pops/types/enumeration_spec.rb new file mode 100644 index 000000000..501bbc3f5 --- /dev/null +++ b/spec/unit/pops/types/enumeration_spec.rb @@ -0,0 +1,52 @@ +require 'spec_helper' +require 'puppet/pops' + +describe 'The enumeration support' do + it 'produces an enumerator for Array' do + expect(Puppet::Pops::Types::Enumeration.enumerator([1,2,3]).is_a?(Enumerable::Enumerator)).to eql(true) + end + + it 'produces an enumerator for Hash' do + expect(Puppet::Pops::Types::Enumeration.enumerator({:a=>1}).is_a?(Enumerable::Enumerator)).to eql(true) + end + + it 'produces a char enumerator for String' do + enum = Puppet::Pops::Types::Enumeration.enumerator("abc") + expect(enum.is_a?(Enumerable::Enumerator)).to eql(true) + expect(enum.next).to eql('a') + end + + it 'produces an enumerator for integer times' do + enum = Puppet::Pops::Types::Enumeration.enumerator(2) + expect(enum.is_a?(Enumerable::Enumerator)).to eql(true) + expect(enum.next).to eql(0) + expect(enum.next).to eql(1) + expect{enum.next}.to raise_error(StopIteration) + end + + it 'produces an enumerator for Integer range' do + range = Puppet::Pops::Types::TypeFactory.range(1,2) + enum = Puppet::Pops::Types::Enumeration.enumerator(range) + expect(enum.is_a?(Enumerable::Enumerator)).to eql(true) + expect(enum.next).to eql(1) + expect(enum.next).to eql(2) + expect{enum.next}.to raise_error(StopIteration) + end + + it 'does not produce an enumerator for infinite Integer range' do + range = Puppet::Pops::Types::TypeFactory.range(1,:default) + enum = Puppet::Pops::Types::Enumeration.enumerator(range) + expect(enum).to be_nil + range = Puppet::Pops::Types::TypeFactory.range(:default,2) + enum = Puppet::Pops::Types::Enumeration.enumerator(range) + expect(enum).to be_nil + end + + [3.14, /.*/, true, false, nil, :something].each do |x| + it "does not produce an enumerator for object of type #{x.class}" do + enum = Puppet::Pops::Types::Enumeration.enumerator(x) + expect(enum).to be_nil + end + end + +end From 70eb5d95477f7a11f6375733cb7be45cca0e66b1 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 6 Feb 2014 17:35:26 +0100 Subject: [PATCH 565/800] (PUP-1029) Make filter function use common Enumeration support. This makes the filter function use the common Enumeration support and makes it consistent with how all iterative functions should behave. The function's documentation is also updated. --- lib/puppet/parser/functions/filter.rb | 75 +++++++++++++------------ spec/unit/parser/methods/filter_spec.rb | 30 ++++++++++ spec/unit/parser/methods/shared.rb | 20 +------ 3 files changed, 70 insertions(+), 55 deletions(-) diff --git a/lib/puppet/parser/functions/filter.rb b/lib/puppet/parser/functions/filter.rb index 6377153ce..70ab024de 100644 --- a/lib/puppet/parser/functions/filter.rb +++ b/lib/puppet/parser/functions/filter.rb @@ -7,54 +7,43 @@ Puppet::Parser::Functions::newfunction( :doc => <<-'ENDHEREDOC') do |args| Applies a parameterized block to each element in a sequence of entries from the first argument and returns an array or hash (same type as left operand for array/hash, and array for - other enumerable types) with the entries for which the block evaluates to true. + other enumerable types) with the entries for which the block evaluates to `true`. - This function takes two mandatory arguments: the first should be an Array, a Hash, or Enumerable Type, + This function takes two mandatory arguments: the first should be an Array, a Hash, or an + Enumerable object (integer, Integer range, or String), and the second a parameterized block as produced by the puppet syntax: $a.filter |$x| { ... } + filter($a) |$x| { ... } - When the first argument is an Array, the block is called with each entry in turn. When the first argument - is a Hash the entry is an array with `[key, value]`. - - The returned filtered object is of the same type as the receiver. + When the first argument is something other than a Hash, the block is called with each entry in turn. + When the first argument is a Hash the entry is an array with `[key, value]`. *Examples* # selects all that end with berry $a = ["raspberry", "blueberry", "orange"] - $a.filter |$x| { $x =~ /berry$/ } + $a.filter |$x| { $x =~ /berry$/ } # rasberry, blueberry - If the first argument is a Type that is enumerable, the second should be a parameterized block on one - of the two forms below. + If the the block defines two parameters, they will be set to `index, value` (with index starting at 0) for all + enumerables except Hash, and to `key, value` for a Hash. *Examples* - Integer[1,10].filter |$x| { ... } - Integer[1,10].filter |$index, $x| { ... } + # selects all that end with 'berry' at an even numbered index + $a = ["raspberry", "blueberry", "orange"] + $a.filter |$index, $x| { $index % 2 == 0 and $x =~ /berry$/ } # raspberry - The first form will pass each element from the enumeration to the block, and the second will pass the index - and value for each element. The index always starts from 0. + # selects all that end with 'berry' and value >= 1 + $a = {"raspberry"=>0, "blueberry"=>1, "orange"=>1} + $a.filter |$key, $x| { $x =~ /berry$/ and $x >= 1 } # blueberry - - Since 3.4 + - Since 3.4 for Array and Hash + - Since 3.5 for other enumerables - requires `parser = future` ENDHEREDOC - def filter_Type(o, scope, pblock) - return nil unless pblock - tc = Puppet::Pops::Types::TypeCalculator.new() - enumerable = tc.enumerable(o) - if enumerable.nil? - raise ArgumentError, ("filter(): given type '#{tc.string(o)}' is not enumerable") - end - serving_size = pblock.parameter_count - if serving_size == 0 - raise ArgumentError, "Block must define at least one parameter; value." - end - if serving_size > 2 - raise ArgumentError, "Block must define at most two parameters; index, value" - end - enumerator = enumerable.each + def filter_Enumerator(enumerator, scope, pblock, serving_size) result = [] index = 0 if serving_size == 1 @@ -77,21 +66,33 @@ Puppet::Parser::Functions::newfunction( receiver = args[0] pblock = args[1] - raise ArgumentError, ("reject(): wrong argument type (#{pblock.class}; must be a parameterized block.") unless pblock.respond_to?(:puppet_lambda) + raise ArgumentError, ("filter(): wrong argument type (#{pblock.class}; must be a parameterized block.") unless pblock.respond_to?(:puppet_lambda) + serving_size = pblock.parameter_count + if serving_size == 0 + raise ArgumentError, "filter(): block must define at least one parameter; value." + end case receiver - when Array - receiver.select {|x| pblock.call(self, x) } when Hash - result = receiver.select {|x, y| pblock.call(self, [x, y]) } + if serving_size > 2 + raise ArgumentError, "filter(): block must define at most two parameters; key, value" + end + if serving_size == 1 + result = receiver.select {|x, y| pblock.call(self, [x, y]) } + else + result = receiver.select {|x, y| pblock.call(self, x, y) } + end # Ruby 1.8.7 returns Array result = Hash[result] unless result.is_a? Hash result else - if receiver.is_a?(Puppet::Pops::Types::PAbstractType) - filter_Type(receiver, self, pblock) - else - raise ArgumentError, ("filter(): wrong argument type (#{receiver.class}; must be an Array or a Hash.") + if serving_size > 2 + raise ArgumentError, "filter(): block must define at most two parameters; index, value" end + enum = Puppet::Pops::Types::Enumeration.enumerator(receiver) + unless enum + raise ArgumentError, ("filter(): wrong argument type (#{receiver.class}; must be something enumerable.") + end + filter_Enumerator(enum, self, pblock, serving_size) end end diff --git a/spec/unit/parser/methods/filter_spec.rb b/spec/unit/parser/methods/filter_spec.rb index 7293d505c..c9fed31d2 100644 --- a/spec/unit/parser/methods/filter_spec.rb +++ b/spec/unit/parser/methods/filter_spec.rb @@ -61,6 +61,18 @@ describe 'the filter method' do catalog.resource(:file, "/file_blueberry")['ensure'].should == 'present' end + it 'can filter array using index and value' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = ['strawberry','blueberry','orange'] + $b = $a.filter |$index, $x|{ $index == 0 or $index ==2} + file { "/file_${b[0]}": ensure => present } + file { "/file_${b[1]}": ensure => present } + MANIFEST + + catalog.resource(:file, "/file_strawberry")['ensure'].should == 'present' + catalog.resource(:file, "/file_orange")['ensure'].should == 'present' + end + it 'filters on a hash (all berries) by key' do catalog = compile_to_catalog(<<-MANIFEST) $a = {'strawberry'=>'red','blueberry'=>'blue','orange'=>'orange'} @@ -100,6 +112,24 @@ describe 'the filter method' do catalog.resource(:file, "/file_blueb")['ensure'].should == 'present' end + context 'filter checks arguments and' do + it 'raises an error when block has more than 2 argument' do + expect do + compile_to_catalog(<<-MANIFEST) + [1].filter |$indexm, $x, $yikes|{ } + MANIFEST + end.to raise_error(Puppet::Error, /block must define at most two parameters/) + end + + it 'raises an error when block has fewer than 1 argument' do + expect do + compile_to_catalog(<<-MANIFEST) + [1].filter || { } + MANIFEST + end.to raise_error(Puppet::Error, /block must define at least one parameter/) + end + end + it_should_behave_like 'all iterative functions argument checks', 'filter' it_should_behave_like 'all iterative functions hash handling', 'filter' end diff --git a/spec/unit/parser/methods/shared.rb b/spec/unit/parser/methods/shared.rb index b06cccf5a..42cfd2359 100644 --- a/spec/unit/parser/methods/shared.rb +++ b/spec/unit/parser/methods/shared.rb @@ -11,28 +11,12 @@ end shared_examples_for 'all iterative functions argument checks' do |func| - it 'raises an error when defined with more than 1 argument' do - expect do - compile_to_catalog(<<-MANIFEST) - [1].#{func} |$x, $yikes|{ } - MANIFEST - end.to raise_error(Puppet::Error, /Too few arguments/) - end - - it 'raises an error when defined with fewer than 1 argument' do - expect do - compile_to_catalog(<<-MANIFEST) - [1].#{func} || { } - MANIFEST - end.to raise_error(Puppet::Error, /Too many arguments/) - end - it 'raises an error when used against an unsupported type' do expect do compile_to_catalog(<<-MANIFEST) - "not correct".#{func} |$v| { } + 3.14.#{func} |$v| { } MANIFEST - end.to raise_error(Puppet::Error, /must be an Array.*Hash/) + end.to raise_error(Puppet::Error, /must be something enumerable/) end it 'raises an error when called with any parameters besides a block' do From 720059c66d5a29a65f492e66d514d28e967d3442 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 6 Feb 2014 17:40:41 +0100 Subject: [PATCH 566/800] (PUP-1029) Make map function use common Enumeration support. This makes the map function use the common Enumeration support. --- lib/puppet/parser/functions/map.rb | 30 ++++++++++------------------ spec/unit/parser/methods/map_spec.rb | 19 ++++++++++++++++++ 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/lib/puppet/parser/functions/map.rb b/lib/puppet/parser/functions/map.rb index c67107f40..0d61526bc 100644 --- a/lib/puppet/parser/functions/map.rb +++ b/lib/puppet/parser/functions/map.rb @@ -8,12 +8,13 @@ Puppet::Parser::Functions::newfunction( Applies a parameterized block to each element in a sequence of entries from the first argument and returns an array with the result of each invocation of the parameterized block. - This function takes two mandatory arguments: the first should be an Array, Hash, or enumerable type, - and the second a parameterized block as produced by the puppet syntax: + This function takes two mandatory arguments: the first should be an Array, Hash, or of Enumerable type + (integer, Integer range, or String), and the second a parameterized block as produced by the puppet syntax: $a.map |$x| { ... } + map($a) |$x| { ... } - When the first argument `$a` is an Array or enumerable type, the block is called with each entry in turn. + When the first argument `$a` is an Array or of enumerable type, the block is called with each entry in turn. When the first argument is a hash the entry is an array with `[key, value]`. *Examples* @@ -24,8 +25,9 @@ Puppet::Parser::Functions::newfunction( # Turns hash into array of keys $a.map |$x| { $x[0] } - - Since 3.4 - - requires `parser = future`. + - Since 3.4 for Array and Hash + - Since 3.5 for other enumerables + - requires `parser = future` ENDHEREDOC receiver = args[0] @@ -33,19 +35,9 @@ Puppet::Parser::Functions::newfunction( raise ArgumentError, ("map(): wrong argument type (#{pblock.class}; must be a parameterized block.") unless pblock.respond_to?(:puppet_lambda) - case receiver - when Array - when Hash - when Puppet::Pops::Types::PAbstractType - tc = Puppet::Pops::Types::TypeCalculator.new() - enumerable = tc.enumerable(receiver) - if enumerable.nil? - raise ArgumentError, ("map(): given type '#{tc.string(receiver)}' is not enumerable") - end - receiver = enumerable.each - else - raise ArgumentError, ("map(): wrong argument type (#{receiver.class}; must be an Array, Hash, or enumerable type.") + enum = Puppet::Pops::Types::Enumeration.enumerator(receiver) + unless enum + raise ArgumentError, ("map(): wrong argument type (#{receiver.class}; must be something enumerable.") end - - receiver.to_a.map {|x| pblock.call(self, x) } + enum.map {|x| pblock.call(self, x) } end diff --git a/spec/unit/parser/methods/map_spec.rb b/spec/unit/parser/methods/map_spec.rb index 4fd120a32..306509ffa 100644 --- a/spec/unit/parser/methods/map_spec.rb +++ b/spec/unit/parser/methods/map_spec.rb @@ -101,6 +101,25 @@ describe 'the map method' do catalog.resource(:file, "/file_0.something")['ensure'].should == 'present' end end + + context 'map checks arguments and' do + it 'raises an error when block has more than 1 argument' do + expect do + compile_to_catalog(<<-MANIFEST) + [1].map |$x, $yikes|{ } + MANIFEST + end.to raise_error(Puppet::Error, /Too few arguments/) + end + + it 'raises an error when block has fewer than 1 argument' do + expect do + compile_to_catalog(<<-MANIFEST) + [1].map || { } + MANIFEST + end.to raise_error(Puppet::Error, /Too many arguments/) + end + end + it_should_behave_like 'all iterative functions argument checks', 'map' it_should_behave_like 'all iterative functions hash handling', 'map' end From fef9f0137c8116ffaea1651a6262641208540493 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 6 Feb 2014 17:48:47 +0100 Subject: [PATCH 567/800] (PUP-1029) Make each function use the Enumeration support. This makes the each function use the common Enumeration support. Documentation is also updated. --- lib/puppet/parser/functions/each.rb | 113 ++++++++++------------------ 1 file changed, 40 insertions(+), 73 deletions(-) diff --git a/lib/puppet/parser/functions/each.rb b/lib/puppet/parser/functions/each.rb index 3799f18ea..f4de4a155 100644 --- a/lib/puppet/parser/functions/each.rb +++ b/lib/puppet/parser/functions/each.rb @@ -6,17 +6,21 @@ Puppet::Parser::Functions::newfunction( Applies a parameterized block to each element in a sequence of selected entries from the first argument and returns the first argument. - This function takes two mandatory arguments: the first should be an Array or a Hash, and the second + This function takes two mandatory arguments: the first should be an Array or a Hash or something that is + of enumerable type (integer, Integer range, or String), and the second a parameterized block as produced by the puppet syntax: $a.each |$x| { ... } + each($a) |$x| { ... } - When the first argument is an Array, the parameterized block should define one or two block parameters. + When the first argument is an Array (or of enumerable type other than Hash), the parameterized block + should define one or two block parameters. For each application of the block, the next element from the array is selected, and it is passed to the block if the block has one parameter. If the block has two parameters, the first is the elements index, and the second the value. The index starts from 0. $a.each |$index, $value| { ... } + each($a) |$index, $value| { ... } When the first argument is a Hash, the parameterized block should define one or two parameters. When one parameter is defined, the iteration is performed with each entry as an array of `[key, value]`, @@ -25,55 +29,23 @@ Puppet::Parser::Functions::newfunction( $a.each |$entry| { ..."key ${$entry[0]}, value ${$entry[1]}" } $a.each |$key, $value| { ..."key ${key}, value ${value}" } - When the first argument is a Type and the type is Enumerable, the parameterized block should define one - or two block parameters. - For each application of the block, the next element from the type enumerator is selected, and it is passed to - the block if the block has one parameter. If the block has two parameters, the first is the elements - index, and the second the value. The index starts from 0. + *Examples* - Integer[ 10, 20 ].each |$index, $value| { ... } + [1,2,3].each |$val| { ... } # 1, 2, 3 + [5,6,7].each |$index, $val| { ... } # (0, 5), (1, 6), (2, 7) + {a=>1, b=>2, c=>3}].each |$val| { ... } # ['a', 1], ['b', 2], ['c', 3] + {a=>1, b=>2, c=>3}.each |$key, $val| { ... } # ('a', 1), ('b', 2), ('c', 3) + Integer[ 10, 20 ].each |$index, $value| { ... } # (0, 10), (1, 11) ... + "hello".each |$char| { ... } # 'h', 'e', 'l', 'l', 'o' + 3.each |$number| { ... } # 0, 1, 2 - - Since 3.2 + - Since 3.2 for Array and Hash + - Since 3.5 for other enumerables - requires `parser = future`. ENDHEREDOC require 'puppet/parser/ast/lambda' - def foreach_Array(o, scope, pblock) - return nil unless pblock - - serving_size = pblock.parameter_count - if serving_size == 0 - raise ArgumentError, "Block must define at least one parameter; value." - end - if serving_size > 2 - raise ArgumentError, "Block must define at most two parameters; index, value" - end - enumerator = o.each - index = 0 - if serving_size == 1 - (o.size).times do - pblock.call(scope, enumerator.next) - end - else - (o.size).times do - pblock.call(scope, index, enumerator.next) - index = index +1 - end - end - o - end - - def foreach_Hash(o, scope, pblock) - return nil unless pblock - serving_size = pblock.parameter_count - case serving_size - when 0 - raise ArgumentError, "Block must define at least one parameter (for hash entry key)." - when 1 - when 2 - else - raise ArgumentError, "Block must define at most two parameters (for hash entry key and value)." - end + def foreach_Hash(o, scope, pblock, serving_size) enumerator = o.each_pair if serving_size == 1 (o.size).times do @@ -84,24 +56,9 @@ Puppet::Parser::Functions::newfunction( pblock.call(scope, *enumerator.next) end end - o end - def foreach_Type(o, scope, pblock) - return nil unless pblock - tc = Puppet::Pops::Types::TypeCalculator.new() - enumerable = tc.enumerable(o) - if enumerable.nil? - raise ArgumentError, ("each(): given type '#{tc.string(o)}' is not enumerable") - end - serving_size = pblock.parameter_count - if serving_size == 0 - raise ArgumentError, "Block must define at least one parameter; value." - end - if serving_size > 2 - raise ArgumentError, "Block must define at most two parameters; index, value" - end - enumerator = enumerable.each + def foreach_Enumerator(enumerator, scope, pblock, serving_size) index = 0 if serving_size == 1 begin @@ -117,7 +74,6 @@ Puppet::Parser::Functions::newfunction( rescue StopIteration end end - o end raise ArgumentError, ("each(): wrong number of arguments (#{args.length}; must be 2)") if args.length != 2 @@ -125,16 +81,27 @@ Puppet::Parser::Functions::newfunction( pblock = args[1] raise ArgumentError, ("each(): wrong argument type (#{args[1].class}; must be a parameterized block.") unless pblock.respond_to?(:puppet_lambda) - case receiver - when Array - foreach_Array(receiver, self, pblock) - when Hash - foreach_Hash(receiver, self, pblock) - else - if receiver.is_a?(Puppet::Pops::Types::PAbstractType) - foreach_Type(receiver, self, pblock) - else - raise ArgumentError, ("each(): wrong argument type (#{args[0].class}; must be an Array, Hash, or Enumerable Type.") - end + serving_size = pblock.parameter_count + if serving_size == 0 + raise ArgumentError, "each(): block must define at least one parameter; value." end + + case receiver + when Hash + if serving_size > 2 + raise ArgumentError, "each(): block must define at most two parameters; key, value" + end + foreach_Hash(receiver, self, pblock, serving_size) + else + if serving_size > 2 + raise ArgumentError, "each(): block must define at most two parameters; index, value" + end + enum = Puppet::Pops::Types::Enumeration.enumerator(receiver) + unless enum + raise ArgumentError, ("each(): wrong argument type (#{receiver.class}; must be something enumerable.") + end + foreach_Enumerator(enum, self, pblock, serving_size) + end + # each always produces the receiver + receiver end From 0ef996bd0115041eefa4195abe2723d2b78897f4 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 6 Feb 2014 18:00:50 +0100 Subject: [PATCH 568/800] (PUP-1029) Make reduce functon use common Enumeration support. This makes the reduce function use the common Enumeration support. Documentation is updated. --- lib/puppet/parser/functions/reduce.rb | 46 +++++++++++++-------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/lib/puppet/parser/functions/reduce.rb b/lib/puppet/parser/functions/reduce.rb index 5dd454a3e..97632b676 100644 --- a/lib/puppet/parser/functions/reduce.rb +++ b/lib/puppet/parser/functions/reduce.rb @@ -4,23 +4,27 @@ Puppet::Parser::Functions::newfunction( :arity => -2, :doc => <<-'ENDHEREDOC') do |args| Applies a parameterized block to each element in a sequence of entries from the first - argument (_the collection_) and returns the last result of the invocation of the parameterized block. + argument (_the enumerable_) and returns the last result of the invocation of the parameterized block. - This function takes two mandatory arguments: the first should be an Array, Hash, or enumerable type, and - the last a parameterized block as produced by the puppet syntax: + This function takes two mandatory arguments: the first should be an Array, Hash, or something of + enumerable type, and the last a parameterized block as produced by the puppet syntax: $a.reduce |$memo, $x| { ... } + reduce($a) |$memo, $x| { ... } - When the first argument is an Array or an enumerable type, the block is called with each entry in turn. + When the first argument is an Array or someting of an enumerable type, the block is called with each entry in turn. When the first argument is a hash each entry is converted to an array with `[key, value]` before being fed to the block. An optional 'start memo' value may be supplied as an argument between the array/hash and mandatory block. + $a.reduce(start) |$memo, $x| { ... } + reduce($a, start) |$memo, $x| { ... } + If no 'start memo' is given, the first invocation of the parameterized block will be given the first and second - elements of the collection, and if the collection has fewer than 2 elements, the first + elements of the enumeration, and if the enumerable has fewer than 2 elements, the first element is produced as the result of the reduction without invocation of the block. - On each subsequent invocations, the produced value of the invoked parameterized block is given as the memo in the + On each subsequent invocation, the produced value of the invoked parameterized block is given as the memo in the next invocation. *Examples* @@ -35,6 +39,10 @@ Puppet::Parser::Functions::newfunction( $a.reduce |$memo, $entry| { [sum, $memo[1]+$entry[1]] } #=> [sum, 6] + # reverse a string + "abc".reduce |$memo, $char| { "$char$memo" } + #=>"cbe" + It is possible to provide a starting 'memo' as an argument. *Examples* @@ -54,21 +62,13 @@ Puppet::Parser::Functions::newfunction( Integer[1,4].reduce |$memo, $x| { $memo + $x } #=> 10 - - Since 3.2 + - Since 3.2 for Array and Hash + - Since 3.5 for additional enumerable types - requires `parser = future`. ENDHEREDOC require 'puppet/parser/ast/lambda' - def enumerable_Type(o) - tc = Puppet::Pops::Types::TypeCalculator.new() - enumerable = tc.enumerable(o) - if enumerable.nil? - raise ArgumentError, ("reduce(): given type '#{tc.string(o)}' is not enumerable") - end - enumerable.each - end - case args.length when 2 pblock = args[1] @@ -81,16 +81,14 @@ Puppet::Parser::Functions::newfunction( raise ArgumentError, ("reduce(): wrong argument type (#{pblock.class}; must be a parameterized block.") end receiver = args[0] - case receiver - when Array, Hash - when Puppet::Pops::Types::PAbstractType - receiver = enumerable_Type(receiver) - else - raise ArgumentError, ("reduce(): wrong argument type (#{args[0].class}; must be an Array, Hash, or enumerable type.") + enum = Puppet::Pops::Types::Enumeration.enumerator(receiver) + unless enum + raise ArgumentError, ("reduce(): wrong argument type (#{receiver.class}; must be something enumerable.") end + if args.length == 3 - receiver.reduce(args[1]) {|memo, x| pblock.call(self, memo, x) } + enum.reduce(args[1]) {|memo, x| pblock.call(self, memo, x) } else - receiver.reduce {|memo, x| pblock.call(self, memo, x) } + enum.reduce {|memo, x| pblock.call(self, memo, x) } end end From fb6c03b6ae85883375c9d691c1f2b41e82dc3668 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Dal=C3=A9n?= Date: Tue, 4 Feb 2014 20:46:23 +0100 Subject: [PATCH 569/800] (PUP-1588) Add msgpack indirector terminii Adds indirector terminii for catalog, report and node. These are a lot faster to store than the PSON or YAML equivalents. --- lib/puppet/defaults.rb | 4 +- lib/puppet/indirector/catalog/msgpack.rb | 6 + lib/puppet/indirector/msgpack.rb | 82 ++++++++ lib/puppet/indirector/node/msgpack.rb | 7 + lib/puppet/indirector/report/msgpack.rb | 11 + .../indirector/indirector_testing/msgpack.rb | 6 + spec/unit/indirector/catalog/msgpack_spec.rb | 12 ++ spec/unit/indirector/msgpack_spec.rb | 191 ++++++++++++++++++ spec/unit/indirector/node/msgpack_spec.rb | 24 +++ spec/unit/indirector/report/msgpack_spec.rb | 28 +++ 10 files changed, 369 insertions(+), 2 deletions(-) create mode 100644 lib/puppet/indirector/catalog/msgpack.rb create mode 100644 lib/puppet/indirector/msgpack.rb create mode 100644 lib/puppet/indirector/node/msgpack.rb create mode 100644 lib/puppet/indirector/report/msgpack.rb create mode 100644 spec/lib/puppet/indirector/indirector_testing/msgpack.rb create mode 100755 spec/unit/indirector/catalog/msgpack_spec.rb create mode 100755 spec/unit/indirector/msgpack_spec.rb create mode 100755 spec/unit/indirector/node/msgpack_spec.rb create mode 100755 spec/unit/indirector/report/msgpack_spec.rb diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index 8ee73d27b..b429c7e54 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -256,7 +256,7 @@ module Puppet :type => :terminus, :default => nil, :desc => "How to store cached nodes. - Valid values are (none), 'json', 'yaml' or write only yaml ('write_only_yaml'). + Valid values are (none), 'json', 'msgpack', 'yaml' or write only yaml ('write_only_yaml'). The master application defaults to 'write_only_yaml', all others to none.", }, :data_binding_terminus => { @@ -292,7 +292,7 @@ module Puppet :catalog_cache_terminus => { :type => :terminus, :default => nil, - :desc => "How to store cached catalogs. Valid values are 'json' and 'yaml'. The agent application defaults to 'json'." + :desc => "How to store cached catalogs. Valid values are 'json', 'msgpack' and 'yaml'. The agent application defaults to 'json'." }, :facts_terminus => { :default => 'facter', diff --git a/lib/puppet/indirector/catalog/msgpack.rb b/lib/puppet/indirector/catalog/msgpack.rb new file mode 100644 index 000000000..a0e5bfd76 --- /dev/null +++ b/lib/puppet/indirector/catalog/msgpack.rb @@ -0,0 +1,6 @@ +require 'puppet/resource/catalog' +require 'puppet/indirector/msgpack' + +class Puppet::Resource::Catalog::Msgpack < Puppet::Indirector::Msgpack + desc "Store catalogs as flat files, serialized using MessagePack." +end diff --git a/lib/puppet/indirector/msgpack.rb b/lib/puppet/indirector/msgpack.rb new file mode 100644 index 000000000..bcc62866c --- /dev/null +++ b/lib/puppet/indirector/msgpack.rb @@ -0,0 +1,82 @@ +require 'puppet/indirector/terminus' +require 'puppet/util' + +# The base class for MessagePack indirection terminus implementations. +# +# This should generally be preferred to the PSON base for any future +# implementations, since it is ~ 30 times faster +class Puppet::Indirector::Msgpack < Puppet::Indirector::Terminus + def initialize(*args) + if ! Puppet.features.msgpack? + raise "MessagePack terminus not supported without msgpack library" + end + super + end + + def find(request) + load_msgpack_from_file(path(request.key), request.key) + end + + def save(request) + filename = path(request.key) + FileUtils.mkdir_p(File.dirname(filename)) + + Puppet::Util.replace_file(filename, 0660) {|f| f.print to_msgpack(request.instance) } + rescue TypeError => detail + Puppet.log_exception "Could not save #{self.name} #{request.key}: #{detail}" + end + + def destroy(request) + Puppet::FileSystem.unlink(path(request.key)) + rescue => detail + unless detail.is_a? Errno::ENOENT + raise Puppet::Error, "Could not destroy #{self.name} #{request.key}: #{detail}", detail.backtrace + end + 1 # emulate success... + end + + def search(request) + Dir.glob(path(request.key)).collect do |file| + load_msgpack_from_file(file, request.key) + end + end + + # Return the path to a given node's file. + def path(name, ext = '.msgpack') + if name =~ Puppet::Indirector::BadNameRegexp then + Puppet.crit("directory traversal detected in #{self.class}: #{name.inspect}") + raise ArgumentError, "invalid key" + end + + base = Puppet.run_mode.master? ? Puppet[:server_datadir] : Puppet[:client_datadir] + File.join(base, self.class.indirection_name.to_s, name.to_s + ext) + end + + private + + def load_msgpack_from_file(file, key) + msgpack = nil + + begin + msgpack = File.read(file) + rescue Errno::ENOENT + return nil + rescue => detail + raise Puppet::Error, "Could not read MessagePack data for #{indirection.name} #{key}: #{detail}", detail.backtrace + end + + begin + return from_msgpack(msgpack) + rescue => detail + raise Puppet::Error, "Could not parse MessagePack data for #{indirection.name} #{key}: #{detail}", detail.backtrace + end + end + + def from_msgpack(text) + model.convert_from('msgpack', text) + end + + def to_msgpack(object) + object.render('msgpack') + end +end diff --git a/lib/puppet/indirector/node/msgpack.rb b/lib/puppet/indirector/node/msgpack.rb new file mode 100644 index 000000000..bf38a708a --- /dev/null +++ b/lib/puppet/indirector/node/msgpack.rb @@ -0,0 +1,7 @@ +require 'puppet/node' +require 'puppet/indirector/msgpack' + +class Puppet::Node::Msgpack < Puppet::Indirector::Msgpack + desc "Store node information as flat files, serialized using MessagePack, + or deserialize stored MessagePack nodes." +end diff --git a/lib/puppet/indirector/report/msgpack.rb b/lib/puppet/indirector/report/msgpack.rb new file mode 100644 index 000000000..057b359c7 --- /dev/null +++ b/lib/puppet/indirector/report/msgpack.rb @@ -0,0 +1,11 @@ +require 'puppet/transaction/report' +require 'puppet/indirector/msgpack' + +class Puppet::Transaction::Report::Msgpack < Puppet::Indirector::Msgpack + desc "Store last report as a flat file, serialized using MessagePack." + + # Force report to be saved there + def path(name,ext='.msgpack') + Puppet[:lastrunreport] + end +end diff --git a/spec/lib/puppet/indirector/indirector_testing/msgpack.rb b/spec/lib/puppet/indirector/indirector_testing/msgpack.rb new file mode 100644 index 000000000..c8ce89364 --- /dev/null +++ b/spec/lib/puppet/indirector/indirector_testing/msgpack.rb @@ -0,0 +1,6 @@ +require 'puppet/indirector_testing' +require 'puppet/indirector/msgpack' + +class Puppet::IndirectorTesting::Msgpack < Puppet::Indirector::Msgpack + desc "Testing the MessagePack indirector" +end diff --git a/spec/unit/indirector/catalog/msgpack_spec.rb b/spec/unit/indirector/catalog/msgpack_spec.rb new file mode 100755 index 000000000..4d611149a --- /dev/null +++ b/spec/unit/indirector/catalog/msgpack_spec.rb @@ -0,0 +1,12 @@ +#! /usr/bin/env ruby +require 'spec_helper' +require 'puppet/resource/catalog' +require 'puppet/indirector/catalog/msgpack' + +describe Puppet::Resource::Catalog::Msgpack do + # This is it for local functionality: we don't *do* anything else. + it "should be registered with the catalog store indirection" do + Puppet::Resource::Catalog.indirection.terminus(:msgpack). + should be_an_instance_of described_class + end +end diff --git a/spec/unit/indirector/msgpack_spec.rb b/spec/unit/indirector/msgpack_spec.rb new file mode 100755 index 000000000..d11b17382 --- /dev/null +++ b/spec/unit/indirector/msgpack_spec.rb @@ -0,0 +1,191 @@ +#! /usr/bin/env ruby +require 'spec_helper' +require 'puppet_spec/files' +require 'puppet/indirector/indirector_testing/msgpack' + +describe Puppet::Indirector::Msgpack do + include PuppetSpec::Files + + subject { Puppet::IndirectorTesting::Msgpack.new } + let :model do Puppet::IndirectorTesting end + let :indirection do model.indirection end + + context "#path" do + before :each do + Puppet[:server_datadir] = '/sample/datadir/master' + Puppet[:client_datadir] = '/sample/datadir/client' + end + + it "uses the :server_datadir setting if this is the master" do + Puppet.run_mode.stubs(:master?).returns(true) + expected = File.join(Puppet[:server_datadir], 'indirector_testing', 'testing.msgpack') + subject.path('testing').should == expected + end + + it "uses the :client_datadir setting if this is not the master" do + Puppet.run_mode.stubs(:master?).returns(false) + expected = File.join(Puppet[:client_datadir], 'indirector_testing', 'testing.msgpack') + subject.path('testing').should == expected + end + + it "overrides the default extension with a supplied value" do + Puppet.run_mode.stubs(:master?).returns(true) + expected = File.join(Puppet[:server_datadir], 'indirector_testing', 'testing.not-msgpack') + subject.path('testing', '.not-msgpack').should == expected + end + + ['../foo', '..\\foo', './../foo', '.\\..\\foo', + '/foo', '//foo', '\\foo', '\\\\goo', + "test\0/../bar", "test\0\\..\\bar", + "..\\/bar", "/tmp/bar", "/tmp\\bar", "tmp\\bar", + " / bar", " /../ bar", " \\..\\ bar", + "c:\\foo", "c:/foo", "\\\\?\\UNC\\bar", "\\\\foo\\bar", + "\\\\?\\c:\\foo", "//?/UNC/bar", "//foo/bar", + "//?/c:/foo", + ].each do |input| + it "should resist directory traversal attacks (#{input.inspect})" do + expect { subject.path(input) }.to raise_error ArgumentError, 'invalid key' + end + end + end + + context "handling requests" do + before :each do + Puppet.run_mode.stubs(:master?).returns(true) + Puppet[:server_datadir] = tmpdir('msgpackdir') + FileUtils.mkdir_p(File.join(Puppet[:server_datadir], 'indirector_testing')) + end + + let :file do subject.path(request.key) end + + def with_content(text) + FileUtils.mkdir_p(File.dirname(file)) + File.open(file, 'w') {|f| f.write text } + yield if block_given? + end + + it "data saves and then loads again correctly" do + subject.save(indirection.request(:save, 'example', model.new('banana'))) + subject.find(indirection.request(:find, 'example', nil)).value.should == 'banana' + end + + context "#find" do + let :request do indirection.request(:find, 'example', nil) end + + it "returns nil if the file doesn't exist" do + subject.find(request).should be_nil + end + + it "raises a descriptive error when the file can't be read" do + with_content(model.new('foo').to_msgpack) do + # I don't like this, but there isn't a credible alternative that + # also works on Windows, so a stub it is. At least the expectation + # will fail if the implementation changes. Sorry to the next dev. + File.expects(:read).with(file).raises(Errno::EPERM) + expect { subject.find(request) }. + to raise_error Puppet::Error, /Could not read MessagePack/ + end + end + + it "raises a descriptive error when the file content is invalid" do + with_content("this is totally invalid MessagePack") do + expect { subject.find(request) }. + to raise_error Puppet::Error, /Could not parse MessagePack data/ + end + end + + it "should return an instance of the indirected object when valid" do + with_content(model.new(1).to_msgpack) do + instance = subject.find(request) + instance.should be_an_instance_of model + instance.value.should == 1 + end + end + end + + context "#save" do + let :instance do model.new(4) end + let :request do indirection.request(:find, 'example', instance) end + + it "should save the instance of the request as MessagePack to disk" do + subject.save(request) + content = File.read(file) + MessagePack.unpack(content)['value'].should == 4 + end + + it "should create the indirection directory if required" do + target = File.join(Puppet[:server_datadir], 'indirector_testing') + Dir.rmdir(target) + + subject.save(request) + + File.should be_directory(target) + end + end + + context "#destroy" do + let :request do indirection.request(:find, 'example', nil) end + + it "removes an existing file" do + with_content('hello') do + subject.destroy(request) + end + Puppet::FileSystem.exist?(file).should be_false + end + + it "silently succeeds when files don't exist" do + Puppet::FileSystem.unlink(file) rescue nil + subject.destroy(request).should be_true + end + + it "raises an informative error for other failures" do + Puppet::FileSystem.stubs(:unlink).with(file).raises(Errno::EPERM, 'fake permission problem') + with_content('hello') do + expect { subject.destroy(request) }.to raise_error(Puppet::Error) + end + Puppet::FileSystem.unstub(:unlink) # thanks, mocha + end + end + end + + context "#search" do + before :each do + Puppet.run_mode.stubs(:master?).returns(true) + Puppet[:server_datadir] = tmpdir('msgpackdir') + FileUtils.mkdir_p(File.join(Puppet[:server_datadir], 'indirector_testing')) + end + + def request(glob) + indirection.request(:search, glob, nil) + end + + def create_file(name, value = 12) + File.open(subject.path(name, ''), 'w') do |f| + f.write Puppet::IndirectorTesting.new(value).to_msgpack + end + end + + it "returns an empty array when nothing matches the key as a glob" do + subject.search(request('*')).should == [] + end + + it "returns an array with one item if one item matches" do + create_file('foo.msgpack', 'foo') + create_file('bar.msgpack', 'bar') + subject.search(request('f*')).map(&:value).should == ['foo'] + end + + it "returns an array of items when more than one item matches" do + create_file('foo.msgpack', 'foo') + create_file('bar.msgpack', 'bar') + create_file('baz.msgpack', 'baz') + subject.search(request('b*')).map(&:value).should =~ ['bar', 'baz'] + end + + it "only items with the .msgpack extension" do + create_file('foo.msgpack', 'foo-msgpack') + create_file('foo.msgpack~', 'foo-backup') + subject.search(request('f*')).map(&:value).should == ['foo-msgpack'] + end + end +end diff --git a/spec/unit/indirector/node/msgpack_spec.rb b/spec/unit/indirector/node/msgpack_spec.rb new file mode 100755 index 000000000..76735b011 --- /dev/null +++ b/spec/unit/indirector/node/msgpack_spec.rb @@ -0,0 +1,24 @@ +#! /usr/bin/env ruby +require 'spec_helper' + +require 'puppet/node' +require 'puppet/indirector/node/msgpack' + +describe Puppet::Node::Msgpack do + it "should be a subclass of the Msgpack terminus" do + Puppet::Node::Msgpack.superclass.should equal(Puppet::Indirector::Msgpack) + end + + it "should have documentation" do + Puppet::Node::Msgpack.doc.should_not be_nil + end + + it "should be registered with the configuration store indirection" do + indirection = Puppet::Indirector::Indirection.instance(:node) + Puppet::Node::Msgpack.indirection.should equal(indirection) + end + + it "should have its name set to :msgpack" do + Puppet::Node::Msgpack.name.should == :msgpack + end +end diff --git a/spec/unit/indirector/report/msgpack_spec.rb b/spec/unit/indirector/report/msgpack_spec.rb new file mode 100755 index 000000000..a3494865a --- /dev/null +++ b/spec/unit/indirector/report/msgpack_spec.rb @@ -0,0 +1,28 @@ +#! /usr/bin/env ruby +require 'spec_helper' + +require 'puppet/transaction/report' +require 'puppet/indirector/report/msgpack' + +describe Puppet::Transaction::Report::Msgpack do + it "should be a subclass of the Msgpack terminus" do + Puppet::Transaction::Report::Msgpack.superclass.should equal(Puppet::Indirector::Msgpack) + end + + it "should have documentation" do + Puppet::Transaction::Report::Msgpack.doc.should_not be_nil + end + + it "should be registered with the report indirection" do + indirection = Puppet::Indirector::Indirection.instance(:report) + Puppet::Transaction::Report::Msgpack.indirection.should equal(indirection) + end + + it "should have its name set to :msgpack" do + Puppet::Transaction::Report::Msgpack.name.should == :msgpack + end + + it "should unconditionally save/load from the --lastrunreport setting" do + subject.path(:me).should == Puppet[:lastrunreport] + end +end From dcd63c11683c50d2d344dc447fe63c3432a193c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Dal=C3=A9n?= Date: Tue, 4 Feb 2014 21:17:39 +0100 Subject: [PATCH 570/800] (maint) Make msgpack feature do the correct requires --- lib/puppet/feature/msgpack.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/puppet/feature/msgpack.rb b/lib/puppet/feature/msgpack.rb index 8c7b7f963..3971a5e5b 100644 --- a/lib/puppet/feature/msgpack.rb +++ b/lib/puppet/feature/msgpack.rb @@ -1 +1,3 @@ +require 'puppet/util/feature' + Puppet.features.add(:msgpack, :libs => ["msgpack"]) From 6f244517ee540ffe2be5f0ec4ab52ed317797d63 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Thu, 6 Feb 2014 12:42:08 -0800 Subject: [PATCH 571/800] (doc) Point to the local docs Rather than pointing to some remote form of the docs that are part of the repo, we should point to the local files. This makes the docs more self contained and better versioned together. --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 274c99039..5b9cb4b98 100644 --- a/README.md +++ b/README.md @@ -26,13 +26,11 @@ To install an open source release of Puppet, If you need to run Puppet from source as a tester or developer, [see the running from source guide on the docs site.](http://docs.puppetlabs.com/guides/from_source.html) -Contributions +Developing and Contributioning ------ -Please see our [Contribution -Documents](https://github.com/puppetlabs/puppet/blob/master/CONTRIBUTING.md) -and our [Developer -Documentation](https://github.com/puppetlabs/puppet/blob/master/README_DEVELOPER.md). +Please see our [Contribution Documents](CONTRIBUTING.md) +and our [Developer Documentation](README_DEVELOPER.md). License ------- From 411e8fd31185cf319f07f181e1ee795168b47949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Dal=C3=A9n?= Date: Mon, 13 Jan 2014 21:46:12 +0100 Subject: [PATCH 572/800] (PUP-1451) Make SSL file permissions a bit more relaxed for public files --- lib/puppet/defaults.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index 8ee73d27b..f123f2759 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -672,7 +672,7 @@ EOT :type => :directory, :owner => "service", :group => "service", - :mode => 0770, + :mode => 0775, :desc => "The root directory for the certificate authority." }, :cacert => { @@ -680,7 +680,7 @@ EOT :type => :file, :owner => "service", :group => "service", - :mode => 0660, + :mode => 0664, :desc => "The CA certificate." }, :cakey => { @@ -720,6 +720,7 @@ EOT :type => :directory, :owner => "service", :group => "service", + :mode => 0775, :desc => "Where the CA stores certificate requests" }, :signeddir => { @@ -727,7 +728,7 @@ EOT :type => :directory, :owner => "service", :group => "service", - :mode => 0770, + :mode => 0775, :desc => "Where the CA stores signed certificates." }, :capass => { From 1246979f79955d81cca5f777f83d5220be54edbb Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Mon, 6 Jan 2014 15:22:19 -0800 Subject: [PATCH 573/800] (PUP-1120) Make modes of public ssl directories explicit Previously, the mode of the `certs`, `public_keys`, and `certificate_requests` directories within the `ssl` directory was unspecified. This means the actual mode depended on the umask of the user that started puppet, and caused those directories to be created. Since the information in these directories is intended to be public, there is no harm in making the mode explicit 0755. This also matches what I see when installing puppet from packages. --- lib/puppet/defaults.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index f123f2759..5fd1991c2 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -539,6 +539,7 @@ EOT :certdir => { :default => "$ssldir/certs", :type => :directory, + :mode => 0755, :owner => "service", :desc => "The certificate directory." }, @@ -552,12 +553,14 @@ EOT :publickeydir => { :default => "$ssldir/public_keys", :type => :directory, + :mode => 0755, :owner => "service", :desc => "The public key directory." }, :requestdir => { :default => "$ssldir/certificate_requests", :type => :directory, + :mode => 0755, :owner => "service", :desc => "Where host certificate requests are stored." }, From 8fb225386f004f1e2308d3312eb6cf6bc7a133ed Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Mon, 6 Jan 2014 15:16:58 -0800 Subject: [PATCH 574/800] (PUP-1120) Be explicit about group for ssl directory Previously, files and directories contained with the `ssl` directory were inconsistent with respect to their `group`. For example, the `certs` directory had group `root`, but files within it, e.g. hostname.pem, had group `service`. Commit c6846c2b updated the `hostprivkey` and `privatekeydir` settings so that they are readable by the `service` group. This commit makes the `service` group explicit for other files & directories in the `ssl` directory. --- lib/puppet/defaults.rb | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index 5fd1991c2..2aae9e8a6 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -541,6 +541,7 @@ EOT :type => :directory, :mode => 0755, :owner => "service", + :group => "service", :desc => "The certificate directory." }, :ssldir => { @@ -548,6 +549,7 @@ EOT :type => :directory, :mode => 0771, :owner => "service", + :group => "service", :desc => "Where SSL certificates are kept." }, :publickeydir => { @@ -555,6 +557,7 @@ EOT :type => :directory, :mode => 0755, :owner => "service", + :group => "service", :desc => "The public key directory." }, :requestdir => { @@ -562,6 +565,7 @@ EOT :type => :directory, :mode => 0755, :owner => "service", + :group => "service", :desc => "Where host certificate requests are stored." }, :privatekeydir => { @@ -577,6 +581,7 @@ EOT :type => :directory, :mode => 0750, :owner => "service", + :group => "service", :desc => "Where the client stores private certificate information." }, :passfile => { @@ -584,6 +589,7 @@ EOT :type => :file, :mode => 0640, :owner => "service", + :group => "service", :desc => "Where puppet agent stores the password for its private key. Generally unused." }, @@ -592,6 +598,7 @@ EOT :type => :file, :mode => 0644, :owner => "service", + :group => "service", :desc => "Where individual hosts store and look for their certificate requests." }, :hostcert => { @@ -599,6 +606,7 @@ EOT :type => :file, :mode => 0644, :owner => "service", + :group => "service", :desc => "Where individual hosts store and look for their certificates." }, :hostprivkey => { @@ -614,6 +622,7 @@ EOT :type => :file, :mode => 0644, :owner => "service", + :group => "service", :desc => "Where individual hosts store and look for their public key." }, :localcacert => { @@ -621,14 +630,16 @@ EOT :type => :file, :mode => 0644, :owner => "service", + :group => "service", :desc => "Where each client stores the CA certificate." }, :ssl_client_ca_auth => { :type => :file, :mode => 0644, :owner => "service", + :group => "service", :desc => "Certificate authorities who issue server certificates. SSL servers will not be - considered authentic unless they posses a certificate issued by an authority + considered authentic unless they possess a certificate issued by an authority listed in this file. If this setting has no value then the Puppet master's CA certificate (localcacert) will be used." }, @@ -636,8 +647,9 @@ EOT :type => :file, :mode => 0644, :owner => "service", + :group => "service", :desc => "Certificate authorities who issue client certificates. SSL clients will not be - considered authentic unless they posses a certificate issued by an authority + considered authentic unless they possess a certificate issued by an authority listed in this file. If this setting has no value then the Puppet master's CA certificate (localcacert) will be used." }, @@ -646,6 +658,7 @@ EOT :type => :file, :mode => 0644, :owner => "service", + :group => "service", :desc => "Where the host's certificate revocation list can be found. This is distinct from the certificate authority's CRL." }, From f1fed00890c2634261df2009051d728662976f0f Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Wed, 8 Jan 2014 08:53:52 -0800 Subject: [PATCH 575/800] (PUP-1120) Remove group write permission for ca settings Previously, the `service` group was granted write permission for ca related files, but that is not necessary and likely left over from a time when puppet did not manage the group correctly for file settings. This commit removes group write permission for ca settings. --- lib/puppet/defaults.rb | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index 2aae9e8a6..c76d3afb8 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -688,7 +688,7 @@ EOT :type => :directory, :owner => "service", :group => "service", - :mode => 0775, + :mode => 0755, :desc => "The root directory for the certificate authority." }, :cacert => { @@ -696,7 +696,7 @@ EOT :type => :file, :owner => "service", :group => "service", - :mode => 0664, + :mode => 0644, :desc => "The CA certificate." }, :cakey => { @@ -704,7 +704,7 @@ EOT :type => :file, :owner => "service", :group => "service", - :mode => 0660, + :mode => 0640, :desc => "The CA private key." }, :capub => { @@ -712,6 +712,7 @@ EOT :type => :file, :owner => "service", :group => "service", + :mode => 0644, :desc => "The CA public key." }, :cacrl => { @@ -719,8 +720,7 @@ EOT :type => :file, :owner => "service", :group => "service", - :mode => 0664, - + :mode => 0644, :desc => "The certificate revocation list (CRL) for the CA. Will be used if present but otherwise ignored.", }, :caprivatedir => { @@ -728,7 +728,7 @@ EOT :type => :directory, :owner => "service", :group => "service", - :mode => 0770, + :mode => 0750, :desc => "Where the CA stores private certificate information." }, :csrdir => { @@ -736,7 +736,7 @@ EOT :type => :directory, :owner => "service", :group => "service", - :mode => 0775, + :mode => 0755, :desc => "Where the CA stores certificate requests" }, :signeddir => { @@ -744,7 +744,7 @@ EOT :type => :directory, :owner => "service", :group => "service", - :mode => 0775, + :mode => 0755, :desc => "Where the CA stores signed certificates." }, :capass => { @@ -752,7 +752,7 @@ EOT :type => :file, :owner => "service", :group => "service", - :mode => 0660, + :mode => 0640, :desc => "Where the CA stores the password for the private key." }, :serial => { From fe16ed6b9b30ffd94c4ef60130d02b3fe799f95d Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Thu, 6 Feb 2014 13:24:53 -0800 Subject: [PATCH 576/800] (doc) Create a quickstart guide to get people going The README_DEVELOPER document has become too long for many new contributors to quickly get going. The setup instructions were hidding far down inside the file and provided to many caveats for a quick setup. This splits out that information, streamlines it to take an opinionated stance on how to setup a development environment, and start the process of creating a set of documents tightly tied to the puppet codebase. This new docs area should be used for documents about developing on the puppet codebase, documentation of internal architecture, and other developer and development focussed documents. This also moves the acceptance test documents to this new location. --- README.md | 10 ++- README_DEVELOPER.md | 48 +-------------- .../README.md => docs/acceptance_tests.md | 0 docs/index.md | 6 ++ docs/quickstart.md | 61 +++++++++++++++++++ 5 files changed, 77 insertions(+), 48 deletions(-) rename acceptance/README.md => docs/acceptance_tests.md (100%) create mode 100644 docs/index.md create mode 100644 docs/quickstart.md diff --git a/README.md b/README.md index 5b9cb4b98..0e4d4543b 100644 --- a/README.md +++ b/README.md @@ -29,8 +29,14 @@ If you need to run Puppet from source as a tester or developer, Developing and Contributioning ------ -Please see our [Contribution Documents](CONTRIBUTING.md) -and our [Developer Documentation](README_DEVELOPER.md). +We'd love to get contributions from you! For a quick guide to getting your +system setup for developing take a look at our [Quickstart +Guide](docs/quickstart.md). Once you are up and running, take a look at the +[Contribution Documents](CONTRIBUTING.md) to see how to get your changes merged +in. + +For more complete docs on developing with puppet you can take a look at the +rest of the [developer documents](docs/index.md). License ------- diff --git a/README_DEVELOPER.md b/README_DEVELOPER.md index 6ff7fafbb..7aa070b22 100644 --- a/README_DEVELOPER.md +++ b/README_DEVELOPER.md @@ -3,6 +3,8 @@ This file is intended to provide a place for developers and contributors to document what other developers need to know about changes made to Puppet. +### Dependencies + # Internal Structures ## Two Types of Catalog @@ -128,52 +130,6 @@ sure and track down the problem is to wrap the File.open method like so: The settings catalog is populated by the `Puppet::Util::Settings#to\_catalog` method. -# Ruby Dependencies # - -To install the dependencies run: - - $ bundle install --path .bundle/gems/ - -Once this is done, you can interact with puppet through bundler using `bundle -exec ` which will ensure that `` is executed in the context -of puppet's dependencies. - -For example to run the specs: - - $ bundle exec rake spec - -To run puppet itself (for a resource lookup say): - - $ bundle exec puppet resource host localhost - -which should return something like: - - host { 'localhost': - ensure => 'present', - ip => '127.0.0.1', - target => '/etc/hosts', - } - -# Running Tests # - -Puppet Labs projects use a common convention of using Rake to run unit tests. -The tests can be run with the following rake task: - - bundle exec rake spec - -This allows the Rakefile to set up the environment beforehand if needed. This -method is how the unit tests are run in [Jenkins](https://jenkins.puppetlabs.com). - -Under the hood Puppet's tests use `rspec`. To run all of them, you can directly -use 'rspec': - - bundle exec rspec - -To run a single file's worth of tests (much faster!), give the filename, and use -the nested format to see the descriptions: - - bundle exec rspec spec/unit/ssl/host_spec.rb --format nested - ## Testing dependency version requirements Puppet is only compatible with certain versions of RSpec and Mocha. If you are diff --git a/acceptance/README.md b/docs/acceptance_tests.md similarity index 100% rename from acceptance/README.md rename to docs/acceptance_tests.md diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 000000000..b7276af4a --- /dev/null +++ b/docs/index.md @@ -0,0 +1,6 @@ +# Puppet Developer Documentation + +Setting up and running tests + +* [Quickstart Guide](quickstart.md) +* [Running acceptance tests](acceptance_tests.md) diff --git a/docs/quickstart.md b/docs/quickstart.md new file mode 100644 index 000000000..e8a88c3ec --- /dev/null +++ b/docs/quickstart.md @@ -0,0 +1,61 @@ +# Quick Start to Developing on Puppet + +Before diving into the code, you should first take the time to make sure you +have an environment where you can run puppet as a developer. In a nutshell you +need: the puppet codebase, ruby versions, and dependencies. Once you've got all +of that in place you can make sure that you have a working development system +by running the puppet spec tests. + +## The Puppet Codebase + +In order to contribute to puppet you'll need to have a github account. Once you +have your account, fork the puppetlabs/puppet repo, and clone it onto your +local machine. The [github docs have a good +explanation](https://help.github.com/articles/fork-a-repo) of how to do all of +this. + +## Ruby versions + +Puppet needs to work across a variety of ruby versions. At a minimum you need +to try any changes you make on both ruby 1.8.7 and ruby 1.9.3. Ruby 2.0.0 and +2.1.0 are also supported, but they have small enough differences from 1.9.3 +that they are not as important to always check while developing. + +Popular ways of making sure you have access to the various versions of ruby are +to use either [rbenv](https://github.com/sstephenson/rbenv) or +[rvm](http://rvm.io/). You can read up on the linked sites for how to get them +installed on your system. + +## Dependencies + +Make sure you have [bundler](http://bundler.io/) installed. This should be as +simple as: + + $ gem install bundler + +Now you can get all of the dependencies using: + + $ bundle install --path .bundle/gems/ + +Once this is done, you can interact with puppet through bundler using `bundle +exec ` which will ensure that `` is executed in the context +of puppet's dependencies. + +For example to run the specs: + + $ bundle exec rake spec + +To run puppet itself (for a resource lookup say): + + $ bundle exec puppet resource host localhost + +## Running Spec Tests + +Puppet Labs projects use a common convention of using Rake to run unit tests. +The tests can be run with the following rake task: + + bundle exec rake spec + +To run a single file's worth of tests (much faster!), give the filename: + + bundle exec rake spec TEST=spec/unit/ssl/host_spec.rb From 09bfbcb3d8b1a16b0ce31a276c75472a9bf14827 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 6 Feb 2014 23:33:56 +0100 Subject: [PATCH 577/800] (PUP-1029) Make the slice function use the common Enumeration support. This adds use of the common Enumeration support to the slice function. The documentation of the function is also updated. --- lib/puppet/parser/functions/slice.rb | 62 +++++++++++++++----------- spec/unit/parser/methods/slice_spec.rb | 26 ++++++++++- 2 files changed, 59 insertions(+), 29 deletions(-) diff --git a/lib/puppet/parser/functions/slice.rb b/lib/puppet/parser/functions/slice.rb index db7d9d20b..e18e043b5 100644 --- a/lib/puppet/parser/functions/slice.rb +++ b/lib/puppet/parser/functions/slice.rb @@ -7,11 +7,12 @@ Puppet::Parser::Functions::newfunction( argument and returns the first argument, or if no block is given returns a new array with a concatenation of the slices. - This function takes two mandatory arguments: the first, `$a`, should be an Array, Hash, or enumerable type, - and the second, `$n`, the number of elements to include in each slice. The optional third argument should be a - a parameterized block as produced by the puppet syntax: + This function takes two mandatory arguments: the first, `$a`, should be an Array, Hash, or something of + enumerable type (integer, Integer range, or String), and the second, `$n`, the number of elements to include + in each slice. The optional third argument should be a a parameterized block as produced by the puppet syntax: $a.slice($n) |$x| { ... } + slice($a) |$x| { ... } The parameterized block should have either one parameter (receiving an array with the slice), or the same number of parameters as specified by the slice size (each parameter receiving its part of the slice). @@ -21,8 +22,8 @@ Puppet::Parser::Functions::newfunction( $a.slice(2) |$first, $second| { ... } - When the first argument is a Hash, each key,value entry is counted as one, e.g, a slice size of 2 will produce - an array of two arrays with key, value. + When the first argument is a Hash, each `key,value` entry is counted as one, e.g, a slice size of 2 will produce + an array of two arrays with key, and value. $a.slice(2) |$entry| { notice "first ${$entry[0]}, second ${$entry[1]}" } $a.slice(2) |$first, $second| { notice "first ${first}, second ${second}" } @@ -31,38 +32,49 @@ Puppet::Parser::Functions::newfunction( slice([1,2,3,4,5,6], 2) # produces [[1,2], [3,4], [5,6]] slice(Integer[1,6], 2) # produces [[1,2], [3,4], [5,6]] + slice(4,2) # produces [[0,1], [2,3]] + slice('hello',2) # produces [[h, e], [l, l], [o]] - - Since 3.2 + - Since 3.2 for Array and Hash + - Since 3.5 for additional enumerable types - requires `parser = future`. ENDHEREDOC require 'puppet/parser/ast/lambda' require 'puppet/parser/scope' - def each_Common(o, size, slice_size, filler, scope, pblock) + def each_Common(o, slice_size, filler, scope, pblock) serving_size = pblock ? pblock.parameter_count : 1 if serving_size == 0 - raise ArgumentError, "Block must define at least one parameter." + raise ArgumentError, "slice(): block must define at least one parameter." end unless serving_size == 1 || serving_size == slice_size - raise ArgumentError, "Block must define one parameter, or the same number of parameters as the given size of the slice (#{slice_size})." + raise ArgumentError, "slice(): block must define one parameter, or the same number of parameters as the given size of the slice (#{slice_size})." end enumerator = o.each_slice(slice_size) result = [] if serving_size == 1 - ((size.to_f / slice_size).ceil).times do + begin if pblock - pblock.call(scope, enumerator.next) + loop do + pblock.call(scope, enumerator.next) + end else - result << enumerator.next + loop do + result << enumerator.next + end end + rescue StopIteration end else - ((size.to_f / slice_size).ceil).times do - a = enumerator.next - if a.size < serving_size - a = a.dup.fill(filler, a.length...serving_size) + begin + loop do + a = enumerator.next + if a.size < serving_size + a = a.dup.fill(filler, a.length...serving_size) + end + pblock.call(scope, *a) end - pblock.call(scope, *a) + rescue StopIteration end end if pblock @@ -71,6 +83,7 @@ Puppet::Parser::Functions::newfunction( result end end + raise ArgumentError, ("slice(): wrong number of arguments (#{args.length}; must be 2 or 3)") unless args.length == 2 || args.length == 3 if args.length >= 2 begin @@ -88,19 +101,14 @@ Puppet::Parser::Functions::newfunction( raise ArgumentError, ("slice(): wrong argument type (#{args[2].class}; must be a parameterized block.") unless pblock.respond_to?(:puppet_lambda) || args.length == 2 case receiver - when Array - each_Common(receiver, receiver.size, slice_size, :undef, self, pblock) when Hash - each_Common(receiver, receiver.size, slice_size, [], self, pblock) - when Puppet::Pops::Types::PAbstractType - tc = Puppet::Pops::Types::TypeCalculator.new() - enumerable = tc.enumerable(receiver) - if enumerable.nil? + each_Common(receiver, slice_size, [], self, pblock) + else + enum = Puppet::Pops::Types::Enumeration.enumerator(receiver) + if enum.nil? raise ArgumentError, ("slice(): given type '#{tc.string(receiver)}' is not enumerable") end - result = each_Common(enumerable.each, receiver.size, slice_size, [], self, pblock) + result = each_Common(enum, slice_size, :undef, self, pblock) pblock ? receiver : result - else - raise ArgumentError, ("slice(): wrong argument type (#{args[0].class}; must be an Array or a Hash.") end end diff --git a/spec/unit/parser/methods/slice_spec.rb b/spec/unit/parser/methods/slice_spec.rb index 0d29dc44a..1de1dd0f1 100644 --- a/spec/unit/parser/methods/slice_spec.rb +++ b/spec/unit/parser/methods/slice_spec.rb @@ -81,8 +81,8 @@ describe 'methods' do end end - context "should be callable on enumerable type as" do - it 'slice with explicit parameters' do + context "should be callable on enumerable types as" do + it 'slice with integer range' do catalog = compile_to_catalog(<<-MANIFEST) $a = Integer[1,4] $a.slice(2) |$a,$b| { @@ -93,6 +93,28 @@ describe 'methods' do catalog.resource(:file, "/file_1.2")['ensure'].should == 'present' catalog.resource(:file, "/file_3.4")['ensure'].should == 'present' end + + it 'slice with integer' do + catalog = compile_to_catalog(<<-MANIFEST) + 4.slice(2) |$a,$b| { + file { "/file_${a}.${b}": ensure => present } + } + MANIFEST + + catalog.resource(:file, "/file_0.1")['ensure'].should == 'present' + catalog.resource(:file, "/file_2.3")['ensure'].should == 'present' + end + + it 'slice with string' do + catalog = compile_to_catalog(<<-MANIFEST) + 'abcd'.slice(2) |$a,$b| { + file { "/file_${a}.${b}": ensure => present } + } + MANIFEST + + catalog.resource(:file, "/file_a.b")['ensure'].should == 'present' + catalog.resource(:file, "/file_c.d")['ensure'].should == 'present' + end end context "when called without a block" do From 253866cd02bd172110421652f8ecf1d79195b61f Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 6 Feb 2014 23:56:04 +0100 Subject: [PATCH 578/800] (PUP-1029) Fix problem with class Enumerator btw. 1.8.7 and 1.9.3 The Enumerator class is in Enumerable::Enumerator in 1.8.7, and is global from 1.9.3. This changes the tests to check if result responds to :next instead. --- spec/unit/pops/types/enumeration_spec.rb | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/spec/unit/pops/types/enumeration_spec.rb b/spec/unit/pops/types/enumeration_spec.rb index 501bbc3f5..ecf5df596 100644 --- a/spec/unit/pops/types/enumeration_spec.rb +++ b/spec/unit/pops/types/enumeration_spec.rb @@ -3,22 +3,21 @@ require 'puppet/pops' describe 'The enumeration support' do it 'produces an enumerator for Array' do - expect(Puppet::Pops::Types::Enumeration.enumerator([1,2,3]).is_a?(Enumerable::Enumerator)).to eql(true) + expect(Puppet::Pops::Types::Enumeration.enumerator([1,2,3]).respond_to?(:next)).to eql(true) end it 'produces an enumerator for Hash' do - expect(Puppet::Pops::Types::Enumeration.enumerator({:a=>1}).is_a?(Enumerable::Enumerator)).to eql(true) + expect(Puppet::Pops::Types::Enumeration.enumerator({:a=>1}).respond_to?(:next)).to eql(true) end it 'produces a char enumerator for String' do enum = Puppet::Pops::Types::Enumeration.enumerator("abc") - expect(enum.is_a?(Enumerable::Enumerator)).to eql(true) + expect(enum.respond_to?(:next)).to eql(true) expect(enum.next).to eql('a') end it 'produces an enumerator for integer times' do enum = Puppet::Pops::Types::Enumeration.enumerator(2) - expect(enum.is_a?(Enumerable::Enumerator)).to eql(true) expect(enum.next).to eql(0) expect(enum.next).to eql(1) expect{enum.next}.to raise_error(StopIteration) @@ -27,7 +26,6 @@ describe 'The enumeration support' do it 'produces an enumerator for Integer range' do range = Puppet::Pops::Types::TypeFactory.range(1,2) enum = Puppet::Pops::Types::Enumeration.enumerator(range) - expect(enum.is_a?(Enumerable::Enumerator)).to eql(true) expect(enum.next).to eql(1) expect(enum.next).to eql(2) expect{enum.next}.to raise_error(StopIteration) From 6e1c325659766dcfe797f4835e1c9477f8ab49b8 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Thu, 6 Feb 2014 13:27:05 -0800 Subject: [PATCH 579/800] (doc) Extract sections of README_DEVELOPER This pulls the various parts of README_DEVELOPER into individual files and links to them from the docs/index.md. This should give us a place to better organize these internal docs. --- README_DEVELOPER.md | 713 --------------------------------------- docs/acceptance_tests.md | 2 +- docs/catalogs.md | 122 +++++++ docs/index.md | 11 + docs/rspec_tutorial.md | 362 ++++++++++++++++++++ docs/static_compiler.md | 83 +++++ docs/unicode.md | 56 +++ docs/windows.md | 73 ++++ 8 files changed, 708 insertions(+), 714 deletions(-) create mode 100644 docs/catalogs.md create mode 100644 docs/rspec_tutorial.md create mode 100644 docs/static_compiler.md create mode 100644 docs/unicode.md create mode 100644 docs/windows.md diff --git a/README_DEVELOPER.md b/README_DEVELOPER.md index 7aa070b22..f54e72ae1 100644 --- a/README_DEVELOPER.md +++ b/README_DEVELOPER.md @@ -5,638 +5,7 @@ document what other developers need to know about changes made to Puppet. ### Dependencies -# Internal Structures -## Two Types of Catalog - -When working on subsystems of Puppet that deal with the catalog it is important -to be aware of the two different types of Catalog. Developers will often find -this difference while working on the static compiler and types and providers. - -The two different types of catalog becomes relevant when writing spec tests -because we frequently need to wire up a fake catalog so that we can exercise -types, providers, or termini that filter the catalog. - -The two different types of catalogs are so-called "resource" catalogs and "RAL" -(resource abstraction layer) catalogs. At a high level, the resource catalog -is the in-memory object we serialize and transfer around the network. The -compiler terminus is expected to produce a resource catalog. The agent takes a -resource catalog and converts it into a RAL catalog. The RAL catalog is what -is used to apply the configuration model to the system. - -Resource dependency information is most easily obtained from a RAL catalog by -walking the graph instance produced by the `relationship_graph` method. - -### Resource Catalog - -If you're writing spec tests for something that deals with a catalog "server -side," a new catalog terminus for example, then you'll be dealing with a -resource catalog. You can produce a resource catalog suitable for spec tests -using something like this: - - let(:catalog) do - catalog = Puppet::Resource::Catalog.new("node-name-val") # NOT certname! - rsrc = Puppet::Resource.new("file", "sshd_config", - :parameters => { - :ensure => 'file', - :source => 'puppet:///modules/filetest/sshd_config', - } - ) - rsrc.file = 'site.pp' - rsrc.line = 21 - catalog.add_resource(rsrc) - end - -The resources in this catalog may be accessed using `catalog.resources`. -Resource dependencies are not easily walked using a resource catalog however. -To walk the dependency tree convert the catalog to a RAL catalog as described -in - -### RAL Catalog - -The resource catalog may be converted to a RAL catalog using `catalog.to_ral`. -The RAL catalog contains `Puppet::Type` instances instead of `Puppet::Resource` -instances as is the case with the resource catalog. - -One very useful feature of the RAL catalog are the methods to work with -resource relationships. For example: - - irb> catalog = catalog.to_ral - irb> graph = catalog.relationship_graph - irb> pp graph.edges - [{ Notify[alpha] => File[/tmp/file_20.txt] }, - { Notify[alpha] => File[/tmp/file_21.txt] }, - { Notify[alpha] => File[/tmp/file_22.txt] }, - { Notify[alpha] => File[/tmp/file_23.txt] }, - { Notify[alpha] => File[/tmp/file_24.txt] }, - { Notify[alpha] => File[/tmp/file_25.txt] }, - { Notify[alpha] => File[/tmp/file_26.txt] }, - { Notify[alpha] => File[/tmp/file_27.txt] }, - { Notify[alpha] => File[/tmp/file_28.txt] }, - { Notify[alpha] => File[/tmp/file_29.txt] }, - { File[/tmp/file_20.txt] => Notify[omega] }, - { File[/tmp/file_21.txt] => Notify[omega] }, - { File[/tmp/file_22.txt] => Notify[omega] }, - { File[/tmp/file_23.txt] => Notify[omega] }, - { File[/tmp/file_24.txt] => Notify[omega] }, - { File[/tmp/file_25.txt] => Notify[omega] }, - { File[/tmp/file_26.txt] => Notify[omega] }, - { File[/tmp/file_27.txt] => Notify[omega] }, - { File[/tmp/file_28.txt] => Notify[omega] }, - { File[/tmp/file_29.txt] => Notify[omega] }] - -If the `relationship_graph` method is throwing exceptions at you, there's a -good chance the catalog is not a RAL catalog. - -## Settings Catalog ## - -Be aware that Puppet creates a mini catalog and applies this catalog locally to -manage file resource from the settings. This behavior made it difficult and -time consuming to track down a race condition in -[2888](http://projects.puppetlabs.com/issues/2888). - -Even more surprising, the `File[puppetdlockfile]` resource is only added to the -settings catalog if the file exists on disk. This caused the race condition as -it will exist when a separate process holds the lock while applying the -catalog. - -It may be sufficient to simply be aware of the settings catalog and the -potential for race conditions it presents. An effective way to be reasonably -sure and track down the problem is to wrap the File.open method like so: - - # We're wrapping ourselves around the File.open method. - # As described at: http://goo.gl/lDsv6 - class File - WHITELIST = [ /pidlock.rb:39/ ] - - class << self - alias xxx_orig_open open - end - - def self.open(name, *rest, &block) - # Check the whitelist for any "good" File.open calls against the # - puppetdlock file - white_listed = caller(0).find do |line| - JJM_WHITELIST.find { |re| re.match(line) } - end - - # If you drop into IRB here, take a look at your caller, it might be - # the ghost in the machine you're looking for. - binding.pry if name =~ /puppetdlock/ and not white_listed - xxx_orig_open(name, *rest, &block) - end - end - -The settings catalog is populated by the `Puppet::Util::Settings#to\_catalog` -method. - -## Testing dependency version requirements - -Puppet is only compatible with certain versions of RSpec and Mocha. If you are -not using Bundler to install the required test libraries you must ensure that -you are using the right library versions. Using unsupported versions of Mocha -and RSpec will probably display many spurious failures. The supported versions -of RSpec and Mocha can be found in the project Gemfile. - -# A brief introduction to testing in Puppet - -Puppet relies heavily on automated testing to ensure that Puppet behaves as -expected and that new features don't interfere with existing behavior. There are -three primary sets of tests that Puppet uses: _unit tests_, _integration tests_, -and _acceptance tests_. - -- - - - -Unit tests are used to test the individual components of Puppet to ensure that -they function as expected in isolation. Unit tests are designed to hide the -actual system implementations and provide canned information so that only the -intended behavior is tested, rather than the targeted code and everything else -connected to it. Unit tests should never affect the state of the system that's -running the test. - -- - - - -Integration tests serve to test different units of code together to ensure that -they interact correctly. While individual methods might perform correctly, when -used with the rest of the system they might fail, so integration tests are a -higher level version of unit tests that serve to check the behavior of -individual subsystems. - -All of the unit and integration tests for Puppet are kept in the spec/ directory. - -- - - - -Acceptance tests are used to test high level behaviors of Puppet that deal with -a number of concerns and aren't easily tested with normal unit tests. Acceptance -tests function by changing system state and checking the system after -the fact to make sure that the intended behavior occurred. Because of this -acceptance tests can be destructive, so the systems being tested should be -throwaway systems. - -All of the acceptance tests for Puppet are kept in the acceptance/tests/ -directory. - -## Puppet Continuous integration - - * Travis-ci (unit tests only): https://travis-ci.org/puppetlabs/puppet/ - * Jenkins (unit and acceptance tests): https://jenkins.puppetlabs.com/view/Puppet%20FOSS/ - -## RSpec - -Puppet uses RSpec to perform unit and integration tests. RSpec handles a number -of concerns to make testing easier: - - * Executing examples and ensuring the actual behavior matches the expected behavior (examples) - * Grouping tests (describe and contexts) - * Setting up test environments and cleaning up afterwards (before and after blocks) - * Isolating tests (mocks and stubs) - -#### Examples and expectations - -At the most basic level, RSpec provides a framework for executing tests (which -are called examples) and ensuring that the actual behavior matches the expected -behavior (which are done with expectations) - -```ruby -# This is an example; it sets the test name and defines the test to run -specify "one equals one" do - # 'should' is an expectation; it adds a check to make sure that the left argument - # matches the right argument - 1.should == 1 -end - -# Examples can be declared with either 'it' or 'specify' -it "one doesn't equal two" do - 1.should_not == 2 -end -``` - -Good examples generally do as little setup as possible and only test one or two -things; it makes tests easier to understand and easier to debug. - -More complete documentation on expectations is available at https://www.relishapp.com/rspec/rspec-expectations/docs - -### Example groups - -Example groups are fairly self explanatory; they group similar examples into a -set. - -```ruby -describe "the number one" do - - it "is larger than zero" do - 1.should be > 0 - end - - it "is an odd number" do - 1.odd?.should be true - end - - it "is not nil" do - 1.should_not be_nil - end -end -``` - -Example groups have a number of uses that we'll get into later, but one of the -simplest demonstrations of what they do is how they help to format -documentation: - -``` -rspec ex.rb --format documentation - -the number one - is larger than zero - is an odd number - is not nil - -Finished in 0.00516 seconds -3 examples, 0 failures -``` - -### Setting up and tearing down tests - -Examples may require some setup before they can run, and might need to clean up -afterwards. `before` and `after` blocks can be used before this, and can be -used inside of example groups to limit how many examples they affect. - -```ruby - -describe "something that could warn" do - before :each do - # Disable warnings for this test - $VERBOSE = nil - end - - after do - # Enable warnings afterwards - $VERBOSE = true - end - - it "doesn't generate a warning" do - MY_CONSTANT = 1 - # reassigning a normally prints out 'warning: already initialized constant FOO' - MY_CONSTANT = 2 - end -end -``` - -### Setting up helper data - -Some examples may require setting up data before hand and making it available to -tests. RSpec provides helper methods with the `let` method call that can be used -inside of tests. - -```ruby -describe "a helper object" do - # This creates an array with three elements that we can retrieve in tests. A - # new copy will be made for each test. - let(:my_helper) do - ['foo', 'bar', 'baz'] - end - - it "should be an array" do - my_helper.should be_a_kind_of Array - end - - it "should have three elements" do - my_helper.should have(3).items - end -end -``` - -Like `before` blocks, helper objects like this are used to avoid doing a lot of -setup in individual examples and share setup between similar tests. - -### Isolating tests with stubs - -RSpec allows you to provide fake data during testing to make sure that -individual tests are only running the code being tested. You can stub out entire -objects, or just stub out individual methods on an object. When a method is -stubbed the method itself will never be called. - -While RSpec comes with its own stubbing framework, Puppet uses the Mocha -framework. - -A brief usage guide for Mocha is available at http://gofreerange.com/mocha/docs/#Usage, -and an overview of Mocha expectations is available at http://gofreerange.com/mocha/docs/Mocha/Expectation.html - -```ruby -describe "stubbing a method on an object" do - let(:my_helper) do - ['foo', 'bar', 'baz'] - end - - it 'should have three items before being stubbed' do - my_helper.size.should == 3 - end - - describe 'when stubbing the size' do - before do - my_helper.stubs(:size).returns 10 - end - - it 'should have the stubbed value for size' do - my_helper.size.should == 10 - end - end -end -``` - -Entire objects can be stubbed as well. - -```ruby -describe "stubbing an object" do - let(:my_helper) do - stub(:not_an_array, :size => 10) - end - - it 'should have the stubbed size' - my_helper.size.should == 10 - end -end -``` - -### Adding expectations with mocks - -It's possible to combine the concepts of stubbing and expectations so that a -method has to be called for the test to pass (like an expectation), and can -return a fixed value (like a stub). - -```ruby -describe "mocking a method on an object" do - let(:my_helper) do - ['foo', 'bar', 'baz'] - end - - describe "when mocking the size" do - before do - my_helper.expects(:size).returns 10 - end - - it "adds an expectation that a method was called" do - my_helper.size - end - end -end -``` - -Like stubs, entire objects can be mocked. - -```ruby -describe "mocking an object" do - let(:my_helper) do - mock(:not_an_array) - end - - before do - not_an_array.expects(:size).returns 10 - end - - it "adds an expectation that the method was called" do - not_an_array.size - end -end -``` -### Writing tests without side effects - -When properly written each test should be able to run in isolation, and tests -should be able to be run in any order. This makes tests more reliable and allows -a single test to be run if only that test is failing, instead of running all -17000+ tests each time something is changed. However, there are a number of ways -that can make tests fail when run in isolation or out of order. - -#### Using instance variables - -Puppet has a number of older tests that use `before` blocks and instance -variables to set up fixture data, instead of `let` blocks. These can retain -state between tests, which can lead to test failures when tests are run out of -order. - -```ruby -# test.rb -RSpec.configure do |c| - c.mock_framework = :mocha -end - -describe "fixture data" do - describe "using instance variables" do - - # BAD - before :all do - # This fixture will be created only once and will retain the `foo` stub - # between tests. - @fixture = stub 'test data' - end - - it "can be stubbed" do - @fixture.stubs(:foo).returns :bar - @fixture.foo.should == :bar - end - - it "should not keep state between tests" do - # The foo stub was added in the previous test and shouldn't be present - # in this test. - expect { @fixture.foo }.to raise_error - end - end - - describe "using `let` blocks" do - - # GOOD - # This will be recreated between tests so that state isn't retained. - let(:fixture) { stub 'test data' } - - it "can be stubbed" do - fixture.stubs(:foo).returns :bar - fixture.foo.should == :bar - end - - it "should not keep state between tests" do - # since let blocks are regenerated between tests, the foo stub added in - # the previous test will not be present here. - expect { fixture.foo }.to raise_error - end - end -end -``` - -``` -bundle exec rspec test.rb -fd - -fixture data - using instance variables - can be stubbed - should not keep state between tests (FAILED - 1) - using `let` blocks - can be stubbed - should not keep state between tests - -Failures: - - 1) fixture data using instance variables should not keep state between tests - Failure/Error: expect { @fixture.foo }.to raise_error - expected Exception but nothing was raised - # ./test.rb:17:in `block (3 levels) in ' - -Finished in 0.00248 seconds -4 examples, 1 failure - -Failed examples: - -rspec ./test.rb:16 # fixture data using instance variables should not keep state between tests -``` - - -### RSpec references - - * RSpec core docs: https://www.relishapp.com/rspec/rspec-core/docs - * RSpec guidelines with Ruby: http://betterspecs.org/ - -### Puppet-acceptance - -[beaker]: https://github.com/puppetlabs/beaker -[test::unit]: http://test-unit.rubyforge.org/ - -Puppet has a custom acceptance testing framework called [beaker][beaker] for -running acceptance tests. -Beaker runs the tests by configuring one or more VMs, copying the test cases -onto the VMs, performing the tests and collecting the results, and ensuring that -the results match the intended behavior. It uses [test::unit][test::unit] to -perform the actual assertions. - -For a detailed guide to running the acceptance tests locally on vagrant boxes, -see the `acceptance/README.md` document in this repo. - -# UTF-8 Handling # - -As Ruby 1.9 becomes more commonly used with Puppet, developers should be aware -of major changes to the way Strings and Regexp objects are handled. -Specifically, every instance of these two classes will have an encoding -attribute determined in a number of ways. - - * If the source file has an encoding specified in the magic comment at the - top, the instance will take on that encoding. - * Otherwise, the encoding will be determined by the LC\_LANG or LANG - environment variables. - * Otherwise, the encoding will default to ASCII-8BIT - -## References ## - -Excellent information about the differences between encodings in Ruby 1.8 and -Ruby 1.9 is published in this blog series: -[Understanding M17n](http://links.puppetlabs.com/understanding_m17n) - -## Encodings of Regexp and String instances ## - -In general, please be aware that Ruby 1.9 regular expressions need to be -compatible with the encoding of a string being used to match them. If they are -not compatible you can expect to receive and error such as: - - Encoding::CompatibilityError: incompatible encoding regexp match (ASCII-8BIT - regexp with UTF-8 string) - -In addition, some escape sequences were valid in Ruby 1.8 are no longer valid -in 1.9 if the regular expression is not marked as an ASCII-8BIT object. You -may expect errors like this in this situation: - - SyntaxError: (irb):7: invalid multibyte escape: /\xFF/ - -This error is particularly common when serializing a string to other -representations like JSON or YAML. To resolve the problem you can explicitly -mark the regular expression as ASCII-8BIT using the /n flag: - - "a" =~ /\342\230\203/n - -Finally, any time you're thinking of a string as an array of bytes rather than -an array of characters, common when escaping a string, you should work with -everything in ASCII-8BIT. Changing the encoding will not change the data -itself and allow the Regexp and the String to deal with bytes rather than -characters. - -Puppet provides a monkey patch to String which returns an encoding suitable for -byte manipulations: - - # Example of how to escape non ASCII printable characters for YAML. - >> snowman = "☃" - >> snowman.to_ascii8bit.gsub(/([\x80-\xFF])/n) { |x| "\\x#{x.unpack("C")[0].to_s(16)} } - => "\\xe2\\x98\\x83" - -If the Regexp is not marked as ASCII-8BIT using /n, then you can expect the -SyntaxError, invalid multibyte escape as mentioned above. - -# Windows # - -If you'd like to run Puppet from source on Windows platforms, the -include `ext/envpuppet.bat` will help. - -To quickly run Puppet from source, assuming you already have Ruby installed -from [rubyinstaller.org](http://rubyinstaller.org). - - C:\> cd C:\work\puppet - C:\work\puppet> set PATH=%PATH%;C:\work\puppet\ext - C:\work\puppet> envpuppet bundle install - C:\work\puppet> envpuppet puppet --version - 2.7.9 - -When writing a test that cannot possibly run on Windows, e.g. there is -no mount type on windows, do the following: - - describe Puppet::MyClass, :unless => Puppet.features.microsoft_windows? do - .. - end - -If the test doesn't currently pass on Windows, e.g. due to on going porting, then use an rspec conditional pending block: - - pending("porting to Windows", :if => Puppet.features.microsoft_windows?) do - - end - - pending("porting to Windows", :if => Puppet.features.microsoft_windows?) do - - end - -Then run the test as: - - C:\work\puppet> envpuppet bundle exec rspec spec - -## Common Issues ## - - * Don't assume file paths start with '/', as that is not a valid path on - Windows. Use Puppet::Util.absolute\_path? to validate that a path is fully - qualified. - - * Use File.expand\_path('/tmp') in tests to generate a fully qualified path - that is valid on POSIX and Windows. In the latter case, the current working - directory will be used to expand the path. - - * Always use binary mode when performing file I/O, unless you explicitly want - Ruby to translate between unix and dos line endings. For example, opening an - executable file in text mode will almost certainly corrupt the resulting - stream, as will occur when using: - - IO.open(path, 'r') { |f| ... } - IO.read(path) - - If in doubt, specify binary mode explicitly: - - IO.open(path, 'rb') - - * Don't assume file paths are separated by ':'. Use `File::PATH_SEPARATOR` - instead, which is ':' on POSIX and ';' on Windows. - - * On Windows, `File::SEPARATOR` is '/', and `File::ALT_SEPARATOR` is '\'. On - POSIX systems, `File::ALT_SEPARATOR` is nil. In general, use '/' as the - separator as most Windows APIs, e.g. CreateFile, accept both types of - separators. - - * Don't use waitpid/waitpid2 if you need the child process' exit code, - as the child process may exit before it has a chance to open the - child's HANDLE and retrieve its exit code. Use Puppet::Util.execute. - - * Don't assume 'C' drive. Use environment variables to look these up: - - "#{ENV['windir']}/system32/netsh.exe" # Configuration Directory # @@ -669,88 +38,6 @@ Please do not monkey patch the constant `Puppet::PUPPETVERSION` or obtain the version using the constant. The only supported way to set and get the Puppet version is through the accessor methods. -# Static Compiler - -The static compiler was added to Puppet in the 2.7.0 release. -[1](http://links.puppetlabs.com/static-compiler-announce) - -The static compiler is intended to provide a configuration catalog that -requires a minimal amount of network communication in order to apply the -catalog to the system. As implemented in Puppet 2.7.x and Puppet 3.0.x this -intention takes the form of replacing all of the source parameters of File -resources with a content parameter containing an address in the form of a -checksum. The expected behavior is that the process applying the catalog to -the node will retrieve the file content from the FileBucket instead of the -FileServer. - -The high level approach can be described as follows. The `StaticCompiler` is a -terminus that inserts itself between the "normal" compiler terminus and the -request. The static compiler takes the resource catalog produced by the -compiler and filters all File resources. Any file resource that contains a -source parameter with a value starting with 'puppet://' is filtered in the -following way in a "standard" single master / networked agents deployment -scenario: - - 1. The content, owner, group, and mode values are retrieved from th - FileServer by the master. - 2. The file content is stored in the file bucket on the master. - 3. The source parameter value is stripped from the File resource. - 4. The content parameter value is set in the File resource using the form - '{XXX}1234567890' which can be thought of as a content address indexed by - checksum. - 5. The owner, group and mode values are set in the File resource if they are - not already set. - 6. The filtered catalog is returned in the response. - -In addition to the catalog terminus, the process requesting the catalog needs -to obtain the file content. The default behavior of `puppet agent` is to -obtain file contents from the local client bucket. The method we expect users -to employ to reconfigure the agent to use the server bucket is to declare the -`Filebucket[puppet]` resource with the address of the master. For example: - - node default { - filebucket { puppet: - server => $server, - path => false, - } - class { filetest: } - } - -This special filebucket resource named "puppet" will cause the agent to fetch -file contents specified by checksum from the remote filebucket instead of the -default clientbucket. - -## Trying out the Static Compiler - -Create a module that recursively downloads something. The jeffmccune-filetest -module will recursively copy the rubygems source tree. - - $ bundle exec puppet module install jeffmccune-filetest - -Start the master with the StaticCompiler turned on: - - $ bundle exec puppet master \ - --catalog_terminus=static_compiler \ - --verbose \ - --no-daemonize - -Add the special Filebucket[puppet] resource: - - # site.pp - node default { - filebucket { puppet: server => $server, path => false } - class { filetest: } - } - -Get the static catalog: - - $ bundle exec puppet agent --test - -You should expect all file metadata to be contained in the catalog, including a -checksum representing the content. When managing an out of sync file resource, -the real contents should be fetched from the server instead of the -clientbucket. - Package Maintainers ===== diff --git a/docs/acceptance_tests.md b/docs/acceptance_tests.md index aa30ddb35..dcb2d7bab 100644 --- a/docs/acceptance_tests.md +++ b/docs/acceptance_tests.md @@ -11,7 +11,7 @@ Table of Contents General Notes ------------- -The rake tasks for running the tests are defined by the Rakefile in the same directory as this file. +The rake tasks for running the tests are defined by the Rakefile in the acceptance test directory. These tasks come with some documentation: `rake -T` will give short descriptions, and a `rake -D` will give full descriptions with information on ENV options required and optional for the various tasks. If you are setting up a new repository for acceptance, you will need to bundle install first. This step assumes you have ruby and the bundler gem installed. diff --git a/docs/catalogs.md b/docs/catalogs.md new file mode 100644 index 000000000..6702a9b27 --- /dev/null +++ b/docs/catalogs.md @@ -0,0 +1,122 @@ +# Two Types of Catalog + +When working on subsystems of Puppet that deal with the catalog it is important +to be aware of the two different types of Catalog. Developers will often find +this difference while working on the static compiler and types and providers. + +The two different types of catalog becomes relevant when writing spec tests +because we frequently need to wire up a fake catalog so that we can exercise +types, providers, or termini that filter the catalog. + +The two different types of catalogs are so-called "resource" catalogs and "RAL" +(resource abstraction layer) catalogs. At a high level, the resource catalog +is the in-memory object we serialize and transfer around the network. The +compiler terminus is expected to produce a resource catalog. The agent takes a +resource catalog and converts it into a RAL catalog. The RAL catalog is what +is used to apply the configuration model to the system. + +Resource dependency information is most easily obtained from a RAL catalog by +walking the graph instance produced by the `relationship_graph` method. + +### Resource Catalog + +If you're writing spec tests for something that deals with a catalog "server +side," a new catalog terminus for example, then you'll be dealing with a +resource catalog. You can produce a resource catalog suitable for spec tests +using something like this: + + let(:catalog) do + catalog = Puppet::Resource::Catalog.new("node-name-val") # NOT certname! + rsrc = Puppet::Resource.new("file", "sshd_config", + :parameters => { + :ensure => 'file', + :source => 'puppet:///modules/filetest/sshd_config', + } + ) + rsrc.file = 'site.pp' + rsrc.line = 21 + catalog.add_resource(rsrc) + end + +The resources in this catalog may be accessed using `catalog.resources`. +Resource dependencies are not easily walked using a resource catalog however. +To walk the dependency tree convert the catalog to a RAL catalog as described +in + +### RAL Catalog + +The resource catalog may be converted to a RAL catalog using `catalog.to_ral`. +The RAL catalog contains `Puppet::Type` instances instead of `Puppet::Resource` +instances as is the case with the resource catalog. + +One very useful feature of the RAL catalog are the methods to work with +resource relationships. For example: + + irb> catalog = catalog.to_ral + irb> graph = catalog.relationship_graph + irb> pp graph.edges + [{ Notify[alpha] => File[/tmp/file_20.txt] }, + { Notify[alpha] => File[/tmp/file_21.txt] }, + { Notify[alpha] => File[/tmp/file_22.txt] }, + { Notify[alpha] => File[/tmp/file_23.txt] }, + { Notify[alpha] => File[/tmp/file_24.txt] }, + { Notify[alpha] => File[/tmp/file_25.txt] }, + { Notify[alpha] => File[/tmp/file_26.txt] }, + { Notify[alpha] => File[/tmp/file_27.txt] }, + { Notify[alpha] => File[/tmp/file_28.txt] }, + { Notify[alpha] => File[/tmp/file_29.txt] }, + { File[/tmp/file_20.txt] => Notify[omega] }, + { File[/tmp/file_21.txt] => Notify[omega] }, + { File[/tmp/file_22.txt] => Notify[omega] }, + { File[/tmp/file_23.txt] => Notify[omega] }, + { File[/tmp/file_24.txt] => Notify[omega] }, + { File[/tmp/file_25.txt] => Notify[omega] }, + { File[/tmp/file_26.txt] => Notify[omega] }, + { File[/tmp/file_27.txt] => Notify[omega] }, + { File[/tmp/file_28.txt] => Notify[omega] }, + { File[/tmp/file_29.txt] => Notify[omega] }] + +If the `relationship_graph` method is throwing exceptions at you, there's a +good chance the catalog is not a RAL catalog. + +## Settings Catalog ## + +Be aware that Puppet creates a mini catalog and applies this catalog locally to +manage file resource from the settings. This behavior made it difficult and +time consuming to track down a race condition in +[2888](http://projects.puppetlabs.com/issues/2888). + +Even more surprising, the `File[puppetdlockfile]` resource is only added to the +settings catalog if the file exists on disk. This caused the race condition as +it will exist when a separate process holds the lock while applying the +catalog. + +It may be sufficient to simply be aware of the settings catalog and the +potential for race conditions it presents. An effective way to be reasonably +sure and track down the problem is to wrap the File.open method like so: + + # We're wrapping ourselves around the File.open method. + # As described at: http://goo.gl/lDsv6 + class File + WHITELIST = [ /pidlock.rb:39/ ] + + class << self + alias xxx_orig_open open + end + + def self.open(name, *rest, &block) + # Check the whitelist for any "good" File.open calls against the # + puppetdlock file + white_listed = caller(0).find do |line| + JJM_WHITELIST.find { |re| re.match(line) } + end + + # If you drop into IRB here, take a look at your caller, it might be + # the ghost in the machine you're looking for. + binding.pry if name =~ /puppetdlock/ and not white_listed + xxx_orig_open(name, *rest, &block) + end + end + +The settings catalog is populated by the `Puppet::Util::Settings#to\_catalog` +method. diff --git a/docs/index.md b/docs/index.md index b7276af4a..283f4b28f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -3,4 +3,15 @@ Setting up and running tests * [Quickstart Guide](quickstart.md) +* [RSpec Tutorial](rspec_tutorial.md) * [Running acceptance tests](acceptance_tests.md) + +Developer References + +* [Various Catalog Forms](catalogs.md) +* [Windows](windows.md) +* [Unicode and you](unicode.md) + +Misc + +* [Static Compiler](static_compiler.md) diff --git a/docs/rspec_tutorial.md b/docs/rspec_tutorial.md new file mode 100644 index 000000000..4545c0096 --- /dev/null +++ b/docs/rspec_tutorial.md @@ -0,0 +1,362 @@ +# A brief introduction to testing in Puppet + +Puppet relies heavily on automated testing to ensure that Puppet behaves as +expected and that new features don't interfere with existing behavior. There are +three primary sets of tests that Puppet uses: _unit tests_, _integration tests_, +and _acceptance tests_. + +- - - + +Unit tests are used to test the individual components of Puppet to ensure that +they function as expected in isolation. Unit tests are designed to hide the +actual system implementations and provide canned information so that only the +intended behavior is tested, rather than the targeted code and everything else +connected to it. Unit tests should never affect the state of the system that's +running the test. + +- - - + +Integration tests serve to test different units of code together to ensure that +they interact correctly. While individual methods might perform correctly, when +used with the rest of the system they might fail, so integration tests are a +higher level version of unit tests that serve to check the behavior of +individual subsystems. + +All of the unit and integration tests for Puppet are kept in the spec/ directory. + +- - - + +Acceptance tests are used to test high level behaviors of Puppet that deal with +a number of concerns and aren't easily tested with normal unit tests. Acceptance +tests function by changing system state and checking the system after +the fact to make sure that the intended behavior occurred. Because of this +acceptance tests can be destructive, so the systems being tested should be +throwaway systems. + +All of the acceptance tests for Puppet are kept in the acceptance/tests/ +directory. + +## Testing dependency version requirements + +Puppet is only compatible with certain versions of RSpec and Mocha. If you are +not using Bundler to install the required test libraries you must ensure that +you are using the right library versions. Using unsupported versions of Mocha +and RSpec will probably display many spurious failures. The supported versions +of RSpec and Mocha can be found in the project Gemfile. + +## Puppet Continuous integration + + * Travis-ci (unit tests only): https://travis-ci.org/puppetlabs/puppet/ + * Jenkins (unit and acceptance tests): https://jenkins.puppetlabs.com/view/Puppet%20FOSS/ + +## RSpec + +Puppet uses RSpec to perform unit and integration tests. RSpec handles a number +of concerns to make testing easier: + + * Executing examples and ensuring the actual behavior matches the expected behavior (examples) + * Grouping tests (describe and contexts) + * Setting up test environments and cleaning up afterwards (before and after blocks) + * Isolating tests (mocks and stubs) + +#### Examples and expectations + +At the most basic level, RSpec provides a framework for executing tests (which +are called examples) and ensuring that the actual behavior matches the expected +behavior (which are done with expectations) + +```ruby +# This is an example; it sets the test name and defines the test to run +specify "one equals one" do + # 'should' is an expectation; it adds a check to make sure that the left argument + # matches the right argument + 1.should == 1 +end + +# Examples can be declared with either 'it' or 'specify' +it "one doesn't equal two" do + 1.should_not == 2 +end +``` + +Good examples generally do as little setup as possible and only test one or two +things; it makes tests easier to understand and easier to debug. + +More complete documentation on expectations is available at https://www.relishapp.com/rspec/rspec-expectations/docs + +### Example groups + +Example groups are fairly self explanatory; they group similar examples into a +set. + +```ruby +describe "the number one" do + + it "is larger than zero" do + 1.should be > 0 + end + + it "is an odd number" do + 1.odd?.should be true + end + + it "is not nil" do + 1.should_not be_nil + end +end +``` + +Example groups have a number of uses that we'll get into later, but one of the +simplest demonstrations of what they do is how they help to format +documentation: + +``` +rspec ex.rb --format documentation + +the number one + is larger than zero + is an odd number + is not nil + +Finished in 0.00516 seconds +3 examples, 0 failures +``` + +### Setting up and tearing down tests + +Examples may require some setup before they can run, and might need to clean up +afterwards. `before` and `after` blocks can be used before this, and can be +used inside of example groups to limit how many examples they affect. + +```ruby + +describe "something that could warn" do + before :each do + # Disable warnings for this test + $VERBOSE = nil + end + + after do + # Enable warnings afterwards + $VERBOSE = true + end + + it "doesn't generate a warning" do + MY_CONSTANT = 1 + # reassigning a normally prints out 'warning: already initialized constant FOO' + MY_CONSTANT = 2 + end +end +``` + +### Setting up helper data + +Some examples may require setting up data before hand and making it available to +tests. RSpec provides helper methods with the `let` method call that can be used +inside of tests. + +```ruby +describe "a helper object" do + # This creates an array with three elements that we can retrieve in tests. A + # new copy will be made for each test. + let(:my_helper) do + ['foo', 'bar', 'baz'] + end + + it "should be an array" do + my_helper.should be_a_kind_of Array + end + + it "should have three elements" do + my_helper.should have(3).items + end +end +``` + +Like `before` blocks, helper objects like this are used to avoid doing a lot of +setup in individual examples and share setup between similar tests. + +### Isolating tests with stubs + +RSpec allows you to provide fake data during testing to make sure that +individual tests are only running the code being tested. You can stub out entire +objects, or just stub out individual methods on an object. When a method is +stubbed the method itself will never be called. + +While RSpec comes with its own stubbing framework, Puppet uses the Mocha +framework. + +A brief usage guide for Mocha is available at http://gofreerange.com/mocha/docs/#Usage, +and an overview of Mocha expectations is available at http://gofreerange.com/mocha/docs/Mocha/Expectation.html + +```ruby +describe "stubbing a method on an object" do + let(:my_helper) do + ['foo', 'bar', 'baz'] + end + + it 'should have three items before being stubbed' do + my_helper.size.should == 3 + end + + describe 'when stubbing the size' do + before do + my_helper.stubs(:size).returns 10 + end + + it 'should have the stubbed value for size' do + my_helper.size.should == 10 + end + end +end +``` + +Entire objects can be stubbed as well. + +```ruby +describe "stubbing an object" do + let(:my_helper) do + stub(:not_an_array, :size => 10) + end + + it 'should have the stubbed size' + my_helper.size.should == 10 + end +end +``` + +### Adding expectations with mocks + +It's possible to combine the concepts of stubbing and expectations so that a +method has to be called for the test to pass (like an expectation), and can +return a fixed value (like a stub). + +```ruby +describe "mocking a method on an object" do + let(:my_helper) do + ['foo', 'bar', 'baz'] + end + + describe "when mocking the size" do + before do + my_helper.expects(:size).returns 10 + end + + it "adds an expectation that a method was called" do + my_helper.size + end + end +end +``` + +Like stubs, entire objects can be mocked. + +```ruby +describe "mocking an object" do + let(:my_helper) do + mock(:not_an_array) + end + + before do + not_an_array.expects(:size).returns 10 + end + + it "adds an expectation that the method was called" do + not_an_array.size + end +end +``` +### Writing tests without side effects + +When properly written each test should be able to run in isolation, and tests +should be able to be run in any order. This makes tests more reliable and allows +a single test to be run if only that test is failing, instead of running all +17000+ tests each time something is changed. However, there are a number of ways +that can make tests fail when run in isolation or out of order. + +#### Using instance variables + +Puppet has a number of older tests that use `before` blocks and instance +variables to set up fixture data, instead of `let` blocks. These can retain +state between tests, which can lead to test failures when tests are run out of +order. + +```ruby +# test.rb +RSpec.configure do |c| + c.mock_framework = :mocha +end + +describe "fixture data" do + describe "using instance variables" do + + # BAD + before :all do + # This fixture will be created only once and will retain the `foo` stub + # between tests. + @fixture = stub 'test data' + end + + it "can be stubbed" do + @fixture.stubs(:foo).returns :bar + @fixture.foo.should == :bar + end + + it "should not keep state between tests" do + # The foo stub was added in the previous test and shouldn't be present + # in this test. + expect { @fixture.foo }.to raise_error + end + end + + describe "using `let` blocks" do + + # GOOD + # This will be recreated between tests so that state isn't retained. + let(:fixture) { stub 'test data' } + + it "can be stubbed" do + fixture.stubs(:foo).returns :bar + fixture.foo.should == :bar + end + + it "should not keep state between tests" do + # since let blocks are regenerated between tests, the foo stub added in + # the previous test will not be present here. + expect { fixture.foo }.to raise_error + end + end +end +``` + +``` +bundle exec rspec test.rb -fd + +fixture data + using instance variables + can be stubbed + should not keep state between tests (FAILED - 1) + using `let` blocks + can be stubbed + should not keep state between tests + +Failures: + + 1) fixture data using instance variables should not keep state between tests + Failure/Error: expect { @fixture.foo }.to raise_error + expected Exception but nothing was raised + # ./test.rb:17:in `block (3 levels) in ' + +Finished in 0.00248 seconds +4 examples, 1 failure + +Failed examples: + +rspec ./test.rb:16 # fixture data using instance variables should not keep state between tests +``` + +### RSpec references + + * RSpec core docs: https://www.relishapp.com/rspec/rspec-core/docs + * RSpec guidelines with Ruby: http://betterspecs.org/ + diff --git a/docs/static_compiler.md b/docs/static_compiler.md new file mode 100644 index 000000000..8a3270eab --- /dev/null +++ b/docs/static_compiler.md @@ -0,0 +1,83 @@ +# Static Compiler + +The static compiler was added to Puppet in the 2.7.0 release. +[1](http://links.puppetlabs.com/static-compiler-announce) + +The static compiler is intended to provide a configuration catalog that +requires a minimal amount of network communication in order to apply the +catalog to the system. As implemented in Puppet 2.7.x and Puppet 3.0.x this +intention takes the form of replacing all of the source parameters of File +resources with a content parameter containing an address in the form of a +checksum. The expected behavior is that the process applying the catalog to +the node will retrieve the file content from the FileBucket instead of the +FileServer. + +The high level approach can be described as follows. The `StaticCompiler` is a +terminus that inserts itself between the "normal" compiler terminus and the +request. The static compiler takes the resource catalog produced by the +compiler and filters all File resources. Any file resource that contains a +source parameter with a value starting with 'puppet://' is filtered in the +following way in a "standard" single master / networked agents deployment +scenario: + + 1. The content, owner, group, and mode values are retrieved from th + FileServer by the master. + 2. The file content is stored in the file bucket on the master. + 3. The source parameter value is stripped from the File resource. + 4. The content parameter value is set in the File resource using the form + '{XXX}1234567890' which can be thought of as a content address indexed by + checksum. + 5. The owner, group and mode values are set in the File resource if they are + not already set. + 6. The filtered catalog is returned in the response. + +In addition to the catalog terminus, the process requesting the catalog needs +to obtain the file content. The default behavior of `puppet agent` is to +obtain file contents from the local client bucket. The method we expect users +to employ to reconfigure the agent to use the server bucket is to declare the +`Filebucket[puppet]` resource with the address of the master. For example: + + node default { + filebucket { puppet: + server => $server, + path => false, + } + class { filetest: } + } + +This special filebucket resource named "puppet" will cause the agent to fetch +file contents specified by checksum from the remote filebucket instead of the +default clientbucket. + +## Trying out the Static Compiler + +Create a module that recursively downloads something. The jeffmccune-filetest +module will recursively copy the rubygems source tree. + + $ bundle exec puppet module install jeffmccune-filetest + +Start the master with the StaticCompiler turned on: + + $ bundle exec puppet master \ + --catalog_terminus=static_compiler \ + --verbose \ + --no-daemonize + +Add the special Filebucket[puppet] resource: + + # site.pp + node default { + filebucket { puppet: server => $server, path => false } + class { filetest: } + } + +Get the static catalog: + + $ bundle exec puppet agent --test + +You should expect all file metadata to be contained in the catalog, including a +checksum representing the content. When managing an out of sync file resource, +the real contents should be fetched from the server instead of the +clientbucket. + + diff --git a/docs/unicode.md b/docs/unicode.md new file mode 100644 index 000000000..a6c81df82 --- /dev/null +++ b/docs/unicode.md @@ -0,0 +1,56 @@ +# UTF-8 Handling # + +As Ruby 1.9 becomes more commonly used with Puppet, developers should be aware +of major changes to the way Strings and Regexp objects are handled. +Specifically, every instance of these two classes will have an encoding +attribute determined in a number of ways. + + * If the source file has an encoding specified in the magic comment at the + top, the instance will take on that encoding. + * Otherwise, the encoding will be determined by the LC\_LANG or LANG + environment variables. + * Otherwise, the encoding will default to ASCII-8BIT + +## References ## + +Excellent information about the differences between encodings in Ruby 1.8 and +Ruby 1.9 is published in this blog series: +[Understanding M17n](http://links.puppetlabs.com/understanding_m17n) + +## Encodings of Regexp and String instances ## + +In general, please be aware that Ruby 1.9 regular expressions need to be +compatible with the encoding of a string being used to match them. If they are +not compatible you can expect to receive and error such as: + + Encoding::CompatibilityError: incompatible encoding regexp match (ASCII-8BIT + regexp with UTF-8 string) + +In addition, some escape sequences were valid in Ruby 1.8 are no longer valid +in 1.9 if the regular expression is not marked as an ASCII-8BIT object. You +may expect errors like this in this situation: + + SyntaxError: (irb):7: invalid multibyte escape: /\xFF/ + +This error is particularly common when serializing a string to other +representations like JSON or YAML. To resolve the problem you can explicitly +mark the regular expression as ASCII-8BIT using the /n flag: + + "a" =~ /\342\230\203/n + +Finally, any time you're thinking of a string as an array of bytes rather than +an array of characters, common when escaping a string, you should work with +everything in ASCII-8BIT. Changing the encoding will not change the data +itself and allow the Regexp and the String to deal with bytes rather than +characters. + +Puppet provides a monkey patch to String which returns an encoding suitable for +byte manipulations: + + # Example of how to escape non ASCII printable characters for YAML. + >> snowman = "☃" + >> snowman.to_ascii8bit.gsub(/([\x80-\xFF])/n) { |x| "\\x#{x.unpack("C")[0].to_s(16)} } + => "\\xe2\\x98\\x83" + +If the Regexp is not marked as ASCII-8BIT using /n, then you can expect the +SyntaxError, invalid multibyte escape as mentioned above. diff --git a/docs/windows.md b/docs/windows.md new file mode 100644 index 000000000..1a61f8b45 --- /dev/null +++ b/docs/windows.md @@ -0,0 +1,73 @@ +# Windows # + +If you'd like to run Puppet from source on Windows platforms, the +include `ext/envpuppet.bat` will help. + +To quickly run Puppet from source, assuming you already have Ruby installed +from [rubyinstaller.org](http://rubyinstaller.org). + + C:\> cd C:\work\puppet + C:\work\puppet> set PATH=%PATH%;C:\work\puppet\ext + C:\work\puppet> envpuppet bundle install + C:\work\puppet> envpuppet puppet --version + 2.7.9 + +When writing a test that cannot possibly run on Windows, e.g. there is +no mount type on windows, do the following: + + describe Puppet::MyClass, :unless => Puppet.features.microsoft_windows? do + .. + end + +If the test doesn't currently pass on Windows, e.g. due to on going porting, then use an rspec conditional pending block: + + pending("porting to Windows", :if => Puppet.features.microsoft_windows?) do + + end + + pending("porting to Windows", :if => Puppet.features.microsoft_windows?) do + + end + +Then run the test as: + + C:\work\puppet> envpuppet bundle exec rspec spec + +## Common Issues ## + + * Don't assume file paths start with '/', as that is not a valid path on + Windows. Use Puppet::Util.absolute\_path? to validate that a path is fully + qualified. + + * Use File.expand\_path('/tmp') in tests to generate a fully qualified path + that is valid on POSIX and Windows. In the latter case, the current working + directory will be used to expand the path. + + * Always use binary mode when performing file I/O, unless you explicitly want + Ruby to translate between unix and dos line endings. For example, opening an + executable file in text mode will almost certainly corrupt the resulting + stream, as will occur when using: + + IO.open(path, 'r') { |f| ... } + IO.read(path) + + If in doubt, specify binary mode explicitly: + + IO.open(path, 'rb') + + * Don't assume file paths are separated by ':'. Use `File::PATH_SEPARATOR` + instead, which is ':' on POSIX and ';' on Windows. + + * On Windows, `File::SEPARATOR` is '/', and `File::ALT_SEPARATOR` is '\'. On + POSIX systems, `File::ALT_SEPARATOR` is nil. In general, use '/' as the + separator as most Windows APIs, e.g. CreateFile, accept both types of + separators. + + * Don't use waitpid/waitpid2 if you need the child process' exit code, + as the child process may exit before it has a chance to open the + child's HANDLE and retrieve its exit code. Use Puppet::Util.execute. + + * Don't assume 'C' drive. Use environment variables to look these up: + + "#{ENV['windir']}/system32/netsh.exe" + From 22e6b07ab157c6d448f130259e36cf4209d1237e Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Thu, 6 Feb 2014 14:47:02 -0800 Subject: [PATCH 580/800] (doc) Remove remaining developer docs The rest of the items in the developer docs (those not moved to the docs directory) are of limited value. Instead of keeping these docs around and having them fall out of date, they are just going to go away. --- README_DEVELOPER.md | 55 --------------------------------------------- 1 file changed, 55 deletions(-) delete mode 100644 README_DEVELOPER.md diff --git a/README_DEVELOPER.md b/README_DEVELOPER.md deleted file mode 100644 index f54e72ae1..000000000 --- a/README_DEVELOPER.md +++ /dev/null @@ -1,55 +0,0 @@ -# Developer README # - -This file is intended to provide a place for developers and contributors to -document what other developers need to know about changes made to Puppet. - -### Dependencies - - - -# Configuration Directory # - -In Puppet 3.x we've simplified the behavior of selecting a configuration file -to load. The intended behavior of reading `puppet.conf` is: - - 1. Use the explicit configuration provided by --confdir or --config if present - 2. If running as root (`Puppet.features.root?`) then use the system - `puppet.conf` - 3. Otherwise, use `~/.puppet/puppet.conf`. - -When Puppet master is started from Rack, Puppet 3.x will read from -~/.puppet/puppet.conf by default. This is intended behavior. Rack -configurations should start Puppet master with an explicit configuration -directory using `ARGV << "--confdir" << "/etc/puppet"`. Please see the -`ext/rack/config.ru` file for an up-to-date example. - -# Determining the Puppet Version - -If you need to programmatically work with the Puppet version, please use the -following: - - require 'puppet/version' - # Get the version baked into the sourcecode: - version = Puppet.version - # Set the version (e.g. in a Rakefile based on `git describe`) - Puppet.version = '2.3.4' - -Please do not monkey patch the constant `Puppet::PUPPETVERSION` or obtain the -version using the constant. The only supported way to set and get the Puppet -version is through the accessor methods. - -Package Maintainers -===== - -Software Version API ------ - -Please see the public API regarding the software version as described in -`lib/puppet/version.rb`. Puppet provides the means to easily specify the exact -version of the software packaged using the VERSION file, for example: - - $ git describe --match "3.0.*" > lib/puppet/VERSION - $ ruby -r puppet/version -e 'puts Puppet.version' - 3.0.1-260-g9ca4e54 - -EOF From e97833006536869af67a61d29896d234cb7c73bb Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Thu, 6 Feb 2014 15:00:33 -0800 Subject: [PATCH 581/800] (doc) Explain some basics for profiling puppet This doesn't make it all clear, but it should give a good starting point for how you might go about profiling puppet. --- docs/index.md | 1 + docs/profiling.md | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 docs/profiling.md diff --git a/docs/index.md b/docs/index.md index 283f4b28f..240d30bab 100644 --- a/docs/index.md +++ b/docs/index.md @@ -8,6 +8,7 @@ Setting up and running tests Developer References +* [Profiling and Benchmarking](profiling.md) * [Various Catalog Forms](catalogs.md) * [Windows](windows.md) * [Unicode and you](unicode.md) diff --git a/docs/profiling.md b/docs/profiling.md new file mode 100644 index 000000000..54c71f447 --- /dev/null +++ b/docs/profiling.md @@ -0,0 +1,41 @@ +# Profiling Puppet + +Puppet is a beast. Puppet is at times a very *slow* beast. Maybe we can find +what is making it slow and fix it. + +## Coarse Grained Profiling + +There is a built-in system of profiling that can be used to identify some slow +spots. This can only work with code that is explicitly instrumented, which, at +the time of this writing, is only the compiler. However, to use it you only +need to use `--profile` on the master. The timing information will be output to +the logs and tagged with the word "PROFILE". + +For the agent there is actually a second system: evaltrace. You can enable this +on the agent by passing it `--evaltrace`. Timing information for each resource +will be output to the logs. + +## Using a Ruby Profiler + +For much finer grained profiling, you'll want to use +[ruby-prof](http://rubygems.org/gems/ruby-prof). Once you have the gem +installed you can either modify the code to profile a certain section (using +RubyProf.profile) or run the master with ruby-prof by adding `use +Rack::RubyProf, :path => '/temp/profile'` to the config.ru for your master. + +## Running the Benchmarks + +Puppet has a number of benchmark scenarios to pinpoint problems in specific, +known, use cases. The benchmark scenarios live in the `benchmarks` directory. + +To run a scenario you do: + + bundle exec rake benchmark: + +If you have ruby-prof installed you can get a calltrace of the benchmark +scenario by running: + + bundle exec rake benchmark::profile + +The calltrace file is viewable with +[kcachegrind](http://kcachegrind.sourceforge.net/html/Home.html). From c65745ce30920917467ba787f25c335440321410 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Thu, 6 Feb 2014 15:56:08 -0800 Subject: [PATCH 582/800] (PUP-1574) Environment setting does not override apply/parser manifest Both `puppet apply` and `puppet parser validate` were relying on the legacy environment behavior to implicit draw in the setting of Puppet[:manifest]. But the new directory environments do not pay attention to Puppet[:manifest] and thus a call such as `puppet apply foo.pp --environment dir_env` would not apply foo.pp if dir_env was in the $environmentpath. This change explicitly creates a new environment based off the currently configured environment and overrides with the manifest this to be applied or validated. --- lib/puppet/application/apply.rb | 90 ++++++++++++---------- lib/puppet/face/parser.rb | 14 +++- lib/puppet/node/environment.rb | 12 +++ spec/integration/application/apply_spec.rb | 27 ++++++- spec/unit/application/apply_spec.rb | 11 --- spec/unit/face/parser_spec.rb | 64 +++++++++------ spec/unit/node/environment_spec.rb | 22 ++++++ 7 files changed, 156 insertions(+), 84 deletions(-) diff --git a/lib/puppet/application/apply.rb b/lib/puppet/application/apply.rb index 111eafcd4..78348f7d0 100644 --- a/lib/puppet/application/apply.rb +++ b/lib/puppet/application/apply.rb @@ -170,7 +170,6 @@ Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License manifest = command_line.args.shift raise "Could not find file #{manifest}" unless Puppet::FileSystem.exist?(manifest) Puppet.warning("Only one file can be applied per run. Skipping #{command_line.args.join(', ')}") if command_line.args.size > 0 - Puppet[:manifest] = manifest end unless Puppet[:node_name_fact].empty? @@ -183,55 +182,62 @@ Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License facts.name = Puppet[:node_name_value] end - # Find our Node - unless node = Puppet::Node.indirection.find(Puppet[:node_name_value]) - raise "Could not find node #{Puppet[:node_name_value]}" - end + configured_environment = Puppet.lookup(:environments).get(Puppet[:environment]) + apply_environment = manifest ? + configured_environment.override_with(:manifest => manifest) : + configured_environment - # Merge in the facts. - node.merge(facts.values) if facts + Puppet.override(:environments => Puppet::Environments::Static.new(apply_environment)) do + # Find our Node + unless node = Puppet::Node.indirection.find(Puppet[:node_name_value]) + raise "Could not find node #{Puppet[:node_name_value]}" + end - # Allow users to load the classes that puppet agent creates. - if options[:loadclasses] - file = Puppet[:classfile] - if Puppet::FileSystem.exist?(file) - unless FileTest.readable?(file) - $stderr.puts "#{file} is not readable" - exit(63) + # Merge in the facts. + node.merge(facts.values) if facts + + # Allow users to load the classes that puppet agent creates. + if options[:loadclasses] + file = Puppet[:classfile] + if Puppet::FileSystem.exist?(file) + unless FileTest.readable?(file) + $stderr.puts "#{file} is not readable" + exit(63) + end + node.classes = ::File.read(file).split(/[\s\n]+/) end - node.classes = ::File.read(file).split(/[\s\n]+/) - end - end - - begin - # Compile our catalog - starttime = Time.now - catalog = Puppet::Resource::Catalog.indirection.find(node.name, :use_node => node) - - # Translate it to a RAL catalog - catalog = catalog.to_ral - - catalog.finalize - - catalog.retrieval_duration = Time.now - starttime - - if options[:write_catalog_summary] - catalog.write_class_file - catalog.write_resource_file end - exit_status = apply_catalog(catalog) + begin + # Compile our catalog + starttime = Time.now + catalog = Puppet::Resource::Catalog.indirection.find(node.name, :use_node => node) - if not exit_status + # Translate it to a RAL catalog + catalog = catalog.to_ral + + catalog.finalize + + catalog.retrieval_duration = Time.now - starttime + + if options[:write_catalog_summary] + catalog.write_class_file + catalog.write_resource_file + end + + exit_status = apply_catalog(catalog) + + if not exit_status + exit(1) + elsif options[:detailed_exitcodes] then + exit(exit_status) + else + exit(0) + end + rescue => detail + Puppet.log_exception(detail) exit(1) - elsif options[:detailed_exitcodes] then - exit(exit_status) - else - exit(0) end - rescue => detail - Puppet.log_exception(detail) - exit(1) end end diff --git a/lib/puppet/face/parser.rb b/lib/puppet/face/parser.rb index cb7aa229b..2950388e2 100644 --- a/lib/puppet/face/parser.rb +++ b/lib/puppet/face/parser.rb @@ -44,16 +44,22 @@ Puppet::Face.define(:parser, '0.0.1') do missing_files = [] files.each do |file| missing_files << file if ! Puppet::FileSystem.exist?(file) - Puppet[:manifest] = file - validate_manifest + validate_manifest(file) end raise Puppet::Error, "One or more file(s) specified did not exist:\n#{missing_files.collect {|f| " " * 3 + f + "\n"}}" if ! missing_files.empty? nil end end - def validate_manifest - Puppet.lookup(:environments).get(Puppet[:environment]).known_resource_types.clear + # @api private + def validate_manifest(manifest = nil) + configured_environment = Puppet.lookup(:environments).get(Puppet[:environment]) + validation_environment = manifest ? + configured_environment.override_with(:manifest => manifest) : + configured_environment + + validation_environment.known_resource_types.clear + rescue => detail Puppet.log_exception(detail) exit(1) diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb index 163d0d9a4..4c6a3cc72 100644 --- a/lib/puppet/node/environment.rb +++ b/lib/puppet/node/environment.rb @@ -119,6 +119,18 @@ class Puppet::Node::Environment @manifest = manifest end + # Creates a new Puppet::Node::Environment instance, overriding any of the passed + # parameters. + # + # @param env_params [Hash<{Symbol => String,Array}>] new environment + # parameters (:modulepath, :manifest) + # @return [Puppet::Node::Environment] + def override_with(env_params) + return self.class.create(name, + env_params[:modulepath] || modulepath, + env_params[:manifest] || manifest) + end + # @param [String] name Environment name to check for valid syntax. # @return [Boolean] true if name is valid # @api public diff --git a/spec/integration/application/apply_spec.rb b/spec/integration/application/apply_spec.rb index b040576bb..5ef732c48 100755 --- a/spec/integration/application/apply_spec.rb +++ b/spec/integration/application/apply_spec.rb @@ -11,7 +11,7 @@ describe "apply" do end describe "when applying provided catalogs" do - it "should be able to apply catalogs provided in a file in pson" do + it "can apply catalogs provided in a file in pson" do file_to_create = tmpfile("pson_catalog") catalog = Puppet::Resource::Catalog.new resource = Puppet::Resource.new(:file, file_to_create, :parameters => {:content => "my stuff"}) @@ -26,8 +26,29 @@ describe "apply" do puppet.apply - Puppet::FileSystem.exist?(file_to_create).should be_true - File.read(file_to_create).should == "my stuff" + expect(Puppet::FileSystem.exist?(file_to_create)).to be_true + expect(File.read(file_to_create)).to eq("my stuff") end end + + it "applies a given file even when a directory environment is specified" do + manifest = tmpfile("manifest.pp") + File.open(manifest, "w") do |f| + f.puts <<-EOF + notice('it was applied') + EOF + end + + env_loader = Puppet::Environments::Static.new( + Puppet::Node::Environment.create(:special, [], '') + ) + Puppet.override(:environments => env_loader) do + Puppet[:environment] = 'special' + puppet = Puppet::Application[:apply] + puppet.stubs(:command_line).returns(stub('command_line', :args => [manifest])) + expect { puppet.run_command }.to exit_with(0) + end + + expect(@logs.map(&:to_s)).to include('it was applied') + end end diff --git a/spec/unit/application/apply_spec.rb b/spec/unit/application/apply_spec.rb index 69da405fc..382436837 100755 --- a/spec/unit/application/apply_spec.rb +++ b/spec/unit/application/apply_spec.rb @@ -215,16 +215,6 @@ describe Puppet::Application::Apply do expect { @apply.main }.to exit_with 0 end - it "should set the manifest if a file is passed on command line and the file exists" do - manifest = tmpfile('site.pp') - FileUtils.touch(manifest) - @apply.command_line.stubs(:args).returns([manifest]) - - Puppet.expects(:[]=).with(:manifest,manifest) - - expect { @apply.main }.to exit_with 0 - end - it "should raise an error if a file is passed on command line and the file does not exist" do noexist = tmpfile('noexist.pp') @apply.command_line.stubs(:args).returns([noexist]) @@ -237,7 +227,6 @@ describe Puppet::Application::Apply do @apply.command_line.stubs(:args).returns([manifest, 'starwarsI', 'starwarsII']) - Puppet.expects(:[]=).with(:manifest,manifest) expect { @apply.main }.to exit_with 0 msg = @logs.find {|m| m.message =~ /Only one file can be applied per run/ } diff --git a/spec/unit/face/parser_spec.rb b/spec/unit/face/parser_spec.rb index a517ae641..78096befd 100644 --- a/spec/unit/face/parser_spec.rb +++ b/spec/unit/face/parser_spec.rb @@ -8,18 +8,49 @@ describe Puppet::Face[:parser, :current] do let(:parser) { Puppet::Face[:parser, :current] } - it "validates the configured site manifest when no files are given" do - Puppet[:manifest] = file_containing('site.pp', "{ invalid =>") - from_an_interactive_terminal + context "from an interactive terminal" do + before :each do + from_an_interactive_terminal + end - expect { parser.validate() }.to exit_with(1) - end + it "validates the configured site manifest when no files are given" do + Puppet[:manifest] = file_containing('site.pp', "{ invalid =>") - it "validates the given file" do - manifest = file_containing('site.pp', "{ invalid =>") - from_an_interactive_terminal + expect { parser.validate() }.to exit_with(1) + end + + it "validates the given file" do + manifest = file_containing('site.pp', "{ invalid =>") + + expect { parser.validate(manifest) }.to exit_with(1) + end + + it "runs error free when there are no validation errors" do + manifest = file_containing('site.pp', "notify { valid: }") + + parser.validate(manifest) + end + + it "reports missing files" do + expect do + parser.validate("missing.pp") + end.to raise_error(Puppet::Error, /One or more file\(s\) specified did not exist.*missing\.pp/m) + end + + it "parses supplied manifest files in the context of a directory environment" do + manifest = file_containing('test.pp', "{ invalid =>") + + env_loader = Puppet::Environments::Static.new( + Puppet::Node::Environment.create(:special, [], '') + ) + Puppet.override(:environments => env_loader) do + Puppet[:environment] = 'special' + expect { parser.validate(manifest) }.to exit_with(1) + end + + expect(@logs.join).to match(/environment special.*Syntax error at '{'/) + end - expect { parser.validate(manifest) }.to exit_with(1) end it "validates the contents of STDIN when no files given and STDIN is not a tty" do @@ -28,21 +59,6 @@ describe Puppet::Face[:parser, :current] do expect { parser.validate() }.to exit_with(1) end - it "runs error free when there are no validation errors" do - manifest = file_containing('site.pp', "notify { valid: }") - from_an_interactive_terminal - - parser.validate(manifest) - end - - it "reports missing files" do - from_an_interactive_terminal - - expect do - parser.validate("missing.pp") - end.to raise_error(Puppet::Error, /One or more file\(s\) specified did not exist.*missing\.pp/m) - end - def from_an_interactive_terminal STDIN.stubs(:tty?).returns(true) end diff --git a/spec/unit/node/environment_spec.rb b/spec/unit/node/environment_spec.rb index 0953e0349..5ce8a2343 100755 --- a/spec/unit/node/environment_spec.rb +++ b/spec/unit/node/environment_spec.rb @@ -43,6 +43,28 @@ describe Puppet::Node::Environment do Puppet::Node::Environment.new(one).should equal(one) end + describe "overriding an existing environment" do + let(:original_path) { [tmpdir('original')] } + let(:new_path) { [tmpdir('new')] } + let(:environment) { Puppet::Node::Environment.create(:overridden, original_path, 'orig.pp') } + + it "overrides modulepath" do + overridden = environment.override_with(:modulepath => new_path) + expect(overridden).to_not be_equal(environment) + expect(overridden.name).to eq(:overridden) + expect(overridden.manifest).to eq('orig.pp') + expect(overridden.modulepath).to eq(new_path) + end + + it "overrides manifest" do + overridden = environment.override_with(:manifest => 'new.pp') + expect(overridden).to_not be_equal(environment) + expect(overridden.name).to eq(:overridden) + expect(overridden.manifest).to eq('new.pp') + expect(overridden.modulepath).to eq(original_path) + end + end + describe "watching a file" do let(:filename) { "filename" } From ba8199db44c94366c64a2e7c7e9b14c1c7e3d988 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 7 Feb 2014 02:10:52 +0100 Subject: [PATCH 583/800] (PUP-1029) Fix problem with Enumerator for String chars on Ruby 2.0.0 Ruby 2.0.0 does not return an Enumerator when calling String#chars, instead an Array with the characters is returned. Ruby 1.8.7 returns Enumerable::Enumerator, and 1.9.3 returns Enumerator. Long live API stability ! --- lib/puppet/pops/types/enumeration.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/puppet/pops/types/enumeration.rb b/lib/puppet/pops/types/enumeration.rb index 183594fe7..cf354b9cb 100644 --- a/lib/puppet/pops/types/enumeration.rb +++ b/lib/puppet/pops/types/enumeration.rb @@ -9,12 +9,14 @@ class Puppet::Pops::Types::Enumeration @@singleton.enumerator(o) end - # Produces an Enumerable::Enumerator for Array, Hash, Integer, Integer Range, and String. + # Produces an Enumerator for Array, Hash, Integer, Integer Range, and String. # def enumerator(o) case o when String - o.chars + x = o.chars + # Ruby 1.8.7 returns Enumerable::Enumerator, Ruby 1.8.9 Enumerator, and 2.0.0 an Array + x.is_a?(Array) ? x.each : x when Integer o.times when Array From 0e283d82a31be22185734f7253468a8db78f7ed6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Tomulik?= Date: Fri, 7 Feb 2014 12:01:54 +0100 Subject: [PATCH 584/800] fix regular expression in ClassGen#is_constant_defined? --- lib/puppet/util/classgen.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/util/classgen.rb b/lib/puppet/util/classgen.rb index ee1ea5f46..78e8732b4 100644 --- a/lib/puppet/util/classgen.rb +++ b/lib/puppet/util/classgen.rb @@ -150,7 +150,7 @@ module Puppet::Util::ClassGen # @api private # def is_constant_defined?(const) - if ::RUBY_VERSION =~ /^1.8/ + if ::RUBY_VERSION =~ /^1\.8/ const_defined?(const) else const_defined?(const, false) From da6f08a6f47034037e22df33d7f57659bc444c50 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 7 Feb 2014 15:27:19 +0100 Subject: [PATCH 585/800] (PUP-1029) Make map function consistent with others The map function only supported a single argument which made it impossible to map based on index in an Array, and made in awkward to map on only hash key or value. Now the map function accepts either value (as before), or index/value, or key/value (for hash). --- lib/puppet/parser/functions/map.rb | 50 +++++++++++++++++-- spec/unit/parser/methods/map_spec.rb | 72 +++++++++++++++++++++++++--- 2 files changed, 110 insertions(+), 12 deletions(-) diff --git a/lib/puppet/parser/functions/map.rb b/lib/puppet/parser/functions/map.rb index 0d61526bc..a41b64669 100644 --- a/lib/puppet/parser/functions/map.rb +++ b/lib/puppet/parser/functions/map.rb @@ -30,14 +30,54 @@ Puppet::Parser::Functions::newfunction( - requires `parser = future` ENDHEREDOC + def map_Enumerator(enumerator, scope, pblock, serving_size) + result = [] + index = 0 + if serving_size == 1 + begin + loop { result << pblock.call(scope, enumerator.next) } + rescue StopIteration + end + else + begin + loop do + result << pblock.call(scope, index, enumerator.next) + index = index +1 + end + rescue StopIteration + end + end + result + end + receiver = args[0] pblock = args[1] raise ArgumentError, ("map(): wrong argument type (#{pblock.class}; must be a parameterized block.") unless pblock.respond_to?(:puppet_lambda) - - enum = Puppet::Pops::Types::Enumeration.enumerator(receiver) - unless enum - raise ArgumentError, ("map(): wrong argument type (#{receiver.class}; must be something enumerable.") + serving_size = pblock.parameter_count + if serving_size == 0 + raise ArgumentError, "map(): block must define at least one parameter; value." end - enum.map {|x| pblock.call(self, x) } + case receiver + when Hash + if serving_size > 2 + raise ArgumentError, "map(): block must define at most two parameters; key, value" + end + if serving_size == 1 + result = receiver.map {|x, y| pblock.call(self, [x, y]) } + else + result = receiver.map {|x, y| pblock.call(self, x, y) } + end + else + if serving_size > 2 + raise ArgumentError, "map(): block must define at most two parameters; index, value" + end + + enum = Puppet::Pops::Types::Enumeration.enumerator(receiver) + unless enum + raise ArgumentError, ("map(): wrong argument type (#{receiver.class}; must be something enumerable.") + end + result = map_Enumerator(enum, self, pblock, serving_size) + end + result end diff --git a/spec/unit/parser/methods/map_spec.rb b/spec/unit/parser/methods/map_spec.rb index 306509ffa..7f8e79789 100644 --- a/spec/unit/parser/methods/map_spec.rb +++ b/spec/unit/parser/methods/map_spec.rb @@ -38,6 +38,43 @@ describe 'the map method' do catalog.resource(:file, "/file_6")['ensure'].should == 'present' end + it 'map on an integer (multiply each by 3)' do + catalog = compile_to_catalog(<<-MANIFEST) + 3.map |$x|{ $x*3}.each |$v|{ + file { "/file_$v": ensure => present } + } + MANIFEST + + catalog.resource(:file, "/file_0")['ensure'].should == 'present' + catalog.resource(:file, "/file_3")['ensure'].should == 'present' + catalog.resource(:file, "/file_6")['ensure'].should == 'present' + end + + it 'map on a string' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = {a=>x, b=>y} + "ab".map |$x|{$a[$x]}.each |$v|{ + file { "/file_$v": ensure => present } + } + MANIFEST + + catalog.resource(:file, "/file_x")['ensure'].should == 'present' + catalog.resource(:file, "/file_y")['ensure'].should == 'present' + end + + it 'map on an array (multiplying value by 10 in even index position)' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = [1,2,3] + $a.map |$i, $x|{ if $i % 2 == 0 {$x} else {$x*10}}.each |$v|{ + file { "/file_$v": ensure => present } + } + MANIFEST + + catalog.resource(:file, "/file_1")['ensure'].should == 'present' + catalog.resource(:file, "/file_20")['ensure'].should == 'present' + catalog.resource(:file, "/file_3")['ensure'].should == 'present' + end + it 'map on a hash selecting keys' do catalog = compile_to_catalog(<<-MANIFEST) $a = {'a'=>1,'b'=>2,'c'=>3} @@ -51,12 +88,33 @@ describe 'the map method' do catalog.resource(:file, "/file_c")['ensure'].should == 'present' end + it 'map on a hash selecting keys - using two block parameters' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = {'a'=>1,'b'=>2,'c'=>3} + $a.map |$k,$v|{ file { "/file_$k": ensure => present } + } + MANIFEST + + catalog.resource(:file, "/file_a")['ensure'].should == 'present' + catalog.resource(:file, "/file_b")['ensure'].should == 'present' + catalog.resource(:file, "/file_c")['ensure'].should == 'present' + end + it 'each on a hash selecting value' do catalog = compile_to_catalog(<<-MANIFEST) $a = {'a'=>1,'b'=>2,'c'=>3} - $a.map |$x|{ $x[1]}.each |$k|{ - file { "/file_$k": ensure => present } - } + $a.map |$x|{ $x[1]}.each |$k|{ file { "/file_$k": ensure => present } } + MANIFEST + + catalog.resource(:file, "/file_1")['ensure'].should == 'present' + catalog.resource(:file, "/file_2")['ensure'].should == 'present' + catalog.resource(:file, "/file_3")['ensure'].should == 'present' + end + + it 'each on a hash selecting value - using two bloc parameters' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = {'a'=>1,'b'=>2,'c'=>3} + $a.map |$k,$v|{ file { "/file_$v": ensure => present } } MANIFEST catalog.resource(:file, "/file_1")['ensure'].should == 'present' @@ -103,12 +161,12 @@ describe 'the map method' do end context 'map checks arguments and' do - it 'raises an error when block has more than 1 argument' do + it 'raises an error when block has more than 2 argument' do expect do compile_to_catalog(<<-MANIFEST) - [1].map |$x, $yikes|{ } + [1].map |$index, $x, $yikes|{ } MANIFEST - end.to raise_error(Puppet::Error, /Too few arguments/) + end.to raise_error(Puppet::Error, /block must define at most two parameters/) end it 'raises an error when block has fewer than 1 argument' do @@ -116,7 +174,7 @@ describe 'the map method' do compile_to_catalog(<<-MANIFEST) [1].map || { } MANIFEST - end.to raise_error(Puppet::Error, /Too many arguments/) + end.to raise_error(Puppet::Error, /block must define at least one parameter/) end end From 34bf00806a3219d6e8c2371a70fc8f8712b7c7b2 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 7 Feb 2014 16:47:27 +0100 Subject: [PATCH 586/800] (PUP-1029) Update documentation of map function Updates documentation of the map function to also show examples of using index/value, key/value. --- lib/puppet/parser/functions/map.rb | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/puppet/parser/functions/map.rb b/lib/puppet/parser/functions/map.rb index a41b64669..8cfd60daf 100644 --- a/lib/puppet/parser/functions/map.rb +++ b/lib/puppet/parser/functions/map.rb @@ -25,8 +25,19 @@ Puppet::Parser::Functions::newfunction( # Turns hash into array of keys $a.map |$x| { $x[0] } + When using a block with 2 parameters, the element's index (starting from 0) for an array, and the key for a hash + is given to the block's first parameter, and the value is given to the block's second parameter.args. + + *Examples* + + # Turns hash into array of values + $a.map |$key,$val|{ $val } + + # Turns hash into array of keys + $a.map |$key,$val|{ $key } + - Since 3.4 for Array and Hash - - Since 3.5 for other enumerables + - Since 3.5 for other enumerables, and support for blocks with 2 parameters - requires `parser = future` ENDHEREDOC From 9dbbdbef0f9671b54eec5db8b010145b053f7847 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 7 Feb 2014 09:24:36 -0800 Subject: [PATCH 587/800] (PUP-753) Write a JMeter CSV log This adds a JMeter format CSV log of the benchmark runs. This log will allow us to track the performance over time via Jenkins and the Performance plugin. --- tasks/benchmark.rake | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tasks/benchmark.rake b/tasks/benchmark.rake index c59a3213a..3fb57ff0e 100644 --- a/tasks/benchmark.rake +++ b/tasks/benchmark.rake @@ -1,5 +1,6 @@ require 'benchmark' require 'tmpdir' +require 'csv' namespace :benchmark do def generate_scenario_tasks(location, name) @@ -33,18 +34,29 @@ namespace :benchmark do else Benchmark::FORMAT end + + report = [] Benchmark.benchmark(Benchmark::CAPTION, 10, format, "> total:", "> avg:") do |b| times = [] ENV['ITERATIONS'].to_i.times do |i| + start_time = Time.now.to_i times << b.report("Run #{i + 1}") do @benchmark.run end + report << [start_time, times.last.real, 200, true, name] end sum = times.inject(Benchmark::Tms.new, &:+) [sum, sum / times.length] end + + CSV.open("#{name}.samples", 'w') do |csv| + csv << %w{timestamp elapsed responsecode success name} + report.each do |line| + csv << line + end + end end desc "Profile a single run of the #{name} scenario." From a5ee135883c68ff04c4fb62f0f72628f49c9dfa2 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 7 Feb 2014 09:26:55 -0800 Subject: [PATCH 588/800] (doc) Stop using non-words "Contributioning", although very cool sounding, is not actually a word. It is "Contributing" to the rest of the world. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0e4d4543b..7424d1eb0 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ To install an open source release of Puppet, If you need to run Puppet from source as a tester or developer, [see the running from source guide on the docs site.](http://docs.puppetlabs.com/guides/from_source.html) -Developing and Contributioning +Developing and Contributing ------ We'd love to get contributions from you! For a quick guide to getting your From 05e1036e852960649f9f116db565498b81d1b50d Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 7 Feb 2014 09:32:05 -0800 Subject: [PATCH 589/800] (doc) Clarify use of --profile There are a few different ways that you can use profile. This splits up the explanation into the 3 different cases in which it can be enabled and a bit of explanation about when you might use each one. --- docs/profiling.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/docs/profiling.md b/docs/profiling.md index 54c71f447..83d4533bd 100644 --- a/docs/profiling.md +++ b/docs/profiling.md @@ -7,9 +7,17 @@ what is making it slow and fix it. There is a built-in system of profiling that can be used to identify some slow spots. This can only work with code that is explicitly instrumented, which, at -the time of this writing, is only the compiler. However, to use it you only -need to use `--profile` on the master. The timing information will be output to -the logs and tagged with the word "PROFILE". +the time of this writing, is primarily the compiler. To enable profiling there +are several options: + +* To profile every request on the master add `--profile` to your master's + startup. +* To profile a single run for an agent add `--profile` to your agent's options + for that run. +* To profile a masterless run add `--profile` to your `puppet apply` options. + +The timing information will be output to the logs and tagged with the word +"PROFILE". For the agent there is actually a second system: evaltrace. You can enable this on the agent by passing it `--evaltrace`. Timing information for each resource From fac2f114e6fa4632b43f4467d8864b0d918ea183 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 7 Feb 2014 09:39:33 -0800 Subject: [PATCH 590/800] (doc) Point readers to how to run acceptance tests --- docs/rspec_tutorial.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/rspec_tutorial.md b/docs/rspec_tutorial.md index 4545c0096..bb790e014 100644 --- a/docs/rspec_tutorial.md +++ b/docs/rspec_tutorial.md @@ -34,7 +34,9 @@ acceptance tests can be destructive, so the systems being tested should be throwaway systems. All of the acceptance tests for Puppet are kept in the acceptance/tests/ -directory. +directory. Running the acceptance tests is much more involved than running the +spec tests. Information about how to run them can be found in the [acceptance +testing documentation](acceptance_tests.md) ## Testing dependency version requirements @@ -46,8 +48,8 @@ of RSpec and Mocha can be found in the project Gemfile. ## Puppet Continuous integration - * Travis-ci (unit tests only): https://travis-ci.org/puppetlabs/puppet/ - * Jenkins (unit and acceptance tests): https://jenkins.puppetlabs.com/view/Puppet%20FOSS/ + * Travis-ci (spec tests only): https://travis-ci.org/puppetlabs/puppet/ + * Jenkins (spec and acceptance tests): https://jenkins.puppetlabs.com/view/Puppet%20FOSS/ ## RSpec From 770194f5ccf14ee927ec3fa01a7d64b14ab78332 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 7 Feb 2014 09:45:38 -0800 Subject: [PATCH 591/800] (doc) Update rspec examples to show desired style There are a couple stylistic points about rspec that we need to follow: * be explicit * say what it does "Be explicity" means that we use `before :each` instead of `before`. This lowers the amount of mental energy that a reader needs to use to understand how things will fit together. "Say what it does" means that the examples read as declarations of what the system under test *does*. A writer must avoid wish-washy words such as "should" or "could" or "may". Instead state exactly what the system does in that situation. --- docs/rspec_tutorial.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/rspec_tutorial.md b/docs/rspec_tutorial.md index bb790e014..1f8630fbb 100644 --- a/docs/rspec_tutorial.md +++ b/docs/rspec_tutorial.md @@ -138,7 +138,7 @@ describe "something that could warn" do $VERBOSE = nil end - after do + after :each do # Enable warnings afterwards $VERBOSE = true end @@ -165,11 +165,11 @@ describe "a helper object" do ['foo', 'bar', 'baz'] end - it "should be an array" do + it "is an array" do my_helper.should be_a_kind_of Array end - it "should have three elements" do + it "has three elements" do my_helper.should have(3).items end end @@ -197,16 +197,16 @@ describe "stubbing a method on an object" do ['foo', 'bar', 'baz'] end - it 'should have three items before being stubbed' do + it 'has three items before being stubbed' do my_helper.size.should == 3 end describe 'when stubbing the size' do - before do + before :each do my_helper.stubs(:size).returns 10 end - it 'should have the stubbed value for size' do + it 'has the stubbed value for size' do my_helper.size.should == 10 end end @@ -221,7 +221,7 @@ describe "stubbing an object" do stub(:not_an_array, :size => 10) end - it 'should have the stubbed size' + it 'has the stubbed size' my_helper.size.should == 10 end end @@ -240,7 +240,7 @@ describe "mocking a method on an object" do end describe "when mocking the size" do - before do + before :each do my_helper.expects(:size).returns 10 end @@ -259,7 +259,7 @@ describe "mocking an object" do mock(:not_an_array) end - before do + before :each do not_an_array.expects(:size).returns 10 end @@ -304,7 +304,7 @@ describe "fixture data" do @fixture.foo.should == :bar end - it "should not keep state between tests" do + it "does not keep state between tests" do # The foo stub was added in the previous test and shouldn't be present # in this test. expect { @fixture.foo }.to raise_error @@ -322,7 +322,7 @@ describe "fixture data" do fixture.foo.should == :bar end - it "should not keep state between tests" do + it "does not keep state between tests" do # since let blocks are regenerated between tests, the foo stub added in # the previous test will not be present here. expect { fixture.foo }.to raise_error From a8391b57246abbdd4e1dadb029eaa7e67688cb0c Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 7 Feb 2014 10:12:11 -0800 Subject: [PATCH 592/800] (PUP-753) Add task to run/profile all scenarios In order to make it easy to bulk collect the benchmark data there needs to be a single command to run all of the scenarios. This adds `benchmark:all:run` and `benchmark:all:profile` to achieve this. --- tasks/benchmark.rake | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tasks/benchmark.rake b/tasks/benchmark.rake index 3fb57ff0e..0f1756fbc 100644 --- a/tasks/benchmark.rake +++ b/tasks/benchmark.rake @@ -75,7 +75,18 @@ namespace :benchmark do end end + scenarios = [] Dir.glob('benchmarks/*') do |location| + name = File.basename(location) + scenarios << name generate_scenario_tasks(location, File.basename(location)) end + + namespace :all do + desc "Profile all of the scenarios. (#{scenarios.join(', ')})" + task :profile => scenarios.collect { |name| "#{name}:profile" } + + desc "Run all of the scenarios. (#{scenarios.join(', ')})" + task :run => scenarios.collect { |name| "#{name}:run" } + end end From 56bdffc66418a31a724d7162d5781c64029068d8 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 7 Feb 2014 10:22:09 -0800 Subject: [PATCH 593/800] (PUP-753) Write times in millis The JMeter format is to use milliseconds to express time. This changes the timestamp and elapsed time to be in millis. --- tasks/benchmark.rake | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/tasks/benchmark.rake b/tasks/benchmark.rake index 0f1756fbc..7fb4668ea 100644 --- a/tasks/benchmark.rake +++ b/tasks/benchmark.rake @@ -43,7 +43,7 @@ namespace :benchmark do times << b.report("Run #{i + 1}") do @benchmark.run end - report << [start_time, times.last.real, 200, true, name] + report << [to_millis(start_time), to_millis(times.last.real), 200, true, name] end sum = times.inject(Benchmark::Tms.new, &:+) @@ -51,12 +51,9 @@ namespace :benchmark do [sum, sum / times.length] end - CSV.open("#{name}.samples", 'w') do |csv| - csv << %w{timestamp elapsed responsecode success name} - report.each do |line| - csv << line - end - end + write_csv("#{name}.samples", + %w{timestamp elapsed responsecode success name}, + report) end desc "Profile a single run of the #{name} scenario." @@ -72,6 +69,19 @@ namespace :benchmark do printer.print(f) end end + + def to_millis(seconds) + (seconds * 1000).round + end + + def write_csv(file, header, data) + CSV.open(file, 'w') do |csv| + csv << header + data.each do |line| + csv << line + end + end + end end end From ba5a68ff329a64d370f6a8ba2aa27bed4157279e Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 7 Feb 2014 11:05:03 -0800 Subject: [PATCH 594/800] (PUP-753) Benchmark system startup This adds a simple benchmark to check how long it takes to get puppet to respond to commands on the CLI. This is an important aspect of percieved performance for many users. And for anyone trying to script puppet commands, this becomes a *very* important aspect of actual system performance. --- benchmarks/system_startup/benchmarker.rb | 17 +++++++++++++++++ benchmarks/system_startup/description | 2 ++ 2 files changed, 19 insertions(+) create mode 100644 benchmarks/system_startup/benchmarker.rb create mode 100644 benchmarks/system_startup/description diff --git a/benchmarks/system_startup/benchmarker.rb b/benchmarks/system_startup/benchmarker.rb new file mode 100644 index 000000000..c48a878dd --- /dev/null +++ b/benchmarks/system_startup/benchmarker.rb @@ -0,0 +1,17 @@ +class Benchmarker + def initialize(target, size) + end + + def setup + end + + def generate + end + + def run + # Just running help is probably a good proxy of a full startup. + # Simply asking for the version might also be good, but it would miss all + # of the app searching and loading parts + `puppet help` + end +end diff --git a/benchmarks/system_startup/description b/benchmarks/system_startup/description new file mode 100644 index 000000000..85c6dea3a --- /dev/null +++ b/benchmarks/system_startup/description @@ -0,0 +1,2 @@ +Benchmark scenario: running puppet commands from the CLI +Benchmark target: overhead of loading puppet From 7b8650e498bf8851d0c2ed537d0fa2608887bedf Mon Sep 17 00:00:00 2001 From: Ashley Penney Date: Thu, 14 Nov 2013 18:54:03 -0500 Subject: [PATCH 595/800] (PUP-789) Break yumrepo into a type/provider. This work strips out all of the provider code from the type, and creates a new ruby provider for yumrepo. While this code still uses inifile it's been rewritten to take advantage of the modernization of Puppet. It's now a little easier to understand and test. This covers: #8758, #9293, #22304 (projects.puppetlabs.com) --- lib/puppet/provider/yumrepo/inifile.rb | 153 +++++ lib/puppet/type/yumrepo.rb | 633 +++++++++------------ spec/unit/provider/yumrepo/inifile_spec.rb | 94 +++ spec/unit/type/yumrepo_spec.rb | 206 +------ 4 files changed, 525 insertions(+), 561 deletions(-) create mode 100644 lib/puppet/provider/yumrepo/inifile.rb create mode 100644 spec/unit/provider/yumrepo/inifile_spec.rb diff --git a/lib/puppet/provider/yumrepo/inifile.rb b/lib/puppet/provider/yumrepo/inifile.rb new file mode 100644 index 000000000..6115ce836 --- /dev/null +++ b/lib/puppet/provider/yumrepo/inifile.rb @@ -0,0 +1,153 @@ +require 'puppet/util/inifile' + +Puppet::Type.type(:yumrepo).provide(:inifile) do + desc 'Manage yum repos' + + PROPERTIES = Puppet::Type.type(:yumrepo).validproperties + + def self.instances + instances = [] + # Iterate over each section of our virtual file. + virtual_inifile.each_section do |section| + attributes_hash = {:name => section.name, :ensure => :present, :provider => :yumrepo} + # We need to build up a attributes hash + section.entries.each do |key, value| + key = key.to_sym + if valid_property?(key) + # We strip the values here to handle cases where distros set values + # like enabled = 1 with spaces. + attributes_hash[key] = value.strip + end + end + instances << new(attributes_hash) + end + return instances + end + + def self.prefetch(resources) + repos = instances + resources.keys.each do |name| + if provider = repos.find { |repo| repo.name == name } + resources[name].provider = provider + end + end + end + + # Search for a reposdir in the yum configuration file and append it to the + # list of repodirs to use. + def self.reposdir(conf='/etc/yum.conf', dirs=['/etc/yum.repos.d', '/etc/yum/repos.d']) + reposdir = find_conf_value('reposdir', conf) + dirs << reposdir if reposdir + + return dirs + end + + # Find configuration values in .conf files and return them + # if found. + def self.find_conf_value(value, conf='/etc/yum.conf') + if File.exists?(conf) + contents = File.read(conf) + match = /^#{value}\s*=\s*(.*)/.match(contents) + end + + return match.captures[0] if match + end + + # Build a virtual inifile by reading in numerous .repo + # files into a single virtual file to ease manipulation. + def self.virtual_inifile + unless @virtual + @virtual = Puppet::Util::IniConfig::File.new + reposdir.each do |dir| + Dir.glob("#{dir}/*.repo").each do |file| + @virtual.read(file) if ::File.file?(file) + end + end + end + return @virtual + end + + def self.valid_property?(key) + PROPERTIES.include?(key) + end + + # Return the named section out of the virtual_inifile. + def self.section(name) + result = self.virtual_inifile[name] + # Create a new section if not found. + unless result + reposdir.each do |dir| + if File.directory?(dir) + path = ::File.join(dir, "#{name}.repo") + Puppet.info("create new repo #{name} in file #{path}") + result = self.virtual_inifile.add_section(name, path) + end + end + end + result + end + + # Store all modifications back to disk + def self.store + inifile = self.virtual_inifile + inifile.store + unless Puppet[:noop] + target_mode = 0644 + inifile.each_file do |file| + current_mode = Puppet::FileSystem.stat(file).mode & 0777 + unless current_mode == target_mode + Puppet.info "changing mode of #{file} from %03o to %03o" % [current_mode, target_mode] + ::File.chmod(target_mode, file) + end + end + end + end + + def create + @property_hash[:ensure] = :present + + # We fetch a list of properties from the type, then iterate + # over them, avoiding ensure. We're relying on .should to + # check if the property has been set and should be modified, + # and if so we set it in the virtual inifile. + PROPERTIES.each do |property| + next if property == :ensure + if value = @resource.should(property) + section(@resource[:name])[property.to_s] = value + @property_hash[property] = value + end + end + end + + def destroy + # Flag file for deletion on flush. + section(@resource[:name]).destroy=(true) + + @property_hash.clear + end + + def flush + self.class.store + end + + def section(name) + self.class.section(name) + end + + # Create all of our setters. + mk_resource_methods + PROPERTIES.each do |property| + # Exclude ensure, as we don't need to create an ensure= + next if property == :ensure + # Builds the property= method. + define_method("#{property.to_s}=") do |value| + section(@property_hash[:name])[property.to_s] = value + @property_hash[property] = value + end + end + + def exists? + @property_hash[:ensure] == :present + end + +end diff --git a/lib/puppet/type/yumrepo.rb b/lib/puppet/type/yumrepo.rb index 8bf1b44fc..d44de1f13 100644 --- a/lib/puppet/type/yumrepo.rb +++ b/lib/puppet/type/yumrepo.rb @@ -1,395 +1,270 @@ -require 'puppet/util/inifile' +require 'uri' -module Puppet - # A property for one entry in a .ini-style file - class IniProperty < Puppet::Property - def insync?(is) - # A should property of :absent is the same as nil - if is.nil? && should == :absent - return true - end - super(is) - end +Puppet::Type.newtype(:yumrepo) do + @doc = "The client-side description of a yum repository. Repository + configurations are found by parsing `/etc/yum.conf` and + the files indicated by the `reposdir` option in that file + (see `yum.conf(5)` for details). - def sync - if safe_insync?(retrieve) - result = nil - else - result = set(self.should) - if should == :absent - resource.section[inikey] = nil - else - resource.section[inikey] = should - end - end - result - end + Most parameters are identical to the ones documented + in the `yum.conf(5)` man page. - def retrieve - resource.section[inikey] - end - - def inikey - name.to_s - end - - # Set the key associated with this property to KEY, instead - # of using the property's NAME - def self.inikey(key) - # Override the inikey instance method - # Is there a way to do this without resorting to strings ? - # Using a block fails because the block can't access - # the variable 'key' in the outer scope - self.class_eval("def inikey ; \"#{key.to_s}\" ; end") - end - - end + Continuation lines that yum supports (for the `baseurl`, for example) + are not supported. This type does not attempt to read or verify the + exinstence of files listed in the `include` attribute." + # Ensure yumrepos can be removed too. + ensurable # Doc string for properties that can be made 'absent' ABSENT_DOC="Set this to `absent` to remove it from the file completely." + # False can be false/0/no and True can be true/1/yes in yum. + YUM_BOOLEAN=/(True|False|0|1|No|Yes)/ + YUM_BOOLEAN_DOC="Valid values are: False/0/No or True/1/Yes." - newtype(:yumrepo) do - @doc = "The client-side description of a yum repository. Repository - configurations are found by parsing `/etc/yum.conf` and - the files indicated by the `reposdir` option in that file - (see `yum.conf(5)` for details). + newparam(:name, :namevar => true) do + desc "The name of the repository. This corresponds to the + `repositoryid` parameter in `yum.conf(5)`." + end - Most parameters are identical to the ones documented - in the `yum.conf(5)` man page. + newparam(:target) do + desc "The filename to write the yum repository to." - Continuation lines that yum supports (for the `baseurl`, for example) - are not supported. This type does not attempt to read or verify the - exinstence of files listed in the `include` attribute." + defaultto :absent + end - class << self - attr_accessor :filetype - # The writer is only used for testing, there should be no need - # to change yumconf or inifile in any other context - attr_accessor :yumconf - attr_writer :inifile - end + newproperty(:descr) do + desc "A human-readable description of the repository. + This corresponds to the name parameter in `yum.conf(5)`. + #{ABSENT_DOC}" - self.filetype = Puppet::Util::FileType.filetype(:flat) + newvalues(/.*/, :absent) + end - @inifile = nil + newproperty(:mirrorlist) do + desc "The URL that holds the list of mirrors for this repository. + #{ABSENT_DOC}" - @yumconf = "/etc/yum.conf" - - # Where to put files for brand new sections - @defaultrepodir = nil - - def self.instances - l = [] - check = validproperties - clear - inifile.each_section do |s| - next if s.name == "main" - obj = new(:name => s.name, :audit => check) - current_values = obj.retrieve - obj.eachproperty do |property| - if current_values[property].nil? - obj.delete(property.name) - else - property.should = current_values[property] - end - end - obj.delete(:audit) - l << obj - end - l - end - - # Return the Puppet::Util::IniConfig::File for the whole yum config - def self.inifile - if @inifile.nil? - @inifile = read - main = @inifile['main'] - raise Puppet::Error, "File #{yumconf} does not contain a main section" if main.nil? - reposdir = main['reposdir'] - reposdir ||= "/etc/yum.repos.d, /etc/yum/repos.d" - reposdir.gsub!(/[\n,]/, " ") - reposdir.split.each do |dir| - Dir::glob("#{dir}/*.repo").each do |file| - @inifile.read(file) if ::File.file?(file) - end - end - reposdir.split.each do |dir| - if ::File.directory?(dir) && ::File.writable?(dir) - @defaultrepodir = dir - break - end - end - end - @inifile - end - - # Parse the yum config files. Only exposed for the tests - # Non-test code should use self.inifile to get at the - # underlying file - def self.read - result = Puppet::Util::IniConfig::File.new - result.read(yumconf) - main = result['main'] - raise Puppet::Error, "File #{yumconf} does not contain a main section" if main.nil? - reposdir = main['reposdir'] - reposdir ||= "/etc/yum.repos.d, /etc/yum/repos.d" - reposdir.gsub!(/[\n,]/, " ") - reposdir.split.each do |dir| - Dir::glob("#{dir}/*.repo").each do |file| - result.read(file) if ::File.file?(file) - end - end - if @defaultrepodir.nil? - reposdir.split.each do |dir| - if ::File.directory?(dir) && ::File.writable?(dir) - @defaultrepodir = dir - break - end - end - end - result - end - - # Return the Puppet::Util::IniConfig::Section with name NAME - # from the yum config - def self.section(name) - result = inifile[name] - if result.nil? - # Brand new section - path = yumconf - path = ::File.join(@defaultrepodir, "#{name}.repo") unless @defaultrepodir.nil? - Puppet::info "create new repo #{name} in file #{path}" - result = inifile.add_section(name, path) - end - result - end - - # Store all modifications back to disk - def self.store - inifile.store - unless Puppet[:noop] - target_mode = 0644 # FIXME: should be configurable - inifile.each_file do |file| - current_mode = Puppet::FileSystem.stat(file).mode & 0777 - unless current_mode == target_mode - Puppet::info "changing mode of #{file} from %03o to %03o" % [current_mode, target_mode] - ::File.chmod(target_mode, file) - end - end - end - end - - # This is only used during testing. - def self.clear - @inifile = nil - @yumconf = "/etc/yum.conf" - @defaultrepodir = nil - end - - # Return the Puppet::Util::IniConfig::Section for this yumrepo resource - def section - self.class.section(self[:name]) - end - - # Store modifications to this yumrepo resource back to disk - def flush - self.class.store - end - - newparam(:name) do - desc "The name of the repository. This corresponds to the - `repositoryid` parameter in `yum.conf(5)`." - isnamevar - end - - newproperty(:descr, :parent => Puppet::IniProperty) do - desc "A human-readable description of the repository. - This corresponds to the name parameter in `yum.conf(5)`. - #{ABSENT_DOC}" - newvalue(:absent) { self.should = :absent } - newvalue(/.*/) { } - inikey "name" - end - - newproperty(:mirrorlist, :parent => Puppet::IniProperty) do - desc "The URL that holds the list of mirrors for this repository. - #{ABSENT_DOC}" - newvalue(:absent) { self.should = :absent } - # Should really check that it's a valid URL - newvalue(/.*/) { } - end - - newproperty(:baseurl, :parent => Puppet::IniProperty) do - desc "The URL for this repository. #{ABSENT_DOC}" - newvalue(:absent) { self.should = :absent } - # Should really check that it's a valid URL - newvalue(/.*/) { } - end - - newproperty(:enabled, :parent => Puppet::IniProperty) do - desc "Whether this repository is enabled, as represented by a - `0` or `1`. #{ABSENT_DOC}" - newvalue(:absent) { self.should = :absent } - newvalue(/^(0|1)$/) { } - end - - newproperty(:gpgcheck, :parent => Puppet::IniProperty) do - desc "Whether to check the GPG signature on packages installed - from this repository, as represented by a `0` or `1`. - #{ABSENT_DOC}" - newvalue(:absent) { self.should = :absent } - newvalue(/^(0|1)$/) { } - end - - newproperty(:gpgkey, :parent => Puppet::IniProperty) do - desc "The URL for the GPG key with which packages from this - repository are signed. #{ABSENT_DOC}" - newvalue(:absent) { self.should = :absent } - # Should really check that it's a valid URL - newvalue(/.*/) { } - end - - newproperty(:include, :parent => Puppet::IniProperty) do - desc "The URL of a remote file containing additional yum configuration - settings. Puppet does not check for this file's existence or validity. - #{ABSENT_DOC}" - newvalue(:absent) { self.should = :absent } - # Should really check that it's a valid URL - newvalue(/.*/) { } - end - - newproperty(:exclude, :parent => Puppet::IniProperty) do - desc "List of shell globs. Matching packages will never be - considered in updates or installs for this repo. - #{ABSENT_DOC}" - newvalue(:absent) { self.should = :absent } - newvalue(/.*/) { } - end - - newproperty(:includepkgs, :parent => Puppet::IniProperty) do - desc "List of shell globs. If this is set, only packages - matching one of the globs will be considered for - update or install from this repo. #{ABSENT_DOC}" - newvalue(:absent) { self.should = :absent } - newvalue(/.*/) { } - end - - newproperty(:enablegroups, :parent => Puppet::IniProperty) do - desc "Whether yum will allow the use of package groups for this - repository, as represented by a `0` or `1`. #{ABSENT_DOC}" - newvalue(:absent) { self.should = :absent } - newvalue(/^(0|1)$/) { } - end - - newproperty(:failovermethod, :parent => Puppet::IniProperty) do - desc "The failover methode for this repository; should be either - `roundrobin` or `priority`. #{ABSENT_DOC}" - newvalue(:absent) { self.should = :absent } - newvalue(%r{roundrobin|priority}) { } - end - - newproperty(:keepalive, :parent => Puppet::IniProperty) do - desc "Whether HTTP/1.1 keepalive should be used with this repository, as - represented by a `0` or `1`. #{ABSENT_DOC}" - newvalue(:absent) { self.should = :absent } - newvalue(/^(0|1)$/) { } - end - - newproperty(:http_caching, :parent => Puppet::IniProperty) do - desc "What to cache from this repository. #{ABSENT_DOC}" - newvalue(:absent) { self.should = :absent } - newvalue(%r(packages|all|none)) { } - end - - newproperty(:timeout, :parent => Puppet::IniProperty) do - desc "Number of seconds to wait for a connection before timing - out. #{ABSENT_DOC}" - newvalue(:absent) { self.should = :absent } - newvalue(%r{[0-9]+}) { } - end - - newproperty(:metadata_expire, :parent => Puppet::IniProperty) do - desc "Number of seconds after which the metadata will expire. - #{ABSENT_DOC}" - newvalue(:absent) { self.should = :absent } - newvalue(%r{[0-9]+}) { } - end - - newproperty(:protect, :parent => Puppet::IniProperty) do - desc "Enable or disable protection for this repository. Requires - that the `protectbase` plugin is installed and enabled. - #{ABSENT_DOC}" - newvalue(:absent) { self.should = :absent } - newvalue(/^(0|1)$/) { } - end - - newproperty(:priority, :parent => Puppet::IniProperty) do - desc "Priority of this repository from 1-99. Requires that - the `priorities` plugin is installed and enabled. - #{ABSENT_DOC}" - newvalue(:absent) { self.should = :absent } - newvalue(%r{[1-9][0-9]?}) { } - end - - newproperty(:cost, :parent => Puppet::IniProperty) do - desc "Cost of this repository. #{ABSENT_DOC}" - newvalue(:absent) { self.should = :absent } - newvalue(%r{\d+}) { } - end - - newproperty(:proxy, :parent => Puppet::IniProperty) do - desc "URL to the proxy server for this repository. #{ABSENT_DOC}" - newvalue(:absent) { self.should = :absent } - # Should really check that it's a valid URL - newvalue(/.*/) { } - end - - newproperty(:proxy_username, :parent => Puppet::IniProperty) do - desc "Username for this proxy. #{ABSENT_DOC}" - newvalue(:absent) { self.should = :absent } - newvalue(/.*/) { } - end - - newproperty(:proxy_password, :parent => Puppet::IniProperty) do - desc "Password for this proxy. #{ABSENT_DOC}" - newvalue(:absent) { self.should = :absent } - newvalue(/.*/) { } - end - - newproperty(:s3_enabled, :parent => Puppet::IniProperty) do - desc "Access the repo via S3. #{ABSENT_DOC}" - newvalue(:absent) { self.should = :absent } - newvalue(/^(0|1)$/) { } - end - - newproperty(:sslcacert, :parent => Puppet::IniProperty) do - desc "Path to the directory containing the databases of the - certificate authorities yum should use to verify SSL certificates. - #{ABSENT_DOC}" - newvalue(:absent) { self.should = :absent } - newvalue(/.*/) { } - end - - newproperty(:sslverify, :parent => Puppet::IniProperty) do - desc "Should yum verify SSL certificates/hosts at all. - Possible values are 'True' or 'False'. - #{ABSENT_DOC}" - newvalue(:absent) { self.should = :absent } - newvalue(%r(True|False)) { } - end - - newproperty(:sslclientcert, :parent => Puppet::IniProperty) do - desc "Path to the SSL client certificate yum should use to connect - to repos/remote sites. #{ABSENT_DOC}" - newvalue(:absent) { self.should = :absent } - newvalue(/.*/) { } - end - - newproperty(:sslclientkey, :parent => Puppet::IniProperty) do - desc "Path to the SSL client key yum should use to connect - to repos/remote sites. #{ABSENT_DOC}" - newvalue(:absent) { self.should = :absent } - newvalue(/.*/) { } + newvalues(/.*/, :absent) + validate do |value| + parsed = URI.parse(value) + fail("Must be a valid URL") unless ['file', 'http'].include?(parsed.scheme) end end + + newproperty(:baseurl) do + desc "The URL for this repository. #{ABSENT_DOC}" + + newvalues(/.*/, :absent) + validate do |value| + parsed = URI.parse(value) + fail("Must be a valid URL") unless ['file', 'http'].include?(parsed.scheme) + end + end + + newproperty(:enabled) do + desc "Whether this repository is enabled. + #{YUM_BOOLEAN_DOC} + #{ABSENT_DOC}" + + newvalues(YUM_BOOLEAN, :absent) + end + + newproperty(:gpgcheck) do + desc "Whether to check the GPG signature on packages installed + from this repository. + #{YUM_BOOLEAN_DOC} + #{ABSENT_DOC}" + + newvalues(YUM_BOOLEAN, :absent) + end + + newproperty(:repo_gpgcheck) do + desc "Whether to check the GPG signature on repodata. + #{YUM_BOOLEAN_DOC} + #{ABSENT_DOC}" + + newvalues(YUM_BOOLEAN, :absent) + end + + newproperty(:gpgkey) do + desc "The URL for the GPG key with which packages from this + repository are signed. #{ABSENT_DOC}" + + newvalues(/.*/, :absent) + validate do |value| + parsed = URI.parse(value) + fail("Must be a valid URL") unless ['file', 'http'].include?(parsed.scheme) + end + end + + newproperty(:include) do + desc "The URL of a remote file containing additional yum configuration + settings. Puppet does not check for this file's existence or validity. + #{ABSENT_DOC}" + + newvalues(/.*/, :absent) + validate do |value| + parsed = URI.parse(value) + fail("Must be a valid URL") unless ['file', 'http'].include?(parsed.scheme) + end + end + + newproperty(:exclude) do + desc "List of shell globs. Matching packages will never be + considered in updates or installs for this repo. + #{ABSENT_DOC}" + + newvalues(/.*/, :absent) + end + + newproperty(:includepkgs) do + desc "List of shell globs. If this is set, only packages + matching one of the globs will be considered for + update or install from this repo. #{ABSENT_DOC}" + + newvalues(/.*/, :absent) + end + + newproperty(:enablegroups) do + desc "Whether yum will allow the use of package groups for this + repository. + #{YUM_BOOLEAN_DOC} + #{ABSENT_DOC}" + + newvalues(YUM_BOOLEAN, :absent) + end + + newproperty(:failovermethod) do + desc "The failover method for this repository; should be either + `roundrobin` or `priority`. #{ABSENT_DOC}" + + newvalues(/roundrobin|priority/, :absent) + end + + newproperty(:keepalive) do + desc "Whether HTTP/1.1 keepalive should be used with this repository. + #{YUM_BOOLEAN_DOC} + #{ABSENT_DOC}" + + newvalues(YUM_BOOLEAN, :absent) + end + + newproperty(:http_caching) do + desc "What to cache from this repository. #{ABSENT_DOC}" + + newvalues(/(packages|all|none)/, :absent) + end + + newproperty(:timeout) do + desc "Number of seconds to wait for a connection before timing + out. #{ABSENT_DOC}" + + newvalues(/[0-9]+/, :absent) + end + + newproperty(:metadata_expire) do + desc "Number of seconds after which the metadata will expire. + #{ABSENT_DOC}" + + newvalues(/[0-9]+/, :absent) + end + + newproperty(:protect) do + desc "Enable or disable protection for this repository. Requires + that the `protectbase` plugin is installed and enabled. + #{YUM_BOOLEAN_DOC} + #{ABSENT_DOC}" + + newvalues(YUM_BOOLEAN, :absent) + end + + newproperty(:priority) do + desc "Priority of this repository from 1-99. Requires that + the `priorities` plugin is installed and enabled. + #{ABSENT_DOC}" + + newvalues(/.*/, :absent) + validate do |value| + unless value == :absent or (1..99).include?(value.to_i) + fail("Must be within range 1-99") + end + end + end + + newproperty(:cost) do + desc "Cost of this repository. #{ABSENT_DOC}" + + newvalues(/\d+/, :absent) + end + + newproperty(:proxy) do + desc "URL to the proxy server for this repository. #{ABSENT_DOC}" + + newvalues(/.*/, :absent) + validate do |value| + parsed = URI.parse(value) + fail("Must be a valid URL") unless ['file', 'http'].include?(parsed.scheme) + end + end + + newproperty(:proxy_username) do + desc "Username for this proxy. #{ABSENT_DOC}" + + newvalues(/.*/, :absent) + end + + newproperty(:proxy_password) do + desc "Password for this proxy. #{ABSENT_DOC}" + + newvalues(/.*/, :absent) + end + + newproperty(:s3_enabled) do + desc "Access the repo via S3. + #{YUM_BOOLEAN_DOC} + #{ABSENT_DOC}" + + newvalues(YUM_BOOLEAN, :absent) + end + + newproperty(:sslcacert) do + desc "Path to the directory containing the databases of the + certificate authorities yum should use to verify SSL certificates. + #{ABSENT_DOC}" + + newvalues(/.*/, :absent) + end + + newproperty(:sslverify) do + desc "Should yum verify SSL certificates/hosts at all. + #{YUM_BOOLEAN_DOC} + #{ABSENT_DOC}" + + newvalues(YUM_BOOLEAN, :absent) + end + + newproperty(:sslclientcert) do + desc "Path to the SSL client certificate yum should use to connect + to repos/remote sites. #{ABSENT_DOC}" + + newvalues(/.*/, :absent) + end + + newproperty(:sslclientkey) do + desc "Path to the SSL client key yum should use to connect + to repos/remote sites. #{ABSENT_DOC}" + + newvalues(/.*/, :absent) + end + + newproperty(:metalink) do + desc "Metalink for mirrors. #{ABSENT_DOC}" + + newvalues(/.*/, :absent) + validate do |value| + parsed = URI.parse(value) + fail("Must be a valid URL") unless ['file', 'http'].include?(parsed.scheme) + end + end + end diff --git a/spec/unit/provider/yumrepo/inifile_spec.rb b/spec/unit/provider/yumrepo/inifile_spec.rb new file mode 100644 index 000000000..d362193bc --- /dev/null +++ b/spec/unit/provider/yumrepo/inifile_spec.rb @@ -0,0 +1,94 @@ +require 'spec_helper' +require 'puppet' + +describe Puppet::Type.type(:yumrepo).provider(:inifile) do + let(:yumrepo) { + Puppet::Type.type(:yumrepo).new( + :name => 'puppetlabs-products', + :ensure => :present, + :baseurl => 'http://yum.puppetlabs.com/el/6/products/$basearch', + :descr => 'Puppet Labs Products El 6 - $basearch', + :enabled => '1', + :gpgcheck => '1', + :gpgkey => 'file:///etc/pki/rpm-gpg/RPM-GPG-KEY-puppetlabs' + ) + } + let(:yumrepo_provider) { yumrepo.provider } + let(:repo_file) { ' +[updates] +name="updates" +enabled = 1 +descr="test updates" +' + } + + before :each do + Dir.stubs(:glob).with('/etc/yum.repos.d/*.repo').returns(['/etc/yum.repos.d/test.repo']) + end + + describe 'self.instances' do + before :each do + described_class.stubs(:reposdir).returns(['/etc/yum.repos.d']) + File.expects(:file?).with('/etc/yum.repos.d/test.repo').returns(true) + File.expects(:exist?).with(Pathname.new('/etc/yum.repos.d/test.repo')).returns(true) + File.expects(:read).with('/etc/yum.repos.d/test.repo').returns(repo_file) + end + + it 'finds the update repo' do + providers = yumrepo_provider.class.instances + providers.count.should == 1 + providers[0].name.should == 'updates' + providers[0].enabled.should == '1' + end + end + + describe 'create' do + it 'creates a yumrepo' do + yumrepo_provider.section('puppetlabs-products').expects(:[]=).at_least(1) + yumrepo_provider.create + end + end + + describe 'destroy' do + it 'flags the section to be destroyed' do + yumrepo_provider.section('puppetlabs-products').expects(:destroy=).with(true) + yumrepo_provider.destroy + end + end + + describe 'exists?' do + it 'checks if yumrepo exists' do + described_class.stubs(:reposdir).returns(['/etc/yum.repos.d']) + yumrepo_provider.ensure= :present + yumrepo_provider.exists?.should be_true + end + end + + describe 'reposdir' do + let(:defaults) { ['/etc/yum.repos.d', '/etc/yum/repos.d'] } + let(:all) { ['/etc/yum.repos.d', '/etc/yum/repos.d', '/etc/yum/test'] } + + it 'returns defaults if no yum conf' do + File.expects(:exists?).with('/etc/yum.conf').returns(false) + + described_class.reposdir('/etc/yum.conf').should == defaults + end + + it 'returns defaults if yumconf has no reposdir' do + File.expects(:exists?).with('/etc/yum.conf').returns(true) + File.expects(:read).with('/etc/yum.conf').returns("[main]\ntest = /etc/yum/test") + + described_class.reposdir('/etc/yum.conf').should == defaults + end + + it 'returns all directories if yum.conf contains reposdir' do + File.expects(:exists?).with('/etc/yum.conf').returns(true) + File.expects(:read).with('/etc/yum.conf').returns("[main]\nreposdir = /etc/yum/test") + + described_class.reposdir('/etc/yum.conf').should == all + end + + end + + +end diff --git a/spec/unit/type/yumrepo_spec.rb b/spec/unit/type/yumrepo_spec.rb index 66ddfd3e9..8547989c5 100644 --- a/spec/unit/type/yumrepo_spec.rb +++ b/spec/unit/type/yumrepo_spec.rb @@ -1,18 +1,23 @@ -#! /usr/bin/env ruby - require 'spec_helper' +require 'puppet' describe Puppet::Type.type(:yumrepo) do - include PuppetSpec::Files + let(:yumrepo) { + Puppet::Type.type(:yumrepo).new( + :name => "puppetlabs" + ) + } describe "When validating attributes" do it "should have a 'name' parameter'" do - Puppet::Type.type(:yumrepo).new(:name => "puppetlabs")[:name].should == "puppetlabs" + yumrepo[:name].should == "puppetlabs" end - [:baseurl, :cost, :descr, :enabled, :enablegroups, :exclude, :failovermethod, :gpgcheck, :gpgkey, :http_caching, - :include, :includepkgs, :keepalive, :metadata_expire, :mirrorlist, :priority, :protect, :proxy, :proxy_username, :proxy_password, :timeout, - :sslcacert, :sslverify, :sslclientcert, :sslclientkey, :s3_enabled].each do |param| + [:baseurl, :cost, :descr, :enabled, :enablegroups, :exclude, :failovermethod, + :gpgcheck, :repo_gpgcheck, :gpgkey, :http_caching, :include, :includepkgs, :keepalive, + :metadata_expire, :mirrorlist, :priority, :protect, :proxy, :proxy_username, + :proxy_password, :timeout, :sslcacert, :sslverify, :sslclientcert, + :sslclientkey, :s3_enabled, :metalink].each do |param| it "should have a '#{param}' parameter" do Puppet::Type.type(:yumrepo).attrtype(param).should == :property end @@ -20,34 +25,36 @@ describe Puppet::Type.type(:yumrepo) do end describe "When validating attribute values" do - [:cost, :enabled, :enablegroups, :failovermethod, :gpgcheck, :http_caching, :keepalive, :metadata_expire, :priority, :protect, :timeout].each do |param| + [:cost, :enabled, :enablegroups, :failovermethod, :gpgcheck, :repo_gpgcheck, :http_caching, + :keepalive, :metadata_expire, :priority, :protect, :timeout].each do |param| it "should support :absent as a value to '#{param}' parameter" do - Puppet::Type.type(:yumrepo).new(:name => "puppetlabs.repo", param => :absent) + Puppet::Type.type(:yumrepo).new(:name => 'puppetlabs', param => :absent) end end - [:cost, :enabled, :enablegroups, :gpgcheck, :keepalive, :metadata_expire, :priority, :protect, :timeout].each do |param| - it "should fail if '#{param}' is not a number" do - lambda { Puppet::Type.type(:yumrepo).new(:name => "puppetlabs", param => "notanumber") }.should raise_error + [:cost, :enabled, :enablegroups, :gpgcheck, :repo_gpgcheck, :keepalive, :metadata_expire, + :priority, :protect, :timeout].each do |param| + it "should fail if '#{param}' is not true/false, 0/1, or yes/no" do + expect { Puppet::Type.type(:yumrepo).new(:name => "puppetlabs", param => "notanumber") }.to raise_error end end - [:enabled, :enabledgroups, :gpgcheck, :keepalive, :protect, :s3_enabled].each do |param| + [:enabled, :enabledgroups, :gpgcheck, :repo_gpgcheck, :keepalive, :protect, :s3_enabled].each do |param| it "should fail if '#{param}' does not have one of the following values (0|1)" do - lambda { Puppet::Type.type(:yumrepo).new(:name => "puppetlabs", param => "2") }.should raise_error + expect { Puppet::Type.type(:yumrepo).new(:name => "puppetlabs", param => "2") }.to raise_error end end it "should fail if 'failovermethod' does not have one of the following values (roundrobin|priority)" do - lambda { Puppet::Type.type(:yumrepo).new(:name => "puppetlabs", :failovermethod => "notavalidvalue") }.should raise_error + expect { Puppet::Type.type(:yumrepo).new(:name => "puppetlabs", :failovermethod => "notavalidvalue") }.to raise_error end it "should fail if 'http_caching' does not have one of the following values (packages|all|none)" do - lambda { Puppet::Type.type(:yumrepo).new(:name => "puppetlabs", :http_caching => "notavalidvalue") }.should raise_error + expect { Puppet::Type.type(:yumrepo).new(:name => "puppetlabs", :http_caching => "notavalidvalue") }.to raise_error end it "should fail if 'sslverify' does not have one of the following values (True|False)" do - lambda { Puppet::Type.type(:yumrepo).new(:name => "puppetlabs", :sslverify => "notavalidvalue") }.should raise_error + expect { Puppet::Type.type(:yumrepo).new(:name => "puppetlabs", :sslverify => "notavalidvalue") }.to raise_error end it "should succeed if 'sslverify' has one of the following values (True|False)" do @@ -55,169 +62,4 @@ describe Puppet::Type.type(:yumrepo) do Puppet::Type.type(:yumrepo).new(:name => "puppetlabs", :sslverify => "False")[:sslverify].should == "False" end end - - # these tests were ported from the old spec/unit/type/yumrepo_spec.rb, pretty much verbatim - describe "When manipulating config file" do - def make_repo(name, hash={}) - hash[:name] = name - Puppet::Type.type(:yumrepo).new(hash) - end - - def all_sections(inifile) - sections = [] - inifile.each_section { |section| sections << section.name } - sections.sort - end - - def create_data_files() - File.open(File.join(@yumdir, "fedora.repo"), "w") do |f| - f.print(FEDORA_REPO_FILE) - end - - File.open(File.join(@yumdir, "fedora-devel.repo"), "w") do |f| - f.print(FEDORA_DEVEL_REPO_FILE) - end - end - - before(:each) do - @yumdir = tmpdir("yumrepo_spec_tmpdir") - @yumconf = File.join(@yumdir, "yum.conf") - File.open(@yumconf, "w") do |f| - f.print "[main]\nreposdir=#{@yumdir} /no/such/dir\n" - end - Puppet::Type.type(:yumrepo).yumconf = @yumconf - - # It needs to be reset each time, otherwise the cache is used. - Puppet::Type.type(:yumrepo).inifile = nil - end - - it "should be able to create a valid config file" do - values = { - :descr => "Fedora Core $releasever - $basearch - Base", - :baseurl => "http://example.com/yum/$releasever/$basearch/os/", - :enabled => "1", - :gpgcheck => "1", - :includepkgs => "absent", - :gpgkey => "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora", - :proxy => "http://proxy.example.com:80/", - :proxy_username => "username", - :proxy_password => "password" - } - repo = make_repo("base", values) - - catalog = Puppet::Resource::Catalog.new - # Stop Puppet from doing a bunch of magic; might want to think about a util for specs that handles this - catalog.host_config = false - catalog.add_resource(repo) - catalog.apply - - inifile = Puppet::Type.type(:yumrepo).read - sections = all_sections(inifile) - sections.should == ['base', 'main'] - text = inifile["base"].format - text.should == EXPECTED_CONTENTS_FOR_CREATED_FILE - end - - # Modify one existing section - it "should be able to modify an existing config file" do - create_data_files - - devel = make_repo("development", { :descr => "New description" }) - current_values = devel.retrieve - - devel[:name].should == "development" - current_values[devel.property(:descr)].should == 'Fedora Core $releasever - Development Tree' - devel[:descr].should == 'New description' - - catalog = Puppet::Resource::Catalog.new - # Stop Puppet from doing a bunch of magic; might want to think about a util for specs that handles this - catalog.host_config = false - catalog.add_resource(devel) - catalog.apply - - inifile = Puppet::Type.type(:yumrepo).read - inifile['development']['name'].should == 'New description' - inifile['base']['name'].should == 'Fedora Core $releasever - $basearch - Base' - inifile['base']['exclude'].should == "foo\n bar\n baz" - all_sections(inifile).should == ['base', 'development', 'main'] - end - - # Delete mirrorlist by setting it to :absent and enable baseurl - it "should support 'absent' value" do - create_data_files - - baseurl = 'http://example.com/' - - devel = make_repo( - "development", - { :mirrorlist => 'absent', - - :baseurl => baseurl }) - devel.retrieve - - catalog = Puppet::Resource::Catalog.new - # Stop Puppet from doing a bunch of magic; might want to think about a util for specs that handles this - catalog.host_config = false - catalog.add_resource(devel) - catalog.apply - - inifile = Puppet::Type.type(:yumrepo).read - sec = inifile["development"] - sec["mirrorlist"].should == nil - sec["baseurl"].should == baseurl - end - end end - -EXPECTED_CONTENTS_FOR_CREATED_FILE = <<'EOF' -[base] -name=Fedora Core $releasever - $basearch - Base -baseurl=http://example.com/yum/$releasever/$basearch/os/ -enabled=1 -gpgcheck=1 -gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora -proxy=http://proxy.example.com:80/ -proxy_username=username -proxy_password=password -EOF - -FEDORA_REPO_FILE = < Date: Tue, 4 Feb 2014 23:48:26 +0000 Subject: [PATCH 596/800] (maint) Remove the noop check here, we don't allow flush to be called when noop is set. --- lib/puppet/provider/yumrepo/inifile.rb | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/puppet/provider/yumrepo/inifile.rb b/lib/puppet/provider/yumrepo/inifile.rb index 6115ce836..13f0ed692 100644 --- a/lib/puppet/provider/yumrepo/inifile.rb +++ b/lib/puppet/provider/yumrepo/inifile.rb @@ -91,14 +91,13 @@ Puppet::Type.type(:yumrepo).provide(:inifile) do def self.store inifile = self.virtual_inifile inifile.store - unless Puppet[:noop] - target_mode = 0644 - inifile.each_file do |file| - current_mode = Puppet::FileSystem.stat(file).mode & 0777 - unless current_mode == target_mode - Puppet.info "changing mode of #{file} from %03o to %03o" % [current_mode, target_mode] - ::File.chmod(target_mode, file) - end + + target_mode = 0644 + inifile.each_file do |file| + current_mode = Puppet::FileSystem.stat(file).mode & 0777 + unless current_mode == target_mode + Puppet.info "changing mode of #{file} from %03o to %03o" % [current_mode, target_mode] + ::File.chmod(target_mode, file) end end end From 29dffc48070ed78f6ebe99c383ed009fb5eb121f Mon Sep 17 00:00:00 2001 From: jrussek Date: Thu, 9 Jan 2014 12:38:29 +0100 Subject: [PATCH 597/800] (PUP-1407) Rewrite certificate_revocation_list test to use a temporary CA instead of mocks --- .../ssl/certificate_revocation_list_spec.rb | 58 ++++++------------- 1 file changed, 19 insertions(+), 39 deletions(-) diff --git a/spec/unit/ssl/certificate_revocation_list_spec.rb b/spec/unit/ssl/certificate_revocation_list_spec.rb index 21b7e6d99..abdc233f1 100755 --- a/spec/unit/ssl/certificate_revocation_list_spec.rb +++ b/spec/unit/ssl/certificate_revocation_list_spec.rb @@ -5,8 +5,11 @@ require 'puppet/ssl/certificate_revocation_list' describe Puppet::SSL::CertificateRevocationList do before do - @cert = stub 'cert', :subject => "mysubject" - @key = stub 'key', :private? => true + # let's not mock this to make sure that OpenSSL actually does the right stuff + ca = Puppet::SSL::CertificateAuthority.new + ca.generate_ca_certificate + @cert = ca.instance_variable_get(:@certificate).content + @key = ca.instance_variable_get(:@host).key.content @class = Puppet::SSL::CertificateRevocationList end @@ -47,63 +50,45 @@ describe Puppet::SSL::CertificateRevocationList do describe "when generating the crl" do before do - @real_crl = mock 'crl' - @real_crl.stub_everything - - OpenSSL::X509::CRL.stubs(:new).returns(@real_crl) - - @class.any_instance.stubs(:read_or_generate) - @crl = @class.new("crl") end it "should set its issuer to the subject of the passed certificate" do - @real_crl.expects(:issuer=).with(@cert.subject) - - @crl.generate(@cert, @key) + @crl.generate(@cert, @key).issuer.should == @cert.subject end it "should set its version to 1" do - @real_crl.expects(:version=).with(1) - - @crl.generate(@cert, @key) + @crl.generate(@cert, @key).version.should == 1 end it "should create an instance of OpenSSL::X509::CRL" do - OpenSSL::X509::CRL.expects(:new).returns(@real_crl) - - @crl.generate(@cert, @key) + @crl.generate(@cert, @key).should be_an_instance_of(OpenSSL::X509::CRL) end - # The next three tests aren't good, but at least they - # specify the behaviour. + # taken from certificate_factory_spec.rb it "should add an extension for the CRL number" do - @real_crl.expects(:extensions=) - @crl.generate(@cert, @key) + @crl.generate(@cert, @key).extensions.map { |x| x.to_h }.find { |x| x["oid"] == "crlNumber" }.should == + { "oid" => "crlNumber", + "value" => "0", + "critical" => false } end it "should set the last update time" do - @real_crl.expects(:last_update=) - @crl.generate(@cert, @key) + @crl.generate(@cert, @key).last_update.should_not == nil end it "should set the next update time" do - @real_crl.expects(:next_update=) - @crl.generate(@cert, @key) + @crl.generate(@cert, @key).next_update.should_not == nil end - it "should sign the CRL" do - @real_crl.expects(:sign).with { |key, digest| key == @key } - @crl.generate(@cert, @key) + it "should verify using the CA public_key" do + @crl.generate(@cert, @key).verify(@key.public_key).should == true end it "should set the content to the generated crl" do + # this test shouldn't be needed since we test the return of generate() which should be the content field @crl.generate(@cert, @key) - @crl.content.should equal(@real_crl) - end - - it "should return the generated crl" do - @crl.generate(@cert, @key).should equal(@real_crl) + @crl.content.should be_an_instance_of(OpenSSL::X509::CRL) end end @@ -111,16 +96,11 @@ describe Puppet::SSL::CertificateRevocationList do # SSL stuff is very complicated. It just hits the high points. describe "when revoking a certificate" do before do - @class.wrapped_class.any_instance.stubs(:issuer=) - @class.wrapped_class.any_instance.stubs(:sign) - @crl = @class.new("crl") @crl.generate(@cert, @key) - @crl.content.stubs(:sign) Puppet::SSL::CertificateRevocationList.indirection.stubs :save - @key = mock 'key' end it "should require a serial number and the CA's private key" do From ed2e92bee598a3eec3f487642064d21efb216b2e Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Thu, 6 Feb 2014 17:06:34 -0800 Subject: [PATCH 598/800] (PUP-1407) Verify that CRL times are within expected ranges Modifies the test so that we verify the CRL last and next update times are in UTC time (per the RFC) and that the number of seconds since the epoch are within 5 minutes (plus or minus) of the expected value. Also modifies the test to use public APIs to access the CA cert and private key. --- .../ssl/certificate_revocation_list_spec.rb | 47 +++++++++++-------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/spec/unit/ssl/certificate_revocation_list_spec.rb b/spec/unit/ssl/certificate_revocation_list_spec.rb index abdc233f1..d740849b5 100755 --- a/spec/unit/ssl/certificate_revocation_list_spec.rb +++ b/spec/unit/ssl/certificate_revocation_list_spec.rb @@ -5,15 +5,22 @@ require 'puppet/ssl/certificate_revocation_list' describe Puppet::SSL::CertificateRevocationList do before do - # let's not mock this to make sure that OpenSSL actually does the right stuff ca = Puppet::SSL::CertificateAuthority.new ca.generate_ca_certificate - @cert = ca.instance_variable_get(:@certificate).content - @key = ca.instance_variable_get(:@host).key.content - + @cert = ca.host.certificate.content + @key = ca.host.key.content @class = Puppet::SSL::CertificateRevocationList end + def expects_time_close_to_now(time) + expect(time.to_i).to be_within(5*60).of(Time.now.to_i) + end + + def expects_time_close_to_five_years(time) + future = Time.now + Puppet::SSL::CertificateRevocationList::FIVE_YEARS + expect(time.to_i).to be_within(5*60).of(future.to_i) + end + it "should only support the text format" do @class.supported_formats.should == [:s] end @@ -73,16 +80,22 @@ describe Puppet::SSL::CertificateRevocationList do "critical" => false } end - it "should set the last update time" do - @crl.generate(@cert, @key).last_update.should_not == nil + it "returns the last update time in UTC" do + # http://tools.ietf.org/html/rfc5280#section-5.1.2.4 + thisUpdate = @crl.generate(@cert, @key).last_update + thisUpdate.should be_utc + expects_time_close_to_now(thisUpdate) end - it "should set the next update time" do - @crl.generate(@cert, @key).next_update.should_not == nil + it "returns the next update time in UTC 5 years from now" do + # http://tools.ietf.org/html/rfc5280#section-5.1.2.5 + nextUpdate = @crl.generate(@cert, @key).next_update + nextUpdate.should be_utc + expects_time_close_to_five_years(nextUpdate) end it "should verify using the CA public_key" do - @crl.generate(@cert, @key).verify(@key.public_key).should == true + @crl.generate(@cert, @key).verify(@key.public_key).should be_true end it "should set the content to the generated crl" do @@ -104,7 +117,7 @@ describe Puppet::SSL::CertificateRevocationList do end it "should require a serial number and the CA's private key" do - lambda { @crl.revoke }.should raise_error(ArgumentError) + expect { @crl.revoke }.to raise_error(ArgumentError) end it "should default to OpenSSL::OCSP::REVOKED_STATUS_KEYCOMPROMISE as the revocation reason" do @@ -117,21 +130,15 @@ describe Puppet::SSL::CertificateRevocationList do end it "should mark the CRL as updated at a time that makes it valid now" do - time = Time.now - Time.stubs(:now).returns time - - @crl.content.expects(:last_update=).with(time - 1) - @crl.revoke(1, @key) + + expects_time_close_to_now(@crl.content.last_update) end it "should mark the CRL valid for five years" do - time = Time.now - Time.stubs(:now).returns time - - @crl.content.expects(:next_update=).with(time + (5 * 365*24*60*60)) - @crl.revoke(1, @key) + + expects_time_close_to_five_years(@crl.content.next_update) end it "should sign the CRL with the CA's private key and a digest instance" do From 64f94d1dcd3441883cd69db7949f58439dd5efb0 Mon Sep 17 00:00:00 2001 From: jrussek Date: Thu, 9 Jan 2014 12:40:11 +0100 Subject: [PATCH 599/800] (PUP-1407) Add authorityKeyIdentifier to CRL to make the CA conform to RFC5280 RFC5280 requires CRLs to contain the authorityKeyIdentifier extension[1]. It identifies the public key that corresponds to the private key used to sign the CRL. RFC5280 also requires the use of the key identifier method. This implementation uses the key identifier format described in OpenSSL[2]. In particular, the `always` value is used to require that the extension is present. Also note that we need to invoke the `add_extension` method instead of overwriting the extensions. [1] http://tools.ietf.org/html/rfc5280#section-5.2.1 [2] http://www.openssl.org/docs/apps/x509v3_config.html#Authority_Key_Identifier_ --- lib/puppet/ssl/certificate_revocation_list.rb | 4 +++- spec/unit/ssl/certificate_revocation_list_spec.rb | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/puppet/ssl/certificate_revocation_list.rb b/lib/puppet/ssl/certificate_revocation_list.rb index e19534764..f686ed0a9 100644 --- a/lib/puppet/ssl/certificate_revocation_list.rb +++ b/lib/puppet/ssl/certificate_revocation_list.rb @@ -60,13 +60,15 @@ DOC private def create_crl_issued_by(cert) + ef = OpenSSL::X509::ExtensionFactory.new(cert) @content = wrapped_class.new @content.issuer = cert.subject + @content.add_extension(ef.create_ext("authorityKeyIdentifier", "keyid:always")) @content.version = 1 end def start_at_initial_crl_number - @content.extensions = [crl_number_of(0)] + @content.add_extension(crl_number_of(0)) end def add_certificate_revocation_for(serial, reason, time) diff --git a/spec/unit/ssl/certificate_revocation_list_spec.rb b/spec/unit/ssl/certificate_revocation_list_spec.rb index d740849b5..4bbc93b8d 100755 --- a/spec/unit/ssl/certificate_revocation_list_spec.rb +++ b/spec/unit/ssl/certificate_revocation_list_spec.rb @@ -80,6 +80,11 @@ describe Puppet::SSL::CertificateRevocationList do "critical" => false } end + it "should add an extension for the authority key identifier" do + ef = OpenSSL::X509::ExtensionFactory.new(@cert) + @crl.generate(@cert, @key).extensions.map { |x| x.to_h }.find { |x| x["oid"] == "authorityKeyIdentifier" }.should == + ef.create_extension("authorityKeyIdentifier", "keyid:always", false).to_h + end it "returns the last update time in UTC" do # http://tools.ietf.org/html/rfc5280#section-5.1.2.4 thisUpdate = @crl.generate(@cert, @key).last_update From e32b0e9d37d8e0ee670d4a04a22b0c543fb09798 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 7 Feb 2014 11:42:59 -0800 Subject: [PATCH 600/800] (PUP-1407) Verify extensions are still present after revocation Previously, we were only testing that the crlNumber and authorityKeyIdentifier extensions were present when a CRL is generated, but not when it is revoked. This adds helpers for testing the extensions and updates both cases to use them. It also is more explicit in verifying that the authorityKeyIdentifier extension has the same value as the subjectKeyIdentifier from the issuing CA cert. --- .../ssl/certificate_revocation_list_spec.rb | 43 +++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/spec/unit/ssl/certificate_revocation_list_spec.rb b/spec/unit/ssl/certificate_revocation_list_spec.rb index 4bbc93b8d..8ef55dfb8 100755 --- a/spec/unit/ssl/certificate_revocation_list_spec.rb +++ b/spec/unit/ssl/certificate_revocation_list_spec.rb @@ -21,6 +21,21 @@ describe Puppet::SSL::CertificateRevocationList do expect(time.to_i).to be_within(5*60).of(future.to_i) end + def expects_crlnumber_extension(crl, value) + crlNumber = crl.content.extensions.find { |ext| ext.oid == "crlNumber" } + + expect(crlNumber.value).to eq(value.to_s) + expect(crlNumber).to_not be_critical + end + + def expects_authkeyid_extension(crl, cert) + subjectKeyId = cert.extensions.find { |ext| ext.oid == 'subjectKeyIdentifier' }.value + + authKeyId = crl.content.extensions.find { |ext| ext.oid == "authorityKeyIdentifier" } + expect(authKeyId.value.chomp).to eq("keyid:#{subjectKeyId}") + expect(authKeyId).to_not be_critical + end + it "should only support the text format" do @class.supported_formats.should == [:s] end @@ -72,19 +87,18 @@ describe Puppet::SSL::CertificateRevocationList do @crl.generate(@cert, @key).should be_an_instance_of(OpenSSL::X509::CRL) end - # taken from certificate_factory_spec.rb it "should add an extension for the CRL number" do - @crl.generate(@cert, @key).extensions.map { |x| x.to_h }.find { |x| x["oid"] == "crlNumber" }.should == - { "oid" => "crlNumber", - "value" => "0", - "critical" => false } + @crl.generate(@cert, @key) + + expects_crlnumber_extension(@crl, 0) end it "should add an extension for the authority key identifier" do - ef = OpenSSL::X509::ExtensionFactory.new(@cert) - @crl.generate(@cert, @key).extensions.map { |x| x.to_h }.find { |x| x["oid"] == "authorityKeyIdentifier" }.should == - ef.create_extension("authorityKeyIdentifier", "keyid:always", false).to_h + @crl.generate(@cert, @key) + + expects_authkeyid_extension(@crl, @cert) end + it "returns the last update time in UTC" do # http://tools.ietf.org/html/rfc5280#section-5.1.2.4 thisUpdate = @crl.generate(@cert, @key).last_update @@ -155,5 +169,18 @@ describe Puppet::SSL::CertificateRevocationList do Puppet::SSL::CertificateRevocationList.indirection.expects(:save).with(@crl, nil) @crl.revoke(1, @key) end + + it "adds the crlNumber extension containing the serial number" do + serial = 1 + @crl.revoke(serial, @key) + + expects_crlnumber_extension(@crl, serial) + end + + it "adds the CA cert's subjectKeyId as the authorityKeyIdentifier to the CRL" do + @crl.revoke(1, @key) + + expects_authkeyid_extension(@crl, @cert) + end end end From 4b753118e89af432b2505eedd81b8ca76c868830 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 7 Feb 2014 12:22:23 -0800 Subject: [PATCH 601/800] (PUP-1407) Actually test deserialization and CRLReasons Previously, the test was stubbing everything about how CRL deserialization worked to the point that `from_s('my crl')` was passing when it shouldn't. The from_s method expects a PEM encoded string. This commit actually tests deserialization, and adds tests to ensure the CRL contains CRLReason entry with valid reason codes. --- .../ssl/certificate_revocation_list_spec.rb | 52 +++++++++++-------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/spec/unit/ssl/certificate_revocation_list_spec.rb b/spec/unit/ssl/certificate_revocation_list_spec.rb index 8ef55dfb8..813e6c89d 100755 --- a/spec/unit/ssl/certificate_revocation_list_spec.rb +++ b/spec/unit/ssl/certificate_revocation_list_spec.rb @@ -36,28 +36,33 @@ describe Puppet::SSL::CertificateRevocationList do expect(authKeyId).to_not be_critical end + def expects_crlreason_extension(crl, reason) + revoke = crl.content.revoked.first + + crlNumber = crl.content.extensions.find { |ext| ext.oid == "crlNumber" } + expect(revoke.serial.to_s).to eq(crlNumber.value) + + crlReason = revoke.extensions.find { |ext| ext.oid = 'CRLReason' } + expect(crlReason.value).to eq(reason) + expect(crlReason).to_not be_critical + end + it "should only support the text format" do @class.supported_formats.should == [:s] end describe "when converting from a string" do - it "should create a CRL instance with its name set to 'foo' and its content set to the extracted CRL" do - crl = stub 'crl', :is_a? => true - OpenSSL::X509::CRL.expects(:new).returns(crl) + it "deserializes a CRL" do + crl = @class.new('foo') + crl.generate(@cert, @key) - mycrl = stub 'sslcrl' - mycrl.expects(:content=).with(crl) - - @class.expects(:new).with("foo").returns mycrl - - @class.from_s("my crl").should == mycrl + new_crl = @class.from_s(crl.to_s) + expect(new_crl.content.to_text).to eq(crl.content.to_text) end end describe "when an instance" do before do - @class.any_instance.stubs(:read_or_generate) - @crl = @class.new("whatever") end @@ -132,22 +137,12 @@ describe Puppet::SSL::CertificateRevocationList do @crl.generate(@cert, @key) Puppet::SSL::CertificateRevocationList.indirection.stubs :save - end it "should require a serial number and the CA's private key" do expect { @crl.revoke }.to raise_error(ArgumentError) end - it "should default to OpenSSL::OCSP::REVOKED_STATUS_KEYCOMPROMISE as the revocation reason" do - # This makes it a bit more of an integration test than we'd normally like, but that's life - # with openssl. - reason = OpenSSL::ASN1::Enumerated(OpenSSL::OCSP::REVOKED_STATUS_KEYCOMPROMISE) - OpenSSL::ASN1.expects(:Enumerated).with(OpenSSL::OCSP::REVOKED_STATUS_KEYCOMPROMISE).returns reason - - @crl.revoke(1, @key) - end - it "should mark the CRL as updated at a time that makes it valid now" do @crl.revoke(1, @key) @@ -182,5 +177,20 @@ describe Puppet::SSL::CertificateRevocationList do expects_authkeyid_extension(@crl, @cert) end + + it "adds a non-critical CRL reason specifying key compromise by default" do + # http://tools.ietf.org/html/rfc5280#section-5.3.1 + serial = 1 + @crl.revoke(serial, @key) + + expects_crlreason_extension(@crl, 'Key Compromise') + end + + it "allows alternate reasons to be specified" do + serial = 1 + @crl.revoke(serial, @key, OpenSSL::OCSP::REVOKED_STATUS_CACOMPROMISE) + + expects_crlreason_extension(@crl, 'CA Compromise') + end end end From d65211e0c25d74f6dac385c750cf0b0ef4f21888 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 7 Feb 2014 14:47:39 -0800 Subject: [PATCH 602/800] (PUP-1407) Fix specs on 187 In ruby 1.8.7, OpenSSL::X509::Name does not include Comparable, so `==` is not based on the `<=>` operator. As a result, comparing two Name objects that have the same string representation will return false. In ruby 1.9, the class includes the Comparable module. Changed the test to just compare the String representations. The other test was failing for me on ruby 187, and it seemed completely unnecessary to compute 2**158 every time the test runs. --- spec/unit/ssl/certificate_authority_spec.rb | 4 +++- spec/unit/ssl/certificate_revocation_list_spec.rb | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/spec/unit/ssl/certificate_authority_spec.rb b/spec/unit/ssl/certificate_authority_spec.rb index 758c31d58..69114a403 100755 --- a/spec/unit/ssl/certificate_authority_spec.rb +++ b/spec/unit/ssl/certificate_authority_spec.rb @@ -960,7 +960,9 @@ describe Puppet::SSL::CertificateAuthority do it "handles very large serial numbers" do bighex = '0x4000000000000000000000000000000000000000' - @ca.crl.expects(:revoke).with { |serial, key| serial == 2**(159-1) } + bighex_int = 365375409332725729550921208179070754913983135744 + + @ca.crl.expects(:revoke).with(bighex_int, anything) Puppet::SSL::Certificate.indirection.expects(:find).with(bighex).returns nil @ca.revoke(bighex) diff --git a/spec/unit/ssl/certificate_revocation_list_spec.rb b/spec/unit/ssl/certificate_revocation_list_spec.rb index 813e6c89d..cf25022b0 100755 --- a/spec/unit/ssl/certificate_revocation_list_spec.rb +++ b/spec/unit/ssl/certificate_revocation_list_spec.rb @@ -81,7 +81,7 @@ describe Puppet::SSL::CertificateRevocationList do end it "should set its issuer to the subject of the passed certificate" do - @crl.generate(@cert, @key).issuer.should == @cert.subject + @crl.generate(@cert, @key).issuer.to_s.should == @cert.subject.to_s end it "should set its version to 1" do From 0263cabe131f68e592c3093ccb0ec6513261f7f5 Mon Sep 17 00:00:00 2001 From: Ashley Penney Date: Mon, 10 Feb 2014 16:35:56 +0000 Subject: [PATCH 603/800] Ensure that section cannot accidently return nil. --- lib/puppet/provider/yumrepo/inifile.rb | 18 ++++++++++-------- spec/unit/provider/yumrepo/inifile_spec.rb | 21 +++++++++++++++++++-- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/lib/puppet/provider/yumrepo/inifile.rb b/lib/puppet/provider/yumrepo/inifile.rb index 13f0ed692..24caf8b90 100644 --- a/lib/puppet/provider/yumrepo/inifile.rb +++ b/lib/puppet/provider/yumrepo/inifile.rb @@ -33,13 +33,17 @@ Puppet::Type.type(:yumrepo).provide(:inifile) do end end - # Search for a reposdir in the yum configuration file and append it to the - # list of repodirs to use. + # Return a list of existing directories that could contain repo files. Fail if none found. def self.reposdir(conf='/etc/yum.conf', dirs=['/etc/yum.repos.d', '/etc/yum/repos.d']) reposdir = find_conf_value('reposdir', conf) dirs << reposdir if reposdir - return dirs + dirs.select! { |dir| Puppet::FileSystem.exist?(dir) } + if dirs.empty? + fail("No yum directories were found on the local filesystem") + else + return dirs + end end # Find configuration values in .conf files and return them @@ -77,11 +81,9 @@ Puppet::Type.type(:yumrepo).provide(:inifile) do # Create a new section if not found. unless result reposdir.each do |dir| - if File.directory?(dir) - path = ::File.join(dir, "#{name}.repo") - Puppet.info("create new repo #{name} in file #{path}") - result = self.virtual_inifile.add_section(name, path) - end + path = ::File.join(dir, "#{name}.repo") + Puppet.info("create new repo #{name} in file #{path}") + result = self.virtual_inifile.add_section(name, path) end end result diff --git a/spec/unit/provider/yumrepo/inifile_spec.rb b/spec/unit/provider/yumrepo/inifile_spec.rb index d362193bc..fae5ff28d 100644 --- a/spec/unit/provider/yumrepo/inifile_spec.rb +++ b/spec/unit/provider/yumrepo/inifile_spec.rb @@ -44,6 +44,7 @@ descr="test updates" describe 'create' do it 'creates a yumrepo' do + described_class.expects(:reposdir).returns(['/etc/yum.repos.d']) yumrepo_provider.section('puppetlabs-products').expects(:[]=).at_least(1) yumrepo_provider.create end @@ -69,7 +70,8 @@ descr="test updates" let(:all) { ['/etc/yum.repos.d', '/etc/yum/repos.d', '/etc/yum/test'] } it 'returns defaults if no yum conf' do - File.expects(:exists?).with('/etc/yum.conf').returns(false) + Puppet::FileSystem.expects(:exist?).with('/etc/yum.repos.d').returns(true) + Puppet::FileSystem.expects(:exist?).with('/etc/yum/repos.d').returns(true) described_class.reposdir('/etc/yum.conf').should == defaults end @@ -77,17 +79,32 @@ descr="test updates" it 'returns defaults if yumconf has no reposdir' do File.expects(:exists?).with('/etc/yum.conf').returns(true) File.expects(:read).with('/etc/yum.conf').returns("[main]\ntest = /etc/yum/test") + Puppet::FileSystem.expects(:exist?).with('/etc/yum.repos.d').returns(true) + Puppet::FileSystem.expects(:exist?).with('/etc/yum/repos.d').returns(true) described_class.reposdir('/etc/yum.conf').should == defaults end - it 'returns all directories if yum.conf contains reposdir' do + it 'returns all directories if yum.conf contains reposdir and directory exists' do File.expects(:exists?).with('/etc/yum.conf').returns(true) File.expects(:read).with('/etc/yum.conf').returns("[main]\nreposdir = /etc/yum/test") + Puppet::FileSystem.expects(:exist?).with('/etc/yum.repos.d').returns(true) + Puppet::FileSystem.expects(:exist?).with('/etc/yum/repos.d').returns(true) + Puppet::FileSystem.expects(:exist?).with('/etc/yum/test').returns(true) described_class.reposdir('/etc/yum.conf').should == all end + it 'returns defaults if yum.conf contains reposdir and directory doesnt exist' do + File.expects(:exists?).with('/etc/yum.conf').returns(true) + File.expects(:read).with('/etc/yum.conf').returns("[main]\nreposdir = /etc/yum/test") + Puppet::FileSystem.expects(:exist?).with('/etc/yum.repos.d').returns(true) + Puppet::FileSystem.expects(:exist?).with('/etc/yum/repos.d').returns(true) + Puppet::FileSystem.expects(:exist?).with('/etc/yum/test').returns(false) + + described_class.reposdir('/etc/yum.conf').should == defaults + end + end From dd5d3e13036ddb347b88b2bf2269b4ea13eaf34c Mon Sep 17 00:00:00 2001 From: Ashley Penney Date: Mon, 10 Feb 2014 12:11:15 -0500 Subject: [PATCH 604/800] (maint) Added Yarddoc documentation. --- lib/puppet/provider/yumrepo/inifile.rb | 38 ++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/lib/puppet/provider/yumrepo/inifile.rb b/lib/puppet/provider/yumrepo/inifile.rb index 24caf8b90..e73588261 100644 --- a/lib/puppet/provider/yumrepo/inifile.rb +++ b/lib/puppet/provider/yumrepo/inifile.rb @@ -5,6 +5,8 @@ Puppet::Type.type(:yumrepo).provide(:inifile) do PROPERTIES = Puppet::Type.type(:yumrepo).validproperties + # @return [Array] Return all the providers built up from + # discovered content on the local node. def self.instances instances = [] # Iterate over each section of our virtual file. @@ -24,6 +26,8 @@ Puppet::Type.type(:yumrepo).provide(:inifile) do return instances end + # @param resources [Array] Resources to prefetch. + # @return [Array] Resources with providers set. def self.prefetch(resources) repos = instances resources.keys.each do |name| @@ -34,20 +38,26 @@ Puppet::Type.type(:yumrepo).provide(:inifile) do end # Return a list of existing directories that could contain repo files. Fail if none found. + # @param conf [String] Configuration file to look for directories in. + # @param dirs [Array] Default locations for yum repos. + # @return [Array] Directories that were found to exist on the node. def self.reposdir(conf='/etc/yum.conf', dirs=['/etc/yum.repos.d', '/etc/yum/repos.d']) reposdir = find_conf_value('reposdir', conf) dirs << reposdir if reposdir dirs.select! { |dir| Puppet::FileSystem.exist?(dir) } if dirs.empty? - fail("No yum directories were found on the local filesystem") + fail('No yum directories were found on the local filesystem') else return dirs end end - # Find configuration values in .conf files and return them - # if found. + # Helper method to look up specific values in ini style files. + # @todo Migrate this into Puppet::Util::IniConfig. + # @param value [String] Value to look for in the configuration file. + # @param conf [String] Configuration file to check for value. + # @return [String] The value of a looked up key from the configuration file. def self.find_conf_value(value, conf='/etc/yum.conf') if File.exists?(conf) contents = File.read(conf) @@ -59,6 +69,8 @@ Puppet::Type.type(:yumrepo).provide(:inifile) do # Build a virtual inifile by reading in numerous .repo # files into a single virtual file to ease manipulation. + # @return [Puppet::Util::IniConfig::File] The virtual inifile representing + # multiple real files. def self.virtual_inifile unless @virtual @virtual = Puppet::Util::IniConfig::File.new @@ -71,11 +83,19 @@ Puppet::Type.type(:yumrepo).provide(:inifile) do return @virtual end + # @param key [String] The property to look up. + # @return [Boolean] Returns true if the property is defined in the type. def self.valid_property?(key) PROPERTIES.include?(key) end - # Return the named section out of the virtual_inifile. + # We need to return a valid section from the larger virtual inifile here, + # which we do by first looking it up and then creating a new section for + # the appropriate name if none was found. + # @todo Why do we create a section for every repodir found? + # @todo Does this mean we only return the last section made? + # @param name [String] Section name to lookup in the virtual inifile. + # @return [Puppet::Util::IniConfig] The IniConfig section def self.section(name) result = self.virtual_inifile[name] # Create a new section if not found. @@ -89,7 +109,8 @@ Puppet::Type.type(:yumrepo).provide(:inifile) do result end - # Store all modifications back to disk + # Here we store all modifications to disk, forcing the output file to 0644 if it differs. + # @return [void] def self.store inifile = self.virtual_inifile inifile.store @@ -104,6 +125,7 @@ Puppet::Type.type(:yumrepo).provide(:inifile) do end end + # @return [void] def create @property_hash[:ensure] = :present @@ -120,6 +142,9 @@ Puppet::Type.type(:yumrepo).provide(:inifile) do end end + # We don't actually destroy the file here, merely mark it for + # destruction in the section. + # @return [void] def destroy # Flag file for deletion on flush. section(@resource[:name]).destroy=(true) @@ -127,10 +152,12 @@ Puppet::Type.type(:yumrepo).provide(:inifile) do @property_hash.clear end + # @return [void] def flush self.class.store end + # @return [void] def section(name) self.class.section(name) end @@ -147,6 +174,7 @@ Puppet::Type.type(:yumrepo).provide(:inifile) do end end + # @return [Boolean] Returns true if ensure => present. def exists? @property_hash[:ensure] == :present end From 601b0fa77d63dd918d8f5373d29c2a45f5fc1efb Mon Sep 17 00:00:00 2001 From: Ashley Penney Date: Mon, 10 Feb 2014 14:17:36 -0500 Subject: [PATCH 605/800] Replace .select! with delete_if for ruby 1.8.7 --- lib/puppet/provider/yumrepo/inifile.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/puppet/provider/yumrepo/inifile.rb b/lib/puppet/provider/yumrepo/inifile.rb index e73588261..20f193b89 100644 --- a/lib/puppet/provider/yumrepo/inifile.rb +++ b/lib/puppet/provider/yumrepo/inifile.rb @@ -45,7 +45,9 @@ Puppet::Type.type(:yumrepo).provide(:inifile) do reposdir = find_conf_value('reposdir', conf) dirs << reposdir if reposdir - dirs.select! { |dir| Puppet::FileSystem.exist?(dir) } + # We can't use the below due to Ruby 1.8.7 + # dirs.select! { |dir| Puppet::FileSystem.exist?(dir) } + dirs.delete_if { |dir| ! Puppet::FileSystem.exist?(dir) } if dirs.empty? fail('No yum directories were found on the local filesystem') else From b915e7f95e957b6ad15abab784743bc3c3ca4619 Mon Sep 17 00:00:00 2001 From: Nick Fagerlund Date: Mon, 10 Feb 2014 12:05:32 -0800 Subject: [PATCH 606/800] (docs) Update description for Exec type's logoutput parameter The logoutput parameter can't accept log levels as legal values. This has been wrong since 2008 (commit bb8051bc406d1da67db8212e852bb36d1368e953) and it sounds like it was practically wrong long before that. --- lib/puppet/type/exec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/puppet/type/exec.rb b/lib/puppet/type/exec.rb index 6091baafa..732a0c1c5 100644 --- a/lib/puppet/type/exec.rb +++ b/lib/puppet/type/exec.rb @@ -207,8 +207,8 @@ module Puppet desc "Whether to log command output in addition to logging the exit code. Defaults to `on_failure`, which only logs the output when the command has an exit code that does not match any value - specified by the `returns` attribute. In addition to the values - below, you may set this attribute to any legal log level." + specified by the `returns` attribute. As with any resource type, + the log level can be controlled with the `loglevel` metaparameter." defaultto :on_failure From 065c4f5b5c78ca8f16c189b55051681beb1c5052 Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Mon, 10 Feb 2014 13:38:09 -0800 Subject: [PATCH 607/800] (PUP-1473) Fix issue with UTF-8 in user comments GH-1943 included a change to munge the comment property on the user type so that it gets encoded as ASCII-8BIT. This was to allow the comment property to handle UTF-8 values as existing values are read as ASCII-8BIT. Originally the change called `force_encoding` to force the encoding to ASCII-8BIT. This method does not actually attempt to encode the string, it only changes the string's associated encoding. It was suggested during code review that a call to `encode` should be used instead. However, calling `encode` will fail if the string has UTF-8 characters that cannot be represented in ASCII-8BIT. As we desire the comment property to be treated as a squence of bytes in this case, we should be calling `force_encoding` instead of `encode`. --- lib/puppet/type/user.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/type/user.rb b/lib/puppet/type/user.rb index 5240810b4..f56289256 100644 --- a/lib/puppet/type/user.rb +++ b/lib/puppet/type/user.rb @@ -168,7 +168,7 @@ module Puppet newproperty(:comment) do desc "A description of the user. Generally the user's full name." munge do |v| - v.respond_to?(:encode) ? v.encode(Encoding::ASCII_8BIT) : v + v.respond_to?(:force_encoding) ? v.force_encoding(Encoding::ASCII_8BIT) : v end end From 1d52d350e65b1186445e85ffb568a57e46ff0f5f Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Mon, 10 Feb 2014 14:39:21 -0800 Subject: [PATCH 608/800] (pup-1636) Add a rhel7 config --- acceptance/config/nodes/rhel7.yaml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 acceptance/config/nodes/rhel7.yaml diff --git a/acceptance/config/nodes/rhel7.yaml b/acceptance/config/nodes/rhel7.yaml new file mode 100644 index 000000000..b5706dd47 --- /dev/null +++ b/acceptance/config/nodes/rhel7.yaml @@ -0,0 +1,20 @@ +HOSTS: + master: + roles: + - master + - agent + platform: el-7-x86_64 + hypervisor: vcloud + template: Delivery/Quality Assurance/Templates/vCloud/redhat-7-x86_64 + agent: + roles: + - agent + platform: el-7-x86_64 + hypervisor: vcloud + template: Delivery/Quality Assurance/Templates/vCloud/redhat-7-x86_64 +CONFIG: + filecount: 12 + datastore: instance0 + resourcepool: delivery/Quality Assurance/FOSS/Dynamic + folder: Delivery/Quality Assurance/FOSS/Dynamic + pooling_api: http://vcloud.delivery.puppetlabs.net/ From 9ffb57ae8d49540d774a975d665e87a3d3296659 Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Mon, 10 Feb 2014 14:40:01 -0800 Subject: [PATCH 609/800] (pup-1636) Add firewall service name for rhel7 --- acceptance/lib/puppet/acceptance/install_utils.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance/lib/puppet/acceptance/install_utils.rb b/acceptance/lib/puppet/acceptance/install_utils.rb index 85ac84b01..5df423574 100644 --- a/acceptance/lib/puppet/acceptance/install_utils.rb +++ b/acceptance/lib/puppet/acceptance/install_utils.rb @@ -72,7 +72,7 @@ module Puppet case host['platform'] when /debian/ on host, 'iptables -F' - when /fedora/ + when /fedora|el-7/ on host, puppet('resource', 'service', 'firewalld', 'ensure=stopped') when /el|centos/ on host, puppet('resource', 'service', 'iptables', 'ensure=stopped') From 5a5bd36623b3156a3fd925713c2b2e564d0491be Mon Sep 17 00:00:00 2001 From: Felix Frank Date: Sun, 24 Mar 2013 00:04:39 +0100 Subject: [PATCH 610/800] (#3220) crontab: allow purging unmanaged resources The problem was that unmanaged cronjobs would be parsed, but not implicitly named. Workaround: Pick an implicit name. It's derived from the command. The provider will never put this implicit name into a crontab file when writing it back to disk. The names are not unique if several unmanaged jobs execute the same command. That's a wrinkle and not mission critical, but it can be surprising for users, because the "duplicates" will not be purged during the first run. --- lib/puppet/provider/cron/crontab.rb | 7 ++++++- .../integration/provider/cron/crontab/purged | 8 +++++++ .../integration/provider/cron/crontab_spec.rb | 21 +++++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 spec/fixtures/integration/provider/cron/crontab/purged diff --git a/lib/puppet/provider/cron/crontab.rb b/lib/puppet/provider/cron/crontab.rb index 91047af78..304a1ff04 100644 --- a/lib/puppet/provider/cron/crontab.rb +++ b/lib/puppet/provider/cron/crontab.rb @@ -68,6 +68,7 @@ Puppet::Type.type(:cron).provide(:crontab, :parent => Puppet::Provider::ParsedFi def to_line(record) str = "" + record[:name] = nil if record[:unmanaged] str = "# Puppet Name: #{record[:name]}\n" if record[:name] if record[:environment] and record[:environment] != :absent str += record[:environment].map {|line| "#{line}\n"}.join('') @@ -132,7 +133,8 @@ Puppet::Type.type(:cron).provide(:crontab, :parent => Puppet::Provider::ParsedFi # See if we can match the record against an existing cron job. def self.match(record, resources) # if the record is named, do not even bother (#19876) - return false if record[:name] + # except the resource name was implicitly generated (#3220) + return false if record[:name] and !record[:unmanaged] resources.each do |name, resource| # Match the command first, since it's the most important one. next unless record[:target] == resource[:target] @@ -194,6 +196,9 @@ Puppet::Type.type(:cron).provide(:crontab, :parent => Puppet::Provider::ParsedFi if name record[:name] = name name = nil + else + record[:name] = "unmanaged:" + record[:command].gsub(/\s+/, "_") + record[:unmanaged] = true end if envs.nil? or envs.empty? record[:environment] = :absent diff --git a/spec/fixtures/integration/provider/cron/crontab/purged b/spec/fixtures/integration/provider/cron/crontab/purged new file mode 100644 index 000000000..b302836b0 --- /dev/null +++ b/spec/fixtures/integration/provider/cron/crontab/purged @@ -0,0 +1,8 @@ +# HEADER: some simple +# HEADER: header + +# commend with blankline above and below + + +# Puppet Name: only managed entry +* * * * * /bin/true diff --git a/spec/integration/provider/cron/crontab_spec.rb b/spec/integration/provider/cron/crontab_spec.rb index 3c262c926..84ad4681c 100644 --- a/spec/integration/provider/cron/crontab_spec.rb +++ b/spec/integration/provider/cron/crontab_spec.rb @@ -40,6 +40,10 @@ describe Puppet::Type.type(:cron).provider(:crontab), '(integration)', :unless = resource.expects(:err).never catalog.add_resource(resource) end + + # the resources are not properly contained and generated resources + # will end up with dangling edges without this stubbing: + catalog.stubs(:container_of).returns resources[0] catalog.apply end @@ -48,6 +52,23 @@ describe Puppet::Type.type(:cron).provider(:crontab), '(integration)', :unless = end describe "when managing a cron entry" do + + it "should be able to purge unmanaged entries" do + resource = Puppet::Type.type(:cron).new( + :name => 'only managed entry', + :ensure => :present, + :command => '/bin/true', + :target => crontab_user1, + :user => crontab_user1 + ) + resources = Puppet::Type.type(:resources).new( + :name => 'cron', + :purge => 'true' + ) + run_in_catalog(resource, resources) + expect_output('purged') + end + describe "with ensure absent" do it "should do nothing if entry already absent" do resource = Puppet::Type.type(:cron).new( From 5645ba21ae6a758ab3c0df30c3f10926abf83f3f Mon Sep 17 00:00:00 2001 From: Felix Frank Date: Sat, 2 Nov 2013 01:33:32 +0100 Subject: [PATCH 611/800] (#3220) make sure that generated cron resources belong to the correct user This solves a testing issue that resulted from the #2251 fix. The new problem is that resources are no longer just looked up by name. Now, the resource's user property has to match the file being read from disk. The problem with generated resources is that they use the default user (i.e., the user running the spec test). Therefor, the resources would now be considered independent from the crontab records from the testing fixture. It's fixed by making sure that the respective user property matches the crontab from which the resources are generated. This is done while the resources are marked as "being purged" by overriding the Type#purging method. --- lib/puppet/type/cron.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/puppet/type/cron.rb b/lib/puppet/type/cron.rb index c6d6c3a36..a6aade7a8 100644 --- a/lib/puppet/type/cron.rb +++ b/lib/puppet/type/cron.rb @@ -411,6 +411,22 @@ Puppet::Type.newtype(:cron) do attr_accessor :uid + # Marks the resource as "being purged". + # + # @api public + # + # @note This overrides the Puppet::Type method in order to handle + # an edge case that has so far been observed during testig only. + # Without forcing the should-value for the user property to be + # identical to the original cron file, purging from a fixture + # will not work, because the user property defaults to the user + # running the test. It is not clear whether this scenario can apply + # during normal operation. + def purging + self[:user] = provider.property_hash[:target] + super + end + def value(name) name = name.intern ret = nil From fd200e645206d3ac817609edb2d2c751a7140064 Mon Sep 17 00:00:00 2001 From: Felix Frank Date: Sun, 3 Nov 2013 17:13:09 +0100 Subject: [PATCH 612/800] (#3220) fix a cron testing quirk with potential benefits for normale operation The integration test for the purging of unmanaged cron resources had the bizarre side effect of writing vestiges of the fixture to ./$USER whenever the test was run. The reason were default should-values for the target property of cron resources. Per default, the target for any cron resource is the current user. When generating resources (through the instance method), the default is erronously used. When purging, we can fix this by forcing the should-value of each resource being purged to be the file the resource was generated from. This is done in the override of the purging method. --- lib/puppet/type/cron.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/puppet/type/cron.rb b/lib/puppet/type/cron.rb index a6aade7a8..7f5f1b805 100644 --- a/lib/puppet/type/cron.rb +++ b/lib/puppet/type/cron.rb @@ -422,7 +422,12 @@ Puppet::Type.newtype(:cron) do # will not work, because the user property defaults to the user # running the test. It is not clear whether this scenario can apply # during normal operation. + # + # @note Also, when not forcing the should-value for the target + # property, unpurged file content (such as comments) can end up + # being written to the default target (i.e. the current login name). def purging + self[:target] = provider.property_hash[:target] self[:user] = provider.property_hash[:target] super end From 3957d380bb789fffe563b166d0201187c46bfa23 Mon Sep 17 00:00:00 2001 From: Felix Frank Date: Sun, 3 Nov 2013 18:27:02 +0100 Subject: [PATCH 613/800] (#3220) make the unit test "generating cron resources" match the new behaviour To allow purging of unmanaged resources, they are now implicitly named. The unit test must take that into account. --- spec/unit/provider/cron/parsed_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/unit/provider/cron/parsed_spec.rb b/spec/unit/provider/cron/parsed_spec.rb index a96fa7ed8..333558fdf 100644 --- a/spec/unit/provider/cron/parsed_spec.rb +++ b/spec/unit/provider/cron/parsed_spec.rb @@ -208,7 +208,7 @@ describe Puppet::Type.type(:cron).provider(:crontab) do h end.should == [ { - :name => :absent, + :name => 'unmanaged:$HOME/bin/daily.job_>>_$HOME/tmp/out_2>&1', :minute => ['5'], :hour => ['0'], :weekday => :absent, @@ -222,7 +222,7 @@ describe Puppet::Type.type(:cron).provider(:crontab) do :target => 'foobar' }, { - :name => :absent, + :name => 'unmanaged:$HOME/bin/monthly', :minute => ['15'], :hour => ['14'], :weekday => :absent, From f098f2e7881aa82009ce4ba0be8da74417211d85 Mon Sep 17 00:00:00 2001 From: Felix Frank Date: Fri, 8 Nov 2013 22:57:27 +0100 Subject: [PATCH 614/800] (#3220) save overhead in cron type unit test and add cleanup As it was, the unit spec for the cron type would register a mock provider before each example. Add the :all parameter to 'before' to make that happen only once. There was a new issue with the integration test for the #3220 fix. The reason was that the mock provider from the cron unit test would remain in the list of suitable providers. Purging relies on Type.instances, which in turn tries and invokes the instances method of all suitable providers. Prevent issues by removing the mock provider from the list. --- spec/unit/type/cron_spec.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/spec/unit/type/cron_spec.rb b/spec/unit/type/cron_spec.rb index 3d08ba203..53fff30e6 100755 --- a/spec/unit/type/cron_spec.rb +++ b/spec/unit/type/cron_spec.rb @@ -3,12 +3,16 @@ require 'spec_helper' describe Puppet::Type.type(:cron), :unless => Puppet.features.microsoft_windows? do - before do + before :all do @provider_class = described_class.provide(:simple) { mk_resource_methods } @provider_class.stubs(:suitable?).returns true described_class.stubs(:defaultprovider).returns @provider_class end + after :all do + described_class.unprovide(:simple) + end + it "should have :name be its namevar" do described_class.key_attributes.should == [:name] end From 977aacf7d49716d271311bd1b4d9b3b68e4fdfe8 Mon Sep 17 00:00:00 2001 From: Felix Frank Date: Tue, 4 Feb 2014 16:29:37 +0100 Subject: [PATCH 615/800] (#3220) uniquify implicit record names Adds an numeric suffix to the artifical name that gets generated for unnamed records in crontabs. It will suppress naming collisions for cronjobs that use the exact same command string. --- lib/puppet/provider/cron/crontab.rb | 6 +++++- spec/unit/provider/cron/parsed_spec.rb | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/puppet/provider/cron/crontab.rb b/lib/puppet/provider/cron/crontab.rb index 304a1ff04..a9fd94626 100644 --- a/lib/puppet/provider/cron/crontab.rb +++ b/lib/puppet/provider/cron/crontab.rb @@ -169,6 +169,8 @@ Puppet::Type.type(:cron).provide(:crontab, :parent => Puppet::Provider::ParsedFi false end + @@name_index = 0 + # Collapse name and env records. def self.prefetch_hook(records) name = nil @@ -197,7 +199,9 @@ Puppet::Type.type(:cron).provide(:crontab, :parent => Puppet::Provider::ParsedFi record[:name] = name name = nil else - record[:name] = "unmanaged:" + record[:command].gsub(/\s+/, "_") + cmd_string = record[:command].gsub(/\s+/, "_") + index = ( @@name_index += 1 ) + record[:name] = "unmanaged:#{cmd_string}-#{ index.to_s }" record[:unmanaged] = true end if envs.nil? or envs.empty? diff --git a/spec/unit/provider/cron/parsed_spec.rb b/spec/unit/provider/cron/parsed_spec.rb index 333558fdf..bad859c90 100644 --- a/spec/unit/provider/cron/parsed_spec.rb +++ b/spec/unit/provider/cron/parsed_spec.rb @@ -208,7 +208,7 @@ describe Puppet::Type.type(:cron).provider(:crontab) do h end.should == [ { - :name => 'unmanaged:$HOME/bin/daily.job_>>_$HOME/tmp/out_2>&1', + :name => 'unmanaged:$HOME/bin/daily.job_>>_$HOME/tmp/out_2>&1-1', :minute => ['5'], :hour => ['0'], :weekday => :absent, @@ -222,7 +222,7 @@ describe Puppet::Type.type(:cron).provider(:crontab) do :target => 'foobar' }, { - :name => 'unmanaged:$HOME/bin/monthly', + :name => 'unmanaged:$HOME/bin/monthly-2', :minute => ['15'], :hour => ['14'], :weekday => :absent, From 36ec72dea68f44aea405722d9dc3650d9b534158 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 6 Feb 2014 00:05:32 +0100 Subject: [PATCH 616/800] (PUP-1619) Add first impl of working Tuple Type This adds the Tuple Type which is parameterized with a sequence of types. The parameters also includes the ability to specify a range of occurrences of the last type in the sequence (default 1). This also makes a small refactor of type equality since Tuple and Array may describe exactly the same type of instance, but it is neither's responsibility to know this - hence, equality of types is now performed by the TypeCalculator. This commit also contains the model part of the PStructType implementation. --- lib/puppet/pops/evaluator/access_operator.rb | 17 ++ lib/puppet/pops/evaluator/evaluator_impl.rb | 10 +- lib/puppet/pops/types/type_calculator.rb | 194 ++++++++++++++++++- lib/puppet/pops/types/type_factory.rb | 31 ++- lib/puppet/pops/types/type_parser.rb | 29 ++- lib/puppet/pops/types/types.rb | 67 +++++++ spec/unit/pops/types/type_calculator_spec.rb | 93 ++++++++- spec/unit/pops/types/type_factory_spec.rb | 8 + 8 files changed, 430 insertions(+), 19 deletions(-) diff --git a/lib/puppet/pops/evaluator/access_operator.rb b/lib/puppet/pops/evaluator/access_operator.rb index 88d9dd1f4..bf32344b2 100644 --- a/lib/puppet/pops/evaluator/access_operator.rb +++ b/lib/puppet/pops/evaluator/access_operator.rb @@ -8,6 +8,7 @@ class Puppet::Pops::Evaluator::AccessOperator include Puppet::Pops::Evaluator::Runtime3Support Issues = Puppet::Pops::Issues + TYPEFACTORY = Puppet::Pops::Types::TypeFactory attr_reader :semantic @@ -166,6 +167,22 @@ class Puppet::Pops::Evaluator::AccessOperator Puppet::Pops::Types::TypeFactory.variant(*keys) end + def access_PTupleType(o, scope, keys) + keys.flatten! + if TYPEFACTORY.is_range_parameter?(keys[-2]) && TYPEFACTORY.is_range_parameter?(keys[-1]) + size_type = TYPEFACTORY.range(keys[-2], keys[-1]) + keys = keys[0, keys.size - 2] + elsif TYPEFACTORY.is_range_parameter?(keys[-2]) + size_type = TYPEFACTORY.range(keys[-1], :default) + keys = keys[0, keys.size - 1] + end + assert_keys(keys, o, 1, INFINITY, Puppet::Pops::Types::PAbstractType) + t = Puppet::Pops::Types::TypeFactory.tuple(*keys) + # set size type, or nil for default (exactly 1) + t.size_type = size_type + t + end + def access_PStringType(o, scope, keys) keys.flatten! case keys.size diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index 2771da543..711d756f2 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -420,18 +420,20 @@ class Puppet::Pops::Evaluator::EvaluatorImpl if left.is_a?(Puppet::Pops::Types::PAbstractType) case o.operator when :'==' - @@compare_operator.equals(left,right) + @@type_calculator.equals(left,right) + when :'!=' - ! @@compare_operator.equals(left,right) + !@@type_calculator.equals(left,right) + when :'<' # left can be assigned to right, but they are not equal - @@type_calculator.assignable?(right, left) && ! @@compare_operator.equals(left,right) + @@type_calculator.assignable?(right, left) && ! @@type_calculator.equals(left,right) when :'<=' # left can be assigned to right @@type_calculator.assignable?(right, left) when :'>' # right can be assigned to left, but they are not equal - @@type_calculator.assignable?(left,right) && ! @@compare_operator.equals(left,right) + @@type_calculator.assignable?(left,right) && ! @@type_calculator.equals(left,right) when :'>=' # right can be assigned to left @@type_calculator.assignable?(left, right) diff --git a/lib/puppet/pops/types/type_calculator.rb b/lib/puppet/pops/types/type_calculator.rb index bcf19a130..347d9cbb3 100644 --- a/lib/puppet/pops/types/type_calculator.rb +++ b/lib/puppet/pops/types/type_calculator.rb @@ -177,6 +177,10 @@ class Puppet::Pops::Types::TypeCalculator collection_default_size.to = nil # infinity @collection_default_size_t = collection_default_size +# range1 = Types::PIntegerType.new() +# range1.from = 1 +# range1.to = 1 +# @range_1 = range1 end # Convenience method to get a data type for comparisons @@ -243,6 +247,15 @@ class Puppet::Pops::Types::TypeCalculator @@enumerable_visitor.visit_this_0(self, t) end + # Answers if the two given types describe the same type + def equals(left, right) + return false unless left.is_a?(Types::PAbstractType) && right.is_a?(Types::PAbstractType) + # Types compare per class only - an extra test must be made if the are mutually assignable + # to find all types that represent the same type of instance + # + left == right || (assignable?(right, left) && assignable?(left, right)) + end + # Answers 'what is the Puppet Type corresponding to the given Ruby class' # @param c [Class] the class for which a puppet type is wanted # @api public @@ -357,6 +370,21 @@ class Puppet::Pops::Types::TypeCalculator assignable?(size_t, size_t2) end + def instance_of_PTupleType(t, o) + return false unless o.is_a?(Array) + # compute the tuple's min/max size, and check if that size matches + from, to = size_range(t.size_type) + size_t = Types::PIntegerType.new() + size_t.from = t2.types.size - 1 + from + size_t.to = t2.types.size - 1 + to + # compute the array's size as type + size_t2 = size_as_type(o) + return false unless assignable?(size_t, size_t2) + o.each_with_index do |element, index| + return false unless instance_of(t.types[index] || t.types[-1], element) + end + end + def instance_of_PHashType(t, o) return false unless o.is_a?(Hash) key_t = t.key_type @@ -812,6 +840,24 @@ class Puppet::Pops::Types::TypeCalculator trange[0] <= t2range[0] && trange[1] >= t2range[1] end + # Transform int range to a size constraint + # if range == nil the constraint is 1,1 + # if range.from == nil min size = 1 + # if range.to == nil max size == Infinity + # + def size_range(range) + return [1,1] if range.nil? + from = range.from + to = range.to + x = from.nil? ? 1 : from + y = to.nil? ? TheInfinity : to + if x < y + [x, y] + else + [y, x] + end + end + # @api private def from_to_ordered(from, to) x = (from.nil? || from == :default) ? -TheInfinity : from @@ -845,6 +891,85 @@ class Puppet::Pops::Types::TypeCalculator end end + def assignable_PTupleType(t, t2) + return true if t == t2 || t.types.empty? && (t2.is_a?(Types::PArrayType)) + t_regular = t.types[0..-2] + t_ranged = t.types[-1] + t_from, t_to = size_range(t.size_type) + t_required = t_regular.size + t_from + + if t2.is_a?(Types::PTupleType) + t2_regular = t2.types[0..-2] + t2_ranged = t2.types[-1] + t2_from, t2_to = size_range(t2.size_type) + t2_required = t2_regular.size + t2_from + + # tuples with fewer required entries can not be assigned + return false if t_required > t2_required + # tuples with more optionally available entries can not be assigned + return false if t_regular.size + t_to < t2_regular.size + t2_to + + t_required.times do |index| + t_entry = tuple_entry_at(t, t_from, t_to, index) + t2_entry = tuple_entry_at(t2, t2_from, t2_to, index) + return false if t2_entry.nil? || !assignable?(t_entry, t2_entry) + end + # Handle remainder in t2's required + (t2_required - t_required).times do |index| + t_entry = tuple_entry_at(t, t_from, t_to, t_required + index) + t2_entry = tuple_entry_at(t2, t2_from, t2_to, t_required + index) + return false if t2_entry.nil? || !assignable?(t_entry, t2_entry) + end + # Now only a trailing optional type remains - the last type must always be compatible + # irrespective of optionality and count + # + return assignable?(t_ranged, t2_ranged) + + elsif t2.is_a?(Types::PArrayType) + t2_entry = t2.element_type + + # Array of anything can not be assigned (unless tuple is tuple of anything) - this case + # was handled at the top of this method. + # + return false if t2_entry.nil? + + # array type may be size constrained + size_t = t2.size_type || @collection_default_size_t + sz = size_t.range + # Array with fewer min entries can not be assigned + return false if t_required > sz.min + # Array with more optionally available entries can not be assigned + return false if t_regular.size + t_to < sz.max + # each tuple type must be assignable to the element type + t_required.times do |index| + t_entry = tuple_entry_at(t, t_from, t_to, index) + return false unless assignable?(t_entry, t2_entry) + end + # ... and so must the last, possibly optional (ranged) type + return assignable?(t_ranged, t2_entry) + end + end + + # Produces the tuple entry at the given index given a tuple type, its from/to constraints on the last + # type, and an index. + # Produces nil if the index is out of bounds + # from must be less than to, and from may not be less than 0 + # + def tuple_entry_at(tuple_t, from, to, index) + regular = (tuple_t.types.size - 1) + if index < regular + tuple_t.types[index] + elsif index < regular + to + # in the varargs part + tuple_t.types[-1] + else + nil + end + end + + def assignable_PStructType(t, t2) + end + def assignable_POptionalType(t, t2) return true if t2.is_a(Types::PNilType) if t2.is_a?(Types::POptionalType) @@ -941,10 +1066,21 @@ class Puppet::Pops::Types::TypeCalculator # @api private def assignable_PCollectionType(t, t2) - return false unless t2.is_a?(Types::PCollectionType) size_t = t.size_type || @collection_default_size_t - size_t2 = t2.size_type || @collection_default_size_t - assignable?(size_t, size_t2) + case t2 + when Types::PCollectionType + size_t2 = t2.size_type || @collection_default_size_t + assignable?(size_t, size_t2) + when Types::PTupleType + # compute the tuple's min/max size, and check if that size matches + from, to = size_range(t2.size_type) + t2s = Types::PIntegerType.new() + t2s.from = t2.types.size - 1 + from + t2s.to = t2.types.size - 1 + to + assignable?(size_t, t2s) + else + false + end end # @api private @@ -955,12 +1091,45 @@ class Puppet::Pops::Types::TypeCalculator assignable?(t.type, t2.type) end - # Array is assignable if t2 is an Array and t2's element type is assignable + # Array is assignable if t2 is an Array and t2's element type is assignable, or if t2 is a Tuple + # where # @api private def assignable_PArrayType(t, t2) - return false unless t2.is_a?(Types::PArrayType) - return false unless assignable?(t.element_type, t2.element_type) - assignable_PCollectionType(t, t2) + if t2.is_a?(Types::PArrayType) + return false unless assignable?(t.element_type, t2.element_type) + assignable_PCollectionType(t, t2) + + elsif t2.is_a?(Types::PTupleType) + return false unless t2.types.all? {|t2_element| assignable?(t.element_type, t2_element) } + t2_regular = t2.types[0..-2] + t2_ranged = t2.types[-1] + t2_from, t2_to = size_range(t2.size_type) + t2_required = t2_regular.size + t2_from + + t_entry = t.element_type + + # Array of anything can not be assigned (unless tuple is tuple of anything) - this case + # was handled at the top of this method. + # + return false if t_entry.nil? + + # array type may be size constrained + size_t = t.size_type || @collection_default_size_t + sz = size_t.range + # Tuple with fewer min entries can not be assigned + return false if t2_required < sz.min + # Tuple with more optionally available entries can not be assigned + return false if t2_regular.size + t2_to > sz.max + # each tuple type must be assignable to the element type + t2_required.times do |index| + t2_entry = tuple_entry_at(t2, t2_from, t2_to, index) + return false unless assignable?(t_entry, t2_entry) + end + # ... and so must the last, possibly optional (ranged) type + return assignable?(t_entry, t2_ranged) + else + false + end end # Hash is assignable if t2 is a Hash and t2's key and element types are assignable @@ -1109,6 +1278,17 @@ class Puppet::Pops::Types::TypeCalculator "Variant[" << t.types.map {|t2| string(t2) }.join(', ') << ']' end + # @api private + def string_PTupleType(t) + return "Tuple" if t.types.empty? + s = "Tuple[" << t.types.map {|t2| string(t2) }.join(', ') + if !t.types().empty? && !t.size_type.nil? + s << ", " << string(size_type) + end + s << "]" + s + end + # @api private def string_PPatternType(t) return "Pattern" if t.patterns.empty? diff --git a/lib/puppet/pops/types/type_factory.rb b/lib/puppet/pops/types/type_factory.rb index 6208bda08..2c94c656d 100644 --- a/lib/puppet/pops/types/type_factory.rb +++ b/lib/puppet/pops/types/type_factory.rb @@ -18,8 +18,8 @@ module Puppet::Pops::Types::TypeFactory # def self.range(from, to) t = Types::PIntegerType.new() - t.from = from unless from == :default - t.to = to unless to == :default + t.from = from unless (from == :default || from == 'default') + t.to = to unless (to == :default || to == 'default') t end @@ -88,6 +88,27 @@ module Puppet::Pops::Types::TypeFactory t end + # Produces the Struct type, either a non parameterized instance representing all structs (i.e. all hashes) + # or a hash with a given set of keys of String type (names), bound to a value of a given type. Type may be + # a Ruby Class, a Puppet Type, or an instance from which the type is inferred. + # + def self.struct(name_type_hash = {}) + t = Types::PStructType.new + name_type_hash.map do |name, type| + elem = Types::PStructElement.new + elem.name = name + elem.type = type_of(type) + elem + end.each {|elem| t.addElements(elem) } + t + end + + def self.tuple(*types) + t = Types::PTupleType.new + types.each {|elem| t.addTypes(type_of(elem)) } + t + end + # Produces the Boolean type # @api public # @@ -302,4 +323,10 @@ module Puppet::Pops::Types::TypeFactory collection_t.size_type = range(from, to) collection_t end + + # Returns true if the given type t is of valid range parameter type (integer or literal default). + def self.is_range_parameter?(t) + t.is_a?(Integer) || t == 'default' || t == :default + end + end diff --git a/lib/puppet/pops/types/type_parser.rb b/lib/puppet/pops/types/type_parser.rb index bb9d0267f..fe6206bb1 100644 --- a/lib/puppet/pops/types/type_parser.rb +++ b/lib/puppet/pops/types/type_parser.rb @@ -164,6 +164,8 @@ class Puppet::Pops::Types::TypeParser when "type" TYPES.type_type() + when "tuple" + TYPES.tuple() else TYPES.resource(name_ast.value) end @@ -289,6 +291,30 @@ class Puppet::Pops::Types::TypeParser raise_invalid_parameters_error("Variant", "1 or more", parameters.size) unless parameters.size > 1 TYPES.variant(*parameters) + when "tuple" + # 1..m parameters being types (last two optionally integer or literal default + raise_invalid_parameters_error("Tuple", "1 or more", parameters.size) unless parameters.size > 1 + length = parameters.size + if is_range_parameter?(parameters[-2]) + # min, max specification + min = parameters[-2] + min = (min == :default || min == 'default') ? 0 : min + assert_range_parameter(parameters[-1]) + max = parameters[-1] + max = max == :default ? nil : max + parameters = parameters[0, length-2] + elsif is_range_parameter?(parameters[-1]) + min = parameters[-1] + min = (min == :default || min == 'default') ? 0 : min + max = nil + parameters = parameters[0, length-1] + end + t = TYPES.tuple(*parameters) + if min || max + TYPES.constrain_size(t, min, max) + end + t + when "integer" if parameters.size == 1 case parameters[0] @@ -376,9 +402,10 @@ class Puppet::Pops::Types::TypeParser end def assert_range_parameter(t) - raise_invalid_type_speification_error unless t.is_a?(Integer) || t == 'default' || t == :default + raise_invalid_type_speification_error unless TYPES.is_range_parameter?(t) end + def raise_invalid_type_specification_error raise Puppet::ParseError, "The expression <#{@string}> is not a valid type specification." diff --git a/lib/puppet/pops/types/types.rb b/lib/puppet/pops/types/types.rb index 4d3ad1cb1..fc243adfc 100644 --- a/lib/puppet/pops/types/types.rb +++ b/lib/puppet/pops/types/types.rb @@ -138,6 +138,16 @@ module Puppet::Pops::Types 1+(to-from).abs end + def range + f = from || -(1.0 / 0.0) + t = to || (1.0 / 0.0) + if f < t + (f..t) + else + (t..f) + end + end + # Returns Enumerator if no block is given # Returns self if size is infinity (does not yield) def each @@ -252,6 +262,63 @@ module Puppet::Pops::Types end end + class PStructElement < Puppet::Pops::Model::PopsObject + has_attr 'name', String, :lowerBound => 1 + contains_one_uni 'type', PAbstractType + + module ClassModule + def hash + [self.class, type, name].hash + end + + def ==(o) + self.class == o.class && type == o.type && name == o.name + end + end + end + + # @api public + class PStructType < PObjectType + contains_many_uni 'elements', PStructElement, :lowerBound => 1 + has_attr 'hashed_elements', Object, :derived => true + + module ClassModule + def derived_hashed_elements + @_hashed ||= elements.reduce({}) {|memo, e| memo[e.name] = e.type; memo } + @_hashed + end + + def clear_hashed_elements + @_hashed = nil + end + + def hash + [self.class, Set.new(elements)].hash + end + + def ==(o) + self.class == o.class && hashed_elements == o.hashed_elements + end + end + end + + # @api public + class PTupleType < PObjectType + contains_many_uni 'types', PAbstractType, :lowerBound => 1 + # If set, describes repetition of the last type in types + contains_one_uni 'size_type', PIntegerType, :lowerBound => 0 + + module ClassModule + def hash + [self.class, size_type, Set.new(elements)].hash + end + + def ==(o) + self.class == o.class && types == o.types && size_type == o.size_type + end + end + end + # @api public class PArrayType < PCollectionType module ClassModule diff --git a/spec/unit/pops/types/type_calculator_spec.rb b/spec/unit/pops/types/type_calculator_spec.rb index a9393ed55..0071b1fff 100644 --- a/spec/unit/pops/types/type_calculator_spec.rb +++ b/spec/unit/pops/types/type_calculator_spec.rb @@ -55,6 +55,10 @@ describe 'The type calculator' do Puppet::Pops::Types::TypeFactory.collection() end + def tuple_t(*types) + Puppet::Pops::Types::TypeFactory.tuple(*types) + end + def types Puppet::Pops::Types end @@ -81,6 +85,8 @@ describe 'The type calculator' do Puppet::Pops::Types::PPatternType, Puppet::Pops::Types::PEnumType, Puppet::Pops::Types::PVariantType, +# Puppet::Pops::Types::PStructType, + Puppet::Pops::Types::PTupleType, ] end @@ -123,6 +129,8 @@ describe 'The type calculator' do Puppet::Pops::Types::PCollectionType, Puppet::Pops::Types::PHashType, Puppet::Pops::Types::PArrayType, +# Puppet::Pops::Types::PStructType, + Puppet::Pops::Types::PTupleType, ] end @@ -521,17 +529,17 @@ describe 'The type calculator' do end context "for Array, such that" do - it "Array is not assignable to any other Collection type" do + it "Array is not assignable to non Array based Collection type" do t = Puppet::Pops::Types::PArrayType.new() tested_types = collection_types - [ Puppet::Pops::Types::PCollectionType, - Puppet::Pops::Types::PArrayType] + Puppet::Pops::Types::PArrayType, + Puppet::Pops::Types::PTupleType] tested_types.each {|t2| t.should_not be_assignable_to(t2.new) } end it 'Array is not assignable to any disjunct type' do tested_types = all_types - [ - Puppet::Pops::Types::PObjectType, Puppet::Pops::Types::PObjectType, Puppet::Pops::Types::PDataType] - collection_types t = Puppet::Pops::Types::PArrayType.new() @@ -550,7 +558,6 @@ describe 'The type calculator' do it 'Hash is not assignable to any disjunct type' do tested_types = all_types - [ - Puppet::Pops::Types::PObjectType, Puppet::Pops::Types::PObjectType, Puppet::Pops::Types::PDataType] - collection_types t = Puppet::Pops::Types::PHashType.new() @@ -558,6 +565,25 @@ describe 'The type calculator' do end end + context "for Tuple, such that" do + it "Tuple is not assignable to any other non Array based Collection type" do + t = Puppet::Pops::Types::PTupleType.new() + tested_types = collection_types - [ + Puppet::Pops::Types::PCollectionType, + Puppet::Pops::Types::PTupleType, + Puppet::Pops::Types::PArrayType] + tested_types.each {|t2| t.should_not be_assignable_to(t2.new) } + end + + it 'Tuple is not assignable to any disjunct type' do + tested_types = all_types - [ + Puppet::Pops::Types::PObjectType, + Puppet::Pops::Types::PDataType] - collection_types + t = Puppet::Pops::Types::PTupleType.new() + tested_types.each {|t2| t.should_not be_assignable_to(t2.new) } + end + end + it 'should recognize mapped ruby types' do { Integer => Puppet::Pops::Types::PIntegerType.new, @@ -666,11 +692,68 @@ describe 'The type calculator' do it 'should accept enum matching patterns as instanceof' do enum = enum_t('XS', 'S', 'M', 'L' 'XL', 'XXL') pattern = pattern_t('S', 'M', 'L') - calculator.assignable?(pattern, enum) == true + calculator.assignable?(pattern, enum).should == true end end + context 'when dealing with tuples' do + it 'should accept matching tuples' do + tuple1 = tuple_t(1,2) + tuple2 = tuple_t(Integer,Integer) + calculator.assignable?(tuple1, tuple2).should == true + calculator.assignable?(tuple2, tuple1).should == true + end + + it 'should accept matching tuples where one is more general than the other' do + tuple1 = tuple_t(1,2) + tuple2 = tuple_t(Numeric,Numeric) + calculator.assignable?(tuple1, tuple2).should == false + calculator.assignable?(tuple2, tuple1).should == true + end + + it 'should accept ranged tuples' do + tuple1 = tuple_t(1) + factory.constrain_size(tuple1, 5, 5) + tuple2 = tuple_t(Integer,Integer, Integer, Integer, Integer) + calculator.assignable?(tuple1, tuple2).should == true + calculator.assignable?(tuple2, tuple1).should == true + end + + it 'should reject ranged tuples when ranges does not match' do + tuple1 = tuple_t(1) + factory.constrain_size(tuple1, 4, 5) + tuple2 = tuple_t(Integer,Integer, Integer, Integer, Integer) + calculator.assignable?(tuple1, tuple2).should == true + calculator.assignable?(tuple2, tuple1).should == false + end + + it 'should reject ranged tuples when ranges does not match (using infinite upper bound)' do + tuple1 = tuple_t(1) + factory.constrain_size(tuple1, 4, :default) + tuple2 = tuple_t(Integer,Integer, Integer, Integer, Integer) + calculator.assignable?(tuple1, tuple2).should == true + calculator.assignable?(tuple2, tuple1).should == false + end + + it 'should accept matching tuples with optional entries' do + tuple1 = tuple_t(1,2) + factory.constrain_size(tuple1, 0, :default) + tuple2 = tuple_t(Numeric,Numeric) + factory.constrain_size(tuple2, 0, :default) + calculator.assignable?(tuple1, tuple2).should == false + calculator.assignable?(tuple2, tuple1).should == true + end + + it 'should accept matching array' do + tuple1 = tuple_t(1,2) + array = array_t(Integer) + factory.constrain_size(array, 2, 2) + calculator.assignable?(tuple1, array).should == true + calculator.assignable?(array, tuple1).should == true + end + end + it 'should recognize ruby type inheritance' do class Foo end diff --git a/spec/unit/pops/types/type_factory_spec.rb b/spec/unit/pops/types/type_factory_spec.rb index 16db10880..e0f66b4e2 100644 --- a/spec/unit/pops/types/type_factory_spec.rb +++ b/spec/unit/pops/types/type_factory_spec.rb @@ -55,6 +55,14 @@ describe 'The type factory' do Puppet::Pops::Types::TypeFactory.catalog_entry().class().should == Puppet::Pops::Types::PCatalogEntryType end + it 'struct() returns PStructType' do + Puppet::Pops::Types::TypeFactory.struct().class().should == Puppet::Pops::Types::PStructType + end + + it 'tuple() returns PTupleType' do + Puppet::Pops::Types::TypeFactory.tuple().class().should == Puppet::Pops::Types::PTupleType + end + it 'undef() returns PNilType' do Puppet::Pops::Types::TypeFactory.undef().class().should == Puppet::Pops::Types::PNilType end From 9c3068a393aa11f95e281a70c67161b1b1c9cf87 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 9 Feb 2014 18:12:54 +0100 Subject: [PATCH 617/800] (PUP-1619) Fix problem with parsing Tuple There was a problem parsing the occurence constraint for the last type argument (if there was one numeric). This also adds a test. --- lib/puppet/pops/evaluator/access_operator.rb | 2 +- lib/puppet/pops/types/type_calculator.rb | 5 +++-- spec/unit/pops/evaluator/access_ops_spec.rb | 23 ++++++++++++++++++++ 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/lib/puppet/pops/evaluator/access_operator.rb b/lib/puppet/pops/evaluator/access_operator.rb index bf32344b2..6c3615ec0 100644 --- a/lib/puppet/pops/evaluator/access_operator.rb +++ b/lib/puppet/pops/evaluator/access_operator.rb @@ -172,7 +172,7 @@ class Puppet::Pops::Evaluator::AccessOperator if TYPEFACTORY.is_range_parameter?(keys[-2]) && TYPEFACTORY.is_range_parameter?(keys[-1]) size_type = TYPEFACTORY.range(keys[-2], keys[-1]) keys = keys[0, keys.size - 2] - elsif TYPEFACTORY.is_range_parameter?(keys[-2]) + elsif TYPEFACTORY.is_range_parameter?(keys[-1]) size_type = TYPEFACTORY.range(keys[-1], :default) keys = keys[0, keys.size - 1] end diff --git a/lib/puppet/pops/types/type_calculator.rb b/lib/puppet/pops/types/type_calculator.rb index 347d9cbb3..5bff7843d 100644 --- a/lib/puppet/pops/types/type_calculator.rb +++ b/lib/puppet/pops/types/type_calculator.rb @@ -1280,10 +1280,11 @@ class Puppet::Pops::Types::TypeCalculator # @api private def string_PTupleType(t) + range = range_array_part(t.size_type) return "Tuple" if t.types.empty? s = "Tuple[" << t.types.map {|t2| string(t2) }.join(', ') - if !t.types().empty? && !t.size_type.nil? - s << ", " << string(size_type) + unless range.empty? + s << ", " << range.join(', ') end s << "]" s diff --git a/spec/unit/pops/evaluator/access_ops_spec.rb b/spec/unit/pops/evaluator/access_ops_spec.rb index aaecbab4d..c46559f74 100644 --- a/spec/unit/pops/evaluator/access_ops_spec.rb +++ b/spec/unit/pops/evaluator/access_ops_spec.rb @@ -211,6 +211,29 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do expect { evaluate(expr)}.to raise_error(/Array-Type\[\] arguments must be types/) end + # Tuple Type + # + it 'produces a Tuple[String] from the expression Tuple[String]' do + expr = fqr('Tuple')[fqr('String')] + expect(evaluate(expr)).to be_the_type(types.tuple(String)) + + # arguments are flattened + expr = fqr('Tuple')[[fqr('String')]] + expect(evaluate(expr)).to be_the_type(types.tuple(String)) + end + + it 'produces a varargs Tuple when the last two arguments specify size constraint' do + expr = fqr('Tuple')[fqr('String'), 1] + expected_t = types.tuple(String) + types.constrain_size(expected_t, 1, :default) + expect(evaluate(expr)).to be_the_type(expected_t) + + expr = fqr('Tuple')[fqr('String'), 1, 2] + expected_t = types.tuple(String) + types.constrain_size(expected_t, 1, 2) + expect(evaluate(expr)).to be_the_type(expected_t) + end + # Pattern Type # it 'creates a PPatternType instance when applied to a Pattern' do From 690f0295430b6146a4f6a0648416905b93e4079b Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 9 Feb 2014 18:15:02 +0100 Subject: [PATCH 618/800] (maint) Add Array type size constraint test A test for Array[T, from, to] was missing. --- spec/unit/pops/evaluator/access_ops_spec.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/spec/unit/pops/evaluator/access_ops_spec.rb b/spec/unit/pops/evaluator/access_ops_spec.rb index c46559f74..900f14a91 100644 --- a/spec/unit/pops/evaluator/access_ops_spec.rb +++ b/spec/unit/pops/evaluator/access_ops_spec.rb @@ -206,6 +206,18 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do expect(evaluate(expr)).to be_the_type(types.array_of(types.string)) end + it 'produces a size constrained Array when the last two arguments specify this' do + expr = fqr('Array')[fqr('String'), 1] + expected_t = types.array_of(String) + types.constrain_size(expected_t, 1, :default) + expect(evaluate(expr)).to be_the_type(expected_t) + + expr = fqr('Array')[fqr('String'), 1, 2] + expected_t = types.array_of(String) + types.constrain_size(expected_t, 1, 2) + expect(evaluate(expr)).to be_the_type(expected_t) + end + it "gives an error if parameter is not a type" do expr = fqr('Array')['String'] expect { evaluate(expr)}.to raise_error(/Array-Type\[\] arguments must be types/) From 731d2ba1d621becc9d02a56a5af341ca7581f272 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 9 Feb 2014 18:32:50 +0100 Subject: [PATCH 619/800] (PUP-1619) Add more tests of Tuple Type --- spec/unit/pops/evaluator/access_ops_spec.rb | 7 +++++- spec/unit/pops/types/type_calculator_spec.rb | 24 ++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/spec/unit/pops/evaluator/access_ops_spec.rb b/spec/unit/pops/evaluator/access_ops_spec.rb index 900f14a91..6a0c8db33 100644 --- a/spec/unit/pops/evaluator/access_ops_spec.rb +++ b/spec/unit/pops/evaluator/access_ops_spec.rb @@ -218,7 +218,7 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do expect(evaluate(expr)).to be_the_type(expected_t) end - it "gives an error if parameter is not a type" do + it "Array parameterization gives an error if parameter is not a type" do expr = fqr('Array')['String'] expect { evaluate(expr)}.to raise_error(/Array-Type\[\] arguments must be types/) end @@ -234,6 +234,11 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do expect(evaluate(expr)).to be_the_type(types.tuple(String)) end + it "Tuple parameterization gives an error if parameter is not a type" do + expr = fqr('Tuple')['String'] + expect { evaluate(expr)}.to raise_error(/Tuple-Type, Cannot use String where Abstract-Type is expected/) + end + it 'produces a varargs Tuple when the last two arguments specify size constraint' do expr = fqr('Tuple')[fqr('String'), 1] expected_t = types.tuple(String) diff --git a/spec/unit/pops/types/type_calculator_spec.rb b/spec/unit/pops/types/type_calculator_spec.rb index 0071b1fff..4d07d41d4 100644 --- a/spec/unit/pops/types/type_calculator_spec.rb +++ b/spec/unit/pops/types/type_calculator_spec.rb @@ -1104,6 +1104,28 @@ describe 'The type calculator' do calculator.string(factory.constrain_size(arr.copy, 2, :default)).should == 'Array[String, 2, default]' end + it 'should yield \'Tuple[Integer]\' for PTupleType[PIntegerType]' do + t = Puppet::Pops::Types::PTupleType.new() + t.addTypes(Puppet::Pops::Types::PIntegerType.new()) + calculator.string(t).should == 'Tuple[Integer]' + end + + it 'should yield \'Tuple[T, T,..]\' for PTupleType[T, T, ...]' do + t = Puppet::Pops::Types::PTupleType.new() + t.addTypes(Puppet::Pops::Types::PIntegerType.new()) + t.addTypes(Puppet::Pops::Types::PIntegerType.new()) + t.addTypes(Puppet::Pops::Types::PStringType.new()) + calculator.string(t).should == 'Tuple[Integer, Integer, String]' + end + + it 'should yield \'Tuple\' and from/to for PTupleType' do + tuple_t = tuple_t(string_t) + calculator.string(factory.constrain_size(tuple_t.copy, 1,1)).should == 'Tuple[String, 1, 1]' + calculator.string(factory.constrain_size(tuple_t.copy, 1,2)).should == 'Tuple[String, 1, 2]' + calculator.string(factory.constrain_size(tuple_t.copy, :default, 2)).should == 'Tuple[String, default, 2]' + calculator.string(factory.constrain_size(tuple_t.copy, 2, :default)).should == 'Tuple[String, 2, default]' + end + it 'should yield \'Hash[String, Integer]\' for PHashType[PStringType, PIntegerType]' do t = Puppet::Pops::Types::PHashType.new() t.key_type = Puppet::Pops::Types::PStringType.new() @@ -1195,6 +1217,7 @@ describe 'The type calculator' do calculator.infer(Puppet::Pops::Types::PEnumType.new() ).is_a?(ptype).should() == true calculator.infer(Puppet::Pops::Types::PPatternType.new() ).is_a?(ptype).should() == true calculator.infer(Puppet::Pops::Types::PVariantType.new() ).is_a?(ptype).should() == true + calculator.infer(Puppet::Pops::Types::PTupleType.new() ).is_a?(ptype).should() == true end it 'should infer PType as the type of all other types' do @@ -1217,6 +1240,7 @@ describe 'The type calculator' do calculator.string(calculator.infer(Puppet::Pops::Types::PEnumType.new() )).should == "Type[Enum]" calculator.string(calculator.infer(Puppet::Pops::Types::PVariantType.new() )).should == "Type[Variant]" calculator.string(calculator.infer(Puppet::Pops::Types::PPatternType.new() )).should == "Type[Pattern]" + calculator.string(calculator.infer(Puppet::Pops::Types::PTupleType.new() )).should == "Type[Tuple]" end it "computes the common type of PType's type parameter" do From c527b94b130a5c9091b51c063694338387414cd2 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 10 Feb 2014 00:59:07 +0100 Subject: [PATCH 620/800] (PUP-1619) Make Tuple a subtype of Data when its elements are Data --- lib/puppet/pops/types/type_calculator.rb | 13 +++++++++---- lib/puppet/pops/types/types.rb | 2 +- spec/unit/pops/types/type_calculator_spec.rb | 4 +++- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/puppet/pops/types/type_calculator.rb b/lib/puppet/pops/types/type_calculator.rb index 5bff7843d..70584d5bc 100644 --- a/lib/puppet/pops/types/type_calculator.rb +++ b/lib/puppet/pops/types/type_calculator.rb @@ -164,12 +164,21 @@ class Puppet::Pops::Types::TypeCalculator @numeric_t = Types::PNumericType.new() @t = Types::PObjectType.new() + # Data accepts a Tuple that has 0-infinity Data compatible entries (e.g. a Tuple equivalent to Array). + data_tuple = Types::PTupleType.new() + data_tuple.addTypes(Types::PDataType.new()) + data_tuple.size_type = Types::PIntegerType.new() + data_tuple.size_type.from = 0 + data_tuple.size_type.to = nil # infinity + @data_tuple_t = data_tuple + # Variant type compatible with Data data_variant = Types::PVariantType.new() data_variant.addTypes(@data_hash.copy) data_variant.addTypes(@data_array.copy) data_variant.addTypes(Types::PScalarType.new) data_variant.addTypes(Types::PNilType.new) + data_variant.addTypes(@data_tuple_t.copy) @data_variant_t = data_variant collection_default_size = Types::PIntegerType.new() @@ -177,10 +186,6 @@ class Puppet::Pops::Types::TypeCalculator collection_default_size.to = nil # infinity @collection_default_size_t = collection_default_size -# range1 = Types::PIntegerType.new() -# range1.from = 1 -# range1.to = 1 -# @range_1 = range1 end # Convenience method to get a data type for comparisons diff --git a/lib/puppet/pops/types/types.rb b/lib/puppet/pops/types/types.rb index fc243adfc..2f93b00f4 100644 --- a/lib/puppet/pops/types/types.rb +++ b/lib/puppet/pops/types/types.rb @@ -310,7 +310,7 @@ module Puppet::Pops::Types module ClassModule def hash - [self.class, size_type, Set.new(elements)].hash + [self.class, size_type, Set.new(types)].hash end def ==(o) diff --git a/spec/unit/pops/types/type_calculator_spec.rb b/spec/unit/pops/types/type_calculator_spec.rb index 4d07d41d4..430c2a9a4 100644 --- a/spec/unit/pops/types/type_calculator_spec.rb +++ b/spec/unit/pops/types/type_calculator_spec.rb @@ -140,6 +140,8 @@ describe 'The type calculator' do result << array_t(types::PDataType.new) result << types::TypeFactory.hash_of_data result << Puppet::Pops::Types::PNilType + result << (tmp = tuple_t(types::PDataType.new)) + tmp.size_type = int_range(0, nil) result end @@ -929,7 +931,7 @@ describe 'The type calculator' do calculator.instance?(data_t, :undef).should == true end - it 'other symbols should not br considered instance of Data' do + it 'other symbols should not be considered instance of Data' do calculator.instance?(data_t, :love).should == false end From b9840e5095fee3d43718a58069570e11c0bffb88 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 10 Feb 2014 03:14:40 +0100 Subject: [PATCH 621/800] (PUP-1619) Add support for a Struct Type The Struct Type fully qualifies a hash. Keys must be non empty Strings. The type of the value is specified per key. --- lib/puppet/pops/evaluator/access_operator.rb | 5 ++ lib/puppet/pops/types/type_calculator.rb | 77 ++++++++++++++++- lib/puppet/pops/types/type_factory.rb | 3 + lib/puppet/pops/types/type_parser.rb | 3 + lib/puppet/pops/types/types.rb | 2 +- spec/unit/pops/types/type_calculator_spec.rb | 88 ++++++++++++++++---- 6 files changed, 158 insertions(+), 20 deletions(-) diff --git a/lib/puppet/pops/evaluator/access_operator.rb b/lib/puppet/pops/evaluator/access_operator.rb index 6c3615ec0..cfc586706 100644 --- a/lib/puppet/pops/evaluator/access_operator.rb +++ b/lib/puppet/pops/evaluator/access_operator.rb @@ -183,6 +183,11 @@ class Puppet::Pops::Evaluator::AccessOperator t end + def access_PStructType(o, scope, keys) + assert_keys(keys, o, 1, 1, Hash) + TYPEFACTORY.struct(keys[0]) + end + def access_PStringType(o, scope, keys) keys.flatten! case keys.size diff --git a/lib/puppet/pops/types/type_calculator.rb b/lib/puppet/pops/types/type_calculator.rb index 70584d5bc..d09963acd 100644 --- a/lib/puppet/pops/types/type_calculator.rb +++ b/lib/puppet/pops/types/type_calculator.rb @@ -186,6 +186,11 @@ class Puppet::Pops::Types::TypeCalculator collection_default_size.to = nil # infinity @collection_default_size_t = collection_default_size + non_empty_string = Types::PStringType.new + non_empty_string.size_type = Types::PIntegerType.new() + non_empty_string.size_type.from = 1 + non_empty_string.size_type.to = nil # infinity + @non_empty_string_t = non_empty_string end # Convenience method to get a data type for comparisons @@ -390,6 +395,13 @@ class Puppet::Pops::Types::TypeCalculator end end + def instance_of_PStructType(t, o) + return false unless o.is_a?(Hash) + h = t.hashed_elements + # all keys must be present and have a value (even if nil/undef) + return false unless o.size == h.size && h.all? { |k,v| o.has_key?(k) && instance_of(v, o[k]) } + end + def instance_of_PHashType(t, o) return false unless o.is_a?(Hash) key_t = t.key_type @@ -960,6 +972,8 @@ class Puppet::Pops::Types::TypeCalculator # Produces nil if the index is out of bounds # from must be less than to, and from may not be less than 0 # + # @api private + # def tuple_entry_at(tuple_t, from, to, index) regular = (tuple_t.types.size - 1) if index < regular @@ -972,9 +986,31 @@ class Puppet::Pops::Types::TypeCalculator end end + # @api private + # def assignable_PStructType(t, t2) + return true if t == t2 || t.elements.empty? && (t2.is_a?(Types::PHashType)) + h = t.hashed_elements + if t2.is_a?(Types::PStructType) + h2 = t2.hashed_elements + h.size == h2.size && h.all? {|k, v| assignable?(v, h2[k]) } + elsif t2.is_a?(Types::PHashType) + size_t2 = t2.size_type || @collection_default_size_t + size_t = Types::PIntegerType.new + size_t.from = size_t.to = h.size + # compatible size + # hash key type must be string of min 1 size + # hash value t must be assignable to each key + element_type = t2.element_type + assignable?(size_t, size_t2) && + assignable?(@non_empty_string_t, t2.key_type) && + h.all? {|k,v| assignable?(v, element_type) } + else + false + end end + # @api private def assignable_POptionalType(t, t2) return true if t2.is_a(Types::PNilType) if t2.is_a?(Types::POptionalType) @@ -984,6 +1020,7 @@ class Puppet::Pops::Types::TypeCalculator end end + # @api private def assignable_PEnumType(t, t2) return true if t == t2 || (t.values.empty? && (t2.is_a?(Types::PStringType) || t2.is_a?(Types::PEnumType))) if t2.is_a?(Types::PStringType) @@ -1083,6 +1120,12 @@ class Puppet::Pops::Types::TypeCalculator t2s.from = t2.types.size - 1 + from t2s.to = t2.types.size - 1 + to assignable?(size_t, t2s) + when Types::PStructType + from = to = t2.elements.size + t2s = Types::PIntegerType.new() + t2s.from = from + t2s.to = to + assignable?(size_t, t2s) else false end @@ -1140,9 +1183,24 @@ class Puppet::Pops::Types::TypeCalculator # Hash is assignable if t2 is a Hash and t2's key and element types are assignable # @api private def assignable_PHashType(t, t2) - return false unless t2.is_a?(Types::PHashType) - return false unless assignable?(t.key_type, t2.key_type) && assignable?(t.element_type, t2.element_type) - assignable_PCollectionType(t, t2) + case t2 + when Types::PHashType + return false unless assignable?(t.key_type, t2.key_type) && assignable?(t.element_type, t2.element_type) + assignable_PCollectionType(t, t2) + when Types::PStructType + # hash must accept String as key type + # hash must accept all value types + # hash must accept the size of the struct + size_t = t.size_type || @collection_default_size_t + sz = size_t.range + struct_size = t2.elements.size + element_type = t.element_type + ( struct_size >= sz.min && struct_size <= sz.max && + assignable?(t.key_type, @non_emptry_string_t) && + t2.hashed_elements.all? {|k,v| assignable?(element_type, v) }) + else + false + end end # @api private @@ -1234,11 +1292,14 @@ class Puppet::Pops::Types::TypeCalculator end end + # Produces a string from an Integer range type that is used inside other type strings + # @api private def range_array_part(t) return [] if t.nil? || (t.from.nil? && t.to.nil?) [t.from.nil? ? 'default' : t.from , t.to.nil? ? 'default' : t.to ] end + # @api private def string_PFloatType(t) range = range_array_part(t) unless range.empty? @@ -1295,6 +1356,16 @@ class Puppet::Pops::Types::TypeCalculator s end + # @api private + def string_PStructType(t) + return "Struct" if t.elements.empty? + "Struct[{" << t.elements.map {|element| string(element) }.join(', ') << "}]" + end + + def string_PStructElement(t) + "'#{t.name}'=>#{string(t.type)}" + end + # @api private def string_PPatternType(t) return "Pattern" if t.patterns.empty? diff --git a/lib/puppet/pops/types/type_factory.rb b/lib/puppet/pops/types/type_factory.rb index 2c94c656d..9f5860206 100644 --- a/lib/puppet/pops/types/type_factory.rb +++ b/lib/puppet/pops/types/type_factory.rb @@ -96,6 +96,9 @@ module Puppet::Pops::Types::TypeFactory t = Types::PStructType.new name_type_hash.map do |name, type| elem = Types::PStructElement.new + if name.is_a?(String) && name.empty? + raise ArgumentError, "An empty String can not be used where a String[1, default] is expected" + end elem.name = name elem.type = type_of(type) elem diff --git a/lib/puppet/pops/types/type_parser.rb b/lib/puppet/pops/types/type_parser.rb index fe6206bb1..cea59689a 100644 --- a/lib/puppet/pops/types/type_parser.rb +++ b/lib/puppet/pops/types/type_parser.rb @@ -166,6 +166,9 @@ class Puppet::Pops::Types::TypeParser when "tuple" TYPES.tuple() + + when "struct" + TYPES.struct() else TYPES.resource(name_ast.value) end diff --git a/lib/puppet/pops/types/types.rb b/lib/puppet/pops/types/types.rb index 2f93b00f4..14b54843c 100644 --- a/lib/puppet/pops/types/types.rb +++ b/lib/puppet/pops/types/types.rb @@ -283,7 +283,7 @@ module Puppet::Pops::Types has_attr 'hashed_elements', Object, :derived => true module ClassModule - def derived_hashed_elements + def hashed_elements_derived @_hashed ||= elements.reduce({}) {|memo, e| memo[e.name] = e.type; memo } @_hashed end diff --git a/spec/unit/pops/types/type_calculator_spec.rb b/spec/unit/pops/types/type_calculator_spec.rb index 430c2a9a4..c2d90f300 100644 --- a/spec/unit/pops/types/type_calculator_spec.rb +++ b/spec/unit/pops/types/type_calculator_spec.rb @@ -4,7 +4,7 @@ require 'puppet/pops' describe 'The type calculator' do let(:calculator) { Puppet::Pops::Types::TypeCalculator.new() } - def int_range(from, to) + def range_t(from, to) t = Puppet::Pops::Types::PIntegerType.new t.from = from t.to = to @@ -59,6 +59,10 @@ describe 'The type calculator' do Puppet::Pops::Types::TypeFactory.tuple(*types) end + def struct_t(type_hash) + Puppet::Pops::Types::TypeFactory.struct(type_hash) + end + def types Puppet::Pops::Types end @@ -85,7 +89,7 @@ describe 'The type calculator' do Puppet::Pops::Types::PPatternType, Puppet::Pops::Types::PEnumType, Puppet::Pops::Types::PVariantType, -# Puppet::Pops::Types::PStructType, + Puppet::Pops::Types::PStructType, Puppet::Pops::Types::PTupleType, ] end @@ -129,7 +133,7 @@ describe 'The type calculator' do Puppet::Pops::Types::PCollectionType, Puppet::Pops::Types::PHashType, Puppet::Pops::Types::PArrayType, -# Puppet::Pops::Types::PStructType, + Puppet::Pops::Types::PStructType, Puppet::Pops::Types::PTupleType, ] end @@ -141,7 +145,7 @@ describe 'The type calculator' do result << types::TypeFactory.hash_of_data result << Puppet::Pops::Types::PNilType result << (tmp = tuple_t(types::PDataType.new)) - tmp.size_type = int_range(0, nil) + tmp.size_type = range_t(0, nil) result end @@ -554,6 +558,7 @@ describe 'The type calculator' do t = Puppet::Pops::Types::PHashType.new() tested_types = collection_types - [ Puppet::Pops::Types::PCollectionType, + Puppet::Pops::Types::PStructType, Puppet::Pops::Types::PHashType] tested_types.each {|t2| t.should_not be_assignable_to(t2.new) } end @@ -586,6 +591,24 @@ describe 'The type calculator' do end end + context "for Struct, such that" do + it "Struct is not assignable to any other non Hashed based Collection type" do + t = Puppet::Pops::Types::PStructType.new() + tested_types = collection_types - [ + Puppet::Pops::Types::PCollectionType, + Puppet::Pops::Types::PStructType, + Puppet::Pops::Types::PHashType] + tested_types.each {|t2| t.should_not be_assignable_to(t2.new) } + end + + it 'Struct is not assignable to any disjunct type' do + tested_types = all_types - [ + Puppet::Pops::Types::PObjectType, + Puppet::Pops::Types::PDataType] - collection_types + t = Puppet::Pops::Types::PStructType.new() + tested_types.each {|t2| t.should_not be_assignable_to(t2.new) } + end + end it 'should recognize mapped ruby types' do { Integer => Puppet::Pops::Types::PIntegerType.new, @@ -608,37 +631,37 @@ describe 'The type calculator' do context 'when dealing with integer ranges' do it 'should accept an equal range' do - calculator.assignable?(int_range(2,5), int_range(2,5)).should == true + calculator.assignable?(range_t(2,5), range_t(2,5)).should == true end it 'should accept an equal reverse range' do - calculator.assignable?(int_range(2,5), int_range(5,2)).should == true + calculator.assignable?(range_t(2,5), range_t(5,2)).should == true end it 'should accept a narrower range' do - calculator.assignable?(int_range(2,10), int_range(3,5)).should == true + calculator.assignable?(range_t(2,10), range_t(3,5)).should == true end it 'should accept a narrower reverse range' do - calculator.assignable?(int_range(2,10), int_range(5,3)).should == true + calculator.assignable?(range_t(2,10), range_t(5,3)).should == true end it 'should reject a wider range' do - calculator.assignable?(int_range(3,5), int_range(2,10)).should == false + calculator.assignable?(range_t(3,5), range_t(2,10)).should == false end it 'should reject a wider reverse range' do - calculator.assignable?(int_range(3,5), int_range(10,2)).should == false + calculator.assignable?(range_t(3,5), range_t(10,2)).should == false end it 'should reject a partially overlapping range' do - calculator.assignable?(int_range(3,5), int_range(2,4)).should == false - calculator.assignable?(int_range(3,5), int_range(4,6)).should == false + calculator.assignable?(range_t(3,5), range_t(2,4)).should == false + calculator.assignable?(range_t(3,5), range_t(4,6)).should == false end it 'should reject a partially overlapping reverse range' do - calculator.assignable?(int_range(3,5), int_range(4,2)).should == false - calculator.assignable?(int_range(3,5), int_range(6,4)).should == false + calculator.assignable?(range_t(3,5), range_t(4,2)).should == false + calculator.assignable?(range_t(3,5), range_t(6,4)).should == false end end @@ -756,6 +779,32 @@ describe 'The type calculator' do end end + context 'when dealing with structs' do + it 'should accept matching structs' do + struct1 = struct_t({'a'=>Integer, 'b'=>Integer}) + struct2 = struct_t({'a'=>Integer, 'b'=>Integer}) + calculator.assignable?(struct1, struct2).should == true + calculator.assignable?(struct2, struct1).should == true + end + + it 'should accept matching structs where one is more general than the other' do + struct1 = struct_t({'a'=>Integer, 'b'=>Integer}) + struct2 = struct_t({'a'=>Numeric, 'b'=>Numeric}) + calculator.assignable?(struct1, struct2).should == false + calculator.assignable?(struct2, struct1).should == true + end + + it 'should accept matching hash' do + struct1 = struct_t({'a'=>Integer, 'b'=>Integer}) + non_empty_string = string_t() + non_empty_string.size_type = range_t(1, nil) + hsh = hash_t(non_empty_string, Integer) + factory.constrain_size(hsh, 2, 2) + calculator.assignable?(struct1, hsh).should == true + calculator.assignable?(hsh, struct1).should == true + end + end + it 'should recognize ruby type inheritance' do class Foo end @@ -853,7 +902,7 @@ describe 'The type calculator' do end it 'should consider integer in range' do - range = int_range(0,10) + range = range_t(0,10) calculator.instance?(range, 1).should == true calculator.instance?(range, 10).should == true calculator.instance?(range, -1).should == false @@ -919,7 +968,7 @@ describe 'The type calculator' do it 'should consider array[mixed] as instance of Variant[mixed] when mixed types are listed in Variant' do enum = enum_t('XS', 'S', 'M', 'L', 'XL') - sizes = int_range(30, 50) + sizes = range_t(30, 50) array = array_t(variant_t(enum, sizes)) calculator.instance?(array, ['XS', 'S', 30, 50]).should == true calculator.instance?(array, ['XS', 'S', 'XXL']).should == false @@ -1128,6 +1177,13 @@ describe 'The type calculator' do calculator.string(factory.constrain_size(tuple_t.copy, 2, :default)).should == 'Tuple[String, 2, default]' end + it 'should yield \'Struct\' and details for PStructType' do + struct_t = struct_t({'a'=>Integer, 'b'=>String}) + calculator.string(struct_t).should == "Struct[{'a'=>Integer, 'b'=>String}]" + struct_t = struct_t({}) + calculator.string(struct_t).should == "Struct" + end + it 'should yield \'Hash[String, Integer]\' for PHashType[PStringType, PIntegerType]' do t = Puppet::Pops::Types::PHashType.new() t.key_type = Puppet::Pops::Types::PStringType.new() From e372a344ecdf4c6c7a10c46678d41593ea63cbcd Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 10 Feb 2014 03:43:02 +0100 Subject: [PATCH 622/800] (PUP-1619) Accept missing struct keys if type for key is optional A hash with key bound to nil/undef or missing key and the struct type accepts nil/undef should be treated as matching. Without this fix, the hash must has nil/undef bound to keys witout value. --- lib/puppet/pops/types/type_calculator.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/pops/types/type_calculator.rb b/lib/puppet/pops/types/type_calculator.rb index d09963acd..fd6e1f101 100644 --- a/lib/puppet/pops/types/type_calculator.rb +++ b/lib/puppet/pops/types/type_calculator.rb @@ -399,7 +399,7 @@ class Puppet::Pops::Types::TypeCalculator return false unless o.is_a?(Hash) h = t.hashed_elements # all keys must be present and have a value (even if nil/undef) - return false unless o.size == h.size && h.all? { |k,v| o.has_key?(k) && instance_of(v, o[k]) } + (o.keys - h.keys).empty? && h.all? { |k,v| instance_of(v, o[k]) } end def instance_of_PHashType(t, o) From 8c27513a5dcedb3ea7831e823a7ae97ab6e3d0b5 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 10 Feb 2014 03:44:38 +0100 Subject: [PATCH 623/800] (maint) Fix issue with type_of not handling Optional type The Optional Type was treated as a RubyType in the type_of method because it checked against ObjectType instead of AbstractType. --- lib/puppet/pops/types/type_factory.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/puppet/pops/types/type_factory.rb b/lib/puppet/pops/types/type_factory.rb index 9f5860206..5d52aece8 100644 --- a/lib/puppet/pops/types/type_factory.rb +++ b/lib/puppet/pops/types/type_factory.rb @@ -272,13 +272,13 @@ module Puppet::Pops::Types::TypeFactory type end - # Produce a type corresponding to the class of given unless given is a String, Class or a PObjectType. + # Produce a type corresponding to the class of given unless given is a String, Class or a PAbstractType. # When a String is given this is taken as a classname. # def self.type_of(o) if o.is_a?(Class) @type_calculator.type(o) - elsif o.is_a?(Types::PObjectType) + elsif o.is_a?(Types::PAbstractType) o elsif o.is_a?(String) type = Types::PRubyType.new() From e93ac602879713da5420462db09baec68127d5f8 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 10 Feb 2014 03:45:19 +0100 Subject: [PATCH 624/800] (maint) Correct indentation in type parser --- lib/puppet/pops/types/type_parser.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/puppet/pops/types/type_parser.rb b/lib/puppet/pops/types/type_parser.rb index cea59689a..d095913ca 100644 --- a/lib/puppet/pops/types/type_parser.rb +++ b/lib/puppet/pops/types/type_parser.rb @@ -153,10 +153,10 @@ class Puppet::Pops::Types::TypeParser TYPES.object() when "variant" - TYPES.variant() + TYPES.variant() when "optional" - TYPES.optional() + TYPES.optional() when "ruby" TYPES.ruby_type() From dbdff13a56d126a9eec6f19ac13ddde9e912f563 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Mon, 10 Feb 2014 03:55:16 +0100 Subject: [PATCH 625/800] (PUP-1619) Add support for Struct in Type Parser --- lib/puppet/pops/types/type_parser.rb | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/puppet/pops/types/type_parser.rb b/lib/puppet/pops/types/type_parser.rb index d095913ca..eb5531c6d 100644 --- a/lib/puppet/pops/types/type_parser.rb +++ b/lib/puppet/pops/types/type_parser.rb @@ -318,6 +318,12 @@ class Puppet::Pops::Types::TypeParser end t + when "struct" + # 1..m parameters being types (last two optionally integer or literal default + raise_invalid_parameters_error("Struct", "1", parameters.size) unless parameters.size == 1 + assert_struct_parameter(parameters[0]) + TYPES.struct(parameters[0]) + when "integer" if parameters.size == 1 case parameters[0] @@ -402,12 +408,21 @@ class Puppet::Pops::Types::TypeParser def assert_type(t) raise_invalid_type_specification_error unless t.is_a?(Puppet::Pops::Types::PObjectType) + true end def assert_range_parameter(t) - raise_invalid_type_speification_error unless TYPES.is_range_parameter?(t) + raise_invalid_type_specification_error unless TYPES.is_range_parameter?(t) end + def assert_struct_parameter(h) + raise_invalid_type_specification_error unless h.is_a?(Hash) + h.each do |k,v| + # TODO: Should have stricter name rule + raise_invalid_type_specification_error unless k.is_a?(String) && !k.empty? + assert_type(v) + end + end def raise_invalid_type_specification_error raise Puppet::ParseError, @@ -418,6 +433,7 @@ class Puppet::Pops::Types::TypeParser raise Puppet::ParseError, "Invalid number of type parameters specified: #{type} requires #{required}, #{given} provided" end + def raise_unparameterized_type_error(ast) raise Puppet::ParseError, "Not a parameterized type <#{original_text_of(ast)}>" end From d5c26fef5b234074fc46a2ba5a5ad32602f5b4d7 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 11 Feb 2014 00:15:18 +0100 Subject: [PATCH 626/800] (PUP-1619) Add tests and fix issues in type parser The type Optional had no tests in type parser. Tests also added for the new types Tuple and Struct. Also fix issues with Optional type handling in type calculator and type factory. --- lib/puppet/pops/types/type_calculator.rb | 8 ++++++-- lib/puppet/pops/types/type_factory.rb | 2 +- lib/puppet/pops/types/type_parser.rb | 15 +++++++++++++-- spec/unit/pops/types/type_parser_spec.rb | 24 ++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 5 deletions(-) diff --git a/lib/puppet/pops/types/type_calculator.rb b/lib/puppet/pops/types/type_calculator.rb index fd6e1f101..a080c95b5 100644 --- a/lib/puppet/pops/types/type_calculator.rb +++ b/lib/puppet/pops/types/type_calculator.rb @@ -1012,7 +1012,7 @@ class Puppet::Pops::Types::TypeCalculator # @api private def assignable_POptionalType(t, t2) - return true if t2.is_a(Types::PNilType) + return true if t2.is_a?(Types::PNilType) if t2.is_a?(Types::POptionalType) assignable?(t.optional_type, t2.optional_type) else @@ -1425,7 +1425,11 @@ class Puppet::Pops::Types::TypeCalculator end def string_POptionalType(t) - "Optional[#{string(t.optional_type)}]" + if t.optional_type.nil? + "Optional" + else + "Optional[#{string(t.optional_type)}]" + end end # Catches all non enumerable types diff --git a/lib/puppet/pops/types/type_factory.rb b/lib/puppet/pops/types/type_factory.rb index 5d52aece8..3022b98db 100644 --- a/lib/puppet/pops/types/type_factory.rb +++ b/lib/puppet/pops/types/type_factory.rb @@ -66,7 +66,7 @@ module Puppet::Pops::Types::TypeFactory # Produces the Optional type, i.e. a short hand for Variant[T, Undef] def self.optional(optional_type = nil) t = Types::POptionalType.new - t.optional_type = optional_type + t.optional_type = type_of(optional_type) t end diff --git a/lib/puppet/pops/types/type_parser.rb b/lib/puppet/pops/types/type_parser.rb index eb5531c6d..5536665fc 100644 --- a/lib/puppet/pops/types/type_parser.rb +++ b/lib/puppet/pops/types/type_parser.rb @@ -86,14 +86,25 @@ class Puppet::Pops::Types::TypeParser :default end + # @api private def interpret_LiteralInteger(o) o.value end + # @api private def interpret_LiteralFloat(o) o.value end + # @api private + def interpret_LiteralHash(o) + result = {} + o.entries.each do |entry| + result[@type_transformer.visit_this_0(self, entry.key)] = @type_transformer.visit_this_0(self, entry.value) + end + result + end + # @api private def interpret_QualifiedReference(name_ast) case name_ast.value @@ -298,7 +309,7 @@ class Puppet::Pops::Types::TypeParser # 1..m parameters being types (last two optionally integer or literal default raise_invalid_parameters_error("Tuple", "1 or more", parameters.size) unless parameters.size > 1 length = parameters.size - if is_range_parameter?(parameters[-2]) + if TYPES.is_range_parameter?(parameters[-2]) # min, max specification min = parameters[-2] min = (min == :default || min == 'default') ? 0 : min @@ -306,7 +317,7 @@ class Puppet::Pops::Types::TypeParser max = parameters[-1] max = max == :default ? nil : max parameters = parameters[0, length-2] - elsif is_range_parameter?(parameters[-1]) + elsif TYPES.is_range_parameter?(parameters[-1]) min = parameters[-1] min = (min == :default || min == 'default') ? 0 : min max = nil diff --git a/spec/unit/pops/types/type_parser_spec.rb b/spec/unit/pops/types/type_parser_spec.rb index 247d3701d..df9194161 100644 --- a/spec/unit/pops/types/type_parser_spec.rb +++ b/spec/unit/pops/types/type_parser_spec.rb @@ -47,6 +47,9 @@ describe Puppet::Pops::Types::TypeParser do expect(the_type_parsed_from(types.data)).to be_the_type(types.data) expect(the_type_parsed_from(types.catalog_entry)).to be_the_type(types.catalog_entry) expect(the_type_parsed_from(types.collection)).to be_the_type(types.collection) + expect(the_type_parsed_from(types.tuple)).to be_the_type(types.tuple) + expect(the_type_parsed_from(types.struct)).to be_the_type(types.struct) + expect(the_type_parsed_from(types.optional)).to be_the_type(types.optional) end it "interprets an unparameterized Array as an Array of Data" do @@ -89,6 +92,27 @@ describe Puppet::Pops::Types::TypeParser do expect(the_type_parsed_from(parameterized_hash)).to be_the_type(parameterized_hash) end + it "parses optional type" do + opt_t = types.optional(Integer) + expect(the_type_parsed_from(opt_t)).to be_the_type(opt_t) + end + + it "parses tuple type" do + tuple_t = types.tuple(Integer, String) + expect(the_type_parsed_from(tuple_t)).to be_the_type(tuple_t) + end + + it "parses tuple type with occurence constraint" do + tuple_t = types.tuple(Integer, String) + types.constrain_size(tuple_t, 2, 5) + expect(the_type_parsed_from(tuple_t)).to be_the_type(tuple_t) + end + + it "parses struct type" do + struct_t = types.struct({'a'=>Integer, 'b'=>String}) + expect(the_type_parsed_from(struct_t)).to be_the_type(struct_t) + end + it "rejects an collection spec with the wrong number of parameters" do expect { parser.parse("Array[Integer, 1,2,3]") }.to raise_the_parameter_error("Array", "1 to 3", 4) expect { parser.parse("Hash[Integer, Integer, 1,2,3]") }.to raise_the_parameter_error("Hash", "1 to 4", 5) From 8952cdbde46cdbbe1655fec73d975a0cb31bca5d Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 11 Feb 2014 01:39:57 +0100 Subject: [PATCH 627/800] (maint) Fix regexp with unescaped { causing warning when running tests --- spec/unit/face/parser_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/unit/face/parser_spec.rb b/spec/unit/face/parser_spec.rb index 78096befd..3bf24bcb0 100644 --- a/spec/unit/face/parser_spec.rb +++ b/spec/unit/face/parser_spec.rb @@ -48,7 +48,7 @@ describe Puppet::Face[:parser, :current] do expect { parser.validate(manifest) }.to exit_with(1) end - expect(@logs.join).to match(/environment special.*Syntax error at '{'/) + expect(@logs.join).to match(/environment special.*Syntax error at '\{'/) end end From e1d378dd341fe43f78a5300b0ed798c2b389da13 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 11 Feb 2014 03:39:48 +0100 Subject: [PATCH 628/800] (PUP-1619) Fix problems with Ruby 1.8.7 Range min/max & Infinity Billions of blue blistering boiled and barbecued barnacles, the Ruby 1.8.7 implementation of Range min/max is based on Enumeration. This does not work well when the range has +/- Infinity at one of the ends. There is probably a "break" when it reaches Infinity... *sign* This commit changes the implementation from using a Range to using an Array instead of a Range. --- lib/puppet/pops/types/type_calculator.rb | 19 +++++++++---------- lib/puppet/pops/types/types.rb | 6 ++++-- spec/unit/pops/types/type_calculator_spec.rb | 11 ++++++++--- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/lib/puppet/pops/types/type_calculator.rb b/lib/puppet/pops/types/type_calculator.rb index a080c95b5..9c262c3ae 100644 --- a/lib/puppet/pops/types/type_calculator.rb +++ b/lib/puppet/pops/types/type_calculator.rb @@ -413,7 +413,6 @@ class Puppet::Pops::Types::TypeCalculator end def instance_of_PDataType(t, o) - #require 'debugger'; debugger instance_of(@data_variant_t, o) end @@ -952,11 +951,11 @@ class Puppet::Pops::Types::TypeCalculator # array type may be size constrained size_t = t2.size_type || @collection_default_size_t - sz = size_t.range + min, max = size_t.range # Array with fewer min entries can not be assigned - return false if t_required > sz.min + return false if t_required > min # Array with more optionally available entries can not be assigned - return false if t_regular.size + t_to < sz.max + return false if t_regular.size + t_to < max # each tuple type must be assignable to the element type t_required.times do |index| t_entry = tuple_entry_at(t, t_from, t_to, index) @@ -1156,18 +1155,18 @@ class Puppet::Pops::Types::TypeCalculator t_entry = t.element_type - # Array of anything can not be assigned (unless tuple is tuple of anything) - this case + # Tuple of anything can not be assigned (unless array is tuple of anything) - this case # was handled at the top of this method. # return false if t_entry.nil? # array type may be size constrained size_t = t.size_type || @collection_default_size_t - sz = size_t.range + min, max = size_t.range # Tuple with fewer min entries can not be assigned - return false if t2_required < sz.min + return false if t2_required < min # Tuple with more optionally available entries can not be assigned - return false if t2_regular.size + t2_to > sz.max + return false if t2_regular.size + t2_to > max # each tuple type must be assignable to the element type t2_required.times do |index| t2_entry = tuple_entry_at(t2, t2_from, t2_to, index) @@ -1192,10 +1191,10 @@ class Puppet::Pops::Types::TypeCalculator # hash must accept all value types # hash must accept the size of the struct size_t = t.size_type || @collection_default_size_t - sz = size_t.range + min, max = size_t.range struct_size = t2.elements.size element_type = t.element_type - ( struct_size >= sz.min && struct_size <= sz.max && + ( struct_size >= min && struct_size <= max && assignable?(t.key_type, @non_emptry_string_t) && t2.hashed_elements.all? {|k,v| assignable?(element_type, v) }) else diff --git a/lib/puppet/pops/types/types.rb b/lib/puppet/pops/types/types.rb index 14b54843c..89fa24c34 100644 --- a/lib/puppet/pops/types/types.rb +++ b/lib/puppet/pops/types/types.rb @@ -138,13 +138,15 @@ module Puppet::Pops::Types 1+(to-from).abs end + # Returns the range as an array ordered so the smaller number is always first. + # The number may be Infinity or -Infinity. def range f = from || -(1.0 / 0.0) t = to || (1.0 / 0.0) if f < t - (f..t) + [f, t] else - (t..f) + [t,f] end end diff --git a/spec/unit/pops/types/type_calculator_spec.rb b/spec/unit/pops/types/type_calculator_spec.rb index c2d90f300..518f63025 100644 --- a/spec/unit/pops/types/type_calculator_spec.rb +++ b/spec/unit/pops/types/type_calculator_spec.rb @@ -144,7 +144,8 @@ describe 'The type calculator' do result << array_t(types::PDataType.new) result << types::TypeFactory.hash_of_data result << Puppet::Pops::Types::PNilType - result << (tmp = tuple_t(types::PDataType.new)) + tmp = tuple_t(types::PDataType.new) + result << (tmp) tmp.size_type = range_t(0, nil) result end @@ -446,7 +447,9 @@ describe 'The type calculator' do context "for Data, such that" do it 'all scalars + array and hash are assignable to Data' do t = Puppet::Pops::Types::PDataType.new() - data_compatible_types.each { |t2| type_from_class(t2).should be_assignable_to(t) } + data_compatible_types.each { |t2| + type_from_class(t2).should be_assignable_to(t) + } end it 'a Variant of scalar, hash, or array is assignable to Data' do @@ -1179,7 +1182,9 @@ describe 'The type calculator' do it 'should yield \'Struct\' and details for PStructType' do struct_t = struct_t({'a'=>Integer, 'b'=>String}) - calculator.string(struct_t).should == "Struct[{'a'=>Integer, 'b'=>String}]" + s = calculator.string(struct_t) + # Ruby 1.8.7 - noone likes you... + (s == "Struct[{'a'=>Integer, 'b'=>String}]" || s == "Struct[{'b'=>String, 'a'=>Integer}]").should == true struct_t = struct_t({}) calculator.string(struct_t).should == "Struct" end From 1c0e54cf6420e2e3958c763b6baa9726ad6562d0 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Tue, 11 Feb 2014 15:25:58 -0800 Subject: [PATCH 629/800] (PUP-649) Use class instance variable Accessing a class variable from the class produces a warning on ruby 1.9.3. We can instead just use the instance variable, which is also on the class, but is safer to use. --- lib/puppet/provider/cron/crontab.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/puppet/provider/cron/crontab.rb b/lib/puppet/provider/cron/crontab.rb index a9fd94626..9931ee62e 100644 --- a/lib/puppet/provider/cron/crontab.rb +++ b/lib/puppet/provider/cron/crontab.rb @@ -169,7 +169,7 @@ Puppet::Type.type(:cron).provide(:crontab, :parent => Puppet::Provider::ParsedFi false end - @@name_index = 0 + @name_index = 0 # Collapse name and env records. def self.prefetch_hook(records) @@ -200,7 +200,7 @@ Puppet::Type.type(:cron).provide(:crontab, :parent => Puppet::Provider::ParsedFi name = nil else cmd_string = record[:command].gsub(/\s+/, "_") - index = ( @@name_index += 1 ) + index = ( @name_index += 1 ) record[:name] = "unmanaged:#{cmd_string}-#{ index.to_s }" record[:unmanaged] = true end From 48c8152e06ce45d93db561f710aa4b01e9c6fea6 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Tue, 11 Feb 2014 15:38:31 -0800 Subject: [PATCH 630/800] (PUP-649) Loosen checks for crontab provider The tests for the crontab provider exhibited an order dependent failure. If the spec/integration/provider/cron tests were run before the spec/unit/provider/cron tests then the global counter that is used by the crontab provider would not be at the expected value. This isn't really a problem in practice, but the expectations in the tests were for a specific count. This updates the checks so that they are resilient to the exact numbers chosen. --- spec/unit/provider/cron/parsed_spec.rb | 58 ++++++++++++-------------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/spec/unit/provider/cron/parsed_spec.rb b/spec/unit/provider/cron/parsed_spec.rb index bad859c90..68a29c88c 100644 --- a/spec/unit/provider/cron/parsed_spec.rb +++ b/spec/unit/provider/cron/parsed_spec.rb @@ -200,42 +200,38 @@ describe Puppet::Type.type(:cron).provider(:crontab) do it "should be able to create records from not-managed records" do described_class.expects(:target_object).returns File.new(my_fixture('simple')) - described_class.instances.map do |p| + parameters = described_class.instances.map do |p| h = {:name => p.get(:name)} Puppet::Type.type(:cron).validproperties.each do |property| h[property] = p.get(property) end h - end.should == [ - { - :name => 'unmanaged:$HOME/bin/daily.job_>>_$HOME/tmp/out_2>&1-1', - :minute => ['5'], - :hour => ['0'], - :weekday => :absent, - :month => :absent, - :monthday => :absent, - :special => :absent, - :command => '$HOME/bin/daily.job >> $HOME/tmp/out 2>&1', - :ensure => :present, - :environment => :absent, - :user => :absent, - :target => 'foobar' - }, - { - :name => 'unmanaged:$HOME/bin/monthly-2', - :minute => ['15'], - :hour => ['14'], - :weekday => :absent, - :month => :absent, - :monthday => ['1'], - :special => :absent, - :command => '$HOME/bin/monthly', - :ensure => :present, - :environment => :absent, - :user => :absent, - :target => 'foobar' - } - ] + end + + expect(parameters[0][:name]).to match(%r{unmanaged:\$HOME/bin/daily.job_>>_\$HOME/tmp/out_2>&1-\d+}) + expect(parameters[0][:minute]).to eq(['5']) + expect(parameters[0][:hour]).to eq(['0']) + expect(parameters[0][:weekday]).to eq(:absent) + expect(parameters[0][:month]).to eq(:absent) + expect(parameters[0][:monthday]).to eq(:absent) + expect(parameters[0][:special]).to eq(:absent) + expect(parameters[0][:command]).to match(%r{\$HOME/bin/daily.job >> \$HOME/tmp/out 2>&1}) + expect(parameters[0][:ensure]).to eq(:present) + expect(parameters[0][:environment]).to eq(:absent) + expect(parameters[0][:user]).to eq(:absent) + + expect(parameters[1][:name]).to match(%r{unmanaged:\$HOME/bin/monthly-\d+}) + expect(parameters[1][:minute]).to eq(['15']) + expect(parameters[1][:hour]).to eq(['14']) + expect(parameters[1][:weekday]).to eq(:absent) + expect(parameters[1][:month]).to eq(:absent) + expect(parameters[1][:monthday]).to eq(['1']) + expect(parameters[1][:special]).to eq(:absent) + expect(parameters[1][:command]).to match(%r{\$HOME/bin/monthly}) + expect(parameters[1][:ensure]).to eq(:present) + expect(parameters[1][:environment]).to eq(:absent) + expect(parameters[1][:user]).to eq(:absent) + expect(parameters[1][:target]).to eq('foobar') end it "should be able to parse puppet manged cronjobs" do From 39f20154d4f0104d9ec00312505a57d865787ce6 Mon Sep 17 00:00:00 2001 From: Felix Frank Date: Wed, 13 Nov 2013 02:35:38 +0100 Subject: [PATCH 631/800] (PUP-1327) add the file attributes owner, group and mode to nagios types It's desirable to make it possible to write manifests such as nagios_host { "hostname": ... # nagios properties group => "nagios", mode => 640, } instead of the workaround of nagios_host { "hostname": target => "/etc/nagios/hostname.cfg", ... } file { "/etc/nagios/hostname.cfg": ... } To make this possible in a simple fashion, just add the file properties owner, group and mode to each nagios type as parameters. If either of them is specified, the nagios resource generates an additional file resource and passes the parameters as should-values of the respective properties. --- lib/puppet/util/nagios_maker.rb | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/lib/puppet/util/nagios_maker.rb b/lib/puppet/util/nagios_maker.rb index 02e5d8bd7..61cf9248b 100644 --- a/lib/puppet/util/nagios_maker.rb +++ b/lib/puppet/util/nagios_maker.rb @@ -11,7 +11,21 @@ module Puppet::Util::NagiosMaker raise(Puppet::DevError, "No nagios type for #{name}") unless nagtype = Nagios::Base.type(name) - type = Puppet::Type.newtype(full_name) {} + type = Puppet::Type.newtype(full_name) do + + # Generate a file resource if necessary. + # + # @see Puppet::Type::File and its properties owner, group and mode. + def generate + return nil unless self[:owner] or self[:group] or self[:mode] + props = { :name => self[:target] } + [ :owner, :group, :mode ].each do |prop| + props[prop] = self[prop] if self[prop] + end + Puppet::Type.type(:file).new(props) + end + + end type.ensurable @@ -19,6 +33,17 @@ module Puppet::Util::NagiosMaker desc "The name of this nagios_#{nagtype.name} resource." end + [ :owner, :group, :mode].each do |fileprop| + type.newparam(fileprop) do + desc "The desired #{fileprop} of the config file for this nagios_#{nagtype.name} resource. + + NOTE: If the target file is explicitly managed by a file resource in your manifest, + this parameter has no effect. If a parent directory of the target is managed by + a recursive file resource, this limitation does not apply (i.e., this parameter + takes precedence, and if purge is used, the target file is exempt)." + end + end + # We deduplicate the parameters because it makes sense to allow Naginator to have dupes. nagtype.parameters.uniq.each do |param| next if param == nagtype.namevar From ffbf6b37991353cb25423400784ead706af296b8 Mon Sep 17 00:00:00 2001 From: Felix Frank Date: Wed, 13 Nov 2013 08:18:02 +0100 Subject: [PATCH 632/800] (PUP-1327) simple unit test for the new nagios parameters For now, only test for the presence of the parameters. Their function should be tested in an integration spec. --- spec/unit/type/nagios_spec.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spec/unit/type/nagios_spec.rb b/spec/unit/type/nagios_spec.rb index 84d4338de..bc96c26d4 100755 --- a/spec/unit/type/nagios_spec.rb +++ b/spec/unit/type/nagios_spec.rb @@ -258,6 +258,12 @@ describe "Nagios resource types" do puppet_type.attrclass(:target).instance_variable_get("@doc").should_not be_nil end + [ :owner, :group, :mode ].each do |fileprop| + it "should have a #{fileprop} parameter" do + puppet_type.parameters.should be_include(fileprop) + end + end + nagios_type.parameters.reject { |param| param == nagios_type.namevar or param.to_s =~ /^[0-9]/ }.each do |param| it "should have a #{param} property" do puppet_type.should be_validproperty(param) From ede7cf4434a540e73098526d9f85779ce735db8f Mon Sep 17 00:00:00 2001 From: Felix Frank Date: Wed, 13 Nov 2013 22:56:20 +0100 Subject: [PATCH 633/800] (PUP-1327) integration spec for the nagios type This is a very rough draft for a spec test wrt. the "mode" parameter. Testing group and owner is difficult since unprivileged rspec users cannot use chown or chgrp. This is open for discussion and should be revised before merging. --- spec/integration/type/nagios_spec.rb | 78 ++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 spec/integration/type/nagios_spec.rb diff --git a/spec/integration/type/nagios_spec.rb b/spec/integration/type/nagios_spec.rb new file mode 100644 index 000000000..2746fecb3 --- /dev/null +++ b/spec/integration/type/nagios_spec.rb @@ -0,0 +1,78 @@ +#!/usr/bin/env ruby + +require 'spec_helper' +require 'puppet/file_bucket/dipper' + +describe "Nagios file creation" do + include PuppetSpec::Files + + before :each do + FileUtils.touch(target_file) + File.chmod(0600, target_file) + Puppet::FileBucket::Dipper.any_instance.stubs(:backup) # Don't backup to filebucket + end + + let :target_file do + tmpfile('nagios_integration_specs') + end + + # Copied from the crontab integration spec. + # + # @todo This should probably live in the PuppetSpec module instead then. + def run_in_catalog(*resources) + catalog = Puppet::Resource::Catalog.new + catalog.host_config = false + resources.each do |resource| + resource.expects(:err).never + catalog.add_resource(resource) + end + + # the resources are not properly contained and generated resources + # will end up with dangling edges without this stubbing: + catalog.stubs(:container_of).returns resources[0] + catalog.apply + end + + # These three helpers are from file_spec.rb + # + # @todo Define those centrally as well? + def get_mode(file) + Puppet::FileSystem.stat(file).mode + end + + context "when creating a nagios config file" do + context "which is not managed" do + it "should choose the file mode if requested" do + resource = Puppet::Type.type(:nagios_host).new( + :name => 'spechost', + :use => 'spectemplate', + :ensure => 'present', + :target => target_file, + :mode => '0640' + ) + run_in_catalog(resource) + ( "%o" % get_mode(target_file) ).should == "100640" + end + end + + context "which is managed" do + it "should not the mode" do + file_res = Puppet::Type.type(:file).new( + :name => target_file, + :ensure => :present + ) + nag_res = Puppet::Type.type(:nagios_host).new( + :name => 'spechost', + :use => 'spectemplate', + :ensure => :present, + :target => target_file, + :mode => '0640' + ) + run_in_catalog(file_res, nag_res) + ( "%o" % get_mode(target_file) ).should_not == "100640" + end + end + + end + +end From 747a62f40e74813d4eb2d646bbf0bde128fe9333 Mon Sep 17 00:00:00 2001 From: Ashley Penney Date: Wed, 12 Feb 2014 13:28:17 +0000 Subject: [PATCH 634/800] (maint) Switch from an .each to a .last so we don't repeat ourselves. --- lib/puppet/provider/yumrepo/inifile.rb | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/puppet/provider/yumrepo/inifile.rb b/lib/puppet/provider/yumrepo/inifile.rb index 20f193b89..7bffedcc1 100644 --- a/lib/puppet/provider/yumrepo/inifile.rb +++ b/lib/puppet/provider/yumrepo/inifile.rb @@ -94,19 +94,20 @@ Puppet::Type.type(:yumrepo).provide(:inifile) do # We need to return a valid section from the larger virtual inifile here, # which we do by first looking it up and then creating a new section for # the appropriate name if none was found. - # @todo Why do we create a section for every repodir found? - # @todo Does this mean we only return the last section made? # @param name [String] Section name to lookup in the virtual inifile. # @return [Puppet::Util::IniConfig] The IniConfig section def self.section(name) result = self.virtual_inifile[name] # Create a new section if not found. unless result - reposdir.each do |dir| - path = ::File.join(dir, "#{name}.repo") - Puppet.info("create new repo #{name} in file #{path}") - result = self.virtual_inifile.add_section(name, path) - end + # Previously we did an .each on reposdir with the effect that we + # constantly created and overwrote result until the last entry of + # the array. This was done because the ordering is + # [defaults, custom] for reposdir and we want to use the custom if + # we have it and the defaults if not. + path = ::File.join(reposdir.last, "#{name}.repo") + Puppet.info("create new repo #{name} in file #{path}") + result = self.virtual_inifile.add_section(name, path) end result end From cffffb2877e626d20ac06df7511f23c83bb3edb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Dal=C3=A9n?= Date: Tue, 11 Feb 2014 18:06:18 -0500 Subject: [PATCH 635/800] (PUP-1670) Add validate_cmd parameter to file type If this parameter is set, then this file will only be written if the command specified returns 0. Also adds a validate_replacement parameter to allow specifying some other string to use for specifying temporary file name instead of the default of '%'. --- lib/puppet/type/file.rb | 30 ++++++++++++++++++++++++++++++ spec/integration/type/file_spec.rb | 18 ++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/lib/puppet/type/file.rb b/lib/puppet/type/file.rb index de7947a71..1b4249b86 100644 --- a/lib/puppet/type/file.rb +++ b/lib/puppet/type/file.rb @@ -256,6 +256,30 @@ Puppet::Type.newtype(:file) do defaultto :true end + newparam(:validate_cmd) do + desc "If this parameter is set, then this file will only be written if + the command returns 0. For example: + + file { '/etc/apache2/apache2.conf': + content => 'example' + validate_cmd => '/usr/sbin/apache2 -t -f %' + } + + This would replace apache2.conf only if that test returned true. + + The percent sign (%) will be replaced with the path of the temporary + file for this resource. + + Note that the command must be fully qualified." + end + + newparam(:validate_replacement) do + desc "The replacement string that `validate_cmd` uses to replace + with the file name. Defaults to: `%`" + + defaultto '%' + end + # Autorequire the nearest ancestor directory found in the catalog. autorequire(:file) do req = [] @@ -726,6 +750,12 @@ Puppet::Type.newtype(:file) do content_checksum = write_content(file) file.flush fail_if_checksum_is_wrong(file.path, content_checksum) if validate_checksum? + if self[:validate_cmd] + output = Puppet::Util::Execution.execute(self[:validate_cmd].gsub(self[:validate_replacement], file.path), :failonfail => true, :combine => true) + output.split(/\n/).each { |line| + self.debug(line) + } + end end else umask = mode ? 000 : 022 diff --git a/spec/integration/type/file_spec.rb b/spec/integration/type/file_spec.rb index c6a11549d..cf494ba2f 100755 --- a/spec/integration/type/file_spec.rb +++ b/spec/integration/type/file_spec.rb @@ -1329,6 +1329,24 @@ describe Puppet::Type.type(:file) do end end + describe "when using validate_cmd" do + it "should fail the file resource if command fails" do + catalog.add_resource(described_class.new(:path => path, :content => "foo", :validate_cmd => "/usr/bin/env false")) + Puppet::Util::Execution.expects(:execute).with("/usr/bin/env false", {:combine => true, :failonfail => true}).raises(Puppet::ExecutionFailure, "Failed") + report = catalog.apply.report + report.resource_statuses["File[#{path}]"].should be_failed + Puppet::FileSystem.exist?(path).should be_false + end + + it "should succeed the file resource if command succeeds" do + catalog.add_resource(described_class.new(:path => path, :content => "foo", :validate_cmd => "/usr/bin/env true")) + Puppet::Util::Execution.expects(:execute).with("/usr/bin/env true", {:combine => true, :failonfail => true}).returns '' + report = catalog.apply.report + report.resource_statuses["File[#{path}]"].should_not be_failed + Puppet::FileSystem.exist?(path).should be_true + end + end + def tmpfile_with_contents(name, contents) file = tmpfile(name) File.open(file, "w") { |f| f.write contents } From 1ee89b71e6eab71449d65bea123492b67dbd8293 Mon Sep 17 00:00:00 2001 From: Felix Frank Date: Sat, 9 Nov 2013 19:32:23 +0100 Subject: [PATCH 636/800] (PUP-713) cron: forbid the mixing of special and numeric schedules The BSD special schedules such as @reboot or @midnight and regular schedules comprising one or more time fields such as minute or hour are mutually exclusive. Until now it was valid to specify a special schedule alongside numeric fields, though. Add validation logic to the crontype to ensure that a cron resource does not try to manage these incompatible fields together. It is acceptable to declare should-values for the respective properties, but only with the following limitation: If the 'special' property is declard but not :absent, any declared numeric schedule field must be :absent. Raise an ArgumentError type exception that includes a concrete reference to the special property as well as the title of the offensive numeric schedule property. --- lib/puppet/type/cron.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/puppet/type/cron.rb b/lib/puppet/type/cron.rb index c6d6c3a36..0db3f8abf 100644 --- a/lib/puppet/type/cron.rb +++ b/lib/puppet/type/cron.rb @@ -407,6 +407,18 @@ Puppet::Type.newtype(:cron) do } end + validate do + return true unless self[:special] + return true if self[:special] == :absent + # there is a special schedule in @should, so we don't want to see + # any numeric should values + [ :minute, :hour, :weekday, :monthday, :month ].each do |field| + next unless self[field] + next if self[field] == :absent + raise ArgumentError, "#{self.ref} cannot specify both a special schedule and a value for #{field}" + end + end + # We have to reorder things so that :provide is before :target attr_accessor :uid From 75766d5dd2dc5acb85a381dce93eded76051fe7d Mon Sep 17 00:00:00 2001 From: Felix Frank Date: Sun, 10 Nov 2013 01:22:39 +0100 Subject: [PATCH 637/800] (PUP-713) Add tests for the special property of the cron type --- spec/unit/type/cron_spec.rb | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/spec/unit/type/cron_spec.rb b/spec/unit/type/cron_spec.rb index 3d08ba203..469999eed 100755 --- a/spec/unit/type/cron_spec.rb +++ b/spec/unit/type/cron_spec.rb @@ -441,6 +441,40 @@ describe Puppet::Type.type(:cron), :unless => Puppet.features.microsoft_windows? end end + describe "special" do + %w(reboot yearly annually monthly weekly daily midnight hourly).each do |value| + it "should support the value '#{value}'" do + expect { described_class.new(:name => 'foo', :special => value ) }.to_not raise_error(Puppet::Error, /cannot specify both a special schedule and a value/) + end + end + + context "when combined with numeric schedule fields" do + context "which are 'absent'" do + [ %w(reboot yearly annually monthly weekly daily midnight hourly), :absent ].flatten.each { |value| + it "should accept the value '#{value}' for special" do + expect { + described_class.new(:name => 'foo', :minute => :absent, :special => value ) + }.to_not raise_error(Puppet::Error, /cannot specify both a special schedule and a value/) + end + } + end + context "which are not absent" do + %w(reboot yearly annually monthly weekly daily midnight hourly).each { |value| + it "should not accept the value '#{value}' for special" do + expect { + described_class.new(:name => 'foo', :minute => "1", :special => value ) + }.to raise_error(Puppet::Error, /cannot specify both a special schedule and a value/) + end + } + it "should accept the 'absent' value for special" do + expect { + described_class.new(:name => 'foo', :minute => "1", :special => :absent ) + }.to_not raise_error(Puppet::Error, /cannot specify both a special schedule and a value/) + end + end + end + end + describe "environment" do it "it should accept an :environment that looks like a path" do expect do From a2c320c71a63f7ddd3a55b3f0fc3d7970100aba4 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Wed, 12 Feb 2014 13:18:29 -0800 Subject: [PATCH 638/800] (PUP-1588) Constrain new msgpack specs to only run with msgpack lib When msgpack lib is not present (extra group gems not installed), they will be ignored. --- spec/unit/indirector/catalog/msgpack_spec.rb | 2 +- spec/unit/indirector/msgpack_spec.rb | 2 +- spec/unit/indirector/node/msgpack_spec.rb | 2 +- spec/unit/indirector/report/msgpack_spec.rb | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/unit/indirector/catalog/msgpack_spec.rb b/spec/unit/indirector/catalog/msgpack_spec.rb index 4d611149a..f266c897d 100755 --- a/spec/unit/indirector/catalog/msgpack_spec.rb +++ b/spec/unit/indirector/catalog/msgpack_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' require 'puppet/resource/catalog' require 'puppet/indirector/catalog/msgpack' -describe Puppet::Resource::Catalog::Msgpack do +describe Puppet::Resource::Catalog::Msgpack, :if => Puppet.features.msgpack? do # This is it for local functionality: we don't *do* anything else. it "should be registered with the catalog store indirection" do Puppet::Resource::Catalog.indirection.terminus(:msgpack). diff --git a/spec/unit/indirector/msgpack_spec.rb b/spec/unit/indirector/msgpack_spec.rb index d11b17382..9391f94b0 100755 --- a/spec/unit/indirector/msgpack_spec.rb +++ b/spec/unit/indirector/msgpack_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' require 'puppet_spec/files' require 'puppet/indirector/indirector_testing/msgpack' -describe Puppet::Indirector::Msgpack do +describe Puppet::Indirector::Msgpack, :if => Puppet.features.msgpack? do include PuppetSpec::Files subject { Puppet::IndirectorTesting::Msgpack.new } diff --git a/spec/unit/indirector/node/msgpack_spec.rb b/spec/unit/indirector/node/msgpack_spec.rb index 76735b011..18ba50f97 100755 --- a/spec/unit/indirector/node/msgpack_spec.rb +++ b/spec/unit/indirector/node/msgpack_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' require 'puppet/node' require 'puppet/indirector/node/msgpack' -describe Puppet::Node::Msgpack do +describe Puppet::Node::Msgpack, :if => Puppet.features.msgpack? do it "should be a subclass of the Msgpack terminus" do Puppet::Node::Msgpack.superclass.should equal(Puppet::Indirector::Msgpack) end diff --git a/spec/unit/indirector/report/msgpack_spec.rb b/spec/unit/indirector/report/msgpack_spec.rb index a3494865a..20bc225af 100755 --- a/spec/unit/indirector/report/msgpack_spec.rb +++ b/spec/unit/indirector/report/msgpack_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' require 'puppet/transaction/report' require 'puppet/indirector/report/msgpack' -describe Puppet::Transaction::Report::Msgpack do +describe Puppet::Transaction::Report::Msgpack, :if => Puppet.features.msgpack? do it "should be a subclass of the Msgpack terminus" do Puppet::Transaction::Report::Msgpack.superclass.should equal(Puppet::Indirector::Msgpack) end From c73d5900e9a300103da23da452600a6693e122c5 Mon Sep 17 00:00:00 2001 From: Javier Palacios Date: Fri, 27 Sep 2013 14:39:43 +0200 Subject: [PATCH 639/800] (PUP-648) Implement upgradeable and versionable on the pkgin package provider Implemented the upgradeable and versionable features on the pkgin package provider so that packages can use `latest` and version numbers in the ensure property. --- lib/puppet/provider/package/pkgin.rb | 73 ++++++++++----- spec/unit/provider/package/pkgin_spec.rb | 109 ++++++++++++----------- 2 files changed, 104 insertions(+), 78 deletions(-) diff --git a/lib/puppet/provider/package/pkgin.rb b/lib/puppet/provider/package/pkgin.rb index 5061cd3c9..5cdb858d5 100644 --- a/lib/puppet/provider/package/pkgin.rb +++ b/lib/puppet/provider/package/pkgin.rb @@ -5,58 +5,83 @@ Puppet::Type.type(:package).provide :pkgin, :parent => Puppet::Provider::Package commands :pkgin => "pkgin" - defaultfor :operatingsystem => :dragonfly + defaultfor :operatingsystem => [ :dragonfly , :smartos ] - has_feature :installable, :uninstallable + has_feature :installable, :uninstallable, :upgradeable, :versionable - def self.parse_pkgin_line(package, force_status=nil) + def self.parse_pkgin_line(package) # e.g. # vim-7.2.446 = Vim editor (vi clone) without GUI match, name, version, status = *package.match(/(\S+)-(\S+)(?: (=|>|<))?\s+.+$/) if match - ensure_status = if force_status - force_status - elsif status - :present - else - :absent - end - { :name => name, - :ensure => ensure_status, - :provider => :pkgin + :status => status, + :ensure => version } end end + def self.prefetch(packages) + super + # Withouth -f, no fresh pkg_summary files are downloaded + pkgin("-yf", :update) + end + def self.instances pkgin(:list).split("\n").map do |package| - new(parse_pkgin_line(package, :present)) + new(parse_pkgin_line(package)) end end def query - packages = pkgin(:search, resource[:name]).split("\n") + packages = parse_pkgsearch_line - # Remove the last three lines of help text. - packages.slice!(-3, 3) - - matching_package = nil - packages.detect do |package| - properties = self.class.parse_pkgin_line(package) - matching_package = properties if properties && resource[:name] == properties[:name] + if packages.empty? + if @resource[:ensure] == :absent + notice "declared as absent but unavailable #{@resource.file}:#{resource.line}" + return false + else + @resource.fail "No candidate to be installed" + end end - matching_package + packages.first.update( :ensure => :absent ) + end + + def parse_pkgsearch_line + packages = pkgin(:search, resource[:name]).split("\n") + + return [] if packages.length == 1 + + # Remove the last three lines of help text. + packages.slice!(-4, 4) + + pkglist = packages.map{ |line| self.class.parse_pkgin_line(line) } + pkglist.select{ |package| resource[:name] == package[:name] } end def install - pkgin("-y", :install, resource[:name]) + if String === @resource[:ensure] + pkgin("-y", :install, "#{resource[:name]}-#{resource[:ensure]}") + else + pkgin("-y", :install, resource[:name]) + end end def uninstall pkgin("-y", :remove, resource[:name]) end + + def latest + package = parse_pkgsearch_line.detect{ |package| package[:status] == '<' } + return properties[:ensure] if not package + return package[:ensure] + end + + def update + pkgin("-y", :install, resource[:name]) + end + end diff --git a/spec/unit/provider/package/pkgin_spec.rb b/spec/unit/provider/package/pkgin_spec.rb index c62ab685a..fe897eb3d 100644 --- a/spec/unit/provider/package/pkgin_spec.rb +++ b/spec/unit/provider/package/pkgin_spec.rb @@ -3,7 +3,7 @@ require "spec_helper" provider_class = Puppet::Type.type(:package).provider(:pkgin) describe provider_class do - let(:resource) { Puppet::Type.type(:package).new(:name => "vim") } + let(:resource) { Puppet::Type.type(:package).new(:name => "vim", :ensure => "7.2.446") } subject { provider_class.new(resource) } describe "Puppet provider interface" do @@ -13,19 +13,27 @@ describe provider_class do end describe "#install" do - before { resource[:ensure] = :absent } + describe "a package not installed" do + before { resource[:ensure] = :absent } it "uses pkgin install to install" do - subject.expects(:pkgin).with("-y", :install, "vim") + subject.expects(:pkgin).with("-y", :install, "vim").once() subject.install end + end + + describe "a package with a fixed version" do + it "uses pkgin install to install a fixed version" do + subject.expects(:pkgin).with("-y", :install, "vim-7.2.446").once() + subject.install + end + end + end describe "#uninstall" do - before { resource[:ensure] = :present } - it "uses pkgin remove to uninstall" do - subject.expects(:pkgin).with("-y", :remove, "vim") + subject.expects(:pkgin).with("-y", :remove, "vim").once() subject.uninstall end end @@ -50,13 +58,13 @@ describe provider_class do it "populates each provider with an installed package" do zlib_provider, zziplib_provider = provider_class.instances zlib_provider.get(:name).should == "zlib" - zlib_provider.get(:ensure).should == :present + zlib_provider.get(:ensure).should == "1.2.3" zziplib_provider.get(:name).should == "zziplib" - zziplib_provider.get(:ensure).should == :present + zziplib_provider.get(:ensure).should == "0.13.59" end end - describe "#query" do + describe "#latest" do before do provider_class.stubs(:pkgin).with(:search, "vim").returns(pkgin_search_output) end @@ -66,60 +74,62 @@ describe provider_class do "vim-7.2.446 = Vim editor (vi clone) without GUI\nvim-share-7.2.446 = Data files for the vim editor (vi clone)\n\n=: package is installed and up-to-date\n<: package is installed but newer version is available\n>: installed package has a greater version than available package\n" end - it "returns a hash stating the package is present" do - result = subject.query - result[:ensure].should == :present - result[:name].should == "vim" - result[:provider].should == :pkgin + it "returns installed version" do + subject.expects(:properties).returns( { :ensure => "7.2.446" } ) + subject.latest.should == "7.2.446" end end context "when the package is out of date" do let(:pkgin_search_output) do - "vim-7.2.446 < Vim editor (vi clone) without GUI\nvim-share-7.2.446 = Data files for the vim editor (vi clone)\n\n=: package is installed and up-to-date\n<: package is installed but newer version is available\n>: installed package has a greater version than available package\n" + "vim-7.2.447 < Vim editor (vi clone) without GUI\nvim-share-7.2.447 < Data files for the vim editor (vi clone)\n\n=: package is installed and up-to-date\n<: package is installed but newer version is available\n>: installed package has a greater version than available package\n" end - it "returns a hash stating the package is present" do - result = subject.query - result[:ensure].should == :present - result[:name].should == "vim" - result[:provider].should == :pkgin + it "returns the version to be installed" do + subject.latest.should == "7.2.447" end end context "when the package is ahead of date" do let(:pkgin_search_output) do - "vim-7.2.446 > Vim editor (vi clone) without GUI\nvim-share-7.2.446 = Data files for the vim editor (vi clone)\n\n=: package is installed and up-to-date\n<: package is installed but newer version is available\n>: installed package has a greater version than available package\n" + "vim-7.2.446 > Vim editor (vi clone) without GUI\nvim-share-7.2.446 > Data files for the vim editor (vi clone)\n\n=: package is installed and up-to-date\n<: package is installed but newer version is available\n>: installed package has a greater version than available package\n" end - it "returns a hash stating the package is present" do - result = subject.query - result[:ensure].should == :present - result[:name].should == "vim" - result[:provider].should == :pkgin + it "returns current version" do + subject.expects(:properties).returns( { :ensure => "7.2.446" } ) + subject.latest.should == "7.2.446" end end - context "when the package is not installed" do + context "when multiple candidates do exists" do let(:pkgin_search_output) do - "vim-7.2.446 Vim editor (vi clone) without GUI\nvim-share-7.2.446 = Data files for the vim editor (vi clone)\n\n=: package is installed and up-to-date\n<: package is installed but newer version is available\n>: installed package has a greater version than available package\n" + <<-SEARCH +vim-7.1 > Vim editor (vi clone) without GUI +vim-share-7.1 > Data files for the vim editor (vi clone) +vim-7.2.446 = Vim editor (vi clone) without GUI +vim-share-7.2.446 = Data files for the vim editor (vi clone) +vim-7.3 < Vim editor (vi clone) without GUI +vim-share-7.3 < Data files for the vim editor (vi clone) + +=: package is installed and up-to-date +<: package is installed but newer version is available +>: installed package has a greater version than available package +SEARCH end - it "returns a hash stating the package is present" do - result = subject.query - result[:ensure].should == :absent - result[:name].should == "vim" - result[:provider].should == :pkgin + it "returns the newest available version" do + provider_class.stubs(:pkgin).with(:search, "vim").returns(pkgin_search_output) + subject.latest.should == "7.3" end end context "when the package cannot be found" do let(:pkgin_search_output) do - "\n=: package is installed and up-to-date\n<: package is installed but newer version is available\n>: installed package has a greater version than available package\n" + "No results found for is-puppet" end it "returns nil" do - subject.query.should be_nil + expect { subject.latest }.to raise_error(Puppet::Error, "No candidate to be installed") end end end @@ -129,21 +139,19 @@ describe provider_class do let(:package) { "vim-7.2.446 = Vim editor (vi clone) without GUI" } it "extracts the name and status" do - hash = provider_class.parse_pkgin_line(package) - hash[:name].should == "vim" - hash[:ensure].should == :present - hash[:provider].should == :pkgin + provider_class.parse_pkgin_line(package).should == { :name => "vim" , + :status => "=" , + :ensure => "7.2.446" } end end context "with an installed package with a hyphen in the name" do - let(:package) { "ruby18-puppet-0.25.5nb1 = Configuration management framework written in Ruby" } + let(:package) { "ruby18-puppet-0.25.5nb1 > Configuration management framework written in Ruby" } it "extracts the name and status" do - hash = provider_class.parse_pkgin_line(package) - hash[:name].should == "ruby18-puppet" - hash[:ensure].should == :present - hash[:provider].should == :pkgin + provider_class.parse_pkgin_line(package).should == { :name => "ruby18-puppet", + :status => ">" , + :ensure => "0.25.5nb1" } end end @@ -151,18 +159,11 @@ describe provider_class do let(:package) { "vim-7.2.446 Vim editor (vi clone) without GUI" } it "extracts the name and status" do - hash = provider_class.parse_pkgin_line(package) - hash[:name].should == "vim" - hash[:ensure].should == :absent - hash[:provider].should == :pkgin + provider_class.parse_pkgin_line(package).should == { :name => "vim" , + :status => nil , + :ensure => "7.2.446" } end - it "extracts the name and an overridden status" do - hash = provider_class.parse_pkgin_line(package, :present) - hash[:name].should == "vim" - hash[:ensure].should == :present - hash[:provider].should == :pkgin - end end context "with an invalid package" do From d2cfadafb7cab9aa1f9c53d49f170860056527c5 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Wed, 12 Feb 2014 23:57:41 +0100 Subject: [PATCH 640/800] (PUP-1619) Fix typo in Tuple instance_of logic --- lib/puppet/pops/types/type_calculator.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/puppet/pops/types/type_calculator.rb b/lib/puppet/pops/types/type_calculator.rb index 9c262c3ae..7eb70d655 100644 --- a/lib/puppet/pops/types/type_calculator.rb +++ b/lib/puppet/pops/types/type_calculator.rb @@ -385,8 +385,8 @@ class Puppet::Pops::Types::TypeCalculator # compute the tuple's min/max size, and check if that size matches from, to = size_range(t.size_type) size_t = Types::PIntegerType.new() - size_t.from = t2.types.size - 1 + from - size_t.to = t2.types.size - 1 + to + size_t.from = t.types.size - 1 + from + size_t.to = t.types.size - 1 + to # compute the array's size as type size_t2 = size_as_type(o) return false unless assignable?(size_t, size_t2) From 728d836355e5b1a666f0fddcd980829e807e82e2 Mon Sep 17 00:00:00 2001 From: Adrien Thebo Date: Wed, 12 Feb 2014 15:22:50 -0800 Subject: [PATCH 641/800] (maint) Add missing noun that was accidentally --- docs/rspec_tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rspec_tutorial.md b/docs/rspec_tutorial.md index 1f8630fbb..c22e5c6ad 100644 --- a/docs/rspec_tutorial.md +++ b/docs/rspec_tutorial.md @@ -145,7 +145,7 @@ describe "something that could warn" do it "doesn't generate a warning" do MY_CONSTANT = 1 - # reassigning a normally prints out 'warning: already initialized constant FOO' + # reassigning a constant normally prints out 'warning: already initialized constant FOO' MY_CONSTANT = 2 end end From c54a95d74162a5fa4b756b6fc8479eaab5ff0318 Mon Sep 17 00:00:00 2001 From: Felix Frank Date: Sun, 1 Dec 2013 12:52:45 +0100 Subject: [PATCH 642/800] (PUP-1220) apply proper scoping rules to scope#to_hash The fix for #1427 added an instance method to the scope class for exporting all variables from the local scope. This method apparently re-implemented the dynamic scoping rules. When those were removed, the #to_hash method was left as was. As a result, dynamic scoping still worked from within templates, when using the @variable syntax in the erb code. Fix this by using the new #enclosing_scope method so that proper scoping rules are used instead. --- lib/puppet/parser/scope.rb | 4 +-- spec/integration/parser/scope_spec.rb | 36 +++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb index 95b63d438..67dcacc39 100644 --- a/lib/puppet/parser/scope.rb +++ b/lib/puppet/parser/scope.rb @@ -500,8 +500,8 @@ class Puppet::Parser::Scope # shadow parent values. Ephemeral scopes for match results ($0 - $n) are not included. # def to_hash(recursive = true) - if recursive and parent - target = parent.to_hash(recursive) + if recursive and has_enclosing_scope? + target = enclosing_scope.to_hash(recursive) else target = Hash.new end diff --git a/spec/integration/parser/scope_spec.rb b/spec/integration/parser/scope_spec.rb index 8692323f8..c305ff7b3 100644 --- a/spec/integration/parser/scope_spec.rb +++ b/spec/integration/parser/scope_spec.rb @@ -574,6 +574,42 @@ describe "Two step scoping for variables" do MANIFEST end end + + context "when using a template" do + it "ignores the dynamic value of the var when using scope.lookupvar" do + expect_the_message_to_be('node_msg') do <<-MANIFEST + node default { + $var = "node_msg" + include foo + } + class foo { + $var = "foo_msg" + include bar + } + class bar { + notify { 'something': message => inline_template("<%= scope.lookupvar('var') %>"), } + } + MANIFEST + end + end + it "ignores the dynamic value of the var when using the @varname syntax" do + expect_the_message_to_be('node_msg') do <<-MANIFEST + node default { + $var = "node_msg" + include foo + } + class foo { + $var = "foo_msg" + include bar + } + class bar { + notify { 'something': message => inline_template("<%= @var %>"), } + } + MANIFEST + end + end + end + end describe "using plussignment to change in a new scope" do From 29cff9d91866c45e2c5cd1ab78395ae7783c9987 Mon Sep 17 00:00:00 2001 From: Felix Frank Date: Wed, 25 Dec 2013 19:53:28 +0100 Subject: [PATCH 643/800] (PUP-1220) make the dynamic scoping fix for #22800 flexible As suggested by Henrik Lindberg, make the parser only behave correctly when parser=future is configured. If working manifests rely on the regression, the next point release won't break them. Instead, users can test for this using the configuration setting. The next major release should adopt the fix as standard behavior and the legacy support should be removed. Since the variable lookups in templates relies on the accessing of local attributes, there is no way to issue deprecation warnings to users. --- lib/puppet/parser/scope.rb | 33 ++++++++++++++ spec/integration/parser/scope_spec.rb | 64 +++++++++++++-------------- 2 files changed, 64 insertions(+), 33 deletions(-) diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb index 67dcacc39..4341ee10c 100644 --- a/lib/puppet/parser/scope.rb +++ b/lib/puppet/parser/scope.rb @@ -499,7 +499,24 @@ class Puppet::Parser::Scope # by default) including the values defined in parent. Local values # shadow parent values. Ephemeral scopes for match results ($0 - $n) are not included. # + # This is currently a wrapper for to_hash_legacy or to_hash_future. + # + # @see to_hash_future + # + # @see to_hash_legacy def to_hash(recursive = true) + if Puppet['parser'] == 'future' + to_hash_future(recursive) + else + to_hash_legacy(recursive) + end + end + + # Fixed version of to_hash that implements scoping correctly (i.e., with + # dynamic scoping disabled #28200 / PUP-1220 + # + # @see to_hash + def to_hash_future(recursive) if recursive and has_enclosing_scope? target = enclosing_scope.to_hash(recursive) else @@ -511,6 +528,22 @@ class Puppet::Parser::Scope target end + # The old broken implementation of to_hash that retains the dynamic scoping + # semantics + # + # @see to_hash + def to_hash_legacy(recursive = true) + if recursive and parent + target = parent.to_hash(recursive) + else + target = Hash.new + end + + # add all local scopes + @ephemeral.last.add_entries_to(target) + target + end + def namespaces @namespaces.dup end diff --git a/spec/integration/parser/scope_spec.rb b/spec/integration/parser/scope_spec.rb index c305ff7b3..6158b09ff 100644 --- a/spec/integration/parser/scope_spec.rb +++ b/spec/integration/parser/scope_spec.rb @@ -58,6 +58,23 @@ describe "Two step scoping for variables" do end.to raise_error(/The value 'top_msg' cannot be converted to Numeric/) end end + + it "when using a template ignores the dynamic value of the var when using the @varname syntax" do + expect_the_message_to_be('node_msg') do <<-MANIFEST + node default { + $var = "node_msg" + include foo + } + class foo { + $var = "foo_msg" + include bar + } + class bar { + notify { 'something': message => inline_template("<%= @var %>"), } + } + MANIFEST + end + end end shared_examples_for "the scope" do @@ -575,41 +592,22 @@ describe "Two step scoping for variables" do end end - context "when using a template" do - it "ignores the dynamic value of the var when using scope.lookupvar" do - expect_the_message_to_be('node_msg') do <<-MANIFEST - node default { - $var = "node_msg" - include foo - } - class foo { - $var = "foo_msg" - include bar - } - class bar { - notify { 'something': message => inline_template("<%= scope.lookupvar('var') %>"), } - } - MANIFEST - end - end - it "ignores the dynamic value of the var when using the @varname syntax" do - expect_the_message_to_be('node_msg') do <<-MANIFEST - node default { - $var = "node_msg" - include foo - } - class foo { - $var = "foo_msg" - include bar - } - class bar { - notify { 'something': message => inline_template("<%= @var %>"), } - } - MANIFEST - end + it "when using a template ignores the dynamic value of the var when using scope.lookupvar" do + expect_the_message_to_be('node_msg') do <<-MANIFEST + node default { + $var = "node_msg" + include foo + } + class foo { + $var = "foo_msg" + include bar + } + class bar { + notify { 'something': message => inline_template("<%= scope.lookupvar('var') %>"), } + } + MANIFEST end end - end describe "using plussignment to change in a new scope" do From 51b873162c954b3fa70d10a72158983425220eb6 Mon Sep 17 00:00:00 2001 From: Felix Frank Date: Thu, 13 Feb 2014 01:16:39 +0100 Subject: [PATCH 644/800] (PUP-1220) optimize template scoping fix Following enhancements as suggested by Erik Dalen, Andy Parker et al. * prefer Puppet[:parser] over Puppet['parser'] * cache the config value of Puppet[:parser] * pick the future vs. legacy branch only once per template --- lib/puppet/parser/scope.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb index 4341ee10c..dc78ff5e3 100644 --- a/lib/puppet/parser/scope.rb +++ b/lib/puppet/parser/scope.rb @@ -505,7 +505,8 @@ class Puppet::Parser::Scope # # @see to_hash_legacy def to_hash(recursive = true) - if Puppet['parser'] == 'future' + @parser ||= Puppet[:parser] + if @parser == 'future' to_hash_future(recursive) else to_hash_legacy(recursive) @@ -518,7 +519,7 @@ class Puppet::Parser::Scope # @see to_hash def to_hash_future(recursive) if recursive and has_enclosing_scope? - target = enclosing_scope.to_hash(recursive) + target = enclosing_scope.to_hash_future(recursive) else target = Hash.new end @@ -534,7 +535,7 @@ class Puppet::Parser::Scope # @see to_hash def to_hash_legacy(recursive = true) if recursive and parent - target = parent.to_hash(recursive) + target = parent.to_hash_legacy(recursive) else target = Hash.new end From 857291ed0c64119dd13b44934ecab4d11b5f1e60 Mon Sep 17 00:00:00 2001 From: "Ethan J. Brown" Date: Wed, 12 Feb 2014 16:18:15 -0800 Subject: [PATCH 645/800] PUP-1681 Stat doesn't expose correct mode on Win - An issue was discovered where the values returned from Puppet::FileSystem.stat and Puppet::Util::Windows::Security.get_mode were not aligning as they should. The call to get_mode returns a mode value that should more correctly express a simulated POSIX mode on Windows - The stat instance returned from Ruby has been further monkey-patched on Windows to add an appropriate mode value using the existing lower-level code that reads a files security descriptor. - Some tests that were performing special handling of mode values were updated now that mode should be more consistent across platforms. - In some cases, an existing call to File.chmod was changed to call set_mode under Windows. In the future, our FileSystem abstraction should be modified to create an OS-agnostic single point of entry for setting mode on files. --- lib/puppet/file_system.rb | 16 +++++++++++++++- lib/puppet/file_system/file19windows.rb | 4 ++++ lib/puppet/file_system/file_impl.rb | 3 +++ lib/puppet/util/windows/file.rb | 19 +++++++++++++++++-- spec/integration/configurer_spec.rb | 3 ++- spec/integration/type/file_spec.rb | 6 ++++-- spec/integration/type/nagios_spec.rb | 4 +++- spec/integration/util/settings_spec.rb | 2 +- spec/unit/type/file/mode_spec.rb | 3 ++- 9 files changed, 51 insertions(+), 9 deletions(-) diff --git a/lib/puppet/file_system.rb b/lib/puppet/file_system.rb index c8312a289..ab4873eb1 100644 --- a/lib/puppet/file_system.rb +++ b/lib/puppet/file_system.rb @@ -232,7 +232,7 @@ module Puppet::FileSystem end # @return [Boolean] true if the file is a symbolic link. - # + # # @api public # def self.symlink?(path) @@ -340,4 +340,18 @@ module Puppet::FileSystem def self.exclusive_create(path, mode, &block) @impl.exclusive_create(assert_path(path), mode, &block) end + + # Changes permission bits on the named path to the bit pattern represented + # by mode. + # + # @param mode [Integer] The mode to apply to the file if it is created + # @param path [String] The path to the file, can also accept [PathName] + # + # @raise [Errno::ENOENT]: path doesn't exist + # + # @api public + # + def self.chmod(mode, path) + @impl.chmod(mode, path) + end end diff --git a/lib/puppet/file_system/file19windows.rb b/lib/puppet/file_system/file19windows.rb index 1b33bcad1..bf114bc97 100644 --- a/lib/puppet/file_system/file19windows.rb +++ b/lib/puppet/file_system/file19windows.rb @@ -91,6 +91,10 @@ class Puppet::FileSystem::File19Windows < Puppet::FileSystem::File19 Puppet::Util::Windows::File.lstat(path) end + def chmod(mode, path) + Puppet::Util::Windows::Security.set_mode(mode, path.to_s) + end + private def raise_if_symlinks_unsupported diff --git a/lib/puppet/file_system/file_impl.rb b/lib/puppet/file_system/file_impl.rb index bf8ff31e5..f682a1752 100644 --- a/lib/puppet/file_system/file_impl.rb +++ b/lib/puppet/file_system/file_impl.rb @@ -135,4 +135,7 @@ class Puppet::FileSystem::FileImpl open(path, 0, 'rb') { |this| FileUtils.compare_stream(this, stream) } end + def chmod(mode, path) + FileUtils.chmod(mode, path) + end end diff --git a/lib/puppet/util/windows/file.rb b/lib/puppet/util/windows/file.rb index 6e124b812..15a6ef469 100644 --- a/lib/puppet/util/windows/file.rb +++ b/lib/puppet/util/windows/file.rb @@ -219,14 +219,23 @@ module Puppet::Util::Windows::File def stat(file_name) file_name = file_name.to_s # accomodate PathName or String stat = File.stat(file_name) + singleton_class = class << stat; self; end + target_path = file_name + if symlink?(file_name) - link_ftype = File.stat(readlink(file_name)).ftype + target_path = readlink(file_name) + link_ftype = File.stat(target_path).ftype + # sigh, monkey patch instance method for instance, and close over link_ftype - singleton_class = class << stat; self; end singleton_class.send(:define_method, :ftype) do link_ftype end end + + singleton_class.send(:define_method, :mode) do + Puppet::Util::Windows::Security.get_mode(target_path) + end + stat end module_function :stat @@ -235,6 +244,12 @@ module Puppet::Util::Windows::File file_name = file_name.to_s # accomodate PathName or String # monkey'ing around! stat = File.lstat(file_name) + + singleton_class = class << stat; self; end + singleton_class.send(:define_method, :mode) do + Puppet::Util::Windows::Security.get_mode(file_name) + end + if symlink?(file_name) def stat.ftype "link" diff --git a/spec/integration/configurer_spec.rb b/spec/integration/configurer_spec.rb index 5e65355c5..99eaeaefd 100755 --- a/spec/integration/configurer_spec.rb +++ b/spec/integration/configurer_spec.rb @@ -60,7 +60,8 @@ describe Puppet::Configurer do @configurer.run :catalog => @catalog, :report => report t2 = Time.now.tv_sec - file_mode = Puppet.features.microsoft_windows? ? '100644' : '100666' + # sticky bit only applies to directories in windows + file_mode = Puppet.features.microsoft_windows? ? '666' : '100666' Puppet::FileSystem.stat(Puppet[:lastrunfile]).mode.to_s(8).should == file_mode diff --git a/spec/integration/type/file_spec.rb b/spec/integration/type/file_spec.rb index cf494ba2f..f864fe06a 100755 --- a/spec/integration/type/file_spec.rb +++ b/spec/integration/type/file_spec.rb @@ -886,13 +886,14 @@ describe Puppet::Type.type(:file) do it "should be able to copy files with spaces in their names" do dest = tmpfile("destwith spaces") source = tmpfile_with_contents("filewith spaces", "foo") - File.chmod(0755, source) + + expected_mode = 0755 + Puppet::FileSystem.chmod(expected_mode, source) catalog.add_resource described_class.new(:path => dest, :source => source) catalog.apply - expected_mode = Puppet.features.microsoft_windows? ? 0644 : 0755 File.read(dest).should == "foo" (Puppet::FileSystem.stat(dest).mode & 007777).should == expected_mode end @@ -1021,6 +1022,7 @@ describe Puppet::Type.type(:file) do end it "should provide valid default values when ACLs are not supported" do + Puppet::Util::Windows::Security.stubs(:supports_acl?).returns(false) Puppet::Util::Windows::Security.stubs(:supports_acl?).with(source).returns false file = described_class.new( diff --git a/spec/integration/type/nagios_spec.rb b/spec/integration/type/nagios_spec.rb index 2746fecb3..818b61649 100644 --- a/spec/integration/type/nagios_spec.rb +++ b/spec/integration/type/nagios_spec.rb @@ -51,7 +51,9 @@ describe "Nagios file creation" do :mode => '0640' ) run_in_catalog(resource) - ( "%o" % get_mode(target_file) ).should == "100640" + # sticky bit only applies to directories in Windows + mode = Puppet.features.microsoft_windows? ? "640" : "100640" + ( "%o" % get_mode(target_file) ).should == mode end end diff --git a/spec/integration/util/settings_spec.rb b/spec/integration/util/settings_spec.rb index 783f56032..a76ade637 100755 --- a/spec/integration/util/settings_spec.rb +++ b/spec/integration/util/settings_spec.rb @@ -41,7 +41,7 @@ describe Puppet::Settings do settings.use(:main) - expect(Puppet::FileSystem.stat(settings[:maindir]).mode & 007777).to eq(Puppet.features.microsoft_windows? ? 0755 : 0750) + expect(Puppet::FileSystem.stat(settings[:maindir]).mode & 007777).to eq(0750) end it "reparses configuration if configuration file is touched", :if => !Puppet.features.microsoft_windows? do diff --git a/spec/unit/type/file/mode_spec.rb b/spec/unit/type/file/mode_spec.rb index 3f2dd90bb..9936ebdbc 100755 --- a/spec/unit/type/file/mode_spec.rb +++ b/spec/unit/type/file/mode_spec.rb @@ -185,7 +185,8 @@ describe Puppet::Type.type(:file).attrclass(:mode) do it "changes only the requested bits" do # lower nibble must be set to 4 for the sake of passing on Windows - FileUtils.chmod 0464, path + Puppet::FileSystem.chmod(0464, path) + mode_sym.sync stat = Puppet::FileSystem.stat(path) (stat.mode & 0777).to_s(8).should == "644" From 5820d4cdf9715e603df08afbe593034ca778ed25 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Wed, 12 Feb 2014 21:25:29 -0800 Subject: [PATCH 646/800] (PUP-1681) Always go through the native windows methods Previously, on 2003 systems where symlinks are not supported, the call to Puppet::FileSystem.stat would delegate to File.stat, which does not return the emulated POSIX mode. Instead we always want to delegate to Puppet::Util::Windows::File.stat. This was caught by existing tests, so new tests have been added. --- lib/puppet/file_system/file19windows.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/puppet/file_system/file19windows.rb b/lib/puppet/file_system/file19windows.rb index bf114bc97..7ebba2cf4 100644 --- a/lib/puppet/file_system/file19windows.rb +++ b/lib/puppet/file_system/file19windows.rb @@ -78,9 +78,6 @@ class Puppet::FileSystem::File19Windows < Puppet::FileSystem::File19 end def stat(path) - if ! Puppet.features.manages_symlinks? - return super - end Puppet::Util::Windows::File.stat(path) end From 5b8e79a51d466d1bdb0b86af24430c4705da1aa0 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 13 Feb 2014 16:05:41 +0100 Subject: [PATCH 647/800] (PUP-1619) Add instance? tests for struct and tuple These tests were missing in the previous commits. --- lib/puppet/pops/types/type_calculator.rb | 1 + spec/unit/pops/types/type_calculator_spec.rb | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/lib/puppet/pops/types/type_calculator.rb b/lib/puppet/pops/types/type_calculator.rb index 7eb70d655..7ff8096cd 100644 --- a/lib/puppet/pops/types/type_calculator.rb +++ b/lib/puppet/pops/types/type_calculator.rb @@ -393,6 +393,7 @@ class Puppet::Pops::Types::TypeCalculator o.each_with_index do |element, index| return false unless instance_of(t.types[index] || t.types[-1], element) end + true end def instance_of_PStructType(t, o) diff --git a/spec/unit/pops/types/type_calculator_spec.rb b/spec/unit/pops/types/type_calculator_spec.rb index 518f63025..d3c51662f 100644 --- a/spec/unit/pops/types/type_calculator_spec.rb +++ b/spec/unit/pops/types/type_calculator_spec.rb @@ -978,6 +978,22 @@ describe 'The type calculator' do calculator.instance?(array, ['XS', 'S', 29]).should == false end + it 'should consider array[seq] as instance of Tuple[seq] when elements of seq are instance of' do + tuple = tuple_t(Integer, String, Float) + calculator.instance?(tuple, [1, 'a', 3.14]).should == true + calculator.instance?(tuple, [1.2, 'a', 3.14]).should == false + calculator.instance?(tuple, [1, 1, 3.14]).should == false + calculator.instance?(tuple, [1, 'a', 1]).should == false + end + + it 'should consider hash[cont] as instance of Struct[cont-t]' do + struct = struct_t({'a'=>Integer, 'b'=>String, 'c'=>Float}) + calculator.instance?(struct, {'a'=>1, 'b'=>'a', 'c'=>3.14}).should == true + calculator.instance?(struct, {'a'=>1.2, 'b'=>'a', 'c'=>3.14}).should == false + calculator.instance?(struct, {'a'=>1, 'b'=>1, 'c'=>3.14}).should == false + calculator.instance?(struct, {'a'=>1, 'b'=>'a', 'c'=>1}).should == false + end + context 'and t is Data' do it 'undef should be considered instance of Data' do calculator.instance?(data_t, :undef).should == true From 18a87a4c6ea93cd30dc6db2e4577f205c732edfb Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Thu, 13 Feb 2014 13:59:12 -0800 Subject: [PATCH 648/800] (maint) Fix pkgin package provider spec to ensure resource uses the pkgin provider. GH-2023 included a change that added a version onto the resource. However, since a provider was not declared, it used the system's default provider. Because of this, running the spec on a system with a default provider that doesn't supoprt the versionable feature would cause the spec to fail. The fix is to set the provider on the resource and move the ensured version to the particular test case. --- spec/unit/provider/package/pkgin_spec.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spec/unit/provider/package/pkgin_spec.rb b/spec/unit/provider/package/pkgin_spec.rb index fe897eb3d..8b6d4e51d 100644 --- a/spec/unit/provider/package/pkgin_spec.rb +++ b/spec/unit/provider/package/pkgin_spec.rb @@ -3,8 +3,8 @@ require "spec_helper" provider_class = Puppet::Type.type(:package).provider(:pkgin) describe provider_class do - let(:resource) { Puppet::Type.type(:package).new(:name => "vim", :ensure => "7.2.446") } - subject { provider_class.new(resource) } + let(:resource) { Puppet::Type.type(:package).new(:name => "vim", :provider => :pkgin) } + subject { resource.provider } describe "Puppet provider interface" do it "can return the list of all packages" do @@ -23,6 +23,7 @@ describe provider_class do end describe "a package with a fixed version" do + before { resource[:ensure] = '7.2.446' } it "uses pkgin install to install a fixed version" do subject.expects(:pkgin).with("-y", :install, "vim-7.2.446").once() subject.install From 96fc0cf46510224c68d838a968e365eb0d7d547e Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 14 Feb 2014 00:22:59 +0100 Subject: [PATCH 649/800] (maint) Add missing class method instance? to TypeCalculator --- lib/puppet/pops/types/type_calculator.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/puppet/pops/types/type_calculator.rb b/lib/puppet/pops/types/type_calculator.rb index 7ff8096cd..b716cc350 100644 --- a/lib/puppet/pops/types/type_calculator.rb +++ b/lib/puppet/pops/types/type_calculator.rb @@ -431,6 +431,13 @@ class Puppet::Pops::Types::TypeCalculator t.types.any? { |option_t| instance_of(option_t, o) } end + # Answers 'is o an instance of type t' + # @api public + # + def self.instance?(t, o) + singleton.instance_of(t,o) + end + # Answers 'is o an instance of type t' # @api public # From c1a17ff8015a15ef72c8579a8a418232b362ba6c Mon Sep 17 00:00:00 2001 From: John Julien Date: Mon, 4 Nov 2013 06:40:13 -0600 Subject: [PATCH 650/800] (PUP-1510) Fixed forcelocal bug with ensure => absent The forcelocal option was referencing a command that was not previously defined which was throwing an exception. The forcelocal feature does not need to use a special command to delete local users, only to add them. So the special logic for delete was removed and forcelocal now uses userdel to ensure absent on a user. --- lib/puppet/provider/user/useradd.rb | 6 +----- spec/unit/provider/user/useradd_spec.rb | 7 +++++++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/puppet/provider/user/useradd.rb b/lib/puppet/provider/user/useradd.rb index 10c4060a9..dd2d24b7d 100644 --- a/lib/puppet/provider/user/useradd.rb +++ b/lib/puppet/provider/user/useradd.rb @@ -186,11 +186,7 @@ Puppet::Type.type(:user).provide :useradd, :parent => Puppet::Provider::NameServ end def deletecmd - if @resource.forcelocal? - cmd = [command(:localdelete)] - else - cmd = [command(:delete)] - end + cmd = [command(:delete)] cmd += @resource.managehome? ? ['-r'] : [] cmd << @resource[:name] end diff --git a/spec/unit/provider/user/useradd_spec.rb b/spec/unit/provider/user/useradd_spec.rb index 9d42017a7..6188dca84 100755 --- a/spec/unit/provider/user/useradd_spec.rb +++ b/spec/unit/provider/user/useradd_spec.rb @@ -125,6 +125,13 @@ describe Puppet::Type.type(:user).provider(:useradd) do provider.expects(:execute).with(all_of(includes('/usr/sbin/usermod'), includes('-e'))) provider.create end + + it "should use userdel to delete users" do + resource[:ensure] = :absent + provider.stubs(:exists?).returns(true) + provider.expects(:execute).with(includes('/usr/sbin/userdel')) + provider.delete + end end describe "on systems that allow to set shell" do From 6d26a2092fafea133113d016e1463133e6ce5e48 Mon Sep 17 00:00:00 2001 From: Nick Fagerlund Date: Thu, 13 Feb 2014 17:26:03 -0800 Subject: [PATCH 651/800] (maint) Revise and reformat docs for validate_cmd parameter --- lib/puppet/type/file.rb | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/lib/puppet/type/file.rb b/lib/puppet/type/file.rb index 1b4249b86..9351bc0fd 100644 --- a/lib/puppet/type/file.rb +++ b/lib/puppet/type/file.rb @@ -135,7 +135,7 @@ Puppet::Type.newtype(:file) do but not the local (destination) directory. Allows copying of a few files into a directory containing many unmanaged files without scanning all the local files. - This can only be used when a source parameter is specified. + This can only be used when a source parameter is specified. * `false` --- Default of no recursion. " @@ -257,25 +257,33 @@ Puppet::Type.newtype(:file) do end newparam(:validate_cmd) do - desc "If this parameter is set, then this file will only be written if - the command returns 0. For example: + desc "A command for validating the file's syntax before replacing it. If + Puppet would need to rewrite a file due to new `source` or `content`, it + will check the new content's validity first. If validation fails, the file + resource will fail. - file { '/etc/apache2/apache2.conf': - content => 'example' - validate_cmd => '/usr/sbin/apache2 -t -f %' - } + This command must have a fully qualified path, and should contain a + percent (`%`) token where it would expect an input file. It must exit `0` + if the syntax is correct, and non-zero otherwise. The command will be + run on the target system while applying the catalog, not on the puppet master. - This would replace apache2.conf only if that test returned true. + Example: - The percent sign (%) will be replaced with the path of the temporary - file for this resource. + file { '/etc/apache2/apache2.conf': + content => 'example', + validate_cmd => '/usr/sbin/apache2 -t -f %', + } - Note that the command must be fully qualified." + This would replace apache2.conf only if the test returned true. + + Note that if a validation command requires a `%` as part of its text, + you can specify a different placeholder token with the + `validate_replacement` attribute." end newparam(:validate_replacement) do - desc "The replacement string that `validate_cmd` uses to replace - with the file name. Defaults to: `%`" + desc "The replacement string in a `validate_cmd` that will be replaced + with an input file name. Defaults to: `%`" defaultto '%' end From a76d681946642e916e93a374d8e055b44bab2898 Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Thu, 13 Feb 2014 18:33:07 -0800 Subject: [PATCH 652/800] (maint) Fix yumrepo inifile spec to stub the correct method The spec stubs File::exist? which does not take into account the various implementations in Puppet::FileSystem. The spec should instead stub Puppet::FileSystem::exist so that it behaves consistently on all platforms. --- spec/unit/provider/yumrepo/inifile_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/unit/provider/yumrepo/inifile_spec.rb b/spec/unit/provider/yumrepo/inifile_spec.rb index fae5ff28d..33d7abfdd 100644 --- a/spec/unit/provider/yumrepo/inifile_spec.rb +++ b/spec/unit/provider/yumrepo/inifile_spec.rb @@ -30,7 +30,7 @@ descr="test updates" before :each do described_class.stubs(:reposdir).returns(['/etc/yum.repos.d']) File.expects(:file?).with('/etc/yum.repos.d/test.repo').returns(true) - File.expects(:exist?).with(Pathname.new('/etc/yum.repos.d/test.repo')).returns(true) + Puppet::FileSystem.expects(:exist?).with('/etc/yum.repos.d/test.repo').returns(true) File.expects(:read).with('/etc/yum.repos.d/test.repo').returns(repo_file) end From a0e98829d5a14895d6ce974115da905a3b525793 Mon Sep 17 00:00:00 2001 From: Adrien Thebo Date: Fri, 14 Feb 2014 09:47:22 -0800 Subject: [PATCH 653/800] (maint) Implement FileSystem.file? for consistency The filesystem abstraction implements `.directory?` but not `.file?` which is somewhat inconsistent. This commit adds that method and uses the same behavior implemented by `.directory?`. --- lib/puppet/file_system.rb | 9 +++++++++ lib/puppet/file_system/file_impl.rb | 4 ++++ lib/puppet/file_system/memory_impl.rb | 4 ++++ spec/unit/file_system_spec.rb | 4 ++++ 4 files changed, 21 insertions(+) diff --git a/lib/puppet/file_system.rb b/lib/puppet/file_system.rb index ab4873eb1..6db15ee37 100644 --- a/lib/puppet/file_system.rb +++ b/lib/puppet/file_system.rb @@ -157,6 +157,15 @@ module Puppet::FileSystem @impl.directory?(assert_path(path)) end + # Determines if a file is a file. + # + # @return [Boolean] true if the given file is a file. + # + # @api public + def self.file?(path) + @impl.file?(assert_path(path)) + end + # Determines if a file is executable. # # @todo Should this take into account extensions on the windows platform? diff --git a/lib/puppet/file_system/file_impl.rb b/lib/puppet/file_system/file_impl.rb index f682a1752..c87bb4603 100644 --- a/lib/puppet/file_system/file_impl.rb +++ b/lib/puppet/file_system/file_impl.rb @@ -87,6 +87,10 @@ class Puppet::FileSystem::FileImpl ::File.directory?(path) end + def file?(path) + ::File.file?(path) + end + def executable?(path) ::File.executable?(path) end diff --git a/lib/puppet/file_system/memory_impl.rb b/lib/puppet/file_system/memory_impl.rb index 314d2a5f1..19abf66a6 100644 --- a/lib/puppet/file_system/memory_impl.rb +++ b/lib/puppet/file_system/memory_impl.rb @@ -11,6 +11,10 @@ class Puppet::FileSystem::MemoryImpl path.directory? end + def file?(path) + path.file? + end + def executable?(path) path.executable? end diff --git a/spec/unit/file_system_spec.rb b/spec/unit/file_system_spec.rb index 9118030df..f3b36a66d 100644 --- a/spec/unit/file_system_spec.rb +++ b/spec/unit/file_system_spec.rb @@ -149,6 +149,10 @@ describe "Puppet::FileSystem" do Puppet::FileSystem.exist?(file).should be_true end + it "should return true for file? on a present file" do + Puppet::FileSystem.file?(file).should be_true + end + it "should return false for exist? on a non-existant file" do Puppet::FileSystem.exist?(missing_file).should be_false end From d032a1646413d17caeb792fb59c7ae2d8a4fc41a Mon Sep 17 00:00:00 2001 From: Adrien Thebo Date: Fri, 14 Feb 2014 10:43:32 -0800 Subject: [PATCH 654/800] (maint) fixup yumrepo provider to use fs abstraction There were a few places where the filesystem abstraction was being ignored; this commit corrects those cases. This commit also refactors the associated tesets and simplifies #create for easier testing. --- lib/puppet/provider/yumrepo/inifile.rb | 12 +- spec/unit/provider/yumrepo/inifile_spec.rb | 142 ++++++++++----------- 2 files changed, 75 insertions(+), 79 deletions(-) diff --git a/lib/puppet/provider/yumrepo/inifile.rb b/lib/puppet/provider/yumrepo/inifile.rb index 7bffedcc1..4882dedb8 100644 --- a/lib/puppet/provider/yumrepo/inifile.rb +++ b/lib/puppet/provider/yumrepo/inifile.rb @@ -61,8 +61,8 @@ Puppet::Type.type(:yumrepo).provide(:inifile) do # @param conf [String] Configuration file to check for value. # @return [String] The value of a looked up key from the configuration file. def self.find_conf_value(value, conf='/etc/yum.conf') - if File.exists?(conf) - contents = File.read(conf) + if Puppet::FileSystem.exist?(conf) + contents = Puppet::FileSystem.read(conf) match = /^#{value}\s*=\s*(.*)/.match(contents) end @@ -78,7 +78,7 @@ Puppet::Type.type(:yumrepo).provide(:inifile) do @virtual = Puppet::Util::IniConfig::File.new reposdir.each do |dir| Dir.glob("#{dir}/*.repo").each do |file| - @virtual.read(file) if ::File.file?(file) + @virtual.read(file) if Puppet::FileSystem.file?(file) end end end @@ -123,7 +123,7 @@ Puppet::Type.type(:yumrepo).provide(:inifile) do current_mode = Puppet::FileSystem.stat(file).mode & 0777 unless current_mode == target_mode Puppet.info "changing mode of #{file} from %03o to %03o" % [current_mode, target_mode] - ::File.chmod(target_mode, file) + Puppet::FileSystem.chmod(target_mode, file) end end end @@ -132,6 +132,8 @@ Puppet::Type.type(:yumrepo).provide(:inifile) do def create @property_hash[:ensure] = :present + new_section = section(@resource[:name]) + # We fetch a list of properties from the type, then iterate # over them, avoiding ensure. We're relying on .should to # check if the property has been set and should be modified, @@ -139,7 +141,7 @@ Puppet::Type.type(:yumrepo).provide(:inifile) do PROPERTIES.each do |property| next if property == :ensure if value = @resource.should(property) - section(@resource[:name])[property.to_s] = value + new_section[property.to_s] = value @property_hash[property] = value end end diff --git a/spec/unit/provider/yumrepo/inifile_spec.rb b/spec/unit/provider/yumrepo/inifile_spec.rb index 33d7abfdd..ef3beb8a1 100644 --- a/spec/unit/provider/yumrepo/inifile_spec.rb +++ b/spec/unit/provider/yumrepo/inifile_spec.rb @@ -1,111 +1,105 @@ require 'spec_helper' -require 'puppet' describe Puppet::Type.type(:yumrepo).provider(:inifile) do - let(:yumrepo) { - Puppet::Type.type(:yumrepo).new( - :name => 'puppetlabs-products', - :ensure => :present, - :baseurl => 'http://yum.puppetlabs.com/el/6/products/$basearch', - :descr => 'Puppet Labs Products El 6 - $basearch', - :enabled => '1', - :gpgcheck => '1', - :gpgkey => 'file:///etc/pki/rpm-gpg/RPM-GPG-KEY-puppetlabs' - ) - } - let(:yumrepo_provider) { yumrepo.provider } - let(:repo_file) { ' -[updates] -name="updates" -enabled = 1 -descr="test updates" -' - } + + let(:virtual_inifile) { stub('virtual inifile') } before :each do - Dir.stubs(:glob).with('/etc/yum.repos.d/*.repo').returns(['/etc/yum.repos.d/test.repo']) + described_class.stubs(:virtual_inifile).returns(virtual_inifile) end describe 'self.instances' do - before :each do - described_class.stubs(:reposdir).returns(['/etc/yum.repos.d']) - File.expects(:file?).with('/etc/yum.repos.d/test.repo').returns(true) - Puppet::FileSystem.expects(:exist?).with('/etc/yum.repos.d/test.repo').returns(true) - File.expects(:read).with('/etc/yum.repos.d/test.repo').returns(repo_file) + let(:updates_section) do + stub('inifile updates section', + :name => 'updates', + :entries => {'name' => 'updates', 'enabled' => '1', 'descr' => 'test updates'}) end - it 'finds the update repo' do - providers = yumrepo_provider.class.instances - providers.count.should == 1 + it 'finds any existing sections' do + virtual_inifile.expects(:each_section).yields(updates_section) + + providers = described_class.instances + providers.should have(1).items providers[0].name.should == 'updates' providers[0].enabled.should == '1' end end - describe 'create' do - it 'creates a yumrepo' do - described_class.expects(:reposdir).returns(['/etc/yum.repos.d']) - yumrepo_provider.section('puppetlabs-products').expects(:[]=).at_least(1) - yumrepo_provider.create - end - end + describe "methods used by ensurable" do - describe 'destroy' do - it 'flags the section to be destroyed' do - yumrepo_provider.section('puppetlabs-products').expects(:destroy=).with(true) - yumrepo_provider.destroy + let(:type) do + Puppet::Type.type(:yumrepo).new( + :name => 'puppetlabs-products', + :ensure => :present, + :baseurl => 'http://yum.puppetlabs.com/el/6/products/$basearch', + :descr => 'Puppet Labs Products El 6 - $basearch', + :enabled => '1', + :gpgcheck => '1', + :gpgkey => 'file:///etc/pki/rpm-gpg/RPM-GPG-KEY-puppetlabs' + ) end - end - describe 'exists?' do - it 'checks if yumrepo exists' do - described_class.stubs(:reposdir).returns(['/etc/yum.repos.d']) - yumrepo_provider.ensure= :present - yumrepo_provider.exists?.should be_true - end + let(:provider) { type.provider } + + let(:puppetlabs_section) { stub('inifile puppetlabs section', :name => 'puppetlabs-products') } + + it "#create sets the yumrepo properties on the according section" do + described_class.expects(:section).returns(puppetlabs_section) + puppetlabs_section.expects(:[]=).with('baseurl', 'http://yum.puppetlabs.com/el/6/products/$basearch') + puppetlabs_section.expects(:[]=).with('descr', 'Puppet Labs Products El 6 - $basearch') + puppetlabs_section.expects(:[]=).with('enabled', '1') + puppetlabs_section.expects(:[]=).with('gpgcheck', '1') + puppetlabs_section.expects(:[]=).with('gpgkey', 'file:///etc/pki/rpm-gpg/RPM-GPG-KEY-puppetlabs') + + provider.create + end + + it "#exists? checks if the repo has been marked as present" do + described_class.stubs(:section).returns(stub(:[]= => nil)) + provider.create + expect(provider).to be_exist + end + + it "#destroy deletes the associated ini file section" do + described_class.expects(:section).returns(puppetlabs_section) + puppetlabs_section.expects(:destroy=).with(true) + provider.destroy + end end describe 'reposdir' do let(:defaults) { ['/etc/yum.repos.d', '/etc/yum/repos.d'] } - let(:all) { ['/etc/yum.repos.d', '/etc/yum/repos.d', '/etc/yum/test'] } - it 'returns defaults if no yum conf' do - Puppet::FileSystem.expects(:exist?).with('/etc/yum.repos.d').returns(true) - Puppet::FileSystem.expects(:exist?).with('/etc/yum/repos.d').returns(true) + before do + Puppet::FileSystem.stubs(:exist?).with('/etc/yum.repos.d').returns(true) + Puppet::FileSystem.stubs(:exist?).with('/etc/yum/repos.d').returns(true) + end + it "returns the default directories if yum.conf doesn't contain a `reposdir` entry" do + described_class.stubs(:find_conf_value).with('reposdir', '/etc/yum.conf') described_class.reposdir('/etc/yum.conf').should == defaults end - it 'returns defaults if yumconf has no reposdir' do - File.expects(:exists?).with('/etc/yum.conf').returns(true) - File.expects(:read).with('/etc/yum.conf').returns("[main]\ntest = /etc/yum/test") - Puppet::FileSystem.expects(:exist?).with('/etc/yum.repos.d').returns(true) - Puppet::FileSystem.expects(:exist?).with('/etc/yum/repos.d').returns(true) + it "includes the directory specified by the yum.conf 'reposdir' entry when the directory is present" do + Puppet::FileSystem.expects(:exist?).with("/etc/yum/extra.repos.d").returns(true) - described_class.reposdir('/etc/yum.conf').should == defaults + described_class.expects(:find_conf_value).with('reposdir', '/etc/yum.conf').returns "/etc/yum/extra.repos.d" + described_class.reposdir('/etc/yum.conf').should include("/etc/yum/extra.repos.d") end - it 'returns all directories if yum.conf contains reposdir and directory exists' do - File.expects(:exists?).with('/etc/yum.conf').returns(true) - File.expects(:read).with('/etc/yum.conf').returns("[main]\nreposdir = /etc/yum/test") - Puppet::FileSystem.expects(:exist?).with('/etc/yum.repos.d').returns(true) - Puppet::FileSystem.expects(:exist?).with('/etc/yum/repos.d').returns(true) - Puppet::FileSystem.expects(:exist?).with('/etc/yum/test').returns(true) + it "doesn't the directory specified by the yum.conf 'reposdir' entry when the directory is absent" do + Puppet::FileSystem.expects(:exist?).with("/etc/yum/extra.repos.d").returns(false) - described_class.reposdir('/etc/yum.conf').should == all + described_class.expects(:find_conf_value).with('reposdir', '/etc/yum.conf').returns "/etc/yum/extra.repos.d" + described_class.reposdir('/etc/yum.conf').should_not include("/etc/yum/extra.repos.d") end - it 'returns defaults if yum.conf contains reposdir and directory doesnt exist' do - File.expects(:exists?).with('/etc/yum.conf').returns(true) - File.expects(:read).with('/etc/yum.conf').returns("[main]\nreposdir = /etc/yum/test") - Puppet::FileSystem.expects(:exist?).with('/etc/yum.repos.d').returns(true) - Puppet::FileSystem.expects(:exist?).with('/etc/yum/repos.d').returns(true) - Puppet::FileSystem.expects(:exist?).with('/etc/yum/test').returns(false) + it "raises an entry if none of the specified repo directories exist" do + Puppet::FileSystem.unstub(:exist?) + Puppet::FileSystem.stubs(:exist?).returns false - described_class.reposdir('/etc/yum.conf').should == defaults + described_class.stubs(:find_conf_value).with('reposdir', '/etc/yum.conf') + expect { described_class.reposdir('/etc/yum.conf') }.to raise_error('No yum directories were found on the local filesystem') end - end - - end From a0d87d222ef4b8b951acb87bed335b611e90f6f4 Mon Sep 17 00:00:00 2001 From: Nick Fagerlund Date: Fri, 14 Feb 2014 12:06:20 -0800 Subject: [PATCH 655/800] (maint) Scrub whitespace on global setting descriptions when inserting into man pages These text blobs use the hanging indent style used in a lot of our embedded doc strings, so we have to handle that indent properly with scrub. --- lib/puppet/face/help/man.erb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/puppet/face/help/man.erb b/lib/puppet/face/help/man.erb index bd584889c..8b7c461fc 100644 --- a/lib/puppet/face/help/man.erb +++ b/lib/puppet/face/help/man.erb @@ -38,7 +38,7 @@ configuration options can also be generated by running puppet with <% unless face.display_global_options.empty? face.display_global_options.uniq.sort.each do |name| option = name - desc = Puppet.settings.setting(option).desc + desc = Puppet::Util::Docs.scrub(Puppet.settings.setting(option).desc) type = Puppet.settings.setting(option).default type ||= Puppet.settings.setting(option).type.to_s.upcase -%> <%= "* --#{option} #{type}" %>: @@ -78,7 +78,7 @@ ACTIONS `OPTIONS` <% unique_display_global_options.uniq.sort.each do |name| option = name - desc = Puppet.settings.setting(option).desc + desc = Puppet::Util::Docs.scrub(Puppet.settings.setting(option).desc) type = Puppet.settings.setting(option).default type ||= Puppet.settings.setting(option).type.to_s.upcase -%> <%= "<--#{option} #{type}>" %> - From 24c8fa2455958276eb3240e2fff3fb9d5826df0d Mon Sep 17 00:00:00 2001 From: Nick Fagerlund Date: Fri, 14 Feb 2014 12:07:33 -0800 Subject: [PATCH 656/800] (PUP-1707) Fix possible gsubs on nil in man page ERB template In several places, this template would attempt to fall back to summaries if long descriptions were absent. However, before deciding to use the summary, it would attempt to do a gsub on nil, which would blow up the entire process. This commit does the fallback earlier, and adds final fallback text so we won't be using text methods on nil if the summary is also absent. --- lib/puppet/face/help/man.erb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/puppet/face/help/man.erb b/lib/puppet/face/help/man.erb index 8b7c461fc..29e71222f 100644 --- a/lib/puppet/face/help/man.erb +++ b/lib/puppet/face/help/man.erb @@ -42,14 +42,14 @@ configuration options can also be generated by running puppet with type = Puppet.settings.setting(option).default type ||= Puppet.settings.setting(option).type.to_s.upcase -%> <%= "* --#{option} #{type}" %>: -<%= desc.gsub(/^/, ' ') || ' undocumented setting' %> +<%= (desc || 'Undocumented setting.').gsub(/^/, ' ') %> <% end end -%> <% unless face.options.empty? face.options.sort.each do |name| option = face.get_option name -%> <%= "* " + option.optparse.join(" | " ) %>: -<%= option.description.gsub(/^/, ' ') || ' ' + option.summary %> +<%= (option.description || option.summary || "Undocumented option.").gsub(/^/, ' ') %> <% end end -%> @@ -82,11 +82,11 @@ ACTIONS type = Puppet.settings.setting(option).default type ||= Puppet.settings.setting(option).type.to_s.upcase -%> <%= "<--#{option} #{type}>" %> - -<%= desc.gsub(/^/, ' ') %> +<%= (desc || "Undocumented setting.").gsub(/^/, ' ') %> <% end -%> <% unique_options.sort.each do |name| option = action.get_option name - text = (option.description || option.summary).chomp + "\n" -%> + text = (option.description || option.summary || "Undocumented option.").chomp + "\n" -%> <%= '<' + option.optparse.join("> | <") + '>' %> - <%= text.gsub(/^/, ' ') %> <% end -%> From 0b6446006136458b5d60219730862e6575eac13d Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 7 Feb 2014 15:29:35 -0800 Subject: [PATCH 657/800] (PUP-751) Check for reparse once per compile Previously, whenever the known_resource_types (Puppet::Resource::TypeCollection) was retrieved from the environment it would check if there were any files that needed to be reparsed. If any files needed a reparse, then the entire TypeCollection was thrown away and redone. In most cases the reparse check did not need to touch the filesystem because the Puppet::Util::PeriodicWatcher would return a cached result until the filetimeout had expired. Unfortunately the check was done so often that the overhead of simply checking if enough time had passed caused it to take a considerable amount of time. This changes it so that the environment's known resource types are only recalculated if the new check_for_reparse method determines that it should. The new method is only called at the beginning of a compile and so the files will never be checked while a compilation is being executed. In my local benchmark runs (the many_modules benchmark) this resulted in a speedup from ~0.8 seconds to ~0.6 seconds. --- lib/puppet/node/environment.rb | 70 +++++------------------------- lib/puppet/parser/compiler.rb | 3 +- spec/unit/node/environment_spec.rb | 18 +++----- 3 files changed, 18 insertions(+), 73 deletions(-) diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb index 4c6a3cc72..9fa63fd63 100644 --- a/lib/puppet/node/environment.rb +++ b/lib/puppet/node/environment.rb @@ -10,27 +10,6 @@ end # Puppet::Node::Environment acts as a container for all configuration # that is expected to vary between environments. # -# ## Global variables -# -# The Puppet::Node::Environment uses a number of global variables. -# -# ### `$known_resource_types` -# -# The 'known_resource_types' global variable represents a singleton instance -# of the Puppet::Resource::TypeCollection class. The variable is discarded -# and regenerated if it is accessed by an environment that doesn't match the -# environment of the 'known_resource_types' -# -# This behavior of discarding the known_resource_types every time the -# environment changes is not ideal. In the best case this can cause valid data -# to be discarded and reloaded. If Puppet is being used with numerous -# environments then this penalty will be repeatedly incurred. -# -# In the worst case (#15106) demonstrates that if a different environment is -# accessed during catalog compilation, for whatever reason, the -# known_resource_types can be discarded which loses information that cannot -# be recovered and can cause a catalog compilation to completely fail. -# # ## The root environment # # In addition to normal environments that are defined by the user,there is a @@ -180,45 +159,14 @@ class Puppet::Node::Environment Puppet.settings.value(param, self.name) end - # The current global TypeCollection - # - # @note The environment is loosely coupled with the {Puppet::Resource::TypeCollection} - # class. While there is a 1:1 relationship between an environment and a - # TypeCollection instance, there is only one TypeCollection instance - # available at any given time. It is stored in `$known_resource_types`. - # `$known_resource_types` is accessed as an instance method, but is global - # to all environment variables. - # # @api public # @return [Puppet::Resource::TypeCollection] The current global TypeCollection def known_resource_types - # This makes use of short circuit evaluation to get the right thread-safe - # per environment semantics with an efficient most common cases; we almost - # always just return our thread's known-resource types. Only at the start - # of a compilation (after our thread var has been set to nil) or when the - # environment has changed or when the known resource types have become stale - # do we delve deeper. - $known_resource_types = nil if $known_resource_types && - ($known_resource_types.environment != self || !@known_resource_types_being_imported && $known_resource_types.stale?) - $known_resource_types ||= - if @known_resource_types.nil? or @known_resource_types.require_reparse? - #set the global variable $known_resource_types immediately as it will be queried - #resursively from the parser which would set it anyway, just executing more code in vain - @known_resource_types = $known_resource_types = Puppet::Resource::TypeCollection.new(self) - - #avoid an infinite recursion (called from the parser) if Puppet[:filetimeout] is set to -1 and - #$known_resource_types.stale? returns always true; let's set a flag that we're importing - #so if this method is called recursively we'll skip testing the stale status - begin - @known_resource_types_being_imported = true - @known_resource_types.import_ast(perform_initial_import, '') - ensure - @known_resource_types_being_imported = false - end - @known_resource_types - else - @known_resource_types - end + if @known_resource_types.nil? + @known_resource_types = Puppet::Resource::TypeCollection.new(self) + @known_resource_types.import_ast(perform_initial_import(), '') + end + @known_resource_types end # Yields each modules' plugin directory if the plugin directory (modulename/lib) @@ -386,6 +334,12 @@ class Puppet::Node::Environment known_resource_types.watch_file(file.to_s) end + def check_for_reparse + if @known_resource_types && @known_resource_types.require_reparse? + @known_resource_types = nil + end + end + # @return [String] The stringified value of the `name` instance variable # @api public def to_s @@ -473,7 +427,7 @@ class Puppet::Node::Environment end end rescue => detail - known_resource_types.parse_failed = true + @known_resource_types.parse_failed = true msg = "Could not parse for environment #{self}: #{detail}" error = Puppet::Error.new(msg) diff --git a/lib/puppet/parser/compiler.rb b/lib/puppet/parser/compiler.rb index 7014bf3ad..048b79769 100644 --- a/lib/puppet/parser/compiler.rb +++ b/lib/puppet/parser/compiler.rb @@ -17,10 +17,9 @@ class Puppet::Parser::Compiler include Puppet::Resource::TypeCollectionHelper def self.compile(node) - $known_resource_types = nil $env_module_directories = nil + node.environment.check_for_reparse - # ...and we actually do the compile now we have caching ready. new(node).compile.to_resource rescue => detail message = "#{detail} on node #{node.name}" diff --git a/spec/unit/node/environment_spec.rb b/spec/unit/node/environment_spec.rb index 5ce8a2343..18db102c9 100755 --- a/spec/unit/node/environment_spec.rb +++ b/spec/unit/node/environment_spec.rb @@ -84,7 +84,6 @@ describe Puppet::Node::Environment do before do @collection = Puppet::Resource::TypeCollection.new(env) env.stubs(:perform_initial_import).returns(Puppet::Parser::AST::Hostclass.new('')) - $known_resource_types = nil end it "should create a resource type collection if none exists" do @@ -108,18 +107,13 @@ describe Puppet::Node::Environment do env.known_resource_types.should equal(@collection) end - it "should return the current thread associated collection if there is one" do - $known_resource_types = @collection - - env.known_resource_types.should equal(@collection) - end - it "should generate a new TypeCollection if the current one requires reparsing" do old_type_collection = env.known_resource_types old_type_collection.stubs(:require_reparse?).returns true - $known_resource_types = nil - new_type_collection = env.known_resource_types + env.check_for_reparse + + new_type_collection = env.known_resource_types new_type_collection.should be_a Puppet::Resource::TypeCollection new_type_collection.should_not equal(old_type_collection) end @@ -412,14 +406,13 @@ describe Puppet::Node::Environment do it "should fail helpfully if there is an error importing" do Puppet::FileSystem.stubs(:exist?).returns true - env.stubs(:known_resource_types).returns Puppet::Resource::TypeCollection.new(env) parser, env = parser_and_environment('testing') parser.expects(:file=).once parser.expects(:parse).raises ArgumentError expect do - env.instance_eval { perform_initial_import } + env.known_resource_types end.to raise_error(Puppet::Error) end @@ -436,12 +429,11 @@ describe Puppet::Node::Environment do it "should mark the type collection as needing a reparse when there is an error parsing" do parser, env = parser_and_environment('testing') - env.stubs(:known_resource_types).returns Puppet::Resource::TypeCollection.new(env) parser.expects(:parse).raises Puppet::ParseError.new("Syntax error at ...") expect do - env.instance_eval { perform_initial_import } + env.known_resource_types end.to raise_error(Puppet::Error, /Syntax error at .../) env.known_resource_types.require_reparse?.should be_true end From 0412515e2f9d75f00687bc5c01915da5a51c58e3 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 7 Feb 2014 17:35:51 -0800 Subject: [PATCH 658/800] (maint) Optimize adding tags Resources add a lot of tags and so streamlining that ends up with a overall savings for catalog compilation. This removes an extra call to collect, removes several method calls, and relies on the fact that the @tags object is a set and so we don't need to check for the tag already being there. --- lib/puppet/util/tagging.rb | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/lib/puppet/util/tagging.rb b/lib/puppet/util/tagging.rb index 2e788279b..031b27f93 100644 --- a/lib/puppet/util/tagging.rb +++ b/lib/puppet/util/tagging.rb @@ -1,20 +1,24 @@ require 'puppet/util/tag_set' module Puppet::Util::Tagging + ValidTagRegex = /^\w[-\w:.]*$/ + # Add a tag to our current list. These tags will be added to all # of the objects contained in this scope. def tag(*ary) @tags ||= new_tags - qualified = [] - - ary.collect { |tag| tag.to_s.downcase }.each do |tag| - fail(Puppet::ParseError, "Invalid tag #{tag.inspect}") unless valid_tag?(tag) - qualified << tag if tag.include?("::") - @tags << tag unless @tags.include?(tag) + ary.each do |tag| + name = tag.to_s.downcase + if name =~ ValidTagRegex + @tags << name + name.split("::").each do |section| + @tags << section + end + else + fail(Puppet::ParseError, "Invalid tag #{name}") + end end - - handle_qualified_tags( qualified ) end # Are we tagged with the provided tag? @@ -41,15 +45,6 @@ module Puppet::Util::Tagging private - def handle_qualified_tags(qualified) - qualified.each do |name| - name.split("::").each do |tag| - @tags << tag unless @tags.include?(tag) - end - end - end - - ValidTagRegex = /^\w[-\w:.]*$/ def valid_tag?(tag) tag.is_a?(String) and tag =~ ValidTagRegex end From 40c315b516af34dbc3d6b4602821bee87646ed89 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 7 Feb 2014 17:55:30 -0800 Subject: [PATCH 659/800] (maint) Use JSON for parsing metadata.json Now that puppet depends on JSON we can use it to parse the metadata.json file. The built in json parsers will be much faster than PSON. --- lib/puppet/module.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/puppet/module.rb b/lib/puppet/module.rb index 09661a85b..c579eecd8 100644 --- a/lib/puppet/module.rb +++ b/lib/puppet/module.rb @@ -1,5 +1,6 @@ require 'puppet/util/logging' require 'semver' +require 'json' # Support for modules class Puppet::Module @@ -57,8 +58,8 @@ class Puppet::Module return false unless Puppet::FileSystem.exist?(metadata_file) begin - metadata = PSON.parse(File.read(metadata_file)) - rescue PSON::PSONError => e + metadata = JSON.parse(File.read(metadata_file)) + rescue JSON::JSONError => e Puppet.debug("#{name} has an invalid and unparsable metadata.json file. The parse error: #{e.message}") return false end @@ -112,7 +113,7 @@ class Puppet::Module end def load_metadata - data = PSON.parse File.read(metadata_file) + data = JSON.parse File.read(metadata_file) @forge_name = data['name'].gsub('-', '/') if data['name'] [:source, :author, :version, :license, :puppetversion, :dependencies].each do |attr| From f854342cc234d9d0849e1b2dcc998390e8c03ed4 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 7 Feb 2014 18:30:30 -0800 Subject: [PATCH 660/800] (maint) Remove String check There isn't any need to check for the String class because it also responds to to_str. This reduces the number of calls in the case of things that respond to to_str --- lib/puppet/file_system/file_impl.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/puppet/file_system/file_impl.rb b/lib/puppet/file_system/file_impl.rb index f682a1752..53f74a1c1 100644 --- a/lib/puppet/file_system/file_impl.rb +++ b/lib/puppet/file_system/file_impl.rb @@ -8,14 +8,14 @@ class Puppet::FileSystem::FileImpl def assert_path(path) return path if path.is_a?(Pathname) - # Some paths are string, or (in the case of WatchedFile, it pretends to be one by implementing to_str. - # (sigh). - # - unless path.is_a?(String) || path.respond_to?(:to_str) + + # Some paths are string, or in the case of WatchedFile, it pretends to be + # one by implementing to_str. + if path.respond_to?(:to_str) + Pathname.new(path) + else raise ArgumentError, "FileSystem implementation expected Pathname, got: '#{path.class}'" end - # converts String and #to_str to Pathname - Pathname.new(path) end def path_string(path) From 018e2b4409c2a2cf70b719339798f9787be026a8 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 7 Feb 2014 18:31:43 -0800 Subject: [PATCH 661/800] (maint) Reduce calls to settings During the lifetime of a scope object the value of the trusted data settings should not change. Rather than looking this up on every invocation of setvar, which is fairly expensive, the scope can simply hold onto the value from when it was instantiated. --- lib/puppet/parser/scope.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb index 0a9f9b14d..b659deb2c 100644 --- a/lib/puppet/parser/scope.rb +++ b/lib/puppet/parser/scope.rb @@ -277,6 +277,8 @@ class Puppet::Parser::Scope # The table for storing class singletons. This will only actually # be used by top scopes and node scopes. @class_scopes = {} + + @enable_trusted_data = Puppet[:trusted_node_data] end # Store the fact that we've evaluated a class, and store a reference to @@ -559,7 +561,7 @@ class Puppet::Parser::Scope end # Check for reserved variable names - if Puppet[:trusted_node_data] && !options[:privileged] && RESERVED_VARIABLE_NAMES.include?(name) + if @enable_trusted_data && !options[:privileged] && RESERVED_VARIABLE_NAMES.include?(name) raise Puppet::ParseError, "Attempt to assign to a reserved variable name: '#{name}'" end From c129f40f73fb28a27c62c9b4e3c4756d2c7f5f18 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 7 Feb 2014 18:33:11 -0800 Subject: [PATCH 662/800] (maint) Hold token name in local variabel The tokens name was fetch multiple times in quick succession. The lexer is in a very tight inner loop and so any reduction of method calls can produce improvements in performance. Although most work for language performance is focussed on the new parser, this is a simple, small win that we can easily take now. --- lib/puppet/parser/lexer.rb | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/puppet/parser/lexer.rb b/lib/puppet/parser/lexer.rb index 08e3fab18..986183241 100644 --- a/lib/puppet/parser/lexer.rb +++ b/lib/puppet/parser/lexer.rb @@ -478,26 +478,27 @@ class Puppet::Parser::Lexer next end - lexing_context[:after] = final_token.name unless newline - lexing_context[:string_interpolation_depth] += 1 if final_token.name == :DQPRE - lexing_context[:string_interpolation_depth] -= 1 if final_token.name == :DQPOST + final_token_name = final_token.name + lexing_context[:after] = final_token_name unless newline + lexing_context[:string_interpolation_depth] += 1 if final_token_name == :DQPRE + lexing_context[:string_interpolation_depth] -= 1 if final_token_name == :DQPOST value = token_value[:value] - if match = @@pairs[value] and final_token.name != :DQUOTE and final_token.name != :SQUOTE + if match = @@pairs[value] and final_token_name != :DQUOTE and final_token_name != :SQUOTE @expected << match - elsif exp = @expected[-1] and exp == value and final_token.name != :DQUOTE and final_token.name != :SQUOTE + elsif exp = @expected[-1] and exp == value and final_token_name != :DQUOTE and final_token_name != :SQUOTE @expected.pop end - if final_token.name == :LBRACE or final_token.name == :LPAREN + if final_token_name == :LBRACE or final_token_name == :LPAREN commentpush end - if final_token.name == :RPAREN + if final_token_name == :RPAREN commentpop end - yield [final_token.name, token_value] + yield [final_token_name, token_value] if @previous_token namestack(value) if @previous_token.name == :CLASS and value != '{' From c8c1ea5840dc1a2624132450b969091ea13ff39f Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 7 Feb 2014 19:07:13 -0800 Subject: [PATCH 663/800] (PUP-751) Benchmark defined resource types This adds a benchmark for compiling defined resource types. There are simply multiple modules that have a single defined resource type in each. The site.pp produces 2 instances of each defined type. Current timings on my local machine: user system total real Run 1 0.780000 0.150000 0.930000 ( 0.935276) Run 2 0.760000 0.140000 0.900000 ( 0.901496) Run 3 0.780000 0.130000 0.910000 ( 0.912020) Run 4 0.780000 0.130000 0.910000 ( 0.917095) Run 5 0.780000 0.130000 0.910000 ( 0.908075) Run 6 0.810000 0.140000 0.950000 ( 0.950874) Run 7 0.820000 0.130000 0.950000 ( 0.948420) Run 8 0.780000 0.140000 0.920000 ( 0.918298) Run 9 0.780000 0.130000 0.910000 ( 0.905121) Run 10 0.770000 0.130000 0.900000 ( 0.903823) > total: 7.840000 1.350000 9.190000 ( 9.200498) > avg: 0.784000 0.135000 0.919000 ( 0.920050) --- benchmarks/defined_types/benchmarker.rb | 73 +++++++++++++++++++ benchmarks/defined_types/description | 3 + .../defined_types/module/testing.pp.erb | 3 + benchmarks/defined_types/puppet.conf.erb | 3 + benchmarks/defined_types/site.pp.erb | 4 + 5 files changed, 86 insertions(+) create mode 100644 benchmarks/defined_types/benchmarker.rb create mode 100644 benchmarks/defined_types/description create mode 100644 benchmarks/defined_types/module/testing.pp.erb create mode 100644 benchmarks/defined_types/puppet.conf.erb create mode 100644 benchmarks/defined_types/site.pp.erb diff --git a/benchmarks/defined_types/benchmarker.rb b/benchmarks/defined_types/benchmarker.rb new file mode 100644 index 000000000..77d46e71e --- /dev/null +++ b/benchmarks/defined_types/benchmarker.rb @@ -0,0 +1,73 @@ +require 'erb' +require 'ostruct' +require 'fileutils' +require 'json' + +class Benchmarker + include FileUtils + + def initialize(target, size) + @target = target + @size = size + end + + def setup + require 'puppet' + config = File.join(@target, 'puppet.conf') + Puppet.initialize_settings(['--config', config]) + end + + def run + env = Puppet.lookup(:environments).get('benchmarking') + node = Puppet::Node.new("testing", :environment => env) + Puppet::Resource::Catalog.indirection.find("testing", :use_node => node) + end + + def generate + environment = File.join(@target, 'environments', 'benchmarking') + templates = File.join('benchmarks', 'defined_types') + + mkdir_p(File.join(environment, 'modules')) + mkdir_p(File.join(environment, 'manifests')) + + render(File.join(templates, 'site.pp.erb'), + File.join(environment, 'manifests', 'site.pp'), + :size => @size) + + @size.times do |i| + module_name = "module#{i}" + module_base = File.join(environment, 'modules', module_name) + manifests = File.join(module_base, 'manifests') + + mkdir_p(manifests) + + File.open(File.join(module_base, 'metadata.json'), 'w') do |f| + JSON.dump({ + "types" => [], + "source" => "", + "author" => "Defined Types Benchmark", + "license" => "Apache 2.0", + "version" => "1.0.0", + "description" => "Defined Types benchmark module #{i}", + "summary" => "Just this benchmark module, you know?", + "dependencies" => [], + }, f) + end + + render(File.join(templates, 'module', 'testing.pp.erb'), + File.join(manifests, 'testing.pp'), + :name => module_name) + end + + render(File.join(templates, 'puppet.conf.erb'), + File.join(@target, 'puppet.conf'), + :location => @target) + end + + def render(erb_file, output_file, bindings) + site = ERB.new(File.read(erb_file)) + File.open(output_file, 'w') do |fh| + fh.write(site.result(OpenStruct.new(bindings).instance_eval { binding })) + end + end +end diff --git a/benchmarks/defined_types/description b/benchmarks/defined_types/description new file mode 100644 index 000000000..30c8b7fa1 --- /dev/null +++ b/benchmarks/defined_types/description @@ -0,0 +1,3 @@ +Benchmark scenario: heavy use of defined types +Benchmark target: catalog compilation + diff --git a/benchmarks/defined_types/module/testing.pp.erb b/benchmarks/defined_types/module/testing.pp.erb new file mode 100644 index 000000000..e723561d5 --- /dev/null +++ b/benchmarks/defined_types/module/testing.pp.erb @@ -0,0 +1,3 @@ +define <%= name %>::testing { + notify { "in <%= name %>: $title": } +} diff --git a/benchmarks/defined_types/puppet.conf.erb b/benchmarks/defined_types/puppet.conf.erb new file mode 100644 index 000000000..e0c5d8588 --- /dev/null +++ b/benchmarks/defined_types/puppet.conf.erb @@ -0,0 +1,3 @@ +confdir = <%= location %> +vardir = <%= location %> +environmentpath = <%= File.join(location, 'environments') %> diff --git a/benchmarks/defined_types/site.pp.erb b/benchmarks/defined_types/site.pp.erb new file mode 100644 index 000000000..338b635b2 --- /dev/null +++ b/benchmarks/defined_types/site.pp.erb @@ -0,0 +1,4 @@ +<% size.times do |i| %> + module<%= i %>::testing { "first": } + module<%= i %>::testing{ "second": } +<% end %> From 13ab9b46c5730c4c92a6d7c62f6c8501671fac3f Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Sun, 9 Feb 2014 11:50:28 -0800 Subject: [PATCH 664/800] (PUP-751) Preserve environment across resource copy The resource's environment was lost when a copy was made. This caused the type information to be looked up again, from scratch, as well as a total loss of the associated environment information (it would switch from one environment to another). By preserving the environment any calls to resource_type, or other methods that require the environment, are much faster. This also preserves the rstype cache of the resource_type to cut down the looks a slight amount more. Here is the breakdown of the speedups to this point on my machine: Initial(bca4d4e): > total: 7.520000 1.250000 8.770000 ( 8.789636) > avg: 0.752000 0.125000 0.877000 ( 0.878964) Prefer defined types(2617d61): > total: 7.150000 1.170000 8.320000 ( 8.326319) > avg: 0.715000 0.117000 0.832000 ( 0.832632) Preserve environment(3f8e94c): > total: 6.170000 0.860000 7.030000 ( 7.057820) > avg: 0.617000 0.086000 0.703000 ( 0.705782) I also tried to change the order of loading in Puppet::Parser::Scope#find_resource_type to have it look for defined types first, but that resulted in an overall slowdown: > total: 6.740000 0.730000 7.470000 ( 7.476968) > avg: 0.674000 0.073000 0.747000 ( 0.747697) --- lib/puppet/resource.rb | 2 ++ spec/unit/resource_spec.rb | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/puppet/resource.rb b/lib/puppet/resource.rb index 5c088f932..7a5015ad8 100644 --- a/lib/puppet/resource.rb +++ b/lib/puppet/resource.rb @@ -401,6 +401,8 @@ class Puppet::Resource result.exported = self.exported result.virtual = self.virtual result.tag(*self.tags) + result.environment = environment + result.instance_variable_set(:@rstype, resource_type) result end diff --git a/spec/unit/resource_spec.rb b/spec/unit/resource_spec.rb index b7a95aaba..42d0a7ec8 100755 --- a/spec/unit/resource_spec.rb +++ b/spec/unit/resource_spec.rb @@ -807,7 +807,7 @@ describe Puppet::Resource do end end - describe "it should implement copy_as_resource" do + it "implements copy_as_resource" do resource = Puppet::Resource.new("file", "/my/file") resource.copy_as_resource.should == resource end From 69d03c8d7634ca6b090f8797d10b6a326311d950 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Sun, 9 Feb 2014 12:16:44 -0800 Subject: [PATCH 665/800] (maint) Fork for each scenario Because of the way puppet initializes, we can't safely re-initialize in the same process. In order to run all of the scenarios we need to make sure there is a new process for each one. This changes from having the "all" tasks re-run rake for each of these instead of just making them dependencies of the "all" task. --- tasks/benchmark.rake | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tasks/benchmark.rake b/tasks/benchmark.rake index 7fb4668ea..bd1dcb9d7 100644 --- a/tasks/benchmark.rake +++ b/tasks/benchmark.rake @@ -94,9 +94,17 @@ namespace :benchmark do namespace :all do desc "Profile all of the scenarios. (#{scenarios.join(', ')})" - task :profile => scenarios.collect { |name| "#{name}:profile" } + task :profile do + scenarios.each do |name| + sh "rake benchmark:#{name}:profile" + end + end desc "Run all of the scenarios. (#{scenarios.join(', ')})" - task :run => scenarios.collect { |name| "#{name}:run" } + task :run do + scenarios.each do |name| + sh "rake benchmark:#{name}:run" + end + end end end From 0729c993611842bb725e2a5b6e769a0a0c64892c Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Sun, 9 Feb 2014 12:51:07 -0800 Subject: [PATCH 666/800] (PUP-751) Ensure resource knows it's type By looking up the definition first, many tests were broken because of expectations about filesystem access. By ensuring that the resources are created with the expected resource_type there isn't any lookup neccessary. --- lib/puppet/resource.rb | 10 ++++++++++ lib/puppet/type.rb | 1 + spec/unit/type/whit_spec.rb | 4 ++-- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/puppet/resource.rb b/lib/puppet/resource.rb index 7a5015ad8..249740b93 100644 --- a/lib/puppet/resource.rb +++ b/lib/puppet/resource.rb @@ -231,6 +231,9 @@ class Puppet::Resource catalog ? catalog.resource(to_s) : nil end + # The resource's type implementation + # @return [Puppet::Type, Puppet::Resource::Type] + # @api private def resource_type @rstype ||= case type when "Class"; environment.known_resource_types.hostclass(title == :main ? "" : title) @@ -240,6 +243,13 @@ class Puppet::Resource end end + # Set the resource's type implementation + # @param type [Puppet::Type, Puppet::Resource::Type] + # @api private + def resource_type=(type) + @rstype = type + end + def environment @environment ||= Puppet.lookup(:environments).get(Puppet[:environment]) end diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb index 03e439a50..f382f30ca 100644 --- a/lib/puppet/type.rb +++ b/lib/puppet/type.rb @@ -1182,6 +1182,7 @@ class Type # Now create our resource. resource = Puppet::Resource.new(self.name, title) resource.catalog = hash.delete(:catalog) + resource.resource_type = self hash.each do |param, value| resource[param] = value diff --git a/spec/unit/type/whit_spec.rb b/spec/unit/type/whit_spec.rb index f18e845ad..73498e55d 100755 --- a/spec/unit/type/whit_spec.rb +++ b/spec/unit/type/whit_spec.rb @@ -1,10 +1,10 @@ #! /usr/bin/env ruby require 'spec_helper' -whit = Puppet::Type.type(:whit).new(:name => "Foo::Bar") +whit = Puppet::Type.type(:whit) describe whit do it "should stringify in a way that users will regognise" do - whit.to_s.should == "Foo::Bar" + whit.new(:name => "Foo::Bar").to_s.should == "Foo::Bar" end end From 1e6a37330f5192052a54048f15b25e28a7d971e8 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Thu, 13 Feb 2014 16:36:01 -0800 Subject: [PATCH 667/800] (PUP-751) Fix resource_type to check for changes Since known_resource_types on the Environment no longer checks for stale files, any users of that are going to be working in a long running process need to ensure that the stale check is made before trying to use the known_resource_types. The only place, besides the compiler, that does this is the resource_type indirection. This adds that check there. --- lib/puppet/indirector/resource_type/parser.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/puppet/indirector/resource_type/parser.rb b/lib/puppet/indirector/resource_type/parser.rb index 6632f3d8a..884fd2f03 100644 --- a/lib/puppet/indirector/resource_type/parser.rb +++ b/lib/puppet/indirector/resource_type/parser.rb @@ -24,7 +24,7 @@ class Puppet::Indirector::ResourceType::Parser < Puppet::Indirector::Code # @api public def find(request) Puppet.override(:squelch_parse_errors => true) do - krt = request.environment.known_resource_types + krt = resource_types_in(request.environment) # This is a bit ugly. [:hostclass, :definition, :node].each do |type| @@ -57,7 +57,7 @@ class Puppet::Indirector::ResourceType::Parser < Puppet::Indirector::Code def search(request) Puppet.override(:squelch_parse_errors => true) do - krt = request.environment.known_resource_types + krt = resource_types_in(request.environment) # Make sure we've got all of the types loaded. krt.loader.import_all @@ -93,4 +93,9 @@ class Puppet::Indirector::ResourceType::Parser < Puppet::Indirector::Code result end end + + def resource_types_in(environment) + environment.check_for_reparse + environment.known_resource_types + end end From e9c916dafc060e989a067000b43d94a4db312128 Mon Sep 17 00:00:00 2001 From: Nick Fagerlund Date: Fri, 14 Feb 2014 11:33:25 -0800 Subject: [PATCH 668/800] (maint) Revise docs for evaluator setting --- lib/puppet/defaults.rb | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index 8f3fd791d..d6947913a 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -1780,7 +1780,7 @@ EOT :default => "current", :desc => <<-'EOT' Selects the parser to use for parsing puppet manifests (in puppet DSL - language/'.pp' files). Available choices are `current` (the default), + language/'.pp' files). Available choices are `current` (the default) and `future`. The `curent` parser means that the released version of the parser should @@ -1801,26 +1801,21 @@ EOT end end, :desc => <<-'EOT' - Selects the evaluator to use for evaluation of puppet manifests parsed - with the option "parser = future". Available choices are `current`, - and `future` (the default). + Which evaluator to use when compiling Puppet manifests. Valid values + are `current` and `future` (the default). - The `future` evaluator means that the new evaluator (experimental) will - be used, whereas 'current' means that the "parser future" option will - transform the parsed result to Puppet 3x and use the regular - evaluator. + **Note:** This setting is only used when `parser = future`. It allows + testers to turn off the `future` evaluator when doing detailed tests and + comparisons of the new compilation system. - The `future` evaluator is a "time travel to the future" allowing early - exposure to new language features. What these features are will vary from - release to release and they may be invididually configurable. + Evaluation is the second stage of catalog compilation. After the parser + converts a manifest to a model of expressions, the evaluator processes + each expression. (For example, a resource declaration signals the + evaluator to add a resource to the catalog). - The default for this parameter is 'future', which means that the flag is - there to turn off the future evaluator for A / B testing. - - The experimental features "parser future, evaluator future" are expected - to become what is released as the standard / current in Puppet 4.x. - - The evaluator option has no effect unless parser is set to 'future'. + The `future` parser and evaluator are slated to become default in Puppet + 4. Their purpose is to add new features and improve consistency + and reliability. Available Since Puppet 3.5. EOT From e79d999fe92a589f06820d2ffe6cba53ebb997f4 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 14 Feb 2014 22:51:17 +0100 Subject: [PATCH 669/800] (PUP-1029) Improve error message consistency and information This improves the consistency and content in the iterative functions when the given block has too few, or too many parameters. It now shows what is expected logically, how many parameters were defined, and what their names are. --- lib/puppet/parser/ast/lambda.rb | 4 ++++ lib/puppet/parser/functions/each.rb | 10 ++++++---- lib/puppet/parser/functions/filter.rb | 8 +++++--- lib/puppet/parser/functions/map.rb | 8 +++++--- lib/puppet/parser/functions/reduce.rb | 8 +++++++- lib/puppet/parser/functions/slice.rb | 6 ++++-- lib/puppet/pops/evaluator/closure.rb | 4 ++++ 7 files changed, 35 insertions(+), 13 deletions(-) diff --git a/lib/puppet/parser/ast/lambda.rb b/lib/puppet/parser/ast/lambda.rb index 74ffa710c..64d78d49b 100644 --- a/lib/puppet/parser/ast/lambda.rb +++ b/lib/puppet/parser/ast/lambda.rb @@ -127,5 +127,9 @@ class Puppet::Parser::AST def puppet_lambda() true end + + def parameter_names + @parameters.collect {|p| p[0] } + end end end diff --git a/lib/puppet/parser/functions/each.rb b/lib/puppet/parser/functions/each.rb index f4de4a155..39d26ba38 100644 --- a/lib/puppet/parser/functions/each.rb +++ b/lib/puppet/parser/functions/each.rb @@ -76,25 +76,27 @@ Puppet::Parser::Functions::newfunction( end end - raise ArgumentError, ("each(): wrong number of arguments (#{args.length}; must be 2)") if args.length != 2 + raise ArgumentError, ("each(): wrong number of arguments (#{args.length}; expected 2, got #{args.length})") if args.length != 2 receiver = args[0] pblock = args[1] raise ArgumentError, ("each(): wrong argument type (#{args[1].class}; must be a parameterized block.") unless pblock.respond_to?(:puppet_lambda) serving_size = pblock.parameter_count if serving_size == 0 - raise ArgumentError, "each(): block must define at least one parameter; value." + raise ArgumentError, "each(): block must define at least one parameter; value. Block has 0." end case receiver when Hash if serving_size > 2 - raise ArgumentError, "each(): block must define at most two parameters; key, value" + raise ArgumentError, "each(): block must define at most two parameters; key, value. Block has #{serving_size}; "+ + pblock.parameter_names.join(', ') end foreach_Hash(receiver, self, pblock, serving_size) else if serving_size > 2 - raise ArgumentError, "each(): block must define at most two parameters; index, value" + raise ArgumentError, "each(): block must define at most two parameters; index, value. Block has #{serving_size}; "+ + pblock.parameter_names.join(', ') end enum = Puppet::Pops::Types::Enumeration.enumerator(receiver) unless enum diff --git a/lib/puppet/parser/functions/filter.rb b/lib/puppet/parser/functions/filter.rb index 70ab024de..0296e3337 100644 --- a/lib/puppet/parser/functions/filter.rb +++ b/lib/puppet/parser/functions/filter.rb @@ -69,13 +69,14 @@ Puppet::Parser::Functions::newfunction( raise ArgumentError, ("filter(): wrong argument type (#{pblock.class}; must be a parameterized block.") unless pblock.respond_to?(:puppet_lambda) serving_size = pblock.parameter_count if serving_size == 0 - raise ArgumentError, "filter(): block must define at least one parameter; value." + raise ArgumentError, "filter(): block must define at least one parameter; value. Block has 0." end case receiver when Hash if serving_size > 2 - raise ArgumentError, "filter(): block must define at most two parameters; key, value" + raise ArgumentError, "filter(): block must define at most two parameters; key, value. Block has #{serving_size}; "+ + pblock.parameter_names.join(', ') end if serving_size == 1 result = receiver.select {|x, y| pblock.call(self, [x, y]) } @@ -87,7 +88,8 @@ Puppet::Parser::Functions::newfunction( result else if serving_size > 2 - raise ArgumentError, "filter(): block must define at most two parameters; index, value" + raise ArgumentError, "filter(): block must define at most two parameters; index, value. Block has #{serving_size}; "+ + pblock.parameter_names.join(', ') end enum = Puppet::Pops::Types::Enumeration.enumerator(receiver) unless enum diff --git a/lib/puppet/parser/functions/map.rb b/lib/puppet/parser/functions/map.rb index 8cfd60daf..8bc9fd383 100644 --- a/lib/puppet/parser/functions/map.rb +++ b/lib/puppet/parser/functions/map.rb @@ -67,12 +67,13 @@ Puppet::Parser::Functions::newfunction( raise ArgumentError, ("map(): wrong argument type (#{pblock.class}; must be a parameterized block.") unless pblock.respond_to?(:puppet_lambda) serving_size = pblock.parameter_count if serving_size == 0 - raise ArgumentError, "map(): block must define at least one parameter; value." + raise ArgumentError, "map(): block must define at least one parameter; value. Block has 0." end case receiver when Hash if serving_size > 2 - raise ArgumentError, "map(): block must define at most two parameters; key, value" + raise ArgumentError, "map(): block must define at most two parameters; key, value.args Block has #{serving_size}; "+ + pblock.parameter_names.join(', ') end if serving_size == 1 result = receiver.map {|x, y| pblock.call(self, [x, y]) } @@ -81,7 +82,8 @@ Puppet::Parser::Functions::newfunction( end else if serving_size > 2 - raise ArgumentError, "map(): block must define at most two parameters; index, value" + raise ArgumentError, "map(): block must define at most two parameters; index, value. Block has #{serving_size}; "+ + pblock.parameter_names.join(', ') end enum = Puppet::Pops::Types::Enumeration.enumerator(receiver) diff --git a/lib/puppet/parser/functions/reduce.rb b/lib/puppet/parser/functions/reduce.rb index 97632b676..078ebc2e9 100644 --- a/lib/puppet/parser/functions/reduce.rb +++ b/lib/puppet/parser/functions/reduce.rb @@ -75,7 +75,7 @@ Puppet::Parser::Functions::newfunction( when 3 pblock = args[2] else - raise ArgumentError, ("reduce(): wrong number of arguments (#{args.length}; must be 2 or 3)") + raise ArgumentError, ("reduce(): wrong number of arguments (#{args.length}; expected 2 or 3, got #{args.length})") end unless pblock.respond_to?(:puppet_lambda) raise ArgumentError, ("reduce(): wrong argument type (#{pblock.class}; must be a parameterized block.") @@ -86,6 +86,12 @@ Puppet::Parser::Functions::newfunction( raise ArgumentError, ("reduce(): wrong argument type (#{receiver.class}; must be something enumerable.") end + serving_size = pblock.parameter_count + if serving_size != 2 + raise ArgumentError, "reduce(): block must define 2 parameters; memo, value. Block has #{serving_size}; "+ + pblock.parameter_names.join(', ') + end + if args.length == 3 enum.reduce(args[1]) {|memo, x| pblock.call(self, memo, x) } else diff --git a/lib/puppet/parser/functions/slice.rb b/lib/puppet/parser/functions/slice.rb index e18e043b5..bcc830f74 100644 --- a/lib/puppet/parser/functions/slice.rb +++ b/lib/puppet/parser/functions/slice.rb @@ -45,10 +45,12 @@ Puppet::Parser::Functions::newfunction( def each_Common(o, slice_size, filler, scope, pblock) serving_size = pblock ? pblock.parameter_count : 1 if serving_size == 0 - raise ArgumentError, "slice(): block must define at least one parameter." + raise ArgumentError, "slice(): block must define at least one parameter. Block has 0." end unless serving_size == 1 || serving_size == slice_size - raise ArgumentError, "slice(): block must define one parameter, or the same number of parameters as the given size of the slice (#{slice_size})." + raise ArgumentError, "slice(): block must define one parameter, or " + + "the same number of parameters as the given size of the slice (#{slice_size}). Block has #{serving_size}; "+ + pblock.parameter_names.join(', ') end enumerator = o.each_slice(slice_size) result = [] diff --git a/lib/puppet/pops/evaluator/closure.rb b/lib/puppet/pops/evaluator/closure.rb index 05a4fd55a..d1afed8d1 100644 --- a/lib/puppet/pops/evaluator/closure.rb +++ b/lib/puppet/pops/evaluator/closure.rb @@ -45,4 +45,8 @@ class Puppet::Pops::Evaluator::Closure @model.parameters.count { |p| !p.value.nil? } end + def parameter_names + @model.parameters.collect {|p| p.name } + end + end From 641f5eac4f0cb77cdfcaedaedca63f9d64e716b3 Mon Sep 17 00:00:00 2001 From: Nick Fagerlund Date: Fri, 14 Feb 2014 15:51:25 -0800 Subject: [PATCH 670/800] (maint) Improve Puppet::Util::Docs.scrub's handling of one-liners One-liner handling wasn't behaving as expected. Sometimes one-liners are buffered with extra newlines before and/or after, and in these cases, scrub wasn't catching the leading whitespace. This commit makes it behave as intended, and adds a test showing the intended behavior. --- lib/puppet/util/docs.rb | 4 ++-- spec/unit/util/docs_spec.rb | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/puppet/util/docs.rb b/lib/puppet/util/docs.rb index 0c854b76c..3f0a2fba0 100644 --- a/lib/puppet/util/docs.rb +++ b/lib/puppet/util/docs.rb @@ -112,8 +112,8 @@ module Puppet::Util::Docs # # See tests in spec/unit/util/docs_spec.rb for examples. def scrub(text) - # One-liners are easy! - return text.strip if text !~ /\n/ + # One-liners are easy! (One-liners may be buffered with extra newlines.) + return text.strip if text.strip !~ /\n/ excluding_first_line = text.partition("\n").last indent = excluding_first_line.scan(/^[ \t]*(?=\S)/).min || '' # prevent nil # Clean hanging indent, if any diff --git a/spec/unit/util/docs_spec.rb b/spec/unit/util/docs_spec.rb index eb736ff72..5823e303f 100644 --- a/spec/unit/util/docs_spec.rb +++ b/spec/unit/util/docs_spec.rb @@ -85,6 +85,15 @@ EOT expect(Puppet::Util::Docs.scrub(my_cleaned_output)).to eq my_cleaned_output end + it "trims leading space from one-liners (even when they're buffered with extra newlines)" do + input = " + Updates values in the `puppet.conf` configuration file. + " + expected_output = "Updates values in the `puppet.conf` configuration file." + output = Puppet::Util::Docs.scrub(input) + expect(output).to eq expected_output + end + end end From 85922de44beba3192bd87338165f97c00c6cc2bf Mon Sep 17 00:00:00 2001 From: Nick Fagerlund Date: Fri, 14 Feb 2014 14:41:20 -0800 Subject: [PATCH 671/800] (maint) Typo fixes: repeated words (e.g. "the the") --- lib/puppet/application/cert.rb | 2 +- lib/puppet/face/node/clean.rb | 2 +- lib/puppet/face/status.rb | 2 +- lib/puppet/parser/functions/filter.rb | 2 +- lib/puppet/parser/functions/lookup.rb | 4 ++-- lib/puppet/pops/binder/producers.rb | 8 ++++---- lib/puppet/pops/binder/scheme_handler/symbolic_scheme.rb | 2 +- lib/puppet/pops/evaluator/closure.rb | 2 +- lib/puppet/pops/model/model.rb | 4 ++-- lib/puppet/pops/types/type_parser.rb | 2 +- lib/puppet/provider/group/aix.rb | 2 +- lib/puppet/provider/package/windows.rb | 2 +- lib/puppet/provider/service/debian.rb | 2 +- lib/puppet/provider/user/aix.rb | 2 +- lib/puppet/type/selboolean.rb | 2 +- lib/puppet/type/selmodule.rb | 2 +- lib/puppet/util/command_line/trollop.rb | 2 +- spec/unit/application_spec.rb | 2 +- 18 files changed, 23 insertions(+), 23 deletions(-) diff --git a/lib/puppet/application/cert.rb b/lib/puppet/application/cert.rb index 6ad901593..c505a4995 100644 --- a/lib/puppet/application/cert.rb +++ b/lib/puppet/application/cert.rb @@ -170,7 +170,7 @@ configuration options can also be generated by running puppet cert with 'clean', 'list', and 'fingerprint' actions. * --digest: - Set the digest for fingerprinting (defaults to the the digest used when + Set the digest for fingerprinting (defaults to the digest used when signing the cert). Valid values depends on your openssl and openssl ruby extension version. diff --git a/lib/puppet/face/node/clean.rb b/lib/puppet/face/node/clean.rb index dc1da367e..903e93819 100644 --- a/lib/puppet/face/node/clean.rb +++ b/lib/puppet/face/node/clean.rb @@ -37,7 +37,7 @@ Puppet::Face.define(:node, '0.0.1') do # This seems really bad; run_mode should be set as part of a class # definition, and should not be modifiable beyond that. This is one of # the only places left in the code that tries to manipulate it. Other - # parts of code that handle certificates behave differently if the the + # parts of code that handle certificates behave differently if the # run_mode is master. Those other behaviors are needed for cleaning the # certificates correctly. Puppet.settings.preferred_run_mode = "master" diff --git a/lib/puppet/face/status.rb b/lib/puppet/face/status.rb index e8c87e98d..ffc44a396 100644 --- a/lib/puppet/face/status.rb +++ b/lib/puppet/face/status.rb @@ -27,7 +27,7 @@ Puppet::Indirector::Face.define(:status, '0.0.1') do Over REST, this action will query the configured puppet master by default. To query other servers, including puppet agent nodes started with the - <--listen> option, you can set set the global <--server> and <--masterport> + <--listen> option, you can set the global <--server> and <--masterport> options on the command line; note that agent nodes listen on port 8139. EOT find.short_description <<-EOT diff --git a/lib/puppet/parser/functions/filter.rb b/lib/puppet/parser/functions/filter.rb index 0296e3337..5760a8a75 100644 --- a/lib/puppet/parser/functions/filter.rb +++ b/lib/puppet/parser/functions/filter.rb @@ -25,7 +25,7 @@ Puppet::Parser::Functions::newfunction( $a = ["raspberry", "blueberry", "orange"] $a.filter |$x| { $x =~ /berry$/ } # rasberry, blueberry - If the the block defines two parameters, they will be set to `index, value` (with index starting at 0) for all + If the block defines two parameters, they will be set to `index, value` (with index starting at 0) for all enumerables except Hash, and to `key, value` for a Hash. *Examples* diff --git a/lib/puppet/parser/functions/lookup.rb b/lib/puppet/parser/functions/lookup.rb index 463abdf65..55d56f452 100644 --- a/lib/puppet/parser/functions/lookup.rb +++ b/lib/puppet/parser/functions/lookup.rb @@ -89,14 +89,14 @@ returns the bound value with the given name after having asserted it has the de lookup('the_name') When called with two arguments; **the name**, and **the expected type**, it -returns the the bound value with the given name after having asserted it has the given data +returns the bound value with the given name after having asserted it has the given data type ('String' in the example): lookup('the_name', 'String') # 3.x lookup('the_name', String) # parser future When called with three arguments, **the name**, the **expected type**, and a **default**, it -returns the the bound value with the given name, or the default after having asserted the value +returns the bound value with the given name, or the default after having asserted the value has the given data type (`String` in the example above): lookup('the_name', 'String', 'Fred') # 3x diff --git a/lib/puppet/pops/binder/producers.rb b/lib/puppet/pops/binder/producers.rb index 2cd8fb6db..e200c9eb3 100644 --- a/lib/puppet/pops/binder/producers.rb +++ b/lib/puppet/pops/binder/producers.rb @@ -4,7 +4,7 @@ # It is required that custom producers inherit from this producer (directly or indirectly). # # The selection of a Producer is typically performed by the Innjector when it configures itself -# from a Bindings model where a {Puppet::Pops::Binder::Bindings::ProducerDescriptor} describes +# from a Bindings model where a {Puppet::Pops::Binder::Bindings::ProducerDescriptor} describes # which producer to use. The configuration uses this to create the concrete producer. # It is possible to describe that a particular producer class is to be used, and also to describe that # a custom producer (derived from Producer) should be used. This is available for both regular @@ -462,12 +462,12 @@ module Puppet::Pops::Binder::Producers end # This type of producer should only be created by the Injector. - # + # # @api private # class AssistedInjectProducer < Producer # An Assisted Inject Producer is created when a lookup is made of a type that is - # not bound. It does not support a transformer lambda. + # not bound. It does not support a transformer lambda. # @note This initializer has a different signature than all others. Do not use in regular logic. # @api private # @@ -557,7 +557,7 @@ module Puppet::Pops::Binder::Producers # Collection accepts elements that comply with the array's element type, or the entire type (i.e. Array[element_type]). # If the type is restrictive - e.g. Array[String] and an Array[String] is contributed, the result will not be type # compliant without also using the `:flatten` option, and a type error will be raised. For an array with relaxed typing - # i.e. Array[Data], it it valid to produce a result such as `['a', ['b', 'c'], 'd']` and no flattening is required + # i.e. Array[Data], it is valid to produce a result such as `['a', ['b', 'c'], 'd']` and no flattening is required # and no error is raised (but using the array needs to be aware of potential array, non-array entries. # The use of the option `:flatten` controls how the result is flattened. # diff --git a/lib/puppet/pops/binder/scheme_handler/symbolic_scheme.rb b/lib/puppet/pops/binder/scheme_handler/symbolic_scheme.rb index 0cfb0c486..dc258112d 100644 --- a/lib/puppet/pops/binder/scheme_handler/symbolic_scheme.rb +++ b/lib/puppet/pops/binder/scheme_handler/symbolic_scheme.rb @@ -19,7 +19,7 @@ class Puppet::Pops::Binder::SchemeHandler::SymbolicScheme < Puppetx::Puppet::Bin fqn = fqn_from_path(uri)[1] bindings = Puppet::Pops::Binder::BindingsLoader.provide(scope, fqn) raise ArgumentError, "Cannot load bindings '#{uri}' - no bindings found." unless bindings - # Must clone as the the rest mutates the model + # Must clone as the rest mutates the model cloned_bindings = Marshal.load(Marshal.dump(bindings)) Puppet::Pops::Binder::BindingsFactory.contributed_bindings(fqn, cloned_bindings) end diff --git a/lib/puppet/pops/evaluator/closure.rb b/lib/puppet/pops/evaluator/closure.rb index d1afed8d1..be582e546 100644 --- a/lib/puppet/pops/evaluator/closure.rb +++ b/lib/puppet/pops/evaluator/closure.rb @@ -1,7 +1,7 @@ # A Closure represents logic bound to a particular scope. # As long as the runtime (basically the scope implementation) has the behaviour of Puppet 3x it is not -# safe to use this closure when the the scope given to it when initialized goes "out of scope". +# safe to use this closure when the scope given to it when initialized goes "out of scope". # # Note that the implementation is backwards compatible in that the call method accepts a scope, but this # scope is not used. diff --git a/lib/puppet/pops/model/model.rb b/lib/puppet/pops/model/model.rb index 540e251d7..09a92d02e 100644 --- a/lib/puppet/pops/model/model.rb +++ b/lib/puppet/pops/model/model.rb @@ -157,7 +157,7 @@ module Puppet::Pops::Model contains_many_uni 'values', Expression end - # A Keyed entry has a key and a value expression. It it typically used as an entry in a Hash. + # A Keyed entry has a key and a value expression. It is typically used as an entry in a Hash. # class KeyedEntry < Positioned contains_one_uni 'key', Expression, :lowerBound => 1 @@ -391,7 +391,7 @@ module Puppet::Pops::Model end # A text expression is an interpolation of an expression. If the embedded expression is - # a QualifiedName, it it taken as a variable name and resolved. All other expressions are evaluated. + # a QualifiedName, it is taken as a variable name and resolved. All other expressions are evaluated. # The result is transformed to a string. # class TextExpression < UnaryExpression; end diff --git a/lib/puppet/pops/types/type_parser.rb b/lib/puppet/pops/types/type_parser.rb index 5536665fc..1b8cc8948 100644 --- a/lib/puppet/pops/types/type_parser.rb +++ b/lib/puppet/pops/types/type_parser.rb @@ -30,7 +30,7 @@ class Puppet::Pops::Types::TypeParser # def parse(string) # TODO: This state (@string) can be removed since the parse result of newer future parser - # contains a Locator in its SourcePosAdapter and the Locator keeps the the string. + # contains a Locator in its SourcePosAdapter and the Locator keeps the string. # This way, there is no difference between a parsed "string" and something that has been parsed # earlier and fed to 'interpret' # diff --git a/lib/puppet/provider/group/aix.rb b/lib/puppet/provider/group/aix.rb index 0eeb49c8b..1722748d0 100644 --- a/lib/puppet/provider/group/aix.rb +++ b/lib/puppet/provider/group/aix.rb @@ -9,7 +9,7 @@ require 'puppet/provider/aixobject' Puppet::Type.type(:group).provide :aix, :parent => Puppet::Provider::AixObject do desc "Group management for AIX." - # This will the the default provider for this platform + # This will the default provider for this platform defaultfor :operatingsystem => :aix confine :operatingsystem => :aix diff --git a/lib/puppet/provider/package/windows.rb b/lib/puppet/provider/package/windows.rb index d4936c89b..aaf455181 100644 --- a/lib/puppet/provider/package/windows.rb +++ b/lib/puppet/provider/package/windows.rb @@ -8,7 +8,7 @@ Puppet::Type.type(:package).provide(:windows, :parent => Puppet::Provider::Packa This provider supports either MSI or self-extracting executable installers. This provider requires a `source` attribute when installing the package. - It accepts paths paths to local files, mapped drives, or UNC paths. + It accepts paths to local files, mapped drives, or UNC paths. If the executable requires special arguments to perform a silent install or uninstall, then the appropriate arguments should be specified using the diff --git a/lib/puppet/provider/service/debian.rb b/lib/puppet/provider/service/debian.rb index 7d14eaa3c..5c0105092 100644 --- a/lib/puppet/provider/service/debian.rb +++ b/lib/puppet/provider/service/debian.rb @@ -39,7 +39,7 @@ Puppet::Type.type(:service).provide :debian, :parent => :init do if [104, 106].include?($CHILD_STATUS.exitstatus) return :true elsif [105].include?($CHILD_STATUS.exitstatus) - # 105 is unknown, which generally means the the iniscript does not support query + # 105 is unknown, which generally means the iniscript does not support query # The debian policy states that the initscript should support methods of query # For those that do not, peform the checks manually # http://www.debian.org/doc/debian-policy/ch-opersys.html diff --git a/lib/puppet/provider/user/aix.rb b/lib/puppet/provider/user/aix.rb index 1fc25deab..e75763a0d 100644 --- a/lib/puppet/provider/user/aix.rb +++ b/lib/puppet/provider/user/aix.rb @@ -19,7 +19,7 @@ require 'date' Puppet::Type.type(:user).provide :aix, :parent => Puppet::Provider::AixObject do desc "User management for AIX." - # This will the the default provider for this platform + # This will the default provider for this platform defaultfor :operatingsystem => :aix confine :operatingsystem => :aix diff --git a/lib/puppet/type/selboolean.rb b/lib/puppet/type/selboolean.rb index 204b89056..eb30742a5 100644 --- a/lib/puppet/type/selboolean.rb +++ b/lib/puppet/type/selboolean.rb @@ -9,7 +9,7 @@ module Puppet end newproperty(:value) do - desc "Whether the the SELinux boolean should be enabled or disabled." + desc "Whether the SELinux boolean should be enabled or disabled." newvalue(:on) newvalue(:off) end diff --git a/lib/puppet/type/selmodule.rb b/lib/puppet/type/selmodule.rb index 70ef60581..23bf235de 100644 --- a/lib/puppet/type/selmodule.rb +++ b/lib/puppet/type/selmodule.rb @@ -42,7 +42,7 @@ Puppet::Type.newtype(:selmodule) do desc "If set to `true`, the policy will be reloaded if the version found in the on-disk file differs from the loaded - version. If set to `false` (the default) the the only check + version. If set to `false` (the default) the only check that will be made is if the policy is loaded at all or not." newvalue(:true) diff --git a/lib/puppet/util/command_line/trollop.rb b/lib/puppet/util/command_line/trollop.rb index bce1ea436..25f21d90e 100644 --- a/lib/puppet/util/command_line/trollop.rb +++ b/lib/puppet/util/command_line/trollop.rb @@ -115,7 +115,7 @@ class Parser ## [+:long+] Specify the long form of the argument, i.e. the form with two dashes. If unspecified, will be automatically derived based on the argument name by turning the +name+ option into a string, and replacing any _'s by -'s. ## [+:short+] Specify the short form of the argument, i.e. the form with one dash. If unspecified, will be automatically derived from +name+. ## [+:type+] Require that the argument take a parameter or parameters of type +type+. For a single parameter, the value can be a member of +SINGLE_ARG_TYPES+, or a corresponding Ruby class (e.g. +Integer+ for +:int+). For multiple-argument parameters, the value can be any member of +MULTI_ARG_TYPES+ constant. If unset, the default argument type is +:flag+, meaning that the argument does not take a parameter. The specification of +:type+ is not necessary if a +:default+ is given. - ## [+:default+] Set the default value for an argument. Without a default value, the hash returned by #parse (and thus Trollop::options) will have a +nil+ value for this key unless the argument is given on the commandline. The argument type is derived automatically from the class of the default value given, so specifying a +:type+ is not necessary if a +:default+ is given. (But see below for an important caveat when +:multi+: is specified too.) If the argument is a flag, and the default is set to +true+, then if it is specified on the the commandline the value will be +false+. + ## [+:default+] Set the default value for an argument. Without a default value, the hash returned by #parse (and thus Trollop::options) will have a +nil+ value for this key unless the argument is given on the commandline. The argument type is derived automatically from the class of the default value given, so specifying a +:type+ is not necessary if a +:default+ is given. (But see below for an important caveat when +:multi+: is specified too.) If the argument is a flag, and the default is set to +true+, then if it is specified on the commandline the value will be +false+. ## [+:required+] If set to +true+, the argument must be provided on the commandline. ## [+:multi+] If set to +true+, allows multiple occurrences of the option on the commandline. Otherwise, only a single instance of the option is allowed. (Note that this is different from taking multiple parameters. See below.) ## diff --git a/spec/unit/application_spec.rb b/spec/unit/application_spec.rb index 1c9dd49b8..b96f829d9 100755 --- a/spec/unit/application_spec.rb +++ b/spec/unit/application_spec.rb @@ -604,7 +604,7 @@ describe Puppet::Application do @app.parse_options end - it "should give to OptionParser a block that adds the the value to the options array" do + it "should give to OptionParser a block that adds the value to the options array" do OptionParser.any_instance.stubs(:on) OptionParser.any_instance.stubs(:on).with("--test4","-t").yields(nil) From 69a40519dae4d50822c5a5fd72f2c508d4dfa90d Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Fri, 14 Feb 2014 17:48:40 -0800 Subject: [PATCH 672/800] (maint) Fix spec for UTF-8 strings in user comment properties The first problem with this spec is that it wasn't actually being run due to an always false "if" clause. It was checking for `String` responding to `encode`, but `encode` is an instance method. `method_defined?` should have been used instead. The second problem with this spec is that the test value did not contain any non-ASCII characters. If it had, it would have caught the bug described in PUP-1473. Added a non-ASCII character to test for regressions. --- spec/unit/type/user_spec.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/spec/unit/type/user_spec.rb b/spec/unit/type/user_spec.rb index 6c33acaf1..d9d48d6fc 100755 --- a/spec/unit/type/user_spec.rb +++ b/spec/unit/type/user_spec.rb @@ -1,4 +1,5 @@ #! /usr/bin/env ruby +# encoding: UTF-8 require 'spec_helper' describe Puppet::Type.type(:user) do @@ -343,12 +344,13 @@ describe Puppet::Type.type(:user) do end end - describe "when managing comment on Ruby 1.9", :if => String.respond_to?(:encode) do + describe "when managing comment on Ruby 1.9", :if => String.method_defined?(:encode) do it "should force value encoding to ASCII-8BIT" do - value = 'abcd'.encode(Encoding::UTF_8) - comment = described_class.new(:name => 'foo', :comment => value) - comment[:comment].should == 'abcd' - comment[:comment].encoding.should == Encoding::ASCII_8BIT + value = 'abcd™' + value.encoding.should == Encoding::UTF_8 + user = described_class.new(:name => 'foo', :comment => value) + user[:comment].encoding.should == Encoding::ASCII_8BIT + user[:comment].should == value.force_encoding(Encoding::ASCII_8BIT) end end From 413a733dbff70e8fa64dd81649b7e6764d293d13 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 14 Feb 2014 18:11:02 -0800 Subject: [PATCH 673/800] (Maint) Ensure there is a fully ruby env The tests were neglecting to require 'rubygems', which meant that they were not working with the full environment of modules that puppet needs to run. Setting up the environment is the responsibility of the code that is going to load puppet, which is why this change was made here, rather than changing puppet to require rubygems itself. --- .../backwards_compatibility/13682_do_not_monkey_patch_old_pmt.rb | 1 + acceptance/tests/security/cve-2013-1653_puppet_kick.rb | 1 + 2 files changed, 2 insertions(+) diff --git a/acceptance/tests/modules/backwards_compatibility/13682_do_not_monkey_patch_old_pmt.rb b/acceptance/tests/modules/backwards_compatibility/13682_do_not_monkey_patch_old_pmt.rb index 3402c45ff..8cfb0be3f 100644 --- a/acceptance/tests/modules/backwards_compatibility/13682_do_not_monkey_patch_old_pmt.rb +++ b/acceptance/tests/modules/backwards_compatibility/13682_do_not_monkey_patch_old_pmt.rb @@ -10,6 +10,7 @@ module Puppet end end end +require "rubygems" require "puppet" puts Puppet.version ' diff --git a/acceptance/tests/security/cve-2013-1653_puppet_kick.rb b/acceptance/tests/security/cve-2013-1653_puppet_kick.rb index a3decd0cf..818516aed 100644 --- a/acceptance/tests/security/cve-2013-1653_puppet_kick.rb +++ b/acceptance/tests/security/cve-2013-1653_puppet_kick.rb @@ -14,6 +14,7 @@ test_name "CVE 2013-1653: Puppet Kick Remote Code Exploit" do keyfile = on( exploiter, puppet_agent( '--configprint hostprivkey' )).stdout.chomp exploit = %Q[#!#{exploiter['puppetbindir']}/ruby + require 'rubygems' require 'puppet' require 'openssl' require 'net/https' From cfdcf76ffb78f6956e9374d9e7b754f0bbf4d444 Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Sat, 15 Feb 2014 08:32:23 -0800 Subject: [PATCH 674/800] (maint) Fix two example command lines that were missing 'rake' --- docs/acceptance_tests.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/acceptance_tests.md b/docs/acceptance_tests.md index dcb2d7bab..857d3aef2 100644 --- a/docs/acceptance_tests.md +++ b/docs/acceptance_tests.md @@ -73,7 +73,7 @@ Alternatively you may provision via git clone by calling the ci:test:git task. If you have a branch pushed to your fork which you wish to test prior to merging into puppetlabs/puppet, you can do so be setting the FORK environment variable. So, if I have a branch 'issue/master/wonder-if-this-explodes' pushed to my jpartlow puppet fork that I want to test on Windows, I could invoke the following: ```sh -bundle exec ci:test:git CONFIG=config/nodes/win2008r2.yaml SHA=issue/master/wonder-if-this-explodes FORK=jpartlow +bundle exec rake ci:test:git CONFIG=config/nodes/win2008r2.yaml SHA=issue/master/wonder-if-this-explodes FORK=jpartlow ``` #### Source Checkout for Local Branch @@ -108,7 +108,7 @@ NOTE: By default these tasks provision with packages. Set TYPE=git to use sourc If you run a number of jobs with --preserve_hosts or vi ci:test_and_preserve_hosts, you may eventually generate a large number of stale vms. They should be reaped automatically by qa infrastructure within a day or so, but you may also run: - bundle exec ci:destroy_preserved_hosts + bundle exec rake ci:destroy_preserved_hosts to clean them up sooner and free resources. From 654438aec19fb490701234af1af74fbba4882589 Mon Sep 17 00:00:00 2001 From: Dominic Cleal Date: Mon, 17 Feb 2014 13:52:39 +0000 Subject: [PATCH 675/800] (PUP-1722) Permit HTTPS and FTP protocols in yumrepo --- lib/puppet/type/yumrepo.rb | 12 ++++++------ spec/unit/type/yumrepo_spec.rb | 13 +++++++++++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/puppet/type/yumrepo.rb b/lib/puppet/type/yumrepo.rb index d44de1f13..d173155dc 100644 --- a/lib/puppet/type/yumrepo.rb +++ b/lib/puppet/type/yumrepo.rb @@ -47,7 +47,7 @@ Puppet::Type.newtype(:yumrepo) do newvalues(/.*/, :absent) validate do |value| parsed = URI.parse(value) - fail("Must be a valid URL") unless ['file', 'http'].include?(parsed.scheme) + fail("Must be a valid URL") unless ['file', 'http', 'https', 'ftp'].include?(parsed.scheme) end end @@ -57,7 +57,7 @@ Puppet::Type.newtype(:yumrepo) do newvalues(/.*/, :absent) validate do |value| parsed = URI.parse(value) - fail("Must be a valid URL") unless ['file', 'http'].include?(parsed.scheme) + fail("Must be a valid URL") unless ['file', 'http', 'https', 'ftp'].include?(parsed.scheme) end end @@ -93,7 +93,7 @@ Puppet::Type.newtype(:yumrepo) do newvalues(/.*/, :absent) validate do |value| parsed = URI.parse(value) - fail("Must be a valid URL") unless ['file', 'http'].include?(parsed.scheme) + fail("Must be a valid URL") unless ['file', 'http', 'https', 'ftp'].include?(parsed.scheme) end end @@ -105,7 +105,7 @@ Puppet::Type.newtype(:yumrepo) do newvalues(/.*/, :absent) validate do |value| parsed = URI.parse(value) - fail("Must be a valid URL") unless ['file', 'http'].include?(parsed.scheme) + fail("Must be a valid URL") unless ['file', 'http', 'https', 'ftp'].include?(parsed.scheme) end end @@ -203,7 +203,7 @@ Puppet::Type.newtype(:yumrepo) do newvalues(/.*/, :absent) validate do |value| parsed = URI.parse(value) - fail("Must be a valid URL") unless ['file', 'http'].include?(parsed.scheme) + fail("Must be a valid URL") unless ['file', 'http', 'https', 'ftp'].include?(parsed.scheme) end end @@ -263,7 +263,7 @@ Puppet::Type.newtype(:yumrepo) do newvalues(/.*/, :absent) validate do |value| parsed = URI.parse(value) - fail("Must be a valid URL") unless ['file', 'http'].include?(parsed.scheme) + fail("Must be a valid URL") unless ['file', 'http', 'https', 'ftp'].include?(parsed.scheme) end end diff --git a/spec/unit/type/yumrepo_spec.rb b/spec/unit/type/yumrepo_spec.rb index 8547989c5..25dd8d833 100644 --- a/spec/unit/type/yumrepo_spec.rb +++ b/spec/unit/type/yumrepo_spec.rb @@ -61,5 +61,18 @@ describe Puppet::Type.type(:yumrepo) do Puppet::Type.type(:yumrepo).new(:name => "puppetlabs", :sslverify => "True")[:sslverify].should == "True" Puppet::Type.type(:yumrepo).new(:name => "puppetlabs", :sslverify => "False")[:sslverify].should == "False" end + + [:mirrorlist, :baseurl, :gpgkey, :include, :proxy, :metalink].each do |param| + it "should succeed if '#{param}' uses one of the following protocols (file|http|https|ftp)" do + Puppet::Type.type(:yumrepo).new(:name => "puppetlabs", param => "file:///srv/example/")[param].should =~ %r{\Afile://} + Puppet::Type.type(:yumrepo).new(:name => "puppetlabs", param => "http://example.com/")[param].should =~ %r{\Ahttp://} + Puppet::Type.type(:yumrepo).new(:name => "puppetlabs", param => "https://example.com/")[param].should =~ %r{\Ahttps://} + Puppet::Type.type(:yumrepo).new(:name => "puppetlabs", param => "ftp://example.com/")[param].should =~ %r{\Aftp://} + end + + it "should fail if '#{param}' does not use one of the following protocols (file|http|https|ftp)" do + expect { Puppet::Type.type(:yumrepo).new(:name => "puppetlabs", param => "gopher://example.com/") }.to raise_error + end + end end end From 06d5f532567618381db1900409ed291d1723e881 Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Mon, 17 Feb 2014 09:49:01 -0800 Subject: [PATCH 676/800] (maint) Add newline at end-of-file under lib --- lib/puppet/parser/functions/collect.rb | 2 +- lib/puppet/parser/functions/select.rb | 2 +- lib/puppet/pops/binder/binder_issues.rb | 2 +- lib/puppet/pops/binder/bindings_loader.rb | 2 +- lib/puppet/pops/binder/injector.rb | 2 +- lib/puppet/pops/binder/key_factory.rb | 2 +- lib/puppet/pops/binder/producers.rb | 2 +- lib/puppet/pops/evaluator/runtime3_support.rb | 2 +- lib/puppet/pops/issue_reporter.rb | 2 +- lib/puppet/pops/parser/epp_support.rb | 2 +- lib/puppet/pops/parser/heredoc_support.rb | 2 +- lib/puppet/pops/parser/interpolation_support.rb | 2 +- lib/puppet/pops/parser/lexer_support.rb | 2 +- lib/puppet/pops/parser/locator.rb | 2 +- lib/puppet/pops/parser/makefile | 2 +- lib/puppet/pops/parser/slurp_support.rb | 2 +- lib/puppet/pops/types/class_loader.rb | 2 +- lib/puppet/pops/types/enumeration.rb | 2 +- lib/puppet/vendor/safe_yaml/CHANGES.md | 2 +- lib/puppetx.rb | 2 +- lib/puppetx/puppet/syntax_checker.rb | 2 +- 21 files changed, 21 insertions(+), 21 deletions(-) diff --git a/lib/puppet/parser/functions/collect.rb b/lib/puppet/parser/functions/collect.rb index e30a80bb1..fea42a4df 100644 --- a/lib/puppet/parser/functions/collect.rb +++ b/lib/puppet/parser/functions/collect.rb @@ -12,4 +12,4 @@ Puppet::Parser::Functions::newfunction( raise NotImplementedError, "The 'collect' function has been renamed to 'map'. Please update your manifests." -end \ No newline at end of file +end diff --git a/lib/puppet/parser/functions/select.rb b/lib/puppet/parser/functions/select.rb index 659f2013c..93924f9d0 100644 --- a/lib/puppet/parser/functions/select.rb +++ b/lib/puppet/parser/functions/select.rb @@ -12,4 +12,4 @@ Puppet::Parser::Functions::newfunction( raise NotImplementedError, "The 'select' function has been renamed to 'filter'. Please update your manifests." -end \ No newline at end of file +end diff --git a/lib/puppet/pops/binder/binder_issues.rb b/lib/puppet/pops/binder/binder_issues.rb index a23e7735a..98171add5 100644 --- a/lib/puppet/pops/binder/binder_issues.rb +++ b/lib/puppet/pops/binder/binder_issues.rb @@ -119,4 +119,4 @@ module Puppet::Pops::Binder::BinderIssues "#{label.a_an_uc(semantic)} has zero bindings in #{label.label(layer)}" end -end \ No newline at end of file +end diff --git a/lib/puppet/pops/binder/bindings_loader.rb b/lib/puppet/pops/binder/bindings_loader.rb index ca9043f2c..84a4051b8 100644 --- a/lib/puppet/pops/binder/bindings_loader.rb +++ b/lib/puppet/pops/binder/bindings_loader.rb @@ -85,4 +85,4 @@ class Puppet::Pops::Binder::BindingsLoader tr("-", "_"). downcase end -end \ No newline at end of file +end diff --git a/lib/puppet/pops/binder/injector.rb b/lib/puppet/pops/binder/injector.rb index 8087551a4..994394975 100644 --- a/lib/puppet/pops/binder/injector.rb +++ b/lib/puppet/pops/binder/injector.rb @@ -764,4 +764,4 @@ module Private end end end -end \ No newline at end of file +end diff --git a/lib/puppet/pops/binder/key_factory.rb b/lib/puppet/pops/binder/key_factory.rb index 5abff5fa1..0b45d4f02 100644 --- a/lib/puppet/pops/binder/key_factory.rb +++ b/lib/puppet/pops/binder/key_factory.rb @@ -64,4 +64,4 @@ class Puppet::Pops::Binder::KeyFactory return nil unless key.is_a?(Array) key[0] end -end \ No newline at end of file +end diff --git a/lib/puppet/pops/binder/producers.rb b/lib/puppet/pops/binder/producers.rb index 2cd8fb6db..c7f57dd71 100644 --- a/lib/puppet/pops/binder/producers.rb +++ b/lib/puppet/pops/binder/producers.rb @@ -826,4 +826,4 @@ module Puppet::Pops::Binder::Producers end end -end \ No newline at end of file +end diff --git a/lib/puppet/pops/evaluator/runtime3_support.rb b/lib/puppet/pops/evaluator/runtime3_support.rb index d7c1ee062..8c92c315a 100644 --- a/lib/puppet/pops/evaluator/runtime3_support.rb +++ b/lib/puppet/pops/evaluator/runtime3_support.rb @@ -486,4 +486,4 @@ module Puppet::Pops::Evaluator::Runtime3Support class EvaluationError < StandardError end -end \ No newline at end of file +end diff --git a/lib/puppet/pops/issue_reporter.rb b/lib/puppet/pops/issue_reporter.rb index 28ee971fe..2ba3f1088 100644 --- a/lib/puppet/pops/issue_reporter.rb +++ b/lib/puppet/pops/issue_reporter.rb @@ -77,4 +77,4 @@ class Puppet::Pops::IssueReporter return message unless prefix [prefix, message].join(' ') end -end \ No newline at end of file +end diff --git a/lib/puppet/pops/parser/epp_support.rb b/lib/puppet/pops/parser/epp_support.rb index 3d1013690..aaf6f281b 100644 --- a/lib/puppet/pops/parser/epp_support.rb +++ b/lib/puppet/pops/parser/epp_support.rb @@ -241,4 +241,4 @@ module Puppet::Pops::Parser::EppSupport end end -end \ No newline at end of file +end diff --git a/lib/puppet/pops/parser/heredoc_support.rb b/lib/puppet/pops/parser/heredoc_support.rb index eda606569..341b18a4e 100644 --- a/lib/puppet/pops/parser/heredoc_support.rb +++ b/lib/puppet/pops/parser/heredoc_support.rb @@ -187,4 +187,4 @@ module Puppet::Pops::Parser::HeredocSupport end end -end \ No newline at end of file +end diff --git a/lib/puppet/pops/parser/interpolation_support.rb b/lib/puppet/pops/parser/interpolation_support.rb index 696709acb..951be392a 100644 --- a/lib/puppet/pops/parser/interpolation_support.rb +++ b/lib/puppet/pops/parser/interpolation_support.rb @@ -224,4 +224,4 @@ module Puppet::Pops::Parser::InterpolationSupport end end -end \ No newline at end of file +end diff --git a/lib/puppet/pops/parser/lexer_support.rb b/lib/puppet/pops/parser/lexer_support.rb index 22c5703ab..c769255a5 100644 --- a/lib/puppet/pops/parser/lexer_support.rb +++ b/lib/puppet/pops/parser/lexer_support.rb @@ -104,4 +104,4 @@ module Puppet::Pops::Parser::LexerSupport # end -end \ No newline at end of file +end diff --git a/lib/puppet/pops/parser/locator.rb b/lib/puppet/pops/parser/locator.rb index eaada1e06..7e97004f0 100644 --- a/lib/puppet/pops/parser/locator.rb +++ b/lib/puppet/pops/parser/locator.rb @@ -216,4 +216,4 @@ class Puppet::Pops::Parser::Locator string.byteslice(offset, end_offset - offset).length end end -end \ No newline at end of file +end diff --git a/lib/puppet/pops/parser/makefile b/lib/puppet/pops/parser/makefile index 0e59f55ba..802382dd8 100644 --- a/lib/puppet/pops/parser/makefile +++ b/lib/puppet/pops/parser/makefile @@ -3,4 +3,4 @@ eparser.rb: egrammar.ra racc -o$@ egrammar.ra egrammar.output: egrammar.ra - racc -v -o$@ egrammar.ra \ No newline at end of file + racc -v -o$@ egrammar.ra diff --git a/lib/puppet/pops/parser/slurp_support.rb b/lib/puppet/pops/parser/slurp_support.rb index 0f258c83b..17658c156 100644 --- a/lib/puppet/pops/parser/slurp_support.rb +++ b/lib/puppet/pops/parser/slurp_support.rb @@ -92,4 +92,4 @@ module Puppet::Pops::Parser::SlurpSupport } str end -end \ No newline at end of file +end diff --git a/lib/puppet/pops/types/class_loader.rb b/lib/puppet/pops/types/class_loader.rb index d2b3a327b..0cd1b8c2f 100644 --- a/lib/puppet/pops/types/class_loader.rb +++ b/lib/puppet/pops/types/class_loader.rb @@ -115,4 +115,4 @@ class Puppet::Pops::Types::ClassLoader downcase end -end \ No newline at end of file +end diff --git a/lib/puppet/pops/types/enumeration.rb b/lib/puppet/pops/types/enumeration.rb index cf354b9cb..76dc59b67 100644 --- a/lib/puppet/pops/types/enumeration.rb +++ b/lib/puppet/pops/types/enumeration.rb @@ -31,4 +31,4 @@ class Puppet::Pops::Types::Enumeration nil end end -end \ No newline at end of file +end diff --git a/lib/puppet/vendor/safe_yaml/CHANGES.md b/lib/puppet/vendor/safe_yaml/CHANGES.md index 1efd80cba..4dff37d5c 100644 --- a/lib/puppet/vendor/safe_yaml/CHANGES.md +++ b/lib/puppet/vendor/safe_yaml/CHANGES.md @@ -101,4 +101,4 @@ Added support for: 0.1 --- -Initial release \ No newline at end of file +Initial release diff --git a/lib/puppetx.rb b/lib/puppetx.rb index 884e2b8f2..ad779add1 100644 --- a/lib/puppetx.rb +++ b/lib/puppetx.rb @@ -86,4 +86,4 @@ module Puppetx end end end -end \ No newline at end of file +end diff --git a/lib/puppetx/puppet/syntax_checker.rb b/lib/puppetx/puppet/syntax_checker.rb index 6baa1479d..d46ac8fc8 100644 --- a/lib/puppetx/puppet/syntax_checker.rb +++ b/lib/puppetx/puppet/syntax_checker.rb @@ -88,4 +88,4 @@ module Puppetx::Puppet raise NotImplementedError, "The class #{self.class.name} should have implemented the method check()" end end -end \ No newline at end of file +end From 66e0fd26c3cdd09d61d488dae459d663c8f26f6b Mon Sep 17 00:00:00 2001 From: Alex Harvey Date: Tue, 18 Feb 2014 23:01:47 +1100 Subject: [PATCH 677/800] (doc) Fix a typo in docs for the include function There is a typo in the documentation for the include function. This patch merely fixes that typo. --- lib/puppet/parser/functions/include.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/puppet/parser/functions/include.rb b/lib/puppet/parser/functions/include.rb index ea7acd936..29ef45d40 100644 --- a/lib/puppet/parser/functions/include.rb +++ b/lib/puppet/parser/functions/include.rb @@ -1,5 +1,6 @@ # Include the specified classes -Puppet::Parser::Functions::newfunction(:include, :arity => -2, :doc => "Declares one or more classes, causing the resources in them to be +Puppet::Parser::Functions::newfunction(:include, :arity => -2, :doc => +"Declares one or more classes, causing the resources in them to be evaluated and added to the catalog. Accepts a class name, an array of class names, or a comma-separated list of class names. @@ -15,7 +16,7 @@ and resource-like declarations with the same class. The `include` function does not cause classes to be contained in the class where they are declared. For that, see the `contain` function. It also -does not create a dependency relationship between the declared class and th +does not create a dependency relationship between the declared class and the surrounding class; for that, see the `require` function.") do |vals| if vals.is_a?(Array) # Protect against array inside array From ce5d84d80b579c0cd984051723c9cde4ff976e33 Mon Sep 17 00:00:00 2001 From: Nick Fagerlund Date: Fri, 14 Feb 2014 17:23:13 -0800 Subject: [PATCH 678/800] (maint) Revise help for puppet config subcommand Mostly adding context about the idea of sections, inc. list of available sections. --- lib/puppet/face/config.rb | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/lib/puppet/face/config.rb b/lib/puppet/face/config.rb index dd51502dd..a2412f086 100644 --- a/lib/puppet/face/config.rb +++ b/lib/puppet/face/config.rb @@ -5,25 +5,42 @@ Puppet::Face.define(:config, '0.0.1') do copyright "Puppet Labs", 2011 license "Apache 2 license; see COPYING" - summary "Interact with Puppet's configuration options." + summary "Interact with Puppet's settings." + + description "This subcommand can inspect and modify settings from Puppet's + 'puppet.conf' configuration file. For documentation about individual settings, + see http://docs.puppetlabs.com/references/latest/configuration.html." option "--section SECTION_NAME" do default_to { "main" } summary "The section of the configuration file to interact with." + description <<-EOT + The section of the puppet.conf configuration file to interact with. + + The three most commonly used sections are 'main', 'master', and 'agent'. + 'Main' is the default, and is used by all Puppet applications. Other + sections can override 'main' values for specific applications --- the + 'master' section affects puppet master and puppet cert, and the 'agent' + section affects puppet agent. + + Less commonly used is the 'user' section, which affects puppet apply. Any + other section will be treated as the name of a legacy environment + (a deprecated feature), and can only include the 'manifest' and + 'modulepath' settings. + EOT end action(:print) do - summary "Examine Puppet's current configuration settings." + summary "Examine Puppet's current settings." arguments "(all | [ ...]" description <<-'EOT' - Prints the value of a single configuration option or a list of - configuration options. + Prints the value of a single setting or a list of settings. This action is an alternate interface to the information available with `puppet --configprint`. EOT notes <<-'EOT' - By default, this action reads the general configuration in in the 'main' + By default, this action reads the general configuration in the 'main' section. Use the '--section' and '--environment' flags to examine other configuration domains. EOT @@ -55,10 +72,10 @@ Puppet::Face.define(:config, '0.0.1') do end action(:set) do - summary "Set Puppet's configuration settings." + summary "Set Puppet's settings." arguments "[setting_name] [setting_value]" description <<-'EOT' - Update values in the `puppet.conf` configuration file. + Updates values in the `puppet.conf` configuration file. EOT notes <<-'EOT' By default, this action manipulates the configuration in the From 6a5600f8c0906331f64a93ea407938ab2498e2ca Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Wed, 12 Feb 2014 10:33:36 -0800 Subject: [PATCH 679/800] (PUP-1584) Module tool acceptance with_environment tests directory envs Update the acceptance/tests/modules/*/with_environment.rb tests to check both legacy and new directory environments. Also extracts common environment setup to module_utils. --- .../lib/puppet/acceptance/module_utils.rb | 45 +++++++++++++ .../tests/modules/install/with_environment.rb | 41 +++++------- .../tests/modules/list/with_environment.rb | 49 +++++++------- .../modules/uninstall/with_environment.rb | 65 +++++++++++-------- .../tests/modules/upgrade/with_environment.rb | 45 ++++++------- 5 files changed, 146 insertions(+), 99 deletions(-) diff --git a/acceptance/lib/puppet/acceptance/module_utils.rb b/acceptance/lib/puppet/acceptance/module_utils.rb index 38d573198..ac67998a8 100644 --- a/acceptance/lib/puppet/acceptance/module_utils.rb +++ b/acceptance/lib/puppet/acceptance/module_utils.rb @@ -178,6 +178,51 @@ module Puppet on host, %Q{[ ! -d "#{moduledir}/#{module_name}" ]} end + # Create a simple legacy and directory environment at :path_to_environments. + # + # @note Also registers a teardown block to remove generated files. + # + # @param path_to_environments [String] directory to contain all the + # generated environment files + # @return [String] path to the new puppet configuration file defining the + # environments + def generate_base_legacy_and_directory_environments(path_to_environments) + puppet_conf = "#{path_to_environments}/puppet2.conf" + legacy_env = "#{path_to_environments}/legacyenv" + dir_envs = "#{path_to_environments}/environments" + + step "ensure we don't have left over bad state from another, possibly failed run" + on master, "rm -rf #{legacy_env} #{dir_envs} #{puppet_conf}" + + # and register to clean up afterwords + teardown do + on master, "rm -rf #{legacy_env} #{dir_envs} #{puppet_conf}" + end + + step 'Configure a non-default legacy and directory environment' + apply_manifest_on master, %Q{ + file { + [ + '#{legacy_env}', + '#{legacy_env}/modules', + '#{dir_envs}', + '#{dir_envs}/direnv', + ]: + ensure => directory, + } + file { + '#{puppet_conf}': + source => $settings::config, + } + } + + on master, puppet("config", "set", + "modulepath", "#{legacy_env}/modules", + "--section", "legacyenv", + "--config", puppet_conf) + + return puppet_conf + end end end end diff --git a/acceptance/tests/modules/install/with_environment.rb b/acceptance/tests/modules/install/with_environment.rb index 31d2c43ab..cb0e4388e 100644 --- a/acceptance/tests/modules/install/with_environment.rb +++ b/acceptance/tests/modules/install/with_environment.rb @@ -4,40 +4,31 @@ extend Puppet::Acceptance::ModuleUtils module_author = "pmtacceptance" module_name = "nginx" -module_dependencies = [] orig_installed_modules = get_installed_modules_for_hosts hosts teardown do rm_installed_modules_from_hosts orig_installed_modules, (get_installed_modules_for_hosts hosts) - # TODO make helper take environments into account - on master, "rm -rf #{master['puppetpath']}/testenv #{master['puppetpath']}/puppet2.conf" end step 'Setup' stub_forge_on(master) -# Configure a non-default environment -on master, "rm -rf #{master['puppetpath']}/testenv" -apply_manifest_on master, %Q{ - file { - [ - '#{master['puppetpath']}/testenv', - '#{master['puppetpath']}/testenv/modules', - ]: - ensure => directory, - } - file { - '#{master['puppetpath']}/puppet2.conf': - source => $settings::config, - } -} -on master, "{ echo '[testenv]'; echo 'modulepath=#{master['puppetpath']}/testenv/modules'; } >> #{master['puppetpath']}/puppet2.conf" +puppet_conf = generate_base_legacy_and_directory_environments(master['puppetpath']) -step 'Install a module into a non default environment' -on master, "puppet module install #{module_author}-#{module_name} --config=#{master['puppetpath']}/puppet2.conf --environment=testenv" do - assert_module_installed_ui(stdout, module_author, module_name) - assert_match(/#{master['puppetpath']}\/testenv\/modules/, stdout, - "Notice of non default install path was not displayed") +check_module_install_in = lambda do |environment, environment_path| + on master, "puppet module install #{module_author}-#{module_name} --config=#{puppet_conf} --environment=#{environment}" do + assert_module_installed_ui(stdout, module_author, module_name) + assert_match(/#{environment_path}/, stdout, + "Notice of non default install path was not displayed") + end + assert_module_installed_on_disk(master, "#{environment_path}", module_name) +end + +step 'Install a module into a non default legacy environment' do + check_module_install_in.call('legacyenv', "#{master['puppetpath']}/legacyenv/modules") +end + +step 'Install a module into a non default directory environment' do + check_module_install_in.call('direnv', "#{master['puppetpath']}/environments/direnv/modules") end -assert_module_installed_on_disk(master, "#{master['puppetpath']}/testenv/modules", module_name) diff --git a/acceptance/tests/modules/list/with_environment.rb b/acceptance/tests/modules/list/with_environment.rb index 168678473..841265e7a 100644 --- a/acceptance/tests/modules/list/with_environment.rb +++ b/acceptance/tests/modules/list/with_environment.rb @@ -1,33 +1,36 @@ test_name 'puppet module list (with environment)' +require 'puppet/acceptance/module_utils' +extend Puppet::Acceptance::ModuleUtils step 'Setup' stub_forge_on(master) -# Configure a non-default environment -on master, "rm -rf #{master['puppetpath']}/testenv" -apply_manifest_on master, %Q{ - file { - [ - '#{master['puppetpath']}/testenv', - '#{master['puppetpath']}/testenv/modules', - ]: - ensure => directory, - } - file { - '#{master['puppetpath']}/puppet2.conf': - source => $settings::config, - } -} -on master, "{ echo '[testenv]'; echo 'modulepath=#{master['puppetpath']}/testenv/modules'; } >> #{master['puppetpath']}/puppet2.conf" -on master, "puppet module install pmtacceptance-nginx --config=#{master['puppetpath']}/puppet2.conf --environment=testenv" +puppet_conf = generate_base_legacy_and_directory_environments(master['puppetpath']) -teardown do - on master, "rm -rf #{master['puppetpath']}/testenv #{master['puppetpath']}/puppet2.conf" +install_test_module_in = lambda do |environment| + on master, puppet("module", "install", + "pmtacceptance-nginx", + "--config", puppet_conf, + "--environment", environment) end -step 'List modules in a non default environment' -on master, puppet("module list --config=#{master['puppetpath']}/puppet2.conf --environment=testenv") do - assert_match(/testenv\/modules/, stdout) - assert_match(/pmtacceptance-nginx/, stdout) +check_module_list_in = lambda do |environment, environment_path| + on master, puppet("module", "list", + "--config", puppet_conf, + "--environment", environment) do + + assert_match(/#{environment_path}/, stdout) + assert_match(/pmtacceptance-nginx/, stdout) + end +end + +step 'List modules in a non default legacy environment' do + install_test_module_in.call('legacyenv') + check_module_list_in.call('legacyenv', "#{master['puppetpath']}/legacyenv/modules") +end + +step 'List modules in a non default directory environment' do + install_test_module_in.call('direnv') + check_module_list_in.call('direnv', "#{master['puppetpath']}/environments/direnv/modules") end diff --git a/acceptance/tests/modules/uninstall/with_environment.rb b/acceptance/tests/modules/uninstall/with_environment.rb index ac2aff7f2..ac35dadcd 100644 --- a/acceptance/tests/modules/uninstall/with_environment.rb +++ b/acceptance/tests/modules/uninstall/with_environment.rb @@ -1,47 +1,58 @@ test_name 'puppet module uninstall (with environment)' - -teardown do -on master, "rm -rf #{master['puppetpath']}/testenv #{master['puppetpath']}/puppet2.conf" -end +require 'puppet/acceptance/module_utils' +extend Puppet::Acceptance::ModuleUtils step 'Setup' stub_forge_on(master) +puppet_conf = generate_base_legacy_and_directory_environments(master['puppetpath']) + +crakorn_metadata = <<-EOS +{ + "name": "jimmy/crakorn", + "version": "0.4.0", + "source": "", + "author": "jimmy", + "license": "MIT", + "dependencies": [] +} +EOS + # Configure a non-default environment -on master, "rm -rf #{master['puppetpath']}/testenv" apply_manifest_on master, %Q{ file { [ - '#{master['puppetpath']}/testenv', - '#{master['puppetpath']}/testenv/modules', - '#{master['puppetpath']}/testenv/modules/crakorn', + '#{master['puppetpath']}/legacyenv/modules/crakorn', + '#{master['puppetpath']}/environments/direnv/modules', + '#{master['puppetpath']}/environments/direnv/modules/crakorn', ]: ensure => directory, } file { - '#{master['puppetpath']}/testenv/modules/crakorn/metadata.json': - content => '{ - "name": "jimmy/crakorn", - "version": "0.4.0", - "source": "", - "author": "jimmy", - "license": "MIT", - "dependencies": [] - }', + '#{master['puppetpath']}/legacyenv/modules/crakorn/metadata.json': + content => '#{crakorn_metadata}', } file { - '#{master['puppetpath']}/puppet2.conf': - source => $settings::config, + '#{master['puppetpath']}/environments/direnv/modules/crakorn/metadata.json': + content => '#{crakorn_metadata}', } } -on master, %Q{{ echo "[testenv]"; echo "modulepath=#{master['puppetpath']}/testenv/modules"; } >> #{master['puppetpath']}/puppet2.conf} -step 'Uninstall a module from a non default environment' -on master, "puppet module uninstall jimmy-crakorn --config=#{master['puppetpath']}/puppet2.conf --environment=testenv" do - assert_output <<-OUTPUT - \e[mNotice: Preparing to uninstall 'jimmy-crakorn' ...\e[0m - Removed 'jimmy-crakorn' (\e[0;36mv0.4.0\e[0m) from #{master['puppetpath']}/testenv/modules - OUTPUT +check_module_uninstall_in = lambda do |environment, environment_path| + on master, "puppet module uninstall jimmy-crakorn --config=#{puppet_conf} --environment=#{environment}" do + assert_output <<-OUTPUT + \e[mNotice: Preparing to uninstall 'jimmy-crakorn' ...\e[0m + Removed 'jimmy-crakorn' (\e[0;36mv0.4.0\e[0m) from #{environment_path} + OUTPUT + end + on master, "[ ! -d #{environment_path}/crakorn ]" +end + +step 'Uninstall a module from a non default legacy environment' do + check_module_uninstall_in.call('legacyenv', "#{master['puppetpath']}/legacyenv/modules") +end + +step 'Uninstall a module from a non default directory environment' do + check_module_uninstall_in.call('direnv', "#{master['puppetpath']}/environments/direnv/modules") end -on master, "[ ! -d #{master['puppetpath']}/testenv/modules/crakorn ]" diff --git a/acceptance/tests/modules/upgrade/with_environment.rb b/acceptance/tests/modules/upgrade/with_environment.rb index 28824b19b..4dc5dba4c 100644 --- a/acceptance/tests/modules/upgrade/with_environment.rb +++ b/acceptance/tests/modules/upgrade/with_environment.rb @@ -9,37 +9,34 @@ module_dependencies = ["stdlib"] orig_installed_modules = get_installed_modules_for_hosts hosts teardown do rm_installed_modules_from_hosts orig_installed_modules, (get_installed_modules_for_hosts hosts) - # TODO make helper take environments into account - on master, "rm -rf #{master['puppetpath']}/testenv #{master['puppetpath']}/puppet2.conf" end step 'Setup' stub_forge_on(master) -# Configure a non-default environment -apply_manifest_on master, %Q{ - file { - [ - '#{master['puppetpath']}/testenv', - '#{master['puppetpath']}/testenv/modules', - ]: - ensure => directory, - } - file { - '#{master['puppetpath']}/puppet2.conf': - source => $settings::config, - } -} -on master, "{ echo '[testenv]'; echo 'modulepath=#{master['puppetpath']}/testenv/modules'; } >> #{master['puppetpath']}/puppet2.conf" +puppet_conf = generate_base_legacy_and_directory_environments(master['puppetpath']) -on master, puppet("module install #{module_author}-#{module_name} --config=#{master['puppetpath']}/puppet2.conf --version 1.6.0 --environment=testenv") do - assert_module_installed_ui(stdout, module_author, module_name) +install_test_module_in = lambda do |environment| + on master, puppet("module install #{module_author}-#{module_name} --config=#{puppet_conf} --version 1.6.0 --environment=#{environment}") do + assert_module_installed_ui(stdout, module_author, module_name) + end end -step "Upgrade a module that has a more recent version published" -on master, puppet("module upgrade #{module_author}-#{module_name} --config=#{master['puppetpath']}/puppet2.conf --environment=testenv") do - assert_module_installed_ui(stdout, module_author, module_name) - on master, "[ -f #{master['puppetpath']}/testenv/modules/#{module_name}/Modulefile ]" - on master, "grep 1.7.1 #{master['puppetpath']}/testenv/modules/#{module_name}/Modulefile" +check_module_upgrade_in = lambda do |environment, environment_path| + on master, puppet("module upgrade #{module_author}-#{module_name} --config=#{puppet_conf} --environment=#{environment}") do + assert_module_installed_ui(stdout, module_author, module_name) + on master, "[ -f #{environment_path}/#{module_name}/Modulefile ]" + on master, "grep 1.7.1 #{environment_path}/#{module_name}/Modulefile" + end +end + +step "Upgrade a module that has a more recent version published in a legacy environment" do + install_test_module_in.call('legacyenv') + check_module_upgrade_in.call('legacyenv', "#{master['puppetpath']}/legacyenv/modules") +end + +step "Upgrade a module that has a more recent version published in a directory environment" do + install_test_module_in.call('direnv') + check_module_upgrade_in.call('direnv', "#{master['puppetpath']}/environments/direnv/modules") end From 3bd0a2612a372cf33c0bf612e4b1c5357f2cf158 Mon Sep 17 00:00:00 2001 From: Nick Fagerlund Date: Fri, 14 Feb 2014 16:31:59 -0800 Subject: [PATCH 680/800] (maint) Banish "configuration parameter" They are called settings! Note: Two spec files relied on the wording of an error message I changed. I revised them to find the new wording. --- ext/nagios/naggen | 2 +- ext/puppet-test | 4 ++-- ext/upload_facts.rb | 2 +- lib/puppet.rb | 4 ++-- lib/puppet/application/apply.rb | 4 ++-- lib/puppet/application/cert.rb | 4 ++-- lib/puppet/application/device.rb | 2 +- lib/puppet/application/filebucket.rb | 4 ++-- lib/puppet/application/kick.rb | 4 ++-- lib/puppet/application/queue.rb | 4 ++-- lib/puppet/application/resource.rb | 4 ++-- lib/puppet/face/help/man.erb | 4 ++-- lib/puppet/reference/configuration.rb | 2 +- lib/puppet/settings.rb | 20 ++++++++++---------- lib/puppet/type/file.rb | 2 +- spec/unit/hiera_puppet_spec.rb | 6 +++--- spec/unit/settings_spec.rb | 2 +- 17 files changed, 37 insertions(+), 37 deletions(-) diff --git a/ext/nagios/naggen b/ext/nagios/naggen index 6d5be7b0f..0d0e5824f 100755 --- a/ext/nagios/naggen +++ b/ext/nagios/naggen @@ -21,7 +21,7 @@ # # = Options # -# Note that any configuration parameter that's valid in the configuration file +# Note that any setting that's valid in the configuration file # is also a valid long argument. For example, 'ssldir' is a valid configuration # parameter, so you can specify '--ssldir ' as an argument. # diff --git a/ext/puppet-test b/ext/puppet-test index 2078806e7..47c464f86 100755 --- a/ext/puppet-test +++ b/ext/puppet-test @@ -18,7 +18,7 @@ # # = Options # -# Note that any configuration parameter that's valid in the configuration file +# Note that any setting that's valid in the configuration file # is also a valid long argument. For example, 'server' is a valid configuration # parameter, so you can specify '--server ' as an argument. # @@ -474,7 +474,7 @@ begin explicit_waitforcert = false result.each { |opt,arg| case opt - # First check to see if the argument is a valid configuration parameter; + # First check to see if the argument is a valid setting; # if so, set it. when "--compile" $options[:suite] = :configuration diff --git a/ext/upload_facts.rb b/ext/upload_facts.rb index 786c921d8..6e4e30792 100755 --- a/ext/upload_facts.rb +++ b/ext/upload_facts.rb @@ -53,7 +53,7 @@ uploaded. = Options -Note that any configuration parameter that's valid in the configuration file +Note that any setting that's valid in the configuration file is also a valid long argument. For example, 'server' is a valid configuration parameter, so you can specify '--server ' as an argument. diff --git a/lib/puppet.rb b/lib/puppet.rb index ece55c6ab..6b1cee7e5 100644 --- a/lib/puppet.rb +++ b/lib/puppet.rb @@ -71,7 +71,7 @@ module Puppet end end - # configuration parameter access and stuff + # setting access and stuff def self.[]=(param,value) @@settings[param] = value end @@ -107,7 +107,7 @@ module Puppet Puppet::Util::RunMode[@@settings.preferred_run_mode] end - # Load all of the configuration parameters. + # Load all of the settings. require 'puppet/defaults' def self.genmanifest diff --git a/lib/puppet/application/apply.rb b/lib/puppet/application/apply.rb index 78348f7d0..b71ec8fae 100644 --- a/lib/puppet/application/apply.rb +++ b/lib/puppet/application/apply.rb @@ -64,9 +64,9 @@ manifests. OPTIONS ------- -Note that any configuration parameter that's valid in the configuration +Note that any setting that's valid in the configuration file is also a valid long argument. For example, 'tags' is a -valid configuration parameter, so you can specify '--tags ,' +valid setting, so you can specify '--tags ,' as an argument. See the configuration file documentation at diff --git a/lib/puppet/application/cert.rb b/lib/puppet/application/cert.rb index 6ad901593..818e95cd5 100644 --- a/lib/puppet/application/cert.rb +++ b/lib/puppet/application/cert.rb @@ -154,9 +154,9 @@ unless the '--all' option is set. OPTIONS ------- -Note that any configuration parameter that's valid in the configuration +Note that any setting that's valid in the configuration file is also a valid long argument. For example, 'ssldir' is a valid -configuration parameter, so you can specify '--ssldir ' as an +setting, so you can specify '--ssldir ' as an argument. See the configuration file documentation at diff --git a/lib/puppet/application/device.rb b/lib/puppet/application/device.rb index 5eef49fef..a293ab483 100644 --- a/lib/puppet/application/device.rb +++ b/lib/puppet/application/device.rb @@ -107,7 +107,7 @@ Supported url must conforms to: OPTIONS ------- -Note that any configuration parameter that's valid in the configuration file +Note that any setting that's valid in the configuration file is also a valid long argument. For example, 'server' is a valid configuration parameter, so you can specify '--server ' as an argument. diff --git a/lib/puppet/application/filebucket.rb b/lib/puppet/application/filebucket.rb index 3a2e10b86..3f59f7259 100644 --- a/lib/puppet/application/filebucket.rb +++ b/lib/puppet/application/filebucket.rb @@ -57,9 +57,9 @@ use your local file bucket by specifying '--local'. OPTIONS ------- -Note that any configuration parameter that's valid in the configuration +Note that any setting that's valid in the configuration file is also a valid long argument. For example, 'ssldir' is a valid -configuration parameter, so you can specify '--ssldir ' as an +setting, so you can specify '--ssldir ' as an argument. See the configuration file documentation at diff --git a/lib/puppet/application/kick.rb b/lib/puppet/application/kick.rb index 960cdc867..f5e36dd3c 100644 --- a/lib/puppet/application/kick.rb +++ b/lib/puppet/application/kick.rb @@ -102,9 +102,9 @@ about security settings. OPTIONS ------- -Note that any configuration parameter that's valid in the configuration +Note that any setting that's valid in the configuration file is also a valid long argument. For example, 'ssldir' is a valid -configuration parameter, so you can specify '--ssldir ' as an +setting, so you can specify '--ssldir ' as an argument. See the configuration file documentation at diff --git a/lib/puppet/application/queue.rb b/lib/puppet/application/queue.rb index 0f6e95f7b..1efca9be2 100644 --- a/lib/puppet/application/queue.rb +++ b/lib/puppet/application/queue.rb @@ -70,9 +70,9 @@ http://docs.puppetlabs.com/puppetdb/latest OPTIONS ------- -Note that any configuration parameter that's valid in the configuration +Note that any setting that's valid in the configuration file is also a valid long argument. For example, 'server' is a valid -configuration parameter, so you can specify '--server ' as +setting, so you can specify '--server ' as an argument. See the configuration file documentation at diff --git a/lib/puppet/application/resource.rb b/lib/puppet/application/resource.rb index 35f9f155b..c03181671 100644 --- a/lib/puppet/application/resource.rb +++ b/lib/puppet/application/resource.rb @@ -71,9 +71,9 @@ and then apply the saved file as a Puppet transaction. OPTIONS ------- -Note that any configuration parameter that's valid in the configuration +Note that any setting that's valid in the configuration file is also a valid long argument. For example, 'ssldir' is a valid -configuration parameter, so you can specify '--ssldir ' as an +setting, so you can specify '--ssldir ' as an argument. See the configuration file documentation at diff --git a/lib/puppet/face/help/man.erb b/lib/puppet/face/help/man.erb index bd584889c..7e693dd5e 100644 --- a/lib/puppet/face/help/man.erb +++ b/lib/puppet/face/help/man.erb @@ -15,10 +15,10 @@ DESCRIPTION <% end -%> OPTIONS ------- -Note that any configuration parameter that's valid in the configuration +Note that any setting that's valid in the configuration file is also a valid long argument, although it may or may not be relevant to the present action. For example, `server` and `run_mode` are valid -configuration parameters, so you can specify `--server `, or +settings, so you can specify `--server `, or `--run_mode ` as an argument. See the configuration file documentation at diff --git a/lib/puppet/reference/configuration.rb b/lib/puppet/reference/configuration.rb index c1580fe35..0475067a3 100644 --- a/lib/puppet/reference/configuration.rb +++ b/lib/puppet/reference/configuration.rb @@ -1,4 +1,4 @@ -config = Puppet::Util::Reference.newreference(:configuration, :depth => 1, :doc => "A reference for all configuration parameters") do +config = Puppet::Util::Reference.newreference(:configuration, :depth => 1, :doc => "A reference for all settings") do docs = {} Puppet.settings.each do |name, object| docs[name] = object diff --git a/lib/puppet/settings.rb b/lib/puppet/settings.rb index c37a7010f..c4bfc1e55 100644 --- a/lib/puppet/settings.rb +++ b/lib/puppet/settings.rb @@ -114,7 +114,7 @@ class Puppet::Settings # Generate the list of valid arguments, in a format that GetoptLong can # understand, and add them to the passed option list. def addargs(options) - # Add all of the config parameters as valid options. + # Add all of the settings as valid options. self.each { |name, setting| setting.getopt_args.each { |args| options << args } } @@ -125,7 +125,7 @@ class Puppet::Settings # Generate the list of valid arguments, in a format that OptionParser can # understand, and add them to the passed option list. def optparse_addargs(options) - # Add all of the config parameters as valid options. + # Add all of the settings as valid options. self.each { |name, setting| options << setting.optparse_args } @@ -133,7 +133,7 @@ class Puppet::Settings options end - # Is our parameter a boolean parameter? + # Is our setting a boolean setting? def boolean?(param) param = param.to_sym @config.include?(param) and @config[param].kind_of?(BooleanSetting) @@ -405,7 +405,7 @@ class Puppet::Settings end puts "#{v} = #{value(v,env)}" else - puts "invalid parameter: #{v}" + puts "invalid setting: #{v}" return false end end @@ -477,7 +477,7 @@ class Puppet::Settings mode end - # Return all of the parameters associated with a given section. + # Return all of the settings associated with a given section. def params(section = nil) if section section = section.intern if section.is_a? String @@ -811,11 +811,11 @@ class Puppet::Settings name = name.to_sym hash[:name] = name hash[:section] = section - raise ArgumentError, "Parameter #{name} is already defined" if @config.include?(name) + raise ArgumentError, "Setting #{name} is already defined" if @config.include?(name) tryconfig = newsetting(hash) if short = tryconfig.short if other = @shortnames[short] - raise ArgumentError, "Parameter #{other.name} is already using short name '#{short}'" + raise ArgumentError, "Setting #{other.name} is already using short name '#{short}'" end @shortnames[short] = tryconfig end @@ -863,7 +863,7 @@ class Puppet::Settings # Convert our list of config settings into a configuration file. def to_config str = %{The configuration file for #{Puppet.run_mode.name}. Note that this file -is likely to have unused configuration parameters in it; any parameter that's +is likely to have unused settings in it; any setting that's valid anywhere in Puppet can be in any config file, even if it's not used. Every section can specify three special parameters: owner, group, and mode. @@ -972,7 +972,7 @@ Generated on #{Time.now}. setting = @config[param] - # Short circuit to nil for undefined parameters. + # Short circuit to nil for undefined settings. return nil if setting.nil? # Check the cache first. It needs to be a per-environment @@ -1218,7 +1218,7 @@ Generated on #{Time.now}. def set(name, value) if !@defaults[name] raise ArgumentError, - "Attempt to assign a value to unknown configuration parameter #{name.inspect}" + "Attempt to assign a value to unknown setting #{name.inspect}" end if @defaults[name].has_hook? diff --git a/lib/puppet/type/file.rb b/lib/puppet/type/file.rb index 9351bc0fd..b49452c11 100644 --- a/lib/puppet/type/file.rb +++ b/lib/puppet/type/file.rb @@ -250,7 +250,7 @@ Puppet::Type.newtype(:file) do desc "Whether to display differences when the file changes, defaulting to true. This parameter is useful for files that may contain passwords or other secret data, which might otherwise be included in Puppet reports or - other insecure outputs. If the global ``show_diff` configuration parameter + other insecure outputs. If the global ``show_diff` setting is false, then no diffs will be shown even if this parameter is true." defaultto :true diff --git a/spec/unit/hiera_puppet_spec.rb b/spec/unit/hiera_puppet_spec.rb index 782f12e25..221dfa9c6 100644 --- a/spec/unit/hiera_puppet_spec.rb +++ b/spec/unit/hiera_puppet_spec.rb @@ -46,7 +46,7 @@ describe 'HieraPuppet' do begin Puppet.settings[:hiera_config] = nil rescue ArgumentError => detail - raise unless detail.message =~ /unknown configuration parameter/ + raise unless detail.message =~ /unknown setting/ end HieraPuppet.send(:hiera_config_file).should be_nil end @@ -55,7 +55,7 @@ describe 'HieraPuppet' do begin Puppet.settings[:hiera_config] = "/dev/null/my_hiera.yaml" rescue ArgumentError => detail - raise unless detail.message =~ /unknown configuration parameter/ + raise unless detail.message =~ /unknown setting/ pending("This example does not apply to Puppet #{Puppet.version} because it does not have this setting") end @@ -67,7 +67,7 @@ describe 'HieraPuppet' do begin Puppet.settings[:hiera_config] = nil rescue ArgumentError => detail - raise unless detail.message =~ /unknown configuration parameter/ + raise unless detail.message =~ /unknown setting/ end Puppet.settings[:confdir] = "/dev/null/puppet" hiera_config = File.join(Puppet[:confdir], 'hiera.yaml') diff --git a/spec/unit/settings_spec.rb b/spec/unit/settings_spec.rb index b201b3eb8..b2da588b3 100755 --- a/spec/unit/settings_spec.rb +++ b/spec/unit/settings_spec.rb @@ -471,7 +471,7 @@ describe Puppet::Settings do it "should raise an error if we try to set a setting that hasn't been defined'" do lambda{ @settings[:why_so_serious] = "foo" - }.should raise_error(ArgumentError, /unknown configuration parameter/) + }.should raise_error(ArgumentError, /unknown setting/) end end From a07e73b25100f6e958b78e3c53f2f0889eaa2bfb Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Thu, 13 Feb 2014 14:48:51 -0800 Subject: [PATCH 681/800] (PUP-1584) module_tool uses current_environment with target_dir overrides The module tool was relying on the implicit creation and caching of legacy environments based on puppet settings for environment and modulepath. But this does not work for the new directory style of environments. So we are being explicit about using the current environment from the context, and overriding modulepath to prepend a target_dir if given, and then directly inserting the environment to be used into the options rather than expecting the module_tool applications to retrieve the correct environment from any other loaders. Testing is now simpler, but because we are now comparing different but equal environments, I added equality and hash to node/environment to facilitate this. This in turn broke tests, because of Puppet::Node::Environment#known_resource_types which caches using the global $known_resource_types. Previously this would always clear itself when called if $known_resource_types.environment != self, but with the new node equality, state was now leaking between tests. The test_helper now sets $known_resource_types nil after each test. --- lib/puppet/application.rb | 6 +- lib/puppet/module_tool.rb | 46 +++---- .../module_tool/applications/installer.rb | 2 +- .../module_tool/applications/uninstaller.rb | 2 +- .../module_tool/applications/upgrader.rb | 2 +- lib/puppet/node/environment.rb | 11 ++ spec/unit/face/module/install_spec.rb | 129 ++++-------------- spec/unit/face/module/uninstall_spec.rb | 43 +++--- .../applications/checksummer_spec.rb | 2 +- .../applications/installer_spec.rb | 25 ++-- .../applications/uninstaller_spec.rb | 8 +- spec/unit/module_tool_spec.rb | 108 +++------------ 12 files changed, 109 insertions(+), 275 deletions(-) diff --git a/lib/puppet/application.rb b/lib/puppet/application.rb index 7d79fa778..3649f0841 100644 --- a/lib/puppet/application.rb +++ b/lib/puppet/application.rb @@ -353,9 +353,11 @@ class Application plugin_hook('initialize_app_defaults') { initialize_app_defaults } end + new_context = Puppet.base_context(Puppet.settings) + new_context[:current_environment] = new_context[:environments].get(Puppet[:environment]) # Setup a new context using the app's configuration - Puppet.override(Puppet.base_context(Puppet.settings), - "Base context from application's configuration") do + Puppet.override(new_context, + "New base context and current environment from application's configuration") do require 'puppet' require 'puppet/util/instrumentation' Puppet::Util::Instrumentation.init diff --git a/lib/puppet/module_tool.rb b/lib/puppet/module_tool.rb index eb6d91f9a..8ea5754cd 100644 --- a/lib/puppet/module_tool.rb +++ b/lib/puppet/module_tool.rb @@ -103,34 +103,30 @@ module Puppet end end + # @param options [Hash] This hash will contain any + # command-line arguments that are not Settings, as those will have already + # been extracted by the underlying application code. + # + # @note Unfortunately the whole point of this method is the side effect of + # modifying the options parameter. This same hash is referenced both + # when_invoked and when_rendering. For this reason, we are not returning + # a duplicate. + # + # An :environment_instance and a :target_dir are added/updated in the + # options parameter. + # + # @api private def self.set_option_defaults(options) - sep = File::PATH_SEPARATOR + current_environment = Puppet.lookup(:current_environment) + modulepath = [options[:target_dir]] + current_environment.full_modulepath - if options[:environment] - Puppet.settings[:environment] = options[:environment] - else - options[:environment] = Puppet.settings[:environment] - end + face_environment = current_environment.override_with( + :modulepath => modulepath.compact + ) - if options[:modulepath] - Puppet.settings[:modulepath] = options[:modulepath] - else - # (#14872) make sure the module path of the desired environment is used - # when determining the default value of the --target-dir option - Puppet.settings[:modulepath] = options[:modulepath] = - Puppet.settings.value(:modulepath, options[:environment]) - end - - if options[:target_dir] - options[:target_dir] = File.expand_path(options[:target_dir]) - # prepend the target dir to the module path - Puppet.settings[:modulepath] = options[:modulepath] = - options[:target_dir] + sep + options[:modulepath] - else - # default to the first component of the module path - options[:target_dir] = - File.expand_path(options[:modulepath].split(sep).first) - end + options[:environment_instance] = face_environment + # Note: environment will have expanded the path + options[:target_dir] = face_environment.full_modulepath.first end end end diff --git a/lib/puppet/module_tool/applications/installer.rb b/lib/puppet/module_tool/applications/installer.rb index 26e791da5..0b722f808 100644 --- a/lib/puppet/module_tool/applications/installer.rb +++ b/lib/puppet/module_tool/applications/installer.rb @@ -18,7 +18,7 @@ module Puppet::ModuleTool def initialize(name, forge, install_dir, options = {}) super(options) @action = :install - @environment = Puppet.lookup(:environments).get(Puppet[:environment]) + @environment = options[:environment_instance] @force = options[:force] @ignore_dependencies = options[:force] || options[:ignore_dependencies] @name = name diff --git a/lib/puppet/module_tool/applications/uninstaller.rb b/lib/puppet/module_tool/applications/uninstaller.rb index 3cd12394e..c07c98737 100644 --- a/lib/puppet/module_tool/applications/uninstaller.rb +++ b/lib/puppet/module_tool/applications/uninstaller.rb @@ -10,7 +10,7 @@ module Puppet::ModuleTool @unfiltered = [] @installed = [] @suggestions = [] - @environment = Puppet.lookup(:environments).get(options[:environment]) + @environment = options[:environment_instance] end def run diff --git a/lib/puppet/module_tool/applications/upgrader.rb b/lib/puppet/module_tool/applications/upgrader.rb index cb9d0fa54..b2e617ba4 100644 --- a/lib/puppet/module_tool/applications/upgrader.rb +++ b/lib/puppet/module_tool/applications/upgrader.rb @@ -6,7 +6,7 @@ module Puppet::ModuleTool def initialize(name, forge, options) @action = :upgrade - @environment = Puppet.lookup(:environments).get(Puppet[:environment]) + @environment = options[:environment_instance] @module_name = name @options = options @force = options[:force] diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb index 9fa63fd63..7477b7385 100644 --- a/lib/puppet/node/environment.rb +++ b/lib/puppet/node/environment.rb @@ -372,6 +372,17 @@ class Puppet::Node::Environment path_string.split(File::PATH_SEPARATOR) end + def ==(other) + return true if other.kind_of?(Puppet::Node::Environment) && + self.name == other.name && + self.full_modulepath == other.full_modulepath && + self.manifest == other.manifest + end + + def hash + [self.class, name, full_modulepath, manifest].hash + end + private def self.extralibs() diff --git a/spec/unit/face/module/install_spec.rb b/spec/unit/face/module/install_spec.rb index a8c18dd1c..868b22511 100644 --- a/spec/unit/face/module/install_spec.rb +++ b/spec/unit/face/module/install_spec.rb @@ -7,43 +7,43 @@ describe "puppet module install" do subject { Puppet::Face[:module, :current] } - let(:options) do - {} - end - describe "option validation" do - before do - Puppet.settings[:modulepath] = fakemodpath - end - - let(:expected_options) do - { - :target_dir => fakefirstpath, - :modulepath => fakemodpath, - :environment => 'production' - } - end - let(:sep) { File::PATH_SEPARATOR } let(:fakefirstpath) { make_absolute("/my/fake/modpath") } let(:fakesecondpath) { make_absolute("/other/fake/path") } let(:fakemodpath) { "#{fakefirstpath}#{sep}#{fakesecondpath}" } let(:fakedirpath) { make_absolute("/my/fake/path") } + let(:options) { {} } + let(:environment) do + Puppet::Node::Environment.create(:env, [fakefirstpath, fakesecondpath], '') + end + let(:expected_options) do + { + :target_dir => fakefirstpath, + :environment_instance => environment, + } + end + + around(:each) do |example| + Puppet.override(:current_environment => environment) do + example.run + end + end context "without any options" do - it "should require a name" do + it "requires a name" do pattern = /wrong number of arguments/ expect { subject.install }.to raise_error ArgumentError, pattern end - it "should not require any options" do + it "does not require any options" do expects_installer_run_with("puppetlabs-apache", expected_options) subject.install("puppetlabs-apache") end end - it "should accept the --force option" do + it "accepts the --force option" do options[:force] = true expected_options.merge!(options) @@ -52,17 +52,17 @@ describe "puppet module install" do subject.install("puppetlabs-apache", options) end - it "should accept the --target-dir option" do + it "accepts the --target-dir option" do options[:target_dir] = make_absolute("/foo/puppet/modules") expected_options.merge!(options) - expected_options[:modulepath] = "#{options[:target_dir]}#{sep}#{fakemodpath}" + expected_options[:environment_instance] = environment.override_with(:modulepath => [options[:target_dir], fakefirstpath, fakesecondpath]) expects_installer_run_with("puppetlabs-apache", expected_options) subject.install("puppetlabs-apache", options) end - it "should accept the --version option" do + it "accepts the --version option" do options[:version] = "0.0.1" expected_options.merge!(options) @@ -71,7 +71,7 @@ describe "puppet module install" do subject.install("puppetlabs-apache", options) end - it "should accept the --ignore-dependencies option" do + it "accepts the --ignore-dependencies option" do options[:ignore_dependencies] = true expected_options.merge!(options) @@ -79,89 +79,6 @@ describe "puppet module install" do subject.install("puppetlabs-apache", options) end - - describe "when modulepath option is passed" do - let(:expected_options) { { :modulepath => fakemodpath, :environment => Puppet[:environment] } } - let(:options) { { :modulepath => fakemodpath } } - - describe "when target-dir option is not passed" do - it "should set target-dir to be first path from modulepath" do - expected_options[:target_dir] = fakefirstpath - - expects_installer_run_with("puppetlabs-apache", expected_options) - - Puppet::Face[:module, :current].install("puppetlabs-apache", options) - - Puppet.settings[:modulepath].should == fakemodpath - end - - it "should expand the target directory derived from the modulepath" do - options[:modulepath] = "modules" - expanded_path = File.expand_path("modules") - expected_options.merge!(options) - expected_options[:target_dir] = expanded_path - expected_options[:modulepath] = "modules" - - expects_installer_run_with("puppetlabs-apache", expected_options) - Puppet::Face[:module, :current].install("puppetlabs-apache", options) - end - end - - describe "when target-dir option is passed" do - it "should set target-dir to be first path of modulepath" do - options[:target_dir] = fakedirpath - expected_options[:target_dir] = fakedirpath - expected_options[:modulepath] = "#{fakedirpath}#{sep}#{fakemodpath}" - - expects_installer_run_with("puppetlabs-apache", expected_options) - - Puppet::Face[:module, :current].install("puppetlabs-apache", options) - - Puppet.settings[:modulepath].should == "#{fakedirpath}#{sep}#{fakemodpath}" - end - end - end - - describe "when modulepath option is not passed" do - before do - Puppet.settings[:modulepath] = fakemodpath - end - - describe "when target-dir option is not passed" do - it "should set target-dir to be first path of default mod path" do - expected_options[:target_dir] = fakefirstpath - expected_options[:modulepath] = fakemodpath - - expects_installer_run_with("puppetlabs-apache", expected_options) - - Puppet::Face[:module, :current].install("puppetlabs-apache", options) - end - end - - describe "when target-dir option is passed" do - it "should prepend target-dir to modulepath" do - options[:target_dir] = fakedirpath - expected_options[:target_dir] = fakedirpath - expected_options[:modulepath] = "#{options[:target_dir]}#{sep}#{fakemodpath}" - - expects_installer_run_with("puppetlabs-apache", expected_options) - - Puppet::Face[:module, :current].install("puppetlabs-apache", options) - Puppet.settings[:modulepath].should == expected_options[:modulepath] - end - - it "should expand the target directory when target_dir is set" do - options[:target_dir] = "modules" - expanded_path = File.expand_path("modules") - expected_options.merge!(options) - expected_options[:target_dir] = expanded_path - expected_options[:modulepath] = "#{expanded_path}#{sep}#{fakemodpath}" - - expects_installer_run_with("puppetlabs-apache", expected_options) - Puppet::Face[:module, :current].install("puppetlabs-apache", options) - end - end - end end describe "inline documentation" do diff --git a/spec/unit/face/module/uninstall_spec.rb b/spec/unit/face/module/uninstall_spec.rb index f75a1f9cc..5f480d993 100644 --- a/spec/unit/face/module/uninstall_spec.rb +++ b/spec/unit/face/module/uninstall_spec.rb @@ -8,8 +8,24 @@ describe "puppet module uninstall" do let(:options) do {} end + let(:modulepath) { '/module/path' } + let(:environment) do + Puppet::Node::Environment.create(:env, [modulepath], '') + end + let(:expected_options) do + { + :target_dir => modulepath, + :environment_instance => environment, + } + end describe "option validation" do + around(:each) do |example| + Puppet.override(:current_environment => environment) do + example.run + end + end + context "without any options" do it "should require a name" do pattern = /wrong number of arguments/ @@ -22,39 +38,16 @@ describe "puppet module uninstall" do end end - it "should accept the --environment option" do - options[:environment] = "development" - expected_options = { :environment => 'development' } - Puppet::ModuleTool::Applications::Uninstaller.expects(:run).with("puppetlabs-apache", has_entries(expected_options)).once - subject.uninstall("puppetlabs-apache", options) - end - - it "should accept the --modulepath option" do - options[:modulepath] = "/foo/puppet/modules" - expected_options = { - :modulepath => '/foo/puppet/modules', - :environment => 'production', - } - Puppet::ModuleTool::Applications::Uninstaller.expects(:run).with("puppetlabs-apache", has_entries(expected_options)).once - subject.uninstall("puppetlabs-apache", options) - end - it "should accept the --version option" do options[:version] = "1.0.0" - expected_options = { - :version => '1.0.0', - :environment => 'production', - } + expected_options.merge!(:version => '1.0.0') Puppet::ModuleTool::Applications::Uninstaller.expects(:run).with("puppetlabs-apache", has_entries(expected_options)).once subject.uninstall("puppetlabs-apache", options) end it "should accept the --force flag" do options[:force] = true - expected_options = { - :environment => 'production', - :force => true, - } + expected_options.merge!(:force => true) Puppet::ModuleTool::Applications::Uninstaller.expects(:run).with("puppetlabs-apache", has_entries(expected_options)).once subject.uninstall("puppetlabs-apache", options) end diff --git a/spec/unit/module_tool/applications/checksummer_spec.rb b/spec/unit/module_tool/applications/checksummer_spec.rb index 96010f96c..a9db5b430 100644 --- a/spec/unit/module_tool/applications/checksummer_spec.rb +++ b/spec/unit/module_tool/applications/checksummer_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' require 'puppet/module_tool/applications' -describe Puppet::ModuleTool::Applications::Checksummer, :unless => Puppet.features.microsoft_windows? do +describe Puppet::ModuleTool::Applications::Checksummer do subject { Puppet::ModuleTool::Applications::Checksummer.new(module_install_path) } diff --git a/spec/unit/module_tool/applications/installer_spec.rb b/spec/unit/module_tool/applications/installer_spec.rb index dd99fadae..ab9de4403 100644 --- a/spec/unit/module_tool/applications/installer_spec.rb +++ b/spec/unit/module_tool/applications/installer_spec.rb @@ -3,7 +3,7 @@ require 'puppet/module_tool/applications' require 'puppet_spec/modules' require 'semver' -describe Puppet::ModuleTool::Applications::Installer, :unless => Puppet.features.microsoft_windows? do +describe Puppet::ModuleTool::Applications::Installer do include PuppetSpec::Files let(:unpacker) { stub(:run) } @@ -19,16 +19,11 @@ describe Puppet::ModuleTool::Applications::Installer, :unless => Puppet.features mod end let(:env) { Puppet::Node::Environment.create(:env, [modpath1], '') } - let(:options) { { :target_dir => modpath1 } } - - before do - Puppet[:environment] = :env - end - - around :each do |example| - Puppet.override(:environments => Puppet::Environments::Static.new(env)) do - example.run - end + let(:options) do + { + :target_dir => modpath1, + :environment_instance => env, + } end let(:forge) do @@ -228,7 +223,7 @@ describe Puppet::ModuleTool::Applications::Installer, :unless => Puppet.features end it "should install requested module if the '--force' flag is used" do - options = { :force => true, :target_dir => modpath1 } + options.merge!(:force => true) Puppet::ModuleTool::Applications::Unpacker.expects(:new). with('/fake_cache/pmtacceptance-apollo-0.0.2.tar.gz', options). returns(unpacker) @@ -237,7 +232,7 @@ describe Puppet::ModuleTool::Applications::Installer, :unless => Puppet.features end it "should not install dependencies if the '--force' flag is used" do - options = { :force => true, :target_dir => modpath1 } + options.merge!(:force => true) Puppet::ModuleTool::Applications::Unpacker.expects(:new). with('/fake_cache/pmtacceptance-apollo-0.0.2.tar.gz', options). returns(unpacker) @@ -247,7 +242,7 @@ describe Puppet::ModuleTool::Applications::Installer, :unless => Puppet.features end it "should not install dependencies if the '--ignore-dependencies' flag is used" do - options = { :ignore_dependencies => true, :target_dir => modpath1 } + options.merge!(:ignore_dependencies => true) Puppet::ModuleTool::Applications::Unpacker.expects(:new). with('/fake_cache/pmtacceptance-apollo-0.0.2.tar.gz', options). returns(unpacker) @@ -257,7 +252,7 @@ describe Puppet::ModuleTool::Applications::Installer, :unless => Puppet.features end it "should set an error if dependencies can't be resolved" do - options = { :version => '0.0.1', :target_dir => modpath1 } + options.merge!(:version => '0.0.1') oneline = "'pmtacceptance-apollo' (v0.0.1) requested; Invalid dependency cycle" multiline = <<-MSG.strip Could not install module 'pmtacceptance-apollo' (v0.0.1) diff --git a/spec/unit/module_tool/applications/uninstaller_spec.rb b/spec/unit/module_tool/applications/uninstaller_spec.rb index b80a8f5ba..f7b1d9bba 100644 --- a/spec/unit/module_tool/applications/uninstaller_spec.rb +++ b/spec/unit/module_tool/applications/uninstaller_spec.rb @@ -24,15 +24,9 @@ describe Puppet::ModuleTool::Applications::Uninstaller do let(:modpath1) { create_temp_dir("modpath1") } let(:modpath2) { create_temp_dir("modpath2") } let(:env) { Puppet::Node::Environment.create(:env, [modpath1, modpath2], '') } - let(:options) { {:environment => "env"} } + let(:options) { { :environment_instance => env } } let(:uninstaller) { Puppet::ModuleTool::Applications::Uninstaller.new("puppetlabs-foo", options) } - around :each do |example| - Puppet.override(:environments => Puppet::Environments::Static.new(env)) do - example.run - end - end - def create_temp_dir(name) path = File.join(working_dir, name) FileUtils.mkdir_p(path) diff --git a/spec/unit/module_tool_spec.rb b/spec/unit/module_tool_spec.rb index ddee6bf68..81f25c669 100755 --- a/spec/unit/module_tool_spec.rb +++ b/spec/unit/module_tool_spec.rb @@ -154,88 +154,20 @@ TREE end describe '.set_option_defaults' do - describe 'option :environment' do - context 'passed:' do - let (:environment) { "ahgkduerh" } - let (:options) { {:environment => environment} } - - it 'Puppet[:environment] should be set to the value of the option' do - subject.set_option_defaults options - - Puppet[:environment].should == environment - end - - it 'the option value should not be overridden' do - Puppet[:environment] = :foo - subject.set_option_defaults options - - options[:environment].should == environment - end - end - - context 'NOT passed:' do - it 'Puppet[:environment] should NOT be overridden' do - Puppet[:environment] = :foo - - subject.set_option_defaults({}) - Puppet[:environment].should == :foo - end - - it 'the option should be set to the value of Puppet[:environment]' do - options_to_modify = Hash.new - Puppet[:environment] = :abcdefg - - subject.set_option_defaults options_to_modify - - options_to_modify[:environment].should == :abcdefg - end + let(:options) { {} } + let(:modulepath) { ['/env/module/path', '/global/module/path'] } + let(:environment) { Puppet::Node::Environment.create('current', modulepath, '') } + around(:each) do |example| + Puppet.override(:current_environment => environment) do + example.run end end - describe 'option :modulepath' do - context 'passed:' do - let (:modulepath) { PuppetSpec::Files.make_absolute('/bar') } - let (:options) { {:modulepath => modulepath} } - - it 'Puppet[:modulepath] should be set to the value of the option' do - - subject.set_option_defaults options - - Puppet[:modulepath].should == modulepath - end - - it 'the option value should not be overridden' do - Puppet[:modulepath] = "/foo" - - subject.set_option_defaults options - - options[:modulepath].should == modulepath - end - end - - context 'NOT passed:' do - let (:options) { {:environment => :pmttestenv} } - - before(:each) do - Puppet.settings.parse_config(<<-CONF) - [pmttestenv] - modulepath = #{["/foo", "/bar", "/no"].join(File::PATH_SEPARATOR)} - CONF - Puppet[:modulepath] = "/no" - Puppet[:environment] = :pmttestenv - end - - it 'Puppet[:modulepath] should be reset to the module path of the current environment' do - subject.set_option_defaults options - - Puppet[:modulepath].should == Puppet.settings.value(:modulepath, :pmttestenv) - end - - it 'the option should be set to the module path of the current environment' do - subject.set_option_defaults options - - options[:modulepath].should == Puppet.settings.value(:modulepath, :pmttestenv) - end + describe 'option :environment_instance' do + it 'adds an environment_instance to the options hash' do + subject.set_option_defaults(options) + expect(options[:environment_instance].name).to eq(environment.name) + expect(options[:environment_instance].modulepath).to eq(environment.modulepath) end end @@ -245,32 +177,26 @@ TREE context 'passed:' do let (:options) { {:target_dir => target_dir} } - it 'the option value should be prepended to the Puppet[:modulepath]' do - Puppet[:modulepath] = "/fuz" - original_modulepath = Puppet[:modulepath] - + it 'prepends the target_dir into the environment_instance modulepath' do subject.set_option_defaults options - Puppet[:modulepath].should == options[:target_dir] + File::PATH_SEPARATOR + original_modulepath + expect(options[:environment_instance].full_modulepath). + to eq([File.expand_path(target_dir)] + modulepath) end - it 'the option value should be turned into an absolute path' do + it 'expands the target dir' do subject.set_option_defaults options options[:target_dir].should == File.expand_path(target_dir) end end - describe 'NOT passed:' do - before :each do - Puppet[:modulepath] = 'foo' + File::PATH_SEPARATOR + 'bar' - end - + context 'NOT passed:' do it 'the option should be set to the first component of Puppet[:modulepath]' do options = Hash.new subject.set_option_defaults options - options[:target_dir].should == Puppet[:modulepath].split(File::PATH_SEPARATOR)[0] + expect(options[:target_dir]).to eq(environment.full_modulepath.first) end end end From f3c2d6c9c6e561fa322e863c351a785801a25e04 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Tue, 18 Feb 2014 11:42:56 -0800 Subject: [PATCH 682/800] (PUP-1584) Be explicit about adding log functions to root environment Previously log level functions were being initialized on the root environment purely as an artifact of initialization timing. Now we're being explicit about it. --- lib/puppet/parser/functions.rb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/puppet/parser/functions.rb b/lib/puppet/parser/functions.rb index 45928d193..d7ea3e0a9 100644 --- a/lib/puppet/parser/functions.rb +++ b/lib/puppet/parser/functions.rb @@ -22,7 +22,9 @@ module Puppet::Parser::Functions # Runs a newfunction to create a function for each of the log levels Puppet::Util::Log.levels.each do |level| - newfunction(level, :doc => "Log a message on the server at level #{level.to_s}.") do |vals| + newfunction(level, + :environment => Puppet.lookup(:root_environment), + :doc => "Log a message on the server at level #{level.to_s}.") do |vals| send(level, vals.join(" ")) end end @@ -110,12 +112,16 @@ module Puppet::Parser::Functions # zero or more arguments. A function with an arity of 2 must be provided # with exactly two arguments, no more and no less. Added in Puppet 3.1.0. # + # @option options [Puppet::Node::Environment] :environment (nil) can + # explicitly pass the environment we wanted the function added to. Only used + # to set logging functions in root environment + # # @return [Hash] describing the function. # # @api public def self.newfunction(name, options = {}, &block) name = name.intern - environment = Puppet.lookup(:current_environment) + environment = options[:environment] || Puppet.lookup(:current_environment) Puppet.warning "Overwriting previous definition for function #{name}" if get_function(name, environment) From 4bdee687656b182ab44a221516aba959190d44d5 Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Tue, 18 Feb 2014 15:43:02 -0800 Subject: [PATCH 683/800] (pup-1636) Skip enc test on RHEL7 Skip this test on RHEL7 for the same reason it's being skipped on RHEL6 (and other platforms). Note this is a test for a deprecated feature which has proven challenging to setup across a range of platforms. --- .../store_configs/enc_provides_node_when_storeconfigs_enabled.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/acceptance/tests/store_configs/enc_provides_node_when_storeconfigs_enabled.rb b/acceptance/tests/store_configs/enc_provides_node_when_storeconfigs_enabled.rb index c6da0354d..a3e084993 100644 --- a/acceptance/tests/store_configs/enc_provides_node_when_storeconfigs_enabled.rb +++ b/acceptance/tests/store_configs/enc_provides_node_when_storeconfigs_enabled.rb @@ -3,6 +3,7 @@ test_name "ENC node information is used when store configs enabled (#16698)" confine :except, :platform => 'solaris' confine :except, :platform => 'windows' confine :except, :platform => 'el-6' +confine :except, :platform => 'el-7' confine :except, :platform => 'lucid' testdir = master.tmpdir('use_enc') From 159a979769388fd6c1fd18e4e28a68fdd27617ad Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Tue, 18 Feb 2014 17:37:22 -0800 Subject: [PATCH 684/800] (pup-1636) Confine two tests to el5 and el6 which are all the tests are written for --- acceptance/config/git/options.rb | 2 +- .../service/ticket_4123_should_list_all_running_redhat.rb | 2 +- .../resource/service/ticket_4124_should_list_all_disabled.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/acceptance/config/git/options.rb b/acceptance/config/git/options.rb index c20149d3f..70419dc89 100644 --- a/acceptance/config/git/options.rb +++ b/acceptance/config/git/options.rb @@ -1,6 +1,6 @@ { :install => [ - 'git://github.com/puppetlabs/facter.git#stable', + 'git://github.com/puppetlabs/facter.git#master', 'git://github.com/puppetlabs/hiera.git#stable', ], :pre_suite => [ diff --git a/acceptance/tests/resource/service/ticket_4123_should_list_all_running_redhat.rb b/acceptance/tests/resource/service/ticket_4123_should_list_all_running_redhat.rb index 064424766..5a905218a 100755 --- a/acceptance/tests/resource/service/ticket_4123_should_list_all_running_redhat.rb +++ b/acceptance/tests/resource/service/ticket_4123_should_list_all_running_redhat.rb @@ -4,7 +4,7 @@ step "Validate services running agreement ralsh vs. OS service count" # ticket_4123_should_list_all_running_redhat.sh hosts.each do |host| - if host['platform'].include?('el-') + if host['platform'] =~ /el-[56]/ run_script_on(host, File.join(File.dirname(__FILE__), 'ticket_4123_should_list_all_running_redhat.sh')) else skip_test "Test not supported on this plaform" diff --git a/acceptance/tests/resource/service/ticket_4124_should_list_all_disabled.rb b/acceptance/tests/resource/service/ticket_4124_should_list_all_disabled.rb index 7ba3c1a28..02b8bc76e 100755 --- a/acceptance/tests/resource/service/ticket_4124_should_list_all_disabled.rb +++ b/acceptance/tests/resource/service/ticket_4124_should_list_all_disabled.rb @@ -4,7 +4,7 @@ step "Validate disabled services agreement ralsh vs. OS service count" # ticket_4124_should_list_all_disabled.sh hosts.each do |host| - unless host['platform'].include?('el-') + unless host['platform'] =~ /el-[56]/ skip_test "Test not supported on this plaform" else run_script_on(host, File.join(File.dirname(__FILE__), 'ticket_4124_should_list_all_disabled.sh')) From abfc76ca2506beeb76e8ff008746a3636f8b3731 Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Tue, 18 Feb 2014 21:47:29 -0800 Subject: [PATCH 685/800] (PUP-897) Allow rpm package provider to query by virtual package name. Some providers that inherit from the rpm provider, such as the yum provider, do a query after installing a package. If the package is specified by a virtual name, the yum provider installs the package successfully, but the base rpm provider implementation fails to query the installed package. This causes a confusing "could not find package" error even though the package was installed. Passing the '--whatprovides' option to rpm when performing a query instructs rpm to locate packages that provide the given virtual name. This enables child providers to install by virtual package name without errors. --- lib/puppet/provider/package/rpm.rb | 2 +- spec/unit/provider/package/aptrpm_spec.rb | 2 +- spec/unit/provider/package/rpm_spec.rb | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/puppet/provider/package/rpm.rb b/lib/puppet/provider/package/rpm.rb index 2a3a69785..4a544fbb1 100644 --- a/lib/puppet/provider/package/rpm.rb +++ b/lib/puppet/provider/package/rpm.rb @@ -75,7 +75,7 @@ Puppet::Type.type(:package).provide :rpm, :source => :rpm, :parent => Puppet::Pr #NOTE: Prior to a fix for issue 1243, this method potentially returned a cached value #IF YOU CALL THIS METHOD, IT WILL CALL RPM #Use get(:property) to check if cached values are available - cmd = ["-q", @resource[:name], "#{self.class.nosignature}", "#{self.class.nodigest}", "--qf", self.class::NEVRA_FORMAT] + cmd = ["-q", '--whatprovides', @resource[:name], "#{self.class.nosignature}", "#{self.class.nodigest}", "--qf", self.class::NEVRA_FORMAT] begin output = rpm(*cmd) diff --git a/spec/unit/provider/package/aptrpm_spec.rb b/spec/unit/provider/package/aptrpm_spec.rb index 5db0a975e..081996d25 100755 --- a/spec/unit/provider/package/aptrpm_spec.rb +++ b/spec/unit/provider/package/aptrpm_spec.rb @@ -18,7 +18,7 @@ describe Puppet::Type.type(:package).provider(:aptrpm) do def rpm pkg.provider.expects(:rpm). - with('-q', 'faff', '--nosignature', '--nodigest', '--qf', + with('-q', '--whatprovides', 'faff', '--nosignature', '--nodigest', '--qf', "%{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH}\\n") end diff --git a/spec/unit/provider/package/rpm_spec.rb b/spec/unit/provider/package/rpm_spec.rb index 221a77e50..0504b7650 100755 --- a/spec/unit/provider/package/rpm_spec.rb +++ b/spec/unit/provider/package/rpm_spec.rb @@ -188,7 +188,7 @@ describe provider_class do describe "on a modern RPM" do before(:each) do - Puppet::Util::Execution.expects(:execute).with(["/bin/rpm", "-q", "myresource", '--nosignature', '--nodigest', "--qf", nevra_format], execute_options).returns("myresource 0 1.2.3.4 5.el4 noarch\n") + Puppet::Util::Execution.expects(:execute).with(["/bin/rpm", "-q", "--whatprovides", "myresource", '--nosignature', '--nodigest', "--qf", nevra_format], execute_options).returns("myresource 0 1.2.3.4 5.el4 noarch\n") end let(:rpm_version) { "RPM version 4.10.0\n" } @@ -201,7 +201,7 @@ describe provider_class do describe "on an ancient RPM" do before(:each) do - Puppet::Util::Execution.expects(:execute).with(["/bin/rpm", "-q", "myresource", '', '', '--qf', nevra_format], execute_options).returns("myresource 0 1.2.3.4 5.el4 noarch\n") + Puppet::Util::Execution.expects(:execute).with(["/bin/rpm", "-q", "--whatprovides", "myresource", '', '', '--qf', nevra_format], execute_options).returns("myresource 0 1.2.3.4 5.el4 noarch\n") end let(:rpm_version) { "RPM version 3.0.6\n" } @@ -217,7 +217,7 @@ describe provider_class do describe "parsing" do def parser_test(rpm_output_string, gold_hash, number_of_debug_logs = 0) Puppet.expects(:debug).times(number_of_debug_logs) - Puppet::Util::Execution.expects(:execute).with(["/bin/rpm", "-q", resource_name, "--nosignature", "--nodigest", "--qf", nevra_format], execute_options).returns(rpm_output_string) + Puppet::Util::Execution.expects(:execute).with(["/bin/rpm", "-q", "--whatprovides", resource_name, "--nosignature", "--nodigest", "--qf", nevra_format], execute_options).returns(rpm_output_string) expect(provider.query).to eq(gold_hash) end @@ -256,7 +256,7 @@ describe provider_class do it "does not log or fail if rpm returns package not found" do Puppet.expects(:debug).never - Puppet::Util::Execution.expects(:execute).with(["/bin/rpm", "-q", resource_name, "--nosignature", "--nodigest", "--qf", nevra_format], execute_options).raises Puppet::ExecutionFailure.new('package not found') + Puppet::Util::Execution.expects(:execute).with(["/bin/rpm", "-q", "--whatprovides", resource_name, "--nosignature", "--nodigest", "--qf", nevra_format], execute_options).raises Puppet::ExecutionFailure.new('package not found') expect(provider.query).to be_nil end From 4ebf3af5a1c22d5fb6d27dc84510272c1308154d Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Mon, 4 Nov 2013 21:02:44 +0100 Subject: [PATCH 686/800] (PUP-652) Fix error reporting for ParsedFile Puppet::Error may not have a setter for file, so check if there is one first (like ExternalFileError has). This was previously tracked in redmine as #19422 --- lib/puppet/provider/parsedfile.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/provider/parsedfile.rb b/lib/puppet/provider/parsedfile.rb index 1a13c8df3..03ad1e194 100644 --- a/lib/puppet/provider/parsedfile.rb +++ b/lib/puppet/provider/parsedfile.rb @@ -303,7 +303,7 @@ class Puppet::Provider::ParsedFile < Puppet::Provider @target = path return self.parse(text) rescue Puppet::Error => detail - detail.file = @target + detail.file = @target if detail.respond_to?(:file=) raise detail ensure @target = old From 444c3330a5ed4e70b5f851a9082d8e40ea72bc66 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Wed, 19 Feb 2014 10:52:00 -0800 Subject: [PATCH 687/800] (PUP-1584) Expand some modulepath settings for windows A couple of module_tool specs modified for PUP-1584 were not being expanded such that they would work properly on Windows or POSIX. --- spec/unit/face/module/uninstall_spec.rb | 2 +- spec/unit/module_tool_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/unit/face/module/uninstall_spec.rb b/spec/unit/face/module/uninstall_spec.rb index 5f480d993..36209dc96 100644 --- a/spec/unit/face/module/uninstall_spec.rb +++ b/spec/unit/face/module/uninstall_spec.rb @@ -8,7 +8,7 @@ describe "puppet module uninstall" do let(:options) do {} end - let(:modulepath) { '/module/path' } + let(:modulepath) { File.expand_path('/module/path') } let(:environment) do Puppet::Node::Environment.create(:env, [modulepath], '') end diff --git a/spec/unit/module_tool_spec.rb b/spec/unit/module_tool_spec.rb index 81f25c669..63bd5e295 100755 --- a/spec/unit/module_tool_spec.rb +++ b/spec/unit/module_tool_spec.rb @@ -155,7 +155,7 @@ TREE describe '.set_option_defaults' do let(:options) { {} } - let(:modulepath) { ['/env/module/path', '/global/module/path'] } + let(:modulepath) { [File.expand_path('/env/module/path'), File.expand_path('/global/module/path')] } let(:environment) { Puppet::Node::Environment.create('current', modulepath, '') } around(:each) do |example| Puppet.override(:current_environment => environment) do From 5d7ff323279e5c33b4c2715c99076c2e0c28fdd5 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Tue, 18 Feb 2014 18:25:56 -0800 Subject: [PATCH 688/800] (PUP-1676) Puppet::Settings retrieves directory environment settings During a Puppet[:setting] call, legacy environment settings would be loaded from a puppet.conf stanza. But settings from directory environments would never be seen. Areas in the codebase could obtain per environment settings by looking them up directly from an environment. However a call to puppet config or to an application with --configprint, would return the default modulepath if redirected to a directory environment using the --environment setting. A ValuesFromCurrentEnvironment class has been added to Settings to lookup environment settings not found in puppet.conf if there is a match with the :current_environment from the Context. Because of the need to access a global modulepath for environment and directory loader construction, a setting, :basemodulepath, has been added, so that we can initialize without fear of recursing infinitely looking up :modulepath. The possibility for recursion has been there since at least 3.0.0: [legacy] modulepath=/foo:$modulepath would overflow the stack. It could now be: [legacy] modulepath=/foo:$basemodulepath --- lib/puppet.rb | 2 +- lib/puppet/defaults.rb | 8 ++- lib/puppet/settings.rb | 46 +++++++++++++++-- lib/puppet/test/test_helper.rb | 2 +- .../directory_environments_spec.rb | 50 +++++++++++++++++++ 5 files changed, 102 insertions(+), 6 deletions(-) create mode 100644 spec/integration/directory_environments_spec.rb diff --git a/lib/puppet.rb b/lib/puppet.rb index ece55c6ab..8a0a2b871 100644 --- a/lib/puppet.rb +++ b/lib/puppet.rb @@ -175,7 +175,7 @@ module Puppet # @api private def self.base_context(settings) environments = settings[:environmentpath] - modulepath = Puppet::Node::Environment.split_path(settings[:modulepath]) + modulepath = Puppet::Node::Environment.split_path(settings[:basemodulepath]) loaders = Puppet::Environments::Directories.from_path(environments, modulepath) loaders << Puppet::Environments::Legacy.new diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index d6947913a..bb1dc1fc6 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -928,9 +928,15 @@ EOT :type => :boolean, :desc => "Whether the master should function as a certificate authority.", }, - :modulepath => { + :basemodulepath => { :default => "$confdir/modules#{File::PATH_SEPARATOR}/usr/share/puppet/modules", :type => :path, + :desc => "The base non-environment specific search path for modules, included + also in all directory environment and default legacy environment modulepaths.", + }, + :modulepath => { + :default => "$basemodulepath", + :type => :path, :desc => "The search path for modules, as a list of directories separated by the system path separator character. (The POSIX path separator is ':', and the Windows path separator is ';'.)", diff --git a/lib/puppet/settings.rb b/lib/puppet/settings.rb index c37a7010f..ebed1a8f2 100644 --- a/lib/puppet/settings.rb +++ b/lib/puppet/settings.rb @@ -1059,12 +1059,16 @@ Generated on #{Time.now}. end end else + values_from_section = nil if @configuration_file - section = @configuration_file.sections[name] - if section - ValuesFromSection.new(name, section) + if section = @configuration_file.sections[name] + values_from_section = ValuesFromSection.new(name, section) end end + if values_from_section.nil? && name != :main && @global_defaults_initialized + values_from_section = ValuesFromCurrentEnvironment.new(name) + end + values_from_section end end.compact end @@ -1250,4 +1254,40 @@ Generated on #{Time.now}. end end end + + # @api private + class ValuesFromCurrentEnvironment + def initialize(desired_environment) + @desired_environment = desired_environment + end + + def include?(name) + return false unless name == :modulepath || name == :manifest + if i = instance + i.include?(name) + end + end + + def lookup(name) + return nil unless name == :modulepath || name == :manifest + if i = instance + i[name] + end + end + + private + + def instance + unless @instance + env = Puppet.lookup(:current_environment) + if env.name == @desired_environment + @instance = { + :modulepath => env.full_modulepath.join(File::PATH_SEPARATOR), + :manifest => env.manifest, + } + end + end + return @instance + end + end end diff --git a/lib/puppet/test/test_helper.rb b/lib/puppet/test/test_helper.rb index c9c428c25..5c083715a 100644 --- a/lib/puppet/test/test_helper.rb +++ b/lib/puppet/test/test_helper.rb @@ -40,7 +40,7 @@ module Puppet::Test @environmentdir = Dir.mktmpdir('environments') Puppet.push_context(Puppet.base_context({ :environmentpath => @environmentdir, - :modulepath => "", + :basemodulepath => "", :manifest => "/dev/null" }), "Initial for specs") Puppet::Parser::Functions.reset diff --git a/spec/integration/directory_environments_spec.rb b/spec/integration/directory_environments_spec.rb new file mode 100644 index 000000000..71b82a987 --- /dev/null +++ b/spec/integration/directory_environments_spec.rb @@ -0,0 +1,50 @@ +require 'spec_helper' + +describe "directory environments" do + let(:args) { ['--configprint', 'modulepath', '--environment', 'direnv'] } + let(:puppet) do + app = Puppet::Application[:apply] + app.stubs(:command_line).returns(stub('command_line', :args => [])) + app + end + + context "with a single directory environmentpath" do + before(:each) do + environmentdir = Puppet[:environmentpath].split(File::PATH_SEPARATOR).first + FileUtils.mkdir_p(environmentdir + "/direnv/modules") + end + + it "config prints the environments modulepath" do + Puppet.settings.initialize_global_settings(args) + expect do + expect { puppet.run }.to exit_with(0) + end.to have_printed('direnv/modules') + end + + it "config prints the cli --modulepath despite environment" do + args << '--modulepath' << 'completely/different' + Puppet.settings.initialize_global_settings(args) + expect do + expect { puppet.run }.to exit_with(0) + end.to have_printed('completely/different') + end + end + + context "with an environmentpath having multiple directories" do + let(:args) { ['--configprint', 'modulepath', '--environment', 'otherdirenv'] } + + before(:each) do + envdir1 = File.join(Puppet[:confdir], 'env1') + envdir2 = File.join(Puppet[:confdir], 'env2') + Puppet[:environmentpath] = [envdir1, envdir2].join(File::PATH_SEPARATOR) + FileUtils.mkdir_p(envdir2 + "/otherdirenv/modules") + end + + it "config prints a directory environment modulepath" do + Puppet.settings.initialize_global_settings(args) + expect do + expect { puppet.run }.to exit_with(0) + end.to have_printed('otherdirenv/modules') + end + end +end From 8b549139d46a8465023a78e0e1be42710444502d Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Thu, 20 Feb 2014 09:39:57 -0800 Subject: [PATCH 689/800] (pup-1732) Report only services from systemd Prior to this change the systemd service provider would return all systemd-managed types (including sockets, devices, swap, etc). This produced a huge list of instances, many of which were not in fact services. This change simply adds the '--type service' option to the 'systemctl' invocation in .instances. It also updates the spec tests with fixtures for the old (without that option) and new (with that option) way of invoking 'systemctl'. I purposefully did not touch the existing fixture which is tied to a test which is pending on a what-to-do about failed services. If/when we tackle that, we may want to collapse some of these fixtures. --- lib/puppet/provider/service/systemd.rb | 2 +- .../provider/service/systemd/list_units_all | 24 +++++++++++++++++ .../service/systemd/list_units_services | 17 ++++++++++++ spec/unit/provider/service/systemd_spec.rb | 26 ++++++++++++++++--- 4 files changed, 64 insertions(+), 5 deletions(-) create mode 100644 spec/fixtures/unit/provider/service/systemd/list_units_all create mode 100644 spec/fixtures/unit/provider/service/systemd/list_units_services diff --git a/lib/puppet/provider/service/systemd.rb b/lib/puppet/provider/service/systemd.rb index 1c51236a4..7bcc3527e 100644 --- a/lib/puppet/provider/service/systemd.rb +++ b/lib/puppet/provider/service/systemd.rb @@ -10,7 +10,7 @@ Puppet::Type.type(:service).provide :systemd, :parent => :base do def self.instances i = [] - output = systemctl('list-units', '--full', '--all', '--no-pager') + output = systemctl('list-units', '--type', 'service', '--full', '--all', '--no-pager') output.scan(/^(\S+)\s+(loaded|error)\s+(active|inactive)\s+(active|waiting|running|plugged|mounted|dead|exited|listening|elapsed)\s*?(\S.*?)?$/i).each do |m| i << new(:name => m[0]) end diff --git a/spec/fixtures/unit/provider/service/systemd/list_units_all b/spec/fixtures/unit/provider/service/systemd/list_units_all new file mode 100644 index 000000000..93903d7ef --- /dev/null +++ b/spec/fixtures/unit/provider/service/systemd/list_units_all @@ -0,0 +1,24 @@ +UNIT LOAD ACTIVE SUB DESCRIPTION +proc-sys-fs-binfmt_misc.automount loaded active waiting Arbitrary Executable File Formats File System Automount Point +dev-block-8:2.device loaded active plugged LVM PV TdfwOF-f7uf-5By1-aQ3o-zSJd-abDY-hqQcEL on /dev/sda2 +auditd.service loaded active running Security Auditing Service +avahi-daemon.socket loaded active running Avahi mDNS/DNS-SD Stack Activation Socket +basic.target loaded active active Basic System +crond.service loaded active running Command Scheduler +dbus.service loaded active running D-Bus System Message Bus +display-manager.service error inactive dead display-manager.service +ebtables.service loaded inactive dead SYSV: Ethernet Bridge filtering tables +fedora-readonly.service loaded active exited Configure read-only root support +initrd-switch-root.service loaded inactive dead Switch Root +ip6tables.service error inactive dead ip6tables.service +puppet.service loaded inactive dead SYSV: Enables periodic system configuration checks through puppet. +session-17.scope loaded active running Session 17 of user root +-.slice loaded active active Root Slice +systemd-readahead-done.timer loaded active elapsed Stop Read-Ahead Data Collection 10s After Completed Startup + +LOAD = Reflects whether the unit definition was properly loaded. +ACTIVE = The high-level unit activation state, i.e. generalization of SUB. +SUB = The low-level unit activation state, values depend on unit type. + +155 loaded units listed. +To show all installed unit files use 'systemctl list-unit-files'. diff --git a/spec/fixtures/unit/provider/service/systemd/list_units_services b/spec/fixtures/unit/provider/service/systemd/list_units_services new file mode 100644 index 000000000..c2784d854 --- /dev/null +++ b/spec/fixtures/unit/provider/service/systemd/list_units_services @@ -0,0 +1,17 @@ +UNIT LOAD ACTIVE SUB DESCRIPTION +auditd.service loaded active running Security Auditing Service +crond.service loaded active running Command Scheduler +dbus.service loaded active running D-Bus System Message Bus +display-manager.service error inactive dead display-manager.service +ebtables.service loaded inactive dead SYSV: Ethernet Bridge filtering tables +fedora-readonly.service loaded active exited Configure read-only root support +initrd-switch-root.service loaded inactive dead Switch Root +ip6tables.service error inactive dead ip6tables.service +puppet.service loaded inactive dead SYSV: Enables periodic system configuration checks through puppet. + +LOAD = Reflects whether the unit definition was properly loaded. +ACTIVE = The high-level unit activation state, i.e. generalization of SUB. +SUB = The low-level unit activation state, values depend on unit type. + +155 loaded units listed. +To show all installed unit files use 'systemctl list-unit-files'. diff --git a/spec/unit/provider/service/systemd_spec.rb b/spec/unit/provider/service/systemd_spec.rb index df46be4d8..663890aca 100755 --- a/spec/unit/provider/service/systemd_spec.rb +++ b/spec/unit/provider/service/systemd_spec.rb @@ -9,12 +9,12 @@ describe Puppet::Type.type(:service).provider(:systemd) do Puppet::Type.type(:service).stubs(:defaultprovider).returns described_class described_class.stubs(:which).with('systemctl').returns '/bin/systemctl' end - + let :provider do described_class.new(:name => 'sshd.service') end - + osfamily = [ 'archlinux' ] osfamily.each do |osfamily| @@ -22,7 +22,7 @@ describe Puppet::Type.type(:service).provider(:systemd) do Facter.expects(:value).with(:osfamily).returns(osfamily) described_class.default?.should be_true end - end + end [:enabled?, :enable, :disable, :start, :stop, :status, :restart].each do |method| it "should have a #{method} method" do @@ -37,7 +37,7 @@ describe Puppet::Type.type(:service).provider(:systemd) do it "should get a list of services from systemctl list-units" do pending('A service that has been killed (ACTIVE=failed) is not recognized') - described_class.expects(:systemctl).with('list-units', '--full', '--all', '--no-pager').returns File.read(my_fixture('list_units')) + described_class.expects(:systemctl).with('list-units', '--type', 'service', '--full', '--all', '--no-pager').returns File.read(my_fixture('list_units')) described_class.instances.map(&:name).should =~ %w{ auditd.service crond.service @@ -51,6 +51,24 @@ describe Puppet::Type.type(:service).provider(:systemd) do sshd.service } end + + it "should return only services" do + # what we should not do if we want only services + described_class.stubs(:systemctl).with('list-units', '--full', '--all', '--no-pager').returns File.read(my_fixture('list_units_all')) + # what we should do if we want only services + described_class.stubs(:systemctl).with('list-units', '--type', 'service', '--full', '--all', '--no-pager').returns File.read(my_fixture('list_units_services')) + described_class.instances.map(&:name).should =~ %w{ + auditd.service + crond.service + dbus.service + display-manager.service + ebtables.service + fedora-readonly.service + initrd-switch-root.service + ip6tables.service + puppet.service + } + end end describe "#start" do From e0240fe431812638b3ab3b002673cda01694510a Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 20 Feb 2014 22:49:23 +0100 Subject: [PATCH 690/800] (PUP-1592) Speed up loading of user defined types by 2x When a user defined type is loaded a check is always first made to see if the name loads a resource type implemented in ruby. The majority of loads are for non ruby types. The loading must be done with higher precedence of ruby based resource type over user defined. This optimization capitalizes on the fact that ruby resource types are in a flat name space and thus their names can never contain '::'. The optimization is done in metatype/manager.rb where now an attempt to load from the type loader is avoided if the name contains a ':'. This achieves a 2x speedup and generates less permanent garbage (all user defined type names where internalized as symbols, never to be gc'd). --- lib/puppet/metatype/manager.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/puppet/metatype/manager.rb b/lib/puppet/metatype/manager.rb index 2d7b2b6e2..af1379f0b 100644 --- a/lib/puppet/metatype/manager.rb +++ b/lib/puppet/metatype/manager.rb @@ -142,6 +142,11 @@ module Manager # @return [Puppet::Type, nil] the type or nil if the type was not defined and could not be loaded # def type(name) + # Avoid loading if name obviously is not a type name + if name.to_s.include?(':') + return nil + end + @types ||= {} # We are overwhelmingly symbols here, which usually match, so it is worth From 9725e5b535f992f8f3d4e3f5adf549bd9efed733 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Thu, 20 Feb 2014 16:59:55 -0800 Subject: [PATCH 691/800] (PUP-1676) Environmentpath acceptance test uses basemodulepath Given that the Directories environment loader gets initialized with basemodulepath, we have to adjust the master puppet.conf for use_environment_from_environmentpath.rb to also use basemodulepath so that we find globally installed modules. --- .../tests/environment/use_environment_from_environmentpath.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance/tests/environment/use_environment_from_environmentpath.rb b/acceptance/tests/environment/use_environment_from_environmentpath.rb index 2a1c3db45..b4edce410 100644 --- a/acceptance/tests/environment/use_environment_from_environmentpath.rb +++ b/acceptance/tests/environment/use_environment_from_environmentpath.rb @@ -126,7 +126,7 @@ end master_opts = { 'master' => { 'environmentpath' => "#{testdir}/additional:#{testdir}/base", - 'modulepath' => "#{testdir}/modules", + 'basemodulepath' => "#{testdir}/modules", } } From 5208c1a7c91c4e77db4b5028dedbd04e498e7bb5 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Thu, 20 Feb 2014 16:45:36 -0800 Subject: [PATCH 692/800] (PUP-1707) Ensure puppet man works for all applications Adds a spec test that ensures `puppet man ` exits with 0 and does not emit the 'undefined method' error reported in PUP-1707. --- spec/lib/puppet_spec/matchers.rb | 4 ++++ spec/unit/man_spec.rb | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100755 spec/unit/man_spec.rb diff --git a/spec/lib/puppet_spec/matchers.rb b/spec/lib/puppet_spec/matchers.rb index 67a5f4779..449d1706b 100644 --- a/spec/lib/puppet_spec/matchers.rb +++ b/spec/lib/puppet_spec/matchers.rb @@ -87,6 +87,10 @@ class HavePrintedMatcher end end + def failure_message_for_should_not + "expected #{@expected.inspect} to not be printed; got:\n#{@actual}" + end + def description "expect #{@expected.inspect} to be printed" end diff --git a/spec/unit/man_spec.rb b/spec/unit/man_spec.rb new file mode 100755 index 000000000..28a208543 --- /dev/null +++ b/spec/unit/man_spec.rb @@ -0,0 +1,32 @@ +#! /usr/bin/env ruby +require 'spec_helper' +require 'puppet/face' + +describe Puppet::Face[:man, :current] do + let(:pager) { '/path/to/our/pager' } + + around do |example| + oldpager = ENV['MANPAGER'] + ENV['MANPAGER'] = pager + example.run + ENV['MANPAGER'] = oldpager + end + + it "exits with 0 when generating man documentation for each available application" do + Puppet::Util.stubs(:which).with('ronn').returns(nil) + Puppet::Util.stubs(:which).with(pager).returns(pager) + + Puppet::Application.available_application_names.each do |name| + next if %w{man face_base indirection_base}.include? name + + klass = Puppet::Application.find('man') + app = klass.new(Puppet::Util::CommandLine.new('puppet', ['man', name])) + + expect do + IO.stubs(:popen).with(pager, anything).yields($stdout) + + expect { app.run }.to exit_with(0) + end.to_not have_printed(/undefined method `gsub'/) + end + end +end From c7e36979a0ec1b1ea24355940d9e483b9fd5256c Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Thu, 20 Feb 2014 17:59:54 -0800 Subject: [PATCH 693/800] (PUP-1678) List environment settings instead of modules In talking with consumers of the environment information it turns out that listing the modules isn't the desired feature. Instead they really want the settings of the environment (modulepath, manifest). Other settings may be added later. This has the added benefit that listing the environments is now a much cheaper operations since it doesn't need to load the module data. --- api/docs/http_environments.md | 16 ++-- api/schemas/environments.json | 73 +++++++++---------- .../network/http/api/v2/environments.rb | 9 +-- .../network/http/api/v2/environments_spec.rb | 21 ++---- 4 files changed, 57 insertions(+), 62 deletions(-) diff --git a/api/docs/http_environments.md b/api/docs/http_environments.md index 3fefbcb01..a91806de1 100644 --- a/api/docs/http_environments.md +++ b/api/docs/http_environments.md @@ -9,7 +9,7 @@ Get Get the list of known environments. - GET /v2/environments + GET /v2.0/environments ### Parameters @@ -19,7 +19,7 @@ None Note: module lists shortened for readability. - GET /v2/environments + GET /v2.0/environments HTTP 200 OK Content-Type: application/json @@ -28,10 +28,16 @@ Note: module lists shortened for readability. "search_paths": ["/etc/puppet/environments"] "environments": { "production": { - "modules": { - "a-module": { "version": "1.3.5" } - "a-different-module": { "version": "2.4.6" } + "settings": { + "modulepath": ["/first/module/directory", "/second/module/directory"], + "manifest": ["/location/of/manifests"] } } } } + +Schema +------ + +A environments response body adheres to the {file:api/schemas/environments.json +api/schemas/environments.json} schema. diff --git a/api/schemas/environments.json b/api/schemas/environments.json index 01cfc4293..3b99869d1 100644 --- a/api/schemas/environments.json +++ b/api/schemas/environments.json @@ -1,40 +1,39 @@ { - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Environment Enumeration", - "description": "An enumeration of environments and their modules", - "type": "object", - "properties": { - "search_paths": { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1, - "description": "An array of the paths where the master looked for environments." - }, - "environments": { - "type": "object", - "patternProperties": { - "^[a-z0-9_]+$": { - "type": "object", - "properties": { - "modules" : { - "type": "object", - "patternProperties": { - "^[a-z0-9_\-]+$": { - "type": "object", - "properties": { - "version": { "type": "string" } - }, - "required": ["version"] - } - } - } - }, - "required": ["modules"] - } - } - } + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Environment Enumeration", + "description": "An enumeration of environments and their settings", + "type": "object", + "properties": { + "search_paths": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "description": "An array of the paths where the master looked for environments." }, - "required": ["search_paths", "environments"] + "environments": { + "type": "object", + "patternProperties": { + "^[a-z0-9_]+$": { + "type": "object", + "properties": { + "settings" : { + "type": "object", + "properties": { + "manifest": { "type": "string" }, + "modulepath": { + "type": "array", + "items": { "type": "string" } + } + }, + "required": ["modulepath", "manifest"] + } + }, + "required": ["settings"] + } + } + } + }, + "required": ["search_paths", "environments"] } diff --git a/lib/puppet/network/http/api/v2/environments.rb b/lib/puppet/network/http/api/v2/environments.rb index de31a8e8d..6331be857 100644 --- a/lib/puppet/network/http/api/v2/environments.rb +++ b/lib/puppet/network/http/api/v2/environments.rb @@ -10,11 +10,10 @@ class Puppet::Network::HTTP::API::V2::Environments "search_paths" => @env_loader.search_paths, "environments" => Hash[@env_loader.list.collect do |env| [env.name, { - "modules" => Hash[env.modules.collect do |mod| - [mod.name, { - "version" => mod.version - }] - end] + "settings" => { + "modulepath" => env.full_modulepath, + "manifest" => env.manifest + } }] end] })) diff --git a/spec/unit/network/http/api/v2/environments_spec.rb b/spec/unit/network/http/api/v2/environments_spec.rb index 394587238..064dc3ab2 100644 --- a/spec/unit/network/http/api/v2/environments_spec.rb +++ b/spec/unit/network/http/api/v2/environments_spec.rb @@ -7,8 +7,8 @@ require 'matchers/json' describe Puppet::Network::HTTP::API::V2::Environments do include JSONMatchers - it "responds with all of the available environments environments" do - environment = FakeEnvironment.create(:production, [], '') + it "responds with all of the available environments" do + environment = Puppet::Node::Environment.create(:production, ["/first", "/second"], '/manifests') loader = Puppet::Environments::Static.new(environment) handler = Puppet::Network::HTTP::API::V2::Environments.new(loader) response = Puppet::Network::HTTP::MemoryResponse.new @@ -21,10 +21,9 @@ describe Puppet::Network::HTTP::API::V2::Environments do "search_paths" => loader.search_paths, "environments" => { "production" => { - "modules" => { - "testing" => { - "version" => "1.2.3" - } + "settings" => { + "modulepath" => ["/first", "/second"], + "manifest" => "/manifests" } } } @@ -32,7 +31,7 @@ describe Puppet::Network::HTTP::API::V2::Environments do end it "the response conforms to the environments schema" do - environment = FakeEnvironment.create(:production, [], '') + environment = Puppet::Node::Environment.create(:production, [], '') handler = Puppet::Network::HTTP::API::V2::Environments.new(Puppet::Environments::Static.new(environment)) response = Puppet::Network::HTTP::MemoryResponse.new @@ -40,12 +39,4 @@ describe Puppet::Network::HTTP::API::V2::Environments do expect(response.body).to validate_against('api/schemas/environments.json') end - - class FakeEnvironment < Puppet::Node::Environment - def modules - fake_module = Puppet::Module.new('testing', '/somewhere/on/disk', self) - fake_module.version = "1.2.3" - [fake_module] - end - end end From 601c76dc8f842e540e655fedd5add1acf8fe65d5 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Thu, 20 Feb 2014 18:11:03 -0800 Subject: [PATCH 694/800] (PUP-1678) Provide valid, standard source URIs The URIs that were used before for the source information were completely made up. The schema wasn't registered and the path elements didn't mean much. This replaces it with a scheme where the directory environments are identified by a file URI that points to the directory, the legacy environments are identified by a file URI that points to the puppet configuration file, and any static environments are identified by a data URI with the content of "internal". --- lib/puppet/environments.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/puppet/environments.rb b/lib/puppet/environments.rb index dd8fa9341..75471de89 100644 --- a/lib/puppet/environments.rb +++ b/lib/puppet/environments.rb @@ -44,7 +44,7 @@ module Puppet::Environments # @!macro loader_search_paths def search_paths - ["environments://static/memory"] + ["data:text/plain,internal"] end # @!macro loader_list @@ -78,7 +78,7 @@ module Puppet::Environments # @!macro loader_search_paths def search_paths - ["environments://legacy"] + ["file://#{Puppet[:config]}"] end # @note The list of environments for the Legacy environments is always @@ -127,7 +127,7 @@ module Puppet::Environments # @!macro loader_search_paths def search_paths - ["environments://directories/#{@environment_dir}"] + ["file://#{@environment_dir}"] end # @!macro loader_list From 1c4fccde1f1cb1ccd6406be9971cfc07643164f5 Mon Sep 17 00:00:00 2001 From: Eric Sorenson Date: Fri, 31 Jan 2014 02:01:57 -0800 Subject: [PATCH 695/800] (maint) Update README.md for support clarification This commit gives our README.md a little bit of love: - adds verbiage about support lifecycle - moves HTTP API docs up near the rest of the docs - updates the link to the ticket tracker now that we're on JIRA --- README.md | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 7424d1eb0..422f097c6 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,10 @@ Documentation Documentation for Puppet and related projects can be found online at the [Puppet Docs site](http://docs.puppetlabs.com). +HTTP API +-------- +[HTTP API Index](api/docs/http_api_index.md) + Installation ------------ @@ -46,12 +50,25 @@ See [LICENSE](LICENSE) file. Support ------- -Please log tickets and issues at our [Projects -site](http://projects.puppetlabs.com). A [mailing +Please log tickets and issues at our [JIRA tracker](http://tickets.puppetlabs.com). A [mailing list](https://groups.google.com/forum/?fromgroups#!forum/puppet-users) is available for asking questions and getting help from others. In addition there is an active #puppet channel on Freenode. -HTTP API --------- -[HTTP API Index](api/docs/http_api_index.md) +We use semantic version numbers for our releases, and recommend that users stay +as up-to-date as possible by upgrading to patch releases and minor releases as +they become available. + +Bugfixes and ongoing development will occur in minor releases for the current +major version. Security fixes will be backported to a previous major version on +a best-effort basis, until the previous major version is end of lifed. + +For example: If a security vulnerability is discovered in Puppet 4.1.1, we +would fix it in the 4 series, most likely as 4.1.2. Puppet Labs will then make +a best effort to backport that fix onto the latest Puppet 3 release. + +Long-term support, including security patches and bug fixes, is available for +commercial customers. Please see the following page for more details: + +[Puppet Enterprise Support Lifecycle](http://puppetlabs.com/misc/puppet-enterprise-lifecycle) + From 828e5817ea49a41f0f535c7f0d5d158c1c6d3503 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Dal=C3=A9n?= Date: Thu, 20 Feb 2014 17:02:43 -0500 Subject: [PATCH 696/800] (maint) Add to_data_hash on Puppet::SSL::Base Without this to_pson fails on for example Puppet::SSL::Certificate. This caused a failure in for example the ca face when rendering a certificate. --- lib/puppet/ssl/base.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/puppet/ssl/base.rb b/lib/puppet/ssl/base.rb index 5ceda6245..0889ba731 100644 --- a/lib/puppet/ssl/base.rb +++ b/lib/puppet/ssl/base.rb @@ -90,6 +90,10 @@ class Puppet::SSL::Base content.to_pem end + def to_data_hash + to_s + end + # Provide the full text of the thing we're dealing with. def to_text return "" unless content From 30ee60d70aa1ff8dba592764675ea91fc1d6c55f Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Fri, 21 Feb 2014 11:39:19 -0800 Subject: [PATCH 697/800] (PUP-1676) Allow main as a directory environment. Looking again at the settings code, the static sections in puppet.conf currently shadow a directory environment of the same name, because we check the configuration file first. But I'm not seeing any reason to specifically forbid main as a directory environment name. Once static environments are removed, we will need to address how to obtain puppet.conf [main] settings without shadowing directory environments. --- lib/puppet/settings.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/settings.rb b/lib/puppet/settings.rb index ebed1a8f2..be992e2e1 100644 --- a/lib/puppet/settings.rb +++ b/lib/puppet/settings.rb @@ -1065,7 +1065,7 @@ Generated on #{Time.now}. values_from_section = ValuesFromSection.new(name, section) end end - if values_from_section.nil? && name != :main && @global_defaults_initialized + if values_from_section.nil? && @global_defaults_initialized values_from_section = ValuesFromCurrentEnvironment.new(name) end values_from_section From 0b0a3b615c8e7eb1750466934b8c73f73efd41c2 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Fri, 21 Feb 2014 12:57:37 -0800 Subject: [PATCH 698/800] (DOC) Environments route is v2.0 not v2 Change the api docs for the environments end point to correctly specify v2.0 rather than v2. --- api/docs/http_environments.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/docs/http_environments.md b/api/docs/http_environments.md index 3fefbcb01..9c279dda9 100644 --- a/api/docs/http_environments.md +++ b/api/docs/http_environments.md @@ -9,7 +9,7 @@ Get Get the list of known environments. - GET /v2/environments + GET /v2.0/environments ### Parameters @@ -19,7 +19,7 @@ None Note: module lists shortened for readability. - GET /v2/environments + GET /v2.0/environments HTTP 200 OK Content-Type: application/json From 5801c89f34c86f436c104616cda2a79441328f71 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 21 Feb 2014 22:33:10 +0100 Subject: [PATCH 699/800] (maint) Remove stale comment about shortened list in http_environments --- api/docs/http_environments.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/api/docs/http_environments.md b/api/docs/http_environments.md index a91806de1..bf7dce576 100644 --- a/api/docs/http_environments.md +++ b/api/docs/http_environments.md @@ -17,8 +17,6 @@ None ### Example Request & Response -Note: module lists shortened for readability. - GET /v2.0/environments HTTP 200 OK From 3bdb35c31076b2ebd85ad7b162a4b0a7be947333 Mon Sep 17 00:00:00 2001 From: Eric Sorenson Date: Fri, 21 Feb 2014 14:25:14 -0800 Subject: [PATCH 700/800] Fix trailing whitespace, add EOL clarification --- README.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 422f097c6..301491655 100644 --- a/README.md +++ b/README.md @@ -55,19 +55,22 @@ list](https://groups.google.com/forum/?fromgroups#!forum/puppet-users) is available for asking questions and getting help from others. In addition there is an active #puppet channel on Freenode. -We use semantic version numbers for our releases, and recommend that users stay -as up-to-date as possible by upgrading to patch releases and minor releases as +We use semantic version numbers for our releases, and recommend that users stay +as up-to-date as possible by upgrading to patch releases and minor releases as they become available. -Bugfixes and ongoing development will occur in minor releases for the current -major version. Security fixes will be backported to a previous major version on -a best-effort basis, until the previous major version is end of lifed. +Bugfixes and ongoing development will occur in minor releases for the current +major version. Security fixes will be backported to a previous major version on +a best-effort basis, until the previous major version is end of lifed. (End of +life usually happens at "N-2" major versions back, where "N" is the current +series.) -For example: If a security vulnerability is discovered in Puppet 4.1.1, we -would fix it in the 4 series, most likely as 4.1.2. Puppet Labs will then make -a best effort to backport that fix onto the latest Puppet 3 release. -Long-term support, including security patches and bug fixes, is available for +For example: If a security vulnerability is discovered in Puppet 4.1.1, we +would fix it in the 4 series, most likely as 4.1.2. Puppet Labs will then make +a best effort to backport that fix onto the latest Puppet 3 release. + +Long-term support, including security patches and bug fixes, is available for commercial customers. Please see the following page for more details: [Puppet Enterprise Support Lifecycle](http://puppetlabs.com/misc/puppet-enterprise-lifecycle) From 38db379725af7a87e24ac9f3cf4a6617e47eb2ee Mon Sep 17 00:00:00 2001 From: Eric Sorenson Date: Fri, 21 Feb 2014 14:35:51 -0800 Subject: [PATCH 701/800] Wording tweaks from review feedback --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 301491655..0fe5627b3 100644 --- a/README.md +++ b/README.md @@ -61,13 +61,11 @@ they become available. Bugfixes and ongoing development will occur in minor releases for the current major version. Security fixes will be backported to a previous major version on -a best-effort basis, until the previous major version is end of lifed. (End of -life usually happens at "N-2" major versions back, where "N" is the current -series.) +a best-effort basis, until the previous major version is no longer maintained. For example: If a security vulnerability is discovered in Puppet 4.1.1, we -would fix it in the 4 series, most likely as 4.1.2. Puppet Labs will then make +would fix it in the 4 series, most likely as 4.1.2. Maintainers would then make a best effort to backport that fix onto the latest Puppet 3 release. Long-term support, including security patches and bug fixes, is available for From 8ae792fc36be34c795c84bc3ac002e73bc408a97 Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Fri, 21 Feb 2014 15:34:15 -0800 Subject: [PATCH 702/800] (pup-1732) Remove the fixture and stub for what *not* to do --- .../provider/service/systemd/list_units_all | 24 ------------------- spec/unit/provider/service/systemd_spec.rb | 5 +--- 2 files changed, 1 insertion(+), 28 deletions(-) delete mode 100644 spec/fixtures/unit/provider/service/systemd/list_units_all diff --git a/spec/fixtures/unit/provider/service/systemd/list_units_all b/spec/fixtures/unit/provider/service/systemd/list_units_all deleted file mode 100644 index 93903d7ef..000000000 --- a/spec/fixtures/unit/provider/service/systemd/list_units_all +++ /dev/null @@ -1,24 +0,0 @@ -UNIT LOAD ACTIVE SUB DESCRIPTION -proc-sys-fs-binfmt_misc.automount loaded active waiting Arbitrary Executable File Formats File System Automount Point -dev-block-8:2.device loaded active plugged LVM PV TdfwOF-f7uf-5By1-aQ3o-zSJd-abDY-hqQcEL on /dev/sda2 -auditd.service loaded active running Security Auditing Service -avahi-daemon.socket loaded active running Avahi mDNS/DNS-SD Stack Activation Socket -basic.target loaded active active Basic System -crond.service loaded active running Command Scheduler -dbus.service loaded active running D-Bus System Message Bus -display-manager.service error inactive dead display-manager.service -ebtables.service loaded inactive dead SYSV: Ethernet Bridge filtering tables -fedora-readonly.service loaded active exited Configure read-only root support -initrd-switch-root.service loaded inactive dead Switch Root -ip6tables.service error inactive dead ip6tables.service -puppet.service loaded inactive dead SYSV: Enables periodic system configuration checks through puppet. -session-17.scope loaded active running Session 17 of user root --.slice loaded active active Root Slice -systemd-readahead-done.timer loaded active elapsed Stop Read-Ahead Data Collection 10s After Completed Startup - -LOAD = Reflects whether the unit definition was properly loaded. -ACTIVE = The high-level unit activation state, i.e. generalization of SUB. -SUB = The low-level unit activation state, values depend on unit type. - -155 loaded units listed. -To show all installed unit files use 'systemctl list-unit-files'. diff --git a/spec/unit/provider/service/systemd_spec.rb b/spec/unit/provider/service/systemd_spec.rb index 663890aca..7e29b7e28 100755 --- a/spec/unit/provider/service/systemd_spec.rb +++ b/spec/unit/provider/service/systemd_spec.rb @@ -53,10 +53,7 @@ describe Puppet::Type.type(:service).provider(:systemd) do end it "should return only services" do - # what we should not do if we want only services - described_class.stubs(:systemctl).with('list-units', '--full', '--all', '--no-pager').returns File.read(my_fixture('list_units_all')) - # what we should do if we want only services - described_class.stubs(:systemctl).with('list-units', '--type', 'service', '--full', '--all', '--no-pager').returns File.read(my_fixture('list_units_services')) + described_class.expects(:systemctl).with('list-units', '--type', 'service', '--full', '--all', '--no-pager').returns File.read(my_fixture('list_units_services')) described_class.instances.map(&:name).should =~ %w{ auditd.service crond.service From f597f8068f95adcf20a7609624cf798027858949 Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Fri, 21 Feb 2014 15:36:11 -0800 Subject: [PATCH 703/800] (maint) Remove a pending test which isn't pending on anything planned or asked for --- .../unit/provider/service/systemd/list_units | 18 ------------------ spec/unit/provider/service/systemd_spec.rb | 17 ----------------- 2 files changed, 35 deletions(-) delete mode 100644 spec/fixtures/unit/provider/service/systemd/list_units diff --git a/spec/fixtures/unit/provider/service/systemd/list_units b/spec/fixtures/unit/provider/service/systemd/list_units deleted file mode 100644 index 4998a08e4..000000000 --- a/spec/fixtures/unit/provider/service/systemd/list_units +++ /dev/null @@ -1,18 +0,0 @@ -UNIT LOAD ACTIVE SUB DESCRIPTION -auditd.service loaded active running Security Auditing Service -crond.service loaded active running Command Scheduler -dbus.service loaded active running D-Bus System Message Bus -display-manager.service error inactive dead display-manager.service -ebtables.service loaded inactive dead SYSV: Ethernet Bridge filtering tables -fedora-readonly.service loaded active exited Configure read-only root support -initrd-switch-root.service loaded inactive dead Switch Root -ip6tables.service error inactive dead ip6tables.service -puppet.service loaded inactive dead SYSV: Enables periodic system configuration checks through puppet. -sshd.service loaded failed failed OpenSSH server daemon - -LOAD = Reflects whether the unit definition was properly loaded. -ACTIVE = The high-level unit activation state, i.e. generalization of SUB. -SUB = The low-level unit activation state, values depend on unit type. - -155 loaded units listed. -To show all installed unit files use 'systemctl list-unit-files'. diff --git a/spec/unit/provider/service/systemd_spec.rb b/spec/unit/provider/service/systemd_spec.rb index 7e29b7e28..b2c813d63 100755 --- a/spec/unit/provider/service/systemd_spec.rb +++ b/spec/unit/provider/service/systemd_spec.rb @@ -35,23 +35,6 @@ describe Puppet::Type.type(:service).provider(:systemd) do described_class.should respond_to :instances end - it "should get a list of services from systemctl list-units" do - pending('A service that has been killed (ACTIVE=failed) is not recognized') - described_class.expects(:systemctl).with('list-units', '--type', 'service', '--full', '--all', '--no-pager').returns File.read(my_fixture('list_units')) - described_class.instances.map(&:name).should =~ %w{ - auditd.service - crond.service - dbus.service - display-manager.service - ebtables.service - fedora-readonly.service - initrd-switch-root.service - ip6tables.service - puppet.service - sshd.service - } - end - it "should return only services" do described_class.expects(:systemctl).with('list-units', '--type', 'service', '--full', '--all', '--no-pager').returns File.read(my_fixture('list_units_services')) described_class.instances.map(&:name).should =~ %w{ From 04067ed44f2ae65b0d7c99d3d7afbd866f2d0cb8 Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Fri, 21 Feb 2014 15:59:11 -0800 Subject: [PATCH 704/800] (maint) Revert to installing facter.git#stable if testing from github --- acceptance/config/git/options.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance/config/git/options.rb b/acceptance/config/git/options.rb index 70419dc89..c20149d3f 100644 --- a/acceptance/config/git/options.rb +++ b/acceptance/config/git/options.rb @@ -1,6 +1,6 @@ { :install => [ - 'git://github.com/puppetlabs/facter.git#master', + 'git://github.com/puppetlabs/facter.git#stable', 'git://github.com/puppetlabs/hiera.git#stable', ], :pre_suite => [ From f9a51e9d262b0f2c87a060e0f26ac4838e3a70f6 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Fri, 21 Feb 2014 16:05:42 -0800 Subject: [PATCH 705/800] (maint) Broaden managehome acceptance test on windows 2003 The previous regex only captured older facter operatingsystemrelease values. Now we're also sensistive to facter-2 operatingsystemrelease output. --- acceptance/tests/ticket_15560_managehome.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance/tests/ticket_15560_managehome.rb b/acceptance/tests/ticket_15560_managehome.rb index 78dd2df1e..de34258e7 100644 --- a/acceptance/tests/ticket_15560_managehome.rb +++ b/acceptance/tests/ticket_15560_managehome.rb @@ -25,7 +25,7 @@ agents.each do |host| deleteable_profile = true version = on(host, facter('operatingsystemrelease')).stdout.chomp - if version =~ /^5\.[012]/ + if version =~ /^5\.[012]|2003/ homedir = "C:/Documents and Settings/#{username}" deleteable_profile = false else From 445c54e479f6787f52b18ebbfe3f937f048b92c1 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Fri, 21 Feb 2014 17:03:26 -0800 Subject: [PATCH 706/800] (PUP-1678) Puppet::Node::Environment.create expands manifest path Modulepath was already being expanded; this ensures manifest is expanded as well and fixes some specs so that they will work on Windows too. --- lib/puppet/node/environment.rb | 2 +- spec/unit/network/http/api/v2/environments_spec.rb | 4 ++-- spec/unit/node/environment_spec.rb | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb index 7477b7385..c268c9591 100644 --- a/lib/puppet/node/environment.rb +++ b/lib/puppet/node/environment.rb @@ -81,7 +81,7 @@ class Puppet::Node::Environment obj.send(:initialize, name, expand_dirs(extralibs() + modulepath), - manifest) + File.expand_path(manifest)) obj end diff --git a/spec/unit/network/http/api/v2/environments_spec.rb b/spec/unit/network/http/api/v2/environments_spec.rb index 064dc3ab2..6e97f905d 100644 --- a/spec/unit/network/http/api/v2/environments_spec.rb +++ b/spec/unit/network/http/api/v2/environments_spec.rb @@ -22,8 +22,8 @@ describe Puppet::Network::HTTP::API::V2::Environments do "environments" => { "production" => { "settings" => { - "modulepath" => ["/first", "/second"], - "manifest" => "/manifests" + "modulepath" => [File.expand_path("/first"), File.expand_path("/second")], + "manifest" => File.expand_path("/manifests") } } } diff --git a/spec/unit/node/environment_spec.rb b/spec/unit/node/environment_spec.rb index 18db102c9..5b41fcd9b 100755 --- a/spec/unit/node/environment_spec.rb +++ b/spec/unit/node/environment_spec.rb @@ -52,7 +52,7 @@ describe Puppet::Node::Environment do overridden = environment.override_with(:modulepath => new_path) expect(overridden).to_not be_equal(environment) expect(overridden.name).to eq(:overridden) - expect(overridden.manifest).to eq('orig.pp') + expect(overridden.manifest).to eq(File.expand_path('orig.pp')) expect(overridden.modulepath).to eq(new_path) end @@ -60,7 +60,7 @@ describe Puppet::Node::Environment do overridden = environment.override_with(:manifest => 'new.pp') expect(overridden).to_not be_equal(environment) expect(overridden.name).to eq(:overridden) - expect(overridden.manifest).to eq('new.pp') + expect(overridden.manifest).to eq(File.expand_path('new.pp')) expect(overridden.modulepath).to eq(original_path) end end From b5b96cf8fc5286882077966f6e6448f2ae7ed6d9 Mon Sep 17 00:00:00 2001 From: Dominic Cleal Date: Fri, 21 Feb 2014 20:22:17 +0000 Subject: [PATCH 707/800] (PUP-1735) Reimplement deprecated Puppet::Node::Environment.current Puppet::Node::Environment.current is used in some external code, so reimplement it, linking to the context lookup for the current environment and issue a deprecation warning. --- lib/puppet/node/environment.rb | 13 +++++++++++++ spec/unit/node/environment_spec.rb | 9 +++++++++ 2 files changed, 22 insertions(+) diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb index c268c9591..7548d4a29 100644 --- a/lib/puppet/node/environment.rb +++ b/lib/puppet/node/environment.rb @@ -110,6 +110,19 @@ class Puppet::Node::Environment env_params[:manifest] || manifest) end + # Retrieve the environment for the current process. + # + # @note This should only used when a catalog is being compiled. + # + # @api private + # + # @return [Puppet::Node::Environment] the currently set environment if one + # has been explicitly set, else it will return the '*root*' environment + def self.current + Puppet.deprecation_warning("Puppet::Node::Environment.current has been replaced by Puppet.lookup(:current_environment), see http://links.puppetlabs.com/current-env-deprecation") + Puppet.lookup(:current_environment) + end + # @param [String] name Environment name to check for valid syntax. # @return [Boolean] true if name is valid # @api public diff --git a/spec/unit/node/environment_spec.rb b/spec/unit/node/environment_spec.rb index 5b41fcd9b..5d265e00b 100755 --- a/spec/unit/node/environment_spec.rb +++ b/spec/unit/node/environment_spec.rb @@ -454,4 +454,13 @@ describe Puppet::Node::Environment do it_behaves_like 'the environment' end + describe '#current' do + it 'should return the current context' do + env = Puppet::Node::Environment.new(:test) + Puppet::Context.any_instance.expects(:lookup).with(:current_environment).returns(env) + Puppet.expects(:deprecation_warning).once + Puppet::Node::Environment.current.should equal(env) + end + end + end From d5fedb3027416115324ffd86adb4504c9d57f0b0 Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Sun, 23 Feb 2014 22:38:22 -0800 Subject: [PATCH 708/800] (pup-1505) Correct require The previous patch for pup-1505 changes to use a facter top-level method (rather than one buried inside facter) but neglected to update the require to match. This patch updates the require. --- lib/puppet/feature/external_facts.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/feature/external_facts.rb b/lib/puppet/feature/external_facts.rb index ff55e9eee..fb0b4145e 100644 --- a/lib/puppet/feature/external_facts.rb +++ b/lib/puppet/feature/external_facts.rb @@ -1,4 +1,4 @@ -require 'facter/util/config' +require 'facter' Puppet.features.add(:external_facts) { Facter.respond_to?(:search_external) From b6b7906bfaa077932b1d1b5a248100513773662c Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Mon, 24 Feb 2014 22:34:07 -0800 Subject: [PATCH 709/800] (PUP-1707) Ensure ERB generated help content is UTF-8 encoded Previously, `env LANG=C bundle exec puppet man module` could not output non US-ASCII characters correctly due to two issues. First, puppet uses ERB generate the help output. Since the templates did not include a magic encoding comment[1], ERB would set the string encoding of the content it generated to `Encoding.default_external`, which in the case of LANG=C and ruby 1.9 is US-ASCII. When generating ERB help output for the `module` application, the resulting byte stream would be correctly UTF-8 encoded, but `String#encoding` would report US-ASCII. So later when we tried to perform a regex against it, we'd get the `invalid byte sequence in US-ASCII` error. The second issue was that HavePrintedMatcher redirects stdout and stderr to an instance of StringIO, whose default encoding was `Encoding.default_external`. When we try to apply the regexp to what was written to stdout, it would again fail, because the string was UTF-8 encoded, but reported itself to be US-ASCII. This commit sets the ERB magic encoding comment, and sets the StringIO encoding to 'UTF-8'. A test can override this behavior if desired within the `expect { ... }.to have_printed` block. [1] https://github.com/ruby/ruby/blob/v1_9_3_484/lib/erb.rb#L63-L68 --- lib/puppet/face/help/action.erb | 1 + lib/puppet/face/help/face.erb | 1 + lib/puppet/face/help/global.erb | 1 + lib/puppet/face/help/man.erb | 1 + spec/lib/puppet_spec/matchers.rb | 1 + 5 files changed, 5 insertions(+) diff --git a/lib/puppet/face/help/action.erb b/lib/puppet/face/help/action.erb index 2c4983ef1..bdac24300 100644 --- a/lib/puppet/face/help/action.erb +++ b/lib/puppet/face/help/action.erb @@ -1,3 +1,4 @@ +<%# encoding: UTF-8%> <% if action.synopsis -%> USAGE: <%= action.synopsis %> diff --git a/lib/puppet/face/help/face.erb b/lib/puppet/face/help/face.erb index caf84f02c..becdb049c 100644 --- a/lib/puppet/face/help/face.erb +++ b/lib/puppet/face/help/face.erb @@ -1,3 +1,4 @@ +<%# encoding: UTF-8%> <% if face.synopsis -%> USAGE: <%= face.synopsis %> diff --git a/lib/puppet/face/help/global.erb b/lib/puppet/face/help/global.erb index 6333ddcc4..9756ea5e3 100644 --- a/lib/puppet/face/help/global.erb +++ b/lib/puppet/face/help/global.erb @@ -1,3 +1,4 @@ +<%# encoding: UTF-8%> Usage: puppet [options] [options] Available subcommands: diff --git a/lib/puppet/face/help/man.erb b/lib/puppet/face/help/man.erb index 29e71222f..2f2872dae 100644 --- a/lib/puppet/face/help/man.erb +++ b/lib/puppet/face/help/man.erb @@ -1,3 +1,4 @@ +<%# encoding: UTF-8%> puppet-<%= face.name %>(8) -- <%= face.summary || "Undocumented subcommand." %> <%= '=' * (_erbout.length - 1) %> diff --git a/spec/lib/puppet_spec/matchers.rb b/spec/lib/puppet_spec/matchers.rb index 449d1706b..43476e688 100644 --- a/spec/lib/puppet_spec/matchers.rb +++ b/spec/lib/puppet_spec/matchers.rb @@ -59,6 +59,7 @@ class HavePrintedMatcher def matches?(block) begin $stderr = $stdout = StringIO.new + $stdout.set_encoding('UTF-8') block.call $stdout.rewind @actual = $stdout.read From e7b6d9f3e28aedddb588637fd0e9ad0c60e8568b Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 25 Feb 2014 20:50:06 +0100 Subject: [PATCH 710/800] (maint) Make setting not screw up when interpolating a false value This was a class nil/false screwup. --- lib/puppet/settings.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/settings.rb b/lib/puppet/settings.rb index be992e2e1..0bdb8d945 100644 --- a/lib/puppet/settings.rb +++ b/lib/puppet/settings.rb @@ -1199,7 +1199,7 @@ Generated on #{Time.now}. @environment elsif varname == "run_mode" @mode - elsif pval = interpolate(varname.to_sym) + elsif !(pval = interpolate(varname.to_sym)).nil? pval else raise InterpolationError, "Could not find value for #{value}" From e418494c4db97ee7fd9d1c89d2e7827c4e2379e3 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Tue, 25 Feb 2014 12:30:17 -0800 Subject: [PATCH 711/800] (PUP-1707) StringIO#set_encoding doesn't exist in ruby 1.8 Only call the method if the object responds to it. --- spec/lib/puppet_spec/matchers.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/lib/puppet_spec/matchers.rb b/spec/lib/puppet_spec/matchers.rb index 43476e688..448bd1811 100644 --- a/spec/lib/puppet_spec/matchers.rb +++ b/spec/lib/puppet_spec/matchers.rb @@ -59,7 +59,7 @@ class HavePrintedMatcher def matches?(block) begin $stderr = $stdout = StringIO.new - $stdout.set_encoding('UTF-8') + $stdout.set_encoding('UTF-8') if $stdout.respond_to?(:set_encoding) block.call $stdout.rewind @actual = $stdout.read From 8ccb3923b7ddd22e53d53dd193edc618b218a2a6 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Wed, 26 Feb 2014 18:30:29 +0100 Subject: [PATCH 712/800] (PUP-542) Add structured facts to immutable $facts When opting in with trusted_node_data or immutable_node_data the facts are also available via $facts, an immutable hash of data. --- lib/puppet/defaults.rb | 7 +- lib/puppet/indirector/catalog/compiler.rb | 2 +- lib/puppet/parser/compiler.rb | 4 ++ lib/puppet/parser/scope.rb | 11 ++- spec/integration/parser/compiler_spec.rb | 84 +++++++++++++++++++++++ 5 files changed, 103 insertions(+), 5 deletions(-) diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index bb1dc1fc6..97b6c767a 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -426,7 +426,12 @@ module Puppet :default => false, :type => :boolean, :desc => "Stores trusted node data in a hash called $trusted. - When true also prevents $trusted from being overridden in any scope.", + When true also makes node provided information immutable", + }, + :immutable_node_data => { + :default => '$trusted_node_data', + :type => :boolean, + :desc => "When true, makes the top scope variables $trusted and $facts reserved and immutable", } ) Puppet.define_settings(:module_tool, diff --git a/lib/puppet/indirector/catalog/compiler.rb b/lib/puppet/indirector/catalog/compiler.rb index a796fd4fe..abe714b2c 100644 --- a/lib/puppet/indirector/catalog/compiler.rb +++ b/lib/puppet/indirector/catalog/compiler.rb @@ -23,7 +23,7 @@ class Puppet::Resource::Catalog::Compiler < Puppet::Indirector::Code if text_facts.is_a?(Puppet::Node::Facts) facts = text_facts else - # We unescape here because the corrosponding code in Puppet::Configurer::FactHandler escapes + # We unescape here because the corresponding code in Puppet::Configurer::FactHandler escapes facts = Puppet::Node::Facts.convert_from(format, CGI.unescape(text_facts)) end diff --git a/lib/puppet/parser/compiler.rb b/lib/puppet/parser/compiler.rb index 048b79769..b85deb94a 100644 --- a/lib/puppet/parser/compiler.rb +++ b/lib/puppet/parser/compiler.rb @@ -503,6 +503,10 @@ class Puppet::Parser::Compiler if Puppet[:trusted_node_data] @topscope.set_trusted(node.trusted_data) end + if(Puppet[:immutable_node_data]) + facts_hash = node.facts.nil? ? {} : node.facts.values + @topscope.set_facts(facts_hash) + end end def create_settings_scope diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb index b659deb2c..8d557e449 100644 --- a/lib/puppet/parser/scope.rb +++ b/lib/puppet/parser/scope.rb @@ -278,7 +278,7 @@ class Puppet::Parser::Scope # be used by top scopes and node scopes. @class_scopes = {} - @enable_trusted_data = Puppet[:trusted_node_data] + @enable_immutable_data = Puppet[:immutable_node_data] end # Store the fact that we've evaluated a class, and store a reference to @@ -545,7 +545,7 @@ class Puppet::Parser::Scope } end - RESERVED_VARIABLE_NAMES = ['trusted'].freeze + RESERVED_VARIABLE_NAMES = ['trusted', 'facts'].freeze # Set a variable in the current scope. This will override settings # in scopes above, but will not allow variables in the current scope @@ -561,7 +561,7 @@ class Puppet::Parser::Scope end # Check for reserved variable names - if @enable_trusted_data && !options[:privileged] && RESERVED_VARIABLE_NAMES.include?(name) + if @enable_immutable_data && !options[:privileged] && RESERVED_VARIABLE_NAMES.include?(name) raise Puppet::ParseError, "Attempt to assign to a reserved variable name: '#{name}'" end @@ -589,6 +589,10 @@ class Puppet::Parser::Scope setvar('trusted', deep_freeze(hash), :privileged => true) end + def set_facts(hash) + setvar('facts', deep_freeze(hash), :privileged => true) + end + # Deeply freezes the given object. The object and its content must be of the types: # Array, Hash, Numeric, Boolean, Symbol, Regexp, NilClass, or String. All other types raises an Error. # (i.e. if they are assignable to Puppet::Pops::Types::Data type). @@ -597,6 +601,7 @@ class Puppet::Parser::Scope case object when Hash object.each {|k, v| deep_freeze(k); deep_freeze(v) } + object.freeze when NilClass # do nothing when String diff --git a/spec/integration/parser/compiler_spec.rb b/spec/integration/parser/compiler_spec.rb index 9ebd2dda9..dc47393de 100755 --- a/spec/integration/parser/compiler_spec.rb +++ b/spec/integration/parser/compiler_spec.rb @@ -314,6 +314,90 @@ describe "Puppet::Parser::Compiler" do end end + context 'when working with immutable node data' do + context 'and have opted in to immutable_node_data' do + before :each do + Puppet[:immutable_node_data] = true + end + + it 'should make $facts available' do + Puppet[:facts_terminus] = :memory + facts = Puppet::Node::Facts.new("testing", 'the_facts' => 'straight') + Puppet::Node::Facts.indirection.save(facts) + node = Puppet::Node.new("testing") + node.fact_merge + + catalog = compile_to_catalog(<<-MANIFEST, node) + notify { 'test': message => $facts[the_facts] } + MANIFEST + + catalog.resource("Notify[test]")[:message].should == "straight" + end + + it 'should make $facts reserved' do + Puppet[:facts_terminus] = :memory + facts = Puppet::Node::Facts.new("testing", 'the_facts' => 'straight') + Puppet::Node::Facts.indirection.save(facts) + node = Puppet::Node.new("testing") + node.fact_merge + + expect { + catalog = compile_to_catalog(<<-MANIFEST, node) + $facts = {} + notify { 'test': message => $facts[the_facts] } + MANIFEST + }.to raise_error(/assign to a reserved variable name: 'facts'/) + end + + it 'should make $facts immutable' do + Puppet[:facts_terminus] = :memory + facts = Puppet::Node::Facts.new("testing", 'the_facts' => 'straight') + Puppet::Node::Facts.indirection.save(facts) + node = Puppet::Node.new("testing") + node.fact_merge + + expect { + catalog = compile_to_catalog(<<-MANIFEST, node) + $facts[the_earth] = is_flat + notify { 'test': message => $facts[the_earth] } + MANIFEST + }.to raise_error(/frozen Hash/) + end + + it 'should make $facts available even if there are no facts' do + Puppet[:facts_terminus] = :memory + node = Puppet::Node.new("testing2") + node.fact_merge + + catalog = compile_to_catalog(<<-MANIFEST, node) + notify { 'test': message => $facts } + MANIFEST + + catalog.resource("Notify[test]")[:message].should == {} + end + end + + context 'and have not opted in to immutable_node_data' do + before :each do + Puppet[:immutable_node_data] = false + end + + it 'should not make $facts available' do + Puppet[:facts_terminus] = :memory + facts = Puppet::Node::Facts.new("testing", 'the_facts' => 'straight') + Puppet::Node::Facts.indirection.save(facts) + node = Puppet::Node.new("testing") + node.fact_merge + + catalog = compile_to_catalog(<<-MANIFEST, node) + notify { 'test': message => "An $facts space" } + MANIFEST + + catalog.resource("Notify[test]")[:message].should == "An space" + end + end + end + context 'when working with the trusted data hash' do context 'and have opted in to trusted_node_data' do before :each do From b51c12b2ca2811cff7b9458ca594095c207a50b0 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Wed, 26 Feb 2014 18:50:42 +0100 Subject: [PATCH 713/800] (PUP-542) Improve descriptions of trusted & immutable node data settings --- lib/puppet/defaults.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index 97b6c767a..d18c77965 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -426,12 +426,12 @@ module Puppet :default => false, :type => :boolean, :desc => "Stores trusted node data in a hash called $trusted. - When true also makes node provided information immutable", + When true also prevents $trusted from being overridden in any scope.", }, :immutable_node_data => { :default => '$trusted_node_data', :type => :boolean, - :desc => "When true, makes the top scope variables $trusted and $facts reserved and immutable", + :desc => "When true, also prevents $trusted and $facts from being overridden in any scope", } ) Puppet.define_settings(:module_tool, From bf84e35eabd2ba1b579db1913f7cbcfee3cfb2c1 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Wed, 26 Feb 2014 19:01:16 +0100 Subject: [PATCH 714/800] (maint) Add test for interpolation of setting having false value --- spec/unit/settings_spec.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/spec/unit/settings_spec.rb b/spec/unit/settings_spec.rb index b201b3eb8..5c2a8acdf 100755 --- a/spec/unit/settings_spec.rb +++ b/spec/unit/settings_spec.rb @@ -569,6 +569,13 @@ describe Puppet::Settings do @settings.preferred_run_mode.should == :user end + it "interpolates a boolean false without raising an error" do + @settings.define_settings(:section, + :trip_wire => { :type => :boolean, :default => false, :desc => "a trip wire" }, + :tripping => { :default => '$trip_wire', :desc => "once tripped if interpolated was false" }) + @settings[:tripping].should == "false" + end + describe "setbycli" do it "should generate a deprecation warning" do @settings.handlearg("--one", "blah") From 0e794fe5c45088e50fbbe20f52062cd6c00d7b6a Mon Sep 17 00:00:00 2001 From: Ken Barber Date: Wed, 26 Feb 2014 19:41:44 +0000 Subject: [PATCH 715/800] PUP-1772 Change api for Puppet::Util::Profiler#profile to public This patch changes the YardDoc annotations so that the class 'Puppet::Util::Profiler' is now public, and its method #profile is also public. The remaining methods are still marked as private, as they aren't really needed by most external consumers. Signed-off-by: Ken Barber --- lib/puppet/util/profiler.rb | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/puppet/util/profiler.rb b/lib/puppet/util/profiler.rb index c8d1762f5..4246181b4 100644 --- a/lib/puppet/util/profiler.rb +++ b/lib/puppet/util/profiler.rb @@ -2,7 +2,7 @@ require 'benchmark' # A simple profiling callback system. # -# @api private +# @api public module Puppet::Util::Profiler require 'puppet/util/profiler/wall_clock' require 'puppet/util/profiler/object_counts' @@ -11,22 +11,34 @@ module Puppet::Util::Profiler NONE = Puppet::Util::Profiler::None.new # Reset the profiling system to the original state + # + # @api private def self.clear @profiler = nil end # @return This thread's configured profiler + # @api private def self.current @profiler || NONE end # @param profiler [#profile] A profiler for the current thread + # @api private def self.current=(profiler) @profiler = profiler end + # Profile a block of code and log the time it took to execute. + # + # This outputs logs entries to the Puppet masters logging destination + # providing the time it took, a message describing the profiled code + # and a leaf location marking where the profile method was called + # in the profiled hierachy. + # # @param message [String] A description of the profiled event # @param block [Block] The segment of code to profile + # @api public def self.profile(message, &block) current.profile(message, &block) end From 8a877d8c0b4f576f5cd1a102d1e5fee1bf205c9e Mon Sep 17 00:00:00 2001 From: Marc Seeger Date: Tue, 11 Feb 2014 16:08:57 -0500 Subject: [PATCH 716/800] (maint) Add Ruby 2.1.0 to Travis CI --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 09f7db6e5..f8363999f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ script: "bundle exec rake spec" notifications: email: false rvm: + - 2.1.0 - 2.0.0 - 1.9.3 - 1.8.7 From 56521b7d9b077323969990ee90f5d9242d8078e9 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Wed, 26 Feb 2014 21:43:17 +0100 Subject: [PATCH 717/800] (PUP-542) Make tests pass on 1.8.7 ('hash' vs 'Hash' in message) --- spec/integration/parser/compiler_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/integration/parser/compiler_spec.rb b/spec/integration/parser/compiler_spec.rb index dc47393de..b1d447f78 100755 --- a/spec/integration/parser/compiler_spec.rb +++ b/spec/integration/parser/compiler_spec.rb @@ -361,7 +361,7 @@ describe "Puppet::Parser::Compiler" do $facts[the_earth] = is_flat notify { 'test': message => $facts[the_earth] } MANIFEST - }.to raise_error(/frozen Hash/) + }.to raise_error(/frozen [hH]ash/) end it 'should make $facts available even if there are no facts' do From 444455164cd1c8399bfb1b8734cf38971f1ea245 Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Tue, 25 Feb 2014 13:45:03 -0800 Subject: [PATCH 718/800] (pup-1766) Remove duplicate test --- spec/unit/provider_spec.rb | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/spec/unit/provider_spec.rb b/spec/unit/provider_spec.rb index 56987a91b..ce4ff4495 100755 --- a/spec/unit/provider_spec.rb +++ b/spec/unit/provider_spec.rb @@ -155,19 +155,6 @@ describe Puppet::Provider do Puppet::Provider.should respond_to(:specificity) end - it "should consider two defaults to be higher specificity than one default" do - one = provider_of do - defaultfor :osfamily => "solaris" - end - - two = provider_of do - defaultfor :osfamily => "solaris", :operatingsystemrelease => "5.10" - end - - two.specificity.should > one.specificity - end - - it "should be Comparable" do res = Puppet::Type.type(:notify).new(:name => "res") From 5cc77ccd0d195378af3ab780498ce4e8387bfcbd Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Tue, 25 Feb 2014 14:01:00 -0800 Subject: [PATCH 719/800] (pup-1766) Add support for multiple defaultfor's Prior to this change, it was possible to specify a defaultfor to cover, say, multiple osfamily's e.g. with: defaultfor :osfamily => [:os1, :os2] Or it was possible to specify a more complex hash, e.g. with defaultfor :osfamily => :os1, :operatingsystemmajorrelease => "42" But if more specificity for just one of the operatingsystem values was needed, defaultfor couldn't support that. This change allows for this, e.g.: defaultfor :osfamily => :os1 # all variants of os1 defaultfor :osfamily => :os2, :operatingsystemmajrelease => "42" # only release #42 of this os To support this change Provider defaults is now an array of hashes (formerly, it was a merge of the hashes provided by all defaultfor invocations), which requires commensurate updates to the specificity method and the dochook. There are also some new spec tests for representative defaultfor combinations, and two spec tests needed to be updated with the expectation that Facter will be invoked to assess defaultprovider specificity. --- lib/puppet/provider.rb | 51 +++++++++++++++++++++---------------- spec/unit/provider_spec.rb | 52 +++++++++++++++++++++++++++++++++++++- 2 files changed, 80 insertions(+), 23 deletions(-) diff --git a/lib/puppet/provider.rb b/lib/puppet/provider.rb index 6d90f91da..32752f52e 100644 --- a/lib/puppet/provider.rb +++ b/lib/puppet/provider.rb @@ -280,30 +280,35 @@ class Puppet::Provider end # @return [Boolean] Returns whether this implementation satisfies all of the default requirements or not. - # Returns false If defaults are empty. + # Returns false if there is no matching defaultfor # @see Provider.defaultfor # def self.default? - return false if @defaults.empty? - if @defaults.find do |fact, values| + default_match ? true : false + end + + # Look through the array of defaultfor hashes and return the first match. + # @return [Hash<{String => Object}>] the matching hash specified by a defaultfor + # @see Provider.defaultfor + # @api private + def self.default_match + return nil if @defaults.empty? + @defaults.each do |default| + found = default.inject(true) do |result, (fact, values)| values = [values] unless values.is_a? Array + values.map! { |v| v.to_s.downcase.intern } + if fval = Facter.value(fact).to_s and fval != "" fval = fval.to_s.downcase.intern - else - return false - end - # If any of the values match, we're a default. - if values.find do |value| fval == value.to_s.downcase.intern end - false + result &= values.include?(fval) else - true + result = false end end - return false - else - return true + return default if found end + return nil end # Sets a facts filter that determine which of several suitable providers should be picked by default. @@ -315,14 +320,12 @@ class Puppet::Provider # @return [void] # def self.defaultfor(hash) - hash.each do |d,v| - @defaults[d] = v - end + @defaults << hash end # @return [Integer] Returns a numeric specificity for this provider based on how many requirements it has # and number of _ancestors_. The higher the number the more specific the provider. - # The number of requirements is based on the number of defaults set up with {Provider.defaultfor}. + # The number of requirements is based on the hash size of the matching {Provider.defaultfor}. # # The _ancestors_ is the Ruby Module::ancestors method and the number of classes returned is used # to boost the score. The intent is that if two providers are equal, but one is more "derived" than the other @@ -335,13 +338,15 @@ class Puppet::Provider # are to increase the score. What is will actually do is count all classes that Ruby Module::ancestors # returns (which can be other classes than those the parent chain) - in a way, an odd measure of the # complexity of a provider). - (@defaults.length * 100) + ancestors.select { |a| a.is_a? Class }.length + match = default_match + length = match ? match.length : 0 + (length * 100) + ancestors.select { |a| a.is_a? Class }.length end # Initializes defaults and commands (i.e. clears them). # @return [void] def self.initvars - @defaults = {} + @defaults = [] @commands = {} end @@ -491,9 +496,11 @@ class Puppet::Provider dochook(:defaults) do if @defaults.length > 0 - return "Default for " + @defaults.collect do |f, v| - "`#{f}` == `#{[v].flatten.join(', ')}`" - end.sort.join(" and ") + "." + return @defaults.collect do |d| + "Default for " + d.collect do |f, v| + "`#{f}` == `#{[v].flatten.join(', ')}`" + end.sort.join(" and ") + "." + end.join(" ") end end diff --git a/spec/unit/provider_spec.rb b/spec/unit/provider_spec.rb index ce4ff4495..e723f7317 100755 --- a/spec/unit/provider_spec.rb +++ b/spec/unit/provider_spec.rb @@ -288,6 +288,53 @@ describe Puppet::Provider do subject.name.should == type.defaultprovider.name end + describe "when there are multiple defaultfor's of equal specificity" do + before :each do + subject.defaultfor :operatingsystem => :os1 + subject.defaultfor :operatingsystem => :os2 + end + + let(:alternate) { type.provide(:alternate) {} } + + it "should be default for the first defaultfor" do + Facter.expects(:value).with(:operatingsystem).at_least_once.returns :os1 + + provider.should be_default + alternate.should_not be_default + end + + it "should be default for the last defaultfor" do + Facter.expects(:value).with(:operatingsystem).at_least_once.returns :os2 + + provider.should be_default + alternate.should_not be_default + end + end + + describe "when there are multiple defaultfor's with different specificity" do + before :each do + subject.defaultfor :operatingsystem => :os1 + subject.defaultfor :operatingsystem => :os2, :operatingsystemmajrelease => "42" + end + + let(:alternate) { type.provide(:alternate) {} } + + it "should be default for a more specific, but matching, defaultfor" do + Facter.expects(:value).with(:operatingsystem).at_least_once.returns :os2 + Facter.expects(:value).with(:operatingsystemmajrelease).at_least_once.returns "42" + + provider.should be_default + alternate.should_not be_default + end + + it "should be default for a less specific, but matching, defaultfor" do + Facter.expects(:value).with(:operatingsystem).at_least_once.returns :os1 + + provider.should be_default + alternate.should_not be_default + end + end + it "should consider any true value enough to be default" do alternate = type.provide(:alternate) {} @@ -298,13 +345,16 @@ describe Puppet::Provider do alternate.should_not be_default end - it "should not be default if the confine doesn't match" do + it "should not be default if the defaultfor doesn't match" do subject.should_not be_default subject.defaultfor :operatingsystem => :one subject.should_not be_default end it "should consider two defaults to be higher specificity than one default" do + Facter.expects(:value).with(:osfamily).at_least_once.returns "solaris" + Facter.expects(:value).with(:operatingsystemrelease).at_least_once.returns "5.10" + one = type.provide(:one) do defaultfor :osfamily => "solaris" end From 0b5cbc84baca65af73f5cf4d86cef7f766ab1629 Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Tue, 25 Feb 2014 15:14:58 -0800 Subject: [PATCH 720/800] (pup-1766) Set systemd as a default provider for the service type on rhel7 --- lib/puppet/provider/service/systemd.rb | 2 +- spec/unit/provider/service/systemd_spec.rb | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/puppet/provider/service/systemd.rb b/lib/puppet/provider/service/systemd.rb index 7bcc3527e..64cde14be 100644 --- a/lib/puppet/provider/service/systemd.rb +++ b/lib/puppet/provider/service/systemd.rb @@ -5,8 +5,8 @@ Puppet::Type.type(:service).provide :systemd, :parent => :base do commands :systemctl => "systemctl" - #defaultfor :osfamily => [:redhat, :suse] defaultfor :osfamily => [:archlinux] + defaultfor :osfamily => :redhat, :operatingsystemmajrelease => "7" def self.instances i = [] diff --git a/spec/unit/provider/service/systemd_spec.rb b/spec/unit/provider/service/systemd_spec.rb index b2c813d63..108454a77 100755 --- a/spec/unit/provider/service/systemd_spec.rb +++ b/spec/unit/provider/service/systemd_spec.rb @@ -24,6 +24,18 @@ describe Puppet::Type.type(:service).provider(:systemd) do end end + it "should be the default provider on rhel7" do + Facter.expects(:value).with(:osfamily).at_least_once.returns(:redhat) + Facter.expects(:value).with(:operatingsystemmajrelease).returns("7") + described_class.default?.should be_true + end + + it "should not be the default provider on rhel6" do + Facter.expects(:value).with(:osfamily).at_least_once.returns(:redhat) + Facter.expects(:value).with(:operatingsystemmajrelease).returns("6") + described_class.default?.should_not be_true + end + [:enabled?, :enable, :disable, :start, :stop, :status, :restart].each do |method| it "should have a #{method} method" do provider.should respond_to(method) From 784b5e6090ea9090c630055fcd940e8a8f2d4f05 Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Wed, 26 Feb 2014 09:01:39 -0800 Subject: [PATCH 721/800] (pup-1766) Add support for :feature to defaultfor Previous to this change defaultfor had no support for a key of :feature, however the exec/posix.rb provider assumed there was such support. The only reason this worked was that the specificity method evaluated hash length independent of whether the hash matched reality in anyway. E.g. exec/posix.rb could just as easily have specified: defaultfor :foo => :nonsense And it would have a specificity of 1 (yay). This stops working with the changes in this pull request to make specificity actually evaluate whether the specified hash is match in anyway. So, this commit adds the :feature support to defaultfor which exec/posix.rb already assumed it had. That file was the only use of 'defaultfor :feature' I found, but adding this support seems like potentially useful as an enhancement. --- lib/puppet/provider.rb | 33 +++++++++++++++++++++++---------- spec/unit/provider_spec.rb | 23 +++++++++++++++++++++++ 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/lib/puppet/provider.rb b/lib/puppet/provider.rb index 32752f52e..a61dd3660 100644 --- a/lib/puppet/provider.rb +++ b/lib/puppet/provider.rb @@ -294,16 +294,12 @@ class Puppet::Provider def self.default_match return nil if @defaults.empty? @defaults.each do |default| - found = default.inject(true) do |result, (fact, values)| - values = [values] unless values.is_a? Array - values.map! { |v| v.to_s.downcase.intern } - - if fval = Facter.value(fact).to_s and fval != "" - fval = fval.to_s.downcase.intern - - result &= values.include?(fval) - else - result = false + found = default.inject(true) do |result, (key, values)| + case key + when :feature + result &= feature_match(values) + else + result &= fact_match(key, values) end end return default if found @@ -311,6 +307,23 @@ class Puppet::Provider return nil end + def self.fact_match(fact, values) + values = [values] unless values.is_a? Array + values.map! { |v| v.to_s.downcase.intern } + + if fval = Facter.value(fact).to_s and fval != "" + fval = fval.to_s.downcase.intern + + values.include?(fval) + else + false + end + end + + def self.feature_match(value) + Puppet.features.send(value.to_s + "?") + end + # Sets a facts filter that determine which of several suitable providers should be picked by default. # This selection only kicks in if there is more than one suitable provider. # To filter on multiple facts the given hash may contain more than one fact name/value entry. diff --git a/spec/unit/provider_spec.rb b/spec/unit/provider_spec.rb index e723f7317..cd673df81 100755 --- a/spec/unit/provider_spec.rb +++ b/spec/unit/provider_spec.rb @@ -372,6 +372,29 @@ describe Puppet::Provider do child.specificity.should > parent.specificity end + + describe "using a :feature key" do + before :each do + Puppet.features.add(:yay) do true end + Puppet.features.add(:boo) do false end + end + + it "is default for an available feature" do + one = type.provide(:one) do + defaultfor :feature => :yay + end + + one.should be_default + end + + it "is not default for a missing feature" do + two = type.provide(:two) do + defaultfor :feature => :boo + end + + two.should_not be_default + end + end end context "provider commands" do From e37330f8a143bdc438943eabd3a9119286b252fc Mon Sep 17 00:00:00 2001 From: Joshua Hoblitt Date: Fri, 31 Jan 2014 17:19:36 -0700 Subject: [PATCH 722/800] (PUP-1564) add :uninstall_options feature to package rpm provider --- lib/puppet/provider/package/rpm.rb | 9 ++++- spec/unit/provider/package/rpm_spec.rb | 49 ++++++++++++++++++++++++-- 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/lib/puppet/provider/package/rpm.rb b/lib/puppet/provider/package/rpm.rb index 4a544fbb1..8197e484d 100644 --- 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 has_feature :versionable has_feature :install_options + has_feature :uninstall_options # Note: self:: is required here to keep these constants in the context of what will # eventually become this Puppet::Type::Package::ProviderRpm class. @@ -139,7 +140,9 @@ Puppet::Type.type(:package).provide :rpm, :source => :rpm, :parent => Puppet::Pr nvr += ".#{get(:arch)}" end end - rpm "-e", nvr + + flag = ["-e"] + uninstall_options + rpm flag, nvr end def update @@ -150,6 +153,10 @@ Puppet::Type.type(:package).provide :rpm, :source => :rpm, :parent => Puppet::Pr join_options(resource[:install_options]) end + def uninstall_options + join_options(resource[:uninstall_options]) + end + private # Turns a array of options into flags to be passed to rpm install(8) and diff --git a/spec/unit/provider/package/rpm_spec.rb b/spec/unit/provider/package/rpm_spec.rb index 0504b7650..b599c6333 100755 --- a/spec/unit/provider/package/rpm_spec.rb +++ b/spec/unit/provider/package/rpm_spec.rb @@ -194,7 +194,7 @@ describe provider_class do let(:rpm_version) { "RPM version 4.10.0\n" } it "includes the architecture in the package name" do - Puppet::Util::Execution.expects(:execute).with(["/bin/rpm", "-e", 'myresource-1.2.3.4-5.el4.noarch'], execute_options).returns('').at_most_once + Puppet::Util::Execution.expects(:execute).with(["/bin/rpm", ["-e"], 'myresource-1.2.3.4-5.el4.noarch'], execute_options).returns('').at_most_once provider.uninstall end end @@ -207,11 +207,30 @@ describe provider_class do let(:rpm_version) { "RPM version 3.0.6\n" } it "excludes the architecture from the package name" do - Puppet::Util::Execution.expects(:execute).with(["/bin/rpm", "-e", 'myresource-1.2.3.4-5.el4'], execute_options).returns('').at_most_once + Puppet::Util::Execution.expects(:execute).with(["/bin/rpm", ["-e"], 'myresource-1.2.3.4-5.el4'], execute_options).returns('').at_most_once provider.uninstall end end + describe "when uninstalled with options" do + before(:each) do + Puppet::Util::Execution.expects(:execute).with(["/bin/rpm", "-q", "--whatprovides", "myresource", '--nosignature', '--nodigest', "--qf", nevra_format], execute_options).returns("myresource 0 1.2.3.4 5.el4 noarch\n") + end + + let(:resource) do + Puppet::Type.type(:package).new( + :name => resource_name, + :ensure => :absent, + :provider => 'rpm', + :uninstall_options => ['--nodeps'] + ) + end + + it "includes the options" do + Puppet::Util::Execution.expects(:execute).with(["/bin/rpm", ["-e", "--nodeps"], 'myresource-1.2.3.4-5.el4.noarch'], execute_options) + provider.uninstall + end + end end describe "parsing" do @@ -281,12 +300,38 @@ describe provider_class do provider.resource[:install_options] = { '-Darch' => 'vax' } expect(provider.install_options).to eq(['-Darch=vax']) end + it 'returns install_options when an array with hashes' do provider.resource[:install_options] = [ '-L', { '-Darch' => 'vax' }] expect(provider.install_options).to eq(['-L', '-Darch=vax']) end end + describe "#uninstall_options" do + it "returns empty array by default" do + expect(provider.uninstall_options).to eq([]) + end + + it "returns uninstall_options when set" do + provider.resource[:uninstall_options] = ['-n'] + expect(provider.uninstall_options).to eq(['-n']) + end + + it "returns multiple uninstall_options when set" do + provider.resource[:uninstall_options] = ['-L', '/opt/puppet'] + expect(provider.uninstall_options).to eq(['-L', '/opt/puppet']) + end + + it 'returns uninstall_options when set as hash' do + provider.resource[:uninstall_options] = { '-Darch' => 'vax' } + expect(provider.uninstall_options).to eq(['-Darch=vax']) + end + it 'returns uninstall_options when an array with hashes' do + provider.resource[:uninstall_options] = [ '-L', { '-Darch' => 'vax' }] + expect(provider.uninstall_options).to eq(['-L', '-Darch=vax']) + end + end + describe ".nodigest" do { '4.0' => nil, '4.0.1' => nil, From 2968a459fef9fa122dff8249c44ae47a684d73f2 Mon Sep 17 00:00:00 2001 From: Charlie Sharpsteen Date: Wed, 26 Feb 2014 14:54:43 -0800 Subject: [PATCH 723/800] (PUP-1564) Add uninstall_options to RPM provider docs --- lib/puppet/provider/package/rpm.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/puppet/provider/package/rpm.rb b/lib/puppet/provider/package/rpm.rb index 8197e484d..514752ded 100644 --- a/lib/puppet/provider/package/rpm.rb +++ b/lib/puppet/provider/package/rpm.rb @@ -5,11 +5,12 @@ Puppet::Type.type(:package).provide :rpm, :source => :rpm, :parent => Puppet::Pr desc "RPM packaging support; should work anywhere with a working `rpm` binary. - This provider supports the `install_options` attribute, which allows - command-line flags to be passed to the RPM binary. Install options should be - specified as an array, where each element is either a string or a - `{'--flag' => 'value'}` hash. (That hash example would be equivalent to a - `'--flag=value'` string; the hash syntax is available as a convenience.)" + This provider supports the `install_options` and `uninstall_options` + attributes, which allow command-line flags to be passed to the RPM binary. + These options should be specified as an array, where each element is either + a string or a `{'--flag' => 'value'}` hash. (That hash example would be + equivalent to a `'--flag=value'` string; the hash syntax is available as a + convenience.)" has_feature :versionable has_feature :install_options From 6fc7d2509dd54b215d3e8583c685c2ccd5530d07 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 27 Feb 2014 18:36:52 +0100 Subject: [PATCH 724/800] (PUP-1576) Make hyphen in bare words legal (future parser) This adds the ability to use a hyphen in a bare word. The result is the same as if the bare word had been quoted in single quotes. It is not allowed to start or end a bare word with a hyphen, those are always interpreted as :MINUS. --- lib/puppet/pops/parser/lexer2.rb | 17 +++++++++++++---- spec/unit/pops/parser/lexer2_spec.rb | 18 ++++++++++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/lib/puppet/pops/parser/lexer2.rb b/lib/puppet/pops/parser/lexer2.rb index d2ac2eab4..55820caf0 100644 --- a/lib/puppet/pops/parser/lexer2.rb +++ b/lib/puppet/pops/parser/lexer2.rb @@ -154,6 +154,7 @@ class Puppet::Pops::Parser::Lexer2 # PATTERN_CLASSREF = %r{((::){0,1}[A-Z][\w]*)+} PATTERN_NAME = %r{((::)?[a-z][\w]*)(::[a-z][\w]*)*} + PATTERN_BARE_WORD = %r{[a-z_](?:[\w-]*[\w])?} PATTERN_DOLLAR_VAR = %r{\$(::)?(\w+::)*\w+} PATTERN_NUMBER = %r{\b(?:0[xX][0-9A-Fa-f]+|0?\d+(?:\.\d+)?(?:[eE]-?\d+)?)\b} @@ -565,12 +566,20 @@ class Puppet::Pops::Parser::Lexer2 when 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' value = scn.scan(PATTERN_NAME) - if value + # NAME or false start because followed by hyphen(s) and word + if value && !scn.match?(/-+\w/) emit_completed(KEYWORDS[value] || [:NAME, value, scn.pos - before], before) else - # move to faulty position ([a-z] was ok) - scn.pos = scn.pos + 1 - lex_error("Illegal name") + # Restart and check entire pattern (for ease of detecting non allowed trailing hyphen) + scn.pos = before + value = scn.scan(PATTERN_BARE_WORD) + if value + emit_completed([:STRING, value, scn.pos - before], before) + else + # move to faulty position ([a-z] was ok) + scn.pos = scn.pos + 1 + lex_error("Illegal name") + end end when 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', diff --git a/spec/unit/pops/parser/lexer2_spec.rb b/spec/unit/pops/parser/lexer2_spec.rb index b9bdd6eb4..655b6cf54 100644 --- a/spec/unit/pops/parser/lexer2_spec.rb +++ b/spec/unit/pops/parser/lexer2_spec.rb @@ -107,6 +107,24 @@ describe 'Lexer2' do end end + [ 'a-b', 'a--b', 'a-b-c'].each do |string| + it "should lex a BARE WORD STRING on the form '#{string}'" do + tokens_scanned_from(string).should match_tokens2([:STRING, string]) + end + end + + { '-a' => [:MINUS, :NAME], + '--a' => [:MINUS, :MINUS, :NAME], + 'a-' => [:NAME, :MINUS], + 'a- b' => [:NAME, :MINUS, :NAME], + 'a--' => [:NAME, :MINUS, :MINUS], + 'a-$3' => [:NAME, :MINUS, :VARIABLE], + }.each do |source, expected| + it "should lex leading and trailing hyphens from #{source}" do + tokens_scanned_from(source).should match_tokens2(*expected) + end + end + { 'false'=>false, 'true'=>true}.each do |string, value| it "should lex a BOOLEAN on the form '#{string}'" do tokens_scanned_from(string).should match_tokens2([:BOOLEAN, value]) From be1e39d83cb5ba13d86637dd71e6a59269a71804 Mon Sep 17 00:00:00 2001 From: "John (JJ) Jawed" Date: Wed, 12 Feb 2014 01:50:46 +0000 Subject: [PATCH 725/800] (PUP-1665) Fix traversal of signed certs when listing signing requests. With 'puppet cert list', the output is expected to be the list of hosts with outstanding signing requests. However, the implementation still gets the list of signed certificates and traverses this list in an O(N) fashion for each host being returned. Since we know the list of hosts is limited to only signing requests in this case, this traversal is entirely unnecessary and it impacts performance when there are a large number of signed certificates. The fix is to only traverse the list of signed hosts when --all, --signed, or a list of hosts was given on the command line. --- lib/puppet/ssl/certificate_authority.rb | 13 +++++++++---- lib/puppet/ssl/certificate_authority/interface.rb | 5 +++-- .../ssl/certificate_authority/interface_spec.rb | 4 +++- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/lib/puppet/ssl/certificate_authority.rb b/lib/puppet/ssl/certificate_authority.rb index 5453e7b66..0fb6b20b8 100644 --- a/lib/puppet/ssl/certificate_authority.rb +++ b/lib/puppet/ssl/certificate_authority.rb @@ -184,9 +184,11 @@ class Puppet::SSL::CertificateAuthority # Lists the names of all signed certificates. # + # @param name [Array] filter to cerificate names + # # @return [Array] - def list - list_certificates.collect { |c| c.name } + def list(name='*') + list_certificates(name).collect { |c| c.name } end # Return all the certificate objects as found by the indirector @@ -195,13 +197,16 @@ class Puppet::SSL::CertificateAuthority # Created to prevent the case of reading all certs from disk, getting # just their names and verifying the cert for each name, which then # causes the cert to again be read from disk. + # @param name [Array] filter to cerificate names # # @author Jeff Weiss # @api Puppet Enterprise Licensing # + # @param name [Array] filter to cerificate names + # # @return [Array] - def list_certificates - Puppet::SSL::Certificate.indirection.search("*") + def list_certificates(name='*') + Puppet::SSL::Certificate.indirection.search(name) end # Read the next serial from the serial file, and increment the diff --git a/lib/puppet/ssl/certificate_authority/interface.rb b/lib/puppet/ssl/certificate_authority/interface.rb index b68368b8d..20f4d87fd 100644 --- a/lib/puppet/ssl/certificate_authority/interface.rb +++ b/lib/puppet/ssl/certificate_authority/interface.rb @@ -45,7 +45,7 @@ module Puppet # List the hosts. def list(ca) - signed = ca.list + signed = ca.list if [:signed, :all].include?(subjects) requests = ca.waiting? case subjects @@ -57,6 +57,7 @@ module Puppet hosts = requests else hosts = subjects + signed = ca.list(hosts) end certs = {:signed => {}, :invalid => {}, :request => {}} @@ -72,7 +73,7 @@ module Puppet if verify_error certs[:invalid][host] = [ Puppet::SSL::Certificate.indirection.find(host), verify_error ] - elsif signed.include?(host) + elsif (signed and signed.include?(host)) certs[:signed][host] = Puppet::SSL::Certificate.indirection.find(host) else certs[:request][host] = Puppet::SSL::CertificateRequest.indirection.find(host) diff --git a/spec/unit/ssl/certificate_authority/interface_spec.rb b/spec/unit/ssl/certificate_authority/interface_spec.rb index 9e55dd30d..61110b2a9 100755 --- a/spec/unit/ssl/certificate_authority/interface_spec.rb +++ b/spec/unit/ssl/certificate_authority/interface_spec.rb @@ -185,7 +185,7 @@ describe Puppet::SSL::CertificateAuthority::Interface do @digest = mock("digest") @digest.stubs(:to_s).returns("(fingerprint)") @ca.expects(:waiting?).returns %w{host1 host2 host3} - @ca.expects(:list).returns %w{host4 host5 host6} + @ca.expects(:list).returns(%w{host4 host5 host6}).at_most(1) @csr.stubs(:digest).returns @digest @cert.stubs(:digest).returns @digest @ca.stubs(:verify) @@ -207,6 +207,7 @@ describe Puppet::SSL::CertificateAuthority::Interface do describe "and :all was provided" do it "should print a string containing all certificate requests and certificates" do + @ca.expects(:list).returns %w{host4 host5 host6} @ca.stubs(:verify).with("host4").raises(Puppet::SSL::CertificateAuthority::CertificateVerificationError.new(23), "certificate revoked") applier = @class.new(:list, :to => :all) @@ -226,6 +227,7 @@ describe Puppet::SSL::CertificateAuthority::Interface do describe "and :signed was provided" do it "should print a string containing all signed certificate requests and certificates" do + @ca.expects(:list).returns %w{host4 host5 host6} applier = @class.new(:list, :to => :signed) applier.expects(:puts).with(<<-OUTPUT.chomp) From fa522acf47f1ff632ef75a6c4cb4c1d1cf644024 Mon Sep 17 00:00:00 2001 From: Adrien Thebo Date: Thu, 27 Feb 2014 13:45:00 -0800 Subject: [PATCH 726/800] (maint) Allow arrays, bools, and numerics to be frozen --- lib/puppet/parser/scope.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb index 8d557e449..8546dae40 100644 --- a/lib/puppet/parser/scope.rb +++ b/lib/puppet/parser/scope.rb @@ -599,15 +599,18 @@ class Puppet::Parser::Scope # def deep_freeze(object) case object + when Array + object.each {|v| deep_freeze(v) } + object.freeze when Hash object.each {|k, v| deep_freeze(k); deep_freeze(v) } object.freeze - when NilClass + when NilClass, Numeric, TrueClass, FalseClass # do nothing when String object.freeze else - raise Puppet::Error, "Unsupported data type: '#{object.class}" + raise Puppet::Error, "Unsupported data type: '#{object.class}'" end object end From 03734e5ed6e27db0a61524a74867addcf2ea71be Mon Sep 17 00:00:00 2001 From: Patrick Carlisle Date: Fri, 24 May 2013 15:34:17 -0700 Subject: [PATCH 727/800] (#20584) Add acceptance test for safer yaml handling in request body This test uses the handling of report data as a proxy for all payload yaml handling. It assumes that if we have safe yaml handling in one endpoint, that the others will also be using the same code paths for deserialization and therefore be patched correctly. --- ...ve-2013-3567_yaml_deserialization_again.rb | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 acceptance/tests/security/cve-2013-3567_yaml_deserialization_again.rb diff --git a/acceptance/tests/security/cve-2013-3567_yaml_deserialization_again.rb b/acceptance/tests/security/cve-2013-3567_yaml_deserialization_again.rb new file mode 100644 index 000000000..a6c9232c0 --- /dev/null +++ b/acceptance/tests/security/cve-2013-3567_yaml_deserialization_again.rb @@ -0,0 +1,24 @@ +test_name "CVE-2013-3567 Arbitrary YAML Deserialization" + +reportdir = master.tmpdir('yaml_deserialization') + +dangerous_yaml = "--- !ruby/object:Puppet::Transaction::Report { metrics: { resources: !ruby/object:ERB { src: 'exit 0' } }, logs: [], resource_statuses: [], host: '$(puppet master --configprint certname)' }" + +submit_bad_yaml = [ + "curl -k -X PUT", + "--cacert $(puppet master --configprint cacert)", + "--cert $(puppet master --configprint hostcert)", + "--key $(puppet master --configprint hostprivkey)", + "-H 'Content-Type: text/yaml'", + "-d \"#{dangerous_yaml}\"", + "\"https://#{master}:8140/production/report/$(puppet master --configprint certname)\"" +].join(' ') + +with_master_running_on(master, "--reportdir #{reportdir} --reports store --daemonize --dns_alt_names=\"puppet,$(facter hostname),$(facter fqdn)\" --autosign true") do + on master, submit_bad_yaml + on master, "cat #{reportdir}/$(puppet master --configprint certname)/*" do + assert_no_match(/ERB/, stdout, "Improperly propagated ERB object from input into puppet code") + end +end + +on master, "rm -rf #{reportdir}" From 56c2a36826f938067310e5ff226fc9da3ea4270b Mon Sep 17 00:00:00 2001 From: Patrick Carlisle Date: Tue, 28 May 2013 11:33:27 -0700 Subject: [PATCH 728/800] (#20584) Add acceptance test for safer yaml handling in query parameters As in the previous commit this adds an acceptance test to make sure arbitrary objects are not loaded from YAML. This tests input via query parameters, which uses a different code path from YAML submitted in the body of a request. --- ...013-3567_yaml_parameter_deserialization.rb | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 acceptance/tests/security/cve-2013-3567_yaml_parameter_deserialization.rb diff --git a/acceptance/tests/security/cve-2013-3567_yaml_parameter_deserialization.rb b/acceptance/tests/security/cve-2013-3567_yaml_parameter_deserialization.rb new file mode 100644 index 000000000..0b087e938 --- /dev/null +++ b/acceptance/tests/security/cve-2013-3567_yaml_parameter_deserialization.rb @@ -0,0 +1,30 @@ +test_name "CVE-2013-3567 Arbitrary YAML Query Parameter Deserialization" + +CURL_UNABLE_TO_FETCH_PAGE = 22 + +require 'uri' + +dangerous_yaml = "--- !ruby/object:Puppet::Node::Environment { name: 'manage' }" + +submit_bad_yaml_as_parameter = [ + "curl -f -s -S -k -X GET", + "--cacert $(puppet master --configprint cacert)", + "--cert $(puppet master --configprint hostcert)", + "--key $(puppet master --configprint hostprivkey)", + "-H 'Accept: yaml'", + "\"https://#{master}:8140/production/file_metadata/modules/testing/tested?links=#{URI.encode(dangerous_yaml)}\"" +].join(' ') + + +modules = master.tmpdir('modules') +apply_manifest_on master, < directory, owner => puppet } +-> file { "#{modules}/testing": ensure => directory, owner => puppet } +-> file { "#{modules}/testing/files": ensure => directory, owner => puppet } +-> file { "#{modules}/testing/files/tested": ensure => file, content => "test", owner => puppet } +MANIFEST + +with_master_running_on(master, "--modulepath #{modules} --daemonize --dns_alt_names=\"puppet,$(facter hostname),$(facter fqdn)\" --autosign true") do + step "Expect the master to reject the request" + on master, submit_bad_yaml_as_parameter, :acceptable_exit_codes => [CURL_UNABLE_TO_FETCH_PAGE] +end From 8c7cc30da78dac5fc00c300ff3d46401e1ed89a6 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 28 Feb 2014 00:39:23 +0100 Subject: [PATCH 729/800] (maint) Fix typo in unicode escape in future lexer The regular expression was missing a - between A and F --- lib/puppet/pops/parser/slurp_support.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/pops/parser/slurp_support.rb b/lib/puppet/pops/parser/slurp_support.rb index 17658c156..61d47db2b 100644 --- a/lib/puppet/pops/parser/slurp_support.rb +++ b/lib/puppet/pops/parser/slurp_support.rb @@ -65,7 +65,7 @@ module Puppet::Pops::Parser::SlurpSupport # Process unicode escapes first as they require getting 4 hex digits # If later a \u is found it is warned not to be a unicode escape if escapes.include?('u') - str.gsub!(/\\u([\da-fAF]{4})/m) { + str.gsub!(/\\u([\da-fA-F]{4})/m) { [$1.hex].pack("U") } end From d25afca38ff21c77df2bceee9cf627028122863f Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Thu, 27 Feb 2014 17:24:07 -0800 Subject: [PATCH 730/800] (pup-1766) Break out at first failed match The previous version of default_match walked through all of the hash elements even after finding a failure. This patch uses all? to break out at the first failed match; as an additional benefit, the code is cleaner. --- lib/puppet/provider.rb | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/puppet/provider.rb b/lib/puppet/provider.rb index a61dd3660..fe98e6981 100644 --- a/lib/puppet/provider.rb +++ b/lib/puppet/provider.rb @@ -292,19 +292,16 @@ class Puppet::Provider # @see Provider.defaultfor # @api private def self.default_match - return nil if @defaults.empty? - @defaults.each do |default| - found = default.inject(true) do |result, (key, values)| + @defaults.find do |default| + default.all? do |key, values| case key when :feature - result &= feature_match(values) + feature_match(values) else - result &= fact_match(key, values) + fact_match(key, values) end end - return default if found end - return nil end def self.fact_match(fact, values) From 1df0eb44b48e81ea618f08e7c171fbb126833bfc Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Thu, 27 Feb 2014 19:37:41 -0800 Subject: [PATCH 731/800] (maint) Use with_puppet_running_on in backported cve tests. --- .../security/cve-2013-3567_yaml_deserialization_again.rb | 9 ++++++++- .../cve-2013-3567_yaml_parameter_deserialization.rb | 8 +++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/acceptance/tests/security/cve-2013-3567_yaml_deserialization_again.rb b/acceptance/tests/security/cve-2013-3567_yaml_deserialization_again.rb index a6c9232c0..bb216b93b 100644 --- a/acceptance/tests/security/cve-2013-3567_yaml_deserialization_again.rb +++ b/acceptance/tests/security/cve-2013-3567_yaml_deserialization_again.rb @@ -14,7 +14,14 @@ submit_bad_yaml = [ "\"https://#{master}:8140/production/report/$(puppet master --configprint certname)\"" ].join(' ') -with_master_running_on(master, "--reportdir #{reportdir} --reports store --daemonize --dns_alt_names=\"puppet,$(facter hostname),$(facter fqdn)\" --autosign true") do +master_opts = { + 'master' => { + 'reportdir' => reportdir, + 'reports' => 'store', + } +} + +with_puppet_running_on(master, master_opts) do on master, submit_bad_yaml on master, "cat #{reportdir}/$(puppet master --configprint certname)/*" do assert_no_match(/ERB/, stdout, "Improperly propagated ERB object from input into puppet code") diff --git a/acceptance/tests/security/cve-2013-3567_yaml_parameter_deserialization.rb b/acceptance/tests/security/cve-2013-3567_yaml_parameter_deserialization.rb index 0b087e938..d4c059018 100644 --- a/acceptance/tests/security/cve-2013-3567_yaml_parameter_deserialization.rb +++ b/acceptance/tests/security/cve-2013-3567_yaml_parameter_deserialization.rb @@ -24,7 +24,13 @@ apply_manifest_on master, < file { "#{modules}/testing/files/tested": ensure => file, content => "test", owner => puppet } MANIFEST -with_master_running_on(master, "--modulepath #{modules} --daemonize --dns_alt_names=\"puppet,$(facter hostname),$(facter fqdn)\" --autosign true") do +master_opts = { + 'master' => { + 'modulepath' => modules, + } +} + +with_puppet_running_on(master, master_opts) do step "Expect the master to reject the request" on master, submit_bad_yaml_as_parameter, :acceptable_exit_codes => [CURL_UNABLE_TO_FETCH_PAGE] end From e117af8332264adcfbac2c39346f3fc96b8e8d8d Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Thu, 27 Feb 2014 21:26:21 -0800 Subject: [PATCH 732/800] (PUP-1275) Never try to fork on Windows In 3.0.2, in commit 4d1a2f83d9 for #17361 restored the ability for *nix agents to apply a catalog in a forked child process. But the change caused Windows agents to also try to fork if run without the `--onetime` flag. This regression means you can't run Windows agents with `--listen` and kick them. This commit adds a `can_fork?` method to ensure we only attempt to fork if we both can and should fork. There is no change in behavior for *nix agents. --- lib/puppet/agent.rb | 6 +++++- spec/unit/agent_spec.rb | 9 ++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/puppet/agent.rb b/lib/puppet/agent.rb index 14c6c693b..72be5c18d 100644 --- a/lib/puppet/agent.rb +++ b/lib/puppet/agent.rb @@ -15,10 +15,14 @@ class Puppet::Agent def initialize(client_class, should_fork=true) @splayed = false - @should_fork = should_fork + @should_fork = can_fork? && should_fork @client_class = client_class end + def can_fork? + Puppet.features.posix? + end + def needing_restart? Puppet::Application.restart_requested? end diff --git a/spec/unit/agent_spec.rb b/spec/unit/agent_spec.rb index 38e53176d..68197dee8 100755 --- a/spec/unit/agent_spec.rb +++ b/spec/unit/agent_spec.rb @@ -169,7 +169,7 @@ describe Puppet::Agent do @agent.run.should == :result end - describe "when should_fork is true" do + describe "when should_fork is true", :as_platform => :posix do before do @agent = Puppet::Agent.new(AgentTestClient, true) @@ -225,6 +225,13 @@ describe Puppet::Agent do } end end + + describe "on Windows", :as_platform => :windows do + it "should never fork" do + agent = Puppet::Agent.new(AgentTestClient, true) + expect(agent.should_fork).to be_false + end + end end describe "when splaying" do From cad535d94d8813552d7d192a9baac889533d0ceb Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 28 Feb 2014 10:24:17 -0800 Subject: [PATCH 733/800] (maint) Remove desc for generate tasks The benchmark generate tasks are only for internal use by the other tasks that users should use (run and profile). Putting a descritpion on it, though, made it visible in `rake -T` and `rake -D`. This removes it so that users aren't confused. --- tasks/benchmark.rake | 1 - 1 file changed, 1 deletion(-) diff --git a/tasks/benchmark.rake b/tasks/benchmark.rake index bd1dcb9d7..7456c5c0a 100644 --- a/tasks/benchmark.rake +++ b/tasks/benchmark.rake @@ -21,7 +21,6 @@ namespace :benchmark do @benchmark = Benchmarker.new(ENV['TARGET'], ENV['SIZE'].to_i) end - desc "Generate the #{name} scenario." task :generate => :setup do @benchmark.generate @benchmark.setup From a3befa05be59afef1347352c3a9d926d5e583468 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 28 Feb 2014 11:13:40 -0800 Subject: [PATCH 734/800] (PUP-1144) Improve handling of underscore in variable names The last segment of a qualified variable name is allowed to start with an underscore. Previously this was only allowed for a single segment. --- lib/puppet/pops/issues.rb | 2 +- lib/puppet/pops/patterns.rb | 3 ++- .../parser/future_compiler_spec.rb | 24 +++++++++++++++++++ .../pops/evaluator/evaluating_parser_spec.rb | 10 ++++++++ 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/lib/puppet/pops/issues.rb b/lib/puppet/pops/issues.rb index 74cb5525f..f301609a4 100644 --- a/lib/puppet/pops/issues.rb +++ b/lib/puppet/pops/issues.rb @@ -244,7 +244,7 @@ module Puppet::Pops::Issues end ILLEGAL_VAR_NAME = hard_issue :ILLEGAL_VAR_NAME, :name do - "Illegal variable name, The given name '#{name}' does not conform to the naming rule /^((::)?[a-z_]\w*)(::[a-z]\w*)*$/" + "Illegal variable name, The given name '#{name}' does not conform to the naming rule /^((::)?[a-z]\w*)*((::)?[a-z_]\w*)$/" end ILLEGAL_NUMERIC_VAR_NAME = hard_issue :ILLEGAL_NUMERIC_VAR_NAME, :name do diff --git a/lib/puppet/pops/patterns.rb b/lib/puppet/pops/patterns.rb index 61246264f..a2534774d 100644 --- a/lib/puppet/pops/patterns.rb +++ b/lib/puppet/pops/patterns.rb @@ -35,7 +35,8 @@ module Puppet::Pops::Patterns DOLLAR_VAR = %r{\$(::)?(\w+::)*\w+} # VAR_NAME matches the name part of a variable (The $ character is not included) - VAR_NAME = %r{\A((::)?[a-z_]\w*)(::[a-z]\w*)*\z} + # Note, that only the final segment may start with an underscore. + VAR_NAME = %r{\A(:?(::)?[a-z]\w*)*(:?(::)?[a-z_]\w*)\z} # A Numeric var name must be the decimal number 0, or a decimal number not starting with 0 NUMERIC_VAR_NAME = %r{\A(?:0|(?:[1-9][0-9]*))\z} diff --git a/spec/integration/parser/future_compiler_spec.rb b/spec/integration/parser/future_compiler_spec.rb index b2c606e0f..5e56a7459 100644 --- a/spec/integration/parser/future_compiler_spec.rb +++ b/spec/integration/parser/future_compiler_spec.rb @@ -328,6 +328,30 @@ describe "Puppet::Parser::Compiler" do end end + context "when dealing with variable references" do + it 'an initial underscore in a variable name is ok' do + node = Puppet::Node.new("testing_x") + catalog = compile_to_catalog(<<-MANIFEST, node) + class a { $_a = 10} + include a + notify { 'test': message => $a::_a } + MANIFEST + + catalog.resource("Notify[test]")[:message].should == 10 + end + + it 'an initial underscore in not ok if elsewhere than last segment' do + node = Puppet::Node.new("testing_x") + expect { + catalog = compile_to_catalog(<<-MANIFEST, node) + class a { $_a = 10} + include a + notify { 'test': message => $_a::_a } + MANIFEST + }.to raise_error(/Illegal variable name/) + end + end + context 'when working with the trusted data hash' do context 'and have opted in to hashed_node_data' do before :each do diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index 43cfb7737..127427dc8 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -840,6 +840,16 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do expect { parser.evaluate_string(scope, source, __FILE__) }.to raise_error(error_pattern) end end + + context "an initial underscore in the last segment of a var name is allowed" do + { '$_a = 1' => 1, + '$__a = 1' => 1, + }.each do |source, value| + it "as in this example '#{source}'" do + parser.evaluate_string(scope, source, __FILE__).should == value + end + end + end end context "When evaluating relationships" do From 20c3c014ea2990184579f0f4ea32e27ade3ddd60 Mon Sep 17 00:00:00 2001 From: Adrien Thebo Date: Wed, 26 Feb 2014 16:05:13 -0800 Subject: [PATCH 735/800] (PUP-876) Add 'any' confine --- lib/puppet/confine/any.rb | 26 ++++++++++++++++++++++++++ lib/puppet/confiner.rb | 1 + 2 files changed, 27 insertions(+) create mode 100644 lib/puppet/confine/any.rb diff --git a/lib/puppet/confine/any.rb b/lib/puppet/confine/any.rb new file mode 100644 index 000000000..392181287 --- /dev/null +++ b/lib/puppet/confine/any.rb @@ -0,0 +1,26 @@ +class Puppet::Confine::Any < Puppet::Confine + def self.summarize(confines) + confines.inject(0) { |count, confine| count + confine.summary } + end + + def pass?(value) + !! value + end + + def message(value) + "0 confines (of #{value.length}) were true" + end + + def summary + result.find_all { |v| v == true }.length + end + + def valid? + if @values.any? { |value| pass?(value) } + true + else + Puppet.debug("#{label}: #{message(@values)}") + false + end + end +end diff --git a/lib/puppet/confiner.rb b/lib/puppet/confiner.rb index 57176e799..50282fd97 100644 --- a/lib/puppet/confiner.rb +++ b/lib/puppet/confiner.rb @@ -13,6 +13,7 @@ module Puppet::Confiner # * `:true` => a predicate code block returning true # * `:false` => a predicate code block returning false # * `:feature` => name of system feature that must be present + # * `:any` => an array of expressions that will be ORed together # # @example # confine :operatingsystem => [:redhat, :fedora] From 7bdcbea51284b47b2cada15b5f55381fd2dc4815 Mon Sep 17 00:00:00 2001 From: Adrien Thebo Date: Wed, 26 Feb 2014 13:31:06 -0800 Subject: [PATCH 736/800] (PUP-876) Enable upstart service provider on Redhat This commit allows the upstart service provider to function on Redhat systems. Redhat 6 included support for upstart and while it wasn't widely embraced it is available for use. The 'serial' and 'tty' services on Redhat 6 systems have instances which the service type/provider cannot handle, so we have to ignore them for the upstart provider to work out of the box. --- lib/puppet/provider/service/upstart.rb | 23 ++++++++++++++++++---- spec/unit/provider/service/upstart_spec.rb | 8 ++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/lib/puppet/provider/service/upstart.rb b/lib/puppet/provider/service/upstart.rb index 192942284..1aed8dae3 100644 --- a/lib/puppet/provider/service/upstart.rb +++ b/lib/puppet/provider/service/upstart.rb @@ -7,11 +7,14 @@ Puppet::Type.type(:service).provide :upstart, :parent => :debian do desc "Ubuntu service management with `upstart`. - This provider manages `upstart` jobs, which have replaced `initd` services - on Ubuntu. For `upstart` documentation, see . + This provider manages `upstart` jobs on Ubuntu. For `upstart` documentation, + see . " - # confine to :ubuntu for now because I haven't tested on other platforms - confine :operatingsystem => :ubuntu #[:ubuntu, :fedora, :debian] + + confine :any => [ + Facter.value(:operatingsystem) == 'Ubuntu', + (Facter.value(:osfamily) == 'RedHat' and Facter.value(:operatingsystemrelease) =~ /^6\./), + ] defaultfor :operatingsystem => :ubuntu @@ -29,6 +32,18 @@ Puppet::Type.type(:service).provide :upstart, :parent => :debian do self.get_services(self.excludes) # Take exclude list from init provider end + def self.excludes + excludes = super + if Facter.value(:osfamily) == 'RedHat' + # Puppet cannot deal with services that have instances, so we have to + # ignore these services using instances on redhat based systems. + excludes += %w[serial tty] + end + + excludes + end + + def self.get_services(exclude=[]) instances = [] execpipe("#{command(:initctl)} list") { |process| diff --git a/spec/unit/provider/service/upstart_spec.rb b/spec/unit/provider/service/upstart_spec.rb index 249777e58..69c02b6f9 100755 --- a/spec/unit/provider/service/upstart_spec.rb +++ b/spec/unit/provider/service/upstart_spec.rb @@ -22,6 +22,14 @@ describe Puppet::Type.type(:service).provider(:upstart) do provider_class.stubs(:which).with("/sbin/initctl").returns("/sbin/initctl") end + describe "excluding services" do + it "ignores tty and serial on Redhat systems" do + Facter.stubs(:value).with(:osfamily).returns('RedHat') + expect(described_class.excludes).to include 'serial' + expect(described_class.excludes).to include 'tty' + end + end + describe "#instances" do it "should be able to find all instances" do lists_processes_as("rc stop/waiting\nssh start/running, process 712") From 637dd61d66ac7983b48f99560cfafc6cc4a0ad68 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 28 Feb 2014 12:03:26 -0800 Subject: [PATCH 737/800] (maint) Detect reparse when :code changes Previously the detection for reparse didn't take into account the parsing having been done from the Puppet[:code] setting. This meant that if a test tried to compile several different manifests for the same node it would only ever compile from the first value of Puppet[:code] and subsequently reuse that for all other calls (even if Puppet[:code] changed). This adds a check for changes to Puppet[:code] to cause it to re-calculate known_resource_types. --- lib/puppet/node/environment.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb index 7548d4a29..02b05b348 100644 --- a/lib/puppet/node/environment.rb +++ b/lib/puppet/node/environment.rb @@ -348,7 +348,8 @@ class Puppet::Node::Environment end def check_for_reparse - if @known_resource_types && @known_resource_types.require_reparse? + if (Puppet[:code] != @parsed_code) || (@known_resource_types && @known_resource_types.require_reparse?) + @parsed_code = nil @known_resource_types = nil end end @@ -431,8 +432,9 @@ class Puppet::Node::Environment def perform_initial_import return empty_parse_result if Puppet[:ignoreimport] parser = Puppet::Parser::ParserFactory.parser(self) - if code = Puppet[:code] and code != "" - parser.string = code + @parsed_code = Puppet[:code] + if @parsed_code != "" + parser.string = @parsed_code parser.parse else file = self.manifest From 5207ebac96831df25363763fde056310bab49eaf Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 28 Feb 2014 14:12:17 -0800 Subject: [PATCH 738/800] (PUP-542) Test the gamut of mutation There are a lot of ways that the facts hash could be mutated. This checks that none of those ways can happen. --- spec/integration/parser/compiler_spec.rb | 75 +++++++++++++++--------- 1 file changed, 46 insertions(+), 29 deletions(-) diff --git a/spec/integration/parser/compiler_spec.rb b/spec/integration/parser/compiler_spec.rb index b1d447f78..f10ce1adc 100755 --- a/spec/integration/parser/compiler_spec.rb +++ b/spec/integration/parser/compiler_spec.rb @@ -2,9 +2,11 @@ require 'spec_helper' require 'puppet/parser/parser_factory' require 'puppet_spec/compiler' +require 'matchers/resource' describe "Puppet::Parser::Compiler" do include PuppetSpec::Compiler + include Matchers::Resource before :each do @node = Puppet::Node.new "testnode" @@ -320,12 +322,37 @@ describe "Puppet::Parser::Compiler" do Puppet[:immutable_node_data] = true end - it 'should make $facts available' do + def node_with_facts(facts) Puppet[:facts_terminus] = :memory - facts = Puppet::Node::Facts.new("testing", 'the_facts' => 'straight') - Puppet::Node::Facts.indirection.save(facts) + Puppet::Node::Facts.indirection.save(Puppet::Node::Facts.new("testing", facts)) node = Puppet::Node.new("testing") node.fact_merge + node + end + + matcher :fail_compile_with do |node, message_regex| + match do |manifest| + @error = nil + begin + compile_to_catalog(manifest, node) + false + rescue Puppet::Error => e + @error = e + message_regex.match(e.message) + end + end + + failure_message_for_should do + if @error + "failed with #{@error}\n#{@error.backtrace}" + else + "did not fail" + end + end + end + + it 'should make $facts available' do + node = node_with_facts('the_facts' => 'straight') catalog = compile_to_catalog(<<-MANIFEST, node) notify { 'test': message => $facts[the_facts] } @@ -335,45 +362,35 @@ describe "Puppet::Parser::Compiler" do end it 'should make $facts reserved' do - Puppet[:facts_terminus] = :memory - facts = Puppet::Node::Facts.new("testing", 'the_facts' => 'straight') - Puppet::Node::Facts.indirection.save(facts) - node = Puppet::Node.new("testing") - node.fact_merge + node = node_with_facts('the_facts' => 'straight') - expect { - catalog = compile_to_catalog(<<-MANIFEST, node) - $facts = {} - notify { 'test': message => $facts[the_facts] } - MANIFEST - }.to raise_error(/assign to a reserved variable name: 'facts'/) + expect('$facts = {}').to fail_compile_with(node, /assign to a reserved variable name: 'facts'/) + expect('class a { $facts = {} } include a').to fail_compile_with(node, /assign to a reserved variable name: 'facts'/) end it 'should make $facts immutable' do - Puppet[:facts_terminus] = :memory - facts = Puppet::Node::Facts.new("testing", 'the_facts' => 'straight') - Puppet::Node::Facts.indirection.save(facts) - node = Puppet::Node.new("testing") - node.fact_merge + node = node_with_facts('string' => 'value', 'array' => ['string'], 'hash' => { 'a' => 'string' }, 'number' => 1, 'boolean' => true) - expect { - catalog = compile_to_catalog(<<-MANIFEST, node) - $facts[the_earth] = is_flat - notify { 'test': message => $facts[the_earth] } - MANIFEST - }.to raise_error(/frozen [hH]ash/) + expect('$i=inline_template("<% @facts[%q{new}] = 2 %>")').to fail_compile_with(node, /frozen Hash/i) + expect('$i=inline_template("<% @facts[%q{string}].chop! %>")').to fail_compile_with(node, /frozen String/i) + + expect('$i=inline_template("<% @facts[%q{array}][0].chop! %>")').to fail_compile_with(node, /frozen String/i) + expect('$i=inline_template("<% @facts[%q{array}][1] = 2 %>")').to fail_compile_with(node, /frozen Array/i) + + expect('$i=inline_template("<% @facts[%q{hash}][%q{a}].chop! %>")').to fail_compile_with(node, /frozen String/i) + expect('$i=inline_template("<% @facts[%q{hash}][%q{b}] = 2 %>")').to fail_compile_with(node, /frozen Hash/i) end it 'should make $facts available even if there are no facts' do - Puppet[:facts_terminus] = :memory - node = Puppet::Node.new("testing2") - node.fact_merge + Puppet[:facts_terminus] = :memory + node = Puppet::Node.new("testing2") + node.fact_merge catalog = compile_to_catalog(<<-MANIFEST, node) notify { 'test': message => $facts } MANIFEST - catalog.resource("Notify[test]")[:message].should == {} + expect(catalog).to have_resource("Notify[test]").with_parameter(:message, {}) end end From 95a3a096f0a36f32541551bdd3186f99576b4f64 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 28 Feb 2014 14:26:31 -0800 Subject: [PATCH 739/800] (PUP-1814) Make double backslash in sq string behave as documented. This makes it possible to escapce a backslash in a sq string. Previously this did not work which made it impossible to end a sq string with a backslash. --- lib/puppet/pops/parser/slurp_support.rb | 2 +- spec/unit/pops/parser/lexer2_spec.rb | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/puppet/pops/parser/slurp_support.rb b/lib/puppet/pops/parser/slurp_support.rb index 61d47db2b..fb2af8304 100644 --- a/lib/puppet/pops/parser/slurp_support.rb +++ b/lib/puppet/pops/parser/slurp_support.rb @@ -12,7 +12,7 @@ module Puppet::Pops::Parser::SlurpSupport SLURP_DQ_PATTERN = /(?:[^\\]|^|[^\\])(?:[\\]{2})*(["]|[$]\{?)/ SLURP_UQ_PATTERN = /(?:[^\\]|^|[^\\])(?:[\\]{2})*([$]\{?|\z)/ SLURP_ALL_PATTERN = /.*(\z)/ - SQ_ESCAPES = %w{ ' } + SQ_ESCAPES = %w{ \\ ' } DQ_ESCAPES = %w{ \\ $ ' " r n t s u}+["\r\n", "\n"] UQ_ESCAPES = %w{ \\ $ r n t s u}+["\r\n", "\n"] diff --git a/spec/unit/pops/parser/lexer2_spec.rb b/spec/unit/pops/parser/lexer2_spec.rb index 655b6cf54..ce1f0ab8a 100644 --- a/spec/unit/pops/parser/lexer2_spec.rb +++ b/spec/unit/pops/parser/lexer2_spec.rb @@ -164,7 +164,14 @@ describe 'Lexer2' do { "''" => '', "'a'" => 'a', "'a\\'b'" =>"a'b", - "'a\\r\\n\\t\\s\\$\\\"\\\\b'" => "a\\r\\n\\t\\s\\$\\\"\\\\b" + "'a\\rb'" =>"a\\rb", + "'a\\nb'" =>"a\\nb", + "'a\\tb'" =>"a\\tb", + "'a\\sb'" =>"a\\sb", + "'a\\$b'" =>"a\\$b", + "'a\\\"b'" =>"a\\\"b", + "'a\\\\b'" =>"a\\b", + "'a\\\\'" =>"a\\", }.each do |source, expected| it "should lex a single quoted STRING on the form #{source}" do tokens_scanned_from(source).should match_tokens2([:STRING, expected]) From 079e3ec91dbe131036292cbb59f95550c050f7a2 Mon Sep 17 00:00:00 2001 From: Kylo Ginsberg Date: Fri, 28 Feb 2014 22:09:20 -0800 Subject: [PATCH 740/800] (pup-1825) Allow use of facter 2.x Before this commit, Puppet uses pessimistic versioning (the ~> syntax) to specify that it could work with any Facter version in the 1.x series greater than some minimum, but was pessimistic about (i.e. didn't assume it supported) a 2.x Facter. In fact, the facter API to puppet has not changed in facter 2.x so puppet can in fact work with a suitable 1.x or any 2.x. This change replaces use of the pessimistic operator with explicit version ranges, effectively extending the range previously provided by the pessmistic operator to include the 2.x series. --- .gemspec | 6 +++--- Gemfile | 2 +- ext/project_data.yaml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.gemspec b/.gemspec index ca47cdf26..ea92fccbd 100644 --- a/.gemspec +++ b/.gemspec @@ -42,14 +42,14 @@ Gem::Specification.new do |s| s.specification_version = 3 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then - s.add_runtime_dependency(%q, ["~> 1.5"]) + s.add_runtime_dependency(%q, ["> 1.5", "< 3"]) s.add_runtime_dependency(%q, ["~> 1.0"]) else - s.add_dependency(%q, ["~> 1.5"]) + s.add_dependency(%q, ["> 1.5", "< 3"]) s.add_dependency(%q, ["~> 1.0"]) end else - s.add_dependency(%q, ["~> 1.5"]) + s.add_dependency(%q, ["> 1.5", "< 3"]) s.add_dependency(%q, ["~> 1.0"]) end end diff --git a/Gemfile b/Gemfile index c5f446704..7188ad9b7 100644 --- a/Gemfile +++ b/Gemfile @@ -19,7 +19,7 @@ platforms :ruby do end gem "puppet", :path => File.dirname(__FILE__), :require => false -gem "facter", *location_for(ENV['FACTER_LOCATION'] || '~> 1.6') +gem "facter", *location_for(ENV['FACTER_LOCATION'] || ['> 1.6', '< 3']) gem "hiera", *location_for(ENV['HIERA_LOCATION'] || '~> 1.0') gem "rake", :require => false gem "rgen", "0.6.5", :require => false diff --git a/ext/project_data.yaml b/ext/project_data.yaml index ed18d8c40..ee3fc71f5 100644 --- a/ext/project_data.yaml +++ b/ext/project_data.yaml @@ -15,7 +15,7 @@ gem_executables: 'puppet' gem_default_executables: 'puppet' gem_forge_project: 'puppet' gem_runtime_dependencies: - facter: '~> 1.6' + facter: ['> 1.6', '< 3'] hiera: '~> 1.0' rgen: '~> 0.6.5' json_pure: From 28490d235e639d60cd72eda4c73d82ad51717997 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sat, 1 Mar 2014 16:32:21 +0100 Subject: [PATCH 741/800] (PUP-1085) Make Pacman provider group-aware The Archlinux Pacman package manager has a concept of package groups, which due to the provider not checking for a group being installed caused all contained packages to be reinstalled. This commit uses `pacman -Qg` in addition to `pacman -Q` to check for installed groups. Because groups don't have versions this will still cause all packages in a group to be reinstalled if ensure is set to latest. --- lib/puppet/provider/package/pacman.rb | 49 +++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/lib/puppet/provider/package/pacman.rb b/lib/puppet/provider/package/pacman.rb index 49a009718..4fd71ffe3 100644 --- a/lib/puppet/provider/package/pacman.rb +++ b/lib/puppet/provider/package/pacman.rb @@ -1,4 +1,5 @@ require 'puppet/provider/package' +require 'set' require 'uri' Puppet::Type.type(:package).provide :pacman, :parent => Puppet::Provider::Package do @@ -68,8 +69,14 @@ Puppet::Type.type(:package).provide :pacman, :parent => Puppet::Provider::Packag [command(:pacman), "-Q"] end - # Fetch the list of packages currently installed on the system. - def self.instances + # Pacman has a concept of package groups as well. + # Package groups have no versions. + def self.listgroupcmd + [command(:pacman), "-Qg"] + end + + # Get installed packages (pacman -Q) + def self.installedpkgs packages = [] begin execpipe(listcmd()) do |process| @@ -87,6 +94,7 @@ Puppet::Type.type(:package).provide :pacman, :parent => Puppet::Provider::Packag hash[:provider] = self.name packages << new(hash) + hash = {} else warning("Failed to match line %s" % line) @@ -99,6 +107,43 @@ Puppet::Type.type(:package).provide :pacman, :parent => Puppet::Provider::Packag packages end + # Get installed groups (pacman -Qg) + def self.installedgroups + packages = [] + begin + execpipe(listgroupcmd()) do |process| + # pacman -Qg output is 'groupname packagename' + # Groups need to be deduplicated + groups = Set[] + + process.each_line { |line| + groups.add(line.split[0]) + } + + groups.each { |line| + hash = { + :name => line, + :ensure => "1", # Groups don't have versions, so ensure => latest + # will still cause a reinstall. + :provider => self.name + } + packages << new(hash) + } + end + rescue Puppet::ExecutionFailure + return nil + end + packages + end + + # Fetch the list of packages currently installed on the system. + def self.instances + packages = self.installedpkgs + packages.concat(self.installedgroups) + packages + end + + # Because Archlinux is a rolling release based distro, installing a package # should always result in the newest release. def update From 18bf7e9a0c0b4299f5150f4a20658e924a9f8fb8 Mon Sep 17 00:00:00 2001 From: Adrien Thebo Date: Mon, 3 Mar 2014 11:11:18 -0800 Subject: [PATCH 742/800] (PUP-876) Skip resource service check on EL 6 Redmine issues (#4123) and (#4124) added acceptance tests on RHEL systems to ensure that the services listed by chkconfig matched the list of services returned by `puppet resource service`, but this assumed that chkconfig would be authoritative for all running services. Commit 7bdcbea enabled the upstart service provider on EL 6 systems which breaks this assumption and causes the acceptance tests to fail. This commit disables these tests on EL 6 since the test is no longer meaningful. --- .../service/ticket_4123_should_list_all_running_redhat.rb | 2 +- .../service/ticket_4124_should_list_all_disabled.rb | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/acceptance/tests/resource/service/ticket_4123_should_list_all_running_redhat.rb b/acceptance/tests/resource/service/ticket_4123_should_list_all_running_redhat.rb index 5a905218a..bf556d6c6 100755 --- a/acceptance/tests/resource/service/ticket_4123_should_list_all_running_redhat.rb +++ b/acceptance/tests/resource/service/ticket_4123_should_list_all_running_redhat.rb @@ -4,7 +4,7 @@ step "Validate services running agreement ralsh vs. OS service count" # ticket_4123_should_list_all_running_redhat.sh hosts.each do |host| - if host['platform'] =~ /el-[56]/ + if host['platform'] =~ /el-5/ run_script_on(host, File.join(File.dirname(__FILE__), 'ticket_4123_should_list_all_running_redhat.sh')) else skip_test "Test not supported on this plaform" diff --git a/acceptance/tests/resource/service/ticket_4124_should_list_all_disabled.rb b/acceptance/tests/resource/service/ticket_4124_should_list_all_disabled.rb index 02b8bc76e..750f142be 100755 --- a/acceptance/tests/resource/service/ticket_4124_should_list_all_disabled.rb +++ b/acceptance/tests/resource/service/ticket_4124_should_list_all_disabled.rb @@ -4,9 +4,9 @@ step "Validate disabled services agreement ralsh vs. OS service count" # ticket_4124_should_list_all_disabled.sh hosts.each do |host| - unless host['platform'] =~ /el-[56]/ - skip_test "Test not supported on this plaform" - else + if host['platform'] =~ /el-5/ run_script_on(host, File.join(File.dirname(__FILE__), 'ticket_4124_should_list_all_disabled.sh')) + else + skip_test "Test not supported on this plaform" end end From e61ac841ae86e4ecf09a458ddbda7adcafe229c3 Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Mon, 3 Mar 2014 11:57:43 -0800 Subject: [PATCH 743/800] (maint) Fix useradd spec when using ruby-shadow 2.3.3. The ruby-shadow gem inserted a new member into the PasswdEntry struct in 2.3.3. This caused a useradd provider spec to fail that was relying on a member that came after the newly inserted member. The failure only occurs when the ruby-shadow gem is installed. The fix is to populate the struct members by name rather than relying on the member order. --- spec/unit/provider/user/useradd_spec.rb | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/spec/unit/provider/user/useradd_spec.rb b/spec/unit/provider/user/useradd_spec.rb index 6188dca84..215fd6cf4 100755 --- a/spec/unit/provider/user/useradd_spec.rb +++ b/spec/unit/provider/user/useradd_spec.rb @@ -25,17 +25,16 @@ describe Puppet::Type.type(:user).provider(:useradd) do let(:shadow_entry) { return unless Puppet.features.libshadow? - Struct::PasswdEntry.new( - 'myuser', # login name - '$6$FvW8Ib8h$qQMI/CR9m.QzIicZKutLpBgCBBdrch1IX0rTnxuI32K1pD9.RXZrmeKQlaC.RzODNuoUtPPIyQDufunvLOQWF0', # encrypted password - 15573, # date of last password change - 10, # minimum password age - 20, # maximum password age - 7, # password warning period - -1, # password inactivity period - 15706, # account expiration date - -1 # reserved field - ) + entry = Struct::PasswdEntry.new + entry[:sp_namp] = 'myuser' # login name + entry[:sp_pwdp] = '$6$FvW8Ib8h$qQMI/CR9m.QzIicZKutLpBgCBBdrch1IX0rTnxuI32K1pD9.RXZrmeKQlaC.RzODNuoUtPPIyQDufunvLOQWF0' # encrypted password + entry[:sp_lstchg] = 15573 # date of last password change + entry[:sp_min] = 10 # minimum password age + entry[:sp_max] = 20 # maximum password age + entry[:sp_warn] = 7 # password warning period + entry[:sp_inact] = -1 # password inactivity period + entry[:sp_expire] = 15706 # account expiration date + entry } describe "#create" do From 88c1012fba3325221b79456afdddafc880f89e74 Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Mon, 3 Mar 2014 14:10:40 -0800 Subject: [PATCH 744/800] (maint) Change Travis CI to output documentation format for specs. We've been observing a sporadic "rake aborted" failure on Travis CI. This change turns on "documentation" format for rspec so that we can at least see if the test run is aborting on the same test each time. --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index f8363999f..5f5634362 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,3 +12,5 @@ rvm: matrix: allow_failures: - rvm: ruby-head +env: + - SPEC_OPTS="--format documentation" \ No newline at end of file From e11cb26d6d95d6beb86de304570fde3f05a5a50e Mon Sep 17 00:00:00 2001 From: Adrien Thebo Date: Mon, 3 Mar 2014 11:33:59 -0800 Subject: [PATCH 745/800] (PUP-1821) Require Facter 1.7 or greater We're starting to rely on facts that were released in Facter 1.7.0, which means that we always need to pull in 1.7 to make those facts available. --- .gemspec | 6 +++--- ext/debian/control | 12 ++++++------ ext/ips/transforms | 2 +- ext/redhat/puppet.spec.erb | 4 ++-- ext/suse/puppet.spec | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.gemspec b/.gemspec index ea92fccbd..fd7379ebc 100644 --- a/.gemspec +++ b/.gemspec @@ -42,14 +42,14 @@ Gem::Specification.new do |s| s.specification_version = 3 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then - s.add_runtime_dependency(%q, ["> 1.5", "< 3"]) + s.add_runtime_dependency(%q, [">= 1.7", "< 3"]) s.add_runtime_dependency(%q, ["~> 1.0"]) else - s.add_dependency(%q, ["> 1.5", "< 3"]) + s.add_dependency(%q, [">= 1.7", "< 3"]) s.add_dependency(%q, ["~> 1.0"]) end else - s.add_dependency(%q, ["> 1.5", "< 3"]) + s.add_dependency(%q, [">= 1.7", "< 3"]) s.add_dependency(%q, ["~> 1.0"]) end end diff --git a/ext/debian/control b/ext/debian/control index d180d062d..d294e3b69 100644 --- a/ext/debian/control +++ b/ext/debian/control @@ -3,7 +3,7 @@ Section: admin Priority: optional Maintainer: Puppet Labs Uploaders: Micah Anderson , Andrew Pollock , Nigel Kersten , Stig Sandbeck Mathisen -Build-Depends-Indep: ruby | ruby-interpreter, libopenssl-ruby, facter (>= 1.6.12) +Build-Depends-Indep: ruby | ruby-interpreter, libopenssl-ruby, facter (>= 1.7.0) Build-Depends: debhelper (>= 7.0.0), openssl Standards-Version: 3.9.1 Vcs-Git: git://github.com/puppetlabs/puppet @@ -11,7 +11,7 @@ Homepage: http://projects.puppetlabs.com/projects/puppet Package: puppet-common Architecture: all -Depends: ${misc:Depends}, ruby | ruby-interpreter, libxmlrpc-ruby, libopenssl-ruby, ruby-shadow | libshadow-ruby1.8, libaugeas-ruby | libaugeas-ruby1.9.1 | libaugeas-ruby1.8, adduser, lsb-base, sysv-rc (>= 2.86) | file-rc, hiera (>= 1.0.0), facter (>= 1.6.12), ruby-rgen (>= 0.6.5), libjson-ruby | ruby-json +Depends: ${misc:Depends}, ruby | ruby-interpreter, libxmlrpc-ruby, libopenssl-ruby, ruby-shadow | libshadow-ruby1.8, libaugeas-ruby | libaugeas-ruby1.9.1 | libaugeas-ruby1.8, adduser, lsb-base, sysv-rc (>= 2.86) | file-rc, hiera (>= 1.0.0), facter (>= 1.7.0), ruby-rgen (>= 0.6.5), libjson-ruby | ruby-json Recommends: lsb-release, debconf-utils Suggests: ruby-selinux | libselinux-ruby1.8, librrd-ruby1.9.1 | librrd-ruby1.8 Breaks: puppet (<< 2.6.0~rc2-1), puppetmaster (<< 0.25.4-1) @@ -56,7 +56,7 @@ Description: Centralized configuration management - agent startup and compatibil Package: puppetmaster-common Architecture: all -Depends: ${misc:Depends}, ruby | ruby-interpreter, puppet-common (= ${binary:Version}), facter (>= 1.6.12), lsb-base +Depends: ${misc:Depends}, ruby | ruby-interpreter, puppet-common (= ${binary:Version}), facter (>= 1.7.0), lsb-base Breaks: puppet (<< 0.24.7-1), puppetmaster (<< 2.6.1~rc2-1) Replaces: puppetmaster (<< 2.6.1~rc2-1) Suggests: apache2 | nginx, puppet-el, vim-puppet, stompserver, ruby-stomp | libstomp-ruby1.8, @@ -78,7 +78,7 @@ Description: Puppet master common scripts Package: puppetmaster Architecture: all -Depends: ${misc:Depends}, ruby | ruby-interpreter, puppetmaster-common (= ${source:Version}), facter (>= 1.6.12), lsb-base +Depends: ${misc:Depends}, ruby | ruby-interpreter, puppetmaster-common (= ${source:Version}), facter (>= 1.7.0), lsb-base Breaks: puppet (<< 0.24.7-1) Suggests: apache2 | nginx, puppet-el, vim-puppet, stompserver, ruby-stomp | libstomp-ruby1.8, rdoc, ruby-ldap | libldap-ruby1.8, puppetdb-terminus @@ -99,7 +99,7 @@ Description: Centralized configuration management - master startup and compatibi Package: puppetmaster-passenger Architecture: all -Depends: ${misc:Depends}, ruby | ruby-interpreter, puppetmaster-common (= ${source:Version}), facter (>= 1.6.12), lsb-base, libapache2-mod-passenger +Depends: ${misc:Depends}, ruby | ruby-interpreter, puppetmaster-common (= ${source:Version}), facter (>= 1.7.0), lsb-base, libapache2-mod-passenger Conflicts: puppetmaster (<< 2.6.1~rc2-1) Replaces: puppetmaster (<< 2.6.1~rc2-1) Description: Centralised configuration management - master setup to run under mod passenger @@ -136,7 +136,7 @@ Description: syntax highlighting for puppet manifests in emacs Package: puppet-testsuite Architecture: all -Depends: ${misc:Depends}, ruby | ruby-interpreter, puppet-common (= ${source:Version}), facter (>= 1.6.12), lsb-base, rails (>= 1.2.3-2), rdoc, ruby-ldap | libldap-ruby1.8, ruby-rspec | librspec-ruby, git-core, ruby-mocha | libmocha-ruby1.8 +Depends: ${misc:Depends}, ruby | ruby-interpreter, puppet-common (= ${source:Version}), facter (>= 1.7.0), lsb-base, rails (>= 1.2.3-2), rdoc, ruby-ldap | libldap-ruby1.8, ruby-rspec | librspec-ruby, git-core, ruby-mocha | libmocha-ruby1.8 Recommends: cron Description: Centralized configuration management - test suite This package provides all the tests from the upstream puppet source code. diff --git a/ext/ips/transforms b/ext/ips/transforms index cd49b2d2e..be91f74eb 100644 --- a/ext/ips/transforms +++ b/ext/ips/transforms @@ -27,7 +27,7 @@ add restart_fmri svc:/system/manifest-import:default> # we depend on facter - emit depend type=require fmri=application/facter@1.6.12> + emit depend type=require fmri=application/facter@1.7.0> # preserve the old conf file on upgrade. add overlay true> diff --git a/ext/redhat/puppet.spec.erb b/ext/redhat/puppet.spec.erb index eb48e811b..865613eae 100644 --- a/ext/redhat/puppet.spec.erb +++ b/ext/redhat/puppet.spec.erb @@ -36,7 +36,7 @@ Group: System Environment/Base BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) -BuildRequires: facter < 1:2.0 +BuildRequires: facter >= 1:1.7.0 # Puppet 3.x drops ruby 1.8.5 support and adds ruby 1.9 support BuildRequires: ruby >= 1.8.7 BuildArch: noarch @@ -53,7 +53,7 @@ Requires: rubygem-json %endif %endif -Requires: facter >= 1.6.11 +Requires: facter >= 1:1.7.0 # Puppet 3.x drops ruby 1.8.5 support and adds ruby 1.9 support # Ruby 1.8.7 available for el5 at: yum.puppetlabs.com/el/5/devel/$ARCH Requires: ruby >= 1.8.7 diff --git a/ext/suse/puppet.spec b/ext/suse/puppet.spec index 486d01ae2..a1a0f7934 100644 --- a/ext/suse/puppet.spec +++ b/ext/suse/puppet.spec @@ -14,7 +14,7 @@ Source0: http://puppetlabs.com/downloads/puppet/%{name}-%{version}.tar.gz PreReq: %{insserv_prereq} %{fillup_prereq} Requires: ruby >= 1.8.7 -Requires: facter >= 1.6.11 +Requires: facter >= 1:1.7.0 Requires: cron Requires: logrotate BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) From 5455b53530bd4d45ff56c72a70373d105722f1fe Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Mon, 3 Mar 2014 16:33:03 -0800 Subject: [PATCH 746/800] (PUP-1839) Flush cache when setting value The `set_value` method was deprecated and rewritten as part of the work to implement `puppet config set`. However the new implementation lost the call to `unsafe_clear_cache` which caused any set values to not show up if the setting's value had previously been read. Also, the method was still used by the `puppet device` code. This is a small fix to get `puppet device` working again. This flushes the cache and removes the deprecation. --- lib/puppet/settings.rb | 2 +- spec/unit/settings_spec.rb | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/puppet/settings.rb b/lib/puppet/settings.rb index 62645bdcf..def1f11f7 100644 --- a/lib/puppet/settings.rb +++ b/lib/puppet/settings.rb @@ -761,9 +761,9 @@ class Puppet::Settings end def set_value(param, value, type, options = {}) - Puppet.deprecation_warning("Puppet.settings.set_value is deprecated. Use Puppet[]= instead.") if @value_sets[type] @value_sets[type].set(param, value) + unsafe_flush_cache end end diff --git a/spec/unit/settings_spec.rb b/spec/unit/settings_spec.rb index 9f605621d..7e3c8c15f 100755 --- a/spec/unit/settings_spec.rb +++ b/spec/unit/settings_spec.rb @@ -473,6 +473,12 @@ describe Puppet::Settings do @settings[:why_so_serious] = "foo" }.should raise_error(ArgumentError, /unknown setting/) end + + it "allows overriding cli args based on the cli-set value" do + @settings.handlearg("--myval", "cliarg") + @settings.set_value(:myval, "modified #{@settings[:myval]}", :cli) + expect(@settings[:myval]).to eq("modified cliarg") + end end describe "when returning values" do From 7e836e5c0e4ce8ca52906c05f218c4a4f0c8957e Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Mon, 3 Mar 2014 17:11:14 -0800 Subject: [PATCH 747/800] (PUP-1839) Re-deprecate set_value The set_value method really is deprecated. --- lib/puppet/settings.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/puppet/settings.rb b/lib/puppet/settings.rb index def1f11f7..3cf0572dd 100644 --- a/lib/puppet/settings.rb +++ b/lib/puppet/settings.rb @@ -761,6 +761,7 @@ class Puppet::Settings end def set_value(param, value, type, options = {}) + Puppet.deprecation_warning("Puppet.settings.set_value is deprecated. Use Puppet[]= instead.") if @value_sets[type] @value_sets[type].set(param, value) unsafe_flush_cache From c967d63a999ea3655c002ef83e581ac5c48500d6 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Tue, 4 Mar 2014 11:18:50 -0800 Subject: [PATCH 748/800] (PUP-1839) Introduce overridden defaults The previous behavior of setting the value of a configuration setting was that the value only took effect if a user had not specified a value for that setting on the command line. This caused lots of confusion and some unsafe situations where the puppet code tries to override a setting in order to continue. This changes the precedence of setting values so that anything set in the code is higher precedence than the cli values. In order to allow overriding the defaults that are determined by hooks on settings, this also adds in an `override_default(setting_name, value)` method so that the hooks can create new defaults. --- lib/puppet/defaults.rb | 10 ++++----- lib/puppet/settings.rb | 15 ++++++++----- spec/unit/application/apply_spec.rb | 2 +- spec/unit/settings_spec.rb | 34 +++++++++-------------------- 4 files changed, 25 insertions(+), 36 deletions(-) diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index d18c77965..d45d21a03 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -364,11 +364,11 @@ module Puppet :hook => proc do |value| if value # This reconfigures the termini for Node, Facts, and Catalog - Puppet.settings[:storeconfigs] = true + Puppet.settings.override_default(:storeconfigs, true) # But then we modify the configuration Puppet::Resource::Catalog.indirection.cache_class = :queue - Puppet.settings[:catalog_cache_terminus] = :queue + Puppet.settings.override_default(:catalog_cache_terminus, :queue) else raise "Cannot disable asynchronous storeconfigs in a running process" end @@ -383,7 +383,7 @@ module Puppet `active_record` backend, but will disable external tools that search the storeconfigs database. Thinning catalogs is generally unnecessary when using PuppetDB to store catalogs.", :hook => proc do |value| - Puppet.settings[:storeconfigs] = true if value + Puppet.settings.override_default(:storeconfigs, true) if value end }, :config_version => { @@ -1226,7 +1226,7 @@ EOT :hook => proc { |value| if value Puppet.deprecation_warning "Setting 'catalog_format' is deprecated; use 'preferred_serialization_format' instead." - Puppet.settings[:preferred_serialization_format] = value + Puppet.settings.override_default(:preferred_serialization_format, value) end } }, @@ -1748,7 +1748,7 @@ EOT if value if not Puppet.settings[:async_storeconfigs] Puppet::Resource::Catalog.indirection.cache_class = :store_configs - Puppet.settings[:catalog_cache_terminus] = :store_configs + Puppet.settings.override_default(:catalog_cache_terminus, :store_configs) end Puppet::Node::Facts.indirection.cache_class = :store_configs diff --git a/lib/puppet/settings.rb b/lib/puppet/settings.rb index 3cf0572dd..416d66306 100644 --- a/lib/puppet/settings.rb +++ b/lib/puppet/settings.rb @@ -79,6 +79,7 @@ class Puppet::Settings :cli => Values.new(:cli, @config), :memory => Values.new(:memory, @config), :application_defaults => Values.new(:application_defaults, @config), + :overridden_defaults => Values.new(:overridden_defaults, @config), } @configuration_file = nil @@ -111,6 +112,11 @@ class Puppet::Settings unsafe_flush_cache end + def override_default(param, value) + @value_sets[:overridden_defaults].set(param, value) + unsafe_flush_cache + end + # Generate the list of valid arguments, in a format that GetoptLong can # understand, and add them to the passed option list. def addargs(options) @@ -161,6 +167,7 @@ class Puppet::Settings end @value_sets[:memory] = Values.new(:memory, @config) + @value_sets[:overridden_defaults] = Values.new(:overridden_defaults, @config) @cache.clear end @@ -708,11 +715,7 @@ class Puppet::Settings # The order in which to search for values. def searchpath(environment = nil) - if environment - [:cli, :memory, environment, :run_mode, :main, :application_defaults] - else - [:cli, :memory, :run_mode, :main, :application_defaults] - end + [:memory, :cli, environment, :run_mode, :main, :application_defaults, :overridden_defaults].compact end # Get a list of objects per section @@ -1050,7 +1053,7 @@ Generated on #{Time.now}. def value_sets_for(environment, mode) searchpath(environment).collect do |name| case name - when :cli, :memory, :application_defaults + when :cli, :memory, :application_defaults, :overridden_defaults @value_sets[name] when :run_mode if @configuration_file diff --git a/spec/unit/application/apply_spec.rb b/spec/unit/application/apply_spec.rb index 382436837..39a5ad59c 100755 --- a/spec/unit/application/apply_spec.rb +++ b/spec/unit/application/apply_spec.rb @@ -74,7 +74,7 @@ describe Puppet::Application::Apply do @apply.options[:verbose].should == true end it "should set options[:show_diff] to true" do - Puppet[:show_diff] = false + Puppet.settings.override_default(:show_diff, false) @apply.setup_test Puppet[:show_diff].should == true end diff --git a/spec/unit/settings_spec.rb b/spec/unit/settings_spec.rb index 7e3c8c15f..df4784a59 100755 --- a/spec/unit/settings_spec.rb +++ b/spec/unit/settings_spec.rb @@ -205,48 +205,40 @@ describe Puppet::Settings do end it "should support a getopt-specific mechanism for turning booleans off" do - @settings[:bool] = true + @settings.override_default(:bool, true) @settings.handlearg("--no-bool", "") @settings[:bool].should == false end it "should support a getopt-specific mechanism for turning booleans on" do # Turn it off first - @settings[:bool] = false + @settings.override_default(:bool, false) @settings.handlearg("--bool", "") @settings[:bool].should == true end it "should consider a cli setting with no argument to be a boolean" do # Turn it off first - @settings[:bool] = false + @settings.override_default(:bool, false) @settings.handlearg("--bool") @settings[:bool].should == true end - it "should consider a cli setting with an empty string as an argument to be a boolean, if the setting itself is a boolean" do - # Turn it off first - @settings[:bool] = false - @settings.handlearg("--bool", "") - @settings[:bool].should == true - end - it "should consider a cli setting with an empty string as an argument to be an empty argument, if the setting itself is not a boolean" do - @settings[:myval] = "bob" + @settings.override_default(:myval, "bob") @settings.handlearg("--myval", "") @settings[:myval].should == "" end it "should consider a cli setting with a boolean as an argument to be a boolean" do # Turn it off first - @settings[:bool] = false + @settings.override_default(:bool, false) @settings.handlearg("--bool", "true") @settings[:bool].should == true end it "should not consider a cli setting of a non boolean with a boolean as an argument to be a boolean" do - # Turn it off first - @settings[:myval] = "bob" + @settings.override_default(:myval, "bob") @settings.handlearg("--no-myval", "") @settings[:myval].should == "" end @@ -257,7 +249,6 @@ describe Puppet::Settings do end it "should flag bool settings from the CLI" do - @settings[:bool] = false @settings.handlearg("--bool") @settings.set_by_cli?(:bool).should be_true end @@ -457,10 +448,11 @@ describe Puppet::Settings do @settings[:bool].should == false end - it "should prefer cli values to values set in Ruby code" do - @settings.handlearg("--myval", "cliarg") + it "should prefer values set in ruby to values set on the cli" do @settings[:myval] = "memarg" - @settings[:myval].should == "cliarg" + @settings.handlearg("--myval", "cliarg") + + @settings[:myval].should == "memarg" end it "should clear the list of environments" do @@ -619,12 +611,6 @@ describe Puppet::Settings do @settings[:one].should == "clival" end - it "should return values set on the cli before values set in Ruby" do - @settings[:one] = "rubyval" - @settings.handlearg("--one", "clival") - @settings[:one].should == "clival" - end - it "should return values set in the mode-specific section before values set in the main section" do text = "[main]\none = mainval\n[agent]\none = modeval\n" @settings.stubs(:read_file).returns(text) From 9088d347dfa98625ca9f87b611ecadbeb2f6143f Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Thu, 27 Feb 2014 10:17:58 -0800 Subject: [PATCH 749/800] (PUP-1765) Directory env respects cmdline modulepath and manifest The addition of directory environments in PUP-1118 caused a regression in the behavior of --modulepath and --manifest set from the commandline. Three cases are seen: 1) given a '$confdir/environments/production' directory, `puppet apply -e 'include "modclass"' --modulepath /some/other/modpath` fails to find the modclass from /some/other/modpath. The same problem is seen if a specific directory environment such as '--environment direnv' is set (assuming that direnv exists). 2) given a '$confdir/environments/production' directory, `puppet module install foo --modulepath /some/other/modpath` installs to '$confdir/environments/production'. If a specific directory environments such as '--environment direnv' is set, then it incorrectly installs to "$confdir/environments/direnv'. 3) given a '$confdir/environments/production' directory, `puppet master --manifest /some/other.pp` does not execute '/some/other.pp' when an agent calls in without specifying an environment. To fix these cases we are now explicitly overriding the configured environment with the stored :cli modulepath and/or manifest setting, both at application run time and when handling a v1 http api call. --- .../cmdline_overrides_environment.rb | 196 ++++++++++++++++++ .../tests/modules/install/with_environment.rb | 37 +++- lib/puppet/application.rb | 5 +- lib/puppet/application/apply.rb | 2 +- lib/puppet/network/http/api/v1.rb | 4 +- lib/puppet/node/environment.rb | 19 ++ lib/puppet/util/autoload.rb | 12 +- spec/integration/application/apply_spec.rb | 53 ++++- spec/unit/application/apply_spec.rb | 7 + 9 files changed, 322 insertions(+), 13 deletions(-) create mode 100644 acceptance/tests/environment/cmdline_overrides_environment.rb diff --git a/acceptance/tests/environment/cmdline_overrides_environment.rb b/acceptance/tests/environment/cmdline_overrides_environment.rb new file mode 100644 index 000000000..42986b9a8 --- /dev/null +++ b/acceptance/tests/environment/cmdline_overrides_environment.rb @@ -0,0 +1,196 @@ +test_name "Commandline modulepath and manifest settings override environment" + +testdir = master.tmpdir('cmdline_and_environment') +environmentpath = "#{testdir}/environments" +modulepath = "#{testdir}/modules" +manifests = "#{testdir}/manifests" +sitepp = "#{manifests}/site.pp" +other_manifestdir = "#{testdir}/other_manifests" +other_sitepp = "#{other_manifestdir}/site.pp" +other_modulepath = "#{testdir}/some_other_modulepath" +cmdline_manifest = "#{testdir}/cmdline.pp" + +step "Prepare manifests and modules" +apply_manifest_on(master, <<-MANIFEST, :catch_failures => true) +File { + ensure => directory, + owner => puppet, + mode => 0700, +} + +############################################## +# The default production directory environment +file { + "#{testdir}":; + "#{environmentpath}":; + "#{environmentpath}/production":; + "#{environmentpath}/production/manifests":; + "#{environmentpath}/production/modules":; + "#{environmentpath}/production/modules/amod":; + "#{environmentpath}/production/modules/amod/manifests":; +} + +file { "#{environmentpath}/production/modules/amod/manifests/init.pp": + ensure => file, + content => 'class amod { + notify { "amod from production environment": } + }' +} + +file { "#{environmentpath}/production/manifests/production.pp": + ensure => file, + content => ' + notify { "in production.pp": } + include amod + ' +} + +############################################################## +# To be set as default manifests and modulepath in puppet.conf +file { + "#{modulepath}":; + "#{modulepath}/amod/":; + "#{modulepath}/amod/manifests":; +} + +file { "#{modulepath}/amod/manifests/init.pp": + ensure => file, + content => 'class amod { + notify { "amod from modulepath": } + }' +} + +file { "#{manifests}": } +file { "#{sitepp}": + ensure => file, + content => ' + notify { "in site.pp": } + include amod + ' +} + +file { "#{other_manifestdir}": } +file { "#{other_sitepp}": + ensure => file, + content => ' + notify { "in other manifestdir site.pp": } + include amod + ' +} + +################################ +# To be specified on commandline +file { + "#{other_modulepath}":; + "#{other_modulepath}/amod/":; + "#{other_modulepath}/amod/manifests":; +} + +file { "#{other_modulepath}/amod/manifests/init.pp": + ensure => file, + content => 'class amod { + notify { "amod from commandline modulepath": } + }' +} + +file { "#{cmdline_manifest}": + ensure => file, + content => ' + notify { "in cmdline.pp": } + include amod + ' +} +MANIFEST + +master_opts = { + 'master' => { + 'environmentpath' => environmentpath, + 'manifest' => sitepp, + 'modulepath' => modulepath, + } +} + +# Note: this is the semantics seen with legacy environments if commandline +# manifest/modulepath are set. +step "puppet master with --manifest and --modulepath overrides existing default production directory environment" do + master_opts = master_opts.merge(:__commandline_args__ => "--manifest=#{cmdline_manifest} --modulepath=#{other_modulepath}") + with_puppet_running_on master, master_opts, testdir do + agents.each do |agent| + on(agent, puppet("agent -t --server #{master}"), :acceptable_exit_codes => [2] ) do + assert_match(/in cmdline\.pp/, stdout) + assert_match(/amod from commandline modulepath/, stdout) + assert_not_match(/production/, stdout) + end + + step "even if environment is specified" + on(agent, puppet("agent -t --server #{master} --environment production"), :acceptable_exit_codes => [2]) do + assert_match(/in cmdline\.pp/, stdout) + assert_match(/amod from commandline modulepath/, stdout) + assert_not_match(/production/, stdout) + end + end + end + + step "or if you set --manifestdir" do + master_opts = master_opts.merge(:__commandline_args__ => "--manifestdir=#{other_manifestdir} --modulepath=#{other_modulepath}") + step "it is ignored if manifest is set in puppet.conf to something not using $manifestdir" + with_puppet_running_on master, master_opts, testdir do + agents.each do |agent| + on(agent, puppet("agent -t --server #{master}"), :acceptable_exit_codes => [2]) do + assert_match(/in production\.pp/, stdout) + assert_match(/amod from commandline modulepath/, stdout) + end + end + end + + step "but does pull in the default manifest via manifestdir if manifest is not set" + master_opts = master_opts.merge(:__commandline_args__ => "--manifestdir=#{other_manifestdir} --modulepath=#{other_modulepath}") + master_opts['master'].delete('manifest') + with_puppet_running_on master, master_opts, testdir do + agents.each do |agent| + on(agent, puppet("agent -t --server #{master}"), :acceptable_exit_codes => [2]) do + assert_match(/in other manifestdir site\.pp/, stdout) + assert_match(/amod from commandline modulepath/, stdout) + assert_not_match(/production/, stdout) + end + end + end + end +end + +step "puppet master with manifest and modulepath set in puppet.conf is overriden by an existing default production directory" do + with_puppet_running_on master, master_opts, testdir do + agents.each do |agent| + step "this case is unfortunate, but will be irrelevant when we remove legacyenv in 4.0" + on(agent, puppet("agent -t --server #{master}"), :acceptable_exit_codes => [2] ) do + assert_match(/in production\.pp/, stdout) + assert_match(/amod from production environment/, stdout) + end + + step "if environment is specified" + on(agent, puppet("agent -t --server #{master} --environment production"), :acceptable_exit_codes => [2]) do + assert_match(/in production\.pp/, stdout) + assert_match(/amod from production environment/, stdout) + end + end + end +end + +step "puppet master with default manifest, modulepath, environment, environmentpath and an existing default production directory environment directory" do + master_opts = { + :__commandline_args__ => "--confdir=#{testdir} --ssldir=#{master[:puppetpath]}/ssl" + } + with_puppet_running_on master, master_opts, testdir do + agents.each do |agent| + step "default production directory environment takes precedence" + on(agent, puppet("agent -t --server #{master}"), :acceptable_exit_codes => [2] ) do + assert_match(/in production\.pp/, stdout) + assert_match(/amod from production environment/, stdout) + end + on(agent, puppet("agent -t --server #{master} --environment production"), :acceptable_exit_codes => [2]) do + assert_match(/in production\.pp/, stdout) + assert_match(/amod from production environment/, stdout) + end + end + end +end diff --git a/acceptance/tests/modules/install/with_environment.rb b/acceptance/tests/modules/install/with_environment.rb index cb0e4388e..d9200d138 100644 --- a/acceptance/tests/modules/install/with_environment.rb +++ b/acceptance/tests/modules/install/with_environment.rb @@ -16,8 +16,8 @@ stub_forge_on(master) puppet_conf = generate_base_legacy_and_directory_environments(master['puppetpath']) -check_module_install_in = lambda do |environment, environment_path| - on master, "puppet module install #{module_author}-#{module_name} --config=#{puppet_conf} --environment=#{environment}" do +check_module_install_in = lambda do |environment_path, module_install_args| + on master, "puppet module install #{module_author}-#{module_name} --config=#{puppet_conf} #{module_install_args}" do assert_module_installed_ui(stdout, module_author, module_name) assert_match(/#{environment_path}/, stdout, "Notice of non default install path was not displayed") @@ -26,9 +26,38 @@ check_module_install_in = lambda do |environment, environment_path| end step 'Install a module into a non default legacy environment' do - check_module_install_in.call('legacyenv', "#{master['puppetpath']}/legacyenv/modules") + check_module_install_in.call("#{master['puppetpath']}/legacyenv/modules", + "--environment=legacyenv") end step 'Install a module into a non default directory environment' do - check_module_install_in.call('direnv', "#{master['puppetpath']}/environments/direnv/modules") + check_module_install_in.call("#{master['puppetpath']}/environments/direnv/modules", + "--environment=direnv") +end + +step 'Prepare a separate modulepath' +modulepath_dir = master.tmpdir("modulepath") +apply_manifest_on(master, <<-MANIFEST , :catch_failures => true) + file { + [ + '#{master['puppetpath']}/environments/production', + '#{modulepath_dir}', + ]: + + ensure => directory, + owner => puppet, + } +MANIFEST + +step "Install a module into --modulepath #{modulepath_dir} despite the implicit production directory env existing" do + check_module_install_in.call(modulepath_dir, "--modulepath=#{modulepath_dir}") +end + +step "Uninstall so we can try a different scenario" do + on master, "puppet module uninstall #{module_author}-#{module_name} --config=#{puppet_conf} --modulepath=#{modulepath_dir}" +end + +step "Install a module into --modulepath #{modulepath_dir} with a directory env specified" do + check_module_install_in.call(modulepath_dir, + "--modulepath=#{modulepath_dir} --environment=direnv") end diff --git a/lib/puppet/application.rb b/lib/puppet/application.rb index 3649f0841..602f27a21 100644 --- a/lib/puppet/application.rb +++ b/lib/puppet/application.rb @@ -354,7 +354,10 @@ class Application end new_context = Puppet.base_context(Puppet.settings) - new_context[:current_environment] = new_context[:environments].get(Puppet[:environment]) + configured_environment = new_context[:environments].get(Puppet[:environment]) + configured_environment = configured_environment.override_from_commandline(Puppet.settings) + new_context[:current_environment] = configured_environment + # Setup a new context using the app's configuration Puppet.override(new_context, "New base context and current environment from application's configuration") do diff --git a/lib/puppet/application/apply.rb b/lib/puppet/application/apply.rb index 78348f7d0..e42c32483 100644 --- a/lib/puppet/application/apply.rb +++ b/lib/puppet/application/apply.rb @@ -182,7 +182,7 @@ Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License facts.name = Puppet[:node_name_value] end - configured_environment = Puppet.lookup(:environments).get(Puppet[:environment]) + configured_environment = Puppet.lookup(:current_environment) apply_environment = manifest ? configured_environment.override_with(:manifest => manifest) : configured_environment diff --git a/lib/puppet/network/http/api/v1.rb b/lib/puppet/network/http/api/v1.rb index 79a7ddcbe..6bbdd87c3 100644 --- a/lib/puppet/network/http/api/v1.rb +++ b/lib/puppet/network/http/api/v1.rb @@ -63,7 +63,9 @@ class Puppet::Network::HTTP::API::V1 method = indirection_method(http_method, indirection) - params[:environment] = Puppet.lookup(:environments).get(environment) + configured_environment = Puppet.lookup(:environments).get(environment) + configured_environment = configured_environment.override_from_commandline(Puppet.settings) + params[:environment] = configured_environment params.delete(:bucket_path) diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb index 7548d4a29..2a18370f9 100644 --- a/lib/puppet/node/environment.rb +++ b/lib/puppet/node/environment.rb @@ -110,6 +110,25 @@ class Puppet::Node::Environment env_params[:manifest] || manifest) end + # Creates a new Puppet::Node::Environment instance, overriding manfiest + # and modulepath from the passed settings if they were originally set from + # the commandline, or returns self if there is nothing to override. + # + # @param settings [Puppet::Settings] an initialized puppet settings instance + # @return [Puppet::Node::Environment] new overridden environment or self if + # there are no commandline changes from settings. + def override_from_commandline(settings) + overrides = {} + overrides[:modulepath] = [settings[:modulepath]] if settings.set_by_cli?(:modulepath) + if settings.set_by_cli?(:manifest) || + (settings.set_by_cli?(:manifestdir) && settings[:manifest].start_with?(settings[:manifestdir])) + overrides[:manifest] = settings[:manifest] + end + overrides.empty? ? + self : + self.override_with(overrides) + end + # Retrieve the environment for the current process. # # @note This should only used when a catalog is being compiled. diff --git a/lib/puppet/util/autoload.rb b/lib/puppet/util/autoload.rb index c0429599b..f8f7c260c 100644 --- a/lib/puppet/util/autoload.rb +++ b/lib/puppet/util/autoload.rb @@ -195,8 +195,16 @@ class Puppet::Util::Autoload self.class.load_file(expand(name), env) end - # Load all instances that we can. This uses require, rather than load, - # so that already-loaded files don't get reloaded unnecessarily. + # Load all instances from a path of Autoload.search_directories matching the + # relative path this Autoloader was initialized with. For example, if we + # have created a Puppet::Util::Autoload for Puppet::Type::User with a path of + # 'puppet/provider/user', the search_directories path will be searched for + # all ruby files matching puppet/provider/user/*.rb and they will then be + # loaded from the first directory in the search path providing them. So + # earlier entries in the search path may shadow later entries. + # + # This uses require, rather than load, so that already-loaded files don't get + # reloaded unnecessarily. def loadall self.class.loadall(@path) end diff --git a/spec/integration/application/apply_spec.rb b/spec/integration/application/apply_spec.rb index 5ef732c48..f464c80ac 100755 --- a/spec/integration/application/apply_spec.rb +++ b/spec/integration/application/apply_spec.rb @@ -39,10 +39,8 @@ describe "apply" do EOF end - env_loader = Puppet::Environments::Static.new( - Puppet::Node::Environment.create(:special, [], '') - ) - Puppet.override(:environments => env_loader) do + special = Puppet::Node::Environment.create(:special, [], '') + Puppet.override(:current_environment => special) do Puppet[:environment] = 'special' puppet = Puppet::Application[:apply] puppet.stubs(:command_line).returns(stub('command_line', :args => [manifest])) @@ -51,4 +49,51 @@ describe "apply" do expect(@logs.map(&:to_s)).to include('it was applied') end + + context "with a module" do + let(:modulepath) { tmpdir('modulepath') } + let(:execute) { 'include amod' } + let(:args) { ['-e', execute, '--modulepath', modulepath] } + + before(:each) do + Puppet::FileSystem.mkpath("#{modulepath}/amod/manifests") + File.open("#{modulepath}/amod/manifests/init.pp", "w") do |f| + f.puts <<-EOF + class amod{ + notice('amod class included') + } + EOF + end + create_default_directory_environment + end + + def create_default_directory_environment + Puppet::FileSystem.mkpath("#{Puppet[:environmentpath]}/#{Puppet[:environment]}") + end + + def init_cli_args_and_apply_app(args, execute) + Puppet.initialize_settings(args) + puppet = Puppet::Application.find(:apply).new(stub('command_line', :subcommand_name => :apply, :args => args)) + puppet.options[:code] = execute + return puppet + end + + it "looks in --modulepath even when the default directory environment exists" do + apply = init_cli_args_and_apply_app(args, execute) + + expect do + expect { apply.run }.to exit_with(0) + end.to have_printed('amod class included') + end + + it "looks in --modulepath even when given a specific directory --environment" do + args << '--environment' << 'production' + apply = init_cli_args_and_apply_app(args, execute) + + expect do + expect { apply.run }.to exit_with(0) + end.to have_printed('amod class included') + end + end + end diff --git a/spec/unit/application/apply_spec.rb b/spec/unit/application/apply_spec.rb index 382436837..f6f010858 100755 --- a/spec/unit/application/apply_spec.rb +++ b/spec/unit/application/apply_spec.rb @@ -199,6 +199,13 @@ describe Puppet::Application::Apply do Puppet::Node::Facts.indirection.cache_class = nil end + around :each do |example| + Puppet.override(:current_environment => + Puppet::Node::Environment.create(:production, [], '')) do + example.run + end + end + it "should set the code to run from --code" do @apply.options[:code] = "code to run" Puppet.expects(:[]=).with(:code,"code to run") From 674bee94bd1b08342067094276c51ad025c10e53 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Tue, 4 Mar 2014 14:06:15 -0800 Subject: [PATCH 750/800] (maint) Match current spec style and usage --- spec/unit/parser/functions/defined_spec.rb | 33 +++++++++++----------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/spec/unit/parser/functions/defined_spec.rb b/spec/unit/parser/functions/defined_spec.rb index b8659063f..d0620797c 100755 --- a/spec/unit/parser/functions/defined_spec.rb +++ b/spec/unit/parser/functions/defined_spec.rb @@ -11,41 +11,40 @@ describe "the 'defined' function" do @scope = Puppet::Parser::Scope.new(@compiler) end - it "should exist" do - Puppet::Parser::Functions.function("defined").should == "function_defined" + it "exists" do + expect(Puppet::Parser::Functions.function("defined")).to be_eql("function_defined") end - it "should be true when the name is defined as a class" do + it "is true when the name is defined as a class" do @scope.known_resource_types.add Puppet::Resource::Type.new(:hostclass, "yayness") - @scope.function_defined(["yayness"]).should be_true + expect(@scope.function_defined(["yayness"])).to be_true end - it "should be true when the name is defined as a definition" do + it "is true when the name is defined as a definition" do @scope.known_resource_types.add Puppet::Resource::Type.new(:definition, "yayness") - @scope.function_defined(["yayness"]).should be_true + expect(@scope.function_defined(["yayness"])).to be_true end - it "should be true when the name is defined as a builtin type" do - @scope.function_defined(["file"]).should be_true + it "is true when the name is defined as a builtin type" do + expect(@scope.function_defined(["file"])).to be_true end - - it "should be true when any of the provided names are defined" do + it "is true when any of the provided names are defined" do @scope.known_resource_types.add Puppet::Resource::Type.new(:definition, "yayness") - @scope.function_defined(["meh", "yayness", "booness"]).should be_true + expect(@scope.function_defined(["meh", "yayness", "booness"])).to be_true end - it "should be false when a single given name is not defined" do - @scope.function_defined(["meh"]).should be_false + it "is false when a single given name is not defined" do + expect(@scope.function_defined(["meh"])).to be_false end - it "should be false when none of the names are defined" do - @scope.function_defined(["meh", "yayness", "booness"]).should be_false + it "is false when none of the names are defined" do + expect(@scope.function_defined(["meh", "yayness", "booness"])).to be_false end - it "should be true when a resource reference is provided and the resource is in the catalog" do + it "is true when a resource reference is provided and the resource is in the catalog" do resource = Puppet::Resource.new("file", "/my/file") @compiler.add_resource(@scope, resource) - @scope.function_defined([resource]).should be_true + expect(@scope.function_defined([resource])).to be_true end end From c40941dfaea3fe313e50dccae7bad0a5d6819fb8 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Wed, 5 Mar 2014 08:55:33 -0800 Subject: [PATCH 751/800] (PUP-1765) Change assert_not_match to assert_no_match I believe this is a minitest vrs test/unit ruby version issue. 'assert_no_match' is the variant available on all platforms that we use throughout the suite. --- .../tests/environment/cmdline_overrides_environment.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/acceptance/tests/environment/cmdline_overrides_environment.rb b/acceptance/tests/environment/cmdline_overrides_environment.rb index 42986b9a8..d2d8949c9 100644 --- a/acceptance/tests/environment/cmdline_overrides_environment.rb +++ b/acceptance/tests/environment/cmdline_overrides_environment.rb @@ -119,14 +119,14 @@ step "puppet master with --manifest and --modulepath overrides existing default on(agent, puppet("agent -t --server #{master}"), :acceptable_exit_codes => [2] ) do assert_match(/in cmdline\.pp/, stdout) assert_match(/amod from commandline modulepath/, stdout) - assert_not_match(/production/, stdout) + assert_no_match(/production/, stdout) end step "even if environment is specified" on(agent, puppet("agent -t --server #{master} --environment production"), :acceptable_exit_codes => [2]) do assert_match(/in cmdline\.pp/, stdout) assert_match(/amod from commandline modulepath/, stdout) - assert_not_match(/production/, stdout) + assert_no_match(/production/, stdout) end end end @@ -151,7 +151,7 @@ step "puppet master with --manifest and --modulepath overrides existing default on(agent, puppet("agent -t --server #{master}"), :acceptable_exit_codes => [2]) do assert_match(/in other manifestdir site\.pp/, stdout) assert_match(/amod from commandline modulepath/, stdout) - assert_not_match(/production/, stdout) + assert_no_match(/production/, stdout) end end end From c977045ff703266f745f0199a4a6d7dae0d99e94 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Tue, 4 Mar 2014 16:56:02 -0800 Subject: [PATCH 752/800] (PUP-1372) Add variable and pops type checks to defined function The original Puppet::Parser::Functions defined() checked whether a class or resource type had been defined, or a particular resource reference declared. We've extended this to allow for checking variable assignment, by looking up a string reference for the variable name. This version can also look up defined classes, types and resources by type reference if the future parser is in effect. Paired with Henrik Lindberg --- lib/puppet/parser/functions/defined.rb | 52 +++++++++++++----- spec/unit/parser/functions/defined_spec.rb | 64 ++++++++++++++++++++++ 2 files changed, 102 insertions(+), 14 deletions(-) diff --git a/lib/puppet/parser/functions/defined.rb b/lib/puppet/parser/functions/defined.rb index 63fbdcc31..a1484fcad 100644 --- a/lib/puppet/parser/functions/defined.rb +++ b/lib/puppet/parser/functions/defined.rb @@ -1,8 +1,11 @@ # Test whether a given class or definition is defined Puppet::Parser::Functions::newfunction(:defined, :type => :rvalue, :arity => -2, :doc => "Determine whether a given class or resource type is defined. This function can also determine whether a - specific resource has been declared. Returns true or false. Accepts class names, - type names, and resource references. + specific resource has been declared, or whether a variable has been assigned a value + (including undef...as opposed to never having been assigned anything). Returns true + or false. Accepts class names, type names, resource references, and variable + reference strings of the form '$name'. When more than one argument is + supplied, defined() returns true if any are defined. The `defined` function checks both native and defined types, including types provided as plugins via modules. Types and classes are both checked using their names: @@ -11,6 +14,7 @@ Puppet::Parser::Functions::newfunction(:defined, :type => :rvalue, :arity => -2, defined(\"customtype\") defined(\"foo\") defined(\"foo::bar\") + defined(\'$name\') Resource declarations are checked using resource references, e.g. `defined( File['/tmp/myfile'] )`. Checking whether a given resource @@ -26,24 +30,44 @@ Puppet::Parser::Functions::newfunction(:defined, :type => :rvalue, :arity => -2, However, this order requirement refers to parse order only, and ordering of resources in the configuration graph (e.g. with `before` or `require`) does not - affect the behavior of `defined`.") do |vals| - result = false + affect the behavior of `defined`. + + If the future parser is in effect, you may also search using types: + + defined(Resource[\'file\',\'/some/file\']) + defined(File[\'/some/file\']) + defined(Class[\'foo\']) + + - Since 2.7.0 + - Since 3.6.0 variable reference and future parser types") do |vals| vals = [vals] unless vals.is_a?(Array) - vals.each do |val| + vals.any? do |val| case val when String - if Puppet::Type.type(val) or find_definition(val) or find_hostclass(val) - result = true - break + if m = /^\$(.+)$/.match(val) + exist?(m[1]) + else + find_resource_type(val) or find_definition(val) or find_hostclass(val) end when Puppet::Resource - if findresource(val.to_s) - result = true - break - end + compiler.findresource(val.type, val.title) else - raise ArgumentError, "Invalid argument of type '#{val.class}' to 'defined'" + if Puppet[:parser] == 'future' + case val + when Puppet::Pops::Types::PResourceType + raise ArgumentError, "The given resource type is a reference to all kind of types" if val.type_name.nil? + if val.title.nil? + find_builtin_resource_type(val.type_name) || find_definition(val.type_name) + else + compiler.findresource(val.type_name, val.title) + end + when Puppet::Pops::Types::PHostClassType + raise ArgumentError, "The given class type is a reference to all classes" if val.class_name.nil? + find_hostclass(val.class_name) + end + else + raise ArgumentError, "Invalid argument of type '#{val.class}' to 'defined'" + end end end - result end diff --git a/spec/unit/parser/functions/defined_spec.rb b/spec/unit/parser/functions/defined_spec.rb index d0620797c..edbf29863 100755 --- a/spec/unit/parser/functions/defined_spec.rb +++ b/spec/unit/parser/functions/defined_spec.rb @@ -1,5 +1,6 @@ #! /usr/bin/env ruby require 'spec_helper' +require 'puppet/pops' describe "the 'defined' function" do before :all do @@ -47,4 +48,67 @@ describe "the 'defined' function" do @compiler.add_resource(@scope, resource) expect(@scope.function_defined([resource])).to be_true end + + context "with string variable references" do + it "is true when variable exists in scope" do + @scope['x'] = 'something' + expect(@scope.function_defined(['$x'])).to be_true + end + + it "is true when at least one variable exists in scope" do + @scope['x'] = 'something' + expect(@scope.function_defined(['$y', '$x', '$z'])).to be_true + end + + it "is false when variable does not exist in scope" do + expect(@scope.function_defined(['$x'])).to be_false + end + end + + context "with future parser" do + before(:each) do + Puppet[:parser] = 'future' + end + + it "is true when a future resource type reference is provided, and the resource is in the catalog" do + resource = Puppet::Resource.new("file", "/my/file") + @compiler.add_resource(@scope, resource) + + resource_type = Puppet::Pops::Types::TypeFactory.resource('file', '/my/file') + expect(@scope.function_defined([resource_type])).to be_true + end + + it "raises an argument error if you ask if Resource is defined" do + resource_type = Puppet::Pops::Types::TypeFactory.resource + expect { @scope.function_defined([resource_type]) }.to raise_error(ArgumentError, /reference to all.*type/) + end + + it "is true if referencing a built in type" do + resource_type = Puppet::Pops::Types::TypeFactory.resource('file') + expect(@scope.function_defined([resource_type])).to be_true + end + + it "is true if referencing a defined type" do + @scope.known_resource_types.add Puppet::Resource::Type.new(:definition, "yayness") + resource_type = Puppet::Pops::Types::TypeFactory.resource('yayness') + expect(@scope.function_defined([resource_type])).to be_true + end + + it "is false if referencing an undefined type" do + resource_type = Puppet::Pops::Types::TypeFactory.resource('barbershops') + expect(@scope.function_defined([resource_type])).to be_false + end + + it "is true when a future class reference type is provided" do + @scope.known_resource_types.add Puppet::Resource::Type.new(:hostclass, "cowabunga") + + class_type = Puppet::Pops::Types::TypeFactory.host_class("cowabunga") + expect(@scope.function_defined([class_type])).to be_true + end + + it "raises an argument error if you ask if Class is defined" do + class_type = Puppet::Pops::Types::TypeFactory.host_class + expect { @scope.function_defined([class_type]) }.to raise_error(ArgumentError, /reference to all.*class/) + end + end end From 5d49d37445d8f60444e8046565e0c0fb1dd3b1fb Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Wed, 5 Mar 2014 11:47:32 -0800 Subject: [PATCH 753/800] (PUP-1765) Use master ssldir setting with default environment test Acceptance git/packages setup differ in the ssldir location. By setting to what has been configured on master, we can shift confdir but retain the previous ssl certs for the final test without worrying about the difference in ssldir location. --- acceptance/tests/environment/cmdline_overrides_environment.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/acceptance/tests/environment/cmdline_overrides_environment.rb b/acceptance/tests/environment/cmdline_overrides_environment.rb index d2d8949c9..1518431b8 100644 --- a/acceptance/tests/environment/cmdline_overrides_environment.rb +++ b/acceptance/tests/environment/cmdline_overrides_environment.rb @@ -177,8 +177,9 @@ step "puppet master with manifest and modulepath set in puppet.conf is overriden end step "puppet master with default manifest, modulepath, environment, environmentpath and an existing default production directory environment directory" do + ssldir = on(master, puppet("master --configprint ssldir")).stdout.chomp master_opts = { - :__commandline_args__ => "--confdir=#{testdir} --ssldir=#{master[:puppetpath]}/ssl" + :__commandline_args__ => "--confdir=#{testdir} --ssldir=#{ssldir}" } with_puppet_running_on master, master_opts, testdir do agents.each do |agent| From c00e883e8bff30e1cd11ce8a649736623063fc41 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Mon, 3 Feb 2014 14:36:14 -0800 Subject: [PATCH 754/800] (maint) Release hosts to the vcloud pooling api Our configurations now uses Scott's vcloud pooling api, so rather than attempt to destroy them directly through vsphere (the code for which was no longer working with current rbvmomi anyway) we are submitting them to the pooling api for deletion. --- acceptance/Rakefile | 50 +++++++++++++++------------------------------ 1 file changed, 16 insertions(+), 34 deletions(-) diff --git a/acceptance/Rakefile b/acceptance/Rakefile index 073628005..c8742d220 100644 --- a/acceptance/Rakefile +++ b/acceptance/Rakefile @@ -166,41 +166,19 @@ def list_preserved_hosts(secs_ago = ONE_DAY_IN_SECS) hosts end -# Plagiarized from Beaker::Vcloud#cleanup -def destroy_preserved_hosts(hosts = nil, secs_ago = ONE_DAY_IN_SECS) +def release_hosts(hosts = nil, secs_ago = ONE_DAY_IN_SECS) secs_ago ||= ONE_DAY_IN_SECS hosts ||= list_preserved_hosts(secs_ago) - require 'beaker/hypervisor/vsphere_helper' - vsphere_credentials = VsphereHelper.load_config("#{ENV['HOME']}/.fog") - - puts "Connecting to vSphere at #{vsphere_credentials[:server]}" + - " with credentials for #{vsphere_credentials[:user]}" - - vsphere_helper = VsphereHelper.new( vsphere_credentials ) - - vm_names = hosts.to_a - pp vm_names - vms = vsphere_helper.find_vms vm_names - vm_names.each do |name| - unless vm = vms[name] - puts "Couldn't find VM #{name} in vSphere!" - next - end - - if vm.runtime.powerState == 'poweredOn' - puts "Shutting down #{vm.name}" - start = Time.now - vm.PowerOffVM_Task.wait_for_completion - puts "Spent %.2f seconds halting #{vm.name}" % (Time.now - start) - end - - start = Time.now - vm.Destroy_Task - puts "Spent %.2f seconds destroying #{vm.name}" % (Time.now - start) - end - - vsphere_helper.close + require 'beaker' + vcloud_pooled = Beaker::VcloudPooled.new(hosts.map { |h| { 'vmhostname' => h } }, + :logger => Beaker::Logger.new, + :dot_fog => "#{ENV['HOME']}/.fog", + 'pooling_api' => 'http://vcloud.delivery.puppetlabs.net' , + 'datastore' => 'not-used', + 'resourcepool' => 'not-used', + 'folder' => 'not-used') + vcloud_pooled.cleanup end def print_preserved(preserved) @@ -283,10 +261,14 @@ Shutdown and destroy any hosts that we have preserved for testing. These should Specify a list of comma separated HOST_NAMES if you have a set of dynamic vcloud host names you want to purge outside of what can be grepped from the logs. You can go back through the last SECS_AGO logs. Default is one day ago in secs. EOS - task :destroy_preserved_hosts do + task :release_hosts do host_names = ENV['HOST_NAMES'].split(',') if ENV['HOST_NAMES'] secs_ago = ENV['SECS_AGO'] - destroy_preserved_hosts(host_names, secs_ago) + release_hosts(host_names, secs_ago) + end + + task :destroy_preserved_hosts => 'ci:release_hosts' do + puts "Note: we are now releasing hosts back to the vcloud pooling api rather than destroying them directly. The rake task for this is ci:release_hosts" end desc <<-EOS From a0ae6960c7b2bf5b818892c01aa45c7687412a99 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Fri, 7 Feb 2014 11:35:10 -0800 Subject: [PATCH 755/800] (maint) Remove unused rsync for preserved acceptance test reruns Package re-installation and rsyncing of local code changes while re-running acceptance tests on preserved vms is something we aren't doing. A better solution for the cycle of acceptance testing against local changes is to use a git install against a local git daemon (see the docs/acceptance_tests.md) to test against changes you have checked in locally. --- acceptance/Rakefile | 6 +---- .../rsync/pre-suite/000_PurgeAndReinstall.rb | 10 -------- .../setup/rsync/pre-suite/010_RsyncSource.rb | 25 ------------------- docs/acceptance_tests.md | 12 ++------- 4 files changed, 3 insertions(+), 50 deletions(-) delete mode 100644 acceptance/setup/rsync/pre-suite/000_PurgeAndReinstall.rb delete mode 100644 acceptance/setup/rsync/pre-suite/010_RsyncSource.rb diff --git a/acceptance/Rakefile b/acceptance/Rakefile index c8742d220..d07245910 100644 --- a/acceptance/Rakefile +++ b/acceptance/Rakefile @@ -274,10 +274,6 @@ You can go back through the last SECS_AGO logs. Default is one day ago in secs. desc <<-EOS Rerun an acceptance test using the last captured preserved_config.yaml to skip provisioning. Or specify a CONFIG_NUMBER from `rake ci:list_preserved`. -Uses the setup/rsync/pre-suite to rsync the local puppet source onto master and agent. -You may specify an RSYNC_FILTER_FILE as well. -You may skip purgeing and reinstalling puppet packages by including SKIP_PACKAGE_REINSTALL. -You may skip rsyncing local puppet files over to the tests hosts by including SKIP_RSYNC. Defaults to a packages run, but you can set it to 'git' with TYPE='git'. EOS task :test_against_preserved_hosts do @@ -290,7 +286,7 @@ Defaults to a packages run, but you can set it to 'git' with TYPE='git'. :hosts_file => "#{config_path}/preserved_config.yaml", :no_provision => true, :preserve_hosts => true, - :pre_suite => ['setup/rsync/pre-suite'] + :pre_suite => [''] ) end end diff --git a/acceptance/setup/rsync/pre-suite/000_PurgeAndReinstall.rb b/acceptance/setup/rsync/pre-suite/000_PurgeAndReinstall.rb deleted file mode 100644 index aa2bdf412..000000000 --- a/acceptance/setup/rsync/pre-suite/000_PurgeAndReinstall.rb +++ /dev/null @@ -1,10 +0,0 @@ -test_name "Purge and Reinstall Packages" do - if !ENV['SKIP_PACKAGE_REINSTALL'] - hosts.each do |host| - host.uninstall_package('puppet') - host.uninstall_package('puppet-common') - additional_switches = '--allow-unauthenticated' if host['platform'] =~ /debian|ubuntu/ - host.install_package('puppet', additional_switches) - end - end -end diff --git a/acceptance/setup/rsync/pre-suite/010_RsyncSource.rb b/acceptance/setup/rsync/pre-suite/010_RsyncSource.rb deleted file mode 100644 index 1bfbda871..000000000 --- a/acceptance/setup/rsync/pre-suite/010_RsyncSource.rb +++ /dev/null @@ -1,25 +0,0 @@ -test_name "Rsync Source" do - if !ENV['SKIP_RSYNC'] - hosts.each do |host| - step "rsyncing local puppet source to #{host}" do - host.install_package('rsync') if !host.check_for_package('rsync') - filter_opt = "--filter='merge #{ENV['RSYNC_FILTER_FILE']}'" if ENV['RSYNC_FILTER_FILE'] - destination_dir = case host['platform'] - when /debian|ubuntu/ - then '/usr/lib/ruby/vendor_ruby' - when /el|centos/ - then '/usr/lib/ruby/site_ruby/1.8' - when /fedora/ - then '/usr/share/ruby/vendor_ruby' - else - raise "We should actually do some #{host['platform']} platform specific rsyncing here..." - end - cmd = "rsync -r --exclude '.*.swp' #{filter_opt} --size-only -i -e'ssh -i id_rsa-acceptance' ../lib/* root@#{host}:#{destination_dir}" - puts "RSYNC: #{cmd}" - result = `#{cmd}` - raise("Failed rsync execution:\n#{result}") if $? != 0 - puts result - end - end - end -end diff --git a/docs/acceptance_tests.md b/docs/acceptance_tests.md index 857d3aef2..8b65d9432 100644 --- a/docs/acceptance_tests.md +++ b/docs/acceptance_tests.md @@ -84,7 +84,7 @@ TODO Fix up the Rakefile's handling of git urls so that there is a simple way to ### Preserving Hosts -If you have local changes to puppet code (outside of acceptance/) that you don't want to repackage for time reasons, or you just want to ssh into the hosts after a test run, you can use the following sequence: +If you need to ssh into the hosts after a test run, you can use the following sequence: bundle exec rake ci:test_and_preserve_hosts CONFIG=some/config.yaml SHA=12345 TEST=a/foo_test.rb @@ -94,15 +94,7 @@ Then you can log into the hosts, or rerun tests against them by: bundle exec rake ci:test_against_preserved_hosts TEST=a/foo_test.rb -This will use the existing hosts, uninstall and reinstall the puppet packages and rsync in any changes from your local source lib dir. To skip reinstalling the packages set SKIP_PACKAGE_REINSTALL=1. To skip rsyncing, set SKIP_RSYNC=1. To use rsync filters, create a file with your rsync filter settings and set RSYNC_FILTER_FILE to the name of that file. For example: - - include puppet - include puppet/defaults.rb - exclude * - -will ensure that only puppet/defaults.rb is copied. - -NOTE: By default these tasks provision with packages. Set TYPE=git to use source checkouts. +This will use the existing hosts. ### Cleaning Up Preserved Hosts From 30aa2bebfdb30b561ef295e304196c790eb0bbdd Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Fri, 7 Feb 2014 11:57:47 -0800 Subject: [PATCH 756/800] (maint) Pin to Beaker 1.7 and default to preserve_hosts onfail Beaker can now be set to preserve hosts if the acceptance run fails. We are now defaulting to that for ci and manual testing, and pinning to Beaker ~>1.7.0, so any release on the 1.7 line. --- acceptance/Gemfile | 2 +- acceptance/Rakefile | 27 ++++++++++++++++++++------- docs/acceptance_tests.md | 2 +- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/acceptance/Gemfile b/acceptance/Gemfile index f15b1caac..134559362 100644 --- a/acceptance/Gemfile +++ b/acceptance/Gemfile @@ -1,6 +1,6 @@ source ENV['GEM_SOURCE'] || "https://rubygems.org" -gem "beaker", "~> 1.3.1" +gem "beaker", "~> 1.7.0" gem "rake" group(:test) do diff --git a/acceptance/Rakefile b/acceptance/Rakefile index d07245910..0d9b877da 100644 --- a/acceptance/Rakefile +++ b/acceptance/Rakefile @@ -22,6 +22,7 @@ module HarnessOptions :timesync => true, :repo_proxy => true, :add_el_extras => true, + :preserve_hosts => 'onfail', } class Aggregator @@ -66,7 +67,9 @@ module HarnessOptions end def beaker_test(mode = :packages, options = {}) - final_options = HarnessOptions.options(mode, options) + delete_options = options[:__delete_options__] || [] + final_options = HarnessOptions.options(mode, + options.reject { |k,v| k == :__delete_options__ }) if mode == :git puppet_fork = ENV['FORK'] || 'puppetlabs' @@ -74,6 +77,10 @@ def beaker_test(mode = :packages, options = {}) final_options[:install] << "git://#{git_server}/#{puppet_fork}/puppet.git##{sha}" end + delete_options.each do |delete_me| + final_options.delete(delete_me) + end + options_file = 'merged_options.rb' File.open(options_file, 'w') do |merged| merged.puts <<-EOS @@ -83,7 +90,7 @@ EOS merged.puts(final_options.pretty_inspect) end - tests = ENV['TESTS'] || ENV['TEST'] + tests = ENV['TESTS'] || ENV['TEST'] tests_opt = "--tests=#{tests}" if tests config_opt = "--hosts=#{config}" if config @@ -92,12 +99,18 @@ EOS args = ["--options-file", options_file, config_opt, tests_opt, overriding_options].compact + preserve_hosts = final_options[:preserve_hosts] + if md = /--preserve-hosts=?\s*['"]?(\w+)/.match(overriding_options) + preserve_hosts = md[1] + end + begin - sh("beaker", *args) + failed = false + sh("beaker", *args) { |ok,res| failed = true if !ok } ensure if (hosts_file = config || final_options[:hosts_file]) && hosts_file !~ /preserved_config/ cp(hosts_file, "log/latest/config.yml") - generate_config_for_latest_hosts if final_options[:preserve_hosts] || overriding_options =~ /--preserve-hosts/ + generate_config_for_latest_hosts if preserve_hosts = 'always' || (failed && preserve_hosts = 'onfail') end mv(options_file, "log/latest") end @@ -247,7 +260,7 @@ Defaults to a packages run, but you can set it to 'git' with TYPE='git'. #{USAGE} EOS task :test_and_preserve_hosts => 'ci:check_env' do - beaker_test(beaker_run_type, :preserve_hosts => true) + beaker_test(beaker_run_type, :preserve_hosts => 'always') end desc "List acceptance runs from the past day which had hosts preserved." @@ -285,8 +298,8 @@ Defaults to a packages run, but you can set it to 'git' with TYPE='git'. beaker_test(beaker_run_type, :hosts_file => "#{config_path}/preserved_config.yaml", :no_provision => true, - :preserve_hosts => true, - :pre_suite => [''] + :preserve_hosts => 'always', + :__delete_options__ => [:pre_suite], ) end end diff --git a/docs/acceptance_tests.md b/docs/acceptance_tests.md index 8b65d9432..4eea425cc 100644 --- a/docs/acceptance_tests.md +++ b/docs/acceptance_tests.md @@ -100,7 +100,7 @@ This will use the existing hosts. If you run a number of jobs with --preserve_hosts or vi ci:test_and_preserve_hosts, you may eventually generate a large number of stale vms. They should be reaped automatically by qa infrastructure within a day or so, but you may also run: - bundle exec rake ci:destroy_preserved_hosts + bundle exec rake ci:release_hosts to clean them up sooner and free resources. From 3da573e09771c0c2fc4b4d1e817e13870831e8eb Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Wed, 5 Mar 2014 18:03:09 -0800 Subject: [PATCH 757/800] (maint) Remove trailing commas Remove trailing commas that cause syntax errors. --- acceptance/Rakefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acceptance/Rakefile b/acceptance/Rakefile index 0d9b877da..05987adea 100644 --- a/acceptance/Rakefile +++ b/acceptance/Rakefile @@ -22,7 +22,7 @@ module HarnessOptions :timesync => true, :repo_proxy => true, :add_el_extras => true, - :preserve_hosts => 'onfail', + :preserve_hosts => 'onfail' } class Aggregator @@ -299,7 +299,7 @@ Defaults to a packages run, but you can set it to 'git' with TYPE='git'. :hosts_file => "#{config_path}/preserved_config.yaml", :no_provision => true, :preserve_hosts => 'always', - :__delete_options__ => [:pre_suite], + :__delete_options__ => [:pre_suite] ) end end From ba3b5d9807eaba07b0117daae21b7b711ebe4e86 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Thu, 6 Mar 2014 14:02:48 -0800 Subject: [PATCH 758/800] (maint) Change regex for picking up beaker hosts to match current output Latest beaker includes timestamp in the log output we are matching to identify hosts. --- acceptance/Rakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance/Rakefile b/acceptance/Rakefile index 05987adea..b0b287cff 100644 --- a/acceptance/Rakefile +++ b/acceptance/Rakefile @@ -170,7 +170,7 @@ def list_preserved_hosts(secs_ago = ONE_DAY_IN_SECS) File.open(log, 'r') do |file| if file.ctime > yesterday file.each_line do |line| - matchdata = /^(\w+) \(.*?\) \$/.match(line.encode!('UTF-8', 'UTF-8', :invalid => :replace)) + matchdata = /^(\w+) \(.*?\) \d\d:\d\d:\d\d\$/.match(line.encode!('UTF-8', 'UTF-8', :invalid => :replace)) hosts.add(matchdata[1]) if matchdata end end From e2fee277daa983ea40745ebf603b608d35b0bff4 Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Wed, 5 Mar 2014 14:58:40 -0800 Subject: [PATCH 759/800] (PUP-1839) Override settings by just assigning Now that the `:memory` level of settings is higher precedence than the cli level, this can now simply set the confdir, vardir, and certname in a very normal manner. --- lib/puppet/application/device.rb | 13 +++---- lib/puppet/settings.rb | 15 +++++++ spec/unit/application/device_spec.rb | 58 +++++++++++++--------------- 3 files changed, 48 insertions(+), 38 deletions(-) diff --git a/lib/puppet/application/device.rb b/lib/puppet/application/device.rb index a293ab483..854188ad1 100644 --- a/lib/puppet/application/device.rb +++ b/lib/puppet/application/device.rb @@ -1,7 +1,6 @@ require 'puppet/application' require 'puppet/util/network_device' - class Puppet::Application::Device < Puppet::Application run_mode :agent @@ -173,9 +172,9 @@ Licensed under the Apache 2.0 License Puppet.info "starting applying configuration to #{device.name} at #{device.url}" # override local $vardir and $certname - Puppet.settings.set_value(:confdir, ::File.join(Puppet[:devicedir], device.name), :cli) - Puppet.settings.set_value(:vardir, ::File.join(Puppet[:devicedir], device.name), :cli) - Puppet.settings.set_value(:certname, device.name, :cli) + Puppet[:confdir] = ::File.join(Puppet[:devicedir], device.name) + Puppet[:vardir] = ::File.join(Puppet[:devicedir], device.name) + Puppet[:certname] = device.name # this will reload and recompute default settings and create the devices sub vardir, or we hope so :-) Puppet.settings.use :main, :agent, :ssl @@ -194,9 +193,9 @@ Licensed under the Apache 2.0 License rescue => detail Puppet.log_exception(detail) ensure - Puppet.settings.set_value(:vardir, vardir, :cli) - Puppet.settings.set_value(:confdir, confdir, :cli) - Puppet.settings.set_value(:certname, certname, :cli) + Puppet[:vardir] = vardir + Puppet[:confdir] = confdir + Puppet[:certname] = certname Puppet::SSL::Host.reset end end diff --git a/lib/puppet/settings.rb b/lib/puppet/settings.rb index 416d66306..1188a7b85 100644 --- a/lib/puppet/settings.rb +++ b/lib/puppet/settings.rb @@ -102,16 +102,31 @@ class Puppet::Settings end # Retrieve a config value + # @param param [Symbol] the name of the setting + # @return [Object] the value of the setting + # @api private def [](param) value(param) end # Set a config value. This doesn't set the defaults, it sets the value itself. + # @param param [Symbol] the name of the setting + # @param value [Object] the new value of the setting + # @api private def []=(param, value) @value_sets[:memory].set(param, value) unsafe_flush_cache end + # Create a new default value for the given setting. The default overrides are + # higher precedence than the defaults given in defaults.rb, but lower + # precedence than any other values for the setting. This allows one setting + # `a` to change the default of setting `b`, but still allow a user to provide + # a value for setting `b`. + # + # @param param [Symbol] the name of the setting + # @param value [Object] the new default value for the setting + # @api private def override_default(param, value) @value_sets[:overridden_defaults].set(param, value) unsafe_flush_cache diff --git a/spec/unit/application/device_spec.rb b/spec/unit/application/device_spec.rb index 7d3e05812..25a4f83de 100755 --- a/spec/unit/application/device_spec.rb +++ b/spec/unit/application/device_spec.rb @@ -300,7 +300,7 @@ describe Puppet::Application::Device do "device2" => OpenStruct.new(:name => "device2", :url => "url", :provider => "cisco"), } Puppet::Util::NetworkDevice::Config.stubs(:devices).returns(@device_hash) - Puppet.settings.stubs(:set_value) + Puppet.stubs(:[]=) Puppet.settings.stubs(:use) @device.stubs(:setup_host) Puppet::Util::NetworkDevice.stubs(:init) @@ -309,18 +309,18 @@ describe Puppet::Application::Device do end it "should set vardir to the device vardir" do - Puppet.settings.expects(:set_value).with(:vardir, make_absolute("/dummy/devices/device1"), :cli) + Puppet.expects(:[]=).with(:vardir, make_absolute("/dummy/devices/device1")) @device.main end it "should set confdir to the device confdir" do - Puppet.settings.expects(:set_value).with(:confdir, make_absolute("/dummy/devices/device1"), :cli) + Puppet.expects(:[]=).with(:confdir, make_absolute("/dummy/devices/device1")) @device.main end it "should set certname to the device certname" do - Puppet.settings.expects(:set_value).with(:certname, "device1", :cli) - Puppet.settings.expects(:set_value).with(:certname, "device2", :cli) + Puppet.expects(:[]=).with(:certname, "device1") + Puppet.expects(:[]=).with(:certname, "device2") @device.main end @@ -349,31 +349,29 @@ describe Puppet::Application::Device do all_devices = Set.new(@device_hash.keys.map do |device_name| make_absolute("/dummy/devices/#{device_name}") end) found_devices = Set.new() - # a block to use in a few places later to validate the arguments passed to "set_value" - p = Proc.new do |my_setting, my_value, my_type| - success = - (my_setting == setting) && - (my_type == :cli) && - (all_devices.include?(my_value)) - found_devices.add(my_value) if success - success + # a block to use in a few places later to validate the updated settings + p = Proc.new do |my_setting, my_value| + if my_setting == setting && all_devices.include?(my_value) + found_devices.add(my_value) + true + else + false + end end seq = sequence("clean up dirs") all_devices.size.times do ## one occurrence of set / run / set("/dummy") for each device - Puppet.settings.expects(:set_value).with(&p).in_sequence(seq) + Puppet.expects(:[]=).with(&p).in_sequence(seq) @configurer.expects(:run).in_sequence(seq) - Puppet.settings.expects(:set_value).with(setting, make_absolute("/dummy"), :cli).in_sequence(seq) + Puppet.expects(:[]=).with(setting, make_absolute("/dummy")).in_sequence(seq) end @device.main - # make sure that we were called with each of the defined devices - all_devices.should == found_devices - + expect(found_devices).to eq(all_devices) end end @@ -381,32 +379,30 @@ describe Puppet::Application::Device do all_devices = Set.new(@device_hash.keys) found_devices = Set.new() - # a block to use in a few places later to validate the arguments passed to "set_value" - p = Proc.new do |my_setting, my_value, my_type| - success = - (my_setting == :certname) && - (my_type == :cli) && - (all_devices.include?(my_value)) - found_devices.add(my_value) if success - success - #true + # a block to use in a few places later to validate the updated settings + p = Proc.new do |my_setting, my_value| + if my_setting == :certname && all_devices.include?(my_value) + found_devices.add(my_value) + true + else + false + end end seq = sequence("clean up certname") all_devices.size.times do ## one occurrence of set / run / set("certname") for each device - Puppet.settings.expects(:set_value).with(&p).in_sequence(seq) + Puppet.expects(:[]=).with(&p).in_sequence(seq) @configurer.expects(:run).in_sequence(seq) - Puppet.settings.expects(:set_value).with(:certname, "certname", :cli).in_sequence(seq) + Puppet.expects(:[]=).with(:certname, "certname").in_sequence(seq) end @device.main # make sure that we were called with each of the defined devices - all_devices.should == found_devices - + expect(found_devices).to eq(all_devices) end it "should expire all cached attributes" do From 93102e58553f9a30df7e30ffd63486ca3da161b2 Mon Sep 17 00:00:00 2001 From: Will Farrington Date: Wed, 5 Mar 2014 10:27:24 +1300 Subject: [PATCH 760/800] (PUP-1846) File content diffing should respect loglevel The current behavior is to log all diff output on file content changes (if show_diff is set) at the info level, rather than the loglevel of the resource. This change alters the behavior to log at the same loglevel as the file resource. --- lib/puppet/type/file/content.rb | 2 +- spec/unit/type/file/content_spec.rb | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/puppet/type/file/content.rb b/lib/puppet/type/file/content.rb index 2ee29bd5b..bdeeedc32 100644 --- a/lib/puppet/type/file/content.rb +++ b/lib/puppet/type/file/content.rb @@ -111,7 +111,7 @@ module Puppet if ! result and Puppet[:show_diff] and resource.show_diff? write_temporarily do |path| - notice "\n" + diff(@resource[:path], path) + send @resource[:loglevel], "\n" + diff(@resource[:path], path) end end result diff --git a/spec/unit/type/file/content_spec.rb b/spec/unit/type/file/content_spec.rb index 08c345781..91ce83100 100755 --- a/spec/unit/type/file/content_spec.rb +++ b/spec/unit/type/file/content_spec.rb @@ -196,12 +196,13 @@ describe content do before do Puppet[:show_diff] = cfg @resource.stubs(:show_diff?).returns param + @resource[:loglevel] = "debug" end if cfg and param it "should display a diff" do @content.expects(:diff).returns("my diff").once - @content.expects(:notice).with("\nmy diff").once + @content.expects(:debug).with("\nmy diff").once @content.should_not be_safe_insync("other content") end else From 2f8428c7941f969455c686488e51680445e1d903 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Wed, 19 Feb 2014 18:28:49 +0100 Subject: [PATCH 761/800] (PUP-30) Add foundation for heredoc and epp (parser and model) This adds the foundation for support of heredoc and epp. The heredoc is at this point functional but does not get location information right since sublocator needs to be supported by the HeredocExpression. EPP is untested. --- lib/puppet/parser/functions/epptemplate.rb | 53 + .../parser/functions/inline_epptempplate.rb | 58 + lib/puppet/pops/evaluator/evaluator_impl.rb | 9 + .../pops/evaluator/external_syntax_support.rb | 47 + lib/puppet/pops/issues.rb | 5 + lib/puppet/pops/model/ast_transformer.rb | 24 + lib/puppet/pops/model/factory.rb | 27 + lib/puppet/pops/model/model.rb | 21 + lib/puppet/pops/model/model_label_provider.rb | 4 + lib/puppet/pops/model/model_tree_dumper.rb | 23 + lib/puppet/pops/parser/egrammar.ra | 26 + lib/puppet/pops/parser/eparser.rb | 2534 +++++++++-------- lib/puppet/pops/parser/epp_parser.rb | 4 +- lib/puppet/pops/parser/heredoc_support.rb | 2 +- .../puppetlabs/syntax_checkers/json.rb | 15 +- 15 files changed, 1643 insertions(+), 1209 deletions(-) create mode 100644 lib/puppet/parser/functions/epptemplate.rb create mode 100644 lib/puppet/parser/functions/inline_epptempplate.rb create mode 100644 lib/puppet/pops/evaluator/external_syntax_support.rb diff --git a/lib/puppet/parser/functions/epptemplate.rb b/lib/puppet/parser/functions/epptemplate.rb new file mode 100644 index 000000000..ee6abf05d --- /dev/null +++ b/lib/puppet/parser/functions/epptemplate.rb @@ -0,0 +1,53 @@ +Puppet::Parser::Functions::newfunction(:epptemplate, :type => :rvalue, :arity => -2, :doc => +"Evaluates one or more Embedded Puppet Template (EPP) files and returns their concatenated result. + +EPP support the following tags: +* `<%= puppet expression %>` - This tag renders the value of the expression it contains. +* `<% puppet expression(s) %>` - This tag will execute the expression(s) it contains, but renders nothing. +* `<%# comment %>` - The tag and its content renders nothing. +* `<%%` or `%%>` - Renders a literal `<%` or `%>` respectively. +* `<%-` - Same as `<%` but suppresses any leading whitespace. +* `-%>` - Same as `%>` but suppresses any trailing whitespace on the same line (including line break). + +EPP supports parameters by placing an optional parameter list as the very first element in the Epp. As an example, +`<%- ($x, $y, $z='unicorn') -%>` when placed first in the EPP text declares that the parameters `x` and `y` must be +given as template arguments when calling `epptemplate`, and that `z` if not given as a template argument +defaults to `'unicorn'`. Template parameters are available as variables, e.g.arguments `$x`, `$y` and `$z` in the example. + +Arguments are passed to the template by calling `epptemplate` with a Hash as the last argument, where parameters +are bound to values, e.g. `epptemplate('templatefile.epp', {'x'=>10, 'y'=>20})`. Excess arguments may be given +(i.e. undeclared parameters). Template parameters shadow variables in outer scopes. Template arguments may be +passed to any template; the template text does not have to have a declaration of parameters. + +Several files may be given as arguments to `epptemplate`, the result is the concatenation of each produced result. +If template arguments are given, they are used for each given file. +") do |arguments| + # accepts one or more arguments (each being a file), except an optional last argument being a hash + # of parameters to pass to each evaluation. + + if(arguments[-1].is_a? Hash) + template_args = arguments[-1] + arguments = arguments[0..-2] + else + template_args = {} + end + require 'puppet/parser/parser_factory' + require 'puppet/parser/ast' + + arguments.collect do |file| + if file.is_a?(Hash) + raise IllegalArgumentException, "A Hash may be given as the last argument only" + end + debug "Retrieving epp template #{file}" + template_file = Puppet::Parser::Files.find_template(file, self.compiler.environment.to_s) + unless template_file + raise Puppet::ParseError, "Could not find template '#{filename}'" + end + + parser = Puppet::Parser::ParserFactory.epp_parser(self.compiler.environment) + parser.file = template_file + result = parser.parse() + raise Puppet::ParseError, "Parsing #{template_file} did not produce an instance of Epp. Got: #{result.class}" unless result.is_a?(Puppet::Parser::AST::Epp) + result.call(self, template_args) + end.join("") +end diff --git a/lib/puppet/parser/functions/inline_epptempplate.rb b/lib/puppet/parser/functions/inline_epptempplate.rb new file mode 100644 index 000000000..ca0076712 --- /dev/null +++ b/lib/puppet/parser/functions/inline_epptempplate.rb @@ -0,0 +1,58 @@ +Puppet::Parser::Functions::newfunction(:inline_epptemplate, :type => :rvalue, :arity => -2, :doc => +"Evaluates one or more Embedded Puppet Template (EPP) strings and returns their concatenated result. + +EPP support the following tags: +* `<%= puppet expression %>` - This tag renders the value of the expression it contains. +* `<% puppet expression(s) %>` - This tag will execute the expression(s) it contains, but renders nothing. +* `<%# comment %>` - The tag and its content renders nothing. +* `<%%` or `%%>` - Renders a literal `<%` or `%>` respectively. +* `<%-` - Same as `<%` but suppresses any leading whitespace. +* `-%>` - Same as `%>` but suppresses any trailing whitespace on the same line (including line break). + +EPP supports parameters by placing an optional parameter list as the very first element in the Epp. As an example, +`<%- ($x, $y, $z='unicorn') -%>` when placed first in the EPP text declares that the parameters `x` and `y` must be +given as template arguments when calling `inline_epptemplate`, and that `z` if not given as a template argument +defaults to `'unicorn'`. Template parameters are available as variables, e.g.arguments `$x`, `$y` and `$z` in the example. + +Arguments are passed to the template by calling `inline_epptemplate` with a Hash as the last argument, where parameters +are bound to values, e.g. `inline_epptemplate('...', {'x'=>10, 'y'=>20})`. Excess arguments may be given +(i.e. undeclared parameters). Template parameters shadow variables in outer scopes. Template arguments may be +passed to any template; the template text does not have to have a declaration of parameters. + +Several strings may be given as arguments to `inline_epptemplate`, the result is the concatenation of each produced result. +If template arguments are given, they are used for each given template string. + +Note: An inline template is best stated using a single-quoted string, or a heredoc since a double-quoted string +is subject to expression interpolation before the string is parsed as an EPP template. Here is an example +using heredoc. + + inline_epptemplate(@(END:epp), {'x'=>'epp template world'}) + <%- ($x) -%> + Hello <%= $x %>! + END + +") do |arguments| + # accepts one or more arguments (each being a epp source string), except an optional last argument being a hash + # of parameters to pass to each evaluation. + + if(arguments[-1].is_a? Hash) + template_args = arguments[-1] + arguments = arguments[0..-2] + else + template_args = {} + end + require 'puppet/parser/parser_factory' + require 'puppet/parser/ast' + + arguments.collect do |text| + if text.is_a?(Hash) + raise IllegalArgumentException, "A Hash may be given as the last argument only" + end + + parser = Puppet::Parser::ParserFactory.epp_parser(self.compiler.environment) + parser.string = text + result = parser.parse() + raise Puppet::ParseError, "Parsing epp string did not produce an instance of Epp. Got: #{result.class}" unless result.is_a?(Puppet::Parser::AST::Epp) + result.call(self, template_args) + end.join("") +end diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index 711d756f2..d5187c14b 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -3,6 +3,7 @@ require 'puppet/pops/evaluator/compare_operator' require 'puppet/pops/evaluator/relationship_operator' require 'puppet/pops/evaluator/access_operator' require 'puppet/pops/evaluator/closure' +require 'puppet/pops/evaluator/external_syntax_support' # This implementation of {Puppet::Pops::Evaluator} performs evaluation using the puppet 3.x runtime system # in a manner largely compatible with Puppet 3.x, but adds new features and introduces constraints. @@ -27,6 +28,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl # This separation has been made to make it easier to later migrate the evaluator to an improved runtime. # include Puppet::Pops::Evaluator::Runtime3Support + include Puppet::Pops::Evaluator::ExternalSyntaxSupport # This constant is not defined as Float::INFINITY in Ruby 1.8.7 (but is available in later version # Refactor when support is dropped for Ruby 1.8.7. @@ -728,6 +730,13 @@ class Puppet::Pops::Evaluator::EvaluatorImpl end end + # Evaluates Puppet DSL Heredoc + def eval_HeredocExpression o, scope + result = evaluate(o.text_expr, scope) + assert_external_syntax(scope, result, o.syntax, o.text_expr) + result + end + # Evaluates Puppet DSL `if` def eval_IfExpression o, scope with_guarded_scope(scope) do diff --git a/lib/puppet/pops/evaluator/external_syntax_support.rb b/lib/puppet/pops/evaluator/external_syntax_support.rb new file mode 100644 index 000000000..b9e502dae --- /dev/null +++ b/lib/puppet/pops/evaluator/external_syntax_support.rb @@ -0,0 +1,47 @@ +# This module is an integral part of the evaluator. It deals with the concern of validating +# external syntax in text produced by heredoc and templates. +# +module Puppet::Pops::Evaluator::ExternalSyntaxSupport + # TODO: This can be simplified if the Factory directly supporteded hash_of/type_of + TYPES = Puppet::Pops::Types::TypeFactory + + def assert_external_syntax(scope, result, syntax, reference_expr) + @@HASH_OF_SYNTAX_CHECKERS ||= TYPES.hash_of(TYPES.type_of(::Puppetx::SYNTAX_CHECKERS_TYPE)) + # ignore 'unspecified syntax' + return if syntax.nil? || syntax == '' + + checker = checker_for_syntax(scope, syntax) + # ignore syntax with no matching checker + return unless checker + + # Call checker and give it the location information from the expression + # (as opposed to where the heredoc tag is (somewhere on the line above)). + acceptor = Puppet::Pops::Validation::Acceptor.new() + source_pos = find_closest_positioned(reference_expr) + checker.check(result, syntax, acceptor, source_pos) + + checker_message = "Invalid produced text having syntax: '#{syntax}'." + Puppet::Pops::IssueReporter.assert_and_report(acceptor, :message => checker_message) + raise ArgumentError, "Internal Error: Configuration of runtime error handling wrong: should have raised exception" + end + + # Finds the most significant checker for the given syntax (most significant is to the right). + # Returns nil if there is no registered checker. + # + def checker_for_syntax(scope, syntax) + checkers_hash = scope.compiler.injector.lookup(scope, @@HASH_OF_SYNTAX_CHECKERS, ::Puppetx::SYNTAX_CHECKERS) || {} + checkers_hash[lookup_keys_for_syntax(syntax).find {|x| checkers_hash[x] }] + end + + # Returns an array of possible syntax names + def lookup_keys_for_syntax(syntax) + segments = syntax.split(/\+/) + result = [] + begin + result << segments.join("+") + segments.shift + end until segments.empty? + result + end + +end diff --git a/lib/puppet/pops/issues.rb b/lib/puppet/pops/issues.rb index f301609a4..0613ba3a4 100644 --- a/lib/puppet/pops/issues.rb +++ b/lib/puppet/pops/issues.rb @@ -449,4 +449,9 @@ module Puppet::Pops::Issues RESULT_IS_INFINITY = hard_issue :RESULT_IS_INFINITY, :operator do "The result of the #{operator} expression is Infinity" end + + # TODO_HEREDOC + EMPTY_HEREDOC_SYNTAX_SEGMENT = issue :EMPTY_HEREDOC_SYNTAX_SEGMENT, :syntax do + "Heredoc syntax specification has empty segment between '+' : '#{syntax}'" + end end diff --git a/lib/puppet/pops/model/ast_transformer.rb b/lib/puppet/pops/model/ast_transformer.rb index 75f0ef26f..4016cc266 100644 --- a/lib/puppet/pops/model/ast_transformer.rb +++ b/lib/puppet/pops/model/ast_transformer.rb @@ -175,6 +175,14 @@ class Puppet::Pops::Model::AstTransformer ast o, AST::Collection, args end + def transform_EppExpression(o) + # TODO: Not supported in 3x TODO_EPP + parameters = o.parameters.collect {|p| transform(p) } + args = { :parameters => parameters } + args[:children] = transform(o.body) unless is_nop?(o.body) + Puppet::Parser::AST::Epp.new(merge_location(args, o)) + end + def transform_ExportedQuery(o) if is_nop?(o.expr) result = :exported @@ -428,6 +436,12 @@ class Puppet::Pops::Model::AstTransformer Puppet::Parser::AST::Hostclass.new(o.name, merge_location(args, o)) end + def transform_HeredocExpression(o) + # TODO_HEREDOC Not supported in 3x + args = {:syntax=> o.syntax(), :expr => transform(o.text_expr()) } + Puppet::Parser::AST::Heredoc.new(merge_location(args, o)) + end + def transform_NodeDefinition(o) # o.host_matches are expressions, and 3.1 AST requires special object AST::HostName # where a HostName is one of NAME, STRING, DEFAULT or Regexp - all of these are strings except regexp @@ -475,6 +489,16 @@ class Puppet::Pops::Model::AstTransformer Puppet::Parser::AST::Relationship.new(transform(o.left_expr), transform(o.right_expr), o.operator.to_s, merge_location({}, o)) end + def transform_RenderStringExpression(o) + # TODO_EPP Not supported in 3x + ast o, AST::RenderString, :value => o.value + end + + def transform_RenderExpression(o) + # TODO_EPP Not supported in 3x + ast o, AST::RenderExpression, :value => transform(o.expr) + end + def transform_ResourceTypeDefinition(o) parameters = o.parameters.collect {|p| transform(p) } args = { :arguments => parameters } diff --git a/lib/puppet/pops/model/factory.rb b/lib/puppet/pops/model/factory.rb index 8c86d80ac..b4c028696 100644 --- a/lib/puppet/pops/model/factory.rb +++ b/lib/puppet/pops/model/factory.rb @@ -122,6 +122,12 @@ class Puppet::Pops::Model::Factory o end + def build_HeredocExpression(o, name, expr) + o.syntax = name + o.text_expr = build(expr) + o + end + # @param name [String] a valid classname # @param parameters [Array] may be empty # @param parent_class_name [String, nil] a valid classname referencing a parent class, optional. @@ -248,6 +254,11 @@ class Puppet::Pops::Model::Factory o end + def build_RenderStringExpression(o, string) + o.value = string; + o + end + def build_ResourceBody(o, title_expression, attribute_operations) o.title = build(title_expression) attribute_operations.each {|ao| o.addOperations(build(ao)) } @@ -506,6 +517,9 @@ class Puppet::Pops::Model::Factory def self.HASH(entries); new(Model::LiteralHash, *entries); end + # TODO_HEREDOC + def self.HEREDOC(name, expr); new(Model::HeredocExpression, name, expr); end + def self.LIST(entries); new(Model::LiteralList, *entries); end def self.PARAM(name, expr=nil); new(Model::Parameter, name, expr); end @@ -534,6 +548,19 @@ class Puppet::Pops::Model::Factory new(Model::TextExpression, new(expr).interpolate) end + # TODO_EPP + def self.RENDER_STRING(o) + new(Model::RenderStringExpression, o) + end + + def self.RENDER_EXPR(expr) + new(Model::RenderExpression, expr) + end + + def self.EPP(parameters, body) + new(Model::EppExpression, parameters, body) + end + # TODO: This is the same a fqn factory method, don't know if callers to fqn and QNAME can live with the # same result or not yet - refactor into one method when decided. # diff --git a/lib/puppet/pops/model/model.rb b/lib/puppet/pops/model/model.rb index 09a92d02e..835c9735b 100644 --- a/lib/puppet/pops/model/model.rb +++ b/lib/puppet/pops/model/model.rb @@ -261,6 +261,15 @@ module Puppet::Pops::Model contains_one_uni 'body', Expression end + # A heredoc is a wrapper around a LiteralString or a ConcatenatedStringExpression with a specification + # of syntax. The expectation is that "syntax" has meaning to a validator. A syntax of nil or '' means + # "unspecified syntax". + # + class HeredocExpression < Expression + has_attr 'syntax', String + contains_one_uni 'text_expr', Expression, :lowerBound => 1 + end + # A class definition # class HostClassDefinition < NamedDefinition @@ -422,6 +431,18 @@ module Puppet::Pops::Model # class VariableExpression < UnaryExpression; end + # Epp start + class EppExpression < Definition + end + + # A string to render + class RenderStringExpression < LiteralString + end + + # An expression to evluate and render + class RenderExpression < UnaryExpression + end + # A resource body describes one resource instance # class ResourceBody < Positioned diff --git a/lib/puppet/pops/model/model_label_provider.rb b/lib/puppet/pops/model/model_label_provider.rb index d02f40520..979aa4bc5 100644 --- a/lib/puppet/pops/model/model_label_provider.rb +++ b/lib/puppet/pops/model/model_label_provider.rb @@ -22,6 +22,7 @@ class Puppet::Pops::Model::ModelLabelProvider < Puppet::Pops::LabelProvider def label_AccessExpression o ; "'[]' expression" end def label_MatchExpression o ; "'#{o.operator}' expression" end def label_CollectExpression o ; label(o.query) end + def label_EppExpression o ; "Epp Template" end def label_ExportedQuery o ; "Exported Query" end def label_VirtualQuery o ; "Virtual Query" end def label_QueryExpression o ; "Collect Query" end @@ -51,6 +52,7 @@ class Puppet::Pops::Model::ModelLabelProvider < Puppet::Pops::LabelProvider def label_UnaryMinusExpression o ; "Unary Minus" end def label_BlockExpression o ; "Block Expression" end def label_ConcatenatedString o ; "Double Quoted String" end + def label_HeredocExpression o ; "'@(#{o.syntax})' expression" end def label_HostClassDefinition o ; "Host Class Definition" end def label_NodeDefinition o ; "Node Definition" end def label_ResourceTypeDefinition o ; "'define' expression" end @@ -63,6 +65,8 @@ class Puppet::Pops::Model::ModelLabelProvider < Puppet::Pops::LabelProvider def label_CallMethodExpression o ; "Method call" end def label_CaseExpression o ; "'case' statement" end def label_CaseOption o ; "Case Option" end + def label_RenderStringExpression o ; "Epp Text" end + def label_RenderExpression o ; "Epp Interpolated Expression" end def label_RelationshipExpression o ; "'#{o.operator}' expression" end def label_ResourceBody o ; "Resource Instance Definition" end def label_ResourceDefaultsExpression o ; "Resource Defaults Expression" end diff --git a/lib/puppet/pops/model/model_tree_dumper.rb b/lib/puppet/pops/model/model_tree_dumper.rb index eca11ced8..adb4eb471 100644 --- a/lib/puppet/pops/model/model_tree_dumper.rb +++ b/lib/puppet/pops/model/model_tree_dumper.rb @@ -58,6 +58,17 @@ class Puppet::Pops::Model::ModelTreeDumper < Puppet::Pops::Model::TreeDumper result end + def dump_EppExpression o + result = ["epp"] + result << ["parameters"] + o.parameters.collect {|p| do_dump(p) } if o.parameters.size() > 0 + if o.body + result << do_dump(o.body) + else + result << [] + end + result + end + def dump_ExportedQuery o result = ["<<| |>>"] result += dump_QueryExpression(o) unless is_nop?(o.expr) @@ -180,6 +191,10 @@ class Puppet::Pops::Model::ModelTreeDumper < Puppet::Pops::Model::TreeDumper ["cat"] + o.segments.collect {|x| do_dump(x)} end + def dump_HeredocExpression(o) + result = ["@(#{o.syntax})", :indent, :break, do_dump(o.text_expr), :dedent, :break] + end + def dump_HostClassDefinition o result = ["class", o.name] result << ["inherits", o.parent_class] if o.parent_class @@ -304,6 +319,14 @@ class Puppet::Pops::Model::ModelTreeDumper < Puppet::Pops::Model::TreeDumper [o.operator.to_s, do_dump(o.left_expr), do_dump(o.right_expr)] end + def dump_RenderStringExpression o + ["render-s", " '#{o.value}'"] + end + + def dump_RenderExpression o + ["render", do_dump(o.expr)] + end + def dump_ResourceBody o result = [do_dump(o.title), :indent] o.operations.each do |p| diff --git a/lib/puppet/pops/parser/egrammar.ra b/lib/puppet/pops/parser/egrammar.ra index 6887e8d66..2eab1b1b9 100644 --- a/lib/puppet/pops/parser/egrammar.ra +++ b/lib/puppet/pops/parser/egrammar.ra @@ -16,6 +16,8 @@ token MATCH NOMATCH REGEX IN_EDGE OUT_EDGE IN_EDGE_SUB OUT_EDGE_SUB token IN UNLESS PIPE token LAMBDA SELBRACE token NUMBER +token HEREDOC +token RENDER_STRING RENDER_EXPR EPP_START token LOW prechigh @@ -27,6 +29,7 @@ prechigh left AT ATAT left DOT left CALL + nonassoc EPP_START left LBRACK LISTSTART left RBRACK left QMARK @@ -51,6 +54,8 @@ prechigh left CASE_COLON left FARROW left COMMA + nonassoc RENDER_EXPR + nonassoc RENDER_STRING left LOW preclow @@ -58,6 +63,7 @@ rule # Produces [Model::BlockExpression, Model::Expression, nil] depending on multiple statements, single statement or empty program : statements { result = create_program(Factory.block_or_expression(*val[0])) } + | epp_expression { result = Factory.block_or_expression(*val[0]) } | nil # Produces a semantic model (non validated, but semantically adjusted). @@ -143,6 +149,7 @@ primary_expression | definition_expression | hostclass_expression | node_definition_expression + | epp_render_expression # Aleways have the same value literal_expression @@ -638,6 +645,7 @@ hash quotedtext : string | dq_string + | heredoc string : STRING { result = Factory.literal(val[0][:value]) ; loc result, val[0] } dq_string : dqpre dqrval { result = Factory.string(val[0], *val[1]) ; loc result, val[0], val[1][-1] } @@ -651,6 +659,24 @@ dqtail : dqpost { result = [val[0]] } | dqmid dqrval { result = [val[0]] + val[1] } +# TODO_HEREDOC +heredoc + : HEREDOC string { result = Factory.HEREDOC(val[0][:value], val[1]); loc result, val[0], val[1] } + | HEREDOC dq_string { result = Factory.HEREDOC(val[0][:value], val[1]); loc result, val[0], val[1] } + +# TODO_EPP +epp_expression + : EPP_START epp_parameters_list statements { result = Factory.EPP(val[1], val[2]); loc result, val[0] } + +epp_parameters_list + : =LOW{ result = [] } + | LPAREN RPAREN { result = [] } + | LPAREN parameters endcomma RPAREN { result = val[1] } + +epp_render_expression + : RENDER_STRING { result = Factory.RENDER_STRING(val[0][:value]); loc result, val[0] } + | RENDER_EXPR expression { result = Factory.RENDER_EXPR(val[1]); loc result, val[0], val[1] } + number : NUMBER { result = Factory.NUMBER(val[0][:value]) ; loc result, val[0] } name : NAME { result = Factory.QNAME_OR_NUMBER(val[0][:value]) ; loc result, val[0] } type : CLASSREF { result = Factory.QREF(val[0][:value]) ; loc result, val[0] } diff --git a/lib/puppet/pops/parser/eparser.rb b/lib/puppet/pops/parser/eparser.rb index 8fc9a10ea..75676e6d3 100644 --- a/lib/puppet/pops/parser/eparser.rb +++ b/lib/puppet/pops/parser/eparser.rb @@ -20,7 +20,7 @@ module Puppet module Parser class Parser < Racc::Parser -module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 710) +module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 736) # Make emacs happy # Local Variables: @@ -30,175 +30,194 @@ module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 710) ##### State transition tables begin ### clist = [ -'54,56,-130,242,49,119,51,54,56,118,241,230,338,290,72,242,229,275,54', -'56,-210,-201,211,13,-128,211,237,236,95,39,99,46,94,48,43,252,47,62', -'58,326,41,61,44,45,-130,128,59,12,126,222,60,54,56,11,98,49,128,51,368', -'126,208,63,-210,-201,79,78,-128,40,63,74,75,57,242,232,13,50,73,253', -'231,63,39,328,46,115,48,43,80,47,62,58,306,41,61,44,45,218,119,59,12', -'225,118,60,54,56,11,323,49,322,51,54,56,119,63,119,289,118,72,118,40', -'308,54,56,57,119,227,13,50,118,249,310,95,39,99,46,94,48,43,292,47,62', -'58,65,41,61,44,45,67,128,59,12,126,207,60,54,56,11,98,49,128,51,366', -'126,234,63,119,235,79,78,118,40,63,74,75,57,251,119,13,50,73,118,274', -'63,39,323,46,322,48,43,80,47,62,58,315,41,61,44,45,316,317,59,12,211', -'273,60,54,56,11,320,49,228,51,364,324,326,63,107,267,249,72,251,40,249', -'334,335,57,266,273,13,50,198,174,67,95,39,99,46,94,48,43,141,47,62,58', -'265,41,61,44,45,345,251,59,12,251,120,60,54,56,11,98,49,249,51,348,107', -'108,63,76,77,79,78,293,40,107,74,75,57,352,222,13,50,73,354,355,356', -'39,357,46,358,48,101,80,47,62,58,104,41,61,68,70,69,71,59,12,360,361', -'60,54,56,11,72,49,362,51,296,67,64,63,369,370,371,72,372,40,95,,99,57', -'94,,13,50,,,,95,39,99,46,94,48,101,,47,62,58,,41,61,,98,,,59,12,,,60', -'54,56,11,98,49,,51,,,,63,76,77,79,78,,40,,74,75,57,,,13,50,73,,,,39', -',46,,48,101,80,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,72,49,,51,,,', -'63,,,,72,,40,95,,99,57,94,,13,50,,,,95,39,99,46,94,48,101,,47,62,58', -',41,61,,98,,,59,12,,,60,54,56,11,98,49,,51,74,75,,63,,,,73,,40,,74,75', -'57,,,13,50,73,,,,39,,46,,48,43,,47,62,58,,41,61,44,45,,,59,12,,,60,54', -'56,11,72,49,,51,,,,63,,,,72,,40,95,,99,57,94,,13,50,,,,95,39,99,46,94', -'48,101,,47,62,58,,41,61,,98,,,59,12,,,60,54,56,11,98,49,,51,74,75,,63', -',,,73,,40,,,,57,,,13,50,73,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59', -'12,,,60,54,56,11,72,49,,51,,,,63,,,,72,,40,95,,99,57,94,,13,50,,,,95', -'39,99,46,94,48,101,,47,62,58,,41,61,,98,,,59,12,,,60,54,56,11,98,49', -',51,,,,63,,,,73,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61', -',,,,59,12,,,60,54,56,11,72,49,,51,,,,63,,,,,,40,95,,99,57,94,,13,50', -',,,,39,,46,,48,101,,47,62,58,,41,61,,98,,,59,12,,,60,54,56,11,,49,,51', -',,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,114,,47,62,58,,41,61,,,,', -'59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,', -'48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,271,,,63,,,', -',,40,,,,57,,,13,50,,,,,39,,46,,48,43,,47,62,58,,41,61,44,45,,,59,12', -',,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101', -',47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,277,,,63,,,,,,40,,', -',57,,,13,50,,,,,39,,46,,48,43,,47,62,58,,41,61,44,45,,,59,12,,,60,54', -'56,11,,49,131,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47', -'62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,133,51,,,,63,,,,,,40,,,,57', -',,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11', -',49,,51,136,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58', -',41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,', -',,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,279', -',,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,43,,47,62,58,,41,61,44,45', -',,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46', -',48,43,,47,62,58,,41,61,44,45,,,59,12,,,60,54,56,11,,49,,51,,,,63,,', -',,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,', -'60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,43,,47', -'62,58,,41,61,44,45,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57', -',,13,50,,,,,39,,46,,48,43,,47,62,58,,41,61,44,45,,,59,12,,,60,54,56', -'11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,43,,47,62,58,', -'41,61,44,45,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50', -',,,,39,,46,,48,43,,47,62,58,,41,61,44,45,,,59,12,,,60,54,56,11,,49,', -'51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,43,,47,62,58,,41,61,44', -'45,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39', -',46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,', -',,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,', -',60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101', -',47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57', -',,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11', -',49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41', -'61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39', -',46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,', -',,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,', -',60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101', -',47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57', -',,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11', -',49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41', -'61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39', -',46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,', -',,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,', -',60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101', -',47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57', -',,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11', -',49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41', -'61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39', -',46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,', -',,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,', -',60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101', -',47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57', -',,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11', -',49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41', -'61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39', -',46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,', -',,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,', -',60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101', -',47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,344,,,63,,,,,,40,,', -'173,57,,,13,50,,,,,39,,46,,48,43,,47,62,58,,41,61,44,45,,,59,12,,,60', -'54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,176,193,187,194,48', -'188,196,189,185,183,,178,191,,,,,59,12,197,192,190,54,56,11,,49,,51', -',,,63,,,,,195,177,,,,57,,,13,50,,,,,39,,46,,48,43,,47,62,58,,41,61,44', -'45,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39', -',46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,', -',,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,', -',60,54,56,11,,49,,51,329,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101', -',47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57', -',,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11', -',49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41', -'61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,176', -'193,187,194,48,188,196,189,185,183,,178,191,,,,,59,12,197,192,190,,', -'11,54,56,,,49,,51,63,,,,,195,177,,,,57,,,,50,,13,205,,,,,39,,46,,48', -'101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,', -',,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56', -'11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58', -',41,61,,,,,59,12,,,60,,,11,54,56,,,49,,51,63,,,,,,40,,,,57,,,,50,,13', -'213,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49', -',51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61', -',,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,', -'46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,299,51,,,,63', -',,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12', -',,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101', -',47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57', -',,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11', -',49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41', -'61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39', -',46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,,,,63,', -',,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,', -',60,54,56,11,,49,,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101', -',47,62,58,,41,61,,,,,59,12,,,60,54,56,11,,49,,51,301,,,63,,,,,,40,,', -',57,,,13,50,,,,,39,,46,,48,101,,47,62,58,,41,61,,,,,59,12,,,60,54,56', -'11,,49,298,51,,,,63,,,,,,40,,,,57,,,13,50,,,,,39,,46,,48,101,,47,62', -'58,,41,61,,,,,59,12,,,60,,,11,,,,,,,,63,,,,,,40,72,,217,57,,,,50,,91', -'92,93,88,83,95,,99,,94,,,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76', -'77,79,78,81,82,,74,75,72,,216,,,73,,,,91,92,93,88,83,95,80,99,,94,,', -'84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,,', -',,,73,,72,,,,,233,,,80,91,92,93,88,83,95,,99,,94,,,84,86,85,87,,,,,', -',,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,72,,215,,,73,,,,91', -'92,93,88,83,95,80,99,,94,,,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,', -'76,77,79,78,81,82,,74,75,72,,,,,73,,,,91,92,93,88,83,95,80,99,,94,,', -'84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,72', -',214,,,73,,,,91,92,93,88,83,95,80,99,,94,,,84,86,85,87,,,,,,,,,,,,,', -',,98,,,,90,89,,,76,77,79,78,81,82,,74,75,72,,,,,73,,,,91,92,93,88,83', -'95,80,99,,94,,203,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79', -'78,81,82,,74,75,72,,,,,73,,,,91,92,93,88,83,95,80,99,,94,,,84,86,85', -'87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,72,,,,,73,', -',,91,92,93,88,83,95,80,99,,94,,,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90', -'89,,,76,77,79,78,81,82,,74,75,72,,,,,73,,,,91,92,93,88,83,95,80,99,', -'94,,,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74', -'75,72,,,,,73,,,,91,92,93,88,83,95,80,99,,94,,,84,86,85,87,,,,,,,,,,', -',,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,72,,,,,73,,,,91,92,93,88', -'83,95,80,99,,94,,,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79', -'78,81,82,,74,75,72,,,,,73,,,,91,92,93,88,83,95,80,99,,94,,,84,86,85', -'87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,72,,,,,73,', -',,91,92,93,88,83,95,80,99,,94,,,84,86,85,87,72,,,,,,,,,,,,,,95,98,99', -',94,90,89,,,76,77,79,78,81,82,,74,75,,,,,,73,,,98,,,72,,,,80,76,77,79', -'78,81,82,,74,75,95,,99,,94,73,,,,,,72,,,,80,,,,,,,,,83,95,98,99,,94', -',,84,,76,77,79,78,81,82,,74,75,,,,,,73,,,98,,,,,72,,80,76,77,79,78,81', -'82,,74,75,,83,95,,99,73,94,,,84,,,,72,,80,,,,,,,,,,,83,95,98,99,,94', -',,84,,76,77,79,78,81,82,,74,75,,,,,,73,,,98,,,,,72,,80,76,77,79,78,81', -'82,,74,75,,83,95,,99,73,94,,,84,,,,,,80,,,,,,,,,,,,,98,,,,,72,,,76,77', -'79,78,81,82,,74,75,88,83,95,,99,73,94,,,84,86,85,87,,,80,,,,,,,,,,,', -',98,,,,,72,,,76,77,79,78,81,82,,74,75,88,83,95,,99,73,94,,,84,86,85', -'87,,,80,,,,,,,,,,,,,98,,,,,89,,,76,77,79,78,81,82,,74,75,72,,,,,73,', -',,91,92,93,88,83,95,80,99,,94,,,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90', -'89,,,76,77,79,78,81,82,,74,75,72,,,,,73,,,,91,92,93,88,83,95,80,99,', -'94,,,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74', -'75,72,,,,,73,,,,91,92,93,88,83,95,80,99,,94,,,84,86,85,87,,,,,,,,,,', -',,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,,,,,,73,,72,,,,,243,,,80', -'91,92,93,88,83,95,,99,,94,,,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,', -',76,77,79,78,81,82,,74,75,72,,96,,,73,,,,91,92,93,88,83,95,80,99,,94', -',,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75', -',,,,,73,,72,,,,,,,,80,91,92,93,88,83,95,247,99,,94,,,84,86,85,87,,,', -',,,,,,,,,,,,98,,,,90,89,,,76,77,79,78,81,82,,74,75,72,,,,,73,,,,91,92', -'93,88,83,95,80,99,,94,,,84,86,85,87,,,,,,,,,,,,,,,,98,,,,90,89,,,76', -'77,79,78,81,82,,74,75,,261,193,260,194,73,258,196,262,256,255,,257,259', -',80,,,,,197,192,263,261,193,260,194,,258,196,262,256,255,,257,259,,', -'195,264,,,197,192,263,261,193,260,194,,258,196,262,256,255,,257,259', -',,195,264,,,197,192,263,,,,,,,,,,,,,,,,195,264' ] - racc_action_table = arr = ::Array.new(5652, nil) +'57,59,267,-132,51,237,53,-130,257,-221,-212,257,255,223,242,305,57,59', +'234,241,344,223,290,14,223,57,59,244,230,41,239,48,243,50,45,126,49', +'69,65,125,43,68,46,47,268,-132,66,13,220,-130,67,-221,-212,12,135,288', +'126,133,57,59,125,70,51,135,53,385,133,42,339,234,338,64,60,62,63,61', +'126,70,240,52,125,14,122,354,322,256,70,41,60,48,257,50,45,311,49,69', +'65,60,43,68,46,47,57,59,66,13,57,59,67,324,126,12,57,59,125,219,51,126', +'53,70,79,125,126,126,326,42,125,125,308,64,60,62,63,307,102,14,106,52', +'101,246,304,41,247,48,135,50,45,133,49,69,65,72,43,68,46,47,249,248', +'66,13,105,339,67,338,266,12,264,70,331,332,57,59,333,70,51,223,53,383', +'60,42,210,336,80,64,60,62,63,75,77,76,78,52,74,14,340,57,59,342,289', +'41,186,48,264,50,45,266,49,69,65,264,43,68,46,47,350,351,66,13,288,288', +'67,74,153,12,151,114,361,282,57,59,362,70,51,135,53,381,133,42,281,266', +'127,64,60,62,63,280,365,114,115,52,266,14,114,369,342,371,70,41,372', +'48,373,50,45,374,49,69,65,60,43,68,46,47,375,111,66,13,377,378,67,379', +'264,12,57,59,74,71,51,386,53,70,79,387,388,389,,42,,,,64,60,62,63,,102', +'14,106,52,101,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,105,,67,,', +'12,57,59,,,51,,53,70,79,81,82,,,42,,,80,64,60,62,63,,102,14,106,52,101', +',,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,105,,67,,,12,57,59,,,51', +',53,70,79,81,82,,,42,,,80,64,60,62,63,,102,14,106,52,101,,,41,,48,,50', +'108,,49,69,65,,43,68,,,,,66,13,105,,67,,,12,57,59,,,51,,53,70,79,81', +'82,,,42,,,80,64,60,62,63,,102,14,106,52,101,,,41,,48,,50,108,,49,69', +'65,,43,68,,,,,66,13,105,,67,,,12,57,59,,,51,,53,70,79,,,,,42,,,80,64', +'60,62,63,,102,14,106,52,101,,,41,,48,,50,45,,49,69,65,,43,68,46,47,', +',66,13,105,,67,,,12,57,59,,,51,,53,70,79,,,,,42,,,,64,60,62,63,,102', +'14,106,52,101,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,105,,67,,', +'12,57,59,,,51,,53,70,79,,,,,42,,,,64,60,62,63,,102,14,106,52,101,,,41', +',48,,50,108,,49,69,65,,43,68,,,,,66,13,105,,67,,,12,57,59,,,51,,53,70', +',,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,', +',,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52', +',,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53', +'70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68', +',,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52', +',,,41,,48,,50,121,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53', +'70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68', +',,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52', +',,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53', +'70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68', +',,,,66,13,,,67,,,12,,,,,57,59,,70,51,,53,286,,42,,,,64,60,62,63,,,,', +'52,,14,,,,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57', +'59,,,51,138,53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108', +',49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,140,53,70,,,,,,42,,', +',64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,', +',67,,,12,,,,,57,59,,70,51,,53,143,,42,,,,64,60,62,63,,,,,52,,14,,,,', +',41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53', +'70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68', +',,,,66,13,,,67,,,12,,,,,57,59,,70,51,,53,292,,42,,,,64,60,62,63,,,,', +'52,,14,,,,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,', +',,,57,59,,70,51,,53,294,,42,,,,64,60,62,63,,,,,52,,14,,,,,,41,,48,,50', +'45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42', +',,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13', +',,67,,,12,,,,,57,59,,70,51,,53,360,,42,,,,64,60,62,63,,,,,52,,14,,,', +',,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51', +',53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,45,,49,69,65,,43', +'68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63', +',,14,,52,,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57', +'59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,45,,49', +'69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64', +'60,62,63,,,14,,52,,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,', +'67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48', +',50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70,,', +',,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,45,,49,69,65,,43,68,46,47', +',,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52', +',,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51', +',53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,', +'43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,', +',14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59', +',,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69', +'65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62', +'63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57', +'59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49', +'69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60', +'62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12', +'57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108', +',49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64', +'60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67', +',,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50', +'108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,', +',,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13', +',,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48', +',50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,', +'42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66', +'13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41', +',48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,', +',,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,', +',,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52', +',,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53', +'70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68', +',,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52', +',,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53', +'70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68', +',,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52', +',,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53', +'70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68', +',,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52', +',,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53', +'70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68', +',,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52', +',,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,,,,,57,59,,70', +'51,,53,345,,42,,,185,64,60,62,63,,,,,52,,14,,,,,,41,,48,,50,108,,49', +'69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60', +'62,63,,,14,,52,,,,188,205,199,206,50,200,208,201,197,195,,190,203,,', +',,66,13,209,204,202,,,12,57,59,,,51,,53,70,,,,,207,189,,,,64,60,62,63', +',,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59', +',,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69', +'65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62', +'63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57', +'59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49', +'69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60', +'62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12', +'57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108', +',49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64', +'60,62,63,,,14,,52,,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,', +'67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,217,52,,,,41,', +'48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,', +',,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,,', +',66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52,', +',,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53', +'70,,,,,,42,,,,64,60,62,63,,,14,225,52,,,,41,,48,,50,108,,49,69,65,,43', +'68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14', +',52,,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51', +'314,53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65', +',43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63', +',,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59', +',,51,313,53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49', +'69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60', +'62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12', +'57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108', +',49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64', +'60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67', +',,12,,,,,57,59,,70,51,,53,316,,42,,,,64,60,62,63,,,,,52,,14,,,,,,41', +',48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,', +',,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,', +',,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52', +',,,188,205,199,206,50,200,208,201,197,195,,190,203,,,,,66,13,209,204', +'202,,,12,,,,,,,,70,,,,,207,189,,,,64,60,62,63,79,,,,52,258,,,,98,99', +'100,95,90,102,,106,,101,,,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,', +'83,84,86,85,88,89,,81,82,79,,229,,,80,,,,98,99,100,95,90,102,,106,,101', +'87,,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81', +'82,79,,228,,,80,,,,98,99,100,95,90,102,,106,,101,87,,91,93,92,94,,,', +',,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,,,79,,,80,,245,', +',,98,99,100,95,90,102,,106,87,101,,,91,93,92,94,,,,,,,,,,,,,,,,105,', +',,97,96,,,83,84,86,85,88,89,,81,82,79,,227,,,80,,,,98,99,100,95,90,102', +',106,,101,87,,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85', +'88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102,,106,,101,87,,91,93,92', +'94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,226,,', +'80,,,,98,99,100,95,90,102,,106,,101,87,,91,93,92,94,,,,,,,,,,,,,,,,105', +',,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102', +',106,,101,87,215,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86', +'85,88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102,,106,,101,87,,91,93', +'92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,', +'80,,,,98,99,100,95,90,102,,106,,101,87,,91,93,92,94,,,,,,,,,,,,,,,,105', +',,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102', +',106,,101,87,,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85', +'88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102,,106,,101,87,,91,93,92', +'94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80', +',,,98,99,100,95,90,102,,106,,101,87,,91,93,92,94,,,,,,,,,,,,,,,,105', +',,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102', +',106,,101,87,,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85', +'88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102,,106,79,101,87,,91,93', +'92,94,,,,,,,102,,106,,101,,,,,105,,,,97,96,,79,83,84,86,85,88,89,,81', +'82,105,,,,102,80,106,,101,,86,85,,,,81,82,,,87,,,80,,,,,,,,105,,,,,', +'87,,,,86,85,,,,81,82,79,,,,,80,,,,98,99,100,95,90,102,,106,,101,87,', +'91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79', +',,,,80,,,,98,99,100,95,90,102,262,106,,101,87,,91,93,92,94,,,,,,,,,', +',,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,103,,,80,,,,98,99', +'100,95,90,102,,106,79,101,87,,91,93,92,94,,,,,,,102,,106,,101,,,,,105', +',,,97,96,,,83,84,86,85,88,89,,81,82,105,,,,,80,,79,83,84,86,85,,,,81', +'82,,,87,,102,80,106,79,101,,,,,,,,,,,87,,102,,106,,101,,,,,105,,,,,', +',,83,84,86,85,,,,81,82,105,,,,,80,,79,83,84,86,85,88,89,,81,82,,,87', +',102,80,106,79,101,,,,,,,,,,,87,90,102,,106,,101,,,91,,105,,,,,,,,83', +'84,86,85,88,89,,81,82,105,,,,,80,,79,83,84,86,85,88,89,,81,82,,,87,90', +'102,80,106,79,101,,,91,,,,,,,,87,90,102,,106,,101,,,91,,105,,,,,,,,83', +'84,86,85,88,89,,81,82,105,,,,,80,,79,83,84,86,85,88,89,,81,82,,,87,90', +'102,80,106,,101,,,91,,,,,,,,87,,,,,,,,,,,105,,,,,79,,,83,84,86,85,88', +'89,,81,82,95,90,102,,106,80,101,,,91,93,92,94,,,,,,,87,,,,,,,,,105,', +',,,79,,,83,84,86,85,88,89,,81,82,95,90,102,,106,80,101,,,91,93,92,94', +',,,,,,87,,,,,,,,,105,,,,,96,,,83,84,86,85,88,89,,81,82,79,,,,,80,,,', +'98,99,100,95,90,102,,106,,101,87,,91,93,92,94,,,,,,,,,,,,,,,,105,,,', +'97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102,,106', +',101,87,,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89', +',81,82,79,,,,,80,,,,98,99,100,95,90,102,,106,,101,87,,91,93,92,94,,', +',,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98', +'99,100,95,90,102,,106,,101,87,,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97', +'96,,,83,84,86,85,88,89,,81,82,,276,205,275,206,80,273,208,277,271,270', +',272,274,,,,,,87,209,204,278,276,205,275,206,,273,208,277,271,270,,272', +'274,,,207,279,,,209,204,278,276,205,275,206,,273,208,277,271,270,,272', +'274,,,207,279,,,209,204,278,,,,,,,,,,,,,,,,207,279' ] + racc_action_table = arr = ::Array.new(6168, nil) idx = 0 clist.each do |str| str.split(',', -1).each do |i| @@ -208,194 +227,215 @@ clist = [ end clist = [ -'0,0,185,212,0,289,0,189,189,289,148,130,289,222,152,148,130,212,47,47', -'190,191,222,0,183,107,140,140,152,0,152,0,152,0,0,186,0,0,0,324,0,0', -'0,0,185,189,0,0,189,116,0,355,355,0,152,355,47,355,355,47,107,0,190', -'191,152,152,183,0,189,152,152,0,280,132,355,0,152,186,132,47,355,280', -'355,43,355,355,152,355,355,355,244,355,355,355,355,114,101,355,355,122', -'101,355,4,4,355,277,4,277,4,225,225,43,355,187,220,43,153,187,355,248', -'227,227,355,114,122,4,355,114,219,250,153,4,153,4,153,4,4,223,4,4,4', -'4,4,4,4,4,142,225,4,4,225,106,4,354,354,4,153,354,227,354,354,227,135', -'4,188,135,153,153,188,4,225,153,153,4,254,46,354,4,153,46,211,227,354', -'320,354,320,354,354,153,354,354,354,268,354,354,354,354,270,272,354', -'354,273,209,354,352,352,354,276,352,124,352,352,278,279,354,205,204', -'283,157,284,354,285,286,287,354,202,291,352,354,97,95,66,157,352,157', -'352,157,352,352,64,352,352,352,200,352,352,352,352,305,180,352,352,307', -'44,352,173,173,352,157,173,179,173,314,315,38,352,157,157,157,157,224', -'352,37,157,157,352,323,121,173,352,157,326,327,331,173,332,173,333,173', -'173,157,173,173,173,36,173,173,7,7,7,7,173,173,339,340,173,11,11,173', -'149,11,343,11,228,5,1,173,359,363,365,158,367,173,149,,149,173,149,', -'11,173,,,,158,11,158,11,158,11,11,,11,11,11,,11,11,,149,,,11,11,,,11', -'12,12,11,158,12,,12,,,,11,158,158,158,158,,11,,158,158,11,,,12,11,158', -',,,12,,12,,12,12,158,12,12,12,,12,12,,,,,12,12,,,12,13,13,12,156,13', -',13,,,,12,,,,155,,12,156,,156,12,156,,13,12,,,,155,13,155,13,155,13', -'13,,13,13,13,,13,13,,156,,,13,13,,,13,335,335,13,155,335,,335,156,156', -',13,,,,156,,13,,155,155,13,,,335,13,155,,,,335,,335,,335,335,,335,335', -'335,,335,335,335,335,,,335,335,,,335,322,322,335,154,322,,322,,,,335', -',,,150,,335,154,,154,335,154,,322,335,,,,150,322,150,322,150,322,322', -',322,322,322,,322,322,,154,,,322,322,,,322,177,177,322,150,177,,177', -'154,154,,322,,,,154,,322,,,,322,,,177,322,150,,,,177,,177,,177,177,', -'177,177,177,,177,177,,,,,177,177,,,177,39,39,177,151,39,,39,,,,177,', -',,100,,177,151,,151,177,151,,39,177,,,,100,39,100,39,100,39,39,,39,39', -'39,,39,39,,151,,,39,39,,,39,40,40,39,100,40,,40,,,,39,,,,151,,39,,,', -'39,,,40,39,,,,,40,,40,,40,40,,40,40,40,,40,40,,,,,40,40,,,40,41,41,40', -'102,41,,41,,,,40,,,,,,40,102,,102,40,102,,41,40,,,,,41,,41,,41,41,,41', -'41,41,,41,41,,102,,,41,41,,,41,42,42,41,,42,,42,,,,41,,,,,,41,,,,41', -',,42,41,,,,,42,,42,,42,42,,42,42,42,,42,42,,,,,42,42,,,42,178,178,42', -',178,,178,,,,42,,,,,,42,,,,42,,,178,42,,,,,178,,178,,178,178,,178,178', -'178,,178,178,,,,,178,178,,,178,207,207,178,,207,,207,207,,,178,,,,,', -'178,,,,178,,,207,178,,,,,207,,207,,207,207,,207,207,207,,207,207,207', -'207,,,207,207,,,207,306,306,207,,306,,306,,,,207,,,,,,207,,,,207,,,306', -'207,,,,,306,,306,,306,306,,306,306,306,,306,306,,,,,306,306,,,306,214', -'214,306,,214,,214,214,,,306,,,,,,306,,,,306,,,214,306,,,,,214,,214,', -'214,214,,214,214,214,,214,214,214,214,,,214,214,,,214,49,49,214,,49', -'49,49,,,,214,,,,,,214,,,,214,,,49,214,,,,,49,,49,,49,49,,49,49,49,,49', -'49,,,,,49,49,,,49,50,50,49,,50,50,50,,,,49,,,,,,49,,,,49,,,50,49,,,', -',50,,50,,50,50,,50,50,50,,50,50,,,,,50,50,,,50,51,51,50,,51,,51,51,', -',50,,,,,,50,,,,50,,,51,50,,,,,51,,51,,51,51,,51,51,51,,51,51,,,,,51', -'51,,,51,55,55,51,,55,,55,,,,51,,,,,,51,,,,51,,,55,51,,,,,55,,55,,55', -'55,,55,55,55,,55,55,,,,,55,55,,,55,215,215,55,,215,,215,215,,,55,,,', -',,55,,,,55,,,215,55,,,,,215,,215,,215,215,,215,215,215,,215,215,215', -'215,,,215,215,,,215,65,65,215,,65,,65,,,,215,,,,,,215,,,,215,,,65,215', -',,,,65,,65,,65,65,,65,65,65,,65,65,65,65,,,65,65,,,65,216,216,65,,216', -',216,,,,65,,,,,,65,,,,65,,,216,65,,,,,216,,216,,216,216,,216,216,216', -',216,216,,,,,216,216,,,216,67,67,216,,67,,67,,,,216,,,,,,216,,,,216', -',,67,216,,,,,67,,67,,67,67,,67,67,67,,67,67,67,67,,,67,67,,,67,68,68', -'67,,68,,68,,,,67,,,,,,67,,,,67,,,68,67,,,,,68,,68,,68,68,,68,68,68,', -'68,68,68,68,,,68,68,,,68,69,69,68,,69,,69,,,,68,,,,,,68,,,,68,,,69,68', -',,,,69,,69,,69,69,,69,69,69,,69,69,69,69,,,69,69,,,69,70,70,69,,70,', -'70,,,,69,,,,,,69,,,,69,,,70,69,,,,,70,,70,,70,70,,70,70,70,,70,70,70', -'70,,,70,70,,,70,71,71,70,,71,,71,,,,70,,,,,,70,,,,70,,,71,70,,,,,71', -',71,,71,71,,71,71,71,,71,71,71,71,,,71,71,,,71,72,72,71,,72,,72,,,,71', -',,,,,71,,,,71,,,72,71,,,,,72,,72,,72,72,,72,72,72,,72,72,,,,,72,72,', -',72,73,73,72,,73,,73,,,,72,,,,,,72,,,,72,,,73,72,,,,,73,,73,,73,73,', -'73,73,73,,73,73,,,,,73,73,,,73,74,74,73,,74,,74,,,,73,,,,,,73,,,,73', -',,74,73,,,,,74,,74,,74,74,,74,74,74,,74,74,,,,,74,74,,,74,75,75,74,', -'75,,75,,,,74,,,,,,74,,,,74,,,75,74,,,,,75,,75,,75,75,,75,75,75,,75,75', -',,,,75,75,,,75,76,76,75,,76,,76,,,,75,,,,,,75,,,,75,,,76,75,,,,,76,', -'76,,76,76,,76,76,76,,76,76,,,,,76,76,,,76,77,77,76,,77,,77,,,,76,,,', -',,76,,,,76,,,77,76,,,,,77,,77,,77,77,,77,77,77,,77,77,,,,,77,77,,,77', -'78,78,77,,78,,78,,,,77,,,,,,77,,,,77,,,78,77,,,,,78,,78,,78,78,,78,78', -'78,,78,78,,,,,78,78,,,78,79,79,78,,79,,79,,,,78,,,,,,78,,,,78,,,79,78', -',,,,79,,79,,79,79,,79,79,79,,79,79,,,,,79,79,,,79,80,80,79,,80,,80,', -',,79,,,,,,79,,,,79,,,80,79,,,,,80,,80,,80,80,,80,80,80,,80,80,,,,,80', -'80,,,80,81,81,80,,81,,81,,,,80,,,,,,80,,,,80,,,81,80,,,,,81,,81,,81', -'81,,81,81,81,,81,81,,,,,81,81,,,81,82,82,81,,82,,82,,,,81,,,,,,81,,', -',81,,,82,81,,,,,82,,82,,82,82,,82,82,82,,82,82,,,,,82,82,,,82,83,83', -'82,,83,,83,,,,82,,,,,,82,,,,82,,,83,82,,,,,83,,83,,83,83,,83,83,83,', -'83,83,,,,,83,83,,,83,84,84,83,,84,,84,,,,83,,,,,,83,,,,83,,,84,83,,', -',,84,,84,,84,84,,84,84,84,,84,84,,,,,84,84,,,84,85,85,84,,85,,85,,,', -'84,,,,,,84,,,,84,,,85,84,,,,,85,,85,,85,85,,85,85,85,,85,85,,,,,85,85', -',,85,86,86,85,,86,,86,,,,85,,,,,,85,,,,85,,,86,85,,,,,86,,86,,86,86', -',86,86,86,,86,86,,,,,86,86,,,86,87,87,86,,87,,87,,,,86,,,,,,86,,,,86', -',,87,86,,,,,87,,87,,87,87,,87,87,87,,87,87,,,,,87,87,,,87,88,88,87,', -'88,,88,,,,87,,,,,,87,,,,87,,,88,87,,,,,88,,88,,88,88,,88,88,88,,88,88', -',,,,88,88,,,88,89,89,88,,89,,89,,,,88,,,,,,88,,,,88,,,89,88,,,,,89,', -'89,,89,89,,89,89,89,,89,89,,,,,89,89,,,89,90,90,89,,90,,90,,,,89,,,', -',,89,,,,89,,,90,89,,,,,90,,90,,90,90,,90,90,90,,90,90,,,,,90,90,,,90', -'91,91,90,,91,,91,,,,90,,,,,,90,,,,90,,,91,90,,,,,91,,91,,91,91,,91,91', -'91,,91,91,,,,,91,91,,,91,92,92,91,,92,,92,,,,91,,,,,,91,,,,91,,,92,91', -',,,,92,,92,,92,92,,92,92,92,,92,92,,,,,92,92,,,92,93,93,92,,93,,93,', -',,92,,,,,,92,,,,92,,,93,92,,,,,93,,93,,93,93,,93,93,93,,93,93,,,,,93', -'93,,,93,94,94,93,,94,,94,,,,93,,,,,,93,,,,93,,,94,93,,,,,94,,94,,94', -'94,,94,94,94,,94,94,,,,,94,94,,,94,293,293,94,,293,,293,293,,,94,,,', -',,94,,,94,94,,,293,94,,,,,293,,293,,293,293,,293,293,293,,293,293,293', -'293,,,293,293,,,293,96,96,293,,96,,96,,,,293,,,,,,293,,,,293,,,96,293', -',,,,96,96,96,96,96,96,96,96,96,96,,96,96,,,,,96,96,96,96,96,292,292', -'96,,292,,292,,,,96,,,,,96,96,,,,96,,,292,96,,,,,292,,292,,292,292,,292', -'292,292,,292,292,292,292,,,292,292,,,292,98,98,292,,98,,98,,,,292,,', -',,,292,,,,292,,,98,292,,,,,98,,98,,98,98,,98,98,98,,98,98,,,,,98,98', -',,98,99,99,98,,99,,99,,,,98,,,,,,98,,,,98,,,99,98,,,,,99,,99,,99,99', -',99,99,99,,99,99,,,,,99,99,,,99,281,281,99,,281,,281,281,,,99,,,,,,99', -',,,99,,,281,99,,,,,281,,281,,281,281,,281,281,281,,281,281,,,,,281,281', -',,281,274,274,281,,274,,274,,,,281,,,,,,281,,,,281,,,274,281,,,,,274', -',274,,274,274,,274,274,274,,274,274,,,,,274,274,,,274,267,267,274,,267', -',267,,,,274,,,,,,274,,,,274,,,267,274,,,,,267,,267,,267,267,,267,267', -'267,,267,267,,,,,267,267,,,267,217,217,267,,217,,217,,,,267,,,,,,267', -',,,267,,,217,267,,,,,217,217,217,217,217,217,217,217,217,217,,217,217', -',,,,217,217,217,217,217,,,217,104,104,,,104,,104,217,,,,,217,217,,,', -'217,,,,217,,104,104,,,,,104,,104,,104,104,,104,104,104,,104,104,,,,', -'104,104,,,104,253,253,104,,253,,253,,,,104,,,,,,104,,,,104,,,253,104', -',,,,253,,253,,253,253,,253,253,253,,253,253,,,,,253,253,,,253,252,252', -'253,,252,,252,,,,253,,,,,,253,,,,253,,,252,253,,,,,252,,252,,252,252', -',252,252,252,,252,252,,,,,252,252,,,252,,,252,108,108,,,108,,108,252', -',,,,,252,,,,252,,,,252,,108,108,,,,,108,,108,,108,108,,108,108,108,', -'108,108,,,,,108,108,,,108,218,218,108,,218,,218,,,,108,,,,,,108,,,,108', -',,218,108,,,,,218,,218,,218,218,,218,218,218,,218,218,,,,,218,218,,', -'218,176,176,218,,176,,176,,,,218,,,,,,218,,,,218,,,176,218,,,,,176,', -'176,,176,176,,176,176,176,,176,176,,,,,176,176,,,176,231,231,176,,231', -'231,231,,,,176,,,,,,176,,,,176,,,231,176,,,,,231,,231,,231,231,,231', -'231,231,,231,231,,,,,231,231,,,231,233,233,231,,233,,233,,,,231,,,,', -',231,,,,231,,,233,231,,,,,233,,233,,233,233,,233,233,233,,233,233,,', -',,233,233,,,233,249,249,233,,249,,249,,,,233,,,,,,233,,,,233,,,249,233', -',,,,249,,249,,249,249,,249,249,249,,249,249,,,,,249,249,,,249,115,115', -'249,,115,,115,,,,249,,,,,,249,,,,249,,,115,249,,,,,115,,115,,115,115', -',115,115,115,,115,115,,,,,115,115,,,115,243,243,115,,243,,243,,,,115', -',,,,,115,,,,115,,,243,115,,,,,243,,243,,243,243,,243,243,243,,243,243', -',,,,243,243,,,243,242,242,243,,242,,242,,,,243,,,,,,243,,,,243,,,242', -'243,,,,,242,,242,,242,242,,242,242,242,,242,242,,,,,242,242,,,242,240', -'240,242,,240,,240,,,,242,,,,,,242,,,,242,,,240,242,,,,,240,,240,,240', -'240,,240,240,240,,240,240,,,,,240,240,,,240,235,235,240,,235,,235,235', -',,240,,,,,,240,,,,240,,,235,240,,,,,235,,235,,235,235,,235,235,235,', -'235,235,,,,,235,235,,,235,229,229,235,,229,229,229,,,,235,,,,,,235,', -',,235,,,229,235,,,,,229,,229,,229,229,,229,229,229,,229,229,,,,,229', -'229,,,229,,,229,,,,,,,,229,,,,,,229,113,,113,229,,,,229,,113,113,113', -'113,113,113,,113,,113,,,113,113,113,113,,,,,,,,,,,,,,,,113,,,,113,113', -',,113,113,113,113,113,113,,113,113,112,,112,,,113,,,,112,112,112,112', -'112,112,113,112,,112,,,112,112,112,112,,,,,,,,,,,,,,,,112,,,,112,112', -',,112,112,112,112,112,112,,112,112,,,,,,112,,134,,,,,134,,,112,134,134', -'134,134,134,134,,134,,134,,,134,134,134,134,,,,,,,,,,,,,,,,134,,,,134', -'134,,,134,134,134,134,134,134,,134,134,111,,111,,,134,,,,111,111,111', -'111,111,111,134,111,,111,,,111,111,111,111,,,,,,,,,,,,,,,,111,,,,111', -'111,,,111,111,111,111,111,111,,111,111,138,,,,,111,,,,138,138,138,138', -'138,138,111,138,,138,,,138,138,138,138,,,,,,,,,,,,,,,,138,,,,138,138', -',,138,138,138,138,138,138,,138,138,109,,109,,,138,,,,109,109,109,109', -'109,109,138,109,,109,,,109,109,109,109,,,,,,,,,,,,,,,,109,,,,109,109', -',,109,109,109,109,109,109,,109,109,103,,,,,109,,,,103,103,103,103,103', -'103,109,103,,103,,103,103,103,103,103,,,,,,,,,,,,,,,,103,,,,103,103', -',,103,103,103,103,103,103,,103,103,297,,,,,103,,,,297,297,297,297,297', -'297,103,297,,297,,,297,297,297,297,,,,,,,,,,,,,,,,297,,,,297,297,,,297', -'297,297,297,297,297,,297,297,300,,,,,297,,,,300,300,300,300,300,300', -'297,300,,300,,,300,300,300,300,,,,,,,,,,,,,,,,300,,,,300,300,,,300,300', -'300,300,300,300,,300,300,304,,,,,300,,,,304,304,304,304,304,304,300', -'304,,304,,,304,304,304,304,,,,,,,,,,,,,,,,304,,,,304,304,,,304,304,304', -'304,304,304,,304,304,312,,,,,304,,,,312,312,312,312,312,312,304,312', -',312,,,312,312,312,312,,,,,,,,,,,,,,,,312,,,,312,312,,,312,312,312,312', -'312,312,,312,312,199,,,,,312,,,,199,199,199,199,199,199,312,199,,199', -',,199,199,199,199,,,,,,,,,,,,,,,,199,,,,199,199,,,199,199,199,199,199', -'199,,199,199,313,,,,,199,,,,313,313,313,313,313,313,199,313,,313,,,313', -'313,313,313,,,,,,,,,,,,,,,,313,,,,313,313,,,313,313,313,313,313,313', -',313,313,319,,,,,313,,,,319,319,319,319,319,319,313,319,,319,,,319,319', -'319,319,159,,,,,,,,,,,,,,159,319,159,,159,319,319,,,319,319,319,319', -'319,319,,319,319,,,,,,319,,,159,,,160,,,,319,159,159,159,159,159,159', -',159,159,160,,160,,160,159,,,,,,161,,,,159,,,,,,,,,161,161,160,161,', -'161,,,161,,160,160,160,160,160,160,,160,160,,,,,,160,,,161,,,,,162,', -'160,161,161,161,161,161,161,,161,161,,162,162,,162,161,162,,,162,,,', -'163,,161,,,,,,,,,,,163,163,162,163,,163,,,163,,162,162,162,162,162,162', -',162,162,,,,,,162,,,163,,,,,164,,162,163,163,163,163,163,163,,163,163', -',164,164,,164,163,164,,,164,,,,,,163,,,,,,,,,,,,,164,,,,,165,,,164,164', -'164,164,164,164,,164,164,165,165,165,,165,164,165,,,165,165,165,165', -',,164,,,,,,,,,,,,,165,,,,,166,,,165,165,165,165,165,165,,165,165,166', -'166,166,,166,165,166,,,166,166,166,166,,,165,,,,,,,,,,,,,166,,,,,166', -',,166,166,166,166,166,166,,166,166,167,,,,,166,,,,167,167,167,167,167', -'167,166,167,,167,,,167,167,167,167,,,,,,,,,,,,,,,,167,,,,167,167,,,167', -'167,167,167,167,167,,167,167,168,,,,,167,,,,168,168,168,168,168,168', -'167,168,,168,,,168,168,168,168,,,,,,,,,,,,,,,,168,,,,168,168,,,168,168', -'168,168,168,168,,168,168,169,,,,,168,,,,169,169,169,169,169,169,168', -'169,,169,,,169,169,169,169,,,,,,,,,,,,,,,,169,,,,169,169,,,169,169,169', -'169,169,169,,169,169,,,,,,169,,170,,,,,170,,,169,170,170,170,170,170', -'170,,170,,170,,,170,170,170,170,,,,,,,,,,,,,,,,170,,,,170,170,,,170', -'170,170,170,170,170,,170,170,10,,10,,,170,,,,10,10,10,10,10,10,170,10', -',10,,,10,10,10,10,,,,,,,,,,,,,,,,10,,,,10,10,,,10,10,10,10,10,10,,10', -'10,,,,,,10,,175,,,,,,,,10,175,175,175,175,175,175,175,175,,175,,,175', -'175,175,175,,,,,,,,,,,,,,,,175,,,,175,175,,,175,175,175,175,175,175', -',175,175,129,,,,,175,,,,129,129,129,129,129,129,175,129,,129,,,129,129', -'129,129,,,,,,,,,,,,,,,,129,,,,129,129,,,129,129,129,129,129,129,,129', -'129,,246,246,246,246,129,246,246,246,246,246,,246,246,,129,,,,,246,246', -'246,198,198,198,198,,198,198,198,198,198,,198,198,,,246,246,,,198,198', -'198,251,251,251,251,,251,251,251,251,251,,251,251,,,198,198,,,251,251', -'251,,,,,,,,,,,,,,,,251,251' ] - racc_action_check = arr = ::Array.new(5652, nil) +'0,0,198,197,0,129,0,195,224,202,203,295,151,114,137,234,237,237,123', +'137,295,151,224,0,234,49,49,139,121,0,129,0,139,0,0,48,0,0,0,48,0,0', +'0,0,198,197,0,0,114,195,0,202,203,0,237,254,121,237,372,372,121,0,372', +'49,372,372,49,0,336,128,336,0,0,0,0,0,304,237,131,0,304,372,45,304,259', +'160,49,372,237,372,160,372,372,240,372,372,372,49,372,372,372,372,60', +'60,372,372,201,201,372,263,45,372,5,5,45,113,5,108,5,372,162,108,199', +'200,265,372,199,200,236,372,372,372,372,235,162,5,162,372,162,142,232', +'5,142,5,201,5,5,201,5,5,5,5,5,5,5,5,147,147,5,5,162,292,5,292,269,5', +'231,201,283,285,371,371,287,5,371,288,371,371,201,5,104,291,162,5,5', +'5,5,8,8,8,8,5,154,371,293,239,239,294,223,371,102,371,298,371,371,299', +'371,371,371,300,371,371,371,371,301,302,371,371,221,306,371,73,71,371', +'61,217,319,216,369,369,321,371,369,239,369,369,239,371,214,323,46,371', +'371,371,371,212,330,331,40,371,192,369,39,339,340,342,239,369,343,369', +'347,369,369,348,369,369,369,239,369,369,369,369,349,38,369,369,355,356', +'369,359,191,369,185,185,6,1,185,376,185,369,168,380,382,384,,369,,,', +'369,369,369,369,,168,185,168,369,168,,,185,,185,,185,185,,185,185,185', +',185,185,,,,,185,185,168,,185,,,185,12,12,,,12,,12,185,167,168,168,', +',185,,,168,185,185,185,185,,167,12,167,185,167,,,12,,12,,12,12,,12,12', +'12,,12,12,,,,,12,12,167,,12,,,12,13,13,,,13,,13,12,166,167,167,,,12', +',,167,12,12,12,12,,166,13,166,12,166,,,13,,13,,13,13,,13,13,13,,13,13', +',,,,13,13,166,,13,,,13,14,14,,,14,,14,13,163,166,166,,,13,,,166,13,13', +'13,13,,163,14,163,13,163,,,14,,14,,14,14,,14,14,14,,14,14,,,,,14,14', +'163,,14,,,14,351,351,,,351,,351,14,161,,,,,14,,,163,14,14,14,14,,161', +'351,161,14,161,,,351,,351,,351,351,,351,351,351,,351,351,351,351,,,351', +'351,161,,351,,,351,338,338,,,338,,338,351,109,,,,,351,,,,351,351,351', +'351,,109,338,109,351,109,,,338,,338,,338,338,,338,338,338,,338,338,', +',,,338,338,109,,338,,,338,188,188,,,188,,188,338,107,,,,,338,,,,338', +'338,338,338,,107,188,107,338,107,,,188,,188,,188,188,,188,188,188,,188', +'188,,,,,188,188,107,,188,,,188,41,41,,,41,,41,188,,,,,,188,,,,188,188', +'188,188,,,41,,188,,,,41,,41,,41,41,,41,41,41,,41,41,,,,,41,41,,,41,', +',41,42,42,,,42,,42,41,,,,,,41,,,,41,41,41,41,,,42,,41,,,,42,,42,,42', +'42,,42,42,42,,42,42,,,,,42,42,,,42,,,42,43,43,,,43,,43,42,,,,,,42,,', +',42,42,42,42,,,43,,42,,,,43,,43,,43,43,,43,43,43,,43,43,,,,,43,43,,', +'43,,,43,44,44,,,44,,44,43,,,,,,43,,,,43,43,43,43,,,44,,43,,,,44,,44', +',44,44,,44,44,44,,44,44,,,,,44,44,,,44,,,44,189,189,,,189,,189,44,,', +',,,44,,,,44,44,44,44,,,189,,44,,,,189,,189,,189,189,,189,189,189,,189', +'189,,,,,189,189,,,189,,,189,190,190,,,190,,190,189,,,,,,189,,,,189,189', +'189,189,,,190,,189,,,,190,,190,,190,190,,190,190,190,,190,190,,,,,190', +'190,,,190,,,190,322,322,,,322,,322,190,,,,,,190,,,,190,190,190,190,', +',322,,190,,,,322,,322,,322,322,,322,322,322,,322,322,,,,,322,322,,,322', +',,322,,,,,219,219,,322,219,,219,219,,322,,,,322,322,322,322,,,,,322', +',219,,,,,,219,,219,,219,219,,219,219,219,,219,219,219,219,,,219,219', +',,219,,,219,51,51,,,51,51,51,219,,,,,,219,,,,219,219,219,219,,,51,,219', +',,,51,,51,,51,51,,51,51,51,,51,51,,,,,51,51,,,51,,,51,52,52,,,52,52', +'52,51,,,,,,51,,,,51,51,51,51,,,52,,51,,,,52,,52,,52,52,,52,52,52,,52', +'52,,,,,52,52,,,52,,,52,,,,,53,53,,52,53,,53,53,,52,,,,52,52,52,52,,', +',,52,,53,,,,,,53,,53,,53,53,,53,53,53,,53,53,,,,,53,53,,,53,,,53,58', +'58,,,58,,58,53,,,,,,53,,,,53,53,53,53,,,58,,53,,,,58,,58,,58,58,,58', +'58,58,,58,58,,,,,58,58,,,58,,,58,,,,,226,226,,58,226,,226,226,,58,,', +',58,58,58,58,,,,,58,,226,,,,,,226,,226,,226,226,,226,226,226,,226,226', +'226,226,,,226,226,,,226,,,226,,,,,227,227,,226,227,,227,227,,226,,,', +'226,226,226,226,,,,,226,,227,,,,,,227,,227,,227,227,,227,227,227,,227', +'227,227,227,,,227,227,,,227,,,227,63,63,,,63,,63,227,,,,,,227,,,,227', +'227,227,227,,,63,,227,,,,63,,63,,63,63,,63,63,63,,63,63,,,,,63,63,,', +'63,,,63,,,,,308,308,,63,308,,308,308,,63,,,,63,63,63,63,,,,,63,,308', +',,,,,308,,308,,308,308,,308,308,308,,308,308,308,308,,,308,308,,,308', +',,308,72,72,,,72,,72,308,,,,,,308,,,,308,308,308,308,,,72,,308,,,,72', +',72,,72,72,,72,72,72,,72,72,72,72,,,72,72,,,72,,,72,307,307,,,307,,307', +'72,,,,,,72,,,,72,72,72,72,,,307,,72,,,,307,,307,,307,307,,307,307,307', +',307,307,307,307,,,307,307,,,307,,,307,74,74,,,74,,74,307,,,,,,307,', +',,307,307,307,307,,,74,,307,,,,74,,74,,74,74,,74,74,74,,74,74,74,74', +',,74,74,,,74,,,74,75,75,,,75,,75,74,,,,,,74,,,,74,74,74,74,,,75,,74', +',,,75,,75,,75,75,,75,75,75,,75,75,75,75,,,75,75,,,75,,,75,76,76,,,76', +',76,75,,,,,,75,,,,75,75,75,75,,,76,,75,,,,76,,76,,76,76,,76,76,76,,76', +'76,76,76,,,76,76,,,76,,,76,77,77,,,77,,77,76,,,,,,76,,,,76,76,76,76', +',,77,,76,,,,77,,77,,77,77,,77,77,77,,77,77,77,77,,,77,77,,,77,,,77,78', +'78,,,78,,78,77,,,,,,77,,,,77,77,77,77,,,78,,77,,,,78,,78,,78,78,,78', +'78,78,,78,78,78,78,,,78,78,,,78,,,78,79,79,,,79,,79,78,,,,,,78,,,,78', +'78,78,78,,,79,,78,,,,79,,79,,79,79,,79,79,79,,79,79,,,,,79,79,,,79,', +',79,80,80,,,80,,80,79,,,,,,79,,,,79,79,79,79,,,80,,79,,,,80,,80,,80', +'80,,80,80,80,,80,80,,,,,80,80,,,80,,,80,81,81,,,81,,81,80,,,,,,80,,', +',80,80,80,80,,,81,,80,,,,81,,81,,81,81,,81,81,81,,81,81,,,,,81,81,,', +'81,,,81,82,82,,,82,,82,81,,,,,,81,,,,81,81,81,81,,,82,,81,,,,82,,82', +',82,82,,82,82,82,,82,82,,,,,82,82,,,82,,,82,83,83,,,83,,83,82,,,,,,82', +',,,82,82,82,82,,,83,,82,,,,83,,83,,83,83,,83,83,83,,83,83,,,,,83,83', +',,83,,,83,84,84,,,84,,84,83,,,,,,83,,,,83,83,83,83,,,84,,83,,,,84,,84', +',84,84,,84,84,84,,84,84,,,,,84,84,,,84,,,84,85,85,,,85,,85,84,,,,,,84', +',,,84,84,84,84,,,85,,84,,,,85,,85,,85,85,,85,85,85,,85,85,,,,,85,85', +',,85,,,85,86,86,,,86,,86,85,,,,,,85,,,,85,85,85,85,,,86,,85,,,,86,,86', +',86,86,,86,86,86,,86,86,,,,,86,86,,,86,,,86,87,87,,,87,,87,86,,,,,,86', +',,,86,86,86,86,,,87,,86,,,,87,,87,,87,87,,87,87,87,,87,87,,,,,87,87', +',,87,,,87,88,88,,,88,,88,87,,,,,,87,,,,87,87,87,87,,,88,,87,,,,88,,88', +',88,88,,88,88,88,,88,88,,,,,88,88,,,88,,,88,89,89,,,89,,89,88,,,,,,88', +',,,88,88,88,88,,,89,,88,,,,89,,89,,89,89,,89,89,89,,89,89,,,,,89,89', +',,89,,,89,90,90,,,90,,90,89,,,,,,89,,,,89,89,89,89,,,90,,89,,,,90,,90', +',90,90,,90,90,90,,90,90,,,,,90,90,,,90,,,90,91,91,,,91,,91,90,,,,,,90', +',,,90,90,90,90,,,91,,90,,,,91,,91,,91,91,,91,91,91,,91,91,,,,,91,91', +',,91,,,91,92,92,,,92,,92,91,,,,,,91,,,,91,91,91,91,,,92,,91,,,,92,,92', +',92,92,,92,92,92,,92,92,,,,,92,92,,,92,,,92,93,93,,,93,,93,92,,,,,,92', +',,,92,92,92,92,,,93,,92,,,,93,,93,,93,93,,93,93,93,,93,93,,,,,93,93', +',,93,,,93,94,94,,,94,,94,93,,,,,,93,,,,93,93,93,93,,,94,,93,,,,94,,94', +',94,94,,94,94,94,,94,94,,,,,94,94,,,94,,,94,95,95,,,95,,95,94,,,,,,94', +',,,94,94,94,94,,,95,,94,,,,95,,95,,95,95,,95,95,95,,95,95,,,,,95,95', +',,95,,,95,96,96,,,96,,96,95,,,,,,95,,,,95,95,95,95,,,96,,95,,,,96,,96', +',96,96,,96,96,96,,96,96,,,,,96,96,,,96,,,96,97,97,,,97,,97,96,,,,,,96', +',,,96,96,96,96,,,97,,96,,,,97,,97,,97,97,,97,97,97,,97,97,,,,,97,97', +',,97,,,97,98,98,,,98,,98,97,,,,,,97,,,,97,97,97,97,,,98,,97,,,,98,,98', +',98,98,,98,98,98,,98,98,,,,,98,98,,,98,,,98,99,99,,,99,,99,98,,,,,,98', +',,,98,98,98,98,,,99,,98,,,,99,,99,,99,99,,99,99,99,,99,99,,,,,99,99', +',,99,,,99,100,100,,,100,,100,99,,,,,,99,,,,99,99,99,99,,,100,,99,,,', +'100,,100,,100,100,,100,100,100,,100,100,,,,,100,100,,,100,,,100,101', +'101,,,101,,101,100,,,,,,100,,,,100,100,100,100,,,101,,100,,,,101,,101', +',101,101,,101,101,101,,101,101,,,,,101,101,,,101,,,101,,,,,296,296,', +'101,296,,296,296,,101,,,101,101,101,101,101,,,,,101,,296,,,,,,296,,296', +',296,296,,296,296,296,,296,296,,,,,296,296,,,296,,,296,103,103,,,103', +',103,296,,,,,,296,,,,296,296,296,296,,,103,,296,,,,103,103,103,103,103', +'103,103,103,103,103,,103,103,,,,,103,103,103,103,103,,,103,289,289,', +',289,,289,103,,,,,103,103,,,,103,103,103,103,,,289,,103,,,,289,,289', +',289,289,,289,289,289,,289,289,,,,,289,289,,,289,,,289,105,105,,,105', +',105,289,,,,,,289,,,,289,289,289,289,,,105,,289,,,,105,,105,,105,105', +',105,105,105,,105,105,,,,,105,105,,,105,,,105,106,106,,,106,,106,105', +',,,,,105,,,,105,105,105,105,,,106,,105,,,,106,,106,,106,106,,106,106', +'106,,106,106,,,,,106,106,,,106,,,106,282,282,,,282,,282,106,,,,,,106', +',,,106,106,106,106,,,282,,106,,,,282,,282,,282,282,,282,282,282,,282', +'282,,,,,282,282,,,282,,,282,268,268,,,268,,268,282,,,,,,282,,,,282,282', +'282,282,,,268,,282,,,,268,,268,,268,268,,268,268,268,,268,268,,,,,268', +'268,,,268,,,268,267,267,,,267,,267,268,,,,,,268,,,,268,268,268,268,', +',267,,268,,,,267,,267,,267,267,,267,267,267,,267,267,,,,,267,267,,,267', +',,267,150,150,,,150,,150,267,,,,,,267,,,,267,267,267,267,,,150,,267', +',,,150,,150,,150,150,,150,150,150,,150,150,150,150,,,150,150,,,150,', +',150,111,111,,,111,,111,150,,,,,,150,,,,150,150,150,150,,,111,111,150', +',,,111,,111,,111,111,,111,111,111,,111,111,,,,,111,111,,,111,,,111,264', +'264,,,264,,264,111,,,,,,111,,,,111,111,111,111,,,264,,111,,,,264,,264', +',264,264,,264,264,264,,264,264,,,,,264,264,,,264,,,264,258,258,,,258', +',258,264,,,,,,264,,,,264,264,264,264,,,258,,264,,,,258,,258,,258,258', +',258,258,258,,258,258,,,,,258,258,,,258,,,258,115,115,,,115,,115,258', +',,,,,258,,,,258,258,258,258,,,115,115,258,,,,115,,115,,115,115,,115', +'115,115,,115,115,,,,,115,115,,,115,,,115,228,228,,,228,,228,115,,,,', +',115,,,,115,115,115,115,,,228,,115,,,,228,,228,,228,228,,228,228,228', +',228,228,,,,,228,228,,,228,,,228,243,243,,,243,243,243,228,,,,,,228', +',,,228,228,228,228,,,243,,228,,,,243,,243,,243,243,,243,243,243,,243', +'243,,,,,243,243,,,243,,,243,230,230,,,230,,230,243,,,,,,243,,,,243,243', +'243,243,,,230,,243,,,,230,,230,,230,230,,230,230,230,,230,230,,,,,230', +'230,,,230,,,230,241,241,,,241,241,241,230,,,,,,230,,,,230,230,230,230', +',,241,,230,,,,241,,241,,241,241,,241,241,241,,241,241,,,,,241,241,,', +'241,,,241,257,257,,,257,,257,241,,,,,,241,,,,241,241,241,241,,,257,', +'241,,,,257,,257,,257,257,,257,257,257,,257,257,,,,,257,257,,,257,,,257', +'122,122,,,122,,122,257,,,,,,257,,,,257,257,257,257,,,122,,257,,,,122', +',122,,122,122,,122,122,122,,122,122,,,,,122,122,,,122,,,122,252,252', +',,252,,252,122,,,,,,122,,,,122,122,122,122,,,252,,122,,,,252,,252,,252', +'252,,252,252,252,,252,252,,,,,252,252,,,252,,,252,,,,,247,247,,252,247', +',247,247,,252,,,,252,252,252,252,,,,,252,,247,,,,,,247,,247,,247,247', +',247,247,247,,247,247,,,,,247,247,,,247,,,247,245,245,,,245,,245,247', +',,,,,247,,,,247,247,247,247,,,245,,247,,,,245,,245,,245,245,,245,245', +'245,,245,245,,,,,245,245,,,245,,,245,229,229,,,229,,229,245,,,,,,245', +',,,245,245,245,245,,,229,,245,,,,229,229,229,229,229,229,229,229,229', +'229,,229,229,,,,,229,229,229,229,229,,,229,,,,,,,,229,,,,,229,229,,', +',229,229,229,229,182,,,,229,182,,,,182,182,182,182,182,182,,182,,182', +',,182,182,182,182,,,,,,,,,,,,,,,,182,,,,182,182,,,182,182,182,182,182', +'182,,182,182,120,,120,,,182,,,,120,120,120,120,120,120,,120,,120,182', +',120,120,120,120,,,,,,,,,,,,,,,,120,,,,120,120,,,120,120,120,120,120', +'120,,120,120,119,,119,,,120,,,,119,119,119,119,119,119,,119,,119,120', +',119,119,119,119,,,,,,,,,,,,,,,,119,,,,119,119,,,119,119,119,119,119', +'119,,119,119,,,141,,,119,,141,,,,141,141,141,141,141,141,,141,119,141', +',,141,141,141,141,,,,,,,,,,,,,,,,141,,,,141,141,,,141,141,141,141,141', +'141,,141,141,118,,118,,,141,,,,118,118,118,118,118,118,,118,,118,141', +',118,118,118,118,,,,,,,,,,,,,,,,118,,,,118,118,,,118,118,118,118,118', +'118,,118,118,145,,,,,118,,,,145,145,145,145,145,145,,145,,145,118,,145', +'145,145,145,,,,,,,,,,,,,,,,145,,,,145,145,,,145,145,145,145,145,145', +',145,145,116,,116,,,145,,,,116,116,116,116,116,116,,116,,116,145,,116', +'116,116,116,,,,,,,,,,,,,,,,116,,,,116,116,,,116,116,116,116,116,116', +',116,116,110,,,,,116,,,,110,110,110,110,110,110,,110,,110,116,110,110', +'110,110,110,,,,,,,,,,,,,,,,110,,,,110,110,,,110,110,110,110,110,110', +',110,110,312,,,,,110,,,,312,312,312,312,312,312,,312,,312,110,,312,312', +'312,312,,,,,,,,,,,,,,,,312,,,,312,312,,,312,312,312,312,312,312,,312', +'312,152,,,,,312,,,,152,152,152,152,152,152,,152,,152,312,,152,152,152', +'152,,,,,,,,,,,,,,,,152,,,,152,152,,,152,152,152,152,152,152,,152,152', +'315,,,,,152,,,,315,315,315,315,315,315,,315,,315,152,,315,315,315,315', +',,,,,,,,,,,,,,,315,,,,315,315,,,315,315,315,315,315,315,,315,315,320', +',,,,315,,,,320,320,320,320,320,320,,320,,320,315,,320,320,320,320,,', +',,,,,,,,,,,,,320,,,,320,320,,,320,320,320,320,320,320,,320,320,211,', +',,,320,,,,211,211,211,211,211,211,,211,,211,320,,211,211,211,211,,,', +',,,,,,,,,,,,211,,,,211,211,,,211,211,211,211,211,211,,211,211,328,,', +',,211,,,,328,328,328,328,328,328,,328,,328,211,,328,328,328,328,,,,', +',,,,,,,,,,,328,,,,328,328,,,328,328,328,328,328,328,,328,328,329,,,', +',328,,,,329,329,329,329,329,329,,329,164,329,328,,329,329,329,329,,', +',,,,164,,164,,164,,,,,329,,,,329,329,,165,329,329,329,329,329,329,,329', +'329,164,,,,165,329,165,,165,,164,164,,,,164,164,,,329,,,164,,,,,,,,165', +',,,,,164,,,,165,165,,,,165,165,335,,,,,165,,,,335,335,335,335,335,335', +',335,,335,165,,335,335,335,335,,,,,,,,,,,,,,,,335,,,,335,335,,,335,335', +'335,335,335,335,,335,335,187,,,,,335,,,,187,187,187,187,187,187,187', +'187,,187,335,,187,187,187,187,,,,,,,,,,,,,,,,187,,,,187,187,,,187,187', +'187,187,187,187,,187,187,11,,11,,,187,,,,11,11,11,11,11,11,,11,169,11', +'187,,11,11,11,11,,,,,,,169,,169,,169,,,,,11,,,,11,11,,,11,11,11,11,11', +'11,,11,11,169,,,,,11,,170,169,169,169,169,,,,169,169,,,11,,170,169,170', +'171,170,,,,,,,,,,,169,,171,,171,,171,,,,,170,,,,,,,,170,170,170,170', +',,,170,170,171,,,,,170,,172,171,171,171,171,171,171,,171,171,,,170,', +'172,171,172,173,172,,,,,,,,,,,171,173,173,,173,,173,,,173,,172,,,,,', +',,172,172,172,172,172,172,,172,172,173,,,,,172,,174,173,173,173,173', +'173,173,,173,173,,,172,174,174,173,174,175,174,,,174,,,,,,,,173,175', +'175,,175,,175,,,175,,174,,,,,,,,174,174,174,174,174,174,,174,174,175', +',,,,174,,176,175,175,175,175,175,175,,175,175,,,174,176,176,175,176', +',176,,,176,,,,,,,,175,,,,,,,,,,,176,,,,,177,,,176,176,176,176,176,176', +',176,176,177,177,177,,177,176,177,,,177,177,177,177,,,,,,,176,,,,,,', +',,177,,,,,178,,,177,177,177,177,177,177,,177,177,178,178,178,,178,177', +'178,,,178,178,178,178,,,,,,,177,,,,,,,,,178,,,,,178,,,178,178,178,178', +'178,178,,178,178,179,,,,,178,,,,179,179,179,179,179,179,,179,,179,178', +',179,179,179,179,,,,,,,,,,,,,,,,179,,,,179,179,,,179,179,179,179,179', +'179,,179,179,180,,,,,179,,,,180,180,180,180,180,180,,180,,180,179,,180', +'180,180,180,,,,,,,,,,,,,,,,180,,,,180,180,,,180,180,180,180,180,180', +',180,180,181,,,,,180,,,,181,181,181,181,181,181,,181,,181,180,,181,181', +'181,181,,,,,,,,,,,,,,,,181,,,,181,181,,,181,181,181,181,181,181,,181', +'181,136,,,,,181,,,,136,136,136,136,136,136,,136,,136,181,,136,136,136', +'136,,,,,,,,,,,,,,,,136,,,,136,136,,,136,136,136,136,136,136,,136,136', +',261,261,261,261,136,261,261,261,261,261,,261,261,,,,,,136,261,261,261', +'266,266,266,266,,266,266,266,266,266,,266,266,,,261,261,,,266,266,266', +'210,210,210,210,,210,210,210,210,210,,210,210,,,266,266,,,210,210,210', +',,,,,,,,,,,,,,,210,210' ] + racc_action_check = arr = ::Array.new(6168, nil) idx = 0 clist.each do |str| str.split(',', -1).each do |i| @@ -405,401 +445,418 @@ clist = [ end racc_action_pointer = [ - -2, 315, nil, nil, 100, 302, nil, 233, nil, nil, - 5384, 304, 355, 406, nil, nil, nil, nil, nil, nil, + -2, 285, nil, nil, nil, 110, 272, nil, 123, nil, + nil, 5447, 334, 388, 442, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, 269, 202, 239, 610, - 661, 712, 763, 75, 209, nil, 139, 16, nil, 1018, - 1069, 1120, nil, nil, nil, 1171, nil, nil, nil, nil, - nil, nil, nil, nil, 239, 1273, 219, 1375, 1426, 1477, - 1528, 1579, 1630, 1681, 1732, 1783, 1834, 1885, 1936, 1987, - 2038, 2089, 2140, 2191, 2242, 2293, 2344, 2395, 2446, 2497, - 2548, 2599, 2650, 2701, 2752, 190, 2854, 221, 2956, 3007, - 620, 60, 711, 4395, 3265, nil, 143, -10, 3421, 4338, - nil, 4224, 4103, 4046, 87, 3727, 24, nil, nil, nil, - nil, 252, 87, nil, 189, nil, nil, nil, nil, 5505, - 4, nil, 66, nil, 4167, 153, nil, nil, 4281, nil, - 22, nil, 134, nil, nil, nil, nil, nil, 3, 303, - 518, 609, 8, 110, 507, 416, 405, 212, 314, 4819, - 4862, 4887, 4932, 4957, 5002, 5047, 5092, 5149, 5206, 5263, - 5327, nil, nil, 253, nil, 5448, 3523, 559, 814, 219, - 237, nil, nil, 13, nil, -9, 24, 77, 128, 5, - 9, 10, nil, nil, nil, nil, nil, nil, 5560, 4680, - 196, nil, 202, nil, 204, 145, nil, 865, nil, 190, - nil, 165, -9, nil, 967, 1222, 1324, 3211, 3472, 87, - 77, nil, -13, 129, 262, 107, nil, 118, 273, 3982, - nil, 3574, nil, 3625, nil, 3931, nil, nil, nil, nil, - 3880, nil, 3829, 3778, 78, nil, 5538, nil, 110, 3676, - 120, 5582, 3367, 3316, 162, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, 3160, 166, nil, - 188, nil, 128, 166, 3109, nil, 198, 73, 203, 181, - 60, 3058, nil, 176, 207, 180, 213, 215, nil, -31, - nil, 214, 2905, 2803, nil, nil, nil, 4452, nil, nil, - 4509, nil, nil, nil, 4566, 239, 916, 240, nil, nil, - nil, nil, 4623, 4737, 253, 193, nil, nil, nil, 4794, - 151, nil, 508, 268, 7, nil, 273, 274, nil, nil, - nil, 274, 276, 278, nil, 457, nil, nil, nil, 277, - 295, nil, nil, 302, nil, nil, nil, nil, nil, nil, - nil, nil, 202, nil, 151, 49, nil, nil, nil, 308, - nil, nil, nil, 309, nil, 310, nil, 312, nil, nil, - nil, nil, nil ] + nil, nil, nil, nil, nil, nil, nil, nil, 248, 182, + 223, 658, 712, 766, 820, 74, 196, nil, -1, 23, + nil, 1094, 1148, 1206, nil, nil, nil, nil, 1260, nil, + 100, 199, nil, 1430, nil, nil, nil, nil, nil, nil, + nil, 222, 1542, 209, 1650, 1704, 1758, 1812, 1866, 1920, + 1974, 2028, 2082, 2136, 2190, 2244, 2298, 2352, 2406, 2460, + 2514, 2568, 2622, 2676, 2730, 2784, 2838, 2892, 2946, 3000, + 3054, 3108, 160, 3220, 172, 3328, 3382, 608, 81, 554, + 4830, 3652, nil, 107, -22, 3814, 4773, nil, 4659, 4543, + 4486, 20, 4138, -7, nil, nil, nil, nil, 44, -7, + nil, 58, nil, nil, nil, nil, 6021, 7, nil, 20, + nil, 4602, 130, nil, nil, 4716, nil, 152, nil, nil, + 3598, -14, 4944, nil, 180, nil, nil, nil, nil, nil, + 78, 500, 114, 446, 5246, 5276, 392, 338, 284, 5464, + 5511, 5528, 5575, 5592, 5639, 5656, 5703, 5748, 5793, 5850, + 5907, 5964, 4429, nil, nil, 280, nil, 5390, 604, 874, + 928, 239, 238, nil, nil, -4, nil, -8, -9, 86, + 87, 104, -2, -1, nil, nil, nil, nil, nil, nil, + 6098, 5115, 198, nil, 215, nil, 215, 155, nil, 1040, + nil, 206, nil, 183, -4, nil, 1318, 1376, 3868, 4358, + 3976, 125, 103, nil, -11, 125, 120, 14, nil, 193, + 53, 4030, nil, 3922, nil, 4304, nil, 4250, nil, nil, + nil, nil, 4192, nil, 43, nil, nil, 4084, 3760, 72, + nil, 6054, nil, 100, 3706, 115, 6076, 3544, 3490, 152, + nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, 3436, 142, nil, 160, nil, 102, 140, 3274, + nil, 172, 129, 185, 165, -1, 3166, nil, 161, 193, + 168, 205, 207, nil, 40, nil, 207, 1596, 1488, nil, + nil, nil, 4887, nil, nil, 5001, nil, nil, nil, 200, + 5058, 221, 982, 227, nil, nil, nil, nil, 5172, 5229, + 237, 177, nil, nil, nil, 5333, 36, nil, 550, 245, + 222, nil, 247, 250, nil, nil, nil, 251, 254, 263, + nil, 496, nil, nil, nil, 250, 268, nil, nil, 270, + nil, nil, nil, nil, nil, nil, nil, nil, nil, 226, + nil, 168, 56, nil, nil, nil, 278, nil, nil, nil, + 282, nil, 283, nil, 284, nil, nil, nil, nil, nil ] racc_action_default = [ - -212, -213, -1, -2, -3, -4, -7, -9, -10, -15, - -105, -213, -213, -213, -44, -45, -46, -47, -48, -49, + -223, -224, -1, -2, -3, -4, -5, -8, -10, -11, + -16, -107, -224, -224, -224, -45, -46, -47, -48, -49, -50, -51, -52, -53, -54, -55, -56, -57, -58, -59, - -60, -61, -62, -63, -64, -65, -70, -71, -75, -213, - -213, -213, -213, -213, -116, -118, -213, -213, -163, -213, - -213, -213, -176, -177, -178, -213, -180, -187, -188, -189, - -190, -191, -192, -193, -213, -213, -6, -213, -213, -213, - -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, - -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, - -213, -213, -213, -213, -213, -213, -125, -120, -212, -212, - -27, -213, -34, -213, -213, -72, -213, -213, -213, -213, - -82, -213, -213, -213, -213, -213, -212, -135, -154, -155, - -117, -212, -212, -144, -146, -147, -148, -149, -150, -42, - -213, -166, -213, -169, -213, -213, -172, -173, -184, -179, - -213, 373, -5, -8, -11, -12, -13, -14, -213, -17, - -18, -19, -20, -21, -22, -23, -24, -25, -26, -28, - -29, -30, -31, -32, -33, -35, -36, -37, -38, -39, - -213, -40, -100, -213, -76, -213, -205, -211, -199, -196, - -194, -114, -126, -188, -129, -192, -213, -202, -200, -208, - -190, -191, -198, -203, -204, -206, -207, -209, -125, -124, - -213, -123, -213, -41, -194, -67, -77, -213, -80, -194, - -159, -162, -213, -74, -213, -213, -213, -125, -213, -196, - -212, -156, -213, -213, -213, -213, -152, -213, -213, -213, - -164, -213, -167, -213, -170, -213, -181, -182, -183, -185, - -213, -16, -213, -213, -194, -102, -125, -113, -213, -197, - -213, -195, -213, -213, -194, -128, -130, -199, -200, -201, - -202, -205, -208, -210, -211, -121, -122, -195, -213, -69, - -213, -79, -213, -195, -213, -73, -213, -85, -213, -91, - -213, -213, -95, -196, -194, -196, -213, -213, -138, -213, - -157, -194, -212, -213, -145, -153, -151, -43, -165, -168, - -175, -171, -174, -186, -104, -213, -195, -194, -108, -115, - -109, -127, -131, -132, -213, -66, -78, -81, -160, -161, - -85, -84, -213, -213, -91, -90, -213, -213, -99, -94, - -96, -213, -213, -213, -111, -212, -139, -140, -141, -213, - -213, -136, -137, -213, -143, -101, -103, -112, -119, -68, - -83, -86, -213, -89, -213, -213, -106, -107, -110, -213, - -158, -133, -142, -213, -88, -213, -93, -213, -98, -134, - -87, -92, -97 ] + -60, -61, -62, -63, -64, -65, -66, -67, -72, -73, + -77, -224, -224, -224, -224, -224, -118, -120, -224, -224, + -165, -224, -224, -224, -178, -179, -180, -181, -224, -183, + -224, -193, -196, -224, -198, -199, -200, -201, -202, -203, + -204, -224, -224, -7, -224, -224, -224, -224, -224, -224, + -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, + -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, + -224, -224, -224, -127, -122, -223, -223, -28, -224, -35, + -224, -224, -74, -224, -224, -224, -224, -84, -224, -224, + -224, -224, -224, -223, -137, -156, -157, -119, -223, -223, + -146, -148, -149, -150, -151, -152, -43, -224, -168, -224, + -171, -224, -224, -174, -175, -187, -182, -224, -190, -191, + -224, -224, -197, 390, -6, -9, -12, -13, -14, -15, + -224, -18, -19, -20, -21, -22, -23, -24, -25, -26, + -27, -29, -30, -31, -32, -33, -34, -36, -37, -38, + -39, -40, -224, -41, -102, -224, -78, -224, -216, -222, + -210, -207, -205, -116, -128, -199, -131, -203, -224, -213, + -211, -219, -201, -202, -209, -214, -215, -217, -218, -220, + -127, -126, -224, -125, -224, -42, -205, -69, -79, -224, + -82, -205, -161, -164, -224, -76, -224, -224, -224, -127, + -224, -207, -223, -158, -224, -224, -224, -224, -154, -224, + -224, -224, -166, -224, -169, -224, -172, -224, -184, -185, + -186, -188, -224, -192, -205, -194, -17, -224, -224, -205, + -104, -127, -115, -224, -208, -224, -206, -224, -224, -205, + -130, -132, -210, -211, -212, -213, -216, -219, -221, -222, + -123, -124, -206, -224, -71, -224, -81, -224, -206, -224, + -75, -224, -87, -224, -93, -224, -224, -97, -207, -205, + -207, -224, -224, -140, -224, -159, -205, -223, -224, -147, + -155, -153, -44, -167, -170, -177, -173, -176, -189, -224, + -106, -224, -206, -205, -110, -117, -111, -129, -133, -134, + -224, -68, -80, -83, -162, -163, -87, -86, -224, -224, + -93, -92, -224, -224, -101, -96, -98, -224, -224, -224, + -113, -223, -141, -142, -143, -224, -224, -138, -139, -224, + -145, -195, -103, -105, -114, -121, -70, -85, -88, -224, + -91, -224, -224, -108, -109, -112, -224, -160, -135, -144, + -224, -90, -224, -95, -224, -100, -136, -89, -94, -99 ] racc_goto_table = [ - 2, 139, 3, 100, 102, 103, 105, 121, 172, 137, - 127, 125, 180, 209, 325, 248, 179, 321, 340, 294, - 220, 295, 200, 202, 282, 223, 244, 309, 250, 246, - 327, 109, 111, 112, 113, 219, 66, 311, 281, 130, - 132, 129, 129, 134, 351, 206, 287, 138, 144, 145, - 146, 147, 268, 336, 224, 286, 171, 272, 318, 353, - 350, 359, 148, 135, 129, 149, 150, 151, 152, 153, - 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, - 164, 165, 166, 167, 168, 169, 170, 245, 175, 330, - 199, 199, 305, 143, 204, 1, 129, 142, 212, 239, - 129, 240, 314, 238, nil, nil, nil, 175, nil, nil, - nil, nil, nil, nil, 254, nil, nil, nil, 221, 331, - nil, 333, nil, 221, 226, nil, nil, nil, 291, nil, - nil, nil, 332, 284, nil, nil, nil, 283, 285, 339, - nil, nil, nil, nil, nil, nil, nil, nil, 121, nil, - nil, nil, 127, 125, nil, 347, nil, nil, nil, nil, - nil, nil, 307, nil, nil, 170, nil, nil, 109, 111, - 112, nil, nil, nil, 269, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, 303, nil, 127, 125, - 127, 125, nil, 302, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, 270, 129, 175, - 175, nil, nil, nil, 276, 278, nil, nil, nil, nil, - 346, 297, 288, 297, nil, 300, nil, 134, nil, nil, - nil, nil, 138, nil, 297, 304, nil, nil, nil, nil, - nil, 175, nil, nil, 312, 313, nil, nil, nil, nil, - 337, nil, nil, nil, nil, nil, nil, nil, nil, 297, - nil, nil, nil, nil, nil, nil, 319, nil, nil, nil, - nil, nil, nil, 129, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, 349, nil, nil, nil, nil, nil, - nil, nil, nil, 343, 342, nil, nil, nil, 170, nil, + 2, 112, 4, 144, 107, 109, 110, 128, 146, 191, + 184, 134, 192, 263, 221, 132, 337, 341, 232, 309, + 356, 310, 1, 235, 156, 157, 158, 159, 231, 73, + 259, 297, 325, 116, 118, 119, 120, 261, 265, 212, + 214, 343, 327, 136, 136, 141, 296, 368, 218, 302, + 145, 254, 352, 301, 236, 152, 183, 334, 142, 155, + 367, 148, 283, 370, 376, 149, 3, 287, 251, 252, + 250, 136, 161, 162, 163, 164, 165, 166, 167, 168, + 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, + 179, 180, 181, 182, 260, 187, 154, 211, 211, 346, + 319, 137, 139, 136, 150, 321, nil, 136, nil, nil, + nil, nil, nil, nil, 187, 330, nil, nil, nil, 269, + 347, nil, 349, nil, nil, 233, nil, nil, nil, 160, + 233, 238, nil, nil, 306, 298, 300, nil, 299, nil, + nil, nil, nil, nil, nil, 348, nil, nil, nil, nil, + 253, nil, 355, nil, nil, nil, nil, nil, 128, nil, + nil, 216, nil, 134, nil, 224, nil, 132, nil, 364, + 323, nil, nil, nil, nil, nil, nil, 182, nil, 284, + 116, 118, 119, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, 317, nil, 134, + nil, 134, 318, 132, nil, 132, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, 285, + 136, 187, 187, nil, nil, nil, 291, 293, nil, nil, + nil, 363, nil, 312, 303, 312, nil, 315, nil, 141, + nil, nil, nil, nil, 145, nil, nil, nil, nil, 312, + 320, nil, nil, nil, nil, nil, 187, nil, nil, 328, + 329, nil, nil, 353, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, 312, nil, nil, nil, nil, nil, + nil, 335, nil, nil, nil, nil, nil, nil, 136, nil, + nil, nil, nil, 366, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, 359, 358, + nil, nil, nil, nil, 182, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, 109, nil, nil, nil, nil, nil, + 116, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, 342, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, 363, nil, 365, 367 ] + nil, nil, nil, 358, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, 380, + nil, 382, 384 ] racc_goto_check = [ - 2, 78, 3, 9, 9, 9, 37, 62, 49, 74, - 29, 35, 54, 42, 45, 53, 52, 44, 64, 70, - 63, 70, 58, 58, 47, 63, 50, 55, 36, 56, - 48, 9, 9, 9, 9, 52, 5, 59, 46, 11, - 11, 9, 9, 9, 43, 41, 66, 9, 7, 7, - 7, 7, 36, 67, 69, 53, 12, 36, 72, 45, - 44, 64, 11, 73, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 49, 9, 47, - 9, 9, 36, 6, 11, 1, 9, 5, 11, 79, - 9, 80, 36, 82, nil, nil, nil, 9, nil, nil, - nil, nil, nil, nil, 54, nil, nil, nil, 3, 53, - nil, 53, nil, 3, 3, nil, nil, nil, 42, nil, - nil, nil, 36, 54, nil, nil, nil, 52, 52, 36, - nil, nil, nil, nil, nil, nil, nil, nil, 62, nil, - nil, nil, 29, 35, nil, 36, nil, nil, nil, nil, - nil, nil, 54, nil, nil, 9, nil, nil, 9, 9, - 9, nil, nil, nil, 37, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, 78, nil, 29, 35, - 29, 35, nil, 74, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, 2, 9, 9, - 9, nil, nil, nil, 2, 2, nil, nil, nil, nil, - 49, 9, 3, 9, nil, 9, nil, 9, nil, nil, - nil, nil, 9, nil, 9, 9, nil, nil, nil, nil, - nil, 9, nil, nil, 9, 9, nil, nil, nil, nil, - 62, nil, nil, nil, nil, nil, nil, nil, nil, 9, - nil, nil, nil, nil, nil, nil, 9, nil, nil, nil, - nil, nil, nil, 9, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, 37, nil, nil, nil, nil, nil, - nil, nil, nil, 2, 3, nil, nil, nil, 9, nil, + 2, 39, 4, 76, 10, 10, 10, 64, 81, 54, + 51, 31, 56, 55, 44, 37, 46, 47, 65, 72, + 66, 72, 1, 65, 8, 8, 8, 8, 54, 6, + 52, 49, 57, 10, 10, 10, 10, 58, 38, 60, + 60, 50, 61, 10, 10, 10, 48, 45, 43, 68, + 10, 44, 69, 55, 71, 10, 13, 74, 75, 7, + 46, 77, 38, 47, 66, 78, 3, 38, 82, 83, + 85, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 51, 10, 6, 10, 10, 49, + 38, 12, 12, 10, 86, 38, nil, 10, nil, nil, + nil, nil, nil, nil, 10, 38, nil, nil, nil, 56, + 55, nil, 55, nil, nil, 4, nil, nil, nil, 12, + 4, 4, nil, nil, 44, 54, 54, nil, 56, nil, + nil, nil, nil, nil, nil, 38, nil, nil, nil, nil, + 2, nil, 38, nil, nil, nil, nil, nil, 64, nil, + nil, 12, nil, 31, nil, 12, nil, 37, nil, 38, + 56, nil, nil, nil, nil, nil, nil, 10, nil, 39, + 10, 10, 10, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, 76, nil, 31, + nil, 31, 81, 37, nil, 37, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, 2, + 10, 10, 10, nil, nil, nil, 2, 2, nil, nil, + nil, 51, nil, 10, 4, 10, nil, 10, nil, 10, + nil, nil, nil, nil, 10, nil, nil, nil, nil, 10, + 10, nil, nil, nil, nil, nil, 10, nil, nil, 10, + 10, nil, nil, 64, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, 10, nil, nil, nil, nil, nil, + nil, 10, nil, nil, nil, nil, nil, nil, 10, nil, + nil, nil, nil, 39, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, 2, 4, + nil, nil, nil, nil, 10, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, 9, nil, nil, nil, nil, nil, + 10, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, 3, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, 2, nil, 2, 2 ] + nil, nil, nil, 4, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, 2, + nil, 2, 2 ] racc_goto_pointer = [ - nil, 95, 0, 2, nil, 32, 26, -20, nil, -8, - nil, -10, -38, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, -37, - nil, nil, nil, nil, nil, -36, -152, -31, nil, nil, - nil, -61, -94, -278, -260, -265, -178, -192, -250, -86, - -147, nil, -80, -164, -84, -222, -146, nil, -76, -214, - nil, nil, -39, -96, -274, nil, -174, -236, nil, -68, - -206, nil, -215, 12, -42, nil, nil, nil, -54, -41, - -39, nil, -37 ] + nil, 22, 0, 66, 2, nil, 24, -15, -51, nil, + -8, nil, 50, -45, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, -38, nil, nil, nil, nil, nil, -34, -154, -38, + nil, nil, nil, -65, -100, -291, -276, -277, -182, -197, + -254, -91, -155, nil, -94, -178, -91, -232, -150, nil, + -66, -224, nil, nil, -41, -105, -287, nil, -183, -252, + nil, -75, -218, nil, -231, 5, -50, 1, 5, nil, + nil, -50, -79, -78, nil, -77, 43 ] racc_goto_default = [ - nil, nil, 341, 201, 4, 5, 6, 7, 8, 10, - 9, 280, nil, 14, 36, 15, 16, 17, 18, 19, + nil, nil, 357, nil, 213, 5, 6, 7, 8, 9, + 11, 10, 295, nil, 15, 38, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, - 30, 31, 32, 33, 34, 35, nil, nil, 37, 38, - 106, nil, nil, 110, nil, nil, nil, nil, nil, nil, - nil, 42, nil, nil, nil, 181, nil, 97, nil, 182, - 186, 184, 117, nil, nil, 116, nil, nil, 122, nil, - 123, 124, 210, nil, nil, 52, 53, 55, nil, nil, - nil, 140, nil ] + 30, 31, 32, 33, 34, 35, 36, 37, nil, nil, + 39, 40, 113, nil, nil, 117, nil, nil, nil, nil, + nil, nil, nil, 44, nil, nil, nil, 193, nil, 104, + nil, 194, 198, 196, 124, nil, nil, 123, nil, nil, + 129, nil, 130, 131, 222, nil, nil, 54, 55, 56, + 58, nil, nil, nil, 147, nil, nil ] racc_reduce_table = [ 0, 0, :racc_error, - 1, 82, :_reduce_1, - 1, 82, :_reduce_none, - 1, 83, :_reduce_3, - 1, 85, :_reduce_4, - 3, 85, :_reduce_5, - 2, 85, :_reduce_6, - 1, 86, :_reduce_7, - 3, 86, :_reduce_8, - 1, 87, :_reduce_none, - 1, 88, :_reduce_10, - 3, 88, :_reduce_11, - 3, 88, :_reduce_12, - 3, 88, :_reduce_13, - 3, 88, :_reduce_14, - 1, 90, :_reduce_none, - 4, 90, :_reduce_16, - 3, 90, :_reduce_17, - 3, 90, :_reduce_18, - 3, 90, :_reduce_19, - 3, 90, :_reduce_20, - 3, 90, :_reduce_21, - 3, 90, :_reduce_22, - 3, 90, :_reduce_23, - 3, 90, :_reduce_24, - 3, 90, :_reduce_25, - 3, 90, :_reduce_26, - 2, 90, :_reduce_27, - 3, 90, :_reduce_28, - 3, 90, :_reduce_29, - 3, 90, :_reduce_30, - 3, 90, :_reduce_31, - 3, 90, :_reduce_32, - 3, 90, :_reduce_33, - 2, 90, :_reduce_34, - 3, 90, :_reduce_35, - 3, 90, :_reduce_36, - 3, 90, :_reduce_37, - 3, 90, :_reduce_38, - 3, 90, :_reduce_39, - 3, 90, :_reduce_40, - 3, 90, :_reduce_41, - 1, 92, :_reduce_42, - 3, 92, :_reduce_43, - 1, 91, :_reduce_none, - 1, 95, :_reduce_none, - 1, 95, :_reduce_none, - 1, 95, :_reduce_none, - 1, 95, :_reduce_none, - 1, 95, :_reduce_none, - 1, 95, :_reduce_none, - 1, 95, :_reduce_none, - 1, 95, :_reduce_none, - 1, 95, :_reduce_none, + 1, 86, :_reduce_1, + 1, 86, :_reduce_2, + 1, 86, :_reduce_none, + 1, 87, :_reduce_4, + 1, 90, :_reduce_5, + 3, 90, :_reduce_6, + 2, 90, :_reduce_7, + 1, 91, :_reduce_8, + 3, 91, :_reduce_9, + 1, 92, :_reduce_none, + 1, 93, :_reduce_11, + 3, 93, :_reduce_12, + 3, 93, :_reduce_13, + 3, 93, :_reduce_14, + 3, 93, :_reduce_15, 1, 95, :_reduce_none, + 4, 95, :_reduce_17, + 3, 95, :_reduce_18, + 3, 95, :_reduce_19, + 3, 95, :_reduce_20, + 3, 95, :_reduce_21, + 3, 95, :_reduce_22, + 3, 95, :_reduce_23, + 3, 95, :_reduce_24, + 3, 95, :_reduce_25, + 3, 95, :_reduce_26, + 3, 95, :_reduce_27, + 2, 95, :_reduce_28, + 3, 95, :_reduce_29, + 3, 95, :_reduce_30, + 3, 95, :_reduce_31, + 3, 95, :_reduce_32, + 3, 95, :_reduce_33, + 3, 95, :_reduce_34, + 2, 95, :_reduce_35, + 3, 95, :_reduce_36, + 3, 95, :_reduce_37, + 3, 95, :_reduce_38, + 3, 95, :_reduce_39, + 3, 95, :_reduce_40, + 3, 95, :_reduce_41, + 3, 95, :_reduce_42, + 1, 97, :_reduce_43, + 3, 97, :_reduce_44, 1, 96, :_reduce_none, - 1, 96, :_reduce_none, - 1, 96, :_reduce_none, - 1, 96, :_reduce_none, - 1, 96, :_reduce_none, - 1, 96, :_reduce_none, - 1, 96, :_reduce_none, - 1, 96, :_reduce_none, - 1, 96, :_reduce_none, - 1, 111, :_reduce_64, - 1, 111, :_reduce_65, - 5, 94, :_reduce_66, - 3, 94, :_reduce_67, - 6, 94, :_reduce_68, - 4, 94, :_reduce_69, - 1, 94, :_reduce_70, - 1, 98, :_reduce_71, - 2, 98, :_reduce_72, - 4, 119, :_reduce_73, - 3, 119, :_reduce_74, - 1, 119, :_reduce_75, - 3, 120, :_reduce_76, - 2, 118, :_reduce_77, - 3, 122, :_reduce_78, - 2, 122, :_reduce_79, - 2, 121, :_reduce_80, - 4, 121, :_reduce_81, - 2, 101, :_reduce_82, - 5, 124, :_reduce_83, - 4, 124, :_reduce_84, - 0, 125, :_reduce_none, - 2, 125, :_reduce_86, - 4, 125, :_reduce_87, - 3, 125, :_reduce_88, - 6, 102, :_reduce_89, - 5, 102, :_reduce_90, - 0, 126, :_reduce_none, - 4, 126, :_reduce_92, - 3, 126, :_reduce_93, - 5, 100, :_reduce_94, - 1, 127, :_reduce_95, - 2, 127, :_reduce_96, - 5, 128, :_reduce_97, - 4, 128, :_reduce_98, - 1, 129, :_reduce_99, - 1, 93, :_reduce_none, - 4, 93, :_reduce_101, - 1, 131, :_reduce_102, - 3, 131, :_reduce_103, - 3, 130, :_reduce_104, - 1, 89, :_reduce_105, - 6, 89, :_reduce_106, - 6, 89, :_reduce_107, - 5, 89, :_reduce_108, - 5, 89, :_reduce_109, - 6, 89, :_reduce_110, - 5, 89, :_reduce_111, - 4, 136, :_reduce_112, - 1, 137, :_reduce_113, - 1, 133, :_reduce_114, - 3, 133, :_reduce_115, - 1, 132, :_reduce_116, - 2, 132, :_reduce_117, - 1, 132, :_reduce_118, - 6, 99, :_reduce_119, - 2, 99, :_reduce_120, - 3, 138, :_reduce_121, - 3, 138, :_reduce_122, - 1, 139, :_reduce_none, - 1, 139, :_reduce_none, - 0, 135, :_reduce_125, - 1, 135, :_reduce_126, - 3, 135, :_reduce_127, - 1, 141, :_reduce_none, - 1, 141, :_reduce_none, - 1, 141, :_reduce_none, - 3, 140, :_reduce_131, - 3, 140, :_reduce_132, - 6, 103, :_reduce_133, - 7, 104, :_reduce_134, - 1, 146, :_reduce_135, + 1, 100, :_reduce_none, + 1, 100, :_reduce_none, + 1, 100, :_reduce_none, + 1, 100, :_reduce_none, + 1, 100, :_reduce_none, + 1, 100, :_reduce_none, + 1, 100, :_reduce_none, + 1, 100, :_reduce_none, + 1, 100, :_reduce_none, + 1, 100, :_reduce_none, + 1, 100, :_reduce_none, + 1, 101, :_reduce_none, + 1, 101, :_reduce_none, + 1, 101, :_reduce_none, + 1, 101, :_reduce_none, + 1, 101, :_reduce_none, + 1, 101, :_reduce_none, + 1, 101, :_reduce_none, + 1, 101, :_reduce_none, + 1, 101, :_reduce_none, + 1, 117, :_reduce_66, + 1, 117, :_reduce_67, + 5, 99, :_reduce_68, + 3, 99, :_reduce_69, + 6, 99, :_reduce_70, + 4, 99, :_reduce_71, + 1, 99, :_reduce_72, + 1, 103, :_reduce_73, + 2, 103, :_reduce_74, + 4, 125, :_reduce_75, + 3, 125, :_reduce_76, + 1, 125, :_reduce_77, + 3, 126, :_reduce_78, + 2, 124, :_reduce_79, + 3, 128, :_reduce_80, + 2, 128, :_reduce_81, + 2, 127, :_reduce_82, + 4, 127, :_reduce_83, + 2, 106, :_reduce_84, + 5, 130, :_reduce_85, + 4, 130, :_reduce_86, + 0, 131, :_reduce_none, + 2, 131, :_reduce_88, + 4, 131, :_reduce_89, + 3, 131, :_reduce_90, + 6, 107, :_reduce_91, + 5, 107, :_reduce_92, + 0, 132, :_reduce_none, + 4, 132, :_reduce_94, + 3, 132, :_reduce_95, + 5, 105, :_reduce_96, + 1, 133, :_reduce_97, + 2, 133, :_reduce_98, + 5, 134, :_reduce_99, + 4, 134, :_reduce_100, + 1, 135, :_reduce_101, + 1, 98, :_reduce_none, + 4, 98, :_reduce_103, + 1, 137, :_reduce_104, + 3, 137, :_reduce_105, + 3, 136, :_reduce_106, + 1, 94, :_reduce_107, + 6, 94, :_reduce_108, + 6, 94, :_reduce_109, + 5, 94, :_reduce_110, + 5, 94, :_reduce_111, + 6, 94, :_reduce_112, + 5, 94, :_reduce_113, + 4, 142, :_reduce_114, + 1, 143, :_reduce_115, + 1, 139, :_reduce_116, + 3, 139, :_reduce_117, + 1, 138, :_reduce_118, + 2, 138, :_reduce_119, + 1, 138, :_reduce_120, + 6, 104, :_reduce_121, + 2, 104, :_reduce_122, + 3, 144, :_reduce_123, + 3, 144, :_reduce_124, 1, 145, :_reduce_none, 1, 145, :_reduce_none, + 0, 141, :_reduce_127, + 1, 141, :_reduce_128, + 3, 141, :_reduce_129, 1, 147, :_reduce_none, - 2, 147, :_reduce_139, - 1, 148, :_reduce_none, - 1, 148, :_reduce_none, - 6, 105, :_reduce_142, - 5, 105, :_reduce_143, - 1, 149, :_reduce_144, - 3, 149, :_reduce_145, - 1, 151, :_reduce_146, - 1, 151, :_reduce_147, - 1, 151, :_reduce_148, + 1, 147, :_reduce_none, + 1, 147, :_reduce_none, + 3, 146, :_reduce_133, + 3, 146, :_reduce_134, + 6, 108, :_reduce_135, + 7, 109, :_reduce_136, + 1, 152, :_reduce_137, 1, 151, :_reduce_none, - 1, 152, :_reduce_150, - 3, 152, :_reduce_151, - 1, 150, :_reduce_none, - 2, 150, :_reduce_153, - 1, 143, :_reduce_154, - 1, 143, :_reduce_155, - 1, 144, :_reduce_156, - 2, 144, :_reduce_157, - 4, 144, :_reduce_158, - 1, 123, :_reduce_159, - 3, 123, :_reduce_160, - 3, 153, :_reduce_161, - 1, 153, :_reduce_162, - 1, 97, :_reduce_163, - 3, 106, :_reduce_164, - 4, 106, :_reduce_165, - 2, 106, :_reduce_166, - 3, 106, :_reduce_167, - 4, 106, :_reduce_168, - 2, 106, :_reduce_169, - 3, 109, :_reduce_170, - 4, 109, :_reduce_171, - 2, 109, :_reduce_172, - 1, 154, :_reduce_173, - 3, 154, :_reduce_174, - 3, 155, :_reduce_175, - 1, 116, :_reduce_none, - 1, 116, :_reduce_none, - 1, 156, :_reduce_178, - 2, 157, :_reduce_179, - 1, 158, :_reduce_180, - 1, 160, :_reduce_181, - 1, 161, :_reduce_182, - 2, 159, :_reduce_183, - 1, 162, :_reduce_184, - 1, 163, :_reduce_185, - 2, 163, :_reduce_186, - 1, 112, :_reduce_187, - 1, 115, :_reduce_188, - 1, 113, :_reduce_189, - 1, 114, :_reduce_190, - 1, 108, :_reduce_191, - 1, 107, :_reduce_192, - 1, 110, :_reduce_193, - 0, 117, :_reduce_none, - 1, 117, :_reduce_195, - 0, 134, :_reduce_none, - 1, 134, :_reduce_none, - 1, 142, :_reduce_none, - 1, 142, :_reduce_none, - 1, 142, :_reduce_none, - 1, 142, :_reduce_none, - 1, 142, :_reduce_none, - 1, 142, :_reduce_none, - 1, 142, :_reduce_none, - 1, 142, :_reduce_none, - 1, 142, :_reduce_none, - 1, 142, :_reduce_none, - 1, 142, :_reduce_none, - 1, 142, :_reduce_none, - 1, 142, :_reduce_none, - 1, 142, :_reduce_none, - 0, 84, :_reduce_212 ] + 1, 151, :_reduce_none, + 1, 153, :_reduce_none, + 2, 153, :_reduce_141, + 1, 154, :_reduce_none, + 1, 154, :_reduce_none, + 6, 110, :_reduce_144, + 5, 110, :_reduce_145, + 1, 155, :_reduce_146, + 3, 155, :_reduce_147, + 1, 157, :_reduce_148, + 1, 157, :_reduce_149, + 1, 157, :_reduce_150, + 1, 157, :_reduce_none, + 1, 158, :_reduce_152, + 3, 158, :_reduce_153, + 1, 156, :_reduce_none, + 2, 156, :_reduce_155, + 1, 149, :_reduce_156, + 1, 149, :_reduce_157, + 1, 150, :_reduce_158, + 2, 150, :_reduce_159, + 4, 150, :_reduce_160, + 1, 129, :_reduce_161, + 3, 129, :_reduce_162, + 3, 159, :_reduce_163, + 1, 159, :_reduce_164, + 1, 102, :_reduce_165, + 3, 112, :_reduce_166, + 4, 112, :_reduce_167, + 2, 112, :_reduce_168, + 3, 112, :_reduce_169, + 4, 112, :_reduce_170, + 2, 112, :_reduce_171, + 3, 115, :_reduce_172, + 4, 115, :_reduce_173, + 2, 115, :_reduce_174, + 1, 160, :_reduce_175, + 3, 160, :_reduce_176, + 3, 161, :_reduce_177, + 1, 122, :_reduce_none, + 1, 122, :_reduce_none, + 1, 122, :_reduce_none, + 1, 162, :_reduce_181, + 2, 163, :_reduce_182, + 1, 165, :_reduce_183, + 1, 167, :_reduce_184, + 1, 168, :_reduce_185, + 2, 166, :_reduce_186, + 1, 169, :_reduce_187, + 1, 170, :_reduce_188, + 2, 170, :_reduce_189, + 2, 164, :_reduce_190, + 2, 164, :_reduce_191, + 3, 88, :_reduce_192, + 0, 171, :_reduce_193, + 2, 171, :_reduce_194, + 4, 171, :_reduce_195, + 1, 111, :_reduce_196, + 2, 111, :_reduce_197, + 1, 118, :_reduce_198, + 1, 121, :_reduce_199, + 1, 119, :_reduce_200, + 1, 120, :_reduce_201, + 1, 114, :_reduce_202, + 1, 113, :_reduce_203, + 1, 116, :_reduce_204, + 0, 123, :_reduce_none, + 1, 123, :_reduce_206, + 0, 140, :_reduce_none, + 1, 140, :_reduce_none, + 1, 148, :_reduce_none, + 1, 148, :_reduce_none, + 1, 148, :_reduce_none, + 1, 148, :_reduce_none, + 1, 148, :_reduce_none, + 1, 148, :_reduce_none, + 1, 148, :_reduce_none, + 1, 148, :_reduce_none, + 1, 148, :_reduce_none, + 1, 148, :_reduce_none, + 1, 148, :_reduce_none, + 1, 148, :_reduce_none, + 1, 148, :_reduce_none, + 1, 148, :_reduce_none, + 0, 89, :_reduce_223 ] -racc_reduce_n = 213 +racc_reduce_n = 224 -racc_shift_n = 373 +racc_shift_n = 390 racc_token_table = { false => 0, @@ -876,15 +933,19 @@ racc_token_table = { :LAMBDA => 71, :SELBRACE => 72, :NUMBER => 73, - :LOW => 74, - :HIGH => 75, - :CALL => 76, - :LISTSTART => 77, - :MODULO => 78, - :TITLE_COLON => 79, - :CASE_COLON => 80 } + :HEREDOC => 74, + :RENDER_STRING => 75, + :RENDER_EXPR => 76, + :EPP_START => 77, + :LOW => 78, + :HIGH => 79, + :CALL => 80, + :LISTSTART => 81, + :MODULO => 82, + :TITLE_COLON => 83, + :CASE_COLON => 84 } -racc_nt_base = 81 +racc_nt_base = 85 racc_use_result_var = true @@ -979,6 +1040,10 @@ Racc_token_to_s_table = [ "LAMBDA", "SELBRACE", "NUMBER", + "HEREDOC", + "RENDER_STRING", + "RENDER_EXPR", + "EPP_START", "LOW", "HIGH", "CALL", @@ -989,6 +1054,7 @@ Racc_token_to_s_table = [ "$start", "program", "statements", + "epp_expression", "nil", "syntactic_statements", "syntactic_statement", @@ -1011,6 +1077,7 @@ Racc_token_to_s_table = [ "definition_expression", "hostclass_expression", "node_definition_expression", + "epp_render_expression", "array", "boolean", "default", @@ -1063,12 +1130,14 @@ Racc_token_to_s_table = [ "hashpair", "string", "dq_string", + "heredoc", "dqpre", "dqrval", "dqpost", "dqmid", "text_expression", - "dqtail" ] + "dqtail", + "epp_parameters_list" ] Racc_debug_parser = false @@ -1076,294 +1145,299 @@ Racc_debug_parser = false # reduce 0 omitted -module_eval(<<'.,.,', 'egrammar.ra', 59) +module_eval(<<'.,.,', 'egrammar.ra', 64) def _reduce_1(val, _values, result) result = create_program(Factory.block_or_expression(*val[0])) result end .,., -# reduce 2 omitted +module_eval(<<'.,.,', 'egrammar.ra', 65) + def _reduce_2(val, _values, result) + result = Factory.block_or_expression(*val[0]) + result + end +.,., -module_eval(<<'.,.,', 'egrammar.ra', 64) - def _reduce_3(val, _values, result) +# reduce 3 omitted + +module_eval(<<'.,.,', 'egrammar.ra', 70) + def _reduce_4(val, _values, result) result = transform_calls(val[0]) result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 70) - def _reduce_4(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 76) + def _reduce_5(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 71) - def _reduce_5(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 77) + def _reduce_6(val, _values, result) result = val[0].push val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 72) - def _reduce_6(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 78) + def _reduce_7(val, _values, result) result = val[0].push val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 76) - def _reduce_7(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 82) + def _reduce_8(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 77) - def _reduce_8(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 83) + def _reduce_9(val, _values, result) result = aryfy(val[0]).push val[2] result end .,., -# reduce 9 omitted +# reduce 10 omitted -module_eval(<<'.,.,', 'egrammar.ra', 83) - def _reduce_10(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 89) + def _reduce_11(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 84) - def _reduce_11(val, _values, result) - result = val[0].relop(val[1][:value], val[2]); loc result, val[1] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 85) +module_eval(<<'.,.,', 'egrammar.ra', 90) def _reduce_12(val, _values, result) result = val[0].relop(val[1][:value], val[2]); loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 86) +module_eval(<<'.,.,', 'egrammar.ra', 91) def _reduce_13(val, _values, result) result = val[0].relop(val[1][:value], val[2]); loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 87) +module_eval(<<'.,.,', 'egrammar.ra', 92) def _reduce_14(val, _values, result) result = val[0].relop(val[1][:value], val[2]); loc result, val[1] result end .,., -# reduce 15 omitted +module_eval(<<'.,.,', 'egrammar.ra', 93) + def _reduce_15(val, _values, result) + result = val[0].relop(val[1][:value], val[2]); loc result, val[1] + result + end +.,., -module_eval(<<'.,.,', 'egrammar.ra', 94) - def _reduce_16(val, _values, result) +# reduce 16 omitted + +module_eval(<<'.,.,', 'egrammar.ra', 100) + def _reduce_17(val, _values, result) result = val[0][*val[2]] ; loc result, val[0], val[3] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 95) - def _reduce_17(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 101) + def _reduce_18(val, _values, result) result = val[0].in val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 96) - def _reduce_18(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 102) + def _reduce_19(val, _values, result) result = val[0] =~ val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 97) - def _reduce_19(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 103) + def _reduce_20(val, _values, result) result = val[0].mne val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 98) - def _reduce_20(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 104) + def _reduce_21(val, _values, result) result = val[0] + val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 99) - def _reduce_21(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 105) + def _reduce_22(val, _values, result) result = val[0] - val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 100) - def _reduce_22(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 106) + def _reduce_23(val, _values, result) result = val[0] / val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 101) - def _reduce_23(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 107) + def _reduce_24(val, _values, result) result = val[0] * val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 102) - def _reduce_24(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 108) + def _reduce_25(val, _values, result) result = val[0] % val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 103) - def _reduce_25(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 109) + def _reduce_26(val, _values, result) result = val[0] << val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 104) - def _reduce_26(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 110) + def _reduce_27(val, _values, result) result = val[0] >> val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 105) - def _reduce_27(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 111) + def _reduce_28(val, _values, result) result = val[1].minus() ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 106) - def _reduce_28(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 112) + def _reduce_29(val, _values, result) result = val[0].ne val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 107) - def _reduce_29(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 113) + def _reduce_30(val, _values, result) result = val[0] == val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 108) - def _reduce_30(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 114) + def _reduce_31(val, _values, result) result = val[0] > val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 109) - def _reduce_31(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 115) + def _reduce_32(val, _values, result) result = val[0] >= val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 110) - def _reduce_32(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 116) + def _reduce_33(val, _values, result) result = val[0] < val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 111) - def _reduce_33(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 117) + def _reduce_34(val, _values, result) result = val[0] <= val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 112) - def _reduce_34(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 118) + def _reduce_35(val, _values, result) result = val[1].not ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 113) - def _reduce_35(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 119) + def _reduce_36(val, _values, result) result = val[0].and val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 114) - def _reduce_36(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 120) + def _reduce_37(val, _values, result) result = val[0].or val[2] ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 115) - def _reduce_37(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 121) + def _reduce_38(val, _values, result) result = val[0].set(val[2]) ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 116) - def _reduce_38(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 122) + def _reduce_39(val, _values, result) result = val[0].plus_set(val[2]) ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 117) - def _reduce_39(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 123) + def _reduce_40(val, _values, result) result = val[0].minus_set(val[2]); loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 118) - def _reduce_40(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 124) + def _reduce_41(val, _values, result) result = val[0].select(*val[2]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 119) - def _reduce_41(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 125) + def _reduce_42(val, _values, result) result = val[1].paren() ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 127) - def _reduce_42(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 133) + def _reduce_43(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 128) - def _reduce_43(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 134) + def _reduce_44(val, _values, result) result = val[0].push(val[2]) result end .,., -# reduce 44 omitted - # reduce 45 omitted # reduce 46 omitted @@ -1402,22 +1476,26 @@ module_eval(<<'.,.,', 'egrammar.ra', 128) # reduce 63 omitted -module_eval(<<'.,.,', 'egrammar.ra', 159) - def _reduce_64(val, _values, result) - result = val[0] - result - end -.,., +# reduce 64 omitted -module_eval(<<'.,.,', 'egrammar.ra', 160) - def _reduce_65(val, _values, result) - result = val[0] - result - end -.,., +# reduce 65 omitted -module_eval(<<'.,.,', 'egrammar.ra', 168) +module_eval(<<'.,.,', 'egrammar.ra', 166) def _reduce_66(val, _values, result) + result = val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 167) + def _reduce_67(val, _values, result) + result = val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 175) + def _reduce_68(val, _values, result) result = Factory.CALL_NAMED(val[0], true, val[2]) loc result, val[0], val[4] @@ -1425,8 +1503,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 168) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 172) - def _reduce_67(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 179) + def _reduce_69(val, _values, result) result = Factory.CALL_NAMED(val[0], true, []) loc result, val[0], val[2] @@ -1434,8 +1512,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 172) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 176) - def _reduce_68(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 183) + def _reduce_70(val, _values, result) result = Factory.CALL_NAMED(val[0], true, val[2]) loc result, val[0], val[4] result.lambda = val[5] @@ -1444,8 +1522,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 176) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 181) - def _reduce_69(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 188) + def _reduce_71(val, _values, result) result = Factory.CALL_NAMED(val[0], true, []) loc result, val[0], val[2] result.lambda = val[3] @@ -1454,50 +1532,50 @@ module_eval(<<'.,.,', 'egrammar.ra', 181) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 185) - def _reduce_70(val, _values, result) - result = val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 190) - def _reduce_71(val, _values, result) - result = val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 191) +module_eval(<<'.,.,', 'egrammar.ra', 192) def _reduce_72(val, _values, result) + result = val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 197) + def _reduce_73(val, _values, result) + result = val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 198) + def _reduce_74(val, _values, result) result = val[0]; val[0].lambda = val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 194) - def _reduce_73(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 201) + def _reduce_75(val, _values, result) result = Factory.CALL_METHOD(val[0], val[2]); loc result, val[1], val[3] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 195) - def _reduce_74(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 202) + def _reduce_76(val, _values, result) result = Factory.CALL_METHOD(val[0], []); loc result, val[1], val[3] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 196) - def _reduce_75(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 203) + def _reduce_77(val, _values, result) result = Factory.CALL_METHOD(val[0], []); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 201) - def _reduce_76(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 208) + def _reduce_78(val, _values, result) result = val[0].dot(Factory.fqn(val[2][:value])) loc result, val[1], val[2] @@ -1505,8 +1583,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 201) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 213) - def _reduce_77(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 220) + def _reduce_79(val, _values, result) result = Factory.LAMBDA(val[0], val[1]) # loc result, val[1] # TODO @@ -1514,36 +1592,36 @@ module_eval(<<'.,.,', 'egrammar.ra', 213) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 218) - def _reduce_78(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 225) + def _reduce_80(val, _values, result) result = val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 219) - def _reduce_79(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 226) + def _reduce_81(val, _values, result) result = nil result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 223) - def _reduce_80(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 230) + def _reduce_82(val, _values, result) result = [] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 224) - def _reduce_81(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 231) + def _reduce_83(val, _values, result) result = val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 234) - def _reduce_82(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 241) + def _reduce_84(val, _values, result) result = val[1] loc(result, val[0], val[1]) @@ -1551,8 +1629,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 234) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 241) - def _reduce_83(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 248) + def _reduce_85(val, _values, result) result = Factory.IF(val[0], Factory.block_or_expression(*val[2]), val[4]) loc(result, val[0], (val[4] ? val[4] : val[3])) @@ -1560,8 +1638,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 241) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 245) - def _reduce_84(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 252) + def _reduce_86(val, _values, result) result = Factory.IF(val[0], nil, val[3]) loc(result, val[0], (val[3] ? val[3] : val[2])) @@ -1569,10 +1647,10 @@ module_eval(<<'.,.,', 'egrammar.ra', 245) end .,., -# reduce 85 omitted +# reduce 87 omitted -module_eval(<<'.,.,', 'egrammar.ra', 253) - def _reduce_86(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 260) + def _reduce_88(val, _values, result) result = val[1] loc(result, val[0], val[1]) @@ -1580,8 +1658,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 253) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 257) - def _reduce_87(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 264) + def _reduce_89(val, _values, result) result = Factory.block_or_expression(*val[2]) loc result, val[0], val[3] @@ -1589,16 +1667,16 @@ module_eval(<<'.,.,', 'egrammar.ra', 257) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 261) - def _reduce_88(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 268) + def _reduce_90(val, _values, result) result = nil # don't think a nop is needed here either result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 270) - def _reduce_89(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 277) + def _reduce_91(val, _values, result) result = Factory.UNLESS(val[1], Factory.block_or_expression(*val[3]), val[5]) loc result, val[0], val[4] @@ -1606,8 +1684,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 270) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 274) - def _reduce_90(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 281) + def _reduce_92(val, _values, result) result = Factory.UNLESS(val[1], nil, nil) loc result, val[0], val[4] @@ -1615,10 +1693,10 @@ module_eval(<<'.,.,', 'egrammar.ra', 274) end .,., -# reduce 91 omitted +# reduce 93 omitted -module_eval(<<'.,.,', 'egrammar.ra', 284) - def _reduce_92(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 291) + def _reduce_94(val, _values, result) result = Factory.block_or_expression(*val[2]) loc result, val[0], val[3] @@ -1626,16 +1704,16 @@ module_eval(<<'.,.,', 'egrammar.ra', 284) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 288) - def _reduce_93(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 295) + def _reduce_95(val, _values, result) result = nil # don't think a nop is needed here either result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 296) - def _reduce_94(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 303) + def _reduce_96(val, _values, result) result = Factory.CASE(val[1], *val[3]) loc result, val[0], val[4] @@ -1643,22 +1721,22 @@ module_eval(<<'.,.,', 'egrammar.ra', 296) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 302) - def _reduce_95(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 309) + def _reduce_97(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 303) - def _reduce_96(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 310) + def _reduce_98(val, _values, result) result = val[0].push val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 308) - def _reduce_97(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 315) + def _reduce_99(val, _values, result) result = Factory.WHEN(val[0], val[3]) loc result, val[1], val[4] @@ -1666,8 +1744,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 308) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 312) - def _reduce_98(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 319) + def _reduce_100(val, _values, result) result = Factory.WHEN(val[0], nil) loc result, val[1], val[3] @@ -1675,54 +1753,54 @@ module_eval(<<'.,.,', 'egrammar.ra', 312) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 316) - def _reduce_99(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 323) + def _reduce_101(val, _values, result) result = val[0] result end .,., -# reduce 100 omitted +# reduce 102 omitted -module_eval(<<'.,.,', 'egrammar.ra', 327) - def _reduce_101(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 334) + def _reduce_103(val, _values, result) result = val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 332) - def _reduce_102(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 339) + def _reduce_104(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 333) - def _reduce_103(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 340) + def _reduce_105(val, _values, result) result = val[0].push val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 338) - def _reduce_104(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 345) + def _reduce_106(val, _values, result) result = Factory.MAP(val[0], val[2]) ; loc result, val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 350) - def _reduce_105(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 357) + def _reduce_107(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 353) - def _reduce_106(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 360) + def _reduce_108(val, _values, result) result = case Factory.resource_shape(val[1]) when :resource, :class tmp = Factory.RESOURCE(Factory.fqn(token_text(val[1])), val[3]) @@ -1741,8 +1819,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 353) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 368) - def _reduce_107(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 375) + def _reduce_109(val, _values, result) result = case Factory.resource_shape(val[1]) when :resource, :class, :defaults, :override error val[1], "Defaults are not virtualizable" @@ -1754,8 +1832,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 368) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 376) - def _reduce_108(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 383) + def _reduce_110(val, _values, result) result = case Factory.resource_shape(val[0]) when :resource, :class Factory.RESOURCE(Factory.fqn(token_text(val[0])), val[2]) @@ -1772,8 +1850,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 376) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 389) - def _reduce_109(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 396) + def _reduce_111(val, _values, result) result = case Factory.resource_shape(val[0]) when :resource, :class # This catches deprecated syntax. @@ -1792,8 +1870,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 389) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 404) - def _reduce_110(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 411) + def _reduce_112(val, _values, result) result = Factory.RESOURCE(Factory.fqn(token_text(val[1])), val[3]) result.form = val[0] loc result, val[1], val[5] @@ -1802,8 +1880,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 404) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 409) - def _reduce_111(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 416) + def _reduce_113(val, _values, result) result = Factory.RESOURCE(Factory.fqn(token_text(val[0])), val[2]) loc result, val[0], val[4] @@ -1811,57 +1889,57 @@ module_eval(<<'.,.,', 'egrammar.ra', 409) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 414) - def _reduce_112(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 421) + def _reduce_114(val, _values, result) result = Factory.RESOURCE_BODY(val[0], val[2]) result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 416) - def _reduce_113(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 423) + def _reduce_115(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 419) - def _reduce_114(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 426) + def _reduce_116(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 420) - def _reduce_115(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 427) + def _reduce_117(val, _values, result) result = val[0].push val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 425) - def _reduce_116(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 432) + def _reduce_118(val, _values, result) result = :virtual result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 426) - def _reduce_117(val, _values, result) - result = :exported - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 427) - def _reduce_118(val, _values, result) - result = :exported - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 439) +module_eval(<<'.,.,', 'egrammar.ra', 433) def _reduce_119(val, _values, result) + result = :exported + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 434) + def _reduce_120(val, _values, result) + result = :exported + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 446) + def _reduce_121(val, _values, result) result = Factory.COLLECT(val[0], val[1], val[3]) loc result, val[0], val[5] @@ -1869,8 +1947,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 439) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 443) - def _reduce_120(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 450) + def _reduce_122(val, _values, result) result = Factory.COLLECT(val[0], val[1], []) loc result, val[0], val[1] @@ -1878,53 +1956,53 @@ module_eval(<<'.,.,', 'egrammar.ra', 443) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 448) - def _reduce_121(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 455) + def _reduce_123(val, _values, result) result = Factory.VIRTUAL_QUERY(val[1]) ; loc result, val[0], val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 449) - def _reduce_122(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 456) + def _reduce_124(val, _values, result) result = Factory.EXPORTED_QUERY(val[1]) ; loc result, val[0], val[2] result end .,., -# reduce 123 omitted +# reduce 125 omitted -# reduce 124 omitted +# reduce 126 omitted -module_eval(<<'.,.,', 'egrammar.ra', 462) - def _reduce_125(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 469) + def _reduce_127(val, _values, result) result = [] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 463) - def _reduce_126(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 470) + def _reduce_128(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 464) - def _reduce_127(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 471) + def _reduce_129(val, _values, result) result = val[0].push(val[2]) result end .,., -# reduce 128 omitted - -# reduce 129 omitted - # reduce 130 omitted -module_eval(<<'.,.,', 'egrammar.ra', 480) - def _reduce_131(val, _values, result) +# reduce 131 omitted + +# reduce 132 omitted + +module_eval(<<'.,.,', 'egrammar.ra', 487) + def _reduce_133(val, _values, result) result = Factory.ATTRIBUTE_OP(val[0][:value], :'=>', val[2]) loc result, val[0], val[2] @@ -1932,8 +2010,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 480) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 484) - def _reduce_132(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 491) + def _reduce_134(val, _values, result) result = Factory.ATTRIBUTE_OP(val[0][:value], :'+>', val[2]) loc result, val[0], val[2] @@ -1941,8 +2019,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 484) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 494) - def _reduce_133(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 501) + def _reduce_135(val, _values, result) result = add_definition(Factory.DEFINITION(classname(val[1][:value]), val[2], val[4])) loc result, val[0], val[5] # New lexer does not keep track of this, this is done in validation @@ -1954,8 +2032,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 494) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 508) - def _reduce_134(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 515) + def _reduce_136(val, _values, result) # Remove this class' name from the namestack as all nested classes have been parsed namepop result = add_definition(Factory.HOSTCLASS(classname(val[1][:value]), val[2], token_text(val[3]), val[5])) @@ -1965,32 +2043,32 @@ module_eval(<<'.,.,', 'egrammar.ra', 508) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 518) - def _reduce_135(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 525) + def _reduce_137(val, _values, result) namestack(val[0][:value]) ; result = val[0] result end .,., -# reduce 136 omitted - -# reduce 137 omitted - # reduce 138 omitted -module_eval(<<'.,.,', 'egrammar.ra', 527) - def _reduce_139(val, _values, result) +# reduce 139 omitted + +# reduce 140 omitted + +module_eval(<<'.,.,', 'egrammar.ra', 534) + def _reduce_141(val, _values, result) result = val[1] result end .,., -# reduce 140 omitted +# reduce 142 omitted -# reduce 141 omitted +# reduce 143 omitted -module_eval(<<'.,.,', 'egrammar.ra', 544) - def _reduce_142(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 551) + def _reduce_144(val, _values, result) result = add_definition(Factory.NODE(val[1], val[2], val[4])) loc result, val[0], val[5] @@ -1998,8 +2076,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 544) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 548) - def _reduce_143(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 555) + def _reduce_145(val, _values, result) result = add_definition(Factory.NODE(val[1], val[2], nil)) loc result, val[0], val[4] @@ -2007,367 +2085,403 @@ module_eval(<<'.,.,', 'egrammar.ra', 548) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 558) - def _reduce_144(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 565) + def _reduce_146(val, _values, result) result = [result] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 559) - def _reduce_145(val, _values, result) - result = val[0].push(val[2]) - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 564) - def _reduce_146(val, _values, result) - result = val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 565) - def _reduce_147(val, _values, result) - result = val[0] - result - end -.,., - module_eval(<<'.,.,', 'egrammar.ra', 566) - def _reduce_148(val, _values, result) - result = Factory.literal(:default); loc result, val[0] - result - end -.,., - -# reduce 149 omitted - -module_eval(<<'.,.,', 'egrammar.ra', 570) - def _reduce_150(val, _values, result) - result = Factory.literal(val[0][:value]); loc result, val[0] + def _reduce_147(val, _values, result) + result = val[0].push(val[2]) result end .,., module_eval(<<'.,.,', 'egrammar.ra', 571) - def _reduce_151(val, _values, result) - result = Factory.concat(val[0], '.', val[2][:value]); loc result, val[0], val[2] - result - end -.,., - -# reduce 152 omitted - -module_eval(<<'.,.,', 'egrammar.ra', 576) - def _reduce_153(val, _values, result) - result = val[1] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 582) - def _reduce_154(val, _values, result) + def _reduce_148(val, _values, result) result = val[0] result end .,., +module_eval(<<'.,.,', 'egrammar.ra', 572) + def _reduce_149(val, _values, result) + result = val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 573) + def _reduce_150(val, _values, result) + result = Factory.literal(:default); loc result, val[0] + result + end +.,., + +# reduce 151 omitted + +module_eval(<<'.,.,', 'egrammar.ra', 577) + def _reduce_152(val, _values, result) + result = Factory.literal(val[0][:value]); loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 578) + def _reduce_153(val, _values, result) + result = Factory.concat(val[0], '.', val[2][:value]); loc result, val[0], val[2] + result + end +.,., + +# reduce 154 omitted + module_eval(<<'.,.,', 'egrammar.ra', 583) def _reduce_155(val, _values, result) - error val[0], "'class' is not a valid classname" - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 587) - def _reduce_156(val, _values, result) - result = [] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 588) - def _reduce_157(val, _values, result) - result = [] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 589) - def _reduce_158(val, _values, result) result = val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 593) - def _reduce_159(val, _values, result) - result = [val[0]] +module_eval(<<'.,.,', 'egrammar.ra', 589) + def _reduce_156(val, _values, result) + result = val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 590) + def _reduce_157(val, _values, result) + error val[0], "'class' is not a valid classname" result end .,., module_eval(<<'.,.,', 'egrammar.ra', 594) + def _reduce_158(val, _values, result) + result = [] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 595) + def _reduce_159(val, _values, result) + result = [] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 596) def _reduce_160(val, _values, result) + result = val[1] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 600) + def _reduce_161(val, _values, result) + result = [val[0]] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 601) + def _reduce_162(val, _values, result) result = val[0].push(val[2]) result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 598) - def _reduce_161(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 605) + def _reduce_163(val, _values, result) result = Factory.PARAM(val[0][:value], val[2]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 599) - def _reduce_162(val, _values, result) - result = Factory.PARAM(val[0][:value]); loc result, val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 612) - def _reduce_163(val, _values, result) - result = Factory.fqn(val[0][:value]).var ; loc result, val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 618) +module_eval(<<'.,.,', 'egrammar.ra', 606) def _reduce_164(val, _values, result) - result = Factory.LIST(val[1]); loc result, val[0], val[2] + result = Factory.PARAM(val[0][:value]); loc result, val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 619) def _reduce_165(val, _values, result) - result = Factory.LIST(val[1]); loc result, val[0], val[3] + result = Factory.fqn(val[0][:value]).var ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 620) +module_eval(<<'.,.,', 'egrammar.ra', 625) def _reduce_166(val, _values, result) - result = Factory.literal([]) ; loc result, val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 621) - def _reduce_167(val, _values, result) result = Factory.LIST(val[1]); loc result, val[0], val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 622) - def _reduce_168(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 626) + def _reduce_167(val, _values, result) result = Factory.LIST(val[1]); loc result, val[0], val[3] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 623) - def _reduce_169(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 627) + def _reduce_168(val, _values, result) result = Factory.literal([]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 626) +module_eval(<<'.,.,', 'egrammar.ra', 628) + def _reduce_169(val, _values, result) + result = Factory.LIST(val[1]); loc result, val[0], val[2] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 629) def _reduce_170(val, _values, result) + result = Factory.LIST(val[1]); loc result, val[0], val[3] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 630) + def _reduce_171(val, _values, result) + result = Factory.literal([]) ; loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 633) + def _reduce_172(val, _values, result) result = Factory.HASH(val[1]); loc result, val[0], val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 627) - def _reduce_171(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 634) + def _reduce_173(val, _values, result) result = Factory.HASH(val[1]); loc result, val[0], val[3] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 628) - def _reduce_172(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 635) + def _reduce_174(val, _values, result) result = Factory.literal({}) ; loc result, val[0], val[3] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 631) - def _reduce_173(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 638) + def _reduce_175(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 632) - def _reduce_174(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 639) + def _reduce_176(val, _values, result) result = val[0].push val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 635) - def _reduce_175(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 642) + def _reduce_177(val, _values, result) result = Factory.KEY_ENTRY(val[0], val[2]); loc result, val[1] result end .,., -# reduce 176 omitted +# reduce 178 omitted -# reduce 177 omitted +# reduce 179 omitted -module_eval(<<'.,.,', 'egrammar.ra', 641) - def _reduce_178(val, _values, result) +# reduce 180 omitted + +module_eval(<<'.,.,', 'egrammar.ra', 649) + def _reduce_181(val, _values, result) result = Factory.literal(val[0][:value]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 642) - def _reduce_179(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 650) + def _reduce_182(val, _values, result) result = Factory.string(val[0], *val[1]) ; loc result, val[0], val[1][-1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 643) - def _reduce_180(val, _values, result) - result = Factory.literal(val[0][:value]); loc result, val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 644) - def _reduce_181(val, _values, result) - result = Factory.literal(val[0][:value]); loc result, val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 645) - def _reduce_182(val, _values, result) - result = Factory.literal(val[0][:value]); loc result, val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 646) - def _reduce_183(val, _values, result) - result = [val[0]] + val[1] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 647) - def _reduce_184(val, _values, result) - result = Factory.TEXT(val[0]) - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 650) - def _reduce_185(val, _values, result) - result = [val[0]] - result - end -.,., - module_eval(<<'.,.,', 'egrammar.ra', 651) + def _reduce_183(val, _values, result) + result = Factory.literal(val[0][:value]); loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 652) + def _reduce_184(val, _values, result) + result = Factory.literal(val[0][:value]); loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 653) + def _reduce_185(val, _values, result) + result = Factory.literal(val[0][:value]); loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 654) def _reduce_186(val, _values, result) result = [val[0]] + val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 653) +module_eval(<<'.,.,', 'egrammar.ra', 655) def _reduce_187(val, _values, result) + result = Factory.TEXT(val[0]) + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 658) + def _reduce_188(val, _values, result) + result = [val[0]] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 659) + def _reduce_189(val, _values, result) + result = [val[0]] + val[1] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 663) + def _reduce_190(val, _values, result) + result = Factory.HEREDOC(val[0][:value], val[1]); loc result, val[0], val[1] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 664) + def _reduce_191(val, _values, result) + result = Factory.HEREDOC(val[0][:value], val[1]); loc result, val[0], val[1] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 668) + def _reduce_192(val, _values, result) + result = Factory.EPP(val[1], val[2]); loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 671) + def _reduce_193(val, _values, result) + result = [] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 672) + def _reduce_194(val, _values, result) + result = [] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 673) + def _reduce_195(val, _values, result) + result = val[1] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 676) + def _reduce_196(val, _values, result) + result = Factory.RENDER_STRING(val[0][:value]); loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 677) + def _reduce_197(val, _values, result) + result = Factory.RENDER_EXPR(val[1]); loc result, val[0], val[1] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 679) + def _reduce_198(val, _values, result) result = Factory.NUMBER(val[0][:value]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 654) - def _reduce_188(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 680) + def _reduce_199(val, _values, result) result = Factory.QNAME_OR_NUMBER(val[0][:value]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 655) - def _reduce_189(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 681) + def _reduce_200(val, _values, result) result = Factory.QREF(val[0][:value]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 656) - def _reduce_190(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 682) + def _reduce_201(val, _values, result) result = Factory.literal(:undef); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 657) - def _reduce_191(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 683) + def _reduce_202(val, _values, result) result = Factory.literal(:default); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 662) - def _reduce_192(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 688) + def _reduce_203(val, _values, result) result = Factory.literal(val[0][:value]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 665) - def _reduce_193(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 691) + def _reduce_204(val, _values, result) result = Factory.literal(val[0][:value]); loc result, val[0] result end .,., -# reduce 194 omitted +# reduce 205 omitted -module_eval(<<'.,.,', 'egrammar.ra', 671) - def _reduce_195(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 697) + def _reduce_206(val, _values, result) result = nil result end .,., -# reduce 196 omitted - -# reduce 197 omitted - -# reduce 198 omitted - -# reduce 199 omitted - -# reduce 200 omitted - -# reduce 201 omitted - -# reduce 202 omitted - -# reduce 203 omitted - -# reduce 204 omitted - -# reduce 205 omitted - -# reduce 206 omitted - # reduce 207 omitted # reduce 208 omitted @@ -2378,8 +2492,30 @@ module_eval(<<'.,.,', 'egrammar.ra', 671) # reduce 211 omitted -module_eval(<<'.,.,', 'egrammar.ra', 694) - def _reduce_212(val, _values, result) +# reduce 212 omitted + +# reduce 213 omitted + +# reduce 214 omitted + +# reduce 215 omitted + +# reduce 216 omitted + +# reduce 217 omitted + +# reduce 218 omitted + +# reduce 219 omitted + +# reduce 220 omitted + +# reduce 221 omitted + +# reduce 222 omitted + +module_eval(<<'.,.,', 'egrammar.ra', 720) + def _reduce_223(val, _values, result) result = nil result end diff --git a/lib/puppet/pops/parser/epp_parser.rb b/lib/puppet/pops/parser/epp_parser.rb index f5ab90d3d..45e1b64bd 100644 --- a/lib/puppet/pops/parser/epp_parser.rb +++ b/lib/puppet/pops/parser/epp_parser.rb @@ -23,8 +23,6 @@ class Puppet::Pops::Parser::EppParser < Puppet::Pops::Parser::Parser # Performs the parsing and returns the resulting model. # The lexer holds state, and this is setup with {#parse_string}, or {#parse_file}. # - # TODO: Drop support for parsing a ruby file this way (should be done where it is decided - # which file to load/run (i.e. loaders), and initial file to run # TODO: deal with options containing origin (i.e. parsing a string from externally known location). # TODO: should return the model, not a Hostclass # @@ -47,5 +45,7 @@ class Puppet::Pops::Parser::EppParser < Puppet::Pops::Parser::Parser return main ensure @lexer.clear + @namestack = [] + @definitions = [] end end diff --git a/lib/puppet/pops/parser/heredoc_support.rb b/lib/puppet/pops/parser/heredoc_support.rb index 341b18a4e..a59c41522 100644 --- a/lib/puppet/pops/parser/heredoc_support.rb +++ b/lib/puppet/pops/parser/heredoc_support.rb @@ -171,7 +171,7 @@ module Puppet::Pops::Parser::HeredocSupport # Given offset is offset in the subspace def offset_on_line(offset) - @locator.offset_on_line + @leading_line_offset + @locator.offset_on_line(offset) + @leading_line_offset end # Given offset is offset in the subspace diff --git a/lib/puppetx/puppetlabs/syntax_checkers/json.rb b/lib/puppetx/puppetlabs/syntax_checkers/json.rb index e31f52688..6af6c59c6 100644 --- a/lib/puppetx/puppetlabs/syntax_checkers/json.rb +++ b/lib/puppetx/puppetlabs/syntax_checkers/json.rb @@ -1,5 +1,6 @@ # A syntax checker for JSON. # @api public +require 'puppetx/puppet/syntax_checker' class Puppetx::Puppetlabs::SyntaxCheckers::Json < Puppetx::Puppet::SyntaxChecker # Checks the text for JSON syntax issues and reports them to the given acceptor. @@ -14,26 +15,26 @@ class Puppetx::Puppetlabs::SyntaxCheckers::Json < Puppetx::Puppet::SyntaxChecker # @return [Boolean] Whether the checked string had issues (warnings and/or errors) or not. # @api public # - def check(text, syntax, acceptor, location_info={}) + def check(text, syntax, acceptor, source_pos) raise ArgumentError.new("Json syntax checker: the text to check must be a String.") unless text.is_a?(String) raise ArgumentError.new("Json syntax checker: the syntax identifier must be a String, e.g. json, data+json") unless syntax.is_a?(String) raise ArgumentError.new("Json syntax checker: invalid Acceptor, got: '#{acceptor.class.name}'.") unless acceptor.is_a?(Puppet::Pops::Validation::Acceptor) - raise ArgumentError.new("Json syntax checker: location_info must be a Hash") unless info.is_a?(Hash) + #raise ArgumentError.new("Json syntax checker: location_info must be a Hash") unless location_info.is_a?(Hash) begin JSON.parse(text) rescue => e # Cap the message to 100 chars and replace newlines - msg = "Json syntax checker:: Cannot parse invalid JSON string. \"#{e.message().slice(0,100).gsub(/\r?\n/, "\\n")}\"" + msg = "JSON syntax checker: Cannot parse invalid JSON string. \"#{e.message().slice(0,100).gsub(/\r?\n/, "\\n")}\"" # TODO: improve the pops API to allow simpler diagnostic creation while still maintaining capabilities # and the issue code. (In this case especially, where there is only a single error message being issued). # issue = Puppet::Pops::Issues::issue(:ILLEGAL_JSON) { msg } - source_pos = Puppet::Pops::Adapters::SourcePosAdapter.new() - source_pos.line = location_info[:line] - source_pos.pos = location_info[:pos] - acceptor.accept(Puppet::Pops::Validation::Diagnostic.new(:error, issue, location_info[:file], source_pos, {})) +# source_pos = Puppet::Pops::Adapters::SourcePosAdapter.new() +# source_pos.line = location_info[:line] +# source_pos.pos = location_info[:pos] + acceptor.accept(Puppet::Pops::Validation::Diagnostic.new(:error, issue, source_pos.locator.file, source_pos, {})) end end end From f409405b4ff15e1e32de66c97593a6bd4d024644 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 20 Feb 2014 19:54:06 +0100 Subject: [PATCH 762/800] (maint) Add eAllContainers enumerator to Containment This utility enumerator makes it easier to find containing objects. --- lib/puppet/pops/containment.rb | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/lib/puppet/pops/containment.rb b/lib/puppet/pops/containment.rb index b28695dc9..6f38f0c1b 100644 --- a/lib/puppet/pops/containment.rb +++ b/lib/puppet/pops/containment.rb @@ -10,6 +10,35 @@ module Puppet::Pops::Containment EAllContentsEnumerator.new(self) end + def eAllContainers + EAllContainersEnumerator.new(self) + end + + class EAllContainersEnumerator + include Enumerable + + def initialize o + @element = o + end + + def each &block + if block_given? + eAllContainers(@element, &block) + else + self + end + end + + def eAllContainers(element, &block) + x = element.eContainer + while !x.nil? do + yield x + x = x.eContainer + end + end + + end + class EAllContentsEnumerator include Enumerable def initialize o From 420edbdb9e07d9f8b3d68b88f4cc4545e5670a63 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 20 Feb 2014 19:56:24 +0100 Subject: [PATCH 763/800] (PUP-30) Add SubLocatableExpression to handle "embedded" expressions This expression helps with handling areas of the source that requires douple/separate lexing (heredoc, etc). It establishes a context from which its children can resolve their offsets. This is requires to make the offset calculations fast (no need to relocate them). Instead the relative location is captured in a SubLocateableExpression. --- lib/puppet/pops/model/model.rb | 71 +++++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 6 deletions(-) diff --git a/lib/puppet/pops/model/model.rb b/lib/puppet/pops/model/model.rb index 835c9735b..824097c48 100644 --- a/lib/puppet/pops/model/model.rb +++ b/lib/puppet/pops/model/model.rb @@ -261,6 +261,71 @@ module Puppet::Pops::Model contains_one_uni 'body', Expression end + class LocatableExpression < Expression + has_many_attr 'line_offsets', Integer + has_attr 'locator', Object, :lowerBound => 1, :transient => true + + module ClassModule + # Go through the gymnastics of making either value or pattern settable + # with synchronization to the other form. A derived value cannot be serialized + # and we want to serialize the pattern. When recreating the object we need to + # recreate it from the pattern string. + # The below sets both values if one is changed. + # + def locator + unless result = getLocator + setLocator(result = Puppet::Pops::Parser::Locator.locator(source_text, source_ref(), line_offsets)) + end + result + end + end + end + + # Contains one expression which has offsets reported virtually (offset against the Program's + # overall locator. + # + class SubLocatableExpression < Expression + contains_one_uni 'expr', Expression, :lowerBound => 1 + + # How much each line start is offset (i.e. "margin") + has_many_attr 'line_offsets', Integer + # Number of preceding lines + has_attr 'leading_line_count', Integer + # This expression's offset is the same as the sublocators leading offset + # has_attr 'leading_offset', Integer + + # + has_attr 'leading_line_offset', Integer + + # The locator for the sub-locatable's children (not for the sublocator itself) + # The locator is not serialized + # + has_attr 'locator', Object, :lowerBound => 1, :transient => true + + module ClassModule + def locator + unless result = getLocator + # Adapt myself to get the Locator for me + adapter = Puppet::Pops::Adapters::SourcePosAdapter.adapt(self) + # Get the program (root), and deal with case when not contained in a program + program = eAllContainers.find {|c| c.is_a?(Program) } + source_ref = program.nil? ? '' : program.source_ref + + # An outer locator is needed since SubLocator only deals with offsets. This outer locator + # has 0,0 as origin. + outer_locator = Puppet::Pops::Parser::Locator.locator(adpater.extract_text, source_ref, line_offsets) + + # Create a sublocator that describes an offset from the outer + # TODO_HEREDOC: Sublocator should move out from HeredocSupport + result = Puppet::Pops::Parser::HeredocSupport::SubLocator.new(outer_locator, + leading_line_count, offset, leading_line_offset) + setLocator(result) + end + result + end + end + end + # A heredoc is a wrapper around a LiteralString or a ConcatenatedStringExpression with a specification # of syntax. The expectation is that "syntax" has meaning to a validator. A syntax of nil or '' means # "unspecified syntax". @@ -527,12 +592,6 @@ module Puppet::Pops::Model has_attr 'locator', Object, :lowerBound => 1, :transient => true module ClassModule - # Go through the gymnastics of making either value or pattern settable - # with synchronization to the other form. A derived value cannot be serialized - # and we want to serialize the pattern. When recreating the object we need to - # recreate it from the pattern string. - # The below sets both values if one is changed. - # def locator unless result = getLocator setLocator(result = Puppet::Pops::Parser::Locator.locator(source_text, source_ref(), line_offsets)) From e93eeaa99792980659437f5d7824228b5ace5b7f Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 20 Feb 2014 19:59:25 +0100 Subject: [PATCH 764/800] (PUP-30) Add support for the SubLocatableExpression This adds (part) of the support for SubLocatableExpression. This expression can now be evaluated, and its locator will be found by its children when they are searching for a locator. --- lib/puppet/pops/adapters.rb | 12 ++++++++++-- lib/puppet/pops/evaluator/evaluator_impl.rb | 5 +++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/puppet/pops/adapters.rb b/lib/puppet/pops/adapters.rb index 54e30f952..f40ddd0bf 100644 --- a/lib/puppet/pops/adapters.rb +++ b/lib/puppet/pops/adapters.rb @@ -33,6 +33,9 @@ module Puppet::Pops::Adapters end def locator + # The locator is always the parent locator, all positioned objects are positioned within their + # parent. If a positioned object also has a locator that locator is for its children! + # @locator ||= find_locator(@adapted.eContainer) end @@ -40,8 +43,13 @@ module Puppet::Pops::Adapters if o.nil? raise ArgumentError, "InternalError: SourcePosAdapter for something that has no locator among parents" end - return o.locator if o.is_a?(Puppet::Pops::Model::Program) - if adapter = self.class.get(o) + case + when o.is_a?(Puppet::Pops::Model::Program) + return o.locator + # TODO_HEREDOC use case of SubLocator instead + when o.respond_to?(:locator) && !(found_locator = o.locator).nil? + return found_locator + when adapter = self.class.get(o) return adapter.locator else find_locator(o.eContainer) diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index d5187c14b..64eecf9dc 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -730,6 +730,11 @@ class Puppet::Pops::Evaluator::EvaluatorImpl end end + # SubLocatable is simply an expression that holds location information + def eval_SubLocatableExpression o, scope + evaluate(o.expr, scope) + end + # Evaluates Puppet DSL Heredoc def eval_HeredocExpression o, scope result = evaluate(o.text_expr, scope) From 74873c4cb78a3be3a6b7952fb2acea607ebefc29 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 21 Feb 2014 22:30:40 +0100 Subject: [PATCH 765/800] (PUP-30) Add SUBLOCATE token to lexer to communicate subspace This token is needed since everything inside the heredoc lives in its own location space and needs to be anchored inside something. This token achieves this by giving the parser it needs (offset and length of the text from which heredoc is extracted). --- lib/puppet/pops/parser/egrammar.ra | 6 +- lib/puppet/pops/parser/eparser.rb | 1487 +++++++++++---------- lib/puppet/pops/parser/heredoc_support.rb | 4 + lib/puppet/pops/parser/parser_support.rb | 4 + 4 files changed, 755 insertions(+), 746 deletions(-) diff --git a/lib/puppet/pops/parser/egrammar.ra b/lib/puppet/pops/parser/egrammar.ra index 2eab1b1b9..90035e175 100644 --- a/lib/puppet/pops/parser/egrammar.ra +++ b/lib/puppet/pops/parser/egrammar.ra @@ -16,7 +16,7 @@ token MATCH NOMATCH REGEX IN_EDGE OUT_EDGE IN_EDGE_SUB OUT_EDGE_SUB token IN UNLESS PIPE token LAMBDA SELBRACE token NUMBER -token HEREDOC +token HEREDOC SUBLOCATE token RENDER_STRING RENDER_EXPR EPP_START token LOW @@ -661,8 +661,8 @@ dqtail # TODO_HEREDOC heredoc - : HEREDOC string { result = Factory.HEREDOC(val[0][:value], val[1]); loc result, val[0], val[1] } - | HEREDOC dq_string { result = Factory.HEREDOC(val[0][:value], val[1]); loc result, val[0], val[1] } + : HEREDOC SUBLOCATE string { result = Factory.HEREDOC(val[0][:value], val[1]); loc result, val[0], val[1] } + | HEREDOC SUBLOCATE dq_string { result = Factory.HEREDOC(val[0][:value], val[1]); loc result, val[0], val[1] } # TODO_EPP epp_expression diff --git a/lib/puppet/pops/parser/eparser.rb b/lib/puppet/pops/parser/eparser.rb index 75676e6d3..3850f3c38 100644 --- a/lib/puppet/pops/parser/eparser.rb +++ b/lib/puppet/pops/parser/eparser.rb @@ -30,194 +30,193 @@ module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 736) ##### State transition tables begin ### clist = [ -'57,59,267,-132,51,237,53,-130,257,-221,-212,257,255,223,242,305,57,59', -'234,241,344,223,290,14,223,57,59,244,230,41,239,48,243,50,45,126,49', -'69,65,125,43,68,46,47,268,-132,66,13,220,-130,67,-221,-212,12,135,288', -'126,133,57,59,125,70,51,135,53,385,133,42,339,234,338,64,60,62,63,61', -'126,70,240,52,125,14,122,354,322,256,70,41,60,48,257,50,45,311,49,69', -'65,60,43,68,46,47,57,59,66,13,57,59,67,324,126,12,57,59,125,219,51,126', -'53,70,79,125,126,126,326,42,125,125,308,64,60,62,63,307,102,14,106,52', -'101,246,304,41,247,48,135,50,45,133,49,69,65,72,43,68,46,47,249,248', -'66,13,105,339,67,338,266,12,264,70,331,332,57,59,333,70,51,223,53,383', -'60,42,210,336,80,64,60,62,63,75,77,76,78,52,74,14,340,57,59,342,289', -'41,186,48,264,50,45,266,49,69,65,264,43,68,46,47,350,351,66,13,288,288', -'67,74,153,12,151,114,361,282,57,59,362,70,51,135,53,381,133,42,281,266', -'127,64,60,62,63,280,365,114,115,52,266,14,114,369,342,371,70,41,372', -'48,373,50,45,374,49,69,65,60,43,68,46,47,375,111,66,13,377,378,67,379', -'264,12,57,59,74,71,51,386,53,70,79,387,388,389,,42,,,,64,60,62,63,,102', -'14,106,52,101,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,105,,67,,', -'12,57,59,,,51,,53,70,79,81,82,,,42,,,80,64,60,62,63,,102,14,106,52,101', -',,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,105,,67,,,12,57,59,,,51', -',53,70,79,81,82,,,42,,,80,64,60,62,63,,102,14,106,52,101,,,41,,48,,50', -'108,,49,69,65,,43,68,,,,,66,13,105,,67,,,12,57,59,,,51,,53,70,79,81', -'82,,,42,,,80,64,60,62,63,,102,14,106,52,101,,,41,,48,,50,108,,49,69', -'65,,43,68,,,,,66,13,105,,67,,,12,57,59,,,51,,53,70,79,,,,,42,,,80,64', -'60,62,63,,102,14,106,52,101,,,41,,48,,50,45,,49,69,65,,43,68,46,47,', -',66,13,105,,67,,,12,57,59,,,51,,53,70,79,,,,,42,,,,64,60,62,63,,102', -'14,106,52,101,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,105,,67,,', -'12,57,59,,,51,,53,70,79,,,,,42,,,,64,60,62,63,,102,14,106,52,101,,,41', -',48,,50,108,,49,69,65,,43,68,,,,,66,13,105,,67,,,12,57,59,,,51,,53,70', -',,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,', -',,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52', -',,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53', -'70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68', -',,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52', -',,,41,,48,,50,121,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53', -'70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68', -',,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52', -',,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53', -'70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68', -',,,,66,13,,,67,,,12,,,,,57,59,,70,51,,53,286,,42,,,,64,60,62,63,,,,', -'52,,14,,,,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57', -'59,,,51,138,53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108', -',49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,140,53,70,,,,,,42,,', -',64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,', -',67,,,12,,,,,57,59,,70,51,,53,143,,42,,,,64,60,62,63,,,,,52,,14,,,,', -',41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53', -'70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68', -',,,,66,13,,,67,,,12,,,,,57,59,,70,51,,53,292,,42,,,,64,60,62,63,,,,', -'52,,14,,,,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,', -',,,57,59,,70,51,,53,294,,42,,,,64,60,62,63,,,,,52,,14,,,,,,41,,48,,50', -'45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42', -',,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13', -',,67,,,12,,,,,57,59,,70,51,,53,360,,42,,,,64,60,62,63,,,,,52,,14,,,', -',,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51', -',53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,45,,49,69,65,,43', -'68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63', -',,14,,52,,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57', -'59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,45,,49', -'69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64', -'60,62,63,,,14,,52,,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,', -'67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48', -',50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70,,', -',,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,45,,49,69,65,,43,68,46,47', -',,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52', -',,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51', -',53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,', -'43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,', -',14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59', -',,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69', -'65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62', -'63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57', -'59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49', +'57,59,-132,268,51,258,53,-130,79,-212,-221,126,258,126,79,125,256,125', +'306,291,222,345,102,14,106,222,101,222,102,41,106,48,101,50,45,233,49', +'69,65,236,43,68,46,47,-132,269,66,13,105,-130,67,-212,-221,12,105,219', +'57,59,248,247,51,70,53,386,238,126,233,42,79,125,80,64,60,122,62,63', +'61,126,241,14,52,125,102,240,106,41,101,48,245,50,45,246,49,69,65,289', +'43,68,46,47,126,126,66,13,125,125,67,355,105,12,57,59,239,243,51,257', +'53,70,242,340,258,339,312,42,79,323,229,64,60,309,62,63,340,14,339,325', +'52,218,102,41,106,48,101,50,45,327,49,69,65,72,43,68,46,47,126,308,66', +'13,125,305,67,57,59,12,105,265,57,59,267,290,51,70,53,384,86,85,332', +'42,79,81,82,64,60,333,62,63,80,334,222,14,52,209,102,337,106,41,101', +'48,289,50,45,87,49,69,65,341,43,68,46,47,343,74,66,13,185,265,67,267', +'105,12,265,351,57,59,352,114,51,70,53,382,289,74,152,42,150,148,362', +'64,60,283,62,63,363,57,59,14,52,57,59,282,267,41,127,48,281,50,45,366', +'49,69,65,114,43,68,46,47,115,267,66,13,114,370,67,343,372,12,57,59,373', +'374,51,135,53,70,133,135,375,376,133,42,111,378,379,64,60,380,62,63', +'265,14,74,71,52,387,70,41,388,48,70,50,108,389,49,69,65,60,43,68,390', +'60,,,66,13,,,67,,,12,57,59,,,51,,53,70,75,77,76,78,,42,,,,64,60,,62', +'63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57', +'59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49', '69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60', -'62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12', -'57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108', -',49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64', -'60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67', -',,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50', -'108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,', -',,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13', -',,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48', +',62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12', +'57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,45,', +'49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,', +',64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,', +',67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48', ',50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,', -'42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66', -'13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41', +'42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66', +'13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41', ',48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,', -',,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,', -',,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52', -',,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53', -'70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68', -',,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52', -',,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53', -'70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68', -',,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52', -',,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53', -'70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68', -',,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52', -',,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53', -'70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68', -',,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52', -',,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,,,,,57,59,,70', -'51,,53,345,,42,,,185,64,60,62,63,,,,,52,,14,,,,,,41,,48,,50,108,,49', +',,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,', +',,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52', +',,41,,48,,50,121,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53', +'70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68', +',,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,', +'52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51', +',53,70,,,,,,42,79,,,64,60,,62,63,,14,,,52,,102,41,106,48,101,50,108', +',49,69,65,,43,68,,,,,66,13,,,67,,,12,105,,57,59,,,51,70,53,287,86,85', +',42,,81,82,64,60,,62,63,80,,,14,52,57,59,,,41,,48,,50,45,87,49,69,65', +',43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,138,53,70,,135,,,133,42,,', +',64,60,,62,63,,14,,,52,,,41,,48,70,50,108,,49,69,65,,43,68,,60,,,66', +'13,57,59,67,,,12,57,59,,,51,140,53,70,,,,,,42,,,,64,60,,62,63,,14,,', +'52,,,41,,48,135,50,108,133,49,69,65,,43,68,,,,,66,13,,,67,,,12,,70,57', +'59,,,51,70,53,143,,,60,42,,,,64,60,,62,63,,,,14,52,,,,,41,,48,,50,108', +',49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,79,,', +'64,60,,62,63,,14,,,52,,102,41,106,48,101,50,108,,49,69,65,,43,68,,,', +',66,13,,,67,,,12,105,,57,59,,,51,70,53,293,,,,42,,81,82,64,60,,62,63', +'80,,,14,52,,,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12', +'57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,45,', +'49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,79', +',,64,60,,62,63,,14,,,52,,102,41,106,48,101,50,108,,49,69,65,,43,68,', +',,,66,13,,,67,,,12,105,,57,59,,,51,70,53,361,,,,42,,,,64,60,,62,63,80', +',,14,52,,,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57', +'59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,45,,49', +'69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64', +'60,,62,63,,14,,,52,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,', +'67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48', +',50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70,,', +',,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,45,,49,69,65,,43,68,46,47', +',,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52', +',,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51', +',53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,45,,49,69,65,,43', +'68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63', +',14,,,52,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57', +'59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49', '69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60', -'62,63,,,14,,52,,,,188,205,199,206,50,200,208,201,197,195,,190,203,,', -',,66,13,209,204,202,,,12,57,59,,,51,,53,70,,,,,207,189,,,,64,60,62,63', -',,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59', -',,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69', -'65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62', -'63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57', -'59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49', -'69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60', -'62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12', -'57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108', +',62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12', +'57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108', ',49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64', -'60,62,63,,,14,,52,,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,', -'67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,217,52,,,,41,', -'48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,', -',,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,,', -',66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52,', +'60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67', +',,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50', +'108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,', +',,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13', +',,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48', +',50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,', +'42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66', +'13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41', +',48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,', +',,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,', +',,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52', ',,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53', -'70,,,,,,42,,,,64,60,62,63,,,14,225,52,,,,41,,48,,50,108,,49,69,65,,43', -'68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14', -',52,,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51', -'314,53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65', -',43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63', -',,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59', -',,51,313,53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49', +'70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68', +',,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,', +'52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51', +',53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,', +'43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63', +',14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59', +',,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69', +'65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62', +'63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57', +'59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49', '69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60', -'62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12', -'57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108', +',62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12', +'57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108', ',49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64', -'60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67', -',,12,,,,,57,59,,70,51,,53,316,,42,,,,64,60,62,63,,,,,52,,14,,,,,,41', +'60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67', +',,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50', +'108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,', +',,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13', +',,67,,,12,,,57,59,,,51,70,53,346,,,,42,,,184,64,60,,62,63,,,,14,52,', +',,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53', +'70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,187,204,198,205,50,199,207,200', +'196,194,,189,202,,,,,66,13,208,203,201,,,12,57,59,,,51,,53,70,,,,,206', +'188,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66', +'13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41', ',48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,', -',,,,42,,,,64,60,62,63,,,14,,52,,,,41,,48,,50,108,,49,69,65,,43,68,,', -',,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,62,63,,,14,,52', -',,,188,205,199,206,50,200,208,201,197,195,,190,203,,,,,66,13,209,204', -'202,,,12,,,,,,,,70,,,,,207,189,,,,64,60,62,63,79,,,,52,258,,,,98,99', -'100,95,90,102,,106,,101,,,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,', -'83,84,86,85,88,89,,81,82,79,,229,,,80,,,,98,99,100,95,90,102,,106,,101', -'87,,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81', -'82,79,,228,,,80,,,,98,99,100,95,90,102,,106,,101,87,,91,93,92,94,,,', -',,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,,,79,,,80,,245,', -',,98,99,100,95,90,102,,106,87,101,,,91,93,92,94,,,,,,,,,,,,,,,,105,', -',,97,96,,,83,84,86,85,88,89,,81,82,79,,227,,,80,,,,98,99,100,95,90,102', -',106,,101,87,,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85', -'88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102,,106,,101,87,,91,93,92', -'94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,226,,', -'80,,,,98,99,100,95,90,102,,106,,101,87,,91,93,92,94,,,,,,,,,,,,,,,,105', -',,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102', -',106,,101,87,215,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86', -'85,88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102,,106,,101,87,,91,93', -'92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,', -'80,,,,98,99,100,95,90,102,,106,,101,87,,91,93,92,94,,,,,,,,,,,,,,,,105', -',,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102', -',106,,101,87,,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85', -'88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102,,106,,101,87,,91,93,92', -'94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80', -',,,98,99,100,95,90,102,,106,,101,87,,91,93,92,94,,,,,,,,,,,,,,,,105', -',,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102', -',106,,101,87,,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85', -'88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102,,106,79,101,87,,91,93', -'92,94,,,,,,,102,,106,,101,,,,,105,,,,97,96,,79,83,84,86,85,88,89,,81', -'82,105,,,,102,80,106,,101,,86,85,,,,81,82,,,87,,,80,,,,,,,,105,,,,,', -'87,,,,86,85,,,,81,82,79,,,,,80,,,,98,99,100,95,90,102,,106,,101,87,', -'91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79', -',,,,80,,,,98,99,100,95,90,102,262,106,,101,87,,91,93,92,94,,,,,,,,,', -',,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,103,,,80,,,,98,99', -'100,95,90,102,,106,79,101,87,,91,93,92,94,,,,,,,102,,106,,101,,,,,105', -',,,97,96,,,83,84,86,85,88,89,,81,82,105,,,,,80,,79,83,84,86,85,,,,81', -'82,,,87,,102,80,106,79,101,,,,,,,,,,,87,,102,,106,,101,,,,,105,,,,,', -',,83,84,86,85,,,,81,82,105,,,,,80,,79,83,84,86,85,88,89,,81,82,,,87', -',102,80,106,79,101,,,,,,,,,,,87,90,102,,106,,101,,,91,,105,,,,,,,,83', -'84,86,85,88,89,,81,82,105,,,,,80,,79,83,84,86,85,88,89,,81,82,,,87,90', -'102,80,106,79,101,,,91,,,,,,,,87,90,102,,106,,101,,,91,,105,,,,,,,,83', -'84,86,85,88,89,,81,82,105,,,,,80,,79,83,84,86,85,88,89,,81,82,,,87,90', -'102,80,106,,101,,,91,,,,,,,,87,,,,,,,,,,,105,,,,,79,,,83,84,86,85,88', -'89,,81,82,95,90,102,,106,80,101,,,91,93,92,94,,,,,,,87,,,,,,,,,105,', -',,,79,,,83,84,86,85,88,89,,81,82,95,90,102,,106,80,101,,,91,93,92,94', -',,,,,,87,,,,,,,,,105,,,,,96,,,83,84,86,85,88,89,,81,82,79,,,,,80,,,', -'98,99,100,95,90,102,,106,,101,87,,91,93,92,94,,,,,,,,,,,,,,,,105,,,', -'97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102,,106', -',101,87,,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89', -',81,82,79,,,,,80,,,,98,99,100,95,90,102,,106,,101,87,,91,93,92,94,,', -',,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98', -'99,100,95,90,102,,106,,101,87,,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97', -'96,,,83,84,86,85,88,89,,81,82,,276,205,275,206,80,273,208,277,271,270', -',272,274,,,,,,87,209,204,278,276,205,275,206,,273,208,277,271,270,,272', -'274,,,207,279,,,209,204,278,276,205,275,206,,273,208,277,271,270,,272', -'274,,,207,279,,,209,204,278,,,,,,,,,,,,,,,,207,279' ] - racc_action_table = arr = ::Array.new(6168, nil) +',,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,', +',,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52', +',,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53', +'70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68', +',,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,79,,,64,60,,62,63,,14', +',,52,,102,41,106,48,101,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12', +'105,,57,59,,,51,70,53,295,,,,42,,81,82,64,60,,62,63,80,,,14,52,,,,,41', +',48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70', +',,,,,42,,,,64,60,,62,63,,14,216,,52,,,41,,48,,50,108,,49,69,65,,43,68', +',,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,', +'52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51', +',53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,', +'43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63', +',14,224,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57', +'59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49', +'69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,315,53,70,,,,,,42,,,,64', +'60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67', +',,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50', +'108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,314,53,70,,,,,,42', +',,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13', +',,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48', +',50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,', +'42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66', +'13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,79,,,64,60,,62,63,,14,,,52,,102', +'41,106,48,101,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,105,,57,59', +',,51,70,53,317,,,,42,,81,82,64,60,,62,63,80,,,14,52,,,,,41,,48,,50,108', +',49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64', +'60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67', +',,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,187,204,198', +'205,50,199,207,200,196,194,,189,202,,,,,66,13,208,203,201,,,12,,,,,', +',,70,,,,,206,188,,,,64,60,79,62,63,,,,,52,,98,99,100,95,90,102,,106', +',101,,,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,', +'81,82,79,,228,,,80,,,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94', +',,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,227,,,80', +',,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105', +',,,97,96,,,83,84,86,85,88,89,,81,82,,79,,,,80,244,,,,98,99,100,95,90', +'102,,106,,101,87,,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86', +'85,88,89,,81,82,79,,226,,,80,,,,98,99,100,95,90,102,,106,,101,,87,91', +'93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,', +',,,80,,,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,,,,,', +',,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,225,,,80,,,,98,99,100', +'95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83', +'84,86,85,88,89,,81,82,,79,,,,80,,,,,98,99,100,95,90,102,,106,,101,87', +'214,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81', +'82,79,,,,,80,,,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,', +',,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99', +'100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96', +',,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102,,106,,101', +',87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81', +'82,79,,,,,80,,,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,', +',,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99', +'100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96', +',,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102,,106,,101', +',87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81', +'82,79,,,,,80,,,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,', +',,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99', +'100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96', +',,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102,263,106', +',101,,87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89', +',81,82,79,,103,,,80,,,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94', +',,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,,79,,,,80,259', +',,,98,99,100,95,90,102,,106,79,101,87,,91,93,92,94,,,,,,,102,,106,,101', +',,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,105,,,79,,80,,,83,84,86', +'85,,,,81,82,102,,106,87,101,80,,,,79,,,,,,,,,,,87,,,102,,106,105,101', +',79,,,,,83,84,86,85,,,,81,82,102,,106,,101,80,105,,,,,,,,83,84,86,85', +'88,89,87,81,82,,,,105,,80,,,79,,,83,84,86,85,88,89,,81,82,87,90,102', +',106,80,101,,79,91,,,,,,,,,,,87,90,102,,106,,101,,105,91,,,,,,,83,84', +'86,85,88,89,,81,82,,,,105,,80,,,79,,,83,84,86,85,88,89,,81,82,87,90', +'102,,106,80,101,,79,91,,,,,,,,,,,87,90,102,,106,,101,,105,91,,,,,,,83', +'84,86,85,88,89,,81,82,,,,105,,80,,,,,79,83,84,86,85,88,89,,81,82,87', +',95,90,102,80,106,,101,,79,91,93,92,94,,,,,,87,,95,90,102,,106,,101', +',105,91,93,92,94,,,,83,84,86,85,88,89,,81,82,,,,105,,80,,,96,,,83,84', +'86,85,88,89,,81,82,87,79,,,,80,,,,,98,99,100,95,90,102,,106,,101,87', +',91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82', +'79,,,,,80,,,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,', +',,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99,100', +'95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83', +'84,86,85,88,89,,81,82,,,,,,80,277,204,276,205,,274,207,278,272,271,', +'273,275,,87,,,,,208,203,279,277,204,276,205,,274,207,278,272,271,,273', +'275,,,206,280,,,208,203,279,277,204,276,205,,274,207,278,272,271,,273', +'275,,,206,280,,,208,203,279,,,,,,,,,,,,,,,,206,280' ] + racc_action_table = arr = ::Array.new(6060, nil) idx = 0 clist.each do |str| str.split(',', -1).each do |i| @@ -227,215 +226,213 @@ clist = [ end clist = [ -'0,0,198,197,0,129,0,195,224,202,203,295,151,114,137,234,237,237,123', -'137,295,151,224,0,234,49,49,139,121,0,129,0,139,0,0,48,0,0,0,48,0,0', -'0,0,198,197,0,0,114,195,0,202,203,0,237,254,121,237,372,372,121,0,372', -'49,372,372,49,0,336,128,336,0,0,0,0,0,304,237,131,0,304,372,45,304,259', -'160,49,372,237,372,160,372,372,240,372,372,372,49,372,372,372,372,60', -'60,372,372,201,201,372,263,45,372,5,5,45,113,5,108,5,372,162,108,199', -'200,265,372,199,200,236,372,372,372,372,235,162,5,162,372,162,142,232', -'5,142,5,201,5,5,201,5,5,5,5,5,5,5,5,147,147,5,5,162,292,5,292,269,5', -'231,201,283,285,371,371,287,5,371,288,371,371,201,5,104,291,162,5,5', -'5,5,8,8,8,8,5,154,371,293,239,239,294,223,371,102,371,298,371,371,299', -'371,371,371,300,371,371,371,371,301,302,371,371,221,306,371,73,71,371', -'61,217,319,216,369,369,321,371,369,239,369,369,239,371,214,323,46,371', -'371,371,371,212,330,331,40,371,192,369,39,339,340,342,239,369,343,369', -'347,369,369,348,369,369,369,239,369,369,369,369,349,38,369,369,355,356', -'369,359,191,369,185,185,6,1,185,376,185,369,168,380,382,384,,369,,,', -'369,369,369,369,,168,185,168,369,168,,,185,,185,,185,185,,185,185,185', -',185,185,,,,,185,185,168,,185,,,185,12,12,,,12,,12,185,167,168,168,', -',185,,,168,185,185,185,185,,167,12,167,185,167,,,12,,12,,12,12,,12,12', -'12,,12,12,,,,,12,12,167,,12,,,12,13,13,,,13,,13,12,166,167,167,,,12', -',,167,12,12,12,12,,166,13,166,12,166,,,13,,13,,13,13,,13,13,13,,13,13', -',,,,13,13,166,,13,,,13,14,14,,,14,,14,13,163,166,166,,,13,,,166,13,13', -'13,13,,163,14,163,13,163,,,14,,14,,14,14,,14,14,14,,14,14,,,,,14,14', -'163,,14,,,14,351,351,,,351,,351,14,161,,,,,14,,,163,14,14,14,14,,161', -'351,161,14,161,,,351,,351,,351,351,,351,351,351,,351,351,351,351,,,351', -'351,161,,351,,,351,338,338,,,338,,338,351,109,,,,,351,,,,351,351,351', -'351,,109,338,109,351,109,,,338,,338,,338,338,,338,338,338,,338,338,', -',,,338,338,109,,338,,,338,188,188,,,188,,188,338,107,,,,,338,,,,338', -'338,338,338,,107,188,107,338,107,,,188,,188,,188,188,,188,188,188,,188', -'188,,,,,188,188,107,,188,,,188,41,41,,,41,,41,188,,,,,,188,,,,188,188', -'188,188,,,41,,188,,,,41,,41,,41,41,,41,41,41,,41,41,,,,,41,41,,,41,', -',41,42,42,,,42,,42,41,,,,,,41,,,,41,41,41,41,,,42,,41,,,,42,,42,,42', -'42,,42,42,42,,42,42,,,,,42,42,,,42,,,42,43,43,,,43,,43,42,,,,,,42,,', -',42,42,42,42,,,43,,42,,,,43,,43,,43,43,,43,43,43,,43,43,,,,,43,43,,', -'43,,,43,44,44,,,44,,44,43,,,,,,43,,,,43,43,43,43,,,44,,43,,,,44,,44', -',44,44,,44,44,44,,44,44,,,,,44,44,,,44,,,44,189,189,,,189,,189,44,,', -',,,44,,,,44,44,44,44,,,189,,44,,,,189,,189,,189,189,,189,189,189,,189', -'189,,,,,189,189,,,189,,,189,190,190,,,190,,190,189,,,,,,189,,,,189,189', -'189,189,,,190,,189,,,,190,,190,,190,190,,190,190,190,,190,190,,,,,190', -'190,,,190,,,190,322,322,,,322,,322,190,,,,,,190,,,,190,190,190,190,', -',322,,190,,,,322,,322,,322,322,,322,322,322,,322,322,,,,,322,322,,,322', -',,322,,,,,219,219,,322,219,,219,219,,322,,,,322,322,322,322,,,,,322', -',219,,,,,,219,,219,,219,219,,219,219,219,,219,219,219,219,,,219,219', -',,219,,,219,51,51,,,51,51,51,219,,,,,,219,,,,219,219,219,219,,,51,,219', -',,,51,,51,,51,51,,51,51,51,,51,51,,,,,51,51,,,51,,,51,52,52,,,52,52', -'52,51,,,,,,51,,,,51,51,51,51,,,52,,51,,,,52,,52,,52,52,,52,52,52,,52', -'52,,,,,52,52,,,52,,,52,,,,,53,53,,52,53,,53,53,,52,,,,52,52,52,52,,', -',,52,,53,,,,,,53,,53,,53,53,,53,53,53,,53,53,,,,,53,53,,,53,,,53,58', -'58,,,58,,58,53,,,,,,53,,,,53,53,53,53,,,58,,53,,,,58,,58,,58,58,,58', -'58,58,,58,58,,,,,58,58,,,58,,,58,,,,,226,226,,58,226,,226,226,,58,,', -',58,58,58,58,,,,,58,,226,,,,,,226,,226,,226,226,,226,226,226,,226,226', -'226,226,,,226,226,,,226,,,226,,,,,227,227,,226,227,,227,227,,226,,,', -'226,226,226,226,,,,,226,,227,,,,,,227,,227,,227,227,,227,227,227,,227', -'227,227,227,,,227,227,,,227,,,227,63,63,,,63,,63,227,,,,,,227,,,,227', -'227,227,227,,,63,,227,,,,63,,63,,63,63,,63,63,63,,63,63,,,,,63,63,,', -'63,,,63,,,,,308,308,,63,308,,308,308,,63,,,,63,63,63,63,,,,,63,,308', -',,,,,308,,308,,308,308,,308,308,308,,308,308,308,308,,,308,308,,,308', -',,308,72,72,,,72,,72,308,,,,,,308,,,,308,308,308,308,,,72,,308,,,,72', -',72,,72,72,,72,72,72,,72,72,72,72,,,72,72,,,72,,,72,307,307,,,307,,307', -'72,,,,,,72,,,,72,72,72,72,,,307,,72,,,,307,,307,,307,307,,307,307,307', -',307,307,307,307,,,307,307,,,307,,,307,74,74,,,74,,74,307,,,,,,307,', -',,307,307,307,307,,,74,,307,,,,74,,74,,74,74,,74,74,74,,74,74,74,74', -',,74,74,,,74,,,74,75,75,,,75,,75,74,,,,,,74,,,,74,74,74,74,,,75,,74', -',,,75,,75,,75,75,,75,75,75,,75,75,75,75,,,75,75,,,75,,,75,76,76,,,76', -',76,75,,,,,,75,,,,75,75,75,75,,,76,,75,,,,76,,76,,76,76,,76,76,76,,76', -'76,76,76,,,76,76,,,76,,,76,77,77,,,77,,77,76,,,,,,76,,,,76,76,76,76', -',,77,,76,,,,77,,77,,77,77,,77,77,77,,77,77,77,77,,,77,77,,,77,,,77,78', -'78,,,78,,78,77,,,,,,77,,,,77,77,77,77,,,78,,77,,,,78,,78,,78,78,,78', +'0,0,196,197,0,223,0,194,162,202,201,199,296,108,160,199,150,108,233', +'223,114,296,162,0,162,150,162,233,160,0,160,0,160,0,0,128,0,0,0,129', +'0,0,0,0,196,197,0,0,162,194,0,202,201,0,160,114,373,373,147,147,373', +'0,373,373,129,198,123,0,107,198,162,0,0,45,0,0,0,48,137,373,0,48,107', +'137,107,373,107,373,142,373,373,142,373,373,373,255,373,373,373,373', +'305,45,373,373,305,45,373,305,107,373,5,5,131,139,5,159,5,373,139,337', +'159,337,239,373,163,260,121,373,373,235,373,373,293,5,293,264,373,113', +'163,5,163,5,163,5,5,266,5,5,5,5,5,5,5,5,121,234,5,5,121,231,5,148,148', +'5,163,230,372,372,270,222,372,5,372,372,163,163,284,5,109,163,163,5', +'5,286,5,5,163,288,289,372,5,104,109,292,109,372,109,372,220,372,372', +'163,372,372,372,294,372,372,372,372,295,153,372,372,102,299,372,300', +'109,372,301,302,370,370,303,216,370,372,370,370,307,73,71,372,61,60', +'320,372,372,215,372,372,322,49,49,370,372,238,238,213,324,370,46,370', +'211,370,370,331,370,370,370,332,370,370,370,370,40,191,370,370,39,340', +'370,341,343,370,184,184,344,348,184,49,184,370,49,238,349,350,238,370', +'38,356,357,370,370,360,370,370,190,184,6,1,370,377,49,184,381,184,238', +'184,184,383,184,184,184,49,184,184,385,238,,,184,184,,,184,,,184,12', +'12,,,12,,12,184,8,8,8,8,,184,,,,184,184,,184,184,,12,,,184,,,12,,12', +',12,12,,12,12,12,,12,12,,,,,12,12,,,12,,,12,13,13,,,13,,13,12,,,,,,12', +',,,12,12,,12,12,,13,,,12,,,13,,13,,13,13,,13,13,13,,13,13,,,,,13,13', +',,13,,,13,14,14,,,14,,14,13,,,,,,13,,,,13,13,,13,13,,14,,,13,,,14,,14', +',14,14,,14,14,14,,14,14,,,,,14,14,,,14,,,14,352,352,,,352,,352,14,,', +',,,14,,,,14,14,,14,14,,352,,,14,,,352,,352,,352,352,,352,352,352,,352', +'352,352,352,,,352,352,,,352,,,352,339,339,,,339,,339,352,,,,,,352,,', +',352,352,,352,352,,339,,,352,,,339,,339,,339,339,,339,339,339,,339,339', +',,,,339,339,,,339,,,339,187,187,,,187,,187,339,,,,,,339,,,,339,339,', +'339,339,,187,,,339,,,187,,187,,187,187,,187,187,187,,187,187,,,,,187', +'187,,,187,,,187,41,41,,,41,,41,187,,,,,,187,,,,187,187,,187,187,,41', +',,187,,,41,,41,,41,41,,41,41,41,,41,41,,,,,41,41,,,41,,,41,42,42,,,42', +',42,41,,,,,,41,,,,41,41,,41,41,,42,,,41,,,42,,42,,42,42,,42,42,42,,42', +'42,,,,,42,42,,,42,,,42,43,43,,,43,,43,42,,,,,,42,,,,42,42,,42,42,,43', +',,42,,,43,,43,,43,43,,43,43,43,,43,43,,,,,43,43,,,43,,,43,44,44,,,44', +',44,43,,,,,,43,,,,43,43,,43,43,,44,,,43,,,44,,44,,44,44,,44,44,44,,44', +'44,,,,,44,44,,,44,,,44,188,188,,,188,,188,44,,,,,,44,,,,44,44,,44,44', +',188,,,44,,,188,,188,,188,188,,188,188,188,,188,188,,,,,188,188,,,188', +',,188,189,189,,,189,,189,188,,,,,,188,,,,188,188,,188,188,,189,,,188', +',,189,,189,,189,189,,189,189,189,,189,189,,,,,189,189,,,189,,,189,323', +'323,,,323,,323,189,,,,,,189,164,,,189,189,,189,189,,323,,,189,,164,323', +'164,323,164,323,323,,323,323,323,,323,323,,,,,323,323,,,323,,,323,164', +',218,218,,,218,323,218,218,164,164,,323,,164,164,323,323,,323,323,164', +',,218,323,236,236,,,218,,218,,218,218,164,218,218,218,,218,218,218,218', +',,218,218,,,218,,,218,51,51,,,51,51,51,218,,236,,,236,218,,,,218,218', +',218,218,,51,,,218,,,51,,51,236,51,51,,51,51,51,,51,51,,236,,,51,51', +'200,200,51,,,51,52,52,,,52,52,52,51,,,,,,51,,,,51,51,,51,51,,52,,,51', +',,52,,52,200,52,52,200,52,52,52,,52,52,,,,,52,52,,,52,,,52,,200,53,53', +',,53,52,53,53,,,200,52,,,,52,52,,52,52,,,,53,52,,,,,53,,53,,53,53,,53', +'53,53,,53,53,,,,,53,53,,,53,,,53,58,58,,,58,,58,53,,,,,,53,167,,,53', +'53,,53,53,,58,,,53,,167,58,167,58,167,58,58,,58,58,58,,58,58,,,,,58', +'58,,,58,,,58,167,,225,225,,,225,58,225,225,,,,58,,167,167,58,58,,58', +'58,167,,,225,58,,,,,225,,225,,225,225,,225,225,225,,225,225,225,225', +',,225,225,,,225,,,225,149,149,,,149,,149,225,,,,,,225,,,,225,225,,225', +'225,,149,,,225,,,149,,149,,149,149,,149,149,149,,149,149,149,149,,,149', +'149,,,149,,,149,63,63,,,63,,63,149,,,,,,149,161,,,149,149,,149,149,', +'63,,,149,,161,63,161,63,161,63,63,,63,63,63,,63,63,,,,,63,63,,,63,,', +'63,161,,309,309,,,309,63,309,309,,,,63,,,,63,63,,63,63,161,,,309,63', +',,,,309,,309,,309,309,,309,309,309,,309,309,309,309,,,309,309,,,309', +',,309,72,72,,,72,,72,309,,,,,,309,,,,309,309,,309,309,,72,,,309,,,72', +',72,,72,72,,72,72,72,,72,72,72,72,,,72,72,,,72,,,72,308,308,,,308,,308', +'72,,,,,,72,,,,72,72,,72,72,,308,,,72,,,308,,308,,308,308,,308,308,308', +',308,308,308,308,,,308,308,,,308,,,308,74,74,,,74,,74,308,,,,,,308,', +',,308,308,,308,308,,74,,,308,,,74,,74,,74,74,,74,74,74,,74,74,74,74', +',,74,74,,,74,,,74,75,75,,,75,,75,74,,,,,,74,,,,74,74,,74,74,,75,,,74', +',,75,,75,,75,75,,75,75,75,,75,75,75,75,,,75,75,,,75,,,75,76,76,,,76', +',76,75,,,,,,75,,,,75,75,,75,75,,76,,,75,,,76,,76,,76,76,,76,76,76,,76', +'76,76,76,,,76,76,,,76,,,76,77,77,,,77,,77,76,,,,,,76,,,,76,76,,76,76', +',77,,,76,,,77,,77,,77,77,,77,77,77,,77,77,77,77,,,77,77,,,77,,,77,78', +'78,,,78,,78,77,,,,,,77,,,,77,77,,77,77,,78,,,77,,,78,,78,,78,78,,78', '78,78,,78,78,78,78,,,78,78,,,78,,,78,79,79,,,79,,79,78,,,,,,78,,,,78', -'78,78,78,,,79,,78,,,,79,,79,,79,79,,79,79,79,,79,79,,,,,79,79,,,79,', -',79,80,80,,,80,,80,79,,,,,,79,,,,79,79,79,79,,,80,,79,,,,80,,80,,80', +'78,,78,78,,79,,,78,,,79,,79,,79,79,,79,79,79,,79,79,,,,,79,79,,,79,', +',79,80,80,,,80,,80,79,,,,,,79,,,,79,79,,79,79,,80,,,79,,,80,,80,,80', '80,,80,80,80,,80,80,,,,,80,80,,,80,,,80,81,81,,,81,,81,80,,,,,,80,,', -',80,80,80,80,,,81,,80,,,,81,,81,,81,81,,81,81,81,,81,81,,,,,81,81,,', -'81,,,81,82,82,,,82,,82,81,,,,,,81,,,,81,81,81,81,,,82,,81,,,,82,,82', +',80,80,,80,80,,81,,,80,,,81,,81,,81,81,,81,81,81,,81,81,,,,,81,81,,', +'81,,,81,82,82,,,82,,82,81,,,,,,81,,,,81,81,,81,81,,82,,,81,,,82,,82', ',82,82,,82,82,82,,82,82,,,,,82,82,,,82,,,82,83,83,,,83,,83,82,,,,,,82', -',,,82,82,82,82,,,83,,82,,,,83,,83,,83,83,,83,83,83,,83,83,,,,,83,83', -',,83,,,83,84,84,,,84,,84,83,,,,,,83,,,,83,83,83,83,,,84,,83,,,,84,,84', +',,,82,82,,82,82,,83,,,82,,,83,,83,,83,83,,83,83,83,,83,83,,,,,83,83', +',,83,,,83,84,84,,,84,,84,83,,,,,,83,,,,83,83,,83,83,,84,,,83,,,84,,84', ',84,84,,84,84,84,,84,84,,,,,84,84,,,84,,,84,85,85,,,85,,85,84,,,,,,84', -',,,84,84,84,84,,,85,,84,,,,85,,85,,85,85,,85,85,85,,85,85,,,,,85,85', -',,85,,,85,86,86,,,86,,86,85,,,,,,85,,,,85,85,85,85,,,86,,85,,,,86,,86', +',,,84,84,,84,84,,85,,,84,,,85,,85,,85,85,,85,85,85,,85,85,,,,,85,85', +',,85,,,85,86,86,,,86,,86,85,,,,,,85,,,,85,85,,85,85,,86,,,85,,,86,,86', ',86,86,,86,86,86,,86,86,,,,,86,86,,,86,,,86,87,87,,,87,,87,86,,,,,,86', -',,,86,86,86,86,,,87,,86,,,,87,,87,,87,87,,87,87,87,,87,87,,,,,87,87', -',,87,,,87,88,88,,,88,,88,87,,,,,,87,,,,87,87,87,87,,,88,,87,,,,88,,88', +',,,86,86,,86,86,,87,,,86,,,87,,87,,87,87,,87,87,87,,87,87,,,,,87,87', +',,87,,,87,88,88,,,88,,88,87,,,,,,87,,,,87,87,,87,87,,88,,,87,,,88,,88', ',88,88,,88,88,88,,88,88,,,,,88,88,,,88,,,88,89,89,,,89,,89,88,,,,,,88', -',,,88,88,88,88,,,89,,88,,,,89,,89,,89,89,,89,89,89,,89,89,,,,,89,89', -',,89,,,89,90,90,,,90,,90,89,,,,,,89,,,,89,89,89,89,,,90,,89,,,,90,,90', +',,,88,88,,88,88,,89,,,88,,,89,,89,,89,89,,89,89,89,,89,89,,,,,89,89', +',,89,,,89,90,90,,,90,,90,89,,,,,,89,,,,89,89,,89,89,,90,,,89,,,90,,90', ',90,90,,90,90,90,,90,90,,,,,90,90,,,90,,,90,91,91,,,91,,91,90,,,,,,90', -',,,90,90,90,90,,,91,,90,,,,91,,91,,91,91,,91,91,91,,91,91,,,,,91,91', -',,91,,,91,92,92,,,92,,92,91,,,,,,91,,,,91,91,91,91,,,92,,91,,,,92,,92', +',,,90,90,,90,90,,91,,,90,,,91,,91,,91,91,,91,91,91,,91,91,,,,,91,91', +',,91,,,91,92,92,,,92,,92,91,,,,,,91,,,,91,91,,91,91,,92,,,91,,,92,,92', ',92,92,,92,92,92,,92,92,,,,,92,92,,,92,,,92,93,93,,,93,,93,92,,,,,,92', -',,,92,92,92,92,,,93,,92,,,,93,,93,,93,93,,93,93,93,,93,93,,,,,93,93', -',,93,,,93,94,94,,,94,,94,93,,,,,,93,,,,93,93,93,93,,,94,,93,,,,94,,94', +',,,92,92,,92,92,,93,,,92,,,93,,93,,93,93,,93,93,93,,93,93,,,,,93,93', +',,93,,,93,94,94,,,94,,94,93,,,,,,93,,,,93,93,,93,93,,94,,,93,,,94,,94', ',94,94,,94,94,94,,94,94,,,,,94,94,,,94,,,94,95,95,,,95,,95,94,,,,,,94', -',,,94,94,94,94,,,95,,94,,,,95,,95,,95,95,,95,95,95,,95,95,,,,,95,95', -',,95,,,95,96,96,,,96,,96,95,,,,,,95,,,,95,95,95,95,,,96,,95,,,,96,,96', +',,,94,94,,94,94,,95,,,94,,,95,,95,,95,95,,95,95,95,,95,95,,,,,95,95', +',,95,,,95,96,96,,,96,,96,95,,,,,,95,,,,95,95,,95,95,,96,,,95,,,96,,96', ',96,96,,96,96,96,,96,96,,,,,96,96,,,96,,,96,97,97,,,97,,97,96,,,,,,96', -',,,96,96,96,96,,,97,,96,,,,97,,97,,97,97,,97,97,97,,97,97,,,,,97,97', -',,97,,,97,98,98,,,98,,98,97,,,,,,97,,,,97,97,97,97,,,98,,97,,,,98,,98', +',,,96,96,,96,96,,97,,,96,,,97,,97,,97,97,,97,97,97,,97,97,,,,,97,97', +',,97,,,97,98,98,,,98,,98,97,,,,,,97,,,,97,97,,97,97,,98,,,97,,,98,,98', ',98,98,,98,98,98,,98,98,,,,,98,98,,,98,,,98,99,99,,,99,,99,98,,,,,,98', -',,,98,98,98,98,,,99,,98,,,,99,,99,,99,99,,99,99,99,,99,99,,,,,99,99', -',,99,,,99,100,100,,,100,,100,99,,,,,,99,,,,99,99,99,99,,,100,,99,,,', +',,,98,98,,98,98,,99,,,98,,,99,,99,,99,99,,99,99,99,,99,99,,,,,99,99', +',,99,,,99,100,100,,,100,,100,99,,,,,,99,,,,99,99,,99,99,,100,,,99,,', '100,,100,,100,100,,100,100,100,,100,100,,,,,100,100,,,100,,,100,101', -'101,,,101,,101,100,,,,,,100,,,,100,100,100,100,,,101,,100,,,,101,,101', -',101,101,,101,101,101,,101,101,,,,,101,101,,,101,,,101,,,,,296,296,', -'101,296,,296,296,,101,,,101,101,101,101,101,,,,,101,,296,,,,,,296,,296', -',296,296,,296,296,296,,296,296,,,,,296,296,,,296,,,296,103,103,,,103', -',103,296,,,,,,296,,,,296,296,296,296,,,103,,296,,,,103,103,103,103,103', -'103,103,103,103,103,,103,103,,,,,103,103,103,103,103,,,103,289,289,', -',289,,289,103,,,,,103,103,,,,103,103,103,103,,,289,,103,,,,289,,289', -',289,289,,289,289,289,,289,289,,,,,289,289,,,289,,,289,105,105,,,105', -',105,289,,,,,,289,,,,289,289,289,289,,,105,,289,,,,105,,105,,105,105', -',105,105,105,,105,105,,,,,105,105,,,105,,,105,106,106,,,106,,106,105', -',,,,,105,,,,105,105,105,105,,,106,,105,,,,106,,106,,106,106,,106,106', -'106,,106,106,,,,,106,106,,,106,,,106,282,282,,,282,,282,106,,,,,,106', -',,,106,106,106,106,,,282,,106,,,,282,,282,,282,282,,282,282,282,,282', -'282,,,,,282,282,,,282,,,282,268,268,,,268,,268,282,,,,,,282,,,,282,282', -'282,282,,,268,,282,,,,268,,268,,268,268,,268,268,268,,268,268,,,,,268', -'268,,,268,,,268,267,267,,,267,,267,268,,,,,,268,,,,268,268,268,268,', -',267,,268,,,,267,,267,,267,267,,267,267,267,,267,267,,,,,267,267,,,267', -',,267,150,150,,,150,,150,267,,,,,,267,,,,267,267,267,267,,,150,,267', -',,,150,,150,,150,150,,150,150,150,,150,150,150,150,,,150,150,,,150,', -',150,111,111,,,111,,111,150,,,,,,150,,,,150,150,150,150,,,111,111,150', -',,,111,,111,,111,111,,111,111,111,,111,111,,,,,111,111,,,111,,,111,264', -'264,,,264,,264,111,,,,,,111,,,,111,111,111,111,,,264,,111,,,,264,,264', -',264,264,,264,264,264,,264,264,,,,,264,264,,,264,,,264,258,258,,,258', -',258,264,,,,,,264,,,,264,264,264,264,,,258,,264,,,,258,,258,,258,258', -',258,258,258,,258,258,,,,,258,258,,,258,,,258,115,115,,,115,,115,258', -',,,,,258,,,,258,258,258,258,,,115,115,258,,,,115,,115,,115,115,,115', -'115,115,,115,115,,,,,115,115,,,115,,,115,228,228,,,228,,228,115,,,,', -',115,,,,115,115,115,115,,,228,,115,,,,228,,228,,228,228,,228,228,228', -',228,228,,,,,228,228,,,228,,,228,243,243,,,243,243,243,228,,,,,,228', -',,,228,228,228,228,,,243,,228,,,,243,,243,,243,243,,243,243,243,,243', -'243,,,,,243,243,,,243,,,243,230,230,,,230,,230,243,,,,,,243,,,,243,243', -'243,243,,,230,,243,,,,230,,230,,230,230,,230,230,230,,230,230,,,,,230', -'230,,,230,,,230,241,241,,,241,241,241,230,,,,,,230,,,,230,230,230,230', -',,241,,230,,,,241,,241,,241,241,,241,241,241,,241,241,,,,,241,241,,', -'241,,,241,257,257,,,257,,257,241,,,,,,241,,,,241,241,241,241,,,257,', -'241,,,,257,,257,,257,257,,257,257,257,,257,257,,,,,257,257,,,257,,,257', -'122,122,,,122,,122,257,,,,,,257,,,,257,257,257,257,,,122,,257,,,,122', -',122,,122,122,,122,122,122,,122,122,,,,,122,122,,,122,,,122,252,252', -',,252,,252,122,,,,,,122,,,,122,122,122,122,,,252,,122,,,,252,,252,,252', -'252,,252,252,252,,252,252,,,,,252,252,,,252,,,252,,,,,247,247,,252,247', -',247,247,,252,,,,252,252,252,252,,,,,252,,247,,,,,,247,,247,,247,247', -',247,247,247,,247,247,,,,,247,247,,,247,,,247,245,245,,,245,,245,247', -',,,,,247,,,,247,247,247,247,,,245,,247,,,,245,,245,,245,245,,245,245', -'245,,245,245,,,,,245,245,,,245,,,245,229,229,,,229,,229,245,,,,,,245', -',,,245,245,245,245,,,229,,245,,,,229,229,229,229,229,229,229,229,229', -'229,,229,229,,,,,229,229,229,229,229,,,229,,,,,,,,229,,,,,229,229,,', -',229,229,229,229,182,,,,229,182,,,,182,182,182,182,182,182,,182,,182', -',,182,182,182,182,,,,,,,,,,,,,,,,182,,,,182,182,,,182,182,182,182,182', -'182,,182,182,120,,120,,,182,,,,120,120,120,120,120,120,,120,,120,182', -',120,120,120,120,,,,,,,,,,,,,,,,120,,,,120,120,,,120,120,120,120,120', -'120,,120,120,119,,119,,,120,,,,119,119,119,119,119,119,,119,,119,120', -',119,119,119,119,,,,,,,,,,,,,,,,119,,,,119,119,,,119,119,119,119,119', -'119,,119,119,,,141,,,119,,141,,,,141,141,141,141,141,141,,141,119,141', -',,141,141,141,141,,,,,,,,,,,,,,,,141,,,,141,141,,,141,141,141,141,141', -'141,,141,141,118,,118,,,141,,,,118,118,118,118,118,118,,118,,118,141', -',118,118,118,118,,,,,,,,,,,,,,,,118,,,,118,118,,,118,118,118,118,118', -'118,,118,118,145,,,,,118,,,,145,145,145,145,145,145,,145,,145,118,,145', -'145,145,145,,,,,,,,,,,,,,,,145,,,,145,145,,,145,145,145,145,145,145', -',145,145,116,,116,,,145,,,,116,116,116,116,116,116,,116,,116,145,,116', -'116,116,116,,,,,,,,,,,,,,,,116,,,,116,116,,,116,116,116,116,116,116', -',116,116,110,,,,,116,,,,110,110,110,110,110,110,,110,,110,116,110,110', -'110,110,110,,,,,,,,,,,,,,,,110,,,,110,110,,,110,110,110,110,110,110', -',110,110,312,,,,,110,,,,312,312,312,312,312,312,,312,,312,110,,312,312', -'312,312,,,,,,,,,,,,,,,,312,,,,312,312,,,312,312,312,312,312,312,,312', -'312,152,,,,,312,,,,152,152,152,152,152,152,,152,,152,312,,152,152,152', -'152,,,,,,,,,,,,,,,,152,,,,152,152,,,152,152,152,152,152,152,,152,152', -'315,,,,,152,,,,315,315,315,315,315,315,,315,,315,152,,315,315,315,315', -',,,,,,,,,,,,,,,315,,,,315,315,,,315,315,315,315,315,315,,315,315,320', -',,,,315,,,,320,320,320,320,320,320,,320,,320,315,,320,320,320,320,,', -',,,,,,,,,,,,,320,,,,320,320,,,320,320,320,320,320,320,,320,320,211,', -',,,320,,,,211,211,211,211,211,211,,211,,211,320,,211,211,211,211,,,', -',,,,,,,,,,,,211,,,,211,211,,,211,211,211,211,211,211,,211,211,328,,', -',,211,,,,328,328,328,328,328,328,,328,,328,211,,328,328,328,328,,,,', -',,,,,,,,,,,328,,,,328,328,,,328,328,328,328,328,328,,328,328,329,,,', -',328,,,,329,329,329,329,329,329,,329,164,329,328,,329,329,329,329,,', -',,,,164,,164,,164,,,,,329,,,,329,329,,165,329,329,329,329,329,329,,329', -'329,164,,,,165,329,165,,165,,164,164,,,,164,164,,,329,,,164,,,,,,,,165', -',,,,,164,,,,165,165,,,,165,165,335,,,,,165,,,,335,335,335,335,335,335', -',335,,335,165,,335,335,335,335,,,,,,,,,,,,,,,,335,,,,335,335,,,335,335', -'335,335,335,335,,335,335,187,,,,,335,,,,187,187,187,187,187,187,187', -'187,,187,335,,187,187,187,187,,,,,,,,,,,,,,,,187,,,,187,187,,,187,187', -'187,187,187,187,,187,187,11,,11,,,187,,,,11,11,11,11,11,11,,11,169,11', -'187,,11,11,11,11,,,,,,,169,,169,,169,,,,,11,,,,11,11,,,11,11,11,11,11', -'11,,11,11,169,,,,,11,,170,169,169,169,169,,,,169,169,,,11,,170,169,170', -'171,170,,,,,,,,,,,169,,171,,171,,171,,,,,170,,,,,,,,170,170,170,170', -',,,170,170,171,,,,,170,,172,171,171,171,171,171,171,,171,171,,,170,', -'172,171,172,173,172,,,,,,,,,,,171,173,173,,173,,173,,,173,,172,,,,,', -',,172,172,172,172,172,172,,172,172,173,,,,,172,,174,173,173,173,173', -'173,173,,173,173,,,172,174,174,173,174,175,174,,,174,,,,,,,,173,175', -'175,,175,,175,,,175,,174,,,,,,,,174,174,174,174,174,174,,174,174,175', -',,,,174,,176,175,175,175,175,175,175,,175,175,,,174,176,176,175,176', -',176,,,176,,,,,,,,175,,,,,,,,,,,176,,,,,177,,,176,176,176,176,176,176', -',176,176,177,177,177,,177,176,177,,,177,177,177,177,,,,,,,176,,,,,,', -',,177,,,,,178,,,177,177,177,177,177,177,,177,177,178,178,178,,178,177', -'178,,,178,178,178,178,,,,,,,177,,,,,,,,,178,,,,,178,,,178,178,178,178', -'178,178,,178,178,179,,,,,178,,,,179,179,179,179,179,179,,179,,179,178', -',179,179,179,179,,,,,,,,,,,,,,,,179,,,,179,179,,,179,179,179,179,179', -'179,,179,179,180,,,,,179,,,,180,180,180,180,180,180,,180,,180,179,,180', -'180,180,180,,,,,,,,,,,,,,,,180,,,,180,180,,,180,180,180,180,180,180', -',180,180,181,,,,,180,,,,181,181,181,181,181,181,,181,,181,180,,181,181', -'181,181,,,,,,,,,,,,,,,,181,,,,181,181,,,181,181,181,181,181,181,,181', -'181,136,,,,,181,,,,136,136,136,136,136,136,,136,,136,181,,136,136,136', -'136,,,,,,,,,,,,,,,,136,,,,136,136,,,136,136,136,136,136,136,,136,136', -',261,261,261,261,136,261,261,261,261,261,,261,261,,,,,,136,261,261,261', -'266,266,266,266,,266,266,266,266,266,,266,266,,,261,261,,,266,266,266', -'210,210,210,210,,210,210,210,210,210,,210,210,,,266,266,,,210,210,210', -',,,,,,,,,,,,,,,210,210' ] - racc_action_check = arr = ::Array.new(6168, nil) +'101,,,101,,101,100,,,,,,100,,,,100,100,,100,100,,101,,,100,,,101,,101', +',101,101,,101,101,101,,101,101,,,,,101,101,,,101,,,101,,,297,297,,,297', +'101,297,297,,,,101,,,101,101,101,,101,101,,,,297,101,,,,,297,,297,,297', +'297,,297,297,297,,297,297,,,,,297,297,,,297,,,297,103,103,,,103,,103', +'297,,,,,,297,,,,297,297,,297,297,,103,,,297,,,103,103,103,103,103,103', +'103,103,103,103,,103,103,,,,,103,103,103,103,103,,,103,290,290,,,290', +',290,103,,,,,103,103,,,,103,103,,103,103,,290,,,103,,,290,,290,,290', +'290,,290,290,290,,290,290,,,,,290,290,,,290,,,290,105,105,,,105,,105', +'290,,,,,,290,,,,290,290,,290,290,,105,,,290,,,105,,105,,105,105,,105', +'105,105,,105,105,,,,,105,105,,,105,,,105,106,106,,,106,,106,105,,,,', +',105,,,,105,105,,105,105,,106,,,105,,,106,,106,,106,106,,106,106,106', +',106,106,,,,,106,106,,,106,,,106,283,283,,,283,,283,106,,,,,,106,,,', +'106,106,,106,106,,283,,,106,,,283,,283,,283,283,,283,283,283,,283,283', +',,,,283,283,,,283,,,283,269,269,,,269,,269,283,,,,,,283,,,,283,283,', +'283,283,,269,,,283,,,269,,269,,269,269,,269,269,269,,269,269,,,,,269', +'269,,,269,,,269,268,268,,,268,,268,269,,,,,,269,166,,,269,269,,269,269', +',268,,,269,,166,268,166,268,166,268,268,,268,268,268,,268,268,,,,,268', +'268,,,268,,,268,166,,226,226,,,226,268,226,226,,,,268,,166,166,268,268', +',268,268,166,,,226,268,,,,,226,,226,,226,226,,226,226,226,,226,226,226', +'226,,,226,226,,,226,,,226,111,111,,,111,,111,226,,,,,,226,,,,226,226', +',226,226,,111,111,,226,,,111,,111,,111,111,,111,111,111,,111,111,,,', +',111,111,,,111,,,111,265,265,,,265,,265,111,,,,,,111,,,,111,111,,111', +'111,,265,,,111,,,265,,265,,265,265,,265,265,265,,265,265,,,,,265,265', +',,265,,,265,259,259,,,259,,259,265,,,,,,265,,,,265,265,,265,265,,259', +',,265,,,259,,259,,259,259,,259,259,259,,259,259,,,,,259,259,,,259,,', +'259,115,115,,,115,,115,259,,,,,,259,,,,259,259,,259,259,,115,115,,259', +',,115,,115,,115,115,,115,115,115,,115,115,,,,,115,115,,,115,,,115,227', +'227,,,227,,227,115,,,,,,115,,,,115,115,,115,115,,227,,,115,,,227,,227', +',227,227,,227,227,227,,227,227,,,,,227,227,,,227,,,227,242,242,,,242', +'242,242,227,,,,,,227,,,,227,227,,227,227,,242,,,227,,,242,,242,,242', +'242,,242,242,242,,242,242,,,,,242,242,,,242,,,242,229,229,,,229,,229', +'242,,,,,,242,,,,242,242,,242,242,,229,,,242,,,229,,229,,229,229,,229', +'229,229,,229,229,,,,,229,229,,,229,,,229,240,240,,,240,240,240,229,', +',,,,229,,,,229,229,,229,229,,240,,,229,,,240,,240,,240,240,,240,240', +'240,,240,240,,,,,240,240,,,240,,,240,258,258,,,258,,258,240,,,,,,240', +',,,240,240,,240,240,,258,,,240,,,258,,258,,258,258,,258,258,258,,258', +'258,,,,,258,258,,,258,,,258,122,122,,,122,,122,258,,,,,,258,,,,258,258', +',258,258,,122,,,258,,,122,,122,,122,122,,122,122,122,,122,122,,,,,122', +'122,,,122,,,122,251,251,,,251,,251,122,,,,,,122,165,,,122,122,,122,122', +',251,,,122,,165,251,165,251,165,251,251,,251,251,251,,251,251,,,,,251', +'251,,,251,,,251,165,,246,246,,,246,251,246,246,,,,251,,165,165,251,251', +',251,251,165,,,246,251,,,,,246,,246,,246,246,,246,246,246,,246,246,', +',,,246,246,,,246,,,246,244,244,,,244,,244,246,,,,,,246,,,,246,246,,246', +'246,,244,,,246,,,244,,244,,244,244,,244,244,244,,244,244,,,,,244,244', +',,244,,,244,228,228,,,228,,228,244,,,,,,244,,,,244,244,,244,244,,228', +',,244,,,228,228,228,228,228,228,228,228,228,228,,228,228,,,,,228,228', +'228,228,228,,,228,,,,,,,,228,,,,,228,228,,,,228,228,136,228,228,,,,', +'228,,136,136,136,136,136,136,,136,,136,,,136,136,136,136,,,,,,,,,,,', +',,,,136,,,,136,136,,,136,136,136,136,136,136,,136,136,120,,120,,,136', +',,,120,120,120,120,120,120,,120,,120,,136,120,120,120,120,,,,,,,,,,', +',,,,,120,,,,120,120,,,120,120,120,120,120,120,,120,120,119,,119,,,120', +',,,119,119,119,119,119,119,,119,,119,,120,119,119,119,119,,,,,,,,,,', +',,,,,119,,,,119,119,,,119,119,119,119,119,119,,119,119,,141,,,,119,141', +',,,141,141,141,141,141,141,,141,,141,119,,141,141,141,141,,,,,,,,,,', +',,,,,141,,,,141,141,,,141,141,141,141,141,141,,141,141,118,,118,,,141', +',,,118,118,118,118,118,118,,118,,118,,141,118,118,118,118,,,,,,,,,,', +',,,,,118,,,,118,118,,,118,118,118,118,118,118,,118,118,145,,,,,118,', +',,145,145,145,145,145,145,,145,,145,,118,145,145,145,145,,,,,,,,,,,', +',,,,145,,,,145,145,,,145,145,145,145,145,145,,145,145,116,,116,,,145', +',,,116,116,116,116,116,116,,116,,116,,145,116,116,116,116,,,,,,,,,,', +',,,,,116,,,,116,116,,,116,116,116,116,116,116,,116,116,,110,,,,116,', +',,,110,110,110,110,110,110,,110,,110,116,110,110,110,110,110,,,,,,,', +',,,,,,,,110,,,,110,110,,,110,110,110,110,110,110,,110,110,313,,,,,110', +',,,313,313,313,313,313,313,,313,,313,,110,313,313,313,313,,,,,,,,,,', +',,,,,313,,,,313,313,,,313,313,313,313,313,313,,313,313,316,,,,,313,', +',,316,316,316,316,316,316,,316,,316,,313,316,316,316,316,,,,,,,,,,,', +',,,,316,,,,316,316,,,316,316,316,316,316,316,,316,316,151,,,,,316,,', +',151,151,151,151,151,151,,151,,151,,316,151,151,151,151,,,,,,,,,,,,', +',,,151,,,,151,151,,,151,151,151,151,151,151,,151,151,321,,,,,151,,,', +'321,321,321,321,321,321,,321,,321,,151,321,321,321,321,,,,,,,,,,,,,', +',,321,,,,321,321,,,321,321,321,321,321,321,,321,321,210,,,,,321,,,,210', +'210,210,210,210,210,,210,,210,,321,210,210,210,210,,,,,,,,,,,,,,,,210', +',,,210,210,,,210,210,210,210,210,210,,210,210,329,,,,,210,,,,329,329', +'329,329,329,329,,329,,329,,210,329,329,329,329,,,,,,,,,,,,,,,,329,,', +',329,329,,,329,329,329,329,329,329,,329,329,330,,,,,329,,,,330,330,330', +'330,330,330,,330,,330,,329,330,330,330,330,,,,,,,,,,,,,,,,330,,,,330', +'330,,,330,330,330,330,330,330,,330,330,336,,,,,330,,,,336,336,336,336', +'336,336,,336,,336,,330,336,336,336,336,,,,,,,,,,,,,,,,336,,,,336,336', +',,336,336,336,336,336,336,,336,336,186,,,,,336,,,,186,186,186,186,186', +'186,186,186,,186,,336,186,186,186,186,,,,,,,,,,,,,,,,186,,,,186,186', +',,186,186,186,186,186,186,,186,186,11,,11,,,186,,,,11,11,11,11,11,11', +',11,,11,,186,11,11,11,11,,,,,,,,,,,,,,,,11,,,,11,11,,,11,11,11,11,11', +'11,,11,11,,181,,,,11,181,,,,181,181,181,181,181,181,,181,168,181,11', +',181,181,181,181,,,,,,,168,,168,,168,,,,,181,,,,181,181,,,181,181,181', +'181,181,181,,181,181,168,,,169,,181,,,168,168,168,168,,,,168,168,169', +',169,181,169,168,,,,170,,,,,,,,,,,168,,,170,,170,169,170,,171,,,,,169', +'169,169,169,,,,169,169,171,,171,,171,169,170,,,,,,,,170,170,170,170', +'170,170,169,170,170,,,,171,,170,,,172,,,171,171,171,171,171,171,,171', +'171,170,172,172,,172,171,172,,173,172,,,,,,,,,,,171,173,173,,173,,173', +',172,173,,,,,,,172,172,172,172,172,172,,172,172,,,,173,,172,,,174,,', +'173,173,173,173,173,173,,173,173,172,174,174,,174,173,174,,175,174,', +',,,,,,,,,173,175,175,,175,,175,,174,175,,,,,,,174,174,174,174,174,174', +',174,174,,,,175,,174,,,,,176,175,175,175,175,175,175,,175,175,174,,176', +'176,176,175,176,,176,,177,176,176,176,176,,,,,,175,,177,177,177,,177', +',177,,176,177,177,177,177,,,,176,176,176,176,176,176,,176,176,,,,177', +',176,,,177,,,177,177,177,177,177,177,,177,177,176,180,,,,177,,,,,180', +'180,180,180,180,180,,180,,180,177,,180,180,180,180,,,,,,,,,,,,,,,,180', +',,,180,180,,,180,180,180,180,180,180,,180,180,179,,,,,180,,,,179,179', +'179,179,179,179,,179,,179,,180,179,179,179,179,,,,,,,,,,,,,,,,179,,', +',179,179,,,179,179,179,179,179,179,,179,179,178,,,,,179,,,,178,178,178', +'178,178,178,,178,,178,,179,178,178,178,178,,,,,,,,,,,,,,,,178,,,,178', +'178,,,178,178,178,178,178,178,,178,178,,,,,,178,209,209,209,209,,209', +'209,209,209,209,,209,209,,178,,,,,209,209,209,267,267,267,267,,267,267', +'267,267,267,,267,267,,,209,209,,,267,267,267,262,262,262,262,,262,262', +'262,262,262,,262,262,,,267,267,,,262,262,262,,,,,,,,,,,,,,,,262,262' ] + racc_action_check = arr = ::Array.new(6060, nil) idx = 0 clist.each do |str| str.split(',', -1).each do |i| @@ -445,45 +442,46 @@ clist = [ end racc_action_pointer = [ - -2, 285, nil, nil, nil, 110, 272, nil, 123, nil, - nil, 5447, 334, 388, 442, nil, nil, nil, nil, nil, + -2, 301, nil, nil, nil, 108, 288, nil, 274, nil, + nil, 5378, 328, 382, 436, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, 248, 182, - 223, 658, 712, 766, 820, 74, 196, nil, -1, 23, - nil, 1094, 1148, 1206, nil, nil, nil, nil, 1260, nil, - 100, 199, nil, 1430, nil, nil, nil, nil, nil, nil, - nil, 222, 1542, 209, 1650, 1704, 1758, 1812, 1866, 1920, - 1974, 2028, 2082, 2136, 2190, 2244, 2298, 2352, 2406, 2460, - 2514, 2568, 2622, 2676, 2730, 2784, 2838, 2892, 2946, 3000, - 3054, 3108, 160, 3220, 172, 3328, 3382, 608, 81, 554, - 4830, 3652, nil, 107, -22, 3814, 4773, nil, 4659, 4543, - 4486, 20, 4138, -7, nil, nil, nil, nil, 44, -7, - nil, 58, nil, nil, nil, nil, 6021, 7, nil, 20, - nil, 4602, 130, nil, nil, 4716, nil, 152, nil, nil, - 3598, -14, 4944, nil, 180, nil, nil, nil, nil, nil, - 78, 500, 114, 446, 5246, 5276, 392, 338, 284, 5464, - 5511, 5528, 5575, 5592, 5639, 5656, 5703, 5748, 5793, 5850, - 5907, 5964, 4429, nil, nil, 280, nil, 5390, 604, 874, - 928, 239, 238, nil, nil, -4, nil, -8, -9, 86, - 87, 104, -2, -1, nil, nil, nil, nil, nil, nil, - 6098, 5115, 198, nil, 215, nil, 215, 155, nil, 1040, - nil, 206, nil, 183, -4, nil, 1318, 1376, 3868, 4358, - 3976, 125, 103, nil, -11, 125, 120, 14, nil, 193, - 53, 4030, nil, 3922, nil, 4304, nil, 4250, nil, nil, - nil, nil, 4192, nil, 43, nil, nil, 4084, 3760, 72, - nil, 6054, nil, 100, 3706, 115, 6076, 3544, 3490, 152, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, 3436, 142, nil, 160, nil, 102, 140, 3274, - nil, 172, 129, 185, 165, -1, 3166, nil, 161, 193, - 168, 205, 207, nil, 40, nil, 207, 1596, 1488, nil, - nil, nil, 4887, nil, nil, 5001, nil, nil, nil, 200, - 5058, 221, 982, 227, nil, nil, nil, nil, 5172, 5229, - 237, 177, nil, nil, nil, 5333, 36, nil, 550, 245, - 222, nil, 247, 250, nil, nil, nil, 251, 254, 263, - nil, 496, nil, nil, nil, 250, 268, nil, nil, 270, - nil, nil, nil, nil, nil, nil, nil, nil, nil, 226, - nil, 168, 56, nil, nil, nil, 278, nil, nil, nil, - 282, nil, 283, nil, 284, nil, nil, nil, nil, nil ] + nil, nil, nil, nil, nil, nil, nil, nil, 265, 200, + 241, 652, 706, 760, 814, 65, 208, nil, 41, 241, + nil, 1086, 1140, 1196, nil, nil, nil, nil, 1250, nil, + 160, 209, nil, 1414, nil, nil, nil, nil, nil, nil, + nil, 232, 1524, 219, 1632, 1686, 1740, 1794, 1848, 1902, + 1956, 2010, 2064, 2118, 2172, 2226, 2280, 2334, 2388, 2442, + 2496, 2550, 2604, 2658, 2712, 2766, 2820, 2874, 2928, 2982, + 3036, 3090, 174, 3200, 183, 3308, 3362, 62, -23, 172, + 4808, 3634, nil, 129, -15, 3796, 4750, nil, 4636, 4521, + 4464, 118, 4120, 41, nil, nil, nil, nil, 10, 27, + nil, 92, nil, nil, nil, nil, 4407, 71, nil, 106, + nil, 4579, 79, nil, nil, 4693, nil, 54, 159, 1360, + -10, 4979, nil, 199, nil, nil, nil, nil, nil, 108, + 8, 1424, 2, 118, 986, 4184, 3534, 1260, 5453, 5496, + 5519, 5539, 5584, 5604, 5649, 5669, 5716, 5736, 5908, 5851, + 5794, 5436, nil, nil, 274, nil, 5321, 598, 868, 922, + 257, 255, nil, nil, -4, nil, -9, -8, 29, -25, + 1134, -1, -2, nil, nil, nil, nil, nil, nil, 5946, + 5093, 207, nil, 226, nil, 227, 155, nil, 1032, nil, + 186, nil, 154, -7, nil, 1306, 3580, 3850, 4338, 3958, + 124, 122, nil, -8, 147, 121, 1057, nil, 245, 82, + 4012, nil, 3904, nil, 4284, nil, 4230, nil, nil, nil, + nil, 4174, nil, nil, nil, 83, nil, nil, 4066, 3742, + 113, nil, 5990, nil, 126, 3688, 136, 5968, 3524, 3470, + 156, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, 3416, 150, nil, 174, nil, 117, 153, + 3254, nil, 184, 100, 196, 178, 0, 3146, nil, 174, + 205, 179, 212, 216, nil, 64, nil, 218, 1578, 1470, + nil, nil, nil, 4865, nil, nil, 4922, nil, nil, nil, + 210, 5036, 233, 976, 238, nil, nil, nil, nil, 5150, + 5207, 248, 191, nil, nil, nil, 5264, 87, nil, 544, + 263, 241, nil, 266, 270, nil, nil, nil, 270, 277, + 278, nil, 490, nil, nil, nil, 265, 283, nil, nil, + 286, nil, nil, nil, nil, nil, nil, nil, nil, nil, + 220, nil, 164, 54, nil, nil, nil, 294, nil, nil, + nil, 297, nil, 302, nil, 309, nil, nil, nil, nil, + nil ] racc_action_default = [ -223, -224, -1, -2, -3, -4, -5, -8, -10, -11, @@ -500,192 +498,182 @@ racc_action_default = [ -224, -224, -74, -224, -224, -224, -224, -84, -224, -224, -224, -224, -224, -223, -137, -156, -157, -119, -223, -223, -146, -148, -149, -150, -151, -152, -43, -224, -168, -224, - -171, -224, -224, -174, -175, -187, -182, -224, -190, -191, - -224, -224, -197, 390, -6, -9, -12, -13, -14, -15, - -224, -18, -19, -20, -21, -22, -23, -24, -25, -26, - -27, -29, -30, -31, -32, -33, -34, -36, -37, -38, - -39, -40, -224, -41, -102, -224, -78, -224, -216, -222, - -210, -207, -205, -116, -128, -199, -131, -203, -224, -213, - -211, -219, -201, -202, -209, -214, -215, -217, -218, -220, - -127, -126, -224, -125, -224, -42, -205, -69, -79, -224, - -82, -205, -161, -164, -224, -76, -224, -224, -224, -127, - -224, -207, -223, -158, -224, -224, -224, -224, -154, -224, - -224, -224, -166, -224, -169, -224, -172, -224, -184, -185, - -186, -188, -224, -192, -205, -194, -17, -224, -224, -205, - -104, -127, -115, -224, -208, -224, -206, -224, -224, -205, - -130, -132, -210, -211, -212, -213, -216, -219, -221, -222, - -123, -124, -206, -224, -71, -224, -81, -224, -206, -224, - -75, -224, -87, -224, -93, -224, -224, -97, -207, -205, - -207, -224, -224, -140, -224, -159, -205, -223, -224, -147, - -155, -153, -44, -167, -170, -177, -173, -176, -189, -224, - -106, -224, -206, -205, -110, -117, -111, -129, -133, -134, - -224, -68, -80, -83, -162, -163, -87, -86, -224, -224, - -93, -92, -224, -224, -101, -96, -98, -224, -224, -224, - -113, -223, -141, -142, -143, -224, -224, -138, -139, -224, - -145, -195, -103, -105, -114, -121, -70, -85, -88, -224, - -91, -224, -224, -108, -109, -112, -224, -160, -135, -144, - -224, -90, -224, -95, -224, -100, -136, -89, -94, -99 ] + -171, -224, -224, -174, -175, -187, -182, -224, -224, -224, + -224, -197, 391, -6, -9, -12, -13, -14, -15, -224, + -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, + -29, -30, -31, -32, -33, -34, -36, -37, -38, -39, + -40, -224, -41, -102, -224, -78, -224, -216, -222, -210, + -207, -205, -116, -128, -199, -131, -203, -224, -213, -211, + -219, -201, -202, -209, -214, -215, -217, -218, -220, -127, + -126, -224, -125, -224, -42, -205, -69, -79, -224, -82, + -205, -161, -164, -224, -76, -224, -224, -224, -127, -224, + -207, -223, -158, -224, -224, -224, -224, -154, -224, -224, + -224, -166, -224, -169, -224, -172, -224, -184, -185, -186, + -188, -224, -190, -191, -192, -205, -194, -17, -224, -224, + -205, -104, -127, -115, -224, -208, -224, -206, -224, -224, + -205, -130, -132, -210, -211, -212, -213, -216, -219, -221, + -222, -123, -124, -206, -224, -71, -224, -81, -224, -206, + -224, -75, -224, -87, -224, -93, -224, -224, -97, -207, + -205, -207, -224, -224, -140, -224, -159, -205, -223, -224, + -147, -155, -153, -44, -167, -170, -177, -173, -176, -189, + -224, -106, -224, -206, -205, -110, -117, -111, -129, -133, + -134, -224, -68, -80, -83, -162, -163, -87, -86, -224, + -224, -93, -92, -224, -224, -101, -96, -98, -224, -224, + -224, -113, -223, -141, -142, -143, -224, -224, -138, -139, + -224, -145, -195, -103, -105, -114, -121, -70, -85, -88, + -224, -91, -224, -224, -108, -109, -112, -224, -160, -135, + -144, -224, -90, -224, -95, -224, -100, -136, -89, -94, + -99 ] racc_goto_table = [ 2, 112, 4, 144, 107, 109, 110, 128, 146, 191, - 184, 134, 192, 263, 221, 132, 337, 341, 232, 309, - 356, 310, 1, 235, 156, 157, 158, 159, 231, 73, - 259, 297, 325, 116, 118, 119, 120, 261, 265, 212, - 214, 343, 327, 136, 136, 141, 296, 368, 218, 302, - 145, 254, 352, 301, 236, 152, 183, 334, 142, 155, - 367, 148, 283, 370, 376, 149, 3, 287, 251, 252, - 250, 136, 161, 162, 163, 164, 165, 166, 167, 168, - 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, - 179, 180, 181, 182, 260, 187, 154, 211, 211, 346, - 319, 137, 139, 136, 150, 321, nil, 136, nil, nil, - nil, nil, nil, nil, 187, 330, nil, nil, nil, 269, - 347, nil, 349, nil, nil, 233, nil, nil, nil, 160, - 233, 238, nil, nil, 306, 298, 300, nil, 299, nil, - nil, nil, nil, nil, nil, 348, nil, nil, nil, nil, - 253, nil, 355, nil, nil, nil, nil, nil, 128, nil, - nil, 216, nil, 134, nil, 224, nil, 132, nil, 364, - 323, nil, nil, nil, nil, nil, nil, 182, nil, 284, - 116, 118, 119, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, 317, nil, 134, - nil, 134, 318, 132, nil, 132, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, 285, - 136, 187, 187, nil, nil, nil, 291, 293, nil, nil, - nil, 363, nil, 312, 303, 312, nil, 315, nil, 141, - nil, nil, nil, nil, 145, nil, nil, nil, nil, 312, - 320, nil, nil, nil, nil, nil, 187, nil, nil, 328, - 329, nil, nil, 353, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, 312, nil, nil, nil, nil, nil, - nil, 335, nil, nil, nil, nil, nil, nil, 136, nil, - nil, nil, nil, 366, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, 359, 358, - nil, nil, nil, nil, 182, nil, nil, nil, nil, nil, + 134, 132, 190, 183, 342, 231, 220, 264, 338, 357, + 234, 155, 156, 157, 158, 310, 1, 311, 211, 213, + 266, 230, 298, 116, 118, 119, 120, 73, 260, 326, + 262, 137, 139, 136, 136, 141, 344, 328, 297, 369, + 145, 217, 255, 303, 284, 151, 353, 302, 235, 288, + 371, 182, 368, 377, 335, 142, 154, 252, 253, 159, + 3, 136, 160, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, + 178, 179, 180, 181, 320, 186, 261, 210, 210, 322, + 250, 215, 347, 136, 153, 223, 251, 136, 249, 331, + 149, nil, nil, nil, 186, 270, nil, nil, nil, nil, + nil, nil, nil, nil, nil, 232, 348, nil, 350, nil, + 232, 237, nil, nil, 300, 307, nil, 299, 301, 349, + nil, nil, nil, nil, nil, nil, 356, nil, nil, 254, + nil, nil, nil, nil, nil, nil, nil, 128, nil, nil, + nil, 134, 132, 365, nil, nil, nil, nil, 324, nil, + nil, nil, nil, nil, nil, nil, 181, nil, 285, 116, + 118, 119, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, 318, 134, 132, 134, + 132, 319, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, 286, 136, + 186, 186, nil, nil, nil, 292, 294, nil, nil, nil, + nil, nil, 313, 304, 313, 364, 316, nil, 141, nil, + nil, nil, nil, 145, nil, nil, nil, nil, nil, nil, + 313, 321, nil, nil, nil, nil, nil, 186, nil, nil, + 329, 330, nil, nil, 354, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, 313, nil, nil, nil, nil, + nil, nil, 336, nil, nil, nil, nil, nil, nil, 136, + nil, nil, nil, nil, 367, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, 360, + 359, nil, nil, nil, nil, 181, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - 116, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, 116, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, 358, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, 380, - nil, 382, 384 ] + nil, nil, nil, nil, 359, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + 381, nil, 383, 385 ] racc_goto_check = [ - 2, 39, 4, 76, 10, 10, 10, 64, 81, 54, - 51, 31, 56, 55, 44, 37, 46, 47, 65, 72, - 66, 72, 1, 65, 8, 8, 8, 8, 54, 6, - 52, 49, 57, 10, 10, 10, 10, 58, 38, 60, - 60, 50, 61, 10, 10, 10, 48, 45, 43, 68, - 10, 44, 69, 55, 71, 10, 13, 74, 75, 7, - 46, 77, 38, 47, 66, 78, 3, 38, 82, 83, - 85, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 2, 39, 4, 76, 10, 10, 10, 64, 81, 56, + 31, 37, 54, 51, 47, 65, 44, 55, 46, 66, + 65, 8, 8, 8, 8, 72, 1, 72, 60, 60, + 38, 54, 49, 10, 10, 10, 10, 6, 52, 57, + 58, 12, 12, 10, 10, 10, 50, 61, 48, 45, + 10, 43, 44, 68, 38, 10, 69, 55, 71, 38, + 47, 13, 46, 66, 74, 75, 7, 77, 78, 12, + 3, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, - 10, 10, 10, 10, 51, 10, 6, 10, 10, 49, - 38, 12, 12, 10, 86, 38, nil, 10, nil, nil, - nil, nil, nil, nil, 10, 38, nil, nil, nil, 56, - 55, nil, 55, nil, nil, 4, nil, nil, nil, 12, - 4, 4, nil, nil, 44, 54, 54, nil, 56, nil, - nil, nil, nil, nil, nil, 38, nil, nil, nil, nil, - 2, nil, 38, nil, nil, nil, nil, nil, 64, nil, - nil, 12, nil, 31, nil, 12, nil, 37, nil, 38, - 56, nil, nil, nil, nil, nil, nil, 10, nil, 39, - 10, 10, 10, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, 76, nil, 31, - nil, 31, 81, 37, nil, 37, nil, nil, nil, nil, + 10, 10, 10, 10, 38, 10, 51, 10, 10, 38, + 82, 12, 49, 10, 6, 12, 83, 10, 85, 38, + 86, nil, nil, nil, 10, 56, nil, nil, nil, nil, + nil, nil, nil, nil, nil, 4, 55, nil, 55, nil, + 4, 4, nil, nil, 56, 44, nil, 54, 54, 38, + nil, nil, nil, nil, nil, nil, 38, nil, nil, 2, + nil, nil, nil, nil, nil, nil, nil, 64, nil, nil, + nil, 31, 37, 38, nil, nil, nil, nil, 56, nil, + nil, nil, nil, nil, nil, nil, 10, nil, 39, 10, + 10, 10, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, 76, 31, 37, 31, + 37, 81, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, 2, 10, + 10, 10, nil, nil, nil, 2, 2, nil, nil, nil, + nil, nil, 10, 4, 10, 51, 10, nil, 10, nil, + nil, nil, nil, 10, nil, nil, nil, nil, nil, nil, + 10, 10, nil, nil, nil, nil, nil, 10, nil, nil, + 10, 10, nil, nil, 64, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, 10, nil, nil, nil, nil, + nil, nil, 10, nil, nil, nil, nil, nil, nil, 10, + nil, nil, nil, nil, 39, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 2, - 10, 10, 10, nil, nil, nil, 2, 2, nil, nil, - nil, 51, nil, 10, 4, 10, nil, 10, nil, 10, - nil, nil, nil, nil, 10, nil, nil, nil, nil, 10, - 10, nil, nil, nil, nil, nil, 10, nil, nil, 10, - 10, nil, nil, 64, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, 10, nil, nil, nil, nil, nil, - nil, 10, nil, nil, nil, nil, nil, nil, 10, nil, - nil, nil, nil, 39, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, 2, 4, - nil, nil, nil, nil, 10, nil, nil, nil, nil, nil, + 4, nil, nil, nil, nil, 10, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - 10, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, 10, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, 4, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, 2, - nil, 2, 2 ] + nil, nil, nil, nil, 4, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + 2, nil, 2, 2 ] racc_goto_pointer = [ - nil, 22, 0, 66, 2, nil, 24, -15, -51, nil, - -8, nil, 50, -45, nil, nil, nil, nil, nil, nil, + nil, 26, 0, 70, 2, nil, 32, -8, -54, nil, + -8, nil, -10, -40, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, -38, nil, nil, nil, nil, nil, -34, -154, -38, - nil, nil, nil, -65, -100, -291, -276, -277, -182, -197, - -254, -91, -155, nil, -94, -178, -91, -232, -150, nil, - -66, -224, nil, nil, -41, -105, -287, nil, -183, -252, - nil, -75, -218, nil, -231, 5, -50, 1, 5, nil, - nil, -50, -79, -78, nil, -77, 43 ] + nil, -39, nil, nil, nil, nil, nil, -38, -161, -38, + nil, nil, nil, -62, -98, -290, -275, -281, -179, -195, + -250, -88, -146, nil, -91, -173, -94, -226, -146, nil, + -77, -220, nil, nil, -41, -108, -289, nil, -178, -249, + nil, -71, -211, nil, -225, 12, -50, -81, -80, nil, + nil, -50, -47, -41, nil, -39, 49 ] racc_goto_default = [ - nil, nil, 357, nil, 213, 5, 6, 7, 8, 9, - 11, 10, 295, nil, 15, 38, 16, 17, 18, 19, + nil, nil, 358, nil, 212, 5, 6, 7, 8, 9, + 11, 10, 296, nil, 15, 38, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, nil, nil, 39, 40, 113, nil, nil, 117, nil, nil, nil, nil, - nil, nil, nil, 44, nil, nil, nil, 193, nil, 104, - nil, 194, 198, 196, 124, nil, nil, 123, nil, nil, - 129, nil, 130, 131, 222, nil, nil, 54, 55, 56, + nil, nil, nil, 44, nil, nil, nil, 192, nil, 104, + nil, 193, 197, 195, 124, nil, nil, 123, nil, nil, + 129, nil, 130, 131, 221, nil, nil, 54, 55, 56, 58, nil, nil, nil, 147, nil, nil ] racc_reduce_table = [ 0, 0, :racc_error, - 1, 86, :_reduce_1, - 1, 86, :_reduce_2, - 1, 86, :_reduce_none, - 1, 87, :_reduce_4, - 1, 90, :_reduce_5, - 3, 90, :_reduce_6, - 2, 90, :_reduce_7, - 1, 91, :_reduce_8, - 3, 91, :_reduce_9, - 1, 92, :_reduce_none, - 1, 93, :_reduce_11, - 3, 93, :_reduce_12, - 3, 93, :_reduce_13, - 3, 93, :_reduce_14, - 3, 93, :_reduce_15, - 1, 95, :_reduce_none, - 4, 95, :_reduce_17, - 3, 95, :_reduce_18, - 3, 95, :_reduce_19, - 3, 95, :_reduce_20, - 3, 95, :_reduce_21, - 3, 95, :_reduce_22, - 3, 95, :_reduce_23, - 3, 95, :_reduce_24, - 3, 95, :_reduce_25, - 3, 95, :_reduce_26, - 3, 95, :_reduce_27, - 2, 95, :_reduce_28, - 3, 95, :_reduce_29, - 3, 95, :_reduce_30, - 3, 95, :_reduce_31, - 3, 95, :_reduce_32, - 3, 95, :_reduce_33, - 3, 95, :_reduce_34, - 2, 95, :_reduce_35, - 3, 95, :_reduce_36, - 3, 95, :_reduce_37, - 3, 95, :_reduce_38, - 3, 95, :_reduce_39, - 3, 95, :_reduce_40, - 3, 95, :_reduce_41, - 3, 95, :_reduce_42, - 1, 97, :_reduce_43, - 3, 97, :_reduce_44, + 1, 87, :_reduce_1, + 1, 87, :_reduce_2, + 1, 87, :_reduce_none, + 1, 88, :_reduce_4, + 1, 91, :_reduce_5, + 3, 91, :_reduce_6, + 2, 91, :_reduce_7, + 1, 92, :_reduce_8, + 3, 92, :_reduce_9, + 1, 93, :_reduce_none, + 1, 94, :_reduce_11, + 3, 94, :_reduce_12, + 3, 94, :_reduce_13, + 3, 94, :_reduce_14, + 3, 94, :_reduce_15, 1, 96, :_reduce_none, - 1, 100, :_reduce_none, - 1, 100, :_reduce_none, - 1, 100, :_reduce_none, - 1, 100, :_reduce_none, - 1, 100, :_reduce_none, - 1, 100, :_reduce_none, - 1, 100, :_reduce_none, - 1, 100, :_reduce_none, - 1, 100, :_reduce_none, - 1, 100, :_reduce_none, - 1, 100, :_reduce_none, + 4, 96, :_reduce_17, + 3, 96, :_reduce_18, + 3, 96, :_reduce_19, + 3, 96, :_reduce_20, + 3, 96, :_reduce_21, + 3, 96, :_reduce_22, + 3, 96, :_reduce_23, + 3, 96, :_reduce_24, + 3, 96, :_reduce_25, + 3, 96, :_reduce_26, + 3, 96, :_reduce_27, + 2, 96, :_reduce_28, + 3, 96, :_reduce_29, + 3, 96, :_reduce_30, + 3, 96, :_reduce_31, + 3, 96, :_reduce_32, + 3, 96, :_reduce_33, + 3, 96, :_reduce_34, + 2, 96, :_reduce_35, + 3, 96, :_reduce_36, + 3, 96, :_reduce_37, + 3, 96, :_reduce_38, + 3, 96, :_reduce_39, + 3, 96, :_reduce_40, + 3, 96, :_reduce_41, + 3, 96, :_reduce_42, + 1, 98, :_reduce_43, + 3, 98, :_reduce_44, + 1, 97, :_reduce_none, 1, 101, :_reduce_none, 1, 101, :_reduce_none, 1, 101, :_reduce_none, @@ -695,168 +683,179 @@ racc_reduce_table = [ 1, 101, :_reduce_none, 1, 101, :_reduce_none, 1, 101, :_reduce_none, - 1, 117, :_reduce_66, - 1, 117, :_reduce_67, - 5, 99, :_reduce_68, - 3, 99, :_reduce_69, - 6, 99, :_reduce_70, - 4, 99, :_reduce_71, - 1, 99, :_reduce_72, - 1, 103, :_reduce_73, - 2, 103, :_reduce_74, - 4, 125, :_reduce_75, - 3, 125, :_reduce_76, - 1, 125, :_reduce_77, - 3, 126, :_reduce_78, - 2, 124, :_reduce_79, - 3, 128, :_reduce_80, - 2, 128, :_reduce_81, - 2, 127, :_reduce_82, - 4, 127, :_reduce_83, - 2, 106, :_reduce_84, - 5, 130, :_reduce_85, - 4, 130, :_reduce_86, - 0, 131, :_reduce_none, - 2, 131, :_reduce_88, - 4, 131, :_reduce_89, - 3, 131, :_reduce_90, - 6, 107, :_reduce_91, - 5, 107, :_reduce_92, + 1, 101, :_reduce_none, + 1, 101, :_reduce_none, + 1, 102, :_reduce_none, + 1, 102, :_reduce_none, + 1, 102, :_reduce_none, + 1, 102, :_reduce_none, + 1, 102, :_reduce_none, + 1, 102, :_reduce_none, + 1, 102, :_reduce_none, + 1, 102, :_reduce_none, + 1, 102, :_reduce_none, + 1, 118, :_reduce_66, + 1, 118, :_reduce_67, + 5, 100, :_reduce_68, + 3, 100, :_reduce_69, + 6, 100, :_reduce_70, + 4, 100, :_reduce_71, + 1, 100, :_reduce_72, + 1, 104, :_reduce_73, + 2, 104, :_reduce_74, + 4, 126, :_reduce_75, + 3, 126, :_reduce_76, + 1, 126, :_reduce_77, + 3, 127, :_reduce_78, + 2, 125, :_reduce_79, + 3, 129, :_reduce_80, + 2, 129, :_reduce_81, + 2, 128, :_reduce_82, + 4, 128, :_reduce_83, + 2, 107, :_reduce_84, + 5, 131, :_reduce_85, + 4, 131, :_reduce_86, 0, 132, :_reduce_none, - 4, 132, :_reduce_94, - 3, 132, :_reduce_95, - 5, 105, :_reduce_96, - 1, 133, :_reduce_97, - 2, 133, :_reduce_98, - 5, 134, :_reduce_99, - 4, 134, :_reduce_100, - 1, 135, :_reduce_101, - 1, 98, :_reduce_none, - 4, 98, :_reduce_103, - 1, 137, :_reduce_104, - 3, 137, :_reduce_105, - 3, 136, :_reduce_106, - 1, 94, :_reduce_107, - 6, 94, :_reduce_108, - 6, 94, :_reduce_109, - 5, 94, :_reduce_110, - 5, 94, :_reduce_111, - 6, 94, :_reduce_112, - 5, 94, :_reduce_113, - 4, 142, :_reduce_114, - 1, 143, :_reduce_115, - 1, 139, :_reduce_116, - 3, 139, :_reduce_117, - 1, 138, :_reduce_118, - 2, 138, :_reduce_119, - 1, 138, :_reduce_120, - 6, 104, :_reduce_121, - 2, 104, :_reduce_122, - 3, 144, :_reduce_123, - 3, 144, :_reduce_124, - 1, 145, :_reduce_none, - 1, 145, :_reduce_none, - 0, 141, :_reduce_127, - 1, 141, :_reduce_128, - 3, 141, :_reduce_129, - 1, 147, :_reduce_none, - 1, 147, :_reduce_none, - 1, 147, :_reduce_none, - 3, 146, :_reduce_133, - 3, 146, :_reduce_134, - 6, 108, :_reduce_135, - 7, 109, :_reduce_136, - 1, 152, :_reduce_137, - 1, 151, :_reduce_none, - 1, 151, :_reduce_none, - 1, 153, :_reduce_none, - 2, 153, :_reduce_141, + 2, 132, :_reduce_88, + 4, 132, :_reduce_89, + 3, 132, :_reduce_90, + 6, 108, :_reduce_91, + 5, 108, :_reduce_92, + 0, 133, :_reduce_none, + 4, 133, :_reduce_94, + 3, 133, :_reduce_95, + 5, 106, :_reduce_96, + 1, 134, :_reduce_97, + 2, 134, :_reduce_98, + 5, 135, :_reduce_99, + 4, 135, :_reduce_100, + 1, 136, :_reduce_101, + 1, 99, :_reduce_none, + 4, 99, :_reduce_103, + 1, 138, :_reduce_104, + 3, 138, :_reduce_105, + 3, 137, :_reduce_106, + 1, 95, :_reduce_107, + 6, 95, :_reduce_108, + 6, 95, :_reduce_109, + 5, 95, :_reduce_110, + 5, 95, :_reduce_111, + 6, 95, :_reduce_112, + 5, 95, :_reduce_113, + 4, 143, :_reduce_114, + 1, 144, :_reduce_115, + 1, 140, :_reduce_116, + 3, 140, :_reduce_117, + 1, 139, :_reduce_118, + 2, 139, :_reduce_119, + 1, 139, :_reduce_120, + 6, 105, :_reduce_121, + 2, 105, :_reduce_122, + 3, 145, :_reduce_123, + 3, 145, :_reduce_124, + 1, 146, :_reduce_none, + 1, 146, :_reduce_none, + 0, 142, :_reduce_127, + 1, 142, :_reduce_128, + 3, 142, :_reduce_129, + 1, 148, :_reduce_none, + 1, 148, :_reduce_none, + 1, 148, :_reduce_none, + 3, 147, :_reduce_133, + 3, 147, :_reduce_134, + 6, 109, :_reduce_135, + 7, 110, :_reduce_136, + 1, 153, :_reduce_137, + 1, 152, :_reduce_none, + 1, 152, :_reduce_none, 1, 154, :_reduce_none, - 1, 154, :_reduce_none, - 6, 110, :_reduce_144, - 5, 110, :_reduce_145, - 1, 155, :_reduce_146, - 3, 155, :_reduce_147, - 1, 157, :_reduce_148, - 1, 157, :_reduce_149, - 1, 157, :_reduce_150, + 2, 154, :_reduce_141, + 1, 155, :_reduce_none, + 1, 155, :_reduce_none, + 6, 111, :_reduce_144, + 5, 111, :_reduce_145, + 1, 156, :_reduce_146, + 3, 156, :_reduce_147, + 1, 158, :_reduce_148, + 1, 158, :_reduce_149, + 1, 158, :_reduce_150, + 1, 158, :_reduce_none, + 1, 159, :_reduce_152, + 3, 159, :_reduce_153, 1, 157, :_reduce_none, - 1, 158, :_reduce_152, - 3, 158, :_reduce_153, - 1, 156, :_reduce_none, - 2, 156, :_reduce_155, - 1, 149, :_reduce_156, - 1, 149, :_reduce_157, - 1, 150, :_reduce_158, - 2, 150, :_reduce_159, - 4, 150, :_reduce_160, - 1, 129, :_reduce_161, - 3, 129, :_reduce_162, - 3, 159, :_reduce_163, - 1, 159, :_reduce_164, - 1, 102, :_reduce_165, - 3, 112, :_reduce_166, - 4, 112, :_reduce_167, - 2, 112, :_reduce_168, - 3, 112, :_reduce_169, - 4, 112, :_reduce_170, - 2, 112, :_reduce_171, - 3, 115, :_reduce_172, - 4, 115, :_reduce_173, - 2, 115, :_reduce_174, - 1, 160, :_reduce_175, - 3, 160, :_reduce_176, - 3, 161, :_reduce_177, - 1, 122, :_reduce_none, - 1, 122, :_reduce_none, - 1, 122, :_reduce_none, - 1, 162, :_reduce_181, - 2, 163, :_reduce_182, - 1, 165, :_reduce_183, - 1, 167, :_reduce_184, - 1, 168, :_reduce_185, - 2, 166, :_reduce_186, - 1, 169, :_reduce_187, - 1, 170, :_reduce_188, - 2, 170, :_reduce_189, - 2, 164, :_reduce_190, - 2, 164, :_reduce_191, - 3, 88, :_reduce_192, - 0, 171, :_reduce_193, - 2, 171, :_reduce_194, - 4, 171, :_reduce_195, - 1, 111, :_reduce_196, - 2, 111, :_reduce_197, - 1, 118, :_reduce_198, - 1, 121, :_reduce_199, - 1, 119, :_reduce_200, - 1, 120, :_reduce_201, - 1, 114, :_reduce_202, - 1, 113, :_reduce_203, - 1, 116, :_reduce_204, - 0, 123, :_reduce_none, - 1, 123, :_reduce_206, - 0, 140, :_reduce_none, - 1, 140, :_reduce_none, - 1, 148, :_reduce_none, - 1, 148, :_reduce_none, - 1, 148, :_reduce_none, - 1, 148, :_reduce_none, - 1, 148, :_reduce_none, - 1, 148, :_reduce_none, - 1, 148, :_reduce_none, - 1, 148, :_reduce_none, - 1, 148, :_reduce_none, - 1, 148, :_reduce_none, - 1, 148, :_reduce_none, - 1, 148, :_reduce_none, - 1, 148, :_reduce_none, - 1, 148, :_reduce_none, - 0, 89, :_reduce_223 ] + 2, 157, :_reduce_155, + 1, 150, :_reduce_156, + 1, 150, :_reduce_157, + 1, 151, :_reduce_158, + 2, 151, :_reduce_159, + 4, 151, :_reduce_160, + 1, 130, :_reduce_161, + 3, 130, :_reduce_162, + 3, 160, :_reduce_163, + 1, 160, :_reduce_164, + 1, 103, :_reduce_165, + 3, 113, :_reduce_166, + 4, 113, :_reduce_167, + 2, 113, :_reduce_168, + 3, 113, :_reduce_169, + 4, 113, :_reduce_170, + 2, 113, :_reduce_171, + 3, 116, :_reduce_172, + 4, 116, :_reduce_173, + 2, 116, :_reduce_174, + 1, 161, :_reduce_175, + 3, 161, :_reduce_176, + 3, 162, :_reduce_177, + 1, 123, :_reduce_none, + 1, 123, :_reduce_none, + 1, 123, :_reduce_none, + 1, 163, :_reduce_181, + 2, 164, :_reduce_182, + 1, 166, :_reduce_183, + 1, 168, :_reduce_184, + 1, 169, :_reduce_185, + 2, 167, :_reduce_186, + 1, 170, :_reduce_187, + 1, 171, :_reduce_188, + 2, 171, :_reduce_189, + 3, 165, :_reduce_190, + 3, 165, :_reduce_191, + 3, 89, :_reduce_192, + 0, 172, :_reduce_193, + 2, 172, :_reduce_194, + 4, 172, :_reduce_195, + 1, 112, :_reduce_196, + 2, 112, :_reduce_197, + 1, 119, :_reduce_198, + 1, 122, :_reduce_199, + 1, 120, :_reduce_200, + 1, 121, :_reduce_201, + 1, 115, :_reduce_202, + 1, 114, :_reduce_203, + 1, 117, :_reduce_204, + 0, 124, :_reduce_none, + 1, 124, :_reduce_206, + 0, 141, :_reduce_none, + 1, 141, :_reduce_none, + 1, 149, :_reduce_none, + 1, 149, :_reduce_none, + 1, 149, :_reduce_none, + 1, 149, :_reduce_none, + 1, 149, :_reduce_none, + 1, 149, :_reduce_none, + 1, 149, :_reduce_none, + 1, 149, :_reduce_none, + 1, 149, :_reduce_none, + 1, 149, :_reduce_none, + 1, 149, :_reduce_none, + 1, 149, :_reduce_none, + 1, 149, :_reduce_none, + 1, 149, :_reduce_none, + 0, 90, :_reduce_223 ] racc_reduce_n = 224 -racc_shift_n = 390 +racc_shift_n = 391 racc_token_table = { false => 0, @@ -934,18 +933,19 @@ racc_token_table = { :SELBRACE => 72, :NUMBER => 73, :HEREDOC => 74, - :RENDER_STRING => 75, - :RENDER_EXPR => 76, - :EPP_START => 77, - :LOW => 78, - :HIGH => 79, - :CALL => 80, - :LISTSTART => 81, - :MODULO => 82, - :TITLE_COLON => 83, - :CASE_COLON => 84 } + :SUBLOCATE => 75, + :RENDER_STRING => 76, + :RENDER_EXPR => 77, + :EPP_START => 78, + :LOW => 79, + :HIGH => 80, + :CALL => 81, + :LISTSTART => 82, + :MODULO => 83, + :TITLE_COLON => 84, + :CASE_COLON => 85 } -racc_nt_base = 85 +racc_nt_base = 86 racc_use_result_var = true @@ -1041,6 +1041,7 @@ Racc_token_to_s_table = [ "SELBRACE", "NUMBER", "HEREDOC", + "SUBLOCATE", "RENDER_STRING", "RENDER_EXPR", "EPP_START", diff --git a/lib/puppet/pops/parser/heredoc_support.rb b/lib/puppet/pops/parser/heredoc_support.rb index a59c41522..0ea552182 100644 --- a/lib/puppet/pops/parser/heredoc_support.rb +++ b/lib/puppet/pops/parser/heredoc_support.rb @@ -90,6 +90,10 @@ module Puppet::Pops::Parser::HeredocSupport # Record position where next heredoc (from same line as current @()) should start scanning for content ctx[:newline_jump] = scn.pos + # Emit a token that provides the grammar with location information about the lines on which the heredoc + # content is based + enqueue_completed([:SUBLOCATE, lines, lines.size], heredoc_offset) + # Process captured lines - remove leading, and trailing newline str = heredoc_text(lines, leading, has_margin, remove_break) diff --git a/lib/puppet/pops/parser/parser_support.rb b/lib/puppet/pops/parser/parser_support.rb index 56a98bc46..2a540a0c2 100644 --- a/lib/puppet/pops/parser/parser_support.rb +++ b/lib/puppet/pops/parser/parser_support.rb @@ -146,6 +146,10 @@ class Puppet::Pops::Parser::Parser factory.record_position(start_locateable, end_locateable) end + def heredoc_loc(factory, start_locateabke, end_locateable = nil) + factory.record_heredoc_position(start_locatable, end_locatable) + end + # Associate documentation with the factory wrapped model object. # @return [Puppet::Pops::Model::Factory] the given factory # @api private From 6e81e03892179af3d9d0c4219e47fe94e3cb2c92 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 22 Feb 2014 02:22:35 +0100 Subject: [PATCH 766/800] (PUP-30) Complete implementation of sublocated expression This completes the implementation of SubLocatedExpression. The SubLocator refactored to Locator (instead of being nested inside the support for Heredoc). Heredoc lexing, and the grammar now cooperate to ensure that sublocated model is wrapped. --- lib/puppet/pops/adapters.rb | 2 +- lib/puppet/pops/evaluator/evaluator_impl.rb | 2 +- lib/puppet/pops/model/factory.rb | 20 + lib/puppet/pops/model/model.rb | 22 +- lib/puppet/pops/parser/egrammar.ra | 7 +- lib/puppet/pops/parser/eparser.rb | 793 ++++++++++---------- lib/puppet/pops/parser/heredoc_support.rb | 77 +- lib/puppet/pops/parser/locator.rb | 72 ++ spec/unit/pops/parser/lexer2_spec.rb | 3 +- 9 files changed, 524 insertions(+), 474 deletions(-) diff --git a/lib/puppet/pops/adapters.rb b/lib/puppet/pops/adapters.rb index f40ddd0bf..bea64041d 100644 --- a/lib/puppet/pops/adapters.rb +++ b/lib/puppet/pops/adapters.rb @@ -47,7 +47,7 @@ module Puppet::Pops::Adapters when o.is_a?(Puppet::Pops::Model::Program) return o.locator # TODO_HEREDOC use case of SubLocator instead - when o.respond_to?(:locator) && !(found_locator = o.locator).nil? + when o.is_a?(Puppet::Pops::Model::SubLocatedExpression) && !(found_locator = o.locator).nil? return found_locator when adapter = self.class.get(o) return adapter.locator diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index 64eecf9dc..995b22dca 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -731,7 +731,7 @@ class Puppet::Pops::Evaluator::EvaluatorImpl end # SubLocatable is simply an expression that holds location information - def eval_SubLocatableExpression o, scope + def eval_SubLocatedExpression o, scope evaluate(o.expr, scope) end diff --git a/lib/puppet/pops/model/factory.rb b/lib/puppet/pops/model/factory.rb index b4c028696..a47a7040f 100644 --- a/lib/puppet/pops/model/factory.rb +++ b/lib/puppet/pops/model/factory.rb @@ -277,6 +277,24 @@ class Puppet::Pops::Model::Factory o end + # Builds a SubLocatedExpression - this wraps the expression in a sublocation configured + # from the given token + # A SubLocated holds its own locator that is used for subexpressions holding positions relative + # to what it describes. + # + def build_SubLocatedExpression(o, token, expression) + o.expr = build(expression) + o.offset = token.offset + o.length = token.length + locator = token.locator + o.locator = locator + o.leading_line_count = locator.leading_line_count + o.leading_line_offset = locator.leading_line_offset + # Index is held in sublocator's parent locator - needed to be able to reconstruct + o.line_offsets = locator.locator.line_index + o + end + def build_SelectorEntry(o, matching, value) o.matching_expr = build(matching) o.value_expr = build(value) @@ -520,6 +538,8 @@ class Puppet::Pops::Model::Factory # TODO_HEREDOC def self.HEREDOC(name, expr); new(Model::HeredocExpression, name, expr); end + def self.SUBLOCATE(token, expr) new(Model::SubLocatedExpression, token, expr); end + def self.LIST(entries); new(Model::LiteralList, *entries); end def self.PARAM(name, expr=nil); new(Model::Parameter, name, expr); end diff --git a/lib/puppet/pops/model/model.rb b/lib/puppet/pops/model/model.rb index 824097c48..48269a212 100644 --- a/lib/puppet/pops/model/model.rb +++ b/lib/puppet/pops/model/model.rb @@ -282,23 +282,23 @@ module Puppet::Pops::Model end # Contains one expression which has offsets reported virtually (offset against the Program's - # overall locator. + # overall locator). # - class SubLocatableExpression < Expression + class SubLocatedExpression < Expression contains_one_uni 'expr', Expression, :lowerBound => 1 - # How much each line start is offset (i.e. "margin") + # line offset index for contained expressions has_many_attr 'line_offsets', Integer - # Number of preceding lines - has_attr 'leading_line_count', Integer - # This expression's offset is the same as the sublocators leading offset - # has_attr 'leading_offset', Integer - # + # Number of preceding lines (before the line_offsets) + has_attr 'leading_line_count', Integer + + # The offset of the leading source line (i.e. size of "left margin"). has_attr 'leading_line_offset', Integer # The locator for the sub-locatable's children (not for the sublocator itself) - # The locator is not serialized + # The locator is not serialized and is recreated on demand from the indexing information + # in self. # has_attr 'locator', Object, :lowerBound => 1, :transient => true @@ -316,8 +316,8 @@ module Puppet::Pops::Model outer_locator = Puppet::Pops::Parser::Locator.locator(adpater.extract_text, source_ref, line_offsets) # Create a sublocator that describes an offset from the outer - # TODO_HEREDOC: Sublocator should move out from HeredocSupport - result = Puppet::Pops::Parser::HeredocSupport::SubLocator.new(outer_locator, + # NOTE: the offset of self is the same as the sublocator's leading_offset + result = Puppet::Pops::Parser::Locator::SubLocator.new(outer_locator, leading_line_count, offset, leading_line_offset) setLocator(result) end diff --git a/lib/puppet/pops/parser/egrammar.ra b/lib/puppet/pops/parser/egrammar.ra index 90035e175..8f2a82e16 100644 --- a/lib/puppet/pops/parser/egrammar.ra +++ b/lib/puppet/pops/parser/egrammar.ra @@ -661,8 +661,11 @@ dqtail # TODO_HEREDOC heredoc - : HEREDOC SUBLOCATE string { result = Factory.HEREDOC(val[0][:value], val[1]); loc result, val[0], val[1] } - | HEREDOC SUBLOCATE dq_string { result = Factory.HEREDOC(val[0][:value], val[1]); loc result, val[0], val[1] } + : HEREDOC sublocated_text { result = Factory.HEREDOC(val[0][:value], val[1]); loc result, val[0] } + +sublocated_text + : SUBLOCATE string { result = Factory.SUBLOCATE(val[0], val[1]); loc result, val[0] } + | SUBLOCATE dq_string { result = Factory.SUBLOCATE(val[0], val[1]); loc result, val[0] } # TODO_EPP epp_expression diff --git a/lib/puppet/pops/parser/eparser.rb b/lib/puppet/pops/parser/eparser.rb index 3850f3c38..47e5bd0a7 100644 --- a/lib/puppet/pops/parser/eparser.rb +++ b/lib/puppet/pops/parser/eparser.rb @@ -20,7 +20,7 @@ module Puppet module Parser class Parser < Racc::Parser -module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 736) +module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 739) # Make emacs happy # Local Variables: @@ -30,22 +30,22 @@ module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 736) ##### State transition tables begin ### clist = [ -'57,59,-132,268,51,258,53,-130,79,-212,-221,126,258,126,79,125,256,125', -'306,291,222,345,102,14,106,222,101,222,102,41,106,48,101,50,45,233,49', -'69,65,236,43,68,46,47,-132,269,66,13,105,-130,67,-212,-221,12,105,219', -'57,59,248,247,51,70,53,386,238,126,233,42,79,125,80,64,60,122,62,63', -'61,126,241,14,52,125,102,240,106,41,101,48,245,50,45,246,49,69,65,289', -'43,68,46,47,126,126,66,13,125,125,67,355,105,12,57,59,239,243,51,257', -'53,70,242,340,258,339,312,42,79,323,229,64,60,309,62,63,340,14,339,325', -'52,218,102,41,106,48,101,50,45,327,49,69,65,72,43,68,46,47,126,308,66', -'13,125,305,67,57,59,12,105,265,57,59,267,290,51,70,53,384,86,85,332', -'42,79,81,82,64,60,333,62,63,80,334,222,14,52,209,102,337,106,41,101', -'48,289,50,45,87,49,69,65,341,43,68,46,47,343,74,66,13,185,265,67,267', -'105,12,265,351,57,59,352,114,51,70,53,382,289,74,152,42,150,148,362', -'64,60,283,62,63,363,57,59,14,52,57,59,282,267,41,127,48,281,50,45,366', -'49,69,65,114,43,68,46,47,115,267,66,13,114,370,67,343,372,12,57,59,373', -'374,51,135,53,70,133,135,375,376,133,42,111,378,379,64,60,380,62,63', -'265,14,74,71,52,387,70,41,388,48,70,50,108,389,49,69,65,60,43,68,390', +'57,59,-132,269,51,259,53,-130,79,-213,-222,126,259,126,79,125,257,125', +'307,292,223,346,102,14,106,223,101,223,102,41,106,48,101,50,45,234,49', +'69,65,237,43,68,46,47,-132,270,66,13,105,-130,67,-213,-222,12,105,220', +'57,59,249,248,51,70,53,387,239,126,234,42,79,125,80,64,60,122,62,63', +'61,126,242,14,52,125,102,241,106,41,101,48,246,50,45,247,49,69,65,290', +'43,68,46,47,126,126,66,13,125,125,67,356,105,12,57,59,240,244,51,258', +'53,70,243,341,259,340,313,42,79,324,230,64,60,310,62,63,341,14,340,326', +'52,219,102,41,106,48,101,50,45,328,49,69,65,72,43,68,46,47,126,309,66', +'13,125,306,67,57,59,12,105,266,57,59,268,291,51,70,53,385,86,85,333', +'42,79,81,82,64,60,334,62,63,80,335,223,14,52,210,102,338,106,41,101', +'48,290,50,45,87,49,69,65,342,43,68,46,47,344,74,66,13,186,266,67,268', +'105,12,266,352,57,59,353,114,51,70,53,383,290,74,153,42,151,149,363', +'64,60,284,62,63,364,57,59,14,52,57,59,283,268,41,127,48,282,50,45,367', +'49,69,65,114,43,68,46,47,115,268,66,13,114,371,67,344,373,12,57,59,374', +'375,51,135,53,70,133,135,376,377,133,42,111,379,380,64,60,381,62,63', +'266,14,74,71,52,388,70,41,389,48,70,50,108,390,49,69,65,60,43,68,391', '60,,,66,13,,,67,,,12,57,59,,,51,,53,70,75,77,76,78,,42,,,,64,60,,62', '63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57', '59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49', @@ -66,7 +66,7 @@ clist = [ ',,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,', '52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51', ',53,70,,,,,,42,79,,,64,60,,62,63,,14,,,52,,102,41,106,48,101,50,108', -',49,69,65,,43,68,,,,,66,13,,,67,,,12,105,,57,59,,,51,70,53,287,86,85', +',49,69,65,,43,68,,,,,66,13,,,67,,,12,105,,57,59,,,51,70,53,288,86,85', ',42,,81,82,64,60,,62,63,80,,,14,52,57,59,,,41,,48,,50,45,87,49,69,65', ',43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,138,53,70,,135,,,133,42,,', ',64,60,,62,63,,14,,,52,,,41,,48,70,50,108,,49,69,65,,43,68,,60,,,66', @@ -75,12 +75,12 @@ clist = [ '59,,,51,70,53,143,,,60,42,,,,64,60,,62,63,,,,14,52,,,,,41,,48,,50,108', ',49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,79,,', '64,60,,62,63,,14,,,52,,102,41,106,48,101,50,108,,49,69,65,,43,68,,,', -',66,13,,,67,,,12,105,,57,59,,,51,70,53,293,,,,42,,81,82,64,60,,62,63', +',66,13,,,67,,,12,105,,57,59,,,51,70,53,294,,,,42,,81,82,64,60,,62,63', '80,,,14,52,,,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12', '57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,45,', '49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,79', ',,64,60,,62,63,,14,,,52,,102,41,106,48,101,50,108,,49,69,65,,43,68,', -',,,66,13,,,67,,,12,105,,57,59,,,51,70,53,361,,,,42,,,,64,60,,62,63,80', +',,,66,13,,,67,,,12,105,,57,59,,,51,70,53,362,,,,42,,,,64,60,,62,63,80', ',,14,52,,,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57', '59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,45,,49', '69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64', @@ -128,11 +128,11 @@ clist = [ ',,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50', '108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,', ',,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13', -',,67,,,12,,,57,59,,,51,70,53,346,,,,42,,,184,64,60,,62,63,,,,14,52,', +',,67,,,12,,,57,59,,,51,70,53,347,,,,42,,,185,64,60,,62,63,,,,14,52,', ',,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53', -'70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,187,204,198,205,50,199,207,200', -'196,194,,189,202,,,,,66,13,208,203,201,,,12,57,59,,,51,,53,70,,,,,206', -'188,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66', +'70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,188,205,199,206,50,200,208,201', +'197,195,,190,203,,,,,66,13,209,204,202,,,12,57,59,,,51,,53,70,,,,,207', +'189,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66', '13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41', ',48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,', ',,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,', @@ -141,44 +141,44 @@ clist = [ '70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68', ',,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,79,,,64,60,,62,63,,14', ',,52,,102,41,106,48,101,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12', -'105,,57,59,,,51,70,53,295,,,,42,,81,82,64,60,,62,63,80,,,14,52,,,,,41', +'105,,57,59,,,51,70,53,296,,,,42,,81,82,64,60,,62,63,80,,,14,52,,,,,41', ',48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70', -',,,,,42,,,,64,60,,62,63,,14,216,,52,,,41,,48,,50,108,,49,69,65,,43,68', +',,,,,42,,,,64,60,,62,63,,14,217,,52,,,41,,48,,50,108,,49,69,65,,43,68', ',,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,', '52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51', ',53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,', '43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63', -',14,224,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57', +',14,225,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57', '59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49', -'69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,315,53,70,,,,,,42,,,,64', +'69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,316,53,70,,,,,,42,,,,64', '60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67', ',,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50', -'108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,314,53,70,,,,,,42', +'108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,315,53,70,,,,,,42', ',,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13', ',,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48', ',50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,', '42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66', '13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,79,,,64,60,,62,63,,14,,,52,,102', '41,106,48,101,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,105,,57,59', -',,51,70,53,317,,,,42,,81,82,64,60,,62,63,80,,,14,52,,,,,41,,48,,50,108', +',,51,70,53,318,,,,42,,81,82,64,60,,62,63,80,,,14,52,,,,,41,,48,,50,108', ',49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64', '60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67', -',,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,187,204,198', -'205,50,199,207,200,196,194,,189,202,,,,,66,13,208,203,201,,,12,,,,,', -',,70,,,,,206,188,,,,64,60,79,62,63,,,,,52,,98,99,100,95,90,102,,106', +',,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,188,205,199', +'206,50,200,208,201,197,195,,190,203,,,,,66,13,209,204,202,,,12,,,,,', +',,70,,,,,207,189,,,,64,60,79,62,63,,,,,52,,98,99,100,95,90,102,,106', ',101,,,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,', -'81,82,79,,228,,,80,,,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94', -',,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,227,,,80', +'81,82,79,,229,,,80,,,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94', +',,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,228,,,80', ',,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105', -',,,97,96,,,83,84,86,85,88,89,,81,82,,79,,,,80,244,,,,98,99,100,95,90', +',,,97,96,,,83,84,86,85,88,89,,81,82,,79,,,,80,245,,,,98,99,100,95,90', '102,,106,,101,87,,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86', -'85,88,89,,81,82,79,,226,,,80,,,,98,99,100,95,90,102,,106,,101,,87,91', +'85,88,89,,81,82,79,,227,,,80,,,,98,99,100,95,90,102,,106,,101,,87,91', '93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,', ',,,80,,,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,,,,,', -',,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,225,,,80,,,,98,99,100', +',,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,226,,,80,,,,98,99,100', '95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83', '84,86,85,88,89,,81,82,,79,,,,80,,,,,98,99,100,95,90,102,,106,,101,87', -'214,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81', +'215,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81', '82,79,,,,,80,,,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,', ',,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99', '100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96', @@ -192,10 +192,10 @@ clist = [ '82,79,,,,,80,,,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,', ',,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99', '100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96', -',,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102,263,106', +',,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102,264,106', ',101,,87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89', ',81,82,79,,103,,,80,,,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94', -',,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,,79,,,,80,259', +',,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,,79,,,,80,260', ',,,98,99,100,95,90,102,,106,79,101,87,,91,93,92,94,,,,,,,102,,106,,101', ',,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,105,,,79,,80,,,83,84,86', '85,,,,81,82,102,,106,87,101,80,,,,79,,,,,,,,,,,87,,,102,,106,105,101', @@ -212,10 +212,10 @@ clist = [ '79,,,,,80,,,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,', ',,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99,100', '95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83', -'84,86,85,88,89,,81,82,,,,,,80,277,204,276,205,,274,207,278,272,271,', -'273,275,,87,,,,,208,203,279,277,204,276,205,,274,207,278,272,271,,273', -'275,,,206,280,,,208,203,279,277,204,276,205,,274,207,278,272,271,,273', -'275,,,206,280,,,208,203,279,,,,,,,,,,,,,,,,206,280' ] +'84,86,85,88,89,,81,82,,,,,,80,278,205,277,206,,275,208,279,273,272,', +'274,276,,87,,,,,209,204,280,278,205,277,206,,275,208,279,273,272,,274', +'276,,,207,281,,,209,204,280,278,205,277,206,,275,208,279,273,272,,274', +'276,,,207,281,,,209,204,280,,,,,,,,,,,,,,,,207,281' ] racc_action_table = arr = ::Array.new(6060, nil) idx = 0 clist.each do |str| @@ -226,67 +226,67 @@ clist = [ end clist = [ -'0,0,196,197,0,223,0,194,162,202,201,199,296,108,160,199,150,108,233', -'223,114,296,162,0,162,150,162,233,160,0,160,0,160,0,0,128,0,0,0,129', -'0,0,0,0,196,197,0,0,162,194,0,202,201,0,160,114,373,373,147,147,373', -'0,373,373,129,198,123,0,107,198,162,0,0,45,0,0,0,48,137,373,0,48,107', -'137,107,373,107,373,142,373,373,142,373,373,373,255,373,373,373,373', -'305,45,373,373,305,45,373,305,107,373,5,5,131,139,5,159,5,373,139,337', -'159,337,239,373,163,260,121,373,373,235,373,373,293,5,293,264,373,113', -'163,5,163,5,163,5,5,266,5,5,5,5,5,5,5,5,121,234,5,5,121,231,5,148,148', -'5,163,230,372,372,270,222,372,5,372,372,163,163,284,5,109,163,163,5', -'5,286,5,5,163,288,289,372,5,104,109,292,109,372,109,372,220,372,372', -'163,372,372,372,294,372,372,372,372,295,153,372,372,102,299,372,300', -'109,372,301,302,370,370,303,216,370,372,370,370,307,73,71,372,61,60', -'320,372,372,215,372,372,322,49,49,370,372,238,238,213,324,370,46,370', -'211,370,370,331,370,370,370,332,370,370,370,370,40,191,370,370,39,340', -'370,341,343,370,184,184,344,348,184,49,184,370,49,238,349,350,238,370', -'38,356,357,370,370,360,370,370,190,184,6,1,370,377,49,184,381,184,238', -'184,184,383,184,184,184,49,184,184,385,238,,,184,184,,,184,,,184,12', -'12,,,12,,12,184,8,8,8,8,,184,,,,184,184,,184,184,,12,,,184,,,12,,12', +'0,0,197,198,0,224,0,195,163,203,202,200,297,108,161,200,151,108,234', +'224,114,297,163,0,163,151,163,234,161,0,161,0,161,0,0,128,0,0,0,129', +'0,0,0,0,197,198,0,0,163,195,0,203,202,0,161,114,374,374,147,147,374', +'0,374,374,129,199,123,0,107,199,163,0,0,45,0,0,0,48,137,374,0,48,107', +'137,107,374,107,374,142,374,374,142,374,374,374,256,374,374,374,374', +'306,45,374,374,306,45,374,306,107,374,5,5,131,139,5,160,5,374,139,338', +'160,338,240,374,164,261,121,374,374,236,374,374,294,5,294,265,374,113', +'164,5,164,5,164,5,5,267,5,5,5,5,5,5,5,5,121,235,5,5,121,232,5,149,149', +'5,164,231,373,373,271,223,373,5,373,373,164,164,285,5,109,164,164,5', +'5,287,5,5,164,289,290,373,5,104,109,293,109,373,109,373,221,373,373', +'164,373,373,373,295,373,373,373,373,296,154,373,373,102,300,373,301', +'109,373,302,303,371,371,304,217,371,373,371,371,308,73,71,373,61,60', +'321,373,373,216,373,373,323,49,49,371,373,239,239,214,325,371,46,371', +'212,371,371,332,371,371,371,333,371,371,371,371,40,192,371,371,39,341', +'371,342,344,371,185,185,345,349,185,49,185,371,49,239,350,351,239,371', +'38,357,358,371,371,361,371,371,191,185,6,1,371,378,49,185,382,185,239', +'185,185,384,185,185,185,49,185,185,386,239,,,185,185,,,185,,,185,12', +'12,,,12,,12,185,8,8,8,8,,185,,,,185,185,,185,185,,12,,,185,,,12,,12', ',12,12,,12,12,12,,12,12,,,,,12,12,,,12,,,12,13,13,,,13,,13,12,,,,,,12', ',,,12,12,,12,12,,13,,,12,,,13,,13,,13,13,,13,13,13,,13,13,,,,,13,13', ',,13,,,13,14,14,,,14,,14,13,,,,,,13,,,,13,13,,13,13,,14,,,13,,,14,,14', -',14,14,,14,14,14,,14,14,,,,,14,14,,,14,,,14,352,352,,,352,,352,14,,', -',,,14,,,,14,14,,14,14,,352,,,14,,,352,,352,,352,352,,352,352,352,,352', -'352,352,352,,,352,352,,,352,,,352,339,339,,,339,,339,352,,,,,,352,,', -',352,352,,352,352,,339,,,352,,,339,,339,,339,339,,339,339,339,,339,339', -',,,,339,339,,,339,,,339,187,187,,,187,,187,339,,,,,,339,,,,339,339,', -'339,339,,187,,,339,,,187,,187,,187,187,,187,187,187,,187,187,,,,,187', -'187,,,187,,,187,41,41,,,41,,41,187,,,,,,187,,,,187,187,,187,187,,41', -',,187,,,41,,41,,41,41,,41,41,41,,41,41,,,,,41,41,,,41,,,41,42,42,,,42', +',14,14,,14,14,14,,14,14,,,,,14,14,,,14,,,14,353,353,,,353,,353,14,,', +',,,14,,,,14,14,,14,14,,353,,,14,,,353,,353,,353,353,,353,353,353,,353', +'353,353,353,,,353,353,,,353,,,353,340,340,,,340,,340,353,,,,,,353,,', +',353,353,,353,353,,340,,,353,,,340,,340,,340,340,,340,340,340,,340,340', +',,,,340,340,,,340,,,340,188,188,,,188,,188,340,,,,,,340,,,,340,340,', +'340,340,,188,,,340,,,188,,188,,188,188,,188,188,188,,188,188,,,,,188', +'188,,,188,,,188,41,41,,,41,,41,188,,,,,,188,,,,188,188,,188,188,,41', +',,188,,,41,,41,,41,41,,41,41,41,,41,41,,,,,41,41,,,41,,,41,42,42,,,42', ',42,41,,,,,,41,,,,41,41,,41,41,,42,,,41,,,42,,42,,42,42,,42,42,42,,42', '42,,,,,42,42,,,42,,,42,43,43,,,43,,43,42,,,,,,42,,,,42,42,,42,42,,43', ',,42,,,43,,43,,43,43,,43,43,43,,43,43,,,,,43,43,,,43,,,43,44,44,,,44', ',44,43,,,,,,43,,,,43,43,,43,43,,44,,,43,,,44,,44,,44,44,,44,44,44,,44', -'44,,,,,44,44,,,44,,,44,188,188,,,188,,188,44,,,,,,44,,,,44,44,,44,44', -',188,,,44,,,188,,188,,188,188,,188,188,188,,188,188,,,,,188,188,,,188', -',,188,189,189,,,189,,189,188,,,,,,188,,,,188,188,,188,188,,189,,,188', -',,189,,189,,189,189,,189,189,189,,189,189,,,,,189,189,,,189,,,189,323', -'323,,,323,,323,189,,,,,,189,164,,,189,189,,189,189,,323,,,189,,164,323', -'164,323,164,323,323,,323,323,323,,323,323,,,,,323,323,,,323,,,323,164', -',218,218,,,218,323,218,218,164,164,,323,,164,164,323,323,,323,323,164', -',,218,323,236,236,,,218,,218,,218,218,164,218,218,218,,218,218,218,218', -',,218,218,,,218,,,218,51,51,,,51,51,51,218,,236,,,236,218,,,,218,218', -',218,218,,51,,,218,,,51,,51,236,51,51,,51,51,51,,51,51,,236,,,51,51', -'200,200,51,,,51,52,52,,,52,52,52,51,,,,,,51,,,,51,51,,51,51,,52,,,51', -',,52,,52,200,52,52,200,52,52,52,,52,52,,,,,52,52,,,52,,,52,,200,53,53', -',,53,52,53,53,,,200,52,,,,52,52,,52,52,,,,53,52,,,,,53,,53,,53,53,,53', -'53,53,,53,53,,,,,53,53,,,53,,,53,58,58,,,58,,58,53,,,,,,53,167,,,53', -'53,,53,53,,58,,,53,,167,58,167,58,167,58,58,,58,58,58,,58,58,,,,,58', -'58,,,58,,,58,167,,225,225,,,225,58,225,225,,,,58,,167,167,58,58,,58', -'58,167,,,225,58,,,,,225,,225,,225,225,,225,225,225,,225,225,225,225', -',,225,225,,,225,,,225,149,149,,,149,,149,225,,,,,,225,,,,225,225,,225', -'225,,149,,,225,,,149,,149,,149,149,,149,149,149,,149,149,149,149,,,149', -'149,,,149,,,149,63,63,,,63,,63,149,,,,,,149,161,,,149,149,,149,149,', -'63,,,149,,161,63,161,63,161,63,63,,63,63,63,,63,63,,,,,63,63,,,63,,', -'63,161,,309,309,,,309,63,309,309,,,,63,,,,63,63,,63,63,161,,,309,63', -',,,,309,,309,,309,309,,309,309,309,,309,309,309,309,,,309,309,,,309', -',,309,72,72,,,72,,72,309,,,,,,309,,,,309,309,,309,309,,72,,,309,,,72', -',72,,72,72,,72,72,72,,72,72,72,72,,,72,72,,,72,,,72,308,308,,,308,,308', -'72,,,,,,72,,,,72,72,,72,72,,308,,,72,,,308,,308,,308,308,,308,308,308', -',308,308,308,308,,,308,308,,,308,,,308,74,74,,,74,,74,308,,,,,,308,', -',,308,308,,308,308,,74,,,308,,,74,,74,,74,74,,74,74,74,,74,74,74,74', +'44,,,,,44,44,,,44,,,44,189,189,,,189,,189,44,,,,,,44,,,,44,44,,44,44', +',189,,,44,,,189,,189,,189,189,,189,189,189,,189,189,,,,,189,189,,,189', +',,189,190,190,,,190,,190,189,,,,,,189,,,,189,189,,189,189,,190,,,189', +',,190,,190,,190,190,,190,190,190,,190,190,,,,,190,190,,,190,,,190,324', +'324,,,324,,324,190,,,,,,190,165,,,190,190,,190,190,,324,,,190,,165,324', +'165,324,165,324,324,,324,324,324,,324,324,,,,,324,324,,,324,,,324,165', +',219,219,,,219,324,219,219,165,165,,324,,165,165,324,324,,324,324,165', +',,219,324,237,237,,,219,,219,,219,219,165,219,219,219,,219,219,219,219', +',,219,219,,,219,,,219,51,51,,,51,51,51,219,,237,,,237,219,,,,219,219', +',219,219,,51,,,219,,,51,,51,237,51,51,,51,51,51,,51,51,,237,,,51,51', +'201,201,51,,,51,52,52,,,52,52,52,51,,,,,,51,,,,51,51,,51,51,,52,,,51', +',,52,,52,201,52,52,201,52,52,52,,52,52,,,,,52,52,,,52,,,52,,201,53,53', +',,53,52,53,53,,,201,52,,,,52,52,,52,52,,,,53,52,,,,,53,,53,,53,53,,53', +'53,53,,53,53,,,,,53,53,,,53,,,53,58,58,,,58,,58,53,,,,,,53,168,,,53', +'53,,53,53,,58,,,53,,168,58,168,58,168,58,58,,58,58,58,,58,58,,,,,58', +'58,,,58,,,58,168,,226,226,,,226,58,226,226,,,,58,,168,168,58,58,,58', +'58,168,,,226,58,,,,,226,,226,,226,226,,226,226,226,,226,226,226,226', +',,226,226,,,226,,,226,150,150,,,150,,150,226,,,,,,226,,,,226,226,,226', +'226,,150,,,226,,,150,,150,,150,150,,150,150,150,,150,150,150,150,,,150', +'150,,,150,,,150,63,63,,,63,,63,150,,,,,,150,162,,,150,150,,150,150,', +'63,,,150,,162,63,162,63,162,63,63,,63,63,63,,63,63,,,,,63,63,,,63,,', +'63,162,,310,310,,,310,63,310,310,,,,63,,,,63,63,,63,63,162,,,310,63', +',,,,310,,310,,310,310,,310,310,310,,310,310,310,310,,,310,310,,,310', +',,310,72,72,,,72,,72,310,,,,,,310,,,,310,310,,310,310,,72,,,310,,,72', +',72,,72,72,,72,72,72,,72,72,72,72,,,72,72,,,72,,,72,309,309,,,309,,309', +'72,,,,,,72,,,,72,72,,72,72,,309,,,72,,,309,,309,,309,309,,309,309,309', +',309,309,309,309,,,309,309,,,309,,,309,74,74,,,74,,74,309,,,,,,309,', +',,309,309,,309,309,,74,,,309,,,74,,74,,74,74,,74,74,74,,74,74,74,74', ',,74,74,,,74,,,74,75,75,,,75,,75,74,,,,,,74,,,,74,74,,74,74,,75,,,74', ',,75,,75,,75,75,,75,75,75,,75,75,75,75,,,75,75,,,75,,,75,76,76,,,76', ',76,75,,,,,,75,,,,75,75,,75,75,,76,,,75,,,76,,76,,76,76,,76,76,76,,76', @@ -328,53 +328,53 @@ clist = [ ',,99,,,99,100,100,,,100,,100,99,,,,,,99,,,,99,99,,99,99,,100,,,99,,', '100,,100,,100,100,,100,100,100,,100,100,,,,,100,100,,,100,,,100,101', '101,,,101,,101,100,,,,,,100,,,,100,100,,100,100,,101,,,100,,,101,,101', -',101,101,,101,101,101,,101,101,,,,,101,101,,,101,,,101,,,297,297,,,297', -'101,297,297,,,,101,,,101,101,101,,101,101,,,,297,101,,,,,297,,297,,297', -'297,,297,297,297,,297,297,,,,,297,297,,,297,,,297,103,103,,,103,,103', -'297,,,,,,297,,,,297,297,,297,297,,103,,,297,,,103,103,103,103,103,103', -'103,103,103,103,,103,103,,,,,103,103,103,103,103,,,103,290,290,,,290', -',290,103,,,,,103,103,,,,103,103,,103,103,,290,,,103,,,290,,290,,290', -'290,,290,290,290,,290,290,,,,,290,290,,,290,,,290,105,105,,,105,,105', -'290,,,,,,290,,,,290,290,,290,290,,105,,,290,,,105,,105,,105,105,,105', +',101,101,,101,101,101,,101,101,,,,,101,101,,,101,,,101,,,298,298,,,298', +'101,298,298,,,,101,,,101,101,101,,101,101,,,,298,101,,,,,298,,298,,298', +'298,,298,298,298,,298,298,,,,,298,298,,,298,,,298,103,103,,,103,,103', +'298,,,,,,298,,,,298,298,,298,298,,103,,,298,,,103,103,103,103,103,103', +'103,103,103,103,,103,103,,,,,103,103,103,103,103,,,103,291,291,,,291', +',291,103,,,,,103,103,,,,103,103,,103,103,,291,,,103,,,291,,291,,291', +'291,,291,291,291,,291,291,,,,,291,291,,,291,,,291,105,105,,,105,,105', +'291,,,,,,291,,,,291,291,,291,291,,105,,,291,,,105,,105,,105,105,,105', '105,105,,105,105,,,,,105,105,,,105,,,105,106,106,,,106,,106,105,,,,', ',105,,,,105,105,,105,105,,106,,,105,,,106,,106,,106,106,,106,106,106', -',106,106,,,,,106,106,,,106,,,106,283,283,,,283,,283,106,,,,,,106,,,', -'106,106,,106,106,,283,,,106,,,283,,283,,283,283,,283,283,283,,283,283', -',,,,283,283,,,283,,,283,269,269,,,269,,269,283,,,,,,283,,,,283,283,', -'283,283,,269,,,283,,,269,,269,,269,269,,269,269,269,,269,269,,,,,269', -'269,,,269,,,269,268,268,,,268,,268,269,,,,,,269,166,,,269,269,,269,269', -',268,,,269,,166,268,166,268,166,268,268,,268,268,268,,268,268,,,,,268', -'268,,,268,,,268,166,,226,226,,,226,268,226,226,,,,268,,166,166,268,268', -',268,268,166,,,226,268,,,,,226,,226,,226,226,,226,226,226,,226,226,226', -'226,,,226,226,,,226,,,226,111,111,,,111,,111,226,,,,,,226,,,,226,226', -',226,226,,111,111,,226,,,111,,111,,111,111,,111,111,111,,111,111,,,', -',111,111,,,111,,,111,265,265,,,265,,265,111,,,,,,111,,,,111,111,,111', -'111,,265,,,111,,,265,,265,,265,265,,265,265,265,,265,265,,,,,265,265', -',,265,,,265,259,259,,,259,,259,265,,,,,,265,,,,265,265,,265,265,,259', -',,265,,,259,,259,,259,259,,259,259,259,,259,259,,,,,259,259,,,259,,', -'259,115,115,,,115,,115,259,,,,,,259,,,,259,259,,259,259,,115,115,,259', -',,115,,115,,115,115,,115,115,115,,115,115,,,,,115,115,,,115,,,115,227', -'227,,,227,,227,115,,,,,,115,,,,115,115,,115,115,,227,,,115,,,227,,227', -',227,227,,227,227,227,,227,227,,,,,227,227,,,227,,,227,242,242,,,242', -'242,242,227,,,,,,227,,,,227,227,,227,227,,242,,,227,,,242,,242,,242', -'242,,242,242,242,,242,242,,,,,242,242,,,242,,,242,229,229,,,229,,229', -'242,,,,,,242,,,,242,242,,242,242,,229,,,242,,,229,,229,,229,229,,229', -'229,229,,229,229,,,,,229,229,,,229,,,229,240,240,,,240,240,240,229,', -',,,,229,,,,229,229,,229,229,,240,,,229,,,240,,240,,240,240,,240,240', -'240,,240,240,,,,,240,240,,,240,,,240,258,258,,,258,,258,240,,,,,,240', -',,,240,240,,240,240,,258,,,240,,,258,,258,,258,258,,258,258,258,,258', -'258,,,,,258,258,,,258,,,258,122,122,,,122,,122,258,,,,,,258,,,,258,258', -',258,258,,122,,,258,,,122,,122,,122,122,,122,122,122,,122,122,,,,,122', -'122,,,122,,,122,251,251,,,251,,251,122,,,,,,122,165,,,122,122,,122,122', -',251,,,122,,165,251,165,251,165,251,251,,251,251,251,,251,251,,,,,251', -'251,,,251,,,251,165,,246,246,,,246,251,246,246,,,,251,,165,165,251,251', -',251,251,165,,,246,251,,,,,246,,246,,246,246,,246,246,246,,246,246,', -',,,246,246,,,246,,,246,244,244,,,244,,244,246,,,,,,246,,,,246,246,,246', -'246,,244,,,246,,,244,,244,,244,244,,244,244,244,,244,244,,,,,244,244', -',,244,,,244,228,228,,,228,,228,244,,,,,,244,,,,244,244,,244,244,,228', -',,244,,,228,228,228,228,228,228,228,228,228,228,,228,228,,,,,228,228', -'228,228,228,,,228,,,,,,,,228,,,,,228,228,,,,228,228,136,228,228,,,,', -'228,,136,136,136,136,136,136,,136,,136,,,136,136,136,136,,,,,,,,,,,', +',106,106,,,,,106,106,,,106,,,106,284,284,,,284,,284,106,,,,,,106,,,', +'106,106,,106,106,,284,,,106,,,284,,284,,284,284,,284,284,284,,284,284', +',,,,284,284,,,284,,,284,270,270,,,270,,270,284,,,,,,284,,,,284,284,', +'284,284,,270,,,284,,,270,,270,,270,270,,270,270,270,,270,270,,,,,270', +'270,,,270,,,270,269,269,,,269,,269,270,,,,,,270,167,,,270,270,,270,270', +',269,,,270,,167,269,167,269,167,269,269,,269,269,269,,269,269,,,,,269', +'269,,,269,,,269,167,,227,227,,,227,269,227,227,,,,269,,167,167,269,269', +',269,269,167,,,227,269,,,,,227,,227,,227,227,,227,227,227,,227,227,227', +'227,,,227,227,,,227,,,227,111,111,,,111,,111,227,,,,,,227,,,,227,227', +',227,227,,111,111,,227,,,111,,111,,111,111,,111,111,111,,111,111,,,', +',111,111,,,111,,,111,266,266,,,266,,266,111,,,,,,111,,,,111,111,,111', +'111,,266,,,111,,,266,,266,,266,266,,266,266,266,,266,266,,,,,266,266', +',,266,,,266,260,260,,,260,,260,266,,,,,,266,,,,266,266,,266,266,,260', +',,266,,,260,,260,,260,260,,260,260,260,,260,260,,,,,260,260,,,260,,', +'260,115,115,,,115,,115,260,,,,,,260,,,,260,260,,260,260,,115,115,,260', +',,115,,115,,115,115,,115,115,115,,115,115,,,,,115,115,,,115,,,115,228', +'228,,,228,,228,115,,,,,,115,,,,115,115,,115,115,,228,,,115,,,228,,228', +',228,228,,228,228,228,,228,228,,,,,228,228,,,228,,,228,243,243,,,243', +'243,243,228,,,,,,228,,,,228,228,,228,228,,243,,,228,,,243,,243,,243', +'243,,243,243,243,,243,243,,,,,243,243,,,243,,,243,230,230,,,230,,230', +'243,,,,,,243,,,,243,243,,243,243,,230,,,243,,,230,,230,,230,230,,230', +'230,230,,230,230,,,,,230,230,,,230,,,230,241,241,,,241,241,241,230,', +',,,,230,,,,230,230,,230,230,,241,,,230,,,241,,241,,241,241,,241,241', +'241,,241,241,,,,,241,241,,,241,,,241,259,259,,,259,,259,241,,,,,,241', +',,,241,241,,241,241,,259,,,241,,,259,,259,,259,259,,259,259,259,,259', +'259,,,,,259,259,,,259,,,259,122,122,,,122,,122,259,,,,,,259,,,,259,259', +',259,259,,122,,,259,,,122,,122,,122,122,,122,122,122,,122,122,,,,,122', +'122,,,122,,,122,252,252,,,252,,252,122,,,,,,122,166,,,122,122,,122,122', +',252,,,122,,166,252,166,252,166,252,252,,252,252,252,,252,252,,,,,252', +'252,,,252,,,252,166,,247,247,,,247,252,247,247,,,,252,,166,166,252,252', +',252,252,166,,,247,252,,,,,247,,247,,247,247,,247,247,247,,247,247,', +',,,247,247,,,247,,,247,245,245,,,245,,245,247,,,,,,247,,,,247,247,,247', +'247,,245,,,247,,,245,,245,,245,245,,245,245,245,,245,245,,,,,245,245', +',,245,,,245,229,229,,,229,,229,245,,,,,,245,,,,245,245,,245,245,,229', +',,245,,,229,229,229,229,229,229,229,229,229,229,,229,229,,,,,229,229', +'229,229,229,,,229,,,,,,,,229,,,,,229,229,,,,229,229,136,229,229,,,,', +'229,,136,136,136,136,136,136,,136,,136,,,136,136,136,136,,,,,,,,,,,', ',,,,136,,,,136,136,,,136,136,136,136,136,136,,136,136,120,,120,,,136', ',,,120,120,120,120,120,120,,120,,120,,136,120,120,120,120,,,,,,,,,,', ',,,,,120,,,,120,120,,,120,120,120,120,120,120,,120,120,119,,119,,,120', @@ -389,49 +389,49 @@ clist = [ ',,,116,116,116,116,116,116,,116,,116,,145,116,116,116,116,,,,,,,,,,', ',,,,,116,,,,116,116,,,116,116,116,116,116,116,,116,116,,110,,,,116,', ',,,110,110,110,110,110,110,,110,,110,116,110,110,110,110,110,,,,,,,', -',,,,,,,,110,,,,110,110,,,110,110,110,110,110,110,,110,110,313,,,,,110', -',,,313,313,313,313,313,313,,313,,313,,110,313,313,313,313,,,,,,,,,,', -',,,,,313,,,,313,313,,,313,313,313,313,313,313,,313,313,316,,,,,313,', -',,316,316,316,316,316,316,,316,,316,,313,316,316,316,316,,,,,,,,,,,', -',,,,316,,,,316,316,,,316,316,316,316,316,316,,316,316,151,,,,,316,,', -',151,151,151,151,151,151,,151,,151,,316,151,151,151,151,,,,,,,,,,,,', -',,,151,,,,151,151,,,151,151,151,151,151,151,,151,151,321,,,,,151,,,', -'321,321,321,321,321,321,,321,,321,,151,321,321,321,321,,,,,,,,,,,,,', -',,321,,,,321,321,,,321,321,321,321,321,321,,321,321,210,,,,,321,,,,210', -'210,210,210,210,210,,210,,210,,321,210,210,210,210,,,,,,,,,,,,,,,,210', -',,,210,210,,,210,210,210,210,210,210,,210,210,329,,,,,210,,,,329,329', -'329,329,329,329,,329,,329,,210,329,329,329,329,,,,,,,,,,,,,,,,329,,', -',329,329,,,329,329,329,329,329,329,,329,329,330,,,,,329,,,,330,330,330', -'330,330,330,,330,,330,,329,330,330,330,330,,,,,,,,,,,,,,,,330,,,,330', -'330,,,330,330,330,330,330,330,,330,330,336,,,,,330,,,,336,336,336,336', -'336,336,,336,,336,,330,336,336,336,336,,,,,,,,,,,,,,,,336,,,,336,336', -',,336,336,336,336,336,336,,336,336,186,,,,,336,,,,186,186,186,186,186', -'186,186,186,,186,,336,186,186,186,186,,,,,,,,,,,,,,,,186,,,,186,186', -',,186,186,186,186,186,186,,186,186,11,,11,,,186,,,,11,11,11,11,11,11', -',11,,11,,186,11,11,11,11,,,,,,,,,,,,,,,,11,,,,11,11,,,11,11,11,11,11', -'11,,11,11,,181,,,,11,181,,,,181,181,181,181,181,181,,181,168,181,11', -',181,181,181,181,,,,,,,168,,168,,168,,,,,181,,,,181,181,,,181,181,181', -'181,181,181,,181,181,168,,,169,,181,,,168,168,168,168,,,,168,168,169', -',169,181,169,168,,,,170,,,,,,,,,,,168,,,170,,170,169,170,,171,,,,,169', -'169,169,169,,,,169,169,171,,171,,171,169,170,,,,,,,,170,170,170,170', -'170,170,169,170,170,,,,171,,170,,,172,,,171,171,171,171,171,171,,171', -'171,170,172,172,,172,171,172,,173,172,,,,,,,,,,,171,173,173,,173,,173', -',172,173,,,,,,,172,172,172,172,172,172,,172,172,,,,173,,172,,,174,,', -'173,173,173,173,173,173,,173,173,172,174,174,,174,173,174,,175,174,', -',,,,,,,,,173,175,175,,175,,175,,174,175,,,,,,,174,174,174,174,174,174', -',174,174,,,,175,,174,,,,,176,175,175,175,175,175,175,,175,175,174,,176', -'176,176,175,176,,176,,177,176,176,176,176,,,,,,175,,177,177,177,,177', -',177,,176,177,177,177,177,,,,176,176,176,176,176,176,,176,176,,,,177', -',176,,,177,,,177,177,177,177,177,177,,177,177,176,180,,,,177,,,,,180', -'180,180,180,180,180,,180,,180,177,,180,180,180,180,,,,,,,,,,,,,,,,180', -',,,180,180,,,180,180,180,180,180,180,,180,180,179,,,,,180,,,,179,179', -'179,179,179,179,,179,,179,,180,179,179,179,179,,,,,,,,,,,,,,,,179,,', -',179,179,,,179,179,179,179,179,179,,179,179,178,,,,,179,,,,178,178,178', -'178,178,178,,178,,178,,179,178,178,178,178,,,,,,,,,,,,,,,,178,,,,178', -'178,,,178,178,178,178,178,178,,178,178,,,,,,178,209,209,209,209,,209', -'209,209,209,209,,209,209,,178,,,,,209,209,209,267,267,267,267,,267,267', -'267,267,267,,267,267,,,209,209,,,267,267,267,262,262,262,262,,262,262', -'262,262,262,,262,262,,,267,267,,,262,262,262,,,,,,,,,,,,,,,,262,262' ] +',,,,,,,,110,,,,110,110,,,110,110,110,110,110,110,,110,110,314,,,,,110', +',,,314,314,314,314,314,314,,314,,314,,110,314,314,314,314,,,,,,,,,,', +',,,,,314,,,,314,314,,,314,314,314,314,314,314,,314,314,317,,,,,314,', +',,317,317,317,317,317,317,,317,,317,,314,317,317,317,317,,,,,,,,,,,', +',,,,317,,,,317,317,,,317,317,317,317,317,317,,317,317,152,,,,,317,,', +',152,152,152,152,152,152,,152,,152,,317,152,152,152,152,,,,,,,,,,,,', +',,,152,,,,152,152,,,152,152,152,152,152,152,,152,152,322,,,,,152,,,', +'322,322,322,322,322,322,,322,,322,,152,322,322,322,322,,,,,,,,,,,,,', +',,322,,,,322,322,,,322,322,322,322,322,322,,322,322,211,,,,,322,,,,211', +'211,211,211,211,211,,211,,211,,322,211,211,211,211,,,,,,,,,,,,,,,,211', +',,,211,211,,,211,211,211,211,211,211,,211,211,330,,,,,211,,,,330,330', +'330,330,330,330,,330,,330,,211,330,330,330,330,,,,,,,,,,,,,,,,330,,', +',330,330,,,330,330,330,330,330,330,,330,330,331,,,,,330,,,,331,331,331', +'331,331,331,,331,,331,,330,331,331,331,331,,,,,,,,,,,,,,,,331,,,,331', +'331,,,331,331,331,331,331,331,,331,331,337,,,,,331,,,,337,337,337,337', +'337,337,,337,,337,,331,337,337,337,337,,,,,,,,,,,,,,,,337,,,,337,337', +',,337,337,337,337,337,337,,337,337,187,,,,,337,,,,187,187,187,187,187', +'187,187,187,,187,,337,187,187,187,187,,,,,,,,,,,,,,,,187,,,,187,187', +',,187,187,187,187,187,187,,187,187,11,,11,,,187,,,,11,11,11,11,11,11', +',11,,11,,187,11,11,11,11,,,,,,,,,,,,,,,,11,,,,11,11,,,11,11,11,11,11', +'11,,11,11,,182,,,,11,182,,,,182,182,182,182,182,182,,182,169,182,11', +',182,182,182,182,,,,,,,169,,169,,169,,,,,182,,,,182,182,,,182,182,182', +'182,182,182,,182,182,169,,,170,,182,,,169,169,169,169,,,,169,169,170', +',170,182,170,169,,,,171,,,,,,,,,,,169,,,171,,171,170,171,,172,,,,,170', +'170,170,170,,,,170,170,172,,172,,172,170,171,,,,,,,,171,171,171,171', +'171,171,170,171,171,,,,172,,171,,,173,,,172,172,172,172,172,172,,172', +'172,171,173,173,,173,172,173,,174,173,,,,,,,,,,,172,174,174,,174,,174', +',173,174,,,,,,,173,173,173,173,173,173,,173,173,,,,174,,173,,,175,,', +'174,174,174,174,174,174,,174,174,173,175,175,,175,174,175,,176,175,', +',,,,,,,,,174,176,176,,176,,176,,175,176,,,,,,,175,175,175,175,175,175', +',175,175,,,,176,,175,,,,,177,176,176,176,176,176,176,,176,176,175,,177', +'177,177,176,177,,177,,178,177,177,177,177,,,,,,176,,178,178,178,,178', +',178,,177,178,178,178,178,,,,177,177,177,177,177,177,,177,177,,,,178', +',177,,,178,,,178,178,178,178,178,178,,178,178,177,181,,,,178,,,,,181', +'181,181,181,181,181,,181,,181,178,,181,181,181,181,,,,,,,,,,,,,,,,181', +',,,181,181,,,181,181,181,181,181,181,,181,181,180,,,,,181,,,,180,180', +'180,180,180,180,,180,,180,,181,180,180,180,180,,,,,,,,,,,,,,,,180,,', +',180,180,,,180,180,180,180,180,180,,180,180,179,,,,,180,,,,179,179,179', +'179,179,179,,179,,179,,180,179,179,179,179,,,,,,,,,,,,,,,,179,,,,179', +'179,,,179,179,179,179,179,179,,179,179,,,,,,179,210,210,210,210,,210', +'210,210,210,210,,210,210,,179,,,,,210,210,210,268,268,268,268,,268,268', +'268,268,268,,268,268,,,210,210,,,268,268,268,263,263,263,263,,263,263', +'263,263,263,,263,263,,,268,268,,,263,263,263,,,,,,,,,,,,,,,,263,263' ] racc_action_check = arr = ::Array.new(6060, nil) idx = 0 clist.each do |str| @@ -456,176 +456,176 @@ racc_action_pointer = [ 4808, 3634, nil, 129, -15, 3796, 4750, nil, 4636, 4521, 4464, 118, 4120, 41, nil, nil, nil, nil, 10, 27, nil, 92, nil, nil, nil, nil, 4407, 71, nil, 106, - nil, 4579, 79, nil, nil, 4693, nil, 54, 159, 1360, - -10, 4979, nil, 199, nil, nil, nil, nil, nil, 108, - 8, 1424, 2, 118, 986, 4184, 3534, 1260, 5453, 5496, - 5519, 5539, 5584, 5604, 5649, 5669, 5716, 5736, 5908, 5851, - 5794, 5436, nil, nil, 274, nil, 5321, 598, 868, 922, - 257, 255, nil, nil, -4, nil, -9, -8, 29, -25, - 1134, -1, -2, nil, nil, nil, nil, nil, nil, 5946, - 5093, 207, nil, 226, nil, 227, 155, nil, 1032, nil, - 186, nil, 154, -7, nil, 1306, 3580, 3850, 4338, 3958, - 124, 122, nil, -8, 147, 121, 1057, nil, 245, 82, - 4012, nil, 3904, nil, 4284, nil, 4230, nil, nil, nil, - nil, 4174, nil, nil, nil, 83, nil, nil, 4066, 3742, - 113, nil, 5990, nil, 126, 3688, 136, 5968, 3524, 3470, - 156, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, 3416, 150, nil, 174, nil, 117, 153, - 3254, nil, 184, 100, 196, 178, 0, 3146, nil, 174, - 205, 179, 212, 216, nil, 64, nil, 218, 1578, 1470, - nil, nil, nil, 4865, nil, nil, 4922, nil, nil, nil, - 210, 5036, 233, 976, 238, nil, nil, nil, nil, 5150, - 5207, 248, 191, nil, nil, nil, 5264, 87, nil, 544, - 263, 241, nil, 266, 270, nil, nil, nil, 270, 277, - 278, nil, 490, nil, nil, nil, 265, 283, nil, nil, - 286, nil, nil, nil, nil, nil, nil, nil, nil, nil, - 220, nil, 164, 54, nil, nil, nil, 294, nil, nil, - nil, 297, nil, 302, nil, 309, nil, nil, nil, nil, - nil ] + nil, 4579, 79, nil, nil, 4693, nil, 54, nil, 159, + 1360, -10, 4979, nil, 199, nil, nil, nil, nil, nil, + 108, 8, 1424, 2, 118, 986, 4184, 3534, 1260, 5453, + 5496, 5519, 5539, 5584, 5604, 5649, 5669, 5716, 5736, 5908, + 5851, 5794, 5436, nil, nil, 274, nil, 5321, 598, 868, + 922, 257, 255, nil, nil, -4, nil, -9, -8, 29, + -25, 1134, -1, -2, nil, nil, nil, nil, nil, nil, + 5946, 5093, 207, nil, 226, nil, 227, 155, nil, 1032, + nil, 186, nil, 154, -7, nil, 1306, 3580, 3850, 4338, + 3958, 124, 122, nil, -8, 147, 121, 1057, nil, 245, + 82, 4012, nil, 3904, nil, 4284, nil, 4230, nil, nil, + nil, nil, 4174, nil, nil, nil, 83, nil, nil, 4066, + 3742, 113, nil, 5990, nil, 126, 3688, 136, 5968, 3524, + 3470, 156, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, 3416, 150, nil, 174, nil, 117, + 153, 3254, nil, 184, 100, 196, 178, 0, 3146, nil, + 174, 205, 179, 212, 216, nil, 64, nil, 218, 1578, + 1470, nil, nil, nil, 4865, nil, nil, 4922, nil, nil, + nil, 210, 5036, 233, 976, 238, nil, nil, nil, nil, + 5150, 5207, 248, 191, nil, nil, nil, 5264, 87, nil, + 544, 263, 241, nil, 266, 270, nil, nil, nil, 270, + 277, 278, nil, 490, nil, nil, nil, 265, 283, nil, + nil, 286, nil, nil, nil, nil, nil, nil, nil, nil, + nil, 220, nil, 164, 54, nil, nil, nil, 294, nil, + nil, nil, 297, nil, 302, nil, 309, nil, nil, nil, + nil, nil ] racc_action_default = [ - -223, -224, -1, -2, -3, -4, -5, -8, -10, -11, - -16, -107, -224, -224, -224, -45, -46, -47, -48, -49, + -224, -225, -1, -2, -3, -4, -5, -8, -10, -11, + -16, -107, -225, -225, -225, -45, -46, -47, -48, -49, -50, -51, -52, -53, -54, -55, -56, -57, -58, -59, -60, -61, -62, -63, -64, -65, -66, -67, -72, -73, - -77, -224, -224, -224, -224, -224, -118, -120, -224, -224, - -165, -224, -224, -224, -178, -179, -180, -181, -224, -183, - -224, -193, -196, -224, -198, -199, -200, -201, -202, -203, - -204, -224, -224, -7, -224, -224, -224, -224, -224, -224, - -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, - -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, - -224, -224, -224, -127, -122, -223, -223, -28, -224, -35, - -224, -224, -74, -224, -224, -224, -224, -84, -224, -224, - -224, -224, -224, -223, -137, -156, -157, -119, -223, -223, - -146, -148, -149, -150, -151, -152, -43, -224, -168, -224, - -171, -224, -224, -174, -175, -187, -182, -224, -224, -224, - -224, -197, 391, -6, -9, -12, -13, -14, -15, -224, - -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, - -29, -30, -31, -32, -33, -34, -36, -37, -38, -39, - -40, -224, -41, -102, -224, -78, -224, -216, -222, -210, - -207, -205, -116, -128, -199, -131, -203, -224, -213, -211, - -219, -201, -202, -209, -214, -215, -217, -218, -220, -127, - -126, -224, -125, -224, -42, -205, -69, -79, -224, -82, - -205, -161, -164, -224, -76, -224, -224, -224, -127, -224, - -207, -223, -158, -224, -224, -224, -224, -154, -224, -224, - -224, -166, -224, -169, -224, -172, -224, -184, -185, -186, - -188, -224, -190, -191, -192, -205, -194, -17, -224, -224, - -205, -104, -127, -115, -224, -208, -224, -206, -224, -224, - -205, -130, -132, -210, -211, -212, -213, -216, -219, -221, - -222, -123, -124, -206, -224, -71, -224, -81, -224, -206, - -224, -75, -224, -87, -224, -93, -224, -224, -97, -207, - -205, -207, -224, -224, -140, -224, -159, -205, -223, -224, - -147, -155, -153, -44, -167, -170, -177, -173, -176, -189, - -224, -106, -224, -206, -205, -110, -117, -111, -129, -133, - -134, -224, -68, -80, -83, -162, -163, -87, -86, -224, - -224, -93, -92, -224, -224, -101, -96, -98, -224, -224, - -224, -113, -223, -141, -142, -143, -224, -224, -138, -139, - -224, -145, -195, -103, -105, -114, -121, -70, -85, -88, - -224, -91, -224, -224, -108, -109, -112, -224, -160, -135, - -144, -224, -90, -224, -95, -224, -100, -136, -89, -94, - -99 ] + -77, -225, -225, -225, -225, -225, -118, -120, -225, -225, + -165, -225, -225, -225, -178, -179, -180, -181, -225, -183, + -225, -194, -197, -225, -199, -200, -201, -202, -203, -204, + -205, -225, -225, -7, -225, -225, -225, -225, -225, -225, + -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, + -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, + -225, -225, -225, -127, -122, -224, -224, -28, -225, -35, + -225, -225, -74, -225, -225, -225, -225, -84, -225, -225, + -225, -225, -225, -224, -137, -156, -157, -119, -224, -224, + -146, -148, -149, -150, -151, -152, -43, -225, -168, -225, + -171, -225, -225, -174, -175, -187, -182, -225, -190, -225, + -225, -225, -198, 392, -6, -9, -12, -13, -14, -15, + -225, -18, -19, -20, -21, -22, -23, -24, -25, -26, + -27, -29, -30, -31, -32, -33, -34, -36, -37, -38, + -39, -40, -225, -41, -102, -225, -78, -225, -217, -223, + -211, -208, -206, -116, -128, -200, -131, -204, -225, -214, + -212, -220, -202, -203, -210, -215, -216, -218, -219, -221, + -127, -126, -225, -125, -225, -42, -206, -69, -79, -225, + -82, -206, -161, -164, -225, -76, -225, -225, -225, -127, + -225, -208, -224, -158, -225, -225, -225, -225, -154, -225, + -225, -225, -166, -225, -169, -225, -172, -225, -184, -185, + -186, -188, -225, -191, -192, -193, -206, -195, -17, -225, + -225, -206, -104, -127, -115, -225, -209, -225, -207, -225, + -225, -206, -130, -132, -211, -212, -213, -214, -217, -220, + -222, -223, -123, -124, -207, -225, -71, -225, -81, -225, + -207, -225, -75, -225, -87, -225, -93, -225, -225, -97, + -208, -206, -208, -225, -225, -140, -225, -159, -206, -224, + -225, -147, -155, -153, -44, -167, -170, -177, -173, -176, + -189, -225, -106, -225, -207, -206, -110, -117, -111, -129, + -133, -134, -225, -68, -80, -83, -162, -163, -87, -86, + -225, -225, -93, -92, -225, -225, -101, -96, -98, -225, + -225, -225, -113, -224, -141, -142, -143, -225, -225, -138, + -139, -225, -145, -196, -103, -105, -114, -121, -70, -85, + -88, -225, -91, -225, -225, -108, -109, -112, -225, -160, + -135, -144, -225, -90, -225, -95, -225, -100, -136, -89, + -94, -99 ] racc_goto_table = [ - 2, 112, 4, 144, 107, 109, 110, 128, 146, 191, - 134, 132, 190, 183, 342, 231, 220, 264, 338, 357, - 234, 155, 156, 157, 158, 310, 1, 311, 211, 213, - 266, 230, 298, 116, 118, 119, 120, 73, 260, 326, - 262, 137, 139, 136, 136, 141, 344, 328, 297, 369, - 145, 217, 255, 303, 284, 151, 353, 302, 235, 288, - 371, 182, 368, 377, 335, 142, 154, 252, 253, 159, - 3, 136, 160, 161, 162, 163, 164, 165, 166, 167, - 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, - 178, 179, 180, 181, 320, 186, 261, 210, 210, 322, - 250, 215, 347, 136, 153, 223, 251, 136, 249, 331, - 149, nil, nil, nil, 186, 270, nil, nil, nil, nil, - nil, nil, nil, nil, nil, 232, 348, nil, 350, nil, - 232, 237, nil, nil, 300, 307, nil, 299, 301, 349, - nil, nil, nil, nil, nil, nil, 356, nil, nil, 254, - nil, nil, nil, nil, nil, nil, nil, 128, nil, nil, - nil, 134, 132, 365, nil, nil, nil, nil, 324, nil, - nil, nil, nil, nil, nil, nil, 181, nil, 285, 116, - 118, 119, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, 318, 134, 132, 134, - 132, 319, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, 286, 136, - 186, 186, nil, nil, nil, 292, 294, nil, nil, nil, - nil, nil, 313, 304, 313, 364, 316, nil, 141, nil, - nil, nil, nil, 145, nil, nil, nil, nil, nil, nil, - 313, 321, nil, nil, nil, nil, nil, 186, nil, nil, - 329, 330, nil, nil, 354, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, 313, nil, nil, nil, nil, - nil, nil, 336, nil, nil, nil, nil, nil, nil, 136, - nil, nil, nil, nil, 367, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, 360, - 359, nil, nil, nil, nil, 181, nil, nil, nil, nil, + 2, 112, 4, 144, 107, 109, 110, 128, 146, 192, + 134, 132, 191, 265, 221, 184, 343, 232, 1, 339, + 358, 311, 235, 312, 299, 156, 157, 158, 159, 212, + 214, 231, 267, 116, 118, 119, 120, 73, 261, 327, + 263, 345, 329, 136, 136, 141, 298, 370, 218, 304, + 145, 256, 354, 303, 236, 152, 285, 183, 336, 142, + 155, 289, 372, 369, 378, 253, 254, 3, 251, 252, + 250, 136, 161, 162, 163, 164, 165, 166, 167, 168, + 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, + 179, 180, 181, 182, 348, 187, 321, 211, 211, 262, + 148, 323, 150, 136, 154, nil, nil, 136, 137, 139, + nil, 332, nil, nil, 187, nil, 271, nil, nil, nil, + nil, nil, 349, nil, 351, 233, nil, nil, nil, nil, + 233, 238, nil, nil, 308, 301, 160, nil, 300, 302, + nil, 350, nil, nil, nil, nil, nil, nil, 357, nil, + 255, nil, nil, nil, nil, nil, nil, nil, 128, nil, + nil, nil, 134, 132, nil, 366, nil, nil, 216, 325, + nil, nil, 224, nil, nil, nil, nil, 182, nil, 286, + 116, 118, 119, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, 319, 134, 132, + 134, 132, 320, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, 287, + 136, 187, 187, nil, nil, nil, 293, 295, nil, nil, + nil, nil, nil, 314, 305, 314, nil, 317, 365, 141, + nil, nil, nil, nil, 145, nil, nil, nil, nil, nil, + nil, 314, 322, nil, nil, nil, nil, nil, 187, nil, + nil, 330, 331, nil, nil, 355, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, 314, nil, nil, nil, + nil, nil, nil, 337, nil, nil, nil, nil, nil, nil, + 136, nil, nil, nil, nil, 368, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, 116, nil, nil, nil, nil, nil, nil, nil, nil, + 361, 360, nil, nil, nil, nil, 182, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, 359, nil, nil, nil, nil, nil, + nil, nil, 116, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - 381, nil, 383, 385 ] + nil, nil, nil, nil, nil, 360, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, 382, nil, 384, 386 ] racc_goto_check = [ 2, 39, 4, 76, 10, 10, 10, 64, 81, 56, - 31, 37, 54, 51, 47, 65, 44, 55, 46, 66, - 65, 8, 8, 8, 8, 72, 1, 72, 60, 60, - 38, 54, 49, 10, 10, 10, 10, 6, 52, 57, - 58, 12, 12, 10, 10, 10, 50, 61, 48, 45, - 10, 43, 44, 68, 38, 10, 69, 55, 71, 38, - 47, 13, 46, 66, 74, 75, 7, 77, 78, 12, - 3, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 31, 37, 54, 55, 44, 51, 47, 65, 1, 46, + 66, 72, 65, 72, 49, 8, 8, 8, 8, 60, + 60, 54, 38, 10, 10, 10, 10, 6, 52, 57, + 58, 50, 61, 10, 10, 10, 48, 45, 43, 68, + 10, 44, 69, 55, 71, 10, 38, 13, 74, 75, + 7, 38, 47, 46, 66, 77, 78, 3, 82, 83, + 85, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, - 10, 10, 10, 10, 38, 10, 51, 10, 10, 38, - 82, 12, 49, 10, 6, 12, 83, 10, 85, 38, - 86, nil, nil, nil, 10, 56, nil, nil, nil, nil, - nil, nil, nil, nil, nil, 4, 55, nil, 55, nil, - 4, 4, nil, nil, 56, 44, nil, 54, 54, 38, - nil, nil, nil, nil, nil, nil, 38, nil, nil, 2, - nil, nil, nil, nil, nil, nil, nil, 64, nil, nil, - nil, 31, 37, 38, nil, nil, nil, nil, 56, nil, - nil, nil, nil, nil, nil, nil, 10, nil, 39, 10, - 10, 10, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, 76, 31, 37, 31, - 37, 81, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, 2, 10, - 10, 10, nil, nil, nil, 2, 2, nil, nil, nil, - nil, nil, 10, 4, 10, 51, 10, nil, 10, nil, - nil, nil, nil, 10, nil, nil, nil, nil, nil, nil, - 10, 10, nil, nil, nil, nil, nil, 10, nil, nil, - 10, 10, nil, nil, 64, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, 10, nil, nil, nil, nil, - nil, nil, 10, nil, nil, nil, nil, nil, nil, 10, - nil, nil, nil, nil, 39, nil, nil, nil, nil, nil, + 10, 10, 10, 10, 49, 10, 38, 10, 10, 51, + 86, 38, 87, 10, 6, nil, nil, 10, 12, 12, + nil, 38, nil, nil, 10, nil, 56, nil, nil, nil, + nil, nil, 55, nil, 55, 4, nil, nil, nil, nil, + 4, 4, nil, nil, 44, 56, 12, nil, 54, 54, + nil, 38, nil, nil, nil, nil, nil, nil, 38, nil, + 2, nil, nil, nil, nil, nil, nil, nil, 64, nil, + nil, nil, 31, 37, nil, 38, nil, nil, 12, 56, + nil, nil, 12, nil, nil, nil, nil, 10, nil, 39, + 10, 10, 10, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, 76, 31, 37, + 31, 37, 81, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 2, - 4, nil, nil, nil, nil, 10, nil, nil, nil, nil, + 10, 10, 10, nil, nil, nil, 2, 2, nil, nil, + nil, nil, nil, 10, 4, 10, nil, 10, 51, 10, + nil, nil, nil, nil, 10, nil, nil, nil, nil, nil, + nil, 10, 10, nil, nil, nil, nil, nil, 10, nil, + nil, 10, 10, nil, nil, 64, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, 10, nil, nil, nil, + nil, nil, nil, 10, nil, nil, nil, nil, nil, nil, + 10, nil, nil, nil, nil, 39, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, 10, nil, nil, nil, nil, nil, nil, nil, nil, + 2, 4, nil, nil, nil, nil, 10, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, 4, nil, nil, nil, nil, nil, + nil, nil, 10, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - 2, nil, 2, 2 ] + nil, nil, nil, nil, nil, 4, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, 2, nil, 2, 2 ] racc_goto_pointer = [ - nil, 26, 0, 70, 2, nil, 32, -8, -54, nil, - -8, nil, -10, -40, nil, nil, nil, nil, nil, nil, + nil, 18, 0, 67, 2, nil, 32, -14, -50, nil, + -8, nil, 57, -44, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, -39, nil, nil, nil, nil, nil, -38, -161, -38, - nil, nil, nil, -62, -98, -290, -275, -281, -179, -195, - -250, -88, -146, nil, -91, -173, -94, -226, -146, nil, - -77, -220, nil, nil, -41, -108, -289, nil, -178, -249, - nil, -71, -211, nil, -225, 12, -50, -81, -80, nil, - nil, -50, -47, -41, nil, -39, 49 ] + nil, -39, nil, nil, nil, nil, nil, -38, -160, -38, + nil, nil, nil, -65, -100, -293, -275, -280, -182, -204, + -256, -86, -147, nil, -91, -178, -94, -227, -147, nil, + -76, -226, nil, nil, -41, -106, -289, nil, -183, -254, + nil, -75, -216, nil, -232, 6, -50, -84, -83, nil, + nil, -50, -79, -78, nil, -77, 40, 41 ] racc_goto_default = [ - nil, nil, 358, nil, 212, 5, 6, 7, 8, 9, - 11, 10, 296, nil, 15, 38, 16, 17, 18, 19, + nil, nil, 359, nil, 213, 5, 6, 7, 8, 9, + 11, 10, 297, nil, 15, 38, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, nil, nil, 39, 40, 113, nil, nil, 117, nil, nil, nil, nil, - nil, nil, nil, 44, nil, nil, nil, 192, nil, 104, - nil, 193, 197, 195, 124, nil, nil, 123, nil, nil, - 129, nil, 130, 131, 221, nil, nil, 54, 55, 56, - 58, nil, nil, nil, 147, nil, nil ] + nil, nil, nil, 44, nil, nil, nil, 193, nil, 104, + nil, 194, 198, 196, 124, nil, nil, 123, nil, nil, + 129, nil, 130, 131, 222, nil, nil, 54, 55, 56, + 58, nil, nil, nil, 147, nil, nil, nil ] racc_reduce_table = [ 0, 0, :racc_error, @@ -818,23 +818,24 @@ racc_reduce_table = [ 1, 170, :_reduce_187, 1, 171, :_reduce_188, 2, 171, :_reduce_189, - 3, 165, :_reduce_190, - 3, 165, :_reduce_191, - 3, 89, :_reduce_192, - 0, 172, :_reduce_193, - 2, 172, :_reduce_194, - 4, 172, :_reduce_195, - 1, 112, :_reduce_196, - 2, 112, :_reduce_197, - 1, 119, :_reduce_198, - 1, 122, :_reduce_199, - 1, 120, :_reduce_200, - 1, 121, :_reduce_201, - 1, 115, :_reduce_202, - 1, 114, :_reduce_203, - 1, 117, :_reduce_204, + 2, 165, :_reduce_190, + 2, 172, :_reduce_191, + 2, 172, :_reduce_192, + 3, 89, :_reduce_193, + 0, 173, :_reduce_194, + 2, 173, :_reduce_195, + 4, 173, :_reduce_196, + 1, 112, :_reduce_197, + 2, 112, :_reduce_198, + 1, 119, :_reduce_199, + 1, 122, :_reduce_200, + 1, 120, :_reduce_201, + 1, 121, :_reduce_202, + 1, 115, :_reduce_203, + 1, 114, :_reduce_204, + 1, 117, :_reduce_205, 0, 124, :_reduce_none, - 1, 124, :_reduce_206, + 1, 124, :_reduce_207, 0, 141, :_reduce_none, 1, 141, :_reduce_none, 1, 149, :_reduce_none, @@ -851,11 +852,11 @@ racc_reduce_table = [ 1, 149, :_reduce_none, 1, 149, :_reduce_none, 1, 149, :_reduce_none, - 0, 90, :_reduce_223 ] + 0, 90, :_reduce_224 ] -racc_reduce_n = 224 +racc_reduce_n = 225 -racc_shift_n = 391 +racc_shift_n = 392 racc_token_table = { false => 0, @@ -1138,6 +1139,7 @@ Racc_token_to_s_table = [ "dqmid", "text_expression", "dqtail", + "sublocated_text", "epp_parameters_list" ] Racc_debug_parser = false @@ -2371,120 +2373,125 @@ module_eval(<<'.,.,', 'egrammar.ra', 659) module_eval(<<'.,.,', 'egrammar.ra', 663) def _reduce_190(val, _values, result) - result = Factory.HEREDOC(val[0][:value], val[1]); loc result, val[0], val[1] + result = Factory.HEREDOC(val[0][:value], val[1]); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 664) +module_eval(<<'.,.,', 'egrammar.ra', 666) def _reduce_191(val, _values, result) - result = Factory.HEREDOC(val[0][:value], val[1]); loc result, val[0], val[1] + result = Factory.SUBLOCATE(val[0], val[1]); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 668) +module_eval(<<'.,.,', 'egrammar.ra', 667) def _reduce_192(val, _values, result) - result = Factory.EPP(val[1], val[2]); loc result, val[0] + result = Factory.SUBLOCATE(val[0], val[1]); loc result, val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 671) def _reduce_193(val, _values, result) - result = [] + result = Factory.EPP(val[1], val[2]); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 672) +module_eval(<<'.,.,', 'egrammar.ra', 674) def _reduce_194(val, _values, result) result = [] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 673) +module_eval(<<'.,.,', 'egrammar.ra', 675) def _reduce_195(val, _values, result) - result = val[1] + result = [] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 676) def _reduce_196(val, _values, result) - result = Factory.RENDER_STRING(val[0][:value]); loc result, val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 677) - def _reduce_197(val, _values, result) - result = Factory.RENDER_EXPR(val[1]); loc result, val[0], val[1] + result = val[1] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 679) - def _reduce_198(val, _values, result) - result = Factory.NUMBER(val[0][:value]) ; loc result, val[0] + def _reduce_197(val, _values, result) + result = Factory.RENDER_STRING(val[0][:value]); loc result, val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 680) - def _reduce_199(val, _values, result) - result = Factory.QNAME_OR_NUMBER(val[0][:value]) ; loc result, val[0] - result - end -.,., - -module_eval(<<'.,.,', 'egrammar.ra', 681) - def _reduce_200(val, _values, result) - result = Factory.QREF(val[0][:value]) ; loc result, val[0] + def _reduce_198(val, _values, result) + result = Factory.RENDER_EXPR(val[1]); loc result, val[0], val[1] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 682) - def _reduce_201(val, _values, result) - result = Factory.literal(:undef); loc result, val[0] + def _reduce_199(val, _values, result) + result = Factory.NUMBER(val[0][:value]) ; loc result, val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 683) - def _reduce_202(val, _values, result) - result = Factory.literal(:default); loc result, val[0] + def _reduce_200(val, _values, result) + result = Factory.QNAME_OR_NUMBER(val[0][:value]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 688) +module_eval(<<'.,.,', 'egrammar.ra', 684) + def _reduce_201(val, _values, result) + result = Factory.QREF(val[0][:value]) ; loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 685) + def _reduce_202(val, _values, result) + result = Factory.literal(:undef); loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 686) def _reduce_203(val, _values, result) - result = Factory.literal(val[0][:value]) ; loc result, val[0] + result = Factory.literal(:default); loc result, val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 691) def _reduce_204(val, _values, result) + result = Factory.literal(val[0][:value]) ; loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 694) + def _reduce_205(val, _values, result) result = Factory.literal(val[0][:value]); loc result, val[0] result end .,., -# reduce 205 omitted +# reduce 206 omitted -module_eval(<<'.,.,', 'egrammar.ra', 697) - def _reduce_206(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 700) + def _reduce_207(val, _values, result) result = nil result end .,., -# reduce 207 omitted - # reduce 208 omitted # reduce 209 omitted @@ -2515,8 +2522,10 @@ module_eval(<<'.,.,', 'egrammar.ra', 697) # reduce 222 omitted -module_eval(<<'.,.,', 'egrammar.ra', 720) - def _reduce_223(val, _values, result) +# reduce 223 omitted + +module_eval(<<'.,.,', 'egrammar.ra', 723) + def _reduce_224(val, _values, result) result = nil result end diff --git a/lib/puppet/pops/parser/heredoc_support.rb b/lib/puppet/pops/parser/heredoc_support.rb index 0ea552182..76897525a 100644 --- a/lib/puppet/pops/parser/heredoc_support.rb +++ b/lib/puppet/pops/parser/heredoc_support.rb @@ -90,16 +90,23 @@ module Puppet::Pops::Parser::HeredocSupport # Record position where next heredoc (from same line as current @()) should start scanning for content ctx[:newline_jump] = scn.pos - # Emit a token that provides the grammar with location information about the lines on which the heredoc - # content is based - enqueue_completed([:SUBLOCATE, lines, lines.size], heredoc_offset) # Process captured lines - remove leading, and trailing newline str = heredoc_text(lines, leading, has_margin, remove_break) # Use a new lexer instance configured with a sub-locator to enable correct positioning sublexer = self.class.new() - locator = SubLocator.sub_locator(str, locator.file, heredoc_line, heredoc_offset, leading.length()) + locator = Puppet::Pops::Parser::Locator::SubLocator.sub_locator(str, + locator.file, heredoc_line, heredoc_offset, leading.length()) + + # Emit a token that provides the grammar with location information about the lines on which the heredoc + # content is based. + enqueue([:SUBLOCATE, + Puppet::Pops::Parser::LexerSupport::TokenValue.new([:SUBLOCATE, + lines, lines.reduce(0) {|size, s| size + s.length} ], + heredoc_offset, + locator)]) + sublexer.lex_unquoted_string(str, locator, resulting_escapes, dqstring_style) sublexer.interpolate_uq_to(self) # Continue scan after @(...) @@ -128,67 +135,5 @@ module Puppet::Pops::Parser::HeredocSupport result end - # A Sublocator locates a concrete locator (subspace) in a virtual space. - # The `leading_line_count` is the (virtual) number of lines preceding the first line in the concrete locator. - # The `leading_offset` is the (virtual) byte offset of the first byte in the concrete locator. - # The `leading_line_offset` is the (virtual) offset / margin in characters for each line. - # - # This illustrates characters in the sublocator (`.`) inside the subspace (`X`): - # - # 1:XXXXXXXX - # 2:XXXX.... .. ... .. - # 3:XXXX. . .... .. - # 4:XXXX............ - # - # This sublocator would be configured with leading_line_count = 1, - # leading_offset=8, and leading_line_offset=4 - # - # Note that leading_offset must be the same for all lines and measured in characters. - # - class SubLocator < Puppet::Pops::Parser::Locator - def self.sub_locator(string, file, leading_line_count, leading_offset, leading_line_offset) - self.new(Puppet::Pops::Parser::Locator.locator(string, file), - leading_line_count, - leading_offset, - leading_line_offset) - end - - def initialize(locator, leading_line_count, leading_offset, leading_line_offset) - @locator = locator - @leading_line_count = leading_line_count - @leading_offset = leading_offset - @leading_line_offset = leading_line_offset - end - - def file - @locator.file - end - - def string - @locator.string - end - - # Given offset is offset in the subspace - def line_for_offset(offset) - @locator.line_for_offset(offset) + @leading_line_count - end - - # Given offset is offset in the subspace - def offset_on_line(offset) - @locator.offset_on_line(offset) + @leading_line_offset - end - - # Given offset is offset in the subspace - def char_offset(offset) - effective_line = @locator.line_for_offset(offset) - locator.char_offset(offset) + (effective_line * @leading_line_offset) + @leading_offset - end - - # Given offsets are offsets in the subspace - def char_length(offset, end_offset) - effective_line = @locator.line_for_offset(end_offset) - @locator.line_for_offset(offset) - locator.char_length(offset, end_offset) + (effective_line * @leading_line_offset) - end - end end diff --git a/lib/puppet/pops/parser/locator.rb b/lib/puppet/pops/parser/locator.rb index 7e97004f0..526126aca 100644 --- a/lib/puppet/pops/parser/locator.rb +++ b/lib/puppet/pops/parser/locator.rb @@ -71,6 +71,78 @@ class Puppet::Pops::Parser::Locator def line_index() end + # A Sublocator locates a concrete locator (subspace) in a virtual space. + # The `leading_line_count` is the (virtual) number of lines preceding the first line in the concrete locator. + # The `leading_offset` is the (virtual) byte offset of the first byte in the concrete locator. + # The `leading_line_offset` is the (virtual) offset / margin in characters for each line. + # + # This illustrates characters in the sublocator (`.`) inside the subspace (`X`): + # + # 1:XXXXXXXX + # 2:XXXX.... .. ... .. + # 3:XXXX. . .... .. + # 4:XXXX............ + # + # This sublocator would be configured with leading_line_count = 1, + # leading_offset=8, and leading_line_offset=4 + # + # Note that leading_offset must be the same for all lines and measured in characters. + # + class SubLocator < Puppet::Pops::Parser::Locator + attr_reader :locator + attr_reader :leading_line_count + attr_reader :leading_offset + attr_reader :leading_line_offset + + def self.sub_locator(string, file, leading_line_count, leading_offset, leading_line_offset) + self.new(Puppet::Pops::Parser::Locator.locator(string, file), + leading_line_count, + leading_offset, + leading_line_offset) + end + + def initialize(locator, leading_line_count, leading_offset, leading_line_offset) + @locator = locator + @leading_line_count = leading_line_count + @leading_offset = leading_offset + @leading_line_offset = leading_line_offset + end + + def file + @locator.file + end + + def string + @locator.string + end + + # Given offset is offset in the subspace + def line_for_offset(offset) + @locator.line_for_offset(offset) + @leading_line_count + end + + # Given offset is offset in the subspace + def offset_on_line(offset) + @locator.offset_on_line(offset) + @leading_line_offset + end + + # Given offset is offset in the subspace + def char_offset(offset) + effective_line = @locator.line_for_offset(offset) + locator.char_offset(offset) + (effective_line * @leading_line_offset) + @leading_offset + end + + # Given offsets are offsets in the subspace + def char_length(offset, end_offset) + effective_line = @locator.line_for_offset(end_offset) - @locator.line_for_offset(offset) + locator.char_length(offset, end_offset) + (effective_line * @leading_line_offset) + end + + def pos_on_line(offset) + offset_on_line(offset) +1 + end + end + private class AbstractLocator < Puppet::Pops::Parser::Locator diff --git a/spec/unit/pops/parser/lexer2_spec.rb b/spec/unit/pops/parser/lexer2_spec.rb index ce1f0ab8a..e07d3722c 100644 --- a/spec/unit/pops/parser/lexer2_spec.rb +++ b/spec/unit/pops/parser/lexer2_spec.rb @@ -296,7 +296,7 @@ describe 'Lexer2' do Tex\\tt\\n |- END CODE - tokens_scanned_from(code).should match_tokens2([:HEREDOC, 'syntax'], [:STRING, "Tex\tt\\n"]) + tokens_scanned_from(code).should match_tokens2([:HEREDOC, 'syntax'], :SUBLOCATE, [:STRING, "Tex\tt\\n"]) end it 'lexes "tag", syntax and escapes, margin, right trim and interpolation' do @@ -307,6 +307,7 @@ describe 'Lexer2' do CODE tokens_scanned_from(code).should match_tokens2( [:HEREDOC, 'syntax'], + :SUBLOCATE, [:DQPRE, "Tex\tt\\n"], [:VARIABLE, "var"], [:DQPOST, " After"] From 022fe3df9ff6402e4f5bd056e8ea53867f2b0717 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 22 Feb 2014 03:52:19 +0100 Subject: [PATCH 767/800] (maint) Fix issue when issure report was called without issues All earlier code paths checked if there were errors before calling assert and report. There was an issue with a reference to a non eisting variable when there were no errors to report. --- lib/puppet/pops/issue_reporter.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/puppet/pops/issue_reporter.rb b/lib/puppet/pops/issue_reporter.rb index 2ba3f1088..cac2efcbc 100644 --- a/lib/puppet/pops/issue_reporter.rb +++ b/lib/puppet/pops/issue_reporter.rb @@ -70,7 +70,6 @@ class Puppet::Pops::IssueReporter exception.file = errors[0].file raise exception end - parse_result end def self.format_with_prefix(prefix, message) From c9f4c588fee5e535bea05f1b6d8a4c5ce70b18e0 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 23 Feb 2014 01:30:15 +0100 Subject: [PATCH 768/800] (maint) Fix missing argument in BindingsModelDumper This fixes a typo (missing argument) in one of the dumper's methods. --- lib/puppet/pops/binder/bindings_model_dumper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/pops/binder/bindings_model_dumper.rb b/lib/puppet/pops/binder/bindings_model_dumper.rb index f47436ea9..1abfd0675 100644 --- a/lib/puppet/pops/binder/bindings_model_dumper.rb +++ b/lib/puppet/pops/binder/bindings_model_dumper.rb @@ -93,7 +93,7 @@ class Puppet::Pops::Binder::BindingsModelDumper < Puppet::Pops::Model::TreeDumpe result << expression_dumper.dump(o.expression) end - def dump_InstanceProducerDescriptor + def dump_InstanceProducerDescriptor o # TODO: o.arguments, o. transformer ['instance', o.class_name] end From e13941e23cfb5cb92bd3ecbfee2abfc25ff7e6f3 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 23 Feb 2014 01:33:04 +0100 Subject: [PATCH 769/800] (maint) Speed up External Syntax Check if there are no errors There is no need to go through the motion of configuring an error issuer and letting it do all its work when it will not report anything. --- lib/puppet/pops/evaluator/external_syntax_support.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/puppet/pops/evaluator/external_syntax_support.rb b/lib/puppet/pops/evaluator/external_syntax_support.rb index b9e502dae..9ee96a863 100644 --- a/lib/puppet/pops/evaluator/external_syntax_support.rb +++ b/lib/puppet/pops/evaluator/external_syntax_support.rb @@ -20,9 +20,11 @@ module Puppet::Pops::Evaluator::ExternalSyntaxSupport source_pos = find_closest_positioned(reference_expr) checker.check(result, syntax, acceptor, source_pos) - checker_message = "Invalid produced text having syntax: '#{syntax}'." - Puppet::Pops::IssueReporter.assert_and_report(acceptor, :message => checker_message) - raise ArgumentError, "Internal Error: Configuration of runtime error handling wrong: should have raised exception" + if acceptor.error_count > 0 + checker_message = "Invalid produced text having syntax: '#{syntax}'." + Puppet::Pops::IssueReporter.assert_and_report(acceptor, :message => checker_message) + raise ArgumentError, "Internal Error: Configuration of runtime error handling wrong: should have raised exception" + end end # Finds the most significant checker for the given syntax (most significant is to the right). From be6449620c7879520c6770edcc99a06db6bcd20b Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 23 Feb 2014 01:38:03 +0100 Subject: [PATCH 770/800] (PUP-30) Add tests for heredoc at parser and evaluator levels This also fixes issues found by running the new tests and debugging them. --- lib/puppet/pops/model/factory.rb | 4 +- lib/puppet/pops/model/model_tree_dumper.rb | 4 + lib/puppet/util/instance_loader.rb | 1 - .../puppetlabs/syntax_checkers/json.rb | 11 +-- .../pops/evaluator/evaluating_parser_spec.rb | 72 +++++++++++++++++- spec/unit/pops/parser/parse_heredoc_spec.rb | 73 +++++++++++++++++++ 6 files changed, 153 insertions(+), 12 deletions(-) create mode 100644 spec/unit/pops/parser/parse_heredoc_spec.rb diff --git a/lib/puppet/pops/model/factory.rb b/lib/puppet/pops/model/factory.rb index a47a7040f..3e22c0fe8 100644 --- a/lib/puppet/pops/model/factory.rb +++ b/lib/puppet/pops/model/factory.rb @@ -36,7 +36,7 @@ class Puppet::Pops::Model::Factory begin @@build_visitor.visit_this(self, o, *args) rescue =>e - # require 'debugger'; debugger # enable this when in trouble... + # debug here when in trouble... raise e end end @@ -46,7 +46,7 @@ class Puppet::Pops::Model::Factory begin @@interpolation_visitor.visit_this_0(self, current) rescue =>e - # require 'debugger'; debugger # enable this when in trouble... + # debug here when in trouble... raise e end end diff --git a/lib/puppet/pops/model/model_tree_dumper.rb b/lib/puppet/pops/model/model_tree_dumper.rb index adb4eb471..2e49092ce 100644 --- a/lib/puppet/pops/model/model_tree_dumper.rb +++ b/lib/puppet/pops/model/model_tree_dumper.rb @@ -363,6 +363,10 @@ class Puppet::Pops::Model::ModelTreeDumper < Puppet::Pops::Model::TreeDumper [do_dump(o.matching_expr), "=>", do_dump(o.value_expr)] end + def dump_SubLocatedExpression o + ["sublocated", do_dump(o.expr)] + end + def dump_Object o [o.class.to_s, o.to_s] end diff --git a/lib/puppet/util/instance_loader.rb b/lib/puppet/util/instance_loader.rb index fe0036b82..c6d6b71d4 100644 --- a/lib/puppet/util/instance_loader.rb +++ b/lib/puppet/util/instance_loader.rb @@ -65,7 +65,6 @@ module Puppet::Util::InstanceLoader name = name.intern return nil unless instances = instance_hash(type) unless instances.include? name -# require 'debugger'; debugger if instance_loader(type).load(name) unless instances.include? name Puppet.warning( diff --git a/lib/puppetx/puppetlabs/syntax_checkers/json.rb b/lib/puppetx/puppetlabs/syntax_checkers/json.rb index 6af6c59c6..c12e0e52e 100644 --- a/lib/puppetx/puppetlabs/syntax_checkers/json.rb +++ b/lib/puppetx/puppetlabs/syntax_checkers/json.rb @@ -7,12 +7,12 @@ class Puppetx::Puppetlabs::SyntaxCheckers::Json < Puppetx::Puppet::SyntaxChecker # This implementation is abstract, it raises {NotImplementedError} since a subclass should have implemented the # method. # + # Error messages from the checker are capped at 100 chars from the source text. + # # @param text [String] The text to check # @param syntax [String] The syntax identifier in mime style (e.g. 'json', 'json-patch+json', 'xml', 'myapp+xml' - # @option location_info [String] :file The filename where the string originates - # @option location_info [Integer] :line The line number identifying the location where the string is being used/checked - # @option location_info [Integer] :position The position on the line identifying the location where the string is being used/checked - # @return [Boolean] Whether the checked string had issues (warnings and/or errors) or not. + # @param acceptor [#accept] A Diagnostic acceptor + # @param source_pos [Puppet::Pops::Adapters::SourcePosAdapter] A source pos adapter with location information # @api public # def check(text, syntax, acceptor, source_pos) @@ -31,9 +31,6 @@ class Puppetx::Puppetlabs::SyntaxCheckers::Json < Puppetx::Puppet::SyntaxChecker # and the issue code. (In this case especially, where there is only a single error message being issued). # issue = Puppet::Pops::Issues::issue(:ILLEGAL_JSON) { msg } -# source_pos = Puppet::Pops::Adapters::SourcePosAdapter.new() -# source_pos.line = location_info[:line] -# source_pos.pos = location_info[:pos] acceptor.accept(Puppet::Pops::Validation::Diagnostic.new(:error, issue, source_pos.locator.file, source_pos, {})) end end diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb index 127427dc8..4667de4bb 100644 --- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb +++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb @@ -1,4 +1,3 @@ -#! /usr/bin/env ruby require 'spec_helper' require 'puppet/pops' @@ -22,9 +21,11 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do # Puppet[:parser] = 'future' Puppet[:evaluator] = 'future' + # Puppetx cannot be loaded until the correct parser has been set (injector is turned off otherwise) + require 'puppetx' end - let(:parser) { Puppet::Pops::Parser::EvaluatingParser::Transitional.new } + let(:parser) { Puppet::Pops::Parser::EvaluatingParser::Transitional.new } let(:node) { 'node.example.com' } let(:scope) { s = create_test_scope_for_node(node); s } types = Puppet::Pops::Types::TypeFactory @@ -894,6 +895,73 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do end end + context "When evaluating heredoc" do + it "evaluates plain heredoc" do + src = "@(END)\nThis is\nheredoc text\nEND\n" + parser.evaluate_string(scope, src).should == "This is\nheredoc text\n" + end + + it "parses heredoc with margin" do + src = [ + "@(END)", + " This is", + " heredoc text", + " | END", + "" + ].join("\n") + parser.evaluate_string(scope, src).should == "This is\nheredoc text\n" + end + + it "parses heredoc with margin and right newline trim" do + src = [ + "@(END)", + " This is", + " heredoc text", + " |- END", + "" + ].join("\n") + parser.evaluate_string(scope, src).should == "This is\nheredoc text" + end + + it "parses escape specification" do + src = <<-CODE + @(END/t) + Tex\\tt\\n + |- END + CODE + parser.evaluate_string(scope, src).should == "Tex\tt\\n" + end + + it "parses syntax checked specification" do + src = <<-CODE + @(END:json) + ["foo", "bar"] + |- END + CODE + parser.evaluate_string(scope, src).should == '["foo", "bar"]' + end + + it "parses syntax checked specification with error and reports it" do + src = <<-CODE + @(END:json) + ['foo', "bar"] + |- END + CODE + expect { parser.evaluate_string(scope, src)}.to raise_error(/Cannot parse invalid JSON string/) + end + + it "parses interpolated heredoc epression" do + src = <<-CODE + $name = 'Fjodor' + @("END") + Hello $name + |- END + CODE + parser.evaluate_string(scope, src).should == "Hello Fjodor" + end + + end + context "Detailed Error messages are reported" do it 'for illegal type references' do source = '1+1 { "title": }' diff --git a/spec/unit/pops/parser/parse_heredoc_spec.rb b/spec/unit/pops/parser/parse_heredoc_spec.rb new file mode 100644 index 000000000..3616b9b2e --- /dev/null +++ b/spec/unit/pops/parser/parse_heredoc_spec.rb @@ -0,0 +1,73 @@ +require 'spec_helper' +require 'puppet/pops' + +# relative to this spec file (./) does not work as this file is loaded by rspec +require File.join(File.dirname(__FILE__), '/parser_rspec_helper') + +describe "egrammar parsing heredoc" do + include ParserRspecHelper + + it "parses plain heredoc" do + dump(parse("@(END)\nThis is\nheredoc text\nEND\n")).should == [ + "(@()", + " (sublocated 'This is\nheredoc text\n')", + ")" + ].join("\n") + end + + it "parses heredoc with margin" do + src = [ + "@(END)", + " This is", + " heredoc text", + " | END", + "" + ].join("\n") + dump(parse(src)).should == [ + "(@()", + " (sublocated 'This is\nheredoc text\n')", + ")" + ].join("\n") + end + + it "parses heredoc with margin and right newline trim" do + src = [ + "@(END)", + " This is", + " heredoc text", + " |- END", + "" + ].join("\n") + dump(parse(src)).should == [ + "(@()", + " (sublocated 'This is\nheredoc text')", + ")" + ].join("\n") + end + + it "parses syntax and escape specification" do + src = <<-CODE + @(END:syntax/t) + Tex\\tt\\n + |- END + CODE + dump(parse(src)).should == [ + "(@(syntax)", + " (sublocated 'Tex\tt\\n')", + ")" + ].join("\n") + end + + it "parses interpolated heredoc epression" do + src = <<-CODE + @("END") + Hello $name + |- END + CODE + dump(parse(src)).should == [ + "(@()", + " (sublocated (cat 'Hello ' (str $name) ''))", + ")" + ].join("\n") + end +end From 6f97e4509a1fe114e9a1312c2eb274efa6c832b3 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 23 Feb 2014 05:37:37 +0100 Subject: [PATCH 771/800] (maint) Correct spelling in comments --- lib/puppet/pops/parser/epp_support.rb | 6 +++--- lib/puppet/pops/parser/lexer2.rb | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/puppet/pops/parser/epp_support.rb b/lib/puppet/pops/parser/epp_support.rb index aaf6f281b..8209963f8 100644 --- a/lib/puppet/pops/parser/epp_support.rb +++ b/lib/puppet/pops/parser/epp_support.rb @@ -18,10 +18,10 @@ module Puppet::Pops::Parser::EppSupport # A block must be passed to scan. It will be called with two arguments, a symbol for the token, # and an instance of LexerSupport::TokenValue - # PERFORMANCE NOTE: The TokenValue is designed to reduce the amount of garbage / termporary data - # and to only convert the lexer's internal tokens on demand. It is slightly mroe costly to create an + # PERFORMANCE NOTE: The TokenValue is designed to reduce the amount of garbage / temporary data + # and to only convert the lexer's internal tokens on demand. It is slightly more costly to create an # instance of a class defined in Ruby than an Array or Hash, but the gain is much bigger since transformation - # logic is avoided for many of its memebers (most are never used (e.g. line/pos information which is only of + # logic is avoided for many of its members (most are never used (e.g. line/pos information which is only of # value in general for error messages, and for some expressions (which the lexer does not know about). # def scan_epp diff --git a/lib/puppet/pops/parser/lexer2.rb b/lib/puppet/pops/parser/lexer2.rb index 55820caf0..7657cb7f0 100644 --- a/lib/puppet/pops/parser/lexer2.rb +++ b/lib/puppet/pops/parser/lexer2.rb @@ -251,10 +251,10 @@ class Puppet::Pops::Parser::Lexer2 # A block must be passed to scan. It will be called with two arguments, a symbol for the token, # and an instance of LexerSupport::TokenValue - # PERFORMANCE NOTE: The TokenValue is designed to reduce the amount of garbage / termporary data - # and to only convert the lexer's internal tokens on demand. It is slightly mroe costly to create an + # PERFORMANCE NOTE: The TokenValue is designed to reduce the amount of garbage / temporary data + # and to only convert the lexer's internal tokens on demand. It is slightly more costly to create an # instance of a class defined in Ruby than an Array or Hash, but the gain is much bigger since transformation - # logic is avoided for many of its memebers (most are never used (e.g. line/pos information which is only of + # logic is avoided for many of its members (most are never used (e.g. line/pos information which is only of # value in general for error messages, and for some expressions (which the lexer does not know about). # def scan From 787d3f33bb1529eb62ffae24dc2f660f9824e72c Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 23 Feb 2014 17:31:03 +0100 Subject: [PATCH 772/800] (PUP-30) Make epp parser emit EPP_START, and generate Program Parsing EPP now results in a Program model (if producing only an EppExpression there are no source code references). The EPP_START tokten was not emitted (and its precence was not tested for). --- lib/puppet/pops.rb | 1 + lib/puppet/pops/evaluator/evaluator_impl.rb | 10 ++++++++++ lib/puppet/pops/model/factory.rb | 7 +++++++ lib/puppet/pops/model/model.rb | 2 ++ lib/puppet/pops/parser/egrammar.ra | 2 +- lib/puppet/pops/parser/eparser.rb | 2 +- lib/puppet/pops/parser/epp_parser.rb | 2 +- lib/puppet/pops/parser/epp_support.rb | 3 +++ lib/puppet/pops/parser/lexer2.rb | 8 ++++---- spec/unit/pops/parser/lexer2_spec.rb | 7 ++++++- 10 files changed, 36 insertions(+), 8 deletions(-) diff --git a/lib/puppet/pops.rb b/lib/puppet/pops.rb index c075be013..63ccf78ef 100644 --- a/lib/puppet/pops.rb +++ b/lib/puppet/pops.rb @@ -77,6 +77,7 @@ module Puppet require 'puppet/pops/parser/lexer' require 'puppet/pops/parser/lexer2' require 'puppet/pops/parser/evaluating_parser' + require 'puppet/pops/parser/epp_parser' end module Validation diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index 995b22dca..f5956de3f 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -394,6 +394,16 @@ class Puppet::Pops::Evaluator::EvaluatorImpl end end + def eval_RenderStringExpression(o, scope) + scope["@epp"] << o.value.dup + nil + end + + def eval_RenderExpression(o, scope) + scope["@epp"] << string(evaluate(o.expr, scope), scope) + nil + end + # Evaluates Puppet DSL ->, ~>, <-, and <~ def eval_RelationshipExpression(o, scope) # First level evaluation, reduction to basic data types or puppet types, the relationship operator then translates this diff --git a/lib/puppet/pops/model/factory.rb b/lib/puppet/pops/model/factory.rb index 3e22c0fe8..075332bad 100644 --- a/lib/puppet/pops/model/factory.rb +++ b/lib/puppet/pops/model/factory.rb @@ -770,6 +770,13 @@ class Puppet::Pops::Model::Factory x end + def build_EppExpression(o, parameters, body) + parameters.each {|p| o.addParameters(build(p)) } + b = f_build_body(body) + o.body = b.current if b + o + end + # If building a factory, simply unwrap the model oject contained in the factory. def build_Factory(o) o.current diff --git a/lib/puppet/pops/model/model.rb b/lib/puppet/pops/model/model.rb index 48269a212..7b65812f1 100644 --- a/lib/puppet/pops/model/model.rb +++ b/lib/puppet/pops/model/model.rb @@ -498,6 +498,8 @@ module Puppet::Pops::Model # Epp start class EppExpression < Definition + contains_many_uni 'parameters', Parameter + contains_one_uni 'body', Expression end # A string to render diff --git a/lib/puppet/pops/parser/egrammar.ra b/lib/puppet/pops/parser/egrammar.ra index 8f2a82e16..b9ee391ef 100644 --- a/lib/puppet/pops/parser/egrammar.ra +++ b/lib/puppet/pops/parser/egrammar.ra @@ -63,7 +63,7 @@ rule # Produces [Model::BlockExpression, Model::Expression, nil] depending on multiple statements, single statement or empty program : statements { result = create_program(Factory.block_or_expression(*val[0])) } - | epp_expression { result = Factory.block_or_expression(*val[0]) } + | epp_expression { result = create_program(Factory.block_or_expression(*val[0])) } | nil # Produces a semantic model (non validated, but semantically adjusted). diff --git a/lib/puppet/pops/parser/eparser.rb b/lib/puppet/pops/parser/eparser.rb index 47e5bd0a7..452c3eda6 100644 --- a/lib/puppet/pops/parser/eparser.rb +++ b/lib/puppet/pops/parser/eparser.rb @@ -1157,7 +1157,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 64) module_eval(<<'.,.,', 'egrammar.ra', 65) def _reduce_2(val, _values, result) - result = Factory.block_or_expression(*val[0]) + result = create_program(Factory.block_or_expression(*val[0])) result end .,., diff --git a/lib/puppet/pops/parser/epp_parser.rb b/lib/puppet/pops/parser/epp_parser.rb index 45e1b64bd..ea5415a56 100644 --- a/lib/puppet/pops/parser/epp_parser.rb +++ b/lib/puppet/pops/parser/epp_parser.rb @@ -6,7 +6,7 @@ class Puppet::Pops::Parser::EppParser < Puppet::Pops::Parser::Parser # @return [void] # def initvars - self.lexer = Puppet::Pops::Parser::Lexer.new({:mode => :epp}) + self.lexer = Puppet::Pops::Parser::Lexer2.new()# {:mode => :epp}) end # Parses a file expected to contain epp text/DSL logic. diff --git a/lib/puppet/pops/parser/epp_support.rb b/lib/puppet/pops/parser/epp_support.rb index 8209963f8..013297d60 100644 --- a/lib/puppet/pops/parser/epp_support.rb +++ b/lib/puppet/pops/parser/epp_support.rb @@ -36,6 +36,8 @@ module Puppet::Pops::Parser::EppSupport lex_error "Internal Error: No string or file given to lexer to process." unless scn ctx[:epp_mode] = :text + enqueue_completed([:EPP_START, nil, 0], 0) + interpolate_epp # This is the lexer's main loop @@ -57,6 +59,7 @@ module Puppet::Pops::Parser::EppSupport ctx = @lexing_context eppscanner = EppScanner.new(scn) before = scn.pos + s = eppscanner.scan(skip_leading) case eppscanner.mode diff --git a/lib/puppet/pops/parser/lexer2.rb b/lib/puppet/pops/parser/lexer2.rb index 7657cb7f0..8f24e2b33 100644 --- a/lib/puppet/pops/parser/lexer2.rb +++ b/lib/puppet/pops/parser/lexer2.rb @@ -94,11 +94,11 @@ class Puppet::Pops::Parser::Lexer2 TOKEN_VARIABLE = [:VARIABLE, nil, 1].freeze TOKEN_VARIABLE_EMPTY = [:VARIABLE, ''.freeze, 1].freeze - # Tokens that start HEREDOC and EPP, both have syntax as an argument. - # These tokens are always unique to what has been lexed. - # + # HEREDOC has syntax as an argument. TOKEN_HEREDOC = [:HEREDOC, nil, 0].freeze - TOKEN_EPPSTART = [:EPPSTART, nil, 0].freeze + + # EPP_START is currently a marker token, may later get syntax + TOKEN_EPPSTART = [:EPP_START, nil, 0].freeze # This is used for unrecognized tokens, will always be a single character. This particular instance # is not used, but is kept here for documentation purposes. diff --git a/spec/unit/pops/parser/lexer2_spec.rb b/spec/unit/pops/parser/lexer2_spec.rb index e07d3722c..ae6099223 100644 --- a/spec/unit/pops/parser/lexer2_spec.rb +++ b/spec/unit/pops/parser/lexer2_spec.rb @@ -333,7 +333,7 @@ describe 'Lexer2' do code = <<-CODE This is just text CODE - epp_tokens_scanned_from(code).should match_tokens2([:RENDER_STRING, " This is just text\n"]) + epp_tokens_scanned_from(code).should match_tokens2(:EPP_START, [:RENDER_STRING, " This is just text\n"]) end it 'epp can contain text with interpolated rendered expressions' do @@ -341,6 +341,7 @@ describe 'Lexer2' do This is <%= $x %> just text CODE epp_tokens_scanned_from(code).should match_tokens2( + :EPP_START, [:RENDER_STRING, " This is "], [:RENDER_EXPR, nil], [:VARIABLE, "x"], @@ -353,6 +354,7 @@ describe 'Lexer2' do This is <% $x=10 %> just text CODE epp_tokens_scanned_from(code).should match_tokens2( + :EPP_START, [:RENDER_STRING, " This is "], [:VARIABLE, "x"], :EQUALS, @@ -367,6 +369,7 @@ describe 'Lexer2' do just text CODE epp_tokens_scanned_from(code).should match_tokens2( + :EPP_START, [:RENDER_STRING, " This is "], [:VARIABLE, "x"], :EQUALS, @@ -382,6 +385,7 @@ describe 'Lexer2' do just text CODE epp_tokens_scanned_from(code).should match_tokens2( + :EPP_START, [:RENDER_STRING, " This is "], [:VARIABLE, "x"], :EQUALS, @@ -396,6 +400,7 @@ describe 'Lexer2' do <%% this is escaped epp %%> CODE epp_tokens_scanned_from(code).should match_tokens2( + :EPP_START, [:RENDER_STRING, " This is "], [:VARIABLE, "x"], :EQUALS, From e692abb4ad8121136ca5941585d1bca41d423b8c Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sun, 23 Feb 2014 17:45:10 +0100 Subject: [PATCH 773/800] (PUP-30) Add tests for epp parser and make them pass This adds tests for parsing epp. A typo was corrected when checking for unbalanced <% %> at the end of the epp input. This caused error not to be raised. (Tests should have been included in an earlier commit). --- lib/puppet/pops/parser/epp_support.rb | 2 +- spec/unit/pops/parser/epp_parser_spec.rb | 77 ++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 spec/unit/pops/parser/epp_parser_spec.rb diff --git a/lib/puppet/pops/parser/epp_support.rb b/lib/puppet/pops/parser/epp_support.rb index 013297d60..9329ec0b8 100644 --- a/lib/puppet/pops/parser/epp_support.rb +++ b/lib/puppet/pops/parser/epp_support.rb @@ -46,7 +46,7 @@ module Puppet::Pops::Parser::EppSupport yield [ ctx[:after] = token[0], token[1] ] end end - if ctx[:epp_position] + if ctx[:epp_open_position] lex_error("Unbalanced epp tag, reached without closing tag.", ctx[:epp_position]) end diff --git a/spec/unit/pops/parser/epp_parser_spec.rb b/spec/unit/pops/parser/epp_parser_spec.rb new file mode 100644 index 000000000..a91148ed1 --- /dev/null +++ b/spec/unit/pops/parser/epp_parser_spec.rb @@ -0,0 +1,77 @@ +require 'spec_helper' +require 'puppet/pops' + +require File.join(File.dirname(__FILE__), '/../factory_rspec_helper') + +module EppParserRspecHelper + include FactoryRspecHelper + def parse(code) + parser = Puppet::Pops::Parser::EppParser.new() + parser.parse_string(code) + end +end + +describe "epp parser" do + include EppParserRspecHelper + + it "should instantiate an epp parser" do + parser = Puppet::Pops::Parser::EppParser.new() + parser.class.should == Puppet::Pops::Parser::EppParser + end + + it "should parse a code string and return a program with epp" do + parser = Puppet::Pops::Parser::EppParser.new() + model = parser.parse_string("Nothing to see here, move along...").current + model.class.should == Puppet::Pops::Model::Program + model.body.class.should == Puppet::Pops::Model::EppExpression + end + + context "when facing bad input it reports" do + it "unbalanced tags" do + expect { dump(parse("<% missing end tag")) }.to raise_error(/Unbalanced/) + end + + it "abrupt end" do + expect { dump(parse("dum di dum di dum <%")) }.to raise_error(/Unbalanced/) + end + + it "nested epp tags" do + expect { dump(parse("<% $a = 10 <% $b = 20 %>%>")) }.to raise_error(/Syntax error/) + end + + it "nested epp expression tags" do + expect { dump(parse("<%= 1+1 <%= 2+2 %>%>")) }.to raise_error(/Syntax error/) + end + end + + context "handles parsing of" do + it "text (and nothing else)" do + dump(parse("Hello World")).should == "(epp (block (render-s 'Hello World')))" + end + + it "template parameters" do + dump(parse("<%($x)%>Hello World")).should == "(epp (parameters x) (block (render-s 'Hello World')))" + end + + it "template parameters with default" do + dump(parse("<%($x='cigar')%>Hello World")).should == "(epp (parameters (= x 'cigar')) (block (render-s 'Hello World')))" + end + + it "template parameters with and without default" do + dump(parse("<%($x='cigar', $y)%>Hello World")).should == "(epp (parameters (= x 'cigar') y) (block (render-s 'Hello World')))" + end + + it "comments" do + dump(parse("<%#($x='cigar', $y)%>Hello World")).should == "(epp (block (render-s 'Hello World')))" + end + + it "verbatim epp tags" do + dump(parse("<%% contemplating %%>Hello World")).should == "(epp (block (render-s '<% contemplating %>Hello World')))" + end + + it "expressions" do + dump(parse("We all live in <%= 3.14 - 2.14 %> world")).should == + "(epp (block (render-s 'We all live in ') (render (- 3.14 2.14)) (render-s ' world')))" + end + end +end From 2437c172d781bc2cf0f2d22cccfa84e1ee310296 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Tue, 25 Feb 2014 18:44:32 +0100 Subject: [PATCH 774/800] (PUP-30) Add call-by-value of closure in evaluator This is needed for calling epp templates, but is also a useful evaluator extension in general as it allows calling any lambda with matching of parameter names (instead of call by args position). --- lib/puppet/pops/evaluator/evaluator_impl.rb | 56 +++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index f5956de3f..d2e1eee74 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -119,6 +119,62 @@ class Puppet::Pops::Evaluator::EvaluatorImpl @@string_visitor.visit_this_1(self, o, scope) end + # Call a closure matching arguments by name - Can only be called with a Closure (for now), may be refactored later + # to also handle other types of calls (function calls are also handled by CallNamedFunction and CallMethod, they + # could create similar objects to Closure, wait until other types of defines are instantiated - they may behave + # as special cases of calls - i.e. 'new'). + # + # Call by name supports a "spill_over" mode where extra arguments in the given args_hash are introduced + # as variables in the resulting scope. + # + # @raise ArgumentError, if there are to many or too few arguments + # @raise ArgumentError, if given closure is not a Puppet::Pops::Evaluator::Closure + # + def call_by_name(closure, args_hash, scope, spill_over = false) + raise ArgumentError, "Can only call a Lambda" unless closure.is_a?(Puppet::Pops::Evaluator::Closure) + pblock = closure.model + parameters = pblock.parameters || [] + + if !spill_over && args.size > parameters.size + raise ArgumentError, "Too many arguments: #{args.size} for #{parameters.size}" + end + + # associate values with parameters + scope_hash = {} + parameters.each do |p| + scope_hash[p.name] = args_hash[p.name] || evaluate(p.value, scope) + end + missing = scope_hash.reduce([]) {|memo, entry| memo << entry[0] if entry[1].nil?; memo } + unless missing.empty? + optional = parameters.count { |p| !p.value.nil? } + raise ArgumentError, "Too few arguments; no value given for required parameters #{missing.join(" ,")}" + end + + if spill_over + # all args from given hash should be used, nil entries replaced by default values should win + scope_hash = args_hash.merge(scope_hash) + end + + # Store the evaluated name => value associations in a new inner/local/ephemeral scope + # (This is made complicated due to the fact that the implementation of scope is overloaded with + # functionality and an inner ephemeral scope must be used (as opposed to just pushing a local scope + # on a scope "stack"). + + # Ensure variable exists with nil value if error occurs. + # Some ruby implementations does not like creating variable on return + result = nil + begin + scope_memo = get_scope_nesting_level(scope) + # change to create local scope_from - cannot give it file and line - that is the place of the call, not + # "here" + create_local_scope_from(scope_hash, scope) + result = evaluate(pblock.body, scope) + ensure + set_scope_nesting_level(scope, scope_memo) + end + result + end + # Call a closure - Can only be called with a Closure (for now), may be refactored later # to also handle other types of calls (function calls are also handled by CallNamedFunction and CallMethod, they # could create similar objects to Closure, wait until other types of defines are instantiated - they may behave From b72749e7b6414f4d7f906feeae3c7d997445ffc1 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Wed, 5 Mar 2014 15:10:29 -0800 Subject: [PATCH 775/800] (PUP-30) Rename epptemplate and inline_epptemplate to use 'epp' This renames the two epp functions - epptemplate and inline_epptemplate to use the shorter versions epp, and inline_epp. --- lib/puppet/parser/functions/{epptemplate.rb => epp.rb} | 0 .../parser/functions/{inline_epptempplate.rb => inline_epp.rb} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename lib/puppet/parser/functions/{epptemplate.rb => epp.rb} (100%) rename lib/puppet/parser/functions/{inline_epptempplate.rb => inline_epp.rb} (100%) diff --git a/lib/puppet/parser/functions/epptemplate.rb b/lib/puppet/parser/functions/epp.rb similarity index 100% rename from lib/puppet/parser/functions/epptemplate.rb rename to lib/puppet/parser/functions/epp.rb diff --git a/lib/puppet/parser/functions/inline_epptempplate.rb b/lib/puppet/parser/functions/inline_epp.rb similarity index 100% rename from lib/puppet/parser/functions/inline_epptempplate.rb rename to lib/puppet/parser/functions/inline_epp.rb From 95aa2d60bbdfb45c7100c2b8e8d7d1bf41b6523c Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 6 Mar 2014 16:11:39 -0800 Subject: [PATCH 776/800] (PUP-30) Complete implementation of functions epp, and inline_epp This completes the impelmentation of the functions epp, and inline_epp that provides the "puppet templates" functionality. --- lib/puppet/parser/functions/epp.rb | 62 ++++++------- lib/puppet/parser/functions/inline_epp.rb | 97 +++++++++++++-------- lib/puppet/parser/scope.rb | 10 +++ lib/puppet/pops.rb | 1 + lib/puppet/pops/evaluator/closure.rb | 5 ++ lib/puppet/pops/evaluator/epp_evaluator.rb | 88 +++++++++++++++++++ lib/puppet/pops/evaluator/evaluator_impl.rb | 20 ++++- lib/puppet/pops/issues.rb | 4 + lib/puppet/pops/model/factory.rb | 14 ++- lib/puppet/pops/model/model.rb | 4 +- lib/puppet/pops/model/model_tree_dumper.rb | 2 +- lib/puppet/pops/parser/egrammar.ra | 4 +- lib/puppet/pops/parser/eparser.rb | 40 ++++----- lib/puppet/pops/parser/evaluating_parser.rb | 16 +++- lib/puppet/pops/validation/checker4_0.rb | 11 ++- 15 files changed, 265 insertions(+), 113 deletions(-) create mode 100644 lib/puppet/pops/evaluator/epp_evaluator.rb diff --git a/lib/puppet/parser/functions/epp.rb b/lib/puppet/parser/functions/epp.rb index ee6abf05d..da6d5f3ff 100644 --- a/lib/puppet/parser/functions/epp.rb +++ b/lib/puppet/parser/functions/epp.rb @@ -1,53 +1,41 @@ -Puppet::Parser::Functions::newfunction(:epptemplate, :type => :rvalue, :arity => -2, :doc => -"Evaluates one or more Embedded Puppet Template (EPP) files and returns their concatenated result. +Puppet::Parser::Functions::newfunction(:epp, :type => :rvalue, :arity => -2, :doc => +"Evaluates an Embedded Puppet Template (EPP) file and returns the rendered text result as a String. EPP support the following tags: + * `<%= puppet expression %>` - This tag renders the value of the expression it contains. * `<% puppet expression(s) %>` - This tag will execute the expression(s) it contains, but renders nothing. * `<%# comment %>` - The tag and its content renders nothing. * `<%%` or `%%>` - Renders a literal `<%` or `%>` respectively. * `<%-` - Same as `<%` but suppresses any leading whitespace. * `-%>` - Same as `%>` but suppresses any trailing whitespace on the same line (including line break). +* `<%-( parameters )-%>` - When placed as the first tag declares the template's parameters. -EPP supports parameters by placing an optional parameter list as the very first element in the Epp. As an example, +File based EPP supports the following visibilities of variables in scope: + +* Global scope (i.e. top + node scopes) - global scope is always visible +* Global + all given arguments - if the EPP template does not declare parameters, and arguments are given +* Global + declared parameters - if the EPP declares parameters, given argument names must match + +EPP supports parameters by placing an optional parameter list as the very first element in the EPP. As an example, `<%- ($x, $y, $z='unicorn') -%>` when placed first in the EPP text declares that the parameters `x` and `y` must be -given as template arguments when calling `epptemplate`, and that `z` if not given as a template argument +given as template arguments when calling `inline_epp`, and that `z` if not given as a template argument defaults to `'unicorn'`. Template parameters are available as variables, e.g.arguments `$x`, `$y` and `$z` in the example. +Note that `<%-` must be used or any leading whitespace will be interpreted as text -Arguments are passed to the template by calling `epptemplate` with a Hash as the last argument, where parameters -are bound to values, e.g. `epptemplate('templatefile.epp', {'x'=>10, 'y'=>20})`. Excess arguments may be given -(i.e. undeclared parameters). Template parameters shadow variables in outer scopes. Template arguments may be -passed to any template; the template text does not have to have a declaration of parameters. +Arguments are passed to the template by calling `epp` with a Hash as the last argument, where parameters +are bound to values, e.g. `epp('...', {'x'=>10, 'y'=>20})`. Excess arguments may be given +(i.e. undeclared parameters) only if the EPP templates does not declare any parameters at all. +Template parameters shadow variables in outer scopes. File based epp does never have access to variables in the +scope where the `epp` function is called from. -Several files may be given as arguments to `epptemplate`, the result is the concatenation of each produced result. -If template arguments are given, they are used for each given file. -") do |arguments| - # accepts one or more arguments (each being a file), except an optional last argument being a hash - # of parameters to pass to each evaluation. - - if(arguments[-1].is_a? Hash) - template_args = arguments[-1] - arguments = arguments[0..-2] - else - template_args = {} +- See function inline_epp for examples of EPP +- Since 3.5 +- Requires Future Parser") do |arguments| + # Requires future parser + unless Puppet[:parser] == "future" + raise ArgumentError, "epp(): function is only available when --parser future is in effect" end - require 'puppet/parser/parser_factory' - require 'puppet/parser/ast' + Puppet::Pops::Evaluator::EppEvaluator.epp(self, arguments[0], self.compiler.environment.to_s, arguments[1]) - arguments.collect do |file| - if file.is_a?(Hash) - raise IllegalArgumentException, "A Hash may be given as the last argument only" - end - debug "Retrieving epp template #{file}" - template_file = Puppet::Parser::Files.find_template(file, self.compiler.environment.to_s) - unless template_file - raise Puppet::ParseError, "Could not find template '#{filename}'" - end - - parser = Puppet::Parser::ParserFactory.epp_parser(self.compiler.environment) - parser.file = template_file - result = parser.parse() - raise Puppet::ParseError, "Parsing #{template_file} did not produce an instance of Epp. Got: #{result.class}" unless result.is_a?(Puppet::Parser::AST::Epp) - result.call(self, template_args) - end.join("") end diff --git a/lib/puppet/parser/functions/inline_epp.rb b/lib/puppet/parser/functions/inline_epp.rb index ca0076712..cfc1597a1 100644 --- a/lib/puppet/parser/functions/inline_epp.rb +++ b/lib/puppet/parser/functions/inline_epp.rb @@ -1,58 +1,79 @@ -Puppet::Parser::Functions::newfunction(:inline_epptemplate, :type => :rvalue, :arity => -2, :doc => -"Evaluates one or more Embedded Puppet Template (EPP) strings and returns their concatenated result. +Puppet::Parser::Functions::newfunction(:inline_epp, :type => :rvalue, :arity => -2, :doc => +"Evaluates an Embedded Puppet Template (EPP) string and returns the rendered text result as a String. EPP support the following tags: + * `<%= puppet expression %>` - This tag renders the value of the expression it contains. * `<% puppet expression(s) %>` - This tag will execute the expression(s) it contains, but renders nothing. * `<%# comment %>` - The tag and its content renders nothing. * `<%%` or `%%>` - Renders a literal `<%` or `%>` respectively. * `<%-` - Same as `<%` but suppresses any leading whitespace. * `-%>` - Same as `%>` but suppresses any trailing whitespace on the same line (including line break). +* `<%-( parameters )-%>` - When placed as the first tag declares the template's parameters. -EPP supports parameters by placing an optional parameter list as the very first element in the Epp. As an example, -`<%- ($x, $y, $z='unicorn') -%>` when placed first in the EPP text declares that the parameters `x` and `y` must be -given as template arguments when calling `inline_epptemplate`, and that `z` if not given as a template argument +Inline EPP supports the following visibilities of variables in scope which depends on how EPP parameters +are used - see further below: + +* Global scope (i.e. top + node scopes) - global scope is always visible +* Global + Enclosing scope - if the EPP template does not declare parameters, and no arguments are given +* Global + all given arguments - if the EPP template does not declare parameters, and arguments are given +* Global + declared parameters - if the EPP declares parameters, given argument names must match + +EPP supports parameters by placing an optional parameter list as the very first element in the EPP. As an example, +`<%-( $x, $y, $z='unicorn' )-%>` when placed first in the EPP text declares that the parameters `x` and `y` must be +given as template arguments when calling `inline_epp`, and that `z` if not given as a template argument defaults to `'unicorn'`. Template parameters are available as variables, e.g.arguments `$x`, `$y` and `$z` in the example. +Note that `<%-` must be used or any leading whitespace will be interpreted as text -Arguments are passed to the template by calling `inline_epptemplate` with a Hash as the last argument, where parameters -are bound to values, e.g. `inline_epptemplate('...', {'x'=>10, 'y'=>20})`. Excess arguments may be given -(i.e. undeclared parameters). Template parameters shadow variables in outer scopes. Template arguments may be -passed to any template; the template text does not have to have a declaration of parameters. - -Several strings may be given as arguments to `inline_epptemplate`, the result is the concatenation of each produced result. -If template arguments are given, they are used for each given template string. +Arguments are passed to the template by calling `inline_epp` with a Hash as the last argument, where parameters +are bound to values, e.g. `inline_epp('...', {'x'=>10, 'y'=>20})`. Excess arguments may be given +(i.e. undeclared parameters) only if the EPP templates does not declare any parameters at all. +Template parameters shadow variables in outer scopes. Note: An inline template is best stated using a single-quoted string, or a heredoc since a double-quoted string -is subject to expression interpolation before the string is parsed as an EPP template. Here is an example -using heredoc. +is subject to expression interpolation before the string is parsed as an EPP template. Here are examples +(using heredoc to define the EPP text): - inline_epptemplate(@(END:epp), {'x'=>'epp template world'}) - <%- ($x) -%> + # produces 'Hello local variable world!' + $x ='local variable' + inline_epptemplate(@(END:epp)) + <%-( $x )-%> + Hello <%= $x %> world! + END + + # produces 'Hello given argument world!' + $x ='local variable world' + inline_epptemplate(@(END:epp), { x =>'given argument'}) + <%-( $x )-%> + Hello <%= $x %> world! + END + + # produces 'Hello given argument world!' + $x ='local variable world' + inline_epptemplate(@(END:epp), { x =>'given argument'}) + <%-( $x )-%> Hello <%= $x %>! END -") do |arguments| - # accepts one or more arguments (each being a epp source string), except an optional last argument being a hash - # of parameters to pass to each evaluation. + # results in error, missing value for y + $x ='local variable world' + inline_epptemplate(@(END:epp), { x =>'given argument'}) + <%-( $x, $y )-%> + Hello <%= $x %>! + END - if(arguments[-1].is_a? Hash) - template_args = arguments[-1] - arguments = arguments[0..-2] - else - template_args = {} + # Produces 'Hello given argument planet' + $x ='local variable world' + inline_epptemplate(@(END:epp), { x =>'given argument'}) + <%-( $x, $y=planet)-%> + Hello <%= $x %> <%= $y %>! + END + +- Since 3.5 +- Requires Future Parser") do |arguments| + # Requires future parser + unless Puppet[:parser] == "future" + raise ArgumentError, "inline_epp(): function is only available when --parser future is in effect" end - require 'puppet/parser/parser_factory' - require 'puppet/parser/ast' - - arguments.collect do |text| - if text.is_a?(Hash) - raise IllegalArgumentException, "A Hash may be given as the last argument only" - end - - parser = Puppet::Parser::ParserFactory.epp_parser(self.compiler.environment) - parser.string = text - result = parser.parse() - raise Puppet::ParseError, "Parsing epp string did not produce an instance of Epp. Got: #{result.class}" unless result.is_a?(Puppet::Parser::AST::Epp) - result.call(self, template_args) - end.join("") + Puppet::Pops::Evaluator::EppEvaluator.inline_epp(self, arguments[0], arguments[1]) end diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb index e043bff5a..f9a8efef4 100644 --- a/lib/puppet/parser/scope.rb +++ b/lib/puppet/parser/scope.rb @@ -239,6 +239,16 @@ class Puppet::Parser::Scope known_resource_types.find_definition(namespaces, name) end + def find_global_scope() + # walk upwards until first found node_scope or top_scope + if is_nodescope? || is_topscope? + self + else + next_scope = inherited_scope || enclosing_scope + next_scope.find_global_scope() + end + end + # This just delegates directly. def_delegator :compiler, :findresource diff --git a/lib/puppet/pops.rb b/lib/puppet/pops.rb index 63ccf78ef..7aa812b59 100644 --- a/lib/puppet/pops.rb +++ b/lib/puppet/pops.rb @@ -90,6 +90,7 @@ module Puppet module Evaluator require 'puppet/pops/evaluator/runtime3_support' require 'puppet/pops/evaluator/evaluator_impl' + require 'puppet/pops/evaluator/epp_evaluator' end end diff --git a/lib/puppet/pops/evaluator/closure.rb b/lib/puppet/pops/evaluator/closure.rb index be582e546..547e15181 100644 --- a/lib/puppet/pops/evaluator/closure.rb +++ b/lib/puppet/pops/evaluator/closure.rb @@ -27,6 +27,11 @@ class Puppet::Pops::Evaluator::Closure @evaluator.call(self, args, @enclosing_scope) end + # Call closure with argument assignment by name + def call_by_name(scope, args_hash, spill_over = false) + @evaluator.call_by_name(self, args_hash, @enclosing_scope, spill_over) + end + # incompatible with 3x except that it is an array of the same size def parameters() @model.parameters || [] diff --git a/lib/puppet/pops/evaluator/epp_evaluator.rb b/lib/puppet/pops/evaluator/epp_evaluator.rb new file mode 100644 index 000000000..f8b341868 --- /dev/null +++ b/lib/puppet/pops/evaluator/epp_evaluator.rb @@ -0,0 +1,88 @@ +# Handler of Epp call/evaluation from the epp and inline_epp functions +# +class Puppet::Pops::Evaluator::EppEvaluator + + def self.inline_epp(scope, epp_source, template_args = nil) + unless epp_source.is_a? String + raise ArgumentError, "inline_epp(): the first argument must be a String with the epp source text, got a #{file.class}" + end + + # Parse and validate the source + parser = Puppet::Pops::Parser::EvaluatingParser::EvaluatingEppParser.new + begin + result = parser.parse_string(epp_source, 'inlined-epp-text') + rescue Puppet::ParseError => e + raise ArgumentError, "inline_epp(): Invalid EPP: #{e.message}" + end + + # Evaluate (and check template_args) + evaluate(parser, 'inline_epp', scope, false, result, template_args) + end + + def self.epp(scope, file, env_name, template_args = nil) + unless file.is_a? String + raise ArgumentError, "epp(): the first argument must be a String with the filename, got a #{file.class}" + end + + file = file + ".epp" unless file =~ /\.epp$/ + scope.debug "Retrieving epp template #{file}" + require 'debugger'; debugger + template_file = Puppet::Parser::Files.find_template(file, env_name) + unless template_file + raise Puppet::ParseError, "Could not find template '#{file}'" + end + + # Parse and validate the source + parser = Puppet::Pops::Parser::EvaluatingParser::EvaluatingEppParser.new + begin + result = parser.parse_file(template_file) + rescue Puppet::ParseError => e + raise ArgumentError, "epp(): Invalid EPP: #{e.message}" + end + + # Evaluate (and check template_args) + evaluate(parser, 'epp', scope, true, result, template_args) + end + + private + + def self.evaluate(parser, func_name, scope, use_global_scope_only, parse_result, template_args) + template_args, template_args_set = handle_template_args(func_name, template_args) + + body = parse_result.body + unless body.is_a?(Puppet::Pops::Model::LambdaExpression) + raise ArgumentError, "#{func_name}(): the parser did not produce a LambdaExpression, got '#{body.class}'" + end + unless body.body.is_a?(Puppet::Pops::Model::EppExpression) + raise ArgumentError, "#{func_name}(): the parser did not produce an EppExpression, got '#{body.body.class}'" + end + unless parse_result.definitions.empty? + raise ArgumentError, "#{func_name}(): The EPP template contains illegal expressions (definitions)" + end + + see_scope = body.body.see_scope + if see_scope && !template_args_set + # no epp params and no arguments were given => inline_epp logic sees all local variables, epp all global + closure_scope = use_global_scope_only ? scope.find_global_scope : scope + spill_over = false + else + # no epp params or user provided arguments in a hash, epp logic only sees global + what was given + closure_scope = scope.find_global_scope + # given spill over if there are no params (e.g. replace closure scope by a new scope with the given args) + spill_over = see_scope + end + evaluated_result = parser.closure(body, closure_scope).call_by_name(scope, template_args, spill_over) + evaluated_result + end + + def self.handle_template_args(func_name, template_args) + if template_args.nil? + [{}, false] + else + unless template_args.is_a?(Hash) + raise ArgumentException, "#{func_name}(): the template_args must be a Hash, got a {template_args.class}" + end + [template_args, true] + end + end +end \ No newline at end of file diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb index d2e1eee74..07be2a25a 100644 --- a/lib/puppet/pops/evaluator/evaluator_impl.rb +++ b/lib/puppet/pops/evaluator/evaluator_impl.rb @@ -135,8 +135,8 @@ class Puppet::Pops::Evaluator::EvaluatorImpl pblock = closure.model parameters = pblock.parameters || [] - if !spill_over && args.size > parameters.size - raise ArgumentError, "Too many arguments: #{args.size} for #{parameters.size}" + if !spill_over && args_hash.size > parameters.size + raise ArgumentError, "Too many arguments: #{args_hash.size} for #{parameters.size}" end # associate values with parameters @@ -149,7 +149,6 @@ class Puppet::Pops::Evaluator::EvaluatorImpl optional = parameters.count { |p| !p.value.nil? } raise ArgumentError, "Too few arguments; no value given for required parameters #{missing.join(" ,")}" end - if spill_over # all args from given hash should be used, nil entries replaced by default values should win scope_hash = args_hash.merge(scope_hash) @@ -450,6 +449,13 @@ class Puppet::Pops::Evaluator::EvaluatorImpl end end + def eval_EppExpression(o, scope) + scope["@epp"] = [] + evaluate(o.body, scope) + result = scope["@epp"].join('') + result + end + def eval_RenderStringExpression(o, scope) scope["@epp"] << o.value.dup nil @@ -740,7 +746,13 @@ class Puppet::Pops::Evaluator::EvaluatorImpl def eval_CallNamedFunctionExpression(o, scope) # The functor expression is not evaluated, it is not possible to select the function to call # via an expression like $a() - unless o.functor_expr.is_a? Puppet::Pops::Model::QualifiedName + case o.functor_expr + when Puppet::Pops::Model::QualifiedName + # ok + when Puppet::Pops::Model::RenderStringExpression + # helpful to point out this easy to make Epp error + fail(Issues::ILLEGAL_EPP_PARAMETERS, o) + else fail(Issues::ILLEGAL_EXPRESSION, o.functor_expr, {:feature=>'function name', :container => o}) end name = o.functor_expr.value diff --git a/lib/puppet/pops/issues.rb b/lib/puppet/pops/issues.rb index 0613ba3a4..8dfe5d6c0 100644 --- a/lib/puppet/pops/issues.rb +++ b/lib/puppet/pops/issues.rb @@ -454,4 +454,8 @@ module Puppet::Pops::Issues EMPTY_HEREDOC_SYNTAX_SEGMENT = issue :EMPTY_HEREDOC_SYNTAX_SEGMENT, :syntax do "Heredoc syntax specification has empty segment between '+' : '#{syntax}'" end + + ILLEGAL_EPP_PARAMETERS = issue :ILLEGAL_EPP_PARAMETERS do + "Ambiguous EPP parameter expression. Probably missing '<%-' before parameters to remove leading whitespace" + end end diff --git a/lib/puppet/pops/model/factory.rb b/lib/puppet/pops/model/factory.rb index 075332bad..452f6a654 100644 --- a/lib/puppet/pops/model/factory.rb +++ b/lib/puppet/pops/model/factory.rb @@ -208,7 +208,7 @@ class Puppet::Pops::Model::Factory def build_LambdaExpression(o, parameters, body) parameters.each {|p| o.addParameters(build(p)) } b = f_build_body(body) - o.body = b.current if b + o.body = to_ops(b) if b o end @@ -578,7 +578,13 @@ class Puppet::Pops::Model::Factory end def self.EPP(parameters, body) - new(Model::EppExpression, parameters, body) + see_scope = false + params = parameters + if parameters.nil? + params = [] + see_scope = true + end + LAMBDA(params, new(Model::EppExpression, see_scope, body)) end # TODO: This is the same a fqn factory method, don't know if callers to fqn and QNAME can live with the @@ -770,8 +776,8 @@ class Puppet::Pops::Model::Factory x end - def build_EppExpression(o, parameters, body) - parameters.each {|p| o.addParameters(build(p)) } + def build_EppExpression(o, see_scope, body) + o.see_scope = see_scope b = f_build_body(body) o.body = b.current if b o diff --git a/lib/puppet/pops/model/model.rb b/lib/puppet/pops/model/model.rb index 7b65812f1..b186aca3f 100644 --- a/lib/puppet/pops/model/model.rb +++ b/lib/puppet/pops/model/model.rb @@ -497,8 +497,8 @@ module Puppet::Pops::Model class VariableExpression < UnaryExpression; end # Epp start - class EppExpression < Definition - contains_many_uni 'parameters', Parameter + class EppExpression < Expression + has_attr 'see_scope', Boolean contains_one_uni 'body', Expression end diff --git a/lib/puppet/pops/model/model_tree_dumper.rb b/lib/puppet/pops/model/model_tree_dumper.rb index 2e49092ce..1c4bf19cd 100644 --- a/lib/puppet/pops/model/model_tree_dumper.rb +++ b/lib/puppet/pops/model/model_tree_dumper.rb @@ -60,7 +60,7 @@ class Puppet::Pops::Model::ModelTreeDumper < Puppet::Pops::Model::TreeDumper def dump_EppExpression o result = ["epp"] - result << ["parameters"] + o.parameters.collect {|p| do_dump(p) } if o.parameters.size() > 0 +# result << ["parameters"] + o.parameters.collect {|p| do_dump(p) } if o.parameters.size() > 0 if o.body result << do_dump(o.body) else diff --git a/lib/puppet/pops/parser/egrammar.ra b/lib/puppet/pops/parser/egrammar.ra index b9ee391ef..69f0e3555 100644 --- a/lib/puppet/pops/parser/egrammar.ra +++ b/lib/puppet/pops/parser/egrammar.ra @@ -659,7 +659,6 @@ dqtail : dqpost { result = [val[0]] } | dqmid dqrval { result = [val[0]] + val[1] } -# TODO_HEREDOC heredoc : HEREDOC sublocated_text { result = Factory.HEREDOC(val[0][:value], val[1]); loc result, val[0] } @@ -667,12 +666,11 @@ sublocated_text : SUBLOCATE string { result = Factory.SUBLOCATE(val[0], val[1]); loc result, val[0] } | SUBLOCATE dq_string { result = Factory.SUBLOCATE(val[0], val[1]); loc result, val[0] } -# TODO_EPP epp_expression : EPP_START epp_parameters_list statements { result = Factory.EPP(val[1], val[2]); loc result, val[0] } epp_parameters_list - : =LOW{ result = [] } + : =LOW{ result = nil } | LPAREN RPAREN { result = [] } | LPAREN parameters endcomma RPAREN { result = val[1] } diff --git a/lib/puppet/pops/parser/eparser.rb b/lib/puppet/pops/parser/eparser.rb index 452c3eda6..a799180d5 100644 --- a/lib/puppet/pops/parser/eparser.rb +++ b/lib/puppet/pops/parser/eparser.rb @@ -20,7 +20,7 @@ module Puppet module Parser class Parser < Racc::Parser -module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 739) +module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 737) # Make emacs happy # Local Variables: @@ -2371,112 +2371,112 @@ module_eval(<<'.,.,', 'egrammar.ra', 659) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 663) +module_eval(<<'.,.,', 'egrammar.ra', 662) def _reduce_190(val, _values, result) result = Factory.HEREDOC(val[0][:value], val[1]); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 666) +module_eval(<<'.,.,', 'egrammar.ra', 665) def _reduce_191(val, _values, result) result = Factory.SUBLOCATE(val[0], val[1]); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 667) +module_eval(<<'.,.,', 'egrammar.ra', 666) def _reduce_192(val, _values, result) result = Factory.SUBLOCATE(val[0], val[1]); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 671) +module_eval(<<'.,.,', 'egrammar.ra', 669) def _reduce_193(val, _values, result) result = Factory.EPP(val[1], val[2]); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 674) +module_eval(<<'.,.,', 'egrammar.ra', 672) def _reduce_194(val, _values, result) - result = [] + result = nil result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 675) +module_eval(<<'.,.,', 'egrammar.ra', 673) def _reduce_195(val, _values, result) result = [] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 676) +module_eval(<<'.,.,', 'egrammar.ra', 674) def _reduce_196(val, _values, result) result = val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 679) +module_eval(<<'.,.,', 'egrammar.ra', 677) def _reduce_197(val, _values, result) result = Factory.RENDER_STRING(val[0][:value]); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 680) +module_eval(<<'.,.,', 'egrammar.ra', 678) def _reduce_198(val, _values, result) result = Factory.RENDER_EXPR(val[1]); loc result, val[0], val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 682) +module_eval(<<'.,.,', 'egrammar.ra', 680) def _reduce_199(val, _values, result) result = Factory.NUMBER(val[0][:value]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 683) +module_eval(<<'.,.,', 'egrammar.ra', 681) def _reduce_200(val, _values, result) result = Factory.QNAME_OR_NUMBER(val[0][:value]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 684) +module_eval(<<'.,.,', 'egrammar.ra', 682) def _reduce_201(val, _values, result) result = Factory.QREF(val[0][:value]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 685) +module_eval(<<'.,.,', 'egrammar.ra', 683) def _reduce_202(val, _values, result) result = Factory.literal(:undef); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 686) +module_eval(<<'.,.,', 'egrammar.ra', 684) def _reduce_203(val, _values, result) result = Factory.literal(:default); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 691) +module_eval(<<'.,.,', 'egrammar.ra', 689) def _reduce_204(val, _values, result) result = Factory.literal(val[0][:value]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 694) +module_eval(<<'.,.,', 'egrammar.ra', 692) def _reduce_205(val, _values, result) result = Factory.literal(val[0][:value]); loc result, val[0] result @@ -2485,7 +2485,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 694) # reduce 206 omitted -module_eval(<<'.,.,', 'egrammar.ra', 700) +module_eval(<<'.,.,', 'egrammar.ra', 698) def _reduce_207(val, _values, result) result = nil result @@ -2524,7 +2524,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 700) # reduce 223 omitted -module_eval(<<'.,.,', 'egrammar.ra', 723) +module_eval(<<'.,.,', 'egrammar.ra', 721) def _reduce_224(val, _values, result) result = nil result diff --git a/lib/puppet/pops/parser/evaluating_parser.rb b/lib/puppet/pops/parser/evaluating_parser.rb index 94ab47e03..ace5b8be0 100644 --- a/lib/puppet/pops/parser/evaluating_parser.rb +++ b/lib/puppet/pops/parser/evaluating_parser.rb @@ -3,6 +3,8 @@ # class Puppet::Pops::Parser::EvaluatingParser + attr_reader :parser + def initialize() @parser = Puppet::Pops::Parser::Parser.new() end @@ -17,7 +19,7 @@ class Puppet::Pops::Parser::EvaluatingParser # Also a possible improvement (if the YAML parser returns positions) is to provide correct output of position. # begin - assert_and_report(@parser.parse_string(s)) + assert_and_report(parser.parse_string(s)) rescue Puppet::ParseError => e # TODO: This is not quite right, why does not the exception have the correct file? e.file = @file_source unless e.file.is_a?(String) && !e.file.empty? @@ -28,7 +30,7 @@ class Puppet::Pops::Parser::EvaluatingParser def parse_file(file) @file_source = file clear() - assert_and_report(@parser.parse_file(file)) + assert_and_report(parser.parse_file(file)) end def evaluate_string(scope, s, file_source='unknown') @@ -180,5 +182,15 @@ class Puppet::Pops::Parser::EvaluatingParser Puppet::Pops::Validation::ValidatorFactory_4_0.new().validator(acceptor) end + # Create a closure that can be called in the given scope + def closure(model, scope) + Puppet::Pops::Evaluator::Closure.new(@@evaluator, model, scope) + end + end + + class EvaluatingEppParser < Transitional + def initialize() + @parser = Puppet::Pops::Parser::EppParser.new() + end end end diff --git a/lib/puppet/pops/validation/checker4_0.rb b/lib/puppet/pops/validation/checker4_0.rb index e162f42da..4b3271ebe 100644 --- a/lib/puppet/pops/validation/checker4_0.rb +++ b/lib/puppet/pops/validation/checker4_0.rb @@ -160,8 +160,15 @@ class Puppet::Pops::Validation::Checker4_0 end def check_CallNamedFunctionExpression(o) - unless o.functor_expr.is_a? Model::QualifiedName - acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.functor_expr, :feature => 'function name', :container => o) + case o.functor_expr + when Puppet::Pops::Model::QualifiedName + # ok + nil + when Puppet::Pops::Model::RenderStringExpression + # helpful to point out this easy to make Epp error + acceptor.accept(Issues::ILLEGAL_EPP_PARAMETERS, o) + else + acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.functor_expr, {:feature=>'function name', :container => o}) end end From 1539c1bfe960d242b37696936083c05f8ccef005 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 6 Mar 2014 17:28:48 -0800 Subject: [PATCH 777/800] (maint) Fix scope.find_global_scope when running tests When running tests there is no global scope (top or node), in that case consider the only existing scope to be the top scope. --- lib/puppet/parser/scope.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb index f9a8efef4..11bb0fde6 100644 --- a/lib/puppet/parser/scope.rb +++ b/lib/puppet/parser/scope.rb @@ -245,7 +245,13 @@ class Puppet::Parser::Scope self else next_scope = inherited_scope || enclosing_scope - next_scope.find_global_scope() + if next_scope.nil? + # this happens when testing, and there is only a single test scope and no link to any + # other scopes + self + else + next_scope.find_global_scope() + end end end From 8ac3bd1e3ecc5574193375caef9bdbc1d5df102d Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 6 Mar 2014 17:29:33 -0800 Subject: [PATCH 778/800] (PUP-30) Fix tests and issues occurring when running tests --- lib/puppet/pops/evaluator/epp_evaluator.rb | 1 - lib/puppet/pops/parser/evaluating_parser.rb | 10 +++- spec/unit/parser/functions/epp_spec.rb | 62 +++++++++++++++++++++ spec/unit/pops/parser/epp_parser_spec.rb | 17 +++--- 4 files changed, 78 insertions(+), 12 deletions(-) create mode 100644 spec/unit/parser/functions/epp_spec.rb diff --git a/lib/puppet/pops/evaluator/epp_evaluator.rb b/lib/puppet/pops/evaluator/epp_evaluator.rb index f8b341868..e5e26efb6 100644 --- a/lib/puppet/pops/evaluator/epp_evaluator.rb +++ b/lib/puppet/pops/evaluator/epp_evaluator.rb @@ -26,7 +26,6 @@ class Puppet::Pops::Evaluator::EppEvaluator file = file + ".epp" unless file =~ /\.epp$/ scope.debug "Retrieving epp template #{file}" - require 'debugger'; debugger template_file = Puppet::Parser::Files.find_template(file, env_name) unless template_file raise Puppet::ParseError, "Could not find template '#{file}'" diff --git a/lib/puppet/pops/parser/evaluating_parser.rb b/lib/puppet/pops/parser/evaluating_parser.rb index ace5b8be0..22fe53720 100644 --- a/lib/puppet/pops/parser/evaluating_parser.rb +++ b/lib/puppet/pops/parser/evaluating_parser.rb @@ -172,10 +172,14 @@ class Puppet::Pops::Parser::EvaluatingParser # will eventually have this behavior instead of using transformation to Puppet 3.x AST class Transitional < Puppet::Pops::Parser::EvaluatingParser + def evaluator + @@evaluator ||= Puppet::Pops::Evaluator::EvaluatorImpl.new() + @@evaluator + end + def evaluate(scope, model) return nil unless model - @@evaluator ||= Puppet::Pops::Evaluator::EvaluatorImpl.new() - @@evaluator.evaluate(model, scope) + evaluator.evaluate(model, scope) end def validator(acceptor) @@ -184,7 +188,7 @@ class Puppet::Pops::Parser::EvaluatingParser # Create a closure that can be called in the given scope def closure(model, scope) - Puppet::Pops::Evaluator::Closure.new(@@evaluator, model, scope) + Puppet::Pops::Evaluator::Closure.new(evaluator, model, scope) end end diff --git a/spec/unit/parser/functions/epp_spec.rb b/spec/unit/parser/functions/epp_spec.rb new file mode 100644 index 000000000..0f664b08f --- /dev/null +++ b/spec/unit/parser/functions/epp_spec.rb @@ -0,0 +1,62 @@ + +require 'spec_helper' + +describe "the template function" do + include PuppetSpec::Files + + before :all do + Puppet::Parser::Functions.autoloader.loadall + end + + before :each do + Puppet[:parser] = 'future' + end + + let :node do Puppet::Node.new('localhost') end + let :compiler do Puppet::Parser::Compiler.new(node) end + let :scope do Puppet::Parser::Scope.new(compiler) end + + context "when accessing scope variables as $ variables" do + it "looks up the value from the scope" do + scope["what"] = "are belong" + eval_template("all your base <%= $what %> to us").should == "all your base are belong to us" + end + + it "get nil accessing a variable that does not exist" do + eval_template("<%= $kryptonite == undef %>").should == "true" + end + + it "get nil accessing a variable that is undef" do + scope['undef_var'] = :undef + eval_template("<%= $undef_var == undef %>").should == "true" + end + end + + # although never a problem with epp + it "is not interfered with by having a variable named 'string' (#14093)" do + scope['string'] = "this output should not be seen" + eval_template("some text that is static").should == "some text that is static" + end + + it "has access to a variable named 'string' (#14093)" do + scope['string'] = "the string value" + eval_template("string was: <%= $string %>").should == "string was: the string value" + end + + + def eval_template_with_args(content, args_hash) + File.stubs(:read).with("template.epp").returns(content) + FileTest.stubs(:exist?).with("template.epp").returns(true) + Puppet::Parser::Files.stubs(:find_template).returns("template.epp") + scope.function_epp(['template', args_hash]) + end + + def eval_template(content) + file_path = tmpdir('epp_spec_content') + filename = File.join(file_path, "template.epp") + File.open(filename, "w+") { |f| f.write(content) } + + Puppet::Parser::Files.stubs(:find_template).returns(filename) + scope.function_epp(['template']) + end +end diff --git a/spec/unit/pops/parser/epp_parser_spec.rb b/spec/unit/pops/parser/epp_parser_spec.rb index a91148ed1..951293fd3 100644 --- a/spec/unit/pops/parser/epp_parser_spec.rb +++ b/spec/unit/pops/parser/epp_parser_spec.rb @@ -23,7 +23,8 @@ describe "epp parser" do parser = Puppet::Pops::Parser::EppParser.new() model = parser.parse_string("Nothing to see here, move along...").current model.class.should == Puppet::Pops::Model::Program - model.body.class.should == Puppet::Pops::Model::EppExpression + model.body.class.should == Puppet::Pops::Model::LambdaExpression + model.body.body.class.should == Puppet::Pops::Model::EppExpression end context "when facing bad input it reports" do @@ -46,32 +47,32 @@ describe "epp parser" do context "handles parsing of" do it "text (and nothing else)" do - dump(parse("Hello World")).should == "(epp (block (render-s 'Hello World')))" + dump(parse("Hello World")).should == "(lambda (epp (block (render-s 'Hello World'))))" end it "template parameters" do - dump(parse("<%($x)%>Hello World")).should == "(epp (parameters x) (block (render-s 'Hello World')))" + dump(parse("<%($x)%>Hello World")).should == "(lambda (parameters x) (epp (block (render-s 'Hello World'))))" end it "template parameters with default" do - dump(parse("<%($x='cigar')%>Hello World")).should == "(epp (parameters (= x 'cigar')) (block (render-s 'Hello World')))" + dump(parse("<%($x='cigar')%>Hello World")).should == "(lambda (parameters (= x 'cigar')) (epp (block (render-s 'Hello World'))))" end it "template parameters with and without default" do - dump(parse("<%($x='cigar', $y)%>Hello World")).should == "(epp (parameters (= x 'cigar') y) (block (render-s 'Hello World')))" + dump(parse("<%($x='cigar', $y)%>Hello World")).should == "(lambda (parameters (= x 'cigar') y) (epp (block (render-s 'Hello World'))))" end it "comments" do - dump(parse("<%#($x='cigar', $y)%>Hello World")).should == "(epp (block (render-s 'Hello World')))" + dump(parse("<%#($x='cigar', $y)%>Hello World")).should == "(lambda (epp (block (render-s 'Hello World'))))" end it "verbatim epp tags" do - dump(parse("<%% contemplating %%>Hello World")).should == "(epp (block (render-s '<% contemplating %>Hello World')))" + dump(parse("<%% contemplating %%>Hello World")).should == "(lambda (epp (block (render-s '<% contemplating %>Hello World'))))" end it "expressions" do dump(parse("We all live in <%= 3.14 - 2.14 %> world")).should == - "(epp (block (render-s 'We all live in ') (render (- 3.14 2.14)) (render-s ' world')))" + "(lambda (epp (block (render-s 'We all live in ') (render (- 3.14 2.14)) (render-s ' world'))))" end end end From b49ccda82659e2ea24ba45d6a37b3977a1a64038 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 6 Mar 2014 17:41:19 -0800 Subject: [PATCH 779/800] (PUP-30) Add more tests of epp() function --- spec/unit/parser/functions/epp_spec.rb | 32 +++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/spec/unit/parser/functions/epp_spec.rb b/spec/unit/parser/functions/epp_spec.rb index 0f664b08f..3ff08d166 100644 --- a/spec/unit/parser/functions/epp_spec.rb +++ b/spec/unit/parser/functions/epp_spec.rb @@ -30,6 +30,30 @@ describe "the template function" do scope['undef_var'] = :undef eval_template("<%= $undef_var == undef %>").should == "true" end + + it "gets shadowed variable if args are given" do + scope['phantom'] = 'of the opera' + eval_template_with_args("<%= $phantom == dragos %>", 'phantom' => 'dragos').should == "true" + end + + it "gets shadowed variable if args are given and parameters are specified" do + scope['x'] = 'wrong one' + eval_template_with_args("<%-( $x )-%><%= $x == correct %>", 'x' => 'correct').should == "true" + end + + it "raises an error if required variable is not given" do + scope['x'] = 'wrong one' + expect { + eval_template_with_args("<%-( $x )-%><%= $x == correct %>", 'y' => 'correct') + }.to raise_error(/no value given for required parameters x/) + end + + it "raises an error if too many arguments are given" do + scope['x'] = 'wrong one' + expect { + eval_template_with_args("<%-( $x )-%><%= $x == correct %>", 'x' => 'correct', 'y' => 'surplus') + }.to raise_error(/Too many arguments: 2 for 1/) + end end # although never a problem with epp @@ -45,9 +69,11 @@ describe "the template function" do def eval_template_with_args(content, args_hash) - File.stubs(:read).with("template.epp").returns(content) - FileTest.stubs(:exist?).with("template.epp").returns(true) - Puppet::Parser::Files.stubs(:find_template).returns("template.epp") + file_path = tmpdir('epp_spec_content') + filename = File.join(file_path, "template.epp") + File.open(filename, "w+") { |f| f.write(content) } + + Puppet::Parser::Files.stubs(:find_template).returns(filename) scope.function_epp(['template', args_hash]) end From d782a60948f8955cd822a8ac486f31bd8ae69136 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 6 Mar 2014 17:43:03 -0800 Subject: [PATCH 780/800] (maint) Correct name of test for epp function --- spec/unit/parser/functions/epp_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/unit/parser/functions/epp_spec.rb b/spec/unit/parser/functions/epp_spec.rb index 3ff08d166..ffbe0d0a8 100644 --- a/spec/unit/parser/functions/epp_spec.rb +++ b/spec/unit/parser/functions/epp_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe "the template function" do +describe "the epp function" do include PuppetSpec::Files before :all do From 2a734210942c1c3691d6d3c3462d9bd67d7e9441 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 6 Mar 2014 17:45:22 -0800 Subject: [PATCH 781/800] (PUP-30) Add tests of inline_epp function --- spec/unit/parser/functions/inline_epp_spec.rb | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 spec/unit/parser/functions/inline_epp_spec.rb diff --git a/spec/unit/parser/functions/inline_epp_spec.rb b/spec/unit/parser/functions/inline_epp_spec.rb new file mode 100644 index 000000000..d97f0d5ba --- /dev/null +++ b/spec/unit/parser/functions/inline_epp_spec.rb @@ -0,0 +1,78 @@ + +require 'spec_helper' + +describe "the inline_epp function" do + include PuppetSpec::Files + + before :all do + Puppet::Parser::Functions.autoloader.loadall + end + + before :each do + Puppet[:parser] = 'future' + end + + let :node do Puppet::Node.new('localhost') end + let :compiler do Puppet::Parser::Compiler.new(node) end + let :scope do Puppet::Parser::Scope.new(compiler) end + + context "when accessing scope variables as $ variables" do + it "looks up the value from the scope" do + scope["what"] = "are belong" + eval_template("all your base <%= $what %> to us").should == "all your base are belong to us" + end + + it "get nil accessing a variable that does not exist" do + eval_template("<%= $kryptonite == undef %>").should == "true" + end + + it "get nil accessing a variable that is undef" do + scope['undef_var'] = :undef + eval_template("<%= $undef_var == undef %>").should == "true" + end + + it "gets shadowed variable if args are given" do + scope['phantom'] = 'of the opera' + eval_template_with_args("<%= $phantom == dragos %>", 'phantom' => 'dragos').should == "true" + end + + it "gets shadowed variable if args are given and parameters are specified" do + scope['x'] = 'wrong one' + eval_template_with_args("<%-( $x )-%><%= $x == correct %>", 'x' => 'correct').should == "true" + end + + it "raises an error if required variable is not given" do + scope['x'] = 'wrong one' + expect { + eval_template_with_args("<%-( $x )-%><%= $x == correct %>", 'y' => 'correct') + }.to raise_error(/no value given for required parameters x/) + end + + it "raises an error if too many arguments are given" do + scope['x'] = 'wrong one' + expect { + eval_template_with_args("<%-( $x )-%><%= $x == correct %>", 'x' => 'correct', 'y' => 'surplus') + }.to raise_error(/Too many arguments: 2 for 1/) + end + end + + # although never a problem with epp + it "is not interfered with by having a variable named 'string' (#14093)" do + scope['string'] = "this output should not be seen" + eval_template("some text that is static").should == "some text that is static" + end + + it "has access to a variable named 'string' (#14093)" do + scope['string'] = "the string value" + eval_template("string was: <%= $string %>").should == "string was: the string value" + end + + + def eval_template_with_args(content, args_hash) + scope.function_inline_epp([content, args_hash]) + end + + def eval_template(content) + scope.function_inline_epp([content]) + end +end From 597cde0f568e733cff6f2f7774572c5b12ee0832 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Thu, 6 Mar 2014 18:53:36 -0800 Subject: [PATCH 782/800] (PUP-30) Fix problem with changed behavior in StringScanner.scan_until A scan_until using a . with the intent of matching all characters must in later rubies use a pattern with //m to make it match. --- lib/puppet/pops/parser/slurp_support.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/pops/parser/slurp_support.rb b/lib/puppet/pops/parser/slurp_support.rb index fb2af8304..a93bbb7b9 100644 --- a/lib/puppet/pops/parser/slurp_support.rb +++ b/lib/puppet/pops/parser/slurp_support.rb @@ -11,7 +11,7 @@ module Puppet::Pops::Parser::SlurpSupport SLURP_SQ_PATTERN = /(?:[^\\]|^|[^\\])(?:[\\]{2})*[']/ SLURP_DQ_PATTERN = /(?:[^\\]|^|[^\\])(?:[\\]{2})*(["]|[$]\{?)/ SLURP_UQ_PATTERN = /(?:[^\\]|^|[^\\])(?:[\\]{2})*([$]\{?|\z)/ - SLURP_ALL_PATTERN = /.*(\z)/ + SLURP_ALL_PATTERN = /.*(\z)/m SQ_ESCAPES = %w{ \\ ' } DQ_ESCAPES = %w{ \\ $ ' " r n t s u}+["\r\n", "\n"] UQ_ESCAPES = %w{ \\ $ r n t s u}+["\r\n", "\n"] From 420a9db6600405fb40a85e5279a36456779a3887 Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Thu, 6 Mar 2014 13:55:57 -0800 Subject: [PATCH 783/800] (maint) Add rake task for parallelizing specs. Adding a 'parallel:spec' task that will group specs and then run those groups in a parallel set of processes. By default, parallel:spec will bunch the specs into groups of approximately 1000 and concurrently run each group up to the number of processors reported by facter. The task takes two parameters: process_count and group_size. Running with a process_count of 1 (parallel:spec[1]) will behave like the existing 'spec' task, except it will still group the tests and recycle the spec running process between groups. This has the added benefit of limiting the spec running process' working set, at the cost of being a little slower. --- .travis.yml | 4 +- spec/unit/face/module/build_spec.rb | 1 + tasks/parallel.rake | 404 ++++++++++++++++++++++++++++ util/rspec_grouper | 117 ++++++++ util/rspec_runner | 53 ++++ 5 files changed, 576 insertions(+), 3 deletions(-) create mode 100644 tasks/parallel.rake create mode 100755 util/rspec_grouper create mode 100755 util/rspec_runner diff --git a/.travis.yml b/.travis.yml index 5f5634362..8193a1496 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: ruby bundler_args: --without development -script: "bundle exec rake spec" +script: "bundle exec rake \"parallel:spec[1]\"" notifications: email: false rvm: @@ -12,5 +12,3 @@ rvm: matrix: allow_failures: - rvm: ruby-head -env: - - SPEC_OPTS="--format documentation" \ No newline at end of file diff --git a/spec/unit/face/module/build_spec.rb b/spec/unit/face/module/build_spec.rb index d372ceb30..02bbb7e9d 100644 --- a/spec/unit/face/module/build_spec.rb +++ b/spec/unit/face/module/build_spec.rb @@ -1,5 +1,6 @@ require 'spec_helper' require 'puppet/face' +require 'puppet/module_tool' describe "puppet module build" do subject { Puppet::Face[:module, :current] } diff --git a/tasks/parallel.rake b/tasks/parallel.rake new file mode 100644 index 000000000..dbdf4cf0a --- /dev/null +++ b/tasks/parallel.rake @@ -0,0 +1,404 @@ +# encoding: utf-8 + +require 'rubygems' +require 'thread' +require 'rspec' +require 'rspec/core/formatters/helpers' +require 'facter' + +module Parallel + module RSpec + # + # Responsible for buffering the output of RSpec's progress formatter. + # + class ProgressFormatBuffer + attr_reader :pending_lines + attr_reader :failure_lines + attr_reader :examples + attr_reader :failures + attr_reader :pending + attr_reader :failed_example_lines + attr_reader :state + + module OutputState + HEADER = 1 + PROGRESS = 2 + SUMMARY = 3 + PENDING = 4 + FAILURES = 5 + DURATION = 6 + COUNTS = 7 + FAILED_EXAMPLES = 8 + end + + def initialize(io, color) + @io = io + @color = color + @state = OutputState::HEADER + @pending_lines = [] + @failure_lines = [] + @examples = 0 + @failures = 0 + @pending = 0 + @failed_example_lines = [] + end + + def color? + @color + end + + def read + # Parse and ignore the one line header + if @state == OutputState::HEADER + begin + @io.readline + rescue EOFError + return nil + end + @state = OutputState::PROGRESS + return '' + end + + # If the progress has been read, parse the summary + if @state == OutputState::SUMMARY + parse_summary + return nil + end + + # Read the progress output up to 128 bytes at a time + # 128 is a small enough number to show some progress, but not too small that + # we're constantly writing synchronized output + data = @io.read(128) + return nil unless data + + data = @remainder + data if @remainder + + # Check for the end of the progress line + if (index = data.index "\n") + @state = OutputState::SUMMARY + @remainder = data[(index+1)..-1] + data = data[0...index] + # Check for partial ANSI escape codes in colorized output + elsif @color && !data.end_with?("\e[0m") && (index = data.rindex("\e[", -6)) + @remainder = data[index..-1] + data = data[0...index] + else + @remainder = nil + end + + data + end + + private + + def parse_summary + # If there is a remainder, concat it with the next line and handle each line + unless @remainder.empty? + lines = @remainder + eof = false + begin + lines += @io.readline + rescue EOFError + eof = true + end + lines.each_line do |line| + parse_summary_line line + end + return if eof + end + + # Process the rest of the lines + begin + @io.each_line do |line| + parse_summary_line line + end + rescue EOFError + end + end + + def parse_summary_line(line) + line.chomp! + return if line.empty? + + if line == 'Pending:' + @status = OutputState::PENDING + return + elsif line == 'Failures:' + @status = OutputState::FAILURES + return + elsif line == 'Failed examples:' + @status = OutputState::FAILED_EXAMPLES + return + elsif (line.match /^Finished in ((\d+\.?\d*) minutes?)? ?(\d+\.?\d*) seconds?$/) + @status = OutputState::DURATION + return + elsif (match = line.gsub(/\e\[\d+m/, '').match /^(\d+) examples?, (\d+) failures?(, (\d+) pending)?$/) + @status = OutputState::COUNTS + @examples = match[1].to_i + @failures = match[2].to_i + @pending = (match[4] || 0).to_i + return + end + + case @status + when OutputState::PENDING + @pending_lines << line + when OutputState::FAILURES + @failure_lines << line + when OutputState::FAILED_EXAMPLES + @failed_example_lines << line + end + end + end + + # + # Responsible for parallelizing spec testing. + # + class Parallelizer + include ::RSpec::Core::Formatters::Helpers + + # Number of processes to use + attr_reader :process_count + # Approximate size of each group of tests + attr_reader :group_size + + def initialize(process_count, group_size, color) + @process_count = process_count + @group_size = group_size + @color = color + end + + def color? + @color + end + + def run + @start_time = Time.now + + groups = group_specs + fail red('error: no specs were found') if groups.length == 0 + + begin + run_specs groups + ensure + groups.each do |file| + File.unlink(file) + end + end + end + + private + + def group_specs + # Spawn the rspec_grouper utility to perform the test grouping + # We do this in a separate process to limit this processes' long-running footprint + io = IO.popen("ruby util/rspec_grouper #{@group_size}") + + header = true + spec_group_files = [] + io.each_line do |line| + line.chomp! + header = false if line.empty? + next if header || line.empty? + spec_group_files << line + end + + _, status = Process.waitpid2(io.pid) + io.close + + fail red('error: no specs were found.') unless status.success? + spec_group_files + end + + def run_specs(groups) + puts "Processing #{groups.length} spec group(s) with #{@process_count} worker(s)" + + interrupted = false + success = true + worker_threads = [] + group_index = -1 + pids = Array.new(@process_count) + mutex = Mutex.new + + # Handle SIGINT by killing child processes + original_handler = Signal.trap :SIGINT do + break if interrupted + interrupted = true + + # Can't synchronize in a trap context, so read dirty + pids.each do |pid| + begin + Process.kill(:SIGKILL, pid) if pid + rescue Errno::ESRCH + end + end + puts yellow("\nshutting down...") + end + + buffers = [] + + process_count.times do |thread_id| + worker_threads << Thread.new do + while !interrupted do + # Get the spec file for this rspec run + group = mutex.synchronize { if group_index < groups.length then groups[group_index += 1] else nil end } + break unless group && !interrupted + + # Spawn the worker process with redirected output + io = IO.popen("ruby util/rspec_runner #{group}") + pids[thread_id] = io.pid + + # TODO: make the buffer pluggable to handle other output formats like documentation + buffer = ProgressFormatBuffer.new(io, @color) + + # Process the output + while !interrupted + output = buffer.read + break unless output && !interrupted + next if output.empty? + mutex.synchronize { print output } + end + + # Kill the process if we were interrupted, just to be sure + if interrupted + begin + Process.kill(:SIGKILL, pids[thread_id]) + rescue Errno::ESRCH + end + end + + # Reap the process + result = Process.waitpid2(pids[thread_id])[1].success? + io.close + pids[thread_id] = nil + mutex.synchronize do + buffers << buffer + success &= result + end + end + end + end + + # Join all worker threads + worker_threads.each do |thread| + thread.join + end + + Signal.trap :SIGINT, original_handler + fail yellow('execution was interrupted') if interrupted + + dump_summary buffers + success + end + + def colorize(text, color_code) + if @color + "#{color_code}#{text}\e[0m" + else + text + end + end + + def red(text) + colorize(text, "\e[31m") + end + + def green(text) + colorize(text, "\e[32m") + end + + def yellow(text) + colorize(text, "\e[33m") + end + + def dump_summary(buffers) + puts + + # Print out the pending tests + print_header = true + buffers.each do |buffer| + next if buffer.pending_lines.empty? + if print_header + puts "\nPending:" + print_header = false + end + puts buffer.pending_lines + end + + # Print out the failures + print_header = true + buffers.each do |buffer| + next if buffer.failure_lines.empty? + if print_header + puts "\nFailures:" + print_header = false + end + puts + puts buffer.failure_lines + end + + # Print out the run time + puts "\nFinished in #{format_duration(Time.now - @start_time)}" + + # Count all of the examples + examples = 0 + failures = 0 + pending = 0 + buffers.each do |buffer| + examples += buffer.examples + failures += buffer.failures + pending += buffer.pending + end + if failures > 0 + puts red(summary_count_line(examples, failures, pending)) + elsif pending > 0 + puts yellow(summary_count_line(examples, failures, pending)) + else + puts green(summary_count_line(examples, failures, pending)) + end + + # Print out the failed examples + print_header = true + buffers.each do |buffer| + next if buffer.failed_example_lines.empty? + if print_header + puts "\nFailed examples:" + print_header = false + end + puts buffer.failed_example_lines + end + end + + def summary_count_line(examples, failures, pending) + summary = pluralize(examples, "example") + summary << ", " << pluralize(failures, "failure") + summary << ", #{pending} pending" if pending > 0 + summary + end + end + end +end + +namespace 'parallel' do + def color_output? + # Check with RSpec to see if color is enabled + config = ::RSpec::Core::Configuration.new + config.error_stream = $stderr + config.output_stream = $stdout + options = ::RSpec::Core::ConfigurationOptions.new [] + options.parse_options + options.configure config + config.color + end + + desc 'Runs specs in parallel.' + task 'spec', :process_count, :group_size do |_, args| + # Default group size in rspec examples + DEFAULT_GROUP_SIZE = 1000 + + process_count = [(args[:process_count] || Facter.processorcount).to_i, 1].max + group_size = [(args[:group_size] || DEFAULT_GROUP_SIZE).to_i, 1].max + + abort unless Parallel::RSpec::Parallelizer.new(process_count, group_size, color_output?).run + end +end \ No newline at end of file diff --git a/util/rspec_grouper b/util/rspec_grouper new file mode 100755 index 000000000..22260d461 --- /dev/null +++ b/util/rspec_grouper @@ -0,0 +1,117 @@ +#!/usr/bin/env ruby +require 'rubygems' +require 'rspec' + +# Disable ruby verbosity +# We need control over output so that the parallel task can parse it correctly +$VERBOSE = nil + +# Monkey patch Proc.source_location for 1.8.7 +unless Proc.method_defined? :source_location + class Proc + def source_location + match = to_s.match(/^#$/) + return unless match + [match[1], match[2]] + end + end +end + +module Parallel + module RSpec + # + # Responsible for grouping rspec examples into groups of a given size. + # + class Grouper + attr_reader :groups + attr_reader :files + attr_reader :total_examples + + def initialize(group_size) + config = ::RSpec::Core::Configuration.new + options = ::RSpec::Core::ConfigurationOptions.new((ENV['TEST'] || ENV['TESTS'] || 'spec').split(';')) + options.parse_options + options.configure config + + # This will scan and load all spec examples + config.load_spec_files + + @total_examples = 0 + + # Populate a map of spec file => example count, sorted ascending by count + # NOTE: this uses a private API of RSpec and is may break if the gem is updated + @files = ::RSpec::Core::ExampleGroup.children.inject({}) do |files, group| + file = group.metadata[:example_group_block].source_location[0] + count = count_examples(group) + files[file] = (files[file] || 0) + count + @total_examples += count + files + end.sort_by { |_, v| v} + + # Group the spec files + @groups = [] + group = nil + example_count = 0 + @files.each do |file, count| + group = [] unless group + group << file + if (example_count += count) > group_size + example_count = 0 + @groups << group + group = nil + end + end + @groups << group if group + end + + private + def count_examples(group) + return 0 unless group + # Each group can have examples as well as child groups, so recursively traverse + group.children.inject(group.examples.count) { |count, g| count + count_examples(g) } + end + end + end +end + +def print_usage + puts 'usage: rspec_grouper ' +end + +if __FILE__ == $0 + if ARGV.length != 1 + print_usage + else + group_size = ARGV[0].to_i + abort 'error: group count must be greater than zero.' if group_size < 1 + grouper = Parallel::RSpec::Grouper.new(group_size) + abort 'error: no rspec examples were found.' if grouper.total_examples == 0 + groups = grouper.groups + puts "Grouped #{grouper.total_examples} rspec example(s) into #{groups.length} group(s) from #{grouper.files.count} file(s)." + puts + + paths = [] + + begin + # Create a temp directory and write out group files + tmpdir = Dir.mktmpdir + groups.each_with_index do |group, index| + path = File.join(tmpdir, "group#{index+1}") + file = File.new(path, 'w') + paths << path + file.puts group + file.close + puts path + end + rescue Exception + # Delete all files on an exception + paths.each do |path| + begin + File.delete path + rescue Exception + end + end + raise + end + end +end \ No newline at end of file diff --git a/util/rspec_runner b/util/rspec_runner new file mode 100755 index 000000000..e4452a01c --- /dev/null +++ b/util/rspec_runner @@ -0,0 +1,53 @@ +#!/usr/bin/env ruby +require 'rubygems' +require 'rspec' +require 'rspec/core/formatters/progress_formatter' +require 'rspec/core/command_line' + +# Disable ruby verbosity +# We need control over output so that the parallel task can parse it correctly +$VERBOSE = nil + +module Parallel + module RSpec + # + # Responsible for formatting output. + # This differs from the built-in progress formatter by not appending an index to failures. + # + class Formatter < ::RSpec::Core::Formatters::ProgressFormatter + def dump_failure(example, _) + # Unlike the super class implementation, do not print the failure number + output.puts "#{short_padding}#{example.full_description}" + dump_failure_info(example) + end + end + # + # Responsible for running spec files given a spec file. + # We do it this way so that we can run very long spec file lists on Windows, since + # Windows has a limited argument length depending on method of invocation. + # + class Runner + def initialize(specs_file) + abort "error: spec list file '#{specs_file}' does not exist." unless File.exists? specs_file + @options = ['-fParallel::RSpec::Formatter'] + File.readlines(specs_file).each { |line| @options << line.chomp } + end + + def run + ::RSpec::Core::CommandLine.new(@options).run($stderr, $stdout) + end + end + end +end + +def print_usage + puts 'usage: rspec_runner ' +end + +if __FILE__ == $0 + if ARGV.length != 1 + print_usage + else + exit Parallel::RSpec::Runner.new(ARGV[0]).run + end +end \ No newline at end of file From 218e0f1cab521763168f0e7a65b04c6759ec4b62 Mon Sep 17 00:00:00 2001 From: Joe Julian Date: Sat, 1 Feb 2014 11:54:16 -0800 Subject: [PATCH 784/800] (PUP-1563) resolve_install_conflicts excessively recurses Previously, resolve_install_conflicts was called from inside the modules_by_path iterator causing each module and its dependencies to be checked for conflict for each installed module, even if the installed module was completely unrelated. With a tree of dependencies and dozens of previously installed modules, this walk can take hours. This patch fixed the problem by moving the recursion outside of the modules_by_path iterator loop. --- lib/puppet/module_tool/applications/installer.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/puppet/module_tool/applications/installer.rb b/lib/puppet/module_tool/applications/installer.rb index 26e791da5..3dd878d57 100644 --- a/lib/puppet/module_tool/applications/installer.rb +++ b/lib/puppet/module_tool/applications/installer.rb @@ -165,9 +165,8 @@ module Puppet::ModuleTool :directory => mod.path, :metadata => metadata end - - resolve_install_conflicts(release[:dependencies], true) end + resolve_install_conflicts(release[:dependencies], true) end end From 9936638e6dc51ae7520f1b18062cc8911e033c51 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 7 Mar 2014 11:52:46 -0800 Subject: [PATCH 785/800] (PUP-1563) Don't recurse when dependencies are empty Previously we would recurse for each leaf dependency unnecessarily. This commit ensures we only recurse if we have a non-empty set of dependencies, and adds a debug message when conflicts are checked. It also adds a spec tests to ensure we evaluate each module for conflicts only once. Paired-with: Josh Partlow --- .../module_tool/applications/installer.rb | 8 ++++- .../applications/installer_spec.rb | 31 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/lib/puppet/module_tool/applications/installer.rb b/lib/puppet/module_tool/applications/installer.rb index 3dd878d57..07608bf5f 100644 --- a/lib/puppet/module_tool/applications/installer.rb +++ b/lib/puppet/module_tool/applications/installer.rb @@ -135,6 +135,8 @@ module Puppet::ModuleTool # install into the same directory 'foo'. # def resolve_install_conflicts(graph, is_dependency = false) + Puppet.debug("Resolving conflicts for #{graph.map {|n| n[:module]}.join(',')}") + graph.each do |release| @environment.modules_by_path[options[:target_dir]].each do |mod| if mod.has_metadata? @@ -166,7 +168,11 @@ module Puppet::ModuleTool :metadata => metadata end end - resolve_install_conflicts(release[:dependencies], true) + + deps = release[:dependencies] + if deps && !deps.empty? + resolve_install_conflicts(deps, true) + end end end diff --git a/spec/unit/module_tool/applications/installer_spec.rb b/spec/unit/module_tool/applications/installer_spec.rb index dd99fadae..2342cd1b4 100644 --- a/spec/unit/module_tool/applications/installer_spec.rb +++ b/spec/unit/module_tool/applications/installer_spec.rb @@ -273,6 +273,37 @@ MSG results[:error][:oneline].should == oneline results[:error][:multiline].should == multiline end + + it "resolves conflicts for each dependency only once" do + Puppet::Log.level = :debug + + installed_modules = [ + Puppet::Module.new('ntp', File.join(modpath1, 'ntp'), env.name), + Puppet::Module.new('mysql', File.join(modpath1, 'mysql'), env.name), + Puppet::Module.new('apache', File.join(modpath1, 'apache'), env.name) + ] + + env.stubs(:modules_by_path).returns({modpath1 => installed_modules}) + + Puppet::ModuleTool::Applications::Unpacker.expects(:new). + with('/fake_cache/pmtacceptance-apollo-0.0.2.tar.gz', options). + returns(unpacker) + Puppet::ModuleTool::Applications::Unpacker.expects(:new). + with('/fake_cache/pmtacceptance-java-1.7.1.tar.gz', options). + returns(unpacker) + Puppet::ModuleTool::Applications::Unpacker.expects(:new). + with('/fake_cache/pmtacceptance-stdlib-1.0.0.tar.gz', options). + returns(unpacker) + + installer_class.run('pmtacceptance-apollo', forge, install_dir, options) + + modules = @logs.map do |log| + data = log.message.match(/Resolving conflicts for (.*)/) + data ? data[1] : nil + end.compact + + expect(modules).to eq(["pmtacceptance-apollo", "pmtacceptance-java,pmtacceptance-stdlib"]) + end end context "when there are modules installed" do From 73c53bb72603000600b54a6dc9ee8364ce0bda0b Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 7 Mar 2014 12:43:03 -0800 Subject: [PATCH 786/800] (PUP-1885) Stringify ignores for file serving Commit 6353ebbbc0d3804e30e9961559b01c9a69a74a93 introduced non-yaml encoding for arrays of HTTP parameters. Unfortunately this also exposed those values to the type conversion that is done to parameters in the HTTP layer, which caused an ignore for a file matching "0" to be turned into the number 0. The file serving code didn't handle getting non-string ignore patterns and would then raise an error. This changes the file server to stringify all of the parameters that it recieves for ignores, which solves both the number problem as well as a related one for booleans. An alternative solution to this would have been to change the HTTP handler so that it didn't try to convert arrays of values. I didn't go for that because it would have caused the parameter handling to be inconsistent. --- lib/puppet/file_serving/fileset.rb | 2 +- spec/unit/file_serving/fileset_spec.rb | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/puppet/file_serving/fileset.rb b/lib/puppet/file_serving/fileset.rb index 6e982708b..a1d9d54d7 100644 --- a/lib/puppet/file_serving/fileset.rb +++ b/lib/puppet/file_serving/fileset.rb @@ -70,7 +70,7 @@ class Puppet::FileServing::Fileset def ignore=(values) values = [values] unless values.is_a?(Array) - @ignore = values + @ignore = values.collect(&:to_s) end def links=(links) diff --git a/spec/unit/file_serving/fileset_spec.rb b/spec/unit/file_serving/fileset_spec.rb index 463d2a2b6..4fa36f6d8 100755 --- a/spec/unit/file_serving/fileset_spec.rb +++ b/spec/unit/file_serving/fileset_spec.rb @@ -150,8 +150,8 @@ describe Puppet::FileServing::Fileset do @files << subdir # relative path subpath = File.join(path, subdir) Puppet::FileSystem.stubs(stat_method).with(subpath).returns @dirstat - Dir.stubs(:entries).with(subpath).returns(%w{.svn CVS file1 file2}) - %w{file1 file2 .svn CVS}.each do |file| + Dir.stubs(:entries).with(subpath).returns(%w{.svn CVS file1 file2 0 false}) + %w{file1 file2 .svn CVS 0 false}.each do |file| @files << File.join(subdir, file) # relative path subfile_path = File.join(subpath, file) Puppet::FileSystem.stubs(stat_method).with(subfile_path).returns(@filestat) @@ -244,6 +244,20 @@ describe Puppet::FileServing::Fileset do @fileset.files.find { |file| file.include?(".svn") or file.include?("CVS") }.should be_nil end + it "ignores files that match a pattern given as a number" do + mock_dir_structure(@path) + @fileset.recurse = true + @fileset.ignore = [0] + @fileset.files.find { |file| file.include?("0") }.should be_nil + end + + it "ignores files that match a pattern given as a boolean" do + mock_dir_structure(@path) + @fileset.recurse = true + @fileset.ignore = [false] + @fileset.files.find { |file| file.include?("false") }.should be_nil + end + it "uses Puppet::FileSystem#stat if :links is set to :follow" do mock_dir_structure(@path, :stat) @fileset.recurse = true From 495efc7d9c6e94a0b45cf3cc531e03f826d6a16a Mon Sep 17 00:00:00 2001 From: Andrew Parker Date: Fri, 7 Mar 2014 12:54:20 -0800 Subject: [PATCH 787/800] (Maint) Remove some duplication in tests This duplication actually bit me when writing the test for PUP-1885. --- spec/unit/file_serving/fileset_spec.rb | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/spec/unit/file_serving/fileset_spec.rb b/spec/unit/file_serving/fileset_spec.rb index 4fa36f6d8..e4df5c93d 100755 --- a/spec/unit/file_serving/fileset_spec.rb +++ b/spec/unit/file_serving/fileset_spec.rb @@ -140,18 +140,21 @@ describe Puppet::FileServing::Fileset do end def mock_dir_structure(path, stat_method = :lstat) - Puppet::FileSystem.stubs(stat_method).with(@path).returns @dirstat - Dir.stubs(:entries).with(path).returns(%w{one two .svn CVS}) + Puppet::FileSystem.stubs(stat_method).with(path).returns @dirstat # Keep track of the files we're stubbing. @files = %w{.} - %w{one two .svn CVS}.each do |subdir| + top_names = %w{one two .svn CVS} + sub_names = %w{file1 file2 .svn CVS 0 false} + + Dir.stubs(:entries).with(path).returns(top_names) + top_names.each do |subdir| @files << subdir # relative path subpath = File.join(path, subdir) Puppet::FileSystem.stubs(stat_method).with(subpath).returns @dirstat - Dir.stubs(:entries).with(subpath).returns(%w{.svn CVS file1 file2 0 false}) - %w{file1 file2 .svn CVS 0 false}.each do |file| + Dir.stubs(:entries).with(subpath).returns(sub_names) + sub_names.each do |file| @files << File.join(subdir, file) # relative path subfile_path = File.join(subpath, file) Puppet::FileSystem.stubs(stat_method).with(subfile_path).returns(@filestat) From 7c4cfe23ec8b57c732b28a99701ca8fdda899c67 Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Fri, 7 Mar 2014 14:05:43 -0800 Subject: [PATCH 788/800] (maint) Fix parallel:spec rake task when dependencies are not present. If rspec isn't installed, rake will fail because the parallel.rake file cannot be loaded. The fix is to only define the task if rspec/facter can be loaded. --- tasks/parallel.rake | 710 ++++++++++++++++++++++---------------------- 1 file changed, 357 insertions(+), 353 deletions(-) diff --git a/tasks/parallel.rake b/tasks/parallel.rake index dbdf4cf0a..87b25cf95 100644 --- a/tasks/parallel.rake +++ b/tasks/parallel.rake @@ -2,403 +2,407 @@ require 'rubygems' require 'thread' -require 'rspec' -require 'rspec/core/formatters/helpers' -require 'facter' +begin + require 'rspec' + require 'rspec/core/formatters/helpers' + require 'facter' +rescue LoadError + # Don't define the task if we don't have rspec or facter present +else + module Parallel + module RSpec + # + # Responsible for buffering the output of RSpec's progress formatter. + # + class ProgressFormatBuffer + attr_reader :pending_lines + attr_reader :failure_lines + attr_reader :examples + attr_reader :failures + attr_reader :pending + attr_reader :failed_example_lines + attr_reader :state -module Parallel - module RSpec - # - # Responsible for buffering the output of RSpec's progress formatter. - # - class ProgressFormatBuffer - attr_reader :pending_lines - attr_reader :failure_lines - attr_reader :examples - attr_reader :failures - attr_reader :pending - attr_reader :failed_example_lines - attr_reader :state + module OutputState + HEADER = 1 + PROGRESS = 2 + SUMMARY = 3 + PENDING = 4 + FAILURES = 5 + DURATION = 6 + COUNTS = 7 + FAILED_EXAMPLES = 8 + end - module OutputState - HEADER = 1 - PROGRESS = 2 - SUMMARY = 3 - PENDING = 4 - FAILURES = 5 - DURATION = 6 - COUNTS = 7 - FAILED_EXAMPLES = 8 - end + def initialize(io, color) + @io = io + @color = color + @state = OutputState::HEADER + @pending_lines = [] + @failure_lines = [] + @examples = 0 + @failures = 0 + @pending = 0 + @failed_example_lines = [] + end - def initialize(io, color) - @io = io - @color = color - @state = OutputState::HEADER - @pending_lines = [] - @failure_lines = [] - @examples = 0 - @failures = 0 - @pending = 0 - @failed_example_lines = [] - end + def color? + @color + end - def color? - @color - end + def read + # Parse and ignore the one line header + if @state == OutputState::HEADER + begin + @io.readline + rescue EOFError + return nil + end + @state = OutputState::PROGRESS + return '' + end - def read - # Parse and ignore the one line header - if @state == OutputState::HEADER - begin - @io.readline - rescue EOFError + # If the progress has been read, parse the summary + if @state == OutputState::SUMMARY + parse_summary return nil end - @state = OutputState::PROGRESS - return '' - end - # If the progress has been read, parse the summary - if @state == OutputState::SUMMARY - parse_summary - return nil - end + # Read the progress output up to 128 bytes at a time + # 128 is a small enough number to show some progress, but not too small that + # we're constantly writing synchronized output + data = @io.read(128) + return nil unless data - # Read the progress output up to 128 bytes at a time - # 128 is a small enough number to show some progress, but not too small that - # we're constantly writing synchronized output - data = @io.read(128) - return nil unless data + data = @remainder + data if @remainder - data = @remainder + data if @remainder - - # Check for the end of the progress line - if (index = data.index "\n") - @state = OutputState::SUMMARY - @remainder = data[(index+1)..-1] - data = data[0...index] - # Check for partial ANSI escape codes in colorized output - elsif @color && !data.end_with?("\e[0m") && (index = data.rindex("\e[", -6)) - @remainder = data[index..-1] - data = data[0...index] - else - @remainder = nil - end - - data - end - - private - - def parse_summary - # If there is a remainder, concat it with the next line and handle each line - unless @remainder.empty? - lines = @remainder - eof = false - begin - lines += @io.readline - rescue EOFError - eof = true + # Check for the end of the progress line + if (index = data.index "\n") + @state = OutputState::SUMMARY + @remainder = data[(index+1)..-1] + data = data[0...index] + # Check for partial ANSI escape codes in colorized output + elsif @color && !data.end_with?("\e[0m") && (index = data.rindex("\e[", -6)) + @remainder = data[index..-1] + data = data[0...index] + else + @remainder = nil end - lines.each_line do |line| - parse_summary_line line - end - return if eof + + data end - # Process the rest of the lines - begin - @io.each_line do |line| - parse_summary_line line - end - rescue EOFError - end - end + private - def parse_summary_line(line) - line.chomp! - return if line.empty? - - if line == 'Pending:' - @status = OutputState::PENDING - return - elsif line == 'Failures:' - @status = OutputState::FAILURES - return - elsif line == 'Failed examples:' - @status = OutputState::FAILED_EXAMPLES - return - elsif (line.match /^Finished in ((\d+\.?\d*) minutes?)? ?(\d+\.?\d*) seconds?$/) - @status = OutputState::DURATION - return - elsif (match = line.gsub(/\e\[\d+m/, '').match /^(\d+) examples?, (\d+) failures?(, (\d+) pending)?$/) - @status = OutputState::COUNTS - @examples = match[1].to_i - @failures = match[2].to_i - @pending = (match[4] || 0).to_i - return - end - - case @status - when OutputState::PENDING - @pending_lines << line - when OutputState::FAILURES - @failure_lines << line - when OutputState::FAILED_EXAMPLES - @failed_example_lines << line - end - end - end - - # - # Responsible for parallelizing spec testing. - # - class Parallelizer - include ::RSpec::Core::Formatters::Helpers - - # Number of processes to use - attr_reader :process_count - # Approximate size of each group of tests - attr_reader :group_size - - def initialize(process_count, group_size, color) - @process_count = process_count - @group_size = group_size - @color = color - end - - def color? - @color - end - - def run - @start_time = Time.now - - groups = group_specs - fail red('error: no specs were found') if groups.length == 0 - - begin - run_specs groups - ensure - groups.each do |file| - File.unlink(file) - end - end - end - - private - - def group_specs - # Spawn the rspec_grouper utility to perform the test grouping - # We do this in a separate process to limit this processes' long-running footprint - io = IO.popen("ruby util/rspec_grouper #{@group_size}") - - header = true - spec_group_files = [] - io.each_line do |line| - line.chomp! - header = false if line.empty? - next if header || line.empty? - spec_group_files << line - end - - _, status = Process.waitpid2(io.pid) - io.close - - fail red('error: no specs were found.') unless status.success? - spec_group_files - end - - def run_specs(groups) - puts "Processing #{groups.length} spec group(s) with #{@process_count} worker(s)" - - interrupted = false - success = true - worker_threads = [] - group_index = -1 - pids = Array.new(@process_count) - mutex = Mutex.new - - # Handle SIGINT by killing child processes - original_handler = Signal.trap :SIGINT do - break if interrupted - interrupted = true - - # Can't synchronize in a trap context, so read dirty - pids.each do |pid| + def parse_summary + # If there is a remainder, concat it with the next line and handle each line + unless @remainder.empty? + lines = @remainder + eof = false begin - Process.kill(:SIGKILL, pid) if pid - rescue Errno::ESRCH + lines += @io.readline + rescue EOFError + eof = true + end + lines.each_line do |line| + parse_summary_line line + end + return if eof + end + + # Process the rest of the lines + begin + @io.each_line do |line| + parse_summary_line line + end + rescue EOFError + end + end + + def parse_summary_line(line) + line.chomp! + return if line.empty? + + if line == 'Pending:' + @status = OutputState::PENDING + return + elsif line == 'Failures:' + @status = OutputState::FAILURES + return + elsif line == 'Failed examples:' + @status = OutputState::FAILED_EXAMPLES + return + elsif (line.match /^Finished in ((\d+\.?\d*) minutes?)? ?(\d+\.?\d*) seconds?$/) + @status = OutputState::DURATION + return + elsif (match = line.gsub(/\e\[\d+m/, '').match /^(\d+) examples?, (\d+) failures?(, (\d+) pending)?$/) + @status = OutputState::COUNTS + @examples = match[1].to_i + @failures = match[2].to_i + @pending = (match[4] || 0).to_i + return + end + + case @status + when OutputState::PENDING + @pending_lines << line + when OutputState::FAILURES + @failure_lines << line + when OutputState::FAILED_EXAMPLES + @failed_example_lines << line + end + end + end + + # + # Responsible for parallelizing spec testing. + # + class Parallelizer + include ::RSpec::Core::Formatters::Helpers + + # Number of processes to use + attr_reader :process_count + # Approximate size of each group of tests + attr_reader :group_size + + def initialize(process_count, group_size, color) + @process_count = process_count + @group_size = group_size + @color = color + end + + def color? + @color + end + + def run + @start_time = Time.now + + groups = group_specs + fail red('error: no specs were found') if groups.length == 0 + + begin + run_specs groups + ensure + groups.each do |file| + File.unlink(file) end end - puts yellow("\nshutting down...") end - buffers = [] + private - process_count.times do |thread_id| - worker_threads << Thread.new do - while !interrupted do - # Get the spec file for this rspec run - group = mutex.synchronize { if group_index < groups.length then groups[group_index += 1] else nil end } - break unless group && !interrupted + def group_specs + # Spawn the rspec_grouper utility to perform the test grouping + # We do this in a separate process to limit this processes' long-running footprint + io = IO.popen("ruby util/rspec_grouper #{@group_size}") - # Spawn the worker process with redirected output - io = IO.popen("ruby util/rspec_runner #{group}") - pids[thread_id] = io.pid + header = true + spec_group_files = [] + io.each_line do |line| + line.chomp! + header = false if line.empty? + next if header || line.empty? + spec_group_files << line + end - # TODO: make the buffer pluggable to handle other output formats like documentation - buffer = ProgressFormatBuffer.new(io, @color) + _, status = Process.waitpid2(io.pid) + io.close - # Process the output - while !interrupted - output = buffer.read - break unless output && !interrupted - next if output.empty? - mutex.synchronize { print output } + fail red('error: no specs were found.') unless status.success? + spec_group_files + end + + def run_specs(groups) + puts "Processing #{groups.length} spec group(s) with #{@process_count} worker(s)" + + interrupted = false + success = true + worker_threads = [] + group_index = -1 + pids = Array.new(@process_count) + mutex = Mutex.new + + # Handle SIGINT by killing child processes + original_handler = Signal.trap :SIGINT do + break if interrupted + interrupted = true + + # Can't synchronize in a trap context, so read dirty + pids.each do |pid| + begin + Process.kill(:SIGKILL, pid) if pid + rescue Errno::ESRCH end + end + puts yellow("\nshutting down...") + end - # Kill the process if we were interrupted, just to be sure - if interrupted - begin - Process.kill(:SIGKILL, pids[thread_id]) - rescue Errno::ESRCH + buffers = [] + + process_count.times do |thread_id| + worker_threads << Thread.new do + while !interrupted do + # Get the spec file for this rspec run + group = mutex.synchronize { if group_index < groups.length then groups[group_index += 1] else nil end } + break unless group && !interrupted + + # Spawn the worker process with redirected output + io = IO.popen("ruby util/rspec_runner #{group}") + pids[thread_id] = io.pid + + # TODO: make the buffer pluggable to handle other output formats like documentation + buffer = ProgressFormatBuffer.new(io, @color) + + # Process the output + while !interrupted + output = buffer.read + break unless output && !interrupted + next if output.empty? + mutex.synchronize { print output } + end + + # Kill the process if we were interrupted, just to be sure + if interrupted + begin + Process.kill(:SIGKILL, pids[thread_id]) + rescue Errno::ESRCH + end + end + + # Reap the process + result = Process.waitpid2(pids[thread_id])[1].success? + io.close + pids[thread_id] = nil + mutex.synchronize do + buffers << buffer + success &= result end end - - # Reap the process - result = Process.waitpid2(pids[thread_id])[1].success? - io.close - pids[thread_id] = nil - mutex.synchronize do - buffers << buffer - success &= result - end end end - end - # Join all worker threads - worker_threads.each do |thread| - thread.join - end - - Signal.trap :SIGINT, original_handler - fail yellow('execution was interrupted') if interrupted - - dump_summary buffers - success - end - - def colorize(text, color_code) - if @color - "#{color_code}#{text}\e[0m" - else - text - end - end - - def red(text) - colorize(text, "\e[31m") - end - - def green(text) - colorize(text, "\e[32m") - end - - def yellow(text) - colorize(text, "\e[33m") - end - - def dump_summary(buffers) - puts - - # Print out the pending tests - print_header = true - buffers.each do |buffer| - next if buffer.pending_lines.empty? - if print_header - puts "\nPending:" - print_header = false + # Join all worker threads + worker_threads.each do |thread| + thread.join end - puts buffer.pending_lines + + Signal.trap :SIGINT, original_handler + fail yellow('execution was interrupted') if interrupted + + dump_summary buffers + success end - # Print out the failures - print_header = true - buffers.each do |buffer| - next if buffer.failure_lines.empty? - if print_header - puts "\nFailures:" - print_header = false + def colorize(text, color_code) + if @color + "#{color_code}#{text}\e[0m" + else + text end + end + + def red(text) + colorize(text, "\e[31m") + end + + def green(text) + colorize(text, "\e[32m") + end + + def yellow(text) + colorize(text, "\e[33m") + end + + def dump_summary(buffers) puts - puts buffer.failure_lines - end - # Print out the run time - puts "\nFinished in #{format_duration(Time.now - @start_time)}" - - # Count all of the examples - examples = 0 - failures = 0 - pending = 0 - buffers.each do |buffer| - examples += buffer.examples - failures += buffer.failures - pending += buffer.pending - end - if failures > 0 - puts red(summary_count_line(examples, failures, pending)) - elsif pending > 0 - puts yellow(summary_count_line(examples, failures, pending)) - else - puts green(summary_count_line(examples, failures, pending)) - end - - # Print out the failed examples - print_header = true - buffers.each do |buffer| - next if buffer.failed_example_lines.empty? - if print_header - puts "\nFailed examples:" - print_header = false + # Print out the pending tests + print_header = true + buffers.each do |buffer| + next if buffer.pending_lines.empty? + if print_header + puts "\nPending:" + print_header = false + end + puts buffer.pending_lines end - puts buffer.failed_example_lines - end - end - def summary_count_line(examples, failures, pending) - summary = pluralize(examples, "example") - summary << ", " << pluralize(failures, "failure") - summary << ", #{pending} pending" if pending > 0 - summary + # Print out the failures + print_header = true + buffers.each do |buffer| + next if buffer.failure_lines.empty? + if print_header + puts "\nFailures:" + print_header = false + end + puts + puts buffer.failure_lines + end + + # Print out the run time + puts "\nFinished in #{format_duration(Time.now - @start_time)}" + + # Count all of the examples + examples = 0 + failures = 0 + pending = 0 + buffers.each do |buffer| + examples += buffer.examples + failures += buffer.failures + pending += buffer.pending + end + if failures > 0 + puts red(summary_count_line(examples, failures, pending)) + elsif pending > 0 + puts yellow(summary_count_line(examples, failures, pending)) + else + puts green(summary_count_line(examples, failures, pending)) + end + + # Print out the failed examples + print_header = true + buffers.each do |buffer| + next if buffer.failed_example_lines.empty? + if print_header + puts "\nFailed examples:" + print_header = false + end + puts buffer.failed_example_lines + end + end + + def summary_count_line(examples, failures, pending) + summary = pluralize(examples, "example") + summary << ", " << pluralize(failures, "failure") + summary << ", #{pending} pending" if pending > 0 + summary + end end end end -end -namespace 'parallel' do - def color_output? - # Check with RSpec to see if color is enabled - config = ::RSpec::Core::Configuration.new - config.error_stream = $stderr - config.output_stream = $stdout - options = ::RSpec::Core::ConfigurationOptions.new [] - options.parse_options - options.configure config - config.color - end + namespace 'parallel' do + def color_output? + # Check with RSpec to see if color is enabled + config = ::RSpec::Core::Configuration.new + config.error_stream = $stderr + config.output_stream = $stdout + options = ::RSpec::Core::ConfigurationOptions.new [] + options.parse_options + options.configure config + config.color + end - desc 'Runs specs in parallel.' - task 'spec', :process_count, :group_size do |_, args| - # Default group size in rspec examples - DEFAULT_GROUP_SIZE = 1000 + desc 'Runs specs in parallel.' + task 'spec', :process_count, :group_size do |_, args| + # Default group size in rspec examples + DEFAULT_GROUP_SIZE = 1000 - process_count = [(args[:process_count] || Facter.processorcount).to_i, 1].max - group_size = [(args[:group_size] || DEFAULT_GROUP_SIZE).to_i, 1].max + process_count = [(args[:process_count] || Facter.processorcount).to_i, 1].max + group_size = [(args[:group_size] || DEFAULT_GROUP_SIZE).to_i, 1].max - abort unless Parallel::RSpec::Parallelizer.new(process_count, group_size, color_output?).run + abort unless Parallel::RSpec::Parallelizer.new(process_count, group_size, color_output?).run + end end end \ No newline at end of file From 2cba220823aab35b3004883684c9992cd03d4b67 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Fri, 7 Mar 2014 15:29:27 -0800 Subject: [PATCH 789/800] (PUP-1895) Change <%( )%> to <%| |%> for EPP parameters --- lib/puppet/pops/parser/egrammar.ra | 4 +- lib/puppet/pops/parser/eparser.rb | 68 +++++++++---------- spec/unit/parser/functions/epp_spec.rb | 4 +- spec/unit/parser/functions/inline_epp_spec.rb | 6 +- spec/unit/pops/parser/epp_parser_spec.rb | 6 +- 5 files changed, 44 insertions(+), 44 deletions(-) diff --git a/lib/puppet/pops/parser/egrammar.ra b/lib/puppet/pops/parser/egrammar.ra index 69f0e3555..bb19cdcc8 100644 --- a/lib/puppet/pops/parser/egrammar.ra +++ b/lib/puppet/pops/parser/egrammar.ra @@ -671,8 +671,8 @@ epp_expression epp_parameters_list : =LOW{ result = nil } - | LPAREN RPAREN { result = [] } - | LPAREN parameters endcomma RPAREN { result = val[1] } + | PIPE PIPE { result = [] } + | PIPE parameters endcomma PIPE { result = val[1] } epp_render_expression : RENDER_STRING { result = Factory.RENDER_STRING(val[0][:value]); loc result, val[0] } diff --git a/lib/puppet/pops/parser/eparser.rb b/lib/puppet/pops/parser/eparser.rb index a799180d5..d232f3a65 100644 --- a/lib/puppet/pops/parser/eparser.rb +++ b/lib/puppet/pops/parser/eparser.rb @@ -30,13 +30,13 @@ module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 737) ##### State transition tables begin ### clist = [ -'57,59,-132,269,51,259,53,-130,79,-213,-222,126,259,126,79,125,257,125', -'307,292,223,346,102,14,106,223,101,223,102,41,106,48,101,50,45,234,49', +'57,59,-132,269,51,259,53,-130,79,-213,-222,126,259,126,79,125,307,125', +'356,292,223,346,102,14,106,223,101,234,102,41,106,48,101,50,45,234,49', '69,65,237,43,68,46,47,-132,270,66,13,105,-130,67,-213,-222,12,105,220', -'57,59,249,248,51,70,53,387,239,126,234,42,79,125,80,64,60,122,62,63', -'61,126,242,14,52,125,102,241,106,41,101,48,246,50,45,247,49,69,65,290', -'43,68,46,47,126,126,66,13,125,125,67,356,105,12,57,59,240,244,51,258', -'53,70,243,341,259,340,313,42,79,324,230,64,60,310,62,63,341,14,340,326', +'57,59,249,248,51,70,53,387,239,223,246,42,79,247,80,64,60,122,62,63', +'61,126,242,14,52,125,102,241,106,41,101,48,290,50,45,126,49,69,65,125', +'43,68,46,47,257,126,66,13,240,125,67,244,105,12,57,59,243,258,51,126', +'53,70,259,125,341,313,340,42,79,324,230,64,60,310,62,63,341,14,340,326', '52,219,102,41,106,48,101,50,45,328,49,69,65,72,43,68,46,47,126,309,66', '13,125,306,67,57,59,12,105,266,57,59,268,291,51,70,53,385,86,85,333', '42,79,81,82,64,60,334,62,63,80,335,223,14,52,210,102,338,106,41,101', @@ -226,13 +226,13 @@ clist = [ end clist = [ -'0,0,197,198,0,224,0,195,163,203,202,200,297,108,161,200,151,108,234', -'224,114,297,163,0,163,151,163,234,161,0,161,0,161,0,0,128,0,0,0,129', +'0,0,197,198,0,224,0,195,163,203,202,306,297,200,161,306,234,200,306', +'224,114,297,163,0,163,234,163,128,161,0,161,0,161,0,0,123,0,0,0,129', '0,0,0,0,197,198,0,0,163,195,0,203,202,0,161,114,374,374,147,147,374', -'0,374,374,129,199,123,0,107,199,163,0,0,45,0,0,0,48,137,374,0,48,107', -'137,107,374,107,374,142,374,374,142,374,374,374,256,374,374,374,374', -'306,45,374,374,306,45,374,306,107,374,5,5,131,139,5,160,5,374,139,338', -'160,338,240,374,164,261,121,374,374,236,374,374,294,5,294,265,374,113', +'0,374,374,129,151,142,0,107,142,163,0,0,45,0,0,0,108,137,374,0,108,107', +'137,107,374,107,374,256,374,374,199,374,374,374,199,374,374,374,374', +'151,45,374,374,131,45,374,139,107,374,5,5,139,160,5,48,5,374,160,48', +'338,240,338,374,164,261,121,374,374,236,374,374,294,5,294,265,374,113', '164,5,164,5,164,5,5,267,5,5,5,5,5,5,5,5,121,235,5,5,121,232,5,149,149', '5,164,231,373,373,271,223,373,5,373,373,164,164,285,5,109,164,164,5', '5,287,5,5,164,289,290,373,5,104,109,293,109,373,109,373,221,373,373', @@ -428,10 +428,10 @@ clist = [ '180,180,180,180,,180,,180,,181,180,180,180,180,,,,,,,,,,,,,,,,180,,', ',180,180,,,180,180,180,180,180,180,,180,180,179,,,,,180,,,,179,179,179', '179,179,179,,179,,179,,180,179,179,179,179,,,,,,,,,,,,,,,,179,,,,179', -'179,,,179,179,179,179,179,179,,179,179,,,,,,179,210,210,210,210,,210', -'210,210,210,210,,210,210,,179,,,,,210,210,210,268,268,268,268,,268,268', -'268,268,268,,268,268,,,210,210,,,268,268,268,263,263,263,263,,263,263', -'263,263,263,,263,263,,,268,268,,,263,263,263,,,,,,,,,,,,,,,,263,263' ] +'179,,,179,179,179,179,179,179,,179,179,,,,,,179,268,268,268,268,,268', +'268,268,268,268,,268,268,,179,,,,,268,268,268,210,210,210,210,,210,210', +'210,210,210,,210,210,,,268,268,,,210,210,210,263,263,263,263,,263,263', +'263,263,263,,263,263,,,210,210,,,263,263,263,,,,,,,,,,,,,,,,263,263' ] racc_action_check = arr = ::Array.new(6060, nil) idx = 0 clist.each do |str| @@ -446,36 +446,36 @@ racc_action_pointer = [ nil, 5378, 328, 382, 436, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 265, 200, - 241, 652, 706, 760, 814, 65, 208, nil, 41, 241, + 241, 652, 706, 760, 814, 65, 208, nil, 79, 241, nil, 1086, 1140, 1196, nil, nil, nil, nil, 1250, nil, - 160, 209, nil, 1414, nil, nil, nil, nil, nil, nil, + 160, 164, nil, 1414, nil, nil, nil, nil, nil, nil, nil, 232, 1524, 219, 1632, 1686, 1740, 1794, 1848, 1902, 1956, 2010, 2064, 2118, 2172, 2226, 2280, 2334, 2388, 2442, 2496, 2550, 2604, 2658, 2712, 2766, 2820, 2874, 2928, 2982, - 3036, 3090, 174, 3200, 183, 3308, 3362, 62, -23, 172, + 3036, 3090, 174, 3200, 183, 3308, 3362, 62, 41, 172, 4808, 3634, nil, 129, -15, 3796, 4750, nil, 4636, 4521, - 4464, 118, 4120, 41, nil, nil, nil, nil, 10, 27, - nil, 92, nil, nil, nil, nil, 4407, 71, nil, 106, - nil, 4579, 79, nil, nil, 4693, nil, 54, nil, 159, - 1360, -10, 4979, nil, 199, nil, nil, nil, nil, nil, - 108, 8, 1424, 2, 118, 986, 4184, 3534, 1260, 5453, + 4464, 118, 4120, 10, nil, nil, nil, nil, 2, 27, + nil, 84, nil, nil, nil, nil, 4407, 71, nil, 100, + nil, 4579, 57, nil, nil, 4693, nil, 54, nil, 159, + 1360, 30, 4979, nil, 199, nil, nil, nil, nil, nil, + 106, 8, 1424, 2, 118, 986, 4184, 3534, 1260, 5453, 5496, 5519, 5539, 5584, 5604, 5649, 5669, 5716, 5736, 5908, 5851, 5794, 5436, nil, nil, 274, nil, 5321, 598, 868, - 922, 257, 255, nil, nil, -4, nil, -9, -8, 29, - -25, 1134, -1, -2, nil, nil, nil, nil, nil, nil, - 5946, 5093, 207, nil, 226, nil, 227, 155, nil, 1032, + 922, 257, 255, nil, nil, -4, nil, -9, -8, 55, + -23, 1134, -1, -2, nil, nil, nil, nil, nil, nil, + 5968, 5093, 207, nil, 226, nil, 227, 155, nil, 1032, nil, 186, nil, 154, -7, nil, 1306, 3580, 3850, 4338, - 3958, 124, 122, nil, -8, 147, 121, 1057, nil, 245, - 82, 4012, nil, 3904, nil, 4284, nil, 4230, nil, nil, - nil, nil, 4174, nil, nil, nil, 83, nil, nil, 4066, - 3742, 113, nil, 5990, nil, 126, 3688, 136, 5968, 3524, + 3958, 124, 122, nil, -10, 147, 121, 1057, nil, 245, + 81, 4012, nil, 3904, nil, 4284, nil, 4230, nil, nil, + nil, nil, 4174, nil, nil, nil, 76, nil, nil, 4066, + 3742, 113, nil, 5990, nil, 126, 3688, 136, 5946, 3524, 3470, 156, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 3416, 150, nil, 174, nil, 117, 153, 3254, nil, 184, 100, 196, 178, 0, 3146, nil, - 174, 205, 179, 212, 216, nil, 64, nil, 218, 1578, + 174, 205, 179, 212, 216, nil, -25, nil, 218, 1578, 1470, nil, nil, nil, 4865, nil, nil, 4922, nil, nil, - nil, 210, 5036, 233, 976, 238, nil, nil, nil, nil, - 5150, 5207, 248, 191, nil, nil, nil, 5264, 87, nil, + nil, 166, 5036, 233, 976, 238, nil, nil, nil, nil, + 5150, 5207, 248, 191, nil, nil, nil, 5264, 88, nil, 544, 263, 241, nil, 266, 270, nil, nil, nil, 270, 277, 278, nil, 490, nil, nil, nil, 265, 283, nil, nil, 286, nil, nil, nil, nil, nil, nil, nil, nil, diff --git a/spec/unit/parser/functions/epp_spec.rb b/spec/unit/parser/functions/epp_spec.rb index ffbe0d0a8..536478ee3 100644 --- a/spec/unit/parser/functions/epp_spec.rb +++ b/spec/unit/parser/functions/epp_spec.rb @@ -44,14 +44,14 @@ describe "the epp function" do it "raises an error if required variable is not given" do scope['x'] = 'wrong one' expect { - eval_template_with_args("<%-( $x )-%><%= $x == correct %>", 'y' => 'correct') + eval_template_with_args("<%-| $x |-%><%= $x == correct %>", 'y' => 'correct') }.to raise_error(/no value given for required parameters x/) end it "raises an error if too many arguments are given" do scope['x'] = 'wrong one' expect { - eval_template_with_args("<%-( $x )-%><%= $x == correct %>", 'x' => 'correct', 'y' => 'surplus') + eval_template_with_args("<%-| $x |-%><%= $x == correct %>", 'x' => 'correct', 'y' => 'surplus') }.to raise_error(/Too many arguments: 2 for 1/) end end diff --git a/spec/unit/parser/functions/inline_epp_spec.rb b/spec/unit/parser/functions/inline_epp_spec.rb index d97f0d5ba..bbd7e8a33 100644 --- a/spec/unit/parser/functions/inline_epp_spec.rb +++ b/spec/unit/parser/functions/inline_epp_spec.rb @@ -38,20 +38,20 @@ describe "the inline_epp function" do it "gets shadowed variable if args are given and parameters are specified" do scope['x'] = 'wrong one' - eval_template_with_args("<%-( $x )-%><%= $x == correct %>", 'x' => 'correct').should == "true" + eval_template_with_args("<%-| $x |-%><%= $x == correct %>", 'x' => 'correct').should == "true" end it "raises an error if required variable is not given" do scope['x'] = 'wrong one' expect { - eval_template_with_args("<%-( $x )-%><%= $x == correct %>", 'y' => 'correct') + eval_template_with_args("<%-| $x |-%><%= $x == correct %>", 'y' => 'correct') }.to raise_error(/no value given for required parameters x/) end it "raises an error if too many arguments are given" do scope['x'] = 'wrong one' expect { - eval_template_with_args("<%-( $x )-%><%= $x == correct %>", 'x' => 'correct', 'y' => 'surplus') + eval_template_with_args("<%-| $x |-%><%= $x == correct %>", 'x' => 'correct', 'y' => 'surplus') }.to raise_error(/Too many arguments: 2 for 1/) end end diff --git a/spec/unit/pops/parser/epp_parser_spec.rb b/spec/unit/pops/parser/epp_parser_spec.rb index 951293fd3..8304f4120 100644 --- a/spec/unit/pops/parser/epp_parser_spec.rb +++ b/spec/unit/pops/parser/epp_parser_spec.rb @@ -51,15 +51,15 @@ describe "epp parser" do end it "template parameters" do - dump(parse("<%($x)%>Hello World")).should == "(lambda (parameters x) (epp (block (render-s 'Hello World'))))" + dump(parse("<%|$x|%>Hello World")).should == "(lambda (parameters x) (epp (block (render-s 'Hello World'))))" end it "template parameters with default" do - dump(parse("<%($x='cigar')%>Hello World")).should == "(lambda (parameters (= x 'cigar')) (epp (block (render-s 'Hello World'))))" + dump(parse("<%|$x='cigar'|%>Hello World")).should == "(lambda (parameters (= x 'cigar')) (epp (block (render-s 'Hello World'))))" end it "template parameters with and without default" do - dump(parse("<%($x='cigar', $y)%>Hello World")).should == "(lambda (parameters (= x 'cigar') y) (epp (block (render-s 'Hello World'))))" + dump(parse("<%|$x='cigar', $y|%>Hello World")).should == "(lambda (parameters (= x 'cigar') y) (epp (block (render-s 'Hello World'))))" end it "comments" do From 12849b5c21809f87d5779aab2929f3b580e6510b Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 8 Mar 2014 10:04:46 -0800 Subject: [PATCH 790/800] (PUP-1898) Fix broken exception raising when inline_epp gets non string This was a copy/paste error from the epp() function and it referenced the non existing "file" variable instead of the source string variable. --- lib/puppet/pops/evaluator/epp_evaluator.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/pops/evaluator/epp_evaluator.rb b/lib/puppet/pops/evaluator/epp_evaluator.rb index e5e26efb6..94b62636d 100644 --- a/lib/puppet/pops/evaluator/epp_evaluator.rb +++ b/lib/puppet/pops/evaluator/epp_evaluator.rb @@ -4,7 +4,7 @@ class Puppet::Pops::Evaluator::EppEvaluator def self.inline_epp(scope, epp_source, template_args = nil) unless epp_source.is_a? String - raise ArgumentError, "inline_epp(): the first argument must be a String with the epp source text, got a #{file.class}" + raise ArgumentError, "inline_epp(): the first argument must be a String with the epp source text, got a #{epp_source.class}" end # Parse and validate the source From a78662b8fdb97ad5a3c0b5598d7bed18a37107f3 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 8 Mar 2014 10:09:15 -0800 Subject: [PATCH 791/800] (PUP-1898) Fix reference to ArgumentException This should have been ArgumentError --- lib/puppet/pops/evaluator/epp_evaluator.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/pops/evaluator/epp_evaluator.rb b/lib/puppet/pops/evaluator/epp_evaluator.rb index 94b62636d..31e59aea7 100644 --- a/lib/puppet/pops/evaluator/epp_evaluator.rb +++ b/lib/puppet/pops/evaluator/epp_evaluator.rb @@ -79,7 +79,7 @@ class Puppet::Pops::Evaluator::EppEvaluator [{}, false] else unless template_args.is_a?(Hash) - raise ArgumentException, "#{func_name}(): the template_args must be a Hash, got a {template_args.class}" + raise ArgumentError, "#{func_name}(): the template_args must be a Hash, got a #{template_args.class}" end [template_args, true] end From cffea9c886c9876cd9ddabb9de5574ba41528bf6 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 8 Mar 2014 10:20:21 -0800 Subject: [PATCH 792/800] (maint) Remove dead code checking non existing ImportExpression ImportExpression was removed some time ago, but the validation of it remained in the 4.0 checker. --- lib/puppet/pops/validation/checker4_0.rb | 8 -------- 1 file changed, 8 deletions(-) diff --git a/lib/puppet/pops/validation/checker4_0.rb b/lib/puppet/pops/validation/checker4_0.rb index 4b3271ebe..91dee95ee 100644 --- a/lib/puppet/pops/validation/checker4_0.rb +++ b/lib/puppet/pops/validation/checker4_0.rb @@ -222,14 +222,6 @@ class Puppet::Pops::Validation::Checker4_0 rvalue(o.test) end - def check_ImportExpression(o) - o.files.each do |f| - unless f.is_a? Model::LiteralString - acceptor.accept(Issues::ILLEGAL_EXPRESSION, f, :feature => 'file name', :container => o) - end - end - end - def check_KeyedEntry(o) rvalue(o.key) rvalue(o.value) From b3f8cb024fb906c76a622115b0d8dea8cd249b57 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 8 Mar 2014 10:36:10 -0800 Subject: [PATCH 793/800] (maint) Remove dead-code check if ImportExpression is r-value ImportExpression does not exist since some time back. This is dead code. --- lib/puppet/pops/validation/checker4_0.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/puppet/pops/validation/checker4_0.rb b/lib/puppet/pops/validation/checker4_0.rb index 91dee95ee..df19b2ef4 100644 --- a/lib/puppet/pops/validation/checker4_0.rb +++ b/lib/puppet/pops/validation/checker4_0.rb @@ -452,8 +452,6 @@ class Puppet::Pops::Validation::Checker4_0 # def rvalue_Expression(o); end - def rvalue_ImportExpression(o) ; acceptor.accept(Issues::NOT_RVALUE, o) ; end - def rvalue_BlockExpression(o) ; acceptor.accept(Issues::NOT_RVALUE, o) ; end def rvalue_ResourceDefaultsExpression(o); acceptor.accept(Issues::NOT_RVALUE, o) ; end From edd3e8ea5e1ce0d95c3d7eb25a53259b41e1e975 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 8 Mar 2014 11:56:57 -0800 Subject: [PATCH 794/800] (PUP-1897) Allow EPP render expression to render a block If a block is given in <%= {} %> the block should be evaluated, and what it produces should be rendered. This commit makes this possible by: * explicitly handle the render block case * allowing {} to be validated as r-value producing --- lib/puppet/pops/parser/egrammar.ra | 1 + lib/puppet/pops/parser/eparser.rb | 936 +++++++++--------- lib/puppet/pops/validation/checker4_0.rb | 2 - spec/unit/parser/functions/inline_epp_spec.rb | 4 + 4 files changed, 481 insertions(+), 462 deletions(-) diff --git a/lib/puppet/pops/parser/egrammar.ra b/lib/puppet/pops/parser/egrammar.ra index bb19cdcc8..962559647 100644 --- a/lib/puppet/pops/parser/egrammar.ra +++ b/lib/puppet/pops/parser/egrammar.ra @@ -677,6 +677,7 @@ epp_parameters_list epp_render_expression : RENDER_STRING { result = Factory.RENDER_STRING(val[0][:value]); loc result, val[0] } | RENDER_EXPR expression { result = Factory.RENDER_EXPR(val[1]); loc result, val[0], val[1] } + | RENDER_EXPR LBRACE statements RBRACE { result = Factory.RENDER_EXPR(Factory.block_or_expression(*val[2])); loc result, val[0], val[3] } number : NUMBER { result = Factory.NUMBER(val[0][:value]) ; loc result, val[0] } name : NAME { result = Factory.QNAME_OR_NUMBER(val[0][:value]) ; loc result, val[0] } diff --git a/lib/puppet/pops/parser/eparser.rb b/lib/puppet/pops/parser/eparser.rb index d232f3a65..55acc6498 100644 --- a/lib/puppet/pops/parser/eparser.rb +++ b/lib/puppet/pops/parser/eparser.rb @@ -20,7 +20,7 @@ module Puppet module Parser class Parser < Racc::Parser -module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 737) +module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 738) # Make emacs happy # Local Variables: @@ -30,23 +30,23 @@ module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 737) ##### State transition tables begin ### clist = [ -'57,59,-132,269,51,259,53,-130,79,-213,-222,126,259,126,79,125,307,125', -'356,292,223,346,102,14,106,223,101,234,102,41,106,48,101,50,45,234,49', -'69,65,237,43,68,46,47,-132,270,66,13,105,-130,67,-213,-222,12,105,220', -'57,59,249,248,51,70,53,387,239,223,246,42,79,247,80,64,60,122,62,63', -'61,126,242,14,52,125,102,241,106,41,101,48,290,50,45,126,49,69,65,125', -'43,68,46,47,257,126,66,13,240,125,67,244,105,12,57,59,243,258,51,126', -'53,70,259,125,341,313,340,42,79,324,230,64,60,310,62,63,341,14,340,326', -'52,219,102,41,106,48,101,50,45,328,49,69,65,72,43,68,46,47,126,309,66', -'13,125,306,67,57,59,12,105,266,57,59,268,291,51,70,53,385,86,85,333', -'42,79,81,82,64,60,334,62,63,80,335,223,14,52,210,102,338,106,41,101', -'48,290,50,45,87,49,69,65,342,43,68,46,47,344,74,66,13,186,266,67,268', -'105,12,266,352,57,59,353,114,51,70,53,383,290,74,153,42,151,149,363', -'64,60,284,62,63,364,57,59,14,52,57,59,283,268,41,127,48,282,50,45,367', -'49,69,65,114,43,68,46,47,115,268,66,13,114,371,67,344,373,12,57,59,374', -'375,51,135,53,70,133,135,376,377,133,42,111,379,380,64,60,381,62,63', -'266,14,74,71,52,388,70,41,389,48,70,50,108,390,49,69,65,60,43,68,391', -'60,,,66,13,,,67,,,12,57,59,,,51,,53,70,75,77,76,78,,42,,,,64,60,,62', +'57,59,-223,-130,51,262,53,-214,79,-132,272,126,262,126,79,125,310,125', +'360,295,224,350,102,14,106,224,101,235,102,41,106,48,101,50,45,293,49', +'69,65,238,43,68,46,47,-223,-130,66,13,105,-214,67,-132,273,12,105,258', +'57,59,250,249,51,70,53,391,240,224,247,42,79,248,80,64,60,122,62,63', +'61,126,243,14,52,125,102,242,106,41,101,48,235,50,45,126,49,69,65,125', +'43,68,46,47,221,126,66,13,325,125,67,245,105,12,57,59,244,261,51,126', +'53,70,262,125,345,241,344,42,79,316,231,64,60,328,62,63,345,14,344,313', +'52,330,102,41,106,48,101,50,45,220,49,69,65,72,43,68,46,47,126,332,66', +'13,125,312,67,57,59,12,105,309,57,59,269,271,51,70,53,389,86,85,74,42', +'79,81,82,64,60,337,62,63,80,338,339,14,52,224,102,211,106,41,101,48', +'342,50,45,87,49,69,65,294,43,68,46,47,346,348,66,13,293,187,67,269,105', +'12,271,269,57,59,356,357,51,70,53,387,114,293,74,42,154,151,149,64,60', +'367,62,63,287,57,59,14,52,57,59,368,286,41,271,48,127,50,45,285,49,69', +'65,371,43,68,46,47,114,115,66,13,271,114,67,375,348,12,57,59,377,378', +'51,135,53,70,133,135,379,380,133,42,381,111,383,64,60,384,62,63,385', +'14,269,74,52,71,70,41,392,48,70,50,108,393,49,69,65,60,43,68,394,60', +'395,,66,13,,,67,,,12,57,59,,,51,,53,70,75,77,76,78,,42,,,,64,60,,62', '63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57', '59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49', '69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60', @@ -66,7 +66,7 @@ clist = [ ',,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,', '52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51', ',53,70,,,,,,42,79,,,64,60,,62,63,,14,,,52,,102,41,106,48,101,50,108', -',49,69,65,,43,68,,,,,66,13,,,67,,,12,105,,57,59,,,51,70,53,288,86,85', +',49,69,65,,43,68,,,,,66,13,,,67,,,12,105,,57,59,,,51,70,53,291,86,85', ',42,,81,82,64,60,,62,63,80,,,14,52,57,59,,,41,,48,,50,45,87,49,69,65', ',43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,138,53,70,,135,,,133,42,,', ',64,60,,62,63,,14,,,52,,,41,,48,70,50,108,,49,69,65,,43,68,,60,,,66', @@ -75,27 +75,24 @@ clist = [ '59,,,51,70,53,143,,,60,42,,,,64,60,,62,63,,,,14,52,,,,,41,,48,,50,108', ',49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,79,,', '64,60,,62,63,,14,,,52,,102,41,106,48,101,50,108,,49,69,65,,43,68,,,', -',66,13,,,67,,,12,105,,57,59,,,51,70,53,294,,,,42,,81,82,64,60,,62,63', +',66,13,,,67,,,12,105,,57,59,,,51,70,53,297,,,,42,,81,82,64,60,,62,63', '80,,,14,52,,,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12', -'57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,45,', -'49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,79', -',,64,60,,62,63,,14,,,52,,102,41,106,48,101,50,108,,49,69,65,,43,68,', -',,,66,13,,,67,,,12,105,,57,59,,,51,70,53,362,,,,42,,,,64,60,,62,63,80', -',,14,52,,,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57', -'59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,45,,49', -'69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64', -'60,,62,63,,14,,,52,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,', -'67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48', -',50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70,,', -',,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,45,,49,69,65,,43,68,46,47', -',,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52', -',,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51', -',53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,45,,49,69,65,,43', -'68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63', -',14,,,52,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57', -'59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49', -'69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60', -',62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12', +',,57,59,,,51,70,53,143,,,,42,,,,64,60,,62,63,,,,14,52,,,,,41,,48,,50', +'45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,153,70,,,,,', +'42,79,,,64,60,,62,63,,14,,,52,,102,41,106,48,101,50,108,,49,69,65,,43', +'68,,,,,66,13,,,67,,,12,105,,57,59,,,51,70,53,366,,,,42,,,,64,60,,62', +'63,80,,,14,52,,,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67', +',,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50', +'45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42', +',,,64,60,,62,63,,14,,,52,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66', +'13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41', +',48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70', +',,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,45,,49,69,65,,43,68,46', +'47,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,', +',52,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,', +',51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,45,,49,69,65', +',43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62', +'63,,14,,,52,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12', '57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108', ',49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64', '60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67', @@ -128,95 +125,102 @@ clist = [ ',,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50', '108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,', ',,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13', -',,67,,,12,,,57,59,,,51,70,53,347,,,,42,,,185,64,60,,62,63,,,,14,52,', -',,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53', -'70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,188,205,199,206,50,200,208,201', -'197,195,,190,203,,,,,66,13,209,204,202,,,12,57,59,,,51,,53,70,,,,,207', -'189,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66', -'13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41', -',48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,', -',,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,', -',,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52', -',,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53', -'70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68', -',,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,79,,,64,60,,62,63,,14', -',,52,,102,41,106,48,101,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12', -'105,,57,59,,,51,70,53,296,,,,42,,81,82,64,60,,62,63,80,,,14,52,,,,,41', -',48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70', -',,,,,42,,,,64,60,,62,63,,14,217,,52,,,41,,48,,50,108,,49,69,65,,43,68', +',,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48', +',50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,', +'42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66', +'13,,,67,,,12,,,57,59,,,51,70,53,351,,,,42,,,186,64,60,,62,63,,,,14,52', +',,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,', +'53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,189,206,200,207,50,201,209', +'202,198,196,,191,204,,,,,66,13,210,205,203,,,12,57,59,,,51,,53,70,,', +',,208,190,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68', ',,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,', '52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51', ',53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,', '43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63', -',14,225,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57', -'59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49', -'69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,316,53,70,,,,,,42,,,,64', -'60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67', -',,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50', -'108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,315,53,70,,,,,,42', -',,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13', -',,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48', -',50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,', -'42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66', -'13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,79,,,64,60,,62,63,,14,,,52,,102', -'41,106,48,101,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,105,,57,59', -',,51,70,53,318,,,,42,,81,82,64,60,,62,63,80,,,14,52,,,,,41,,48,,50,108', +',14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59', +',,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69', +'65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,79,,,64,60,', +'62,63,,14,,,52,,102,41,106,48,101,50,108,,49,69,65,,43,68,,,,,66,13', +',,67,,,12,105,,57,59,,,51,70,53,299,,,,42,,81,82,64,60,,62,63,80,,,14', +'52,,,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59', +',,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,218,,52,,,41,,48,,50,108,,49', +'69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60', +',62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12', +'57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108', ',49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64', -'60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67', -',,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,188,205,199', -'206,50,200,208,201,197,195,,190,203,,,,,66,13,209,204,202,,,12,,,,,', -',,70,,,,,207,189,,,,64,60,79,62,63,,,,,52,,98,99,100,95,90,102,,106', -',101,,,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,', -'81,82,79,,229,,,80,,,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94', -',,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,228,,,80', -',,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105', -',,,97,96,,,83,84,86,85,88,89,,81,82,,79,,,,80,245,,,,98,99,100,95,90', -'102,,106,,101,87,,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86', -'85,88,89,,81,82,79,,227,,,80,,,,98,99,100,95,90,102,,106,,101,,87,91', -'93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,', -',,,80,,,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,,,,,', -',,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,226,,,80,,,,98,99,100', -'95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83', -'84,86,85,88,89,,81,82,,79,,,,80,,,,,98,99,100,95,90,102,,106,,101,87', -'215,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81', -'82,79,,,,,80,,,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,', -',,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99', -'100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96', -',,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102,,106,,101', +'60,,62,63,,14,226,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,', +'67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48', +',50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70,,', +',,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,', +',66,13,,,67,,,12,57,59,,,51,319,53,70,,,,,,42,,,,64,60,,62,63,,14,,', +'52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51', +',53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,', +'43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63', +',14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59', +',,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69', +'65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,318,53,70,,,,,,42,,,,64,60', +',62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12', +'57,59,,,51,,53,70,,,,,,42,79,,,64,60,,62,63,,14,,,52,,102,41,106,48', +'101,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,105,,57,59,,,51,70,53', +'321,,,,42,,81,82,64,60,,62,63,80,,,14,52,,,,,41,,48,,50,108,,49,69,65', +',43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63', +',14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59', +',,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,189,206,200,207,50,201', +'209,202,198,196,,191,204,,,,,66,13,210,205,203,,,12,,,,,,,,70,,,,,208', +'190,,,,64,60,79,62,63,,,,,52,,98,99,100,95,90,102,,106,,101,,,91,93', +'92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,,79,,103', +',80,246,,,,98,99,100,95,90,102,,106,,101,87,,91,93,92,94,,,,,,,,,,,', +',,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,,79,,,,80,246,,,,98,99,100', +'95,90,102,,106,,101,87,,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83', +'84,86,85,88,89,,81,82,79,,230,,,80,,,,98,99,100,95,90,102,,106,,101', ',87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81', '82,79,,,,,80,,,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,', -',,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99', -'100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96', -',,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102,,106,,101', -',87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81', -'82,79,,,,,80,,,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,', -',,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99', -'100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96', -',,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102,264,106', +',,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,229,,,80,,,,98', +'99,100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97', +'96,,,83,84,86,85,88,89,,81,82,79,,228,,,80,,,,98,99,100,95,90,102,,106', ',101,,87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89', -',81,82,79,,103,,,80,,,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94', -',,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,,79,,,,80,260', -',,,98,99,100,95,90,102,,106,79,101,87,,91,93,92,94,,,,,,,102,,106,,101', -',,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,105,,,79,,80,,,83,84,86', -'85,,,,81,82,102,,106,87,101,80,,,,79,,,,,,,,,,,87,,,102,,106,105,101', -',79,,,,,83,84,86,85,,,,81,82,102,,106,,101,80,105,,,,,,,,83,84,86,85', -'88,89,87,81,82,,,,105,,80,,,79,,,83,84,86,85,88,89,,81,82,87,90,102', -',106,80,101,,79,91,,,,,,,,,,,87,90,102,,106,,101,,105,91,,,,,,,83,84', -'86,85,88,89,,81,82,,,,105,,80,,,79,,,83,84,86,85,88,89,,81,82,87,90', -'102,,106,80,101,,79,91,,,,,,,,,,,87,90,102,,106,,101,,105,91,,,,,,,83', -'84,86,85,88,89,,81,82,,,,105,,80,,,,,79,83,84,86,85,88,89,,81,82,87', -',95,90,102,80,106,,101,,79,91,93,92,94,,,,,,87,,95,90,102,,106,,101', -',105,91,93,92,94,,,,83,84,86,85,88,89,,81,82,,,,105,,80,,,96,,,83,84', -'86,85,88,89,,81,82,87,79,,,,80,,,,,98,99,100,95,90,102,,106,,101,87', -',91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82', -'79,,,,,80,,,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,', -',,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99,100', -'95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83', -'84,86,85,88,89,,81,82,,,,,,80,278,205,277,206,,275,208,279,273,272,', -'274,276,,87,,,,,209,204,280,278,205,277,206,,275,208,279,273,272,,274', -'276,,,207,281,,,209,204,280,278,205,277,206,,275,208,279,273,272,,274', -'276,,,207,281,,,209,204,280,,,,,,,,,,,,,,,,207,281' ] - racc_action_table = arr = ::Array.new(6060, nil) +',81,82,79,,227,,,80,,,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94', +',,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,,79,,,,80,,,', +',98,99,100,95,90,102,,106,,101,87,216,91,93,92,94,,,,,,,,,,,,,,,,105', +',,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102', +',106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85', +'88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102,,106,,101,,87,91,93,92', +'94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80', +',,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105', +',,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102', +',106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85', +'88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102,,106,,101,,87,91,93,92', +'94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80', +',,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105', +',,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102', +',106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85', +'88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102,,106,,101,,87,91,93,92', +'94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80', +',,,98,99,100,95,90,102,267,106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105', +',,,97,96,,,83,84,86,85,88,89,,81,82,79,,103,,,80,,,,98,99,100,95,90', +'102,,106,79,101,,87,91,93,92,94,,,,,,,102,,106,,101,,,,,105,,,,97,96', +',,83,84,86,85,88,89,,81,82,105,,,79,,80,,,83,84,86,85,,,,81,82,102,', +'106,87,101,80,,,,79,,,,,,,,,,,87,,,102,,106,105,101,,79,,,,,83,84,86', +'85,,,,81,82,102,,106,,101,80,105,,,,,,,,83,84,86,85,88,89,87,81,82,', +',,105,,80,,,79,,,83,84,86,85,88,89,,81,82,87,90,102,,106,80,101,,79', +'91,,,,,,,,,,,87,90,102,,106,,101,,105,91,,,,,,,83,84,86,85,88,89,,81', +'82,,,,105,,80,,,79,,,83,84,86,85,88,89,,81,82,87,90,102,,106,80,101', +',79,91,,,,,,,,,,,87,90,102,,106,,101,,105,91,,,,,,,83,84,86,85,88,89', +',81,82,,,,105,,80,,,,,79,83,84,86,85,88,89,,81,82,87,,95,90,102,80,106', +',101,,79,91,93,92,94,,,,,,87,,95,90,102,,106,,101,,105,91,93,92,94,', +',,83,84,86,85,88,89,,81,82,,,,105,,80,,,96,,,83,84,86,85,88,89,,81,82', +'87,79,,,,80,263,,,,98,99,100,95,90,102,,106,,101,87,,91,93,92,94,,,', +',,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98', +'99,100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97', +'96,,,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102,,106', +',101,,87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89', +',81,82,79,,,,,80,,,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94,,', +',,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,,,,,,80,281,206', +'280,207,,278,209,282,276,275,,277,279,,87,,,,,210,205,283,281,206,280', +'207,,278,209,282,276,275,,277,279,,,208,284,,,210,205,283,281,206,280', +'207,,278,209,282,276,275,,277,279,,,208,284,,,210,205,283,,,,,,,,,,', +',,,,,208,284' ] + racc_action_table = arr = ::Array.new(6173, nil) idx = 0 clist.each do |str| str.split(',', -1).each do |i| @@ -226,67 +230,67 @@ clist = [ end clist = [ -'0,0,197,198,0,224,0,195,163,203,202,306,297,200,161,306,234,200,306', -'224,114,297,163,0,163,234,163,128,161,0,161,0,161,0,0,123,0,0,0,129', -'0,0,0,0,197,198,0,0,163,195,0,203,202,0,161,114,374,374,147,147,374', -'0,374,374,129,151,142,0,107,142,163,0,0,45,0,0,0,108,137,374,0,108,107', -'137,107,374,107,374,256,374,374,199,374,374,374,199,374,374,374,374', -'151,45,374,374,131,45,374,139,107,374,5,5,139,160,5,48,5,374,160,48', -'338,240,338,374,164,261,121,374,374,236,374,374,294,5,294,265,374,113', -'164,5,164,5,164,5,5,267,5,5,5,5,5,5,5,5,121,235,5,5,121,232,5,149,149', -'5,164,231,373,373,271,223,373,5,373,373,164,164,285,5,109,164,164,5', -'5,287,5,5,164,289,290,373,5,104,109,293,109,373,109,373,221,373,373', -'164,373,373,373,295,373,373,373,373,296,154,373,373,102,300,373,301', -'109,373,302,303,371,371,304,217,371,373,371,371,308,73,71,373,61,60', -'321,373,373,216,373,373,323,49,49,371,373,239,239,214,325,371,46,371', -'212,371,371,332,371,371,371,333,371,371,371,371,40,192,371,371,39,341', -'371,342,344,371,185,185,345,349,185,49,185,371,49,239,350,351,239,371', -'38,357,358,371,371,361,371,371,191,185,6,1,371,378,49,185,382,185,239', -'185,185,384,185,185,185,49,185,185,386,239,,,185,185,,,185,,,185,12', -'12,,,12,,12,185,8,8,8,8,,185,,,,185,185,,185,185,,12,,,185,,,12,,12', -',12,12,,12,12,12,,12,12,,,,,12,12,,,12,,,12,13,13,,,13,,13,12,,,,,,12', -',,,12,12,,12,12,,13,,,12,,,13,,13,,13,13,,13,13,13,,13,13,,,,,13,13', -',,13,,,13,14,14,,,14,,14,13,,,,,,13,,,,13,13,,13,13,,14,,,13,,,14,,14', -',14,14,,14,14,14,,14,14,,,,,14,14,,,14,,,14,353,353,,,353,,353,14,,', -',,,14,,,,14,14,,14,14,,353,,,14,,,353,,353,,353,353,,353,353,353,,353', -'353,353,353,,,353,353,,,353,,,353,340,340,,,340,,340,353,,,,,,353,,', -',353,353,,353,353,,340,,,353,,,340,,340,,340,340,,340,340,340,,340,340', -',,,,340,340,,,340,,,340,188,188,,,188,,188,340,,,,,,340,,,,340,340,', -'340,340,,188,,,340,,,188,,188,,188,188,,188,188,188,,188,188,,,,,188', -'188,,,188,,,188,41,41,,,41,,41,188,,,,,,188,,,,188,188,,188,188,,41', -',,188,,,41,,41,,41,41,,41,41,41,,41,41,,,,,41,41,,,41,,,41,42,42,,,42', +'0,0,203,196,0,225,0,204,163,198,199,309,300,108,109,309,235,108,309', +'225,151,300,163,0,163,235,163,128,109,0,109,0,109,0,0,257,0,0,0,129', +'0,0,0,0,203,196,0,0,163,204,0,198,199,0,109,151,378,378,147,147,378', +'0,378,378,129,114,142,0,107,142,163,0,0,45,0,0,0,48,137,378,0,48,107', +'137,107,378,107,378,123,378,378,200,378,378,378,200,378,378,378,378', +'114,45,378,378,260,45,378,139,107,378,5,5,139,161,5,201,5,378,161,201', +'342,131,342,378,165,241,121,378,378,264,378,378,297,5,297,237,378,268', +'165,5,165,5,165,5,5,113,5,5,5,5,5,5,5,5,121,270,5,5,121,236,5,149,149', +'5,165,233,377,377,232,274,377,5,377,377,165,165,155,5,162,165,165,5', +'5,288,5,5,165,290,292,377,5,293,162,104,162,377,162,377,296,377,377', +'165,377,377,377,224,377,377,377,377,298,299,377,377,222,102,377,303', +'162,377,304,305,375,375,306,307,375,377,375,375,218,311,73,377,71,61', +'60,377,377,324,377,377,217,202,202,375,377,49,49,327,215,375,329,375', +'46,375,375,213,375,375,375,336,375,375,375,375,337,40,375,375,193,39', +'375,345,346,375,186,186,348,349,186,202,186,375,202,49,353,354,49,375', +'355,38,361,375,375,362,375,375,365,186,192,6,375,1,202,186,382,186,49', +'186,186,386,186,186,186,202,186,186,388,49,390,,186,186,,,186,,,186', +'12,12,,,12,,12,186,8,8,8,8,,186,,,,186,186,,186,186,,12,,,186,,,12,', +'12,,12,12,,12,12,12,,12,12,,,,,12,12,,,12,,,12,13,13,,,13,,13,12,,,', +',,12,,,,12,12,,12,12,,13,,,12,,,13,,13,,13,13,,13,13,13,,13,13,,,,,13', +'13,,,13,,,13,14,14,,,14,,14,13,,,,,,13,,,,13,13,,13,13,,14,,,13,,,14', +',14,,14,14,,14,14,14,,14,14,,,,,14,14,,,14,,,14,357,357,,,357,,357,14', +',,,,,14,,,,14,14,,14,14,,357,,,14,,,357,,357,,357,357,,357,357,357,', +'357,357,357,357,,,357,357,,,357,,,357,344,344,,,344,,344,357,,,,,,357', +',,,357,357,,357,357,,344,,,357,,,344,,344,,344,344,,344,344,344,,344', +'344,,,,,344,344,,,344,,,344,189,189,,,189,,189,344,,,,,,344,,,,344,344', +',344,344,,189,,,344,,,189,,189,,189,189,,189,189,189,,189,189,,,,,189', +'189,,,189,,,189,41,41,,,41,,41,189,,,,,,189,,,,189,189,,189,189,,41', +',,189,,,41,,41,,41,41,,41,41,41,,41,41,,,,,41,41,,,41,,,41,42,42,,,42', ',42,41,,,,,,41,,,,41,41,,41,41,,42,,,41,,,42,,42,,42,42,,42,42,42,,42', '42,,,,,42,42,,,42,,,42,43,43,,,43,,43,42,,,,,,42,,,,42,42,,42,42,,43', ',,42,,,43,,43,,43,43,,43,43,43,,43,43,,,,,43,43,,,43,,,43,44,44,,,44', ',44,43,,,,,,43,,,,43,43,,43,43,,44,,,43,,,44,,44,,44,44,,44,44,44,,44', -'44,,,,,44,44,,,44,,,44,189,189,,,189,,189,44,,,,,,44,,,,44,44,,44,44', -',189,,,44,,,189,,189,,189,189,,189,189,189,,189,189,,,,,189,189,,,189', -',,189,190,190,,,190,,190,189,,,,,,189,,,,189,189,,189,189,,190,,,189', -',,190,,190,,190,190,,190,190,190,,190,190,,,,,190,190,,,190,,,190,324', -'324,,,324,,324,190,,,,,,190,165,,,190,190,,190,190,,324,,,190,,165,324', -'165,324,165,324,324,,324,324,324,,324,324,,,,,324,324,,,324,,,324,165', -',219,219,,,219,324,219,219,165,165,,324,,165,165,324,324,,324,324,165', -',,219,324,237,237,,,219,,219,,219,219,165,219,219,219,,219,219,219,219', -',,219,219,,,219,,,219,51,51,,,51,51,51,219,,237,,,237,219,,,,219,219', -',219,219,,51,,,219,,,51,,51,237,51,51,,51,51,51,,51,51,,237,,,51,51', -'201,201,51,,,51,52,52,,,52,52,52,51,,,,,,51,,,,51,51,,51,51,,52,,,51', -',,52,,52,201,52,52,201,52,52,52,,52,52,,,,,52,52,,,52,,,52,,201,53,53', -',,53,52,53,53,,,201,52,,,,52,52,,52,52,,,,53,52,,,,,53,,53,,53,53,,53', +'44,,,,,44,44,,,44,,,44,190,190,,,190,,190,44,,,,,,44,,,,44,44,,44,44', +',190,,,44,,,190,,190,,190,190,,190,190,190,,190,190,,,,,190,190,,,190', +',,190,191,191,,,191,,191,190,,,,,,190,,,,190,190,,190,190,,191,,,190', +',,191,,191,,191,191,,191,191,191,,191,191,,,,,191,191,,,191,,,191,328', +'328,,,328,,328,191,,,,,,191,166,,,191,191,,191,191,,328,,,191,,166,328', +'166,328,166,328,328,,328,328,328,,328,328,,,,,328,328,,,328,,,328,166', +',220,220,,,220,328,220,220,166,166,,328,,166,166,328,328,,328,328,166', +',,220,328,238,238,,,220,,220,,220,220,166,220,220,220,,220,220,220,220', +',,220,220,,,220,,,220,51,51,,,51,51,51,220,,238,,,238,220,,,,220,220', +',220,220,,51,,,220,,,51,,51,238,51,51,,51,51,51,,51,51,,238,,,51,51', +'240,240,51,,,51,52,52,,,52,52,52,51,,,,,,51,,,,51,51,,51,51,,52,,,51', +',,52,,52,240,52,52,240,52,52,52,,52,52,,,,,52,52,,,52,,,52,,240,53,53', +',,53,52,53,53,,,240,52,,,,52,52,,52,52,,,,53,52,,,,,53,,53,,53,53,,53', '53,53,,53,53,,,,,53,53,,,53,,,53,58,58,,,58,,58,53,,,,,,53,168,,,53', '53,,53,53,,58,,,53,,168,58,168,58,168,58,58,,58,58,58,,58,58,,,,,58', -'58,,,58,,,58,168,,226,226,,,226,58,226,226,,,,58,,168,168,58,58,,58', -'58,168,,,226,58,,,,,226,,226,,226,226,,226,226,226,,226,226,226,226', -',,226,226,,,226,,,226,150,150,,,150,,150,226,,,,,,226,,,,226,226,,226', -'226,,150,,,226,,,150,,150,,150,150,,150,150,150,,150,150,150,150,,,150', -'150,,,150,,,150,63,63,,,63,,63,150,,,,,,150,162,,,150,150,,150,150,', -'63,,,150,,162,63,162,63,162,63,63,,63,63,63,,63,63,,,,,63,63,,,63,,', -'63,162,,310,310,,,310,63,310,310,,,,63,,,,63,63,,63,63,162,,,310,63', -',,,,310,,310,,310,310,,310,310,310,,310,310,310,310,,,310,310,,,310', -',,310,72,72,,,72,,72,310,,,,,,310,,,,310,310,,310,310,,72,,,310,,,72', -',72,,72,72,,72,72,72,,72,72,72,72,,,72,72,,,72,,,72,309,309,,,309,,309', -'72,,,,,,72,,,,72,72,,72,72,,309,,,72,,,309,,309,,309,309,,309,309,309', -',309,309,309,309,,,309,309,,,309,,,309,74,74,,,74,,74,309,,,,,,309,', -',,309,309,,309,309,,74,,,309,,,74,,74,,74,74,,74,74,74,,74,74,74,74', +'58,,,58,,,58,168,,227,227,,,227,58,227,227,,,,58,,168,168,58,58,,58', +'58,168,,,227,58,,,,,227,,227,,227,227,,227,227,227,,227,227,227,227', +',,227,227,,,227,,,227,,,153,153,,,153,227,153,153,,,,227,,,,227,227', +',227,227,,,,153,227,,,,,153,,153,,153,153,,153,153,153,,153,153,153', +'153,,,153,153,,,153,,,153,63,63,,,63,,63,153,,,,,,153,164,,,153,153', +',153,153,,63,,,153,,164,63,164,63,164,63,63,,63,63,63,,63,63,,,,,63', +'63,,,63,,,63,164,,313,313,,,313,63,313,313,,,,63,,,,63,63,,63,63,164', +',,313,63,,,,,313,,313,,313,313,,313,313,313,,313,313,313,313,,,313,313', +',,313,,,313,72,72,,,72,,72,313,,,,,,313,,,,313,313,,313,313,,72,,,313', +',,72,,72,,72,72,,72,72,72,,72,72,72,72,,,72,72,,,72,,,72,312,312,,,312', +',312,72,,,,,,72,,,,72,72,,72,72,,312,,,72,,,312,,312,,312,312,,312,312', +'312,,312,312,312,312,,,312,312,,,312,,,312,74,74,,,74,,74,312,,,,,,312', +',,,312,312,,312,312,,74,,,312,,,74,,74,,74,74,,74,74,74,,74,74,74,74', ',,74,74,,,74,,,74,75,75,,,75,,75,74,,,,,,74,,,,74,74,,74,74,,75,,,74', ',,75,,75,,75,75,,75,75,75,,75,75,75,75,,,75,75,,,75,,,75,76,76,,,76', ',76,75,,,,,,75,,,,75,75,,75,75,,76,,,75,,,76,,76,,76,76,,76,76,76,,76', @@ -328,111 +332,115 @@ clist = [ ',,99,,,99,100,100,,,100,,100,99,,,,,,99,,,,99,99,,99,99,,100,,,99,,', '100,,100,,100,100,,100,100,100,,100,100,,,,,100,100,,,100,,,100,101', '101,,,101,,101,100,,,,,,100,,,,100,100,,100,100,,101,,,100,,,101,,101', -',101,101,,101,101,101,,101,101,,,,,101,101,,,101,,,101,,,298,298,,,298', -'101,298,298,,,,101,,,101,101,101,,101,101,,,,298,101,,,,,298,,298,,298', -'298,,298,298,298,,298,298,,,,,298,298,,,298,,,298,103,103,,,103,,103', -'298,,,,,,298,,,,298,298,,298,298,,103,,,298,,,103,103,103,103,103,103', -'103,103,103,103,,103,103,,,,,103,103,103,103,103,,,103,291,291,,,291', -',291,103,,,,,103,103,,,,103,103,,103,103,,291,,,103,,,291,,291,,291', -'291,,291,291,291,,291,291,,,,,291,291,,,291,,,291,105,105,,,105,,105', -'291,,,,,,291,,,,291,291,,291,291,,105,,,291,,,105,,105,,105,105,,105', +',101,101,,101,101,101,,101,101,,,,,101,101,,,101,,,101,,,301,301,,,301', +'101,301,301,,,,101,,,101,101,101,,101,101,,,,301,101,,,,,301,,301,,301', +'301,,301,301,301,,301,301,,,,,301,301,,,301,,,301,103,103,,,103,,103', +'301,,,,,,301,,,,301,301,,301,301,,103,,,301,,,103,103,103,103,103,103', +'103,103,103,103,,103,103,,,,,103,103,103,103,103,,,103,294,294,,,294', +',294,103,,,,,103,103,,,,103,103,,103,103,,294,,,103,,,294,,294,,294', +'294,,294,294,294,,294,294,,,,,294,294,,,294,,,294,105,105,,,105,,105', +'294,,,,,,294,,,,294,294,,294,294,,105,,,294,,,105,,105,,105,105,,105', '105,105,,105,105,,,,,105,105,,,105,,,105,106,106,,,106,,106,105,,,,', ',105,,,,105,105,,105,105,,106,,,105,,,106,,106,,106,106,,106,106,106', -',106,106,,,,,106,106,,,106,,,106,284,284,,,284,,284,106,,,,,,106,,,', -'106,106,,106,106,,284,,,106,,,284,,284,,284,284,,284,284,284,,284,284', -',,,,284,284,,,284,,,284,270,270,,,270,,270,284,,,,,,284,,,,284,284,', -'284,284,,270,,,284,,,270,,270,,270,270,,270,270,270,,270,270,,,,,270', -'270,,,270,,,270,269,269,,,269,,269,270,,,,,,270,167,,,270,270,,270,270', -',269,,,270,,167,269,167,269,167,269,269,,269,269,269,,269,269,,,,,269', -'269,,,269,,,269,167,,227,227,,,227,269,227,227,,,,269,,167,167,269,269', -',269,269,167,,,227,269,,,,,227,,227,,227,227,,227,227,227,,227,227,227', -'227,,,227,227,,,227,,,227,111,111,,,111,,111,227,,,,,,227,,,,227,227', -',227,227,,111,111,,227,,,111,,111,,111,111,,111,111,111,,111,111,,,', -',111,111,,,111,,,111,266,266,,,266,,266,111,,,,,,111,,,,111,111,,111', -'111,,266,,,111,,,266,,266,,266,266,,266,266,266,,266,266,,,,,266,266', -',,266,,,266,260,260,,,260,,260,266,,,,,,266,,,,266,266,,266,266,,260', -',,266,,,260,,260,,260,260,,260,260,260,,260,260,,,,,260,260,,,260,,', -'260,115,115,,,115,,115,260,,,,,,260,,,,260,260,,260,260,,115,115,,260', -',,115,,115,,115,115,,115,115,115,,115,115,,,,,115,115,,,115,,,115,228', -'228,,,228,,228,115,,,,,,115,,,,115,115,,115,115,,228,,,115,,,228,,228', -',228,228,,228,228,228,,228,228,,,,,228,228,,,228,,,228,243,243,,,243', -'243,243,228,,,,,,228,,,,228,228,,228,228,,243,,,228,,,243,,243,,243', -'243,,243,243,243,,243,243,,,,,243,243,,,243,,,243,230,230,,,230,,230', -'243,,,,,,243,,,,243,243,,243,243,,230,,,243,,,230,,230,,230,230,,230', -'230,230,,230,230,,,,,230,230,,,230,,,230,241,241,,,241,241,241,230,', -',,,,230,,,,230,230,,230,230,,241,,,230,,,241,,241,,241,241,,241,241', -'241,,241,241,,,,,241,241,,,241,,,241,259,259,,,259,,259,241,,,,,,241', -',,,241,241,,241,241,,259,,,241,,,259,,259,,259,259,,259,259,259,,259', -'259,,,,,259,259,,,259,,,259,122,122,,,122,,122,259,,,,,,259,,,,259,259', -',259,259,,122,,,259,,,122,,122,,122,122,,122,122,122,,122,122,,,,,122', -'122,,,122,,,122,252,252,,,252,,252,122,,,,,,122,166,,,122,122,,122,122', -',252,,,122,,166,252,166,252,166,252,252,,252,252,252,,252,252,,,,,252', -'252,,,252,,,252,166,,247,247,,,247,252,247,247,,,,252,,166,166,252,252', -',252,252,166,,,247,252,,,,,247,,247,,247,247,,247,247,247,,247,247,', -',,,247,247,,,247,,,247,245,245,,,245,,245,247,,,,,,247,,,,247,247,,247', -'247,,245,,,247,,,245,,245,,245,245,,245,245,245,,245,245,,,,,245,245', -',,245,,,245,229,229,,,229,,229,245,,,,,,245,,,,245,245,,245,245,,229', -',,245,,,229,229,229,229,229,229,229,229,229,229,,229,229,,,,,229,229', -'229,229,229,,,229,,,,,,,,229,,,,,229,229,,,,229,229,136,229,229,,,,', -'229,,136,136,136,136,136,136,,136,,136,,,136,136,136,136,,,,,,,,,,,', -',,,,136,,,,136,136,,,136,136,136,136,136,136,,136,136,120,,120,,,136', -',,,120,120,120,120,120,120,,120,,120,,136,120,120,120,120,,,,,,,,,,', -',,,,,120,,,,120,120,,,120,120,120,120,120,120,,120,120,119,,119,,,120', -',,,119,119,119,119,119,119,,119,,119,,120,119,119,119,119,,,,,,,,,,', -',,,,,119,,,,119,119,,,119,119,119,119,119,119,,119,119,,141,,,,119,141', -',,,141,141,141,141,141,141,,141,,141,119,,141,141,141,141,,,,,,,,,,', -',,,,,141,,,,141,141,,,141,141,141,141,141,141,,141,141,118,,118,,,141', -',,,118,118,118,118,118,118,,118,,118,,141,118,118,118,118,,,,,,,,,,', -',,,,,118,,,,118,118,,,118,118,118,118,118,118,,118,118,145,,,,,118,', -',,145,145,145,145,145,145,,145,,145,,118,145,145,145,145,,,,,,,,,,,', -',,,,145,,,,145,145,,,145,145,145,145,145,145,,145,145,116,,116,,,145', -',,,116,116,116,116,116,116,,116,,116,,145,116,116,116,116,,,,,,,,,,', -',,,,,116,,,,116,116,,,116,116,116,116,116,116,,116,116,,110,,,,116,', -',,,110,110,110,110,110,110,,110,,110,116,110,110,110,110,110,,,,,,,', -',,,,,,,,110,,,,110,110,,,110,110,110,110,110,110,,110,110,314,,,,,110', -',,,314,314,314,314,314,314,,314,,314,,110,314,314,314,314,,,,,,,,,,', -',,,,,314,,,,314,314,,,314,314,314,314,314,314,,314,314,317,,,,,314,', -',,317,317,317,317,317,317,,317,,317,,314,317,317,317,317,,,,,,,,,,,', -',,,,317,,,,317,317,,,317,317,317,317,317,317,,317,317,152,,,,,317,,', -',152,152,152,152,152,152,,152,,152,,317,152,152,152,152,,,,,,,,,,,,', -',,,152,,,,152,152,,,152,152,152,152,152,152,,152,152,322,,,,,152,,,', -'322,322,322,322,322,322,,322,,322,,152,322,322,322,322,,,,,,,,,,,,,', -',,322,,,,322,322,,,322,322,322,322,322,322,,322,322,211,,,,,322,,,,211', -'211,211,211,211,211,,211,,211,,322,211,211,211,211,,,,,,,,,,,,,,,,211', -',,,211,211,,,211,211,211,211,211,211,,211,211,330,,,,,211,,,,330,330', -'330,330,330,330,,330,,330,,211,330,330,330,330,,,,,,,,,,,,,,,,330,,', -',330,330,,,330,330,330,330,330,330,,330,330,331,,,,,330,,,,331,331,331', -'331,331,331,,331,,331,,330,331,331,331,331,,,,,,,,,,,,,,,,331,,,,331', -'331,,,331,331,331,331,331,331,,331,331,337,,,,,331,,,,337,337,337,337', -'337,337,,337,,337,,331,337,337,337,337,,,,,,,,,,,,,,,,337,,,,337,337', -',,337,337,337,337,337,337,,337,337,187,,,,,337,,,,187,187,187,187,187', -'187,187,187,,187,,337,187,187,187,187,,,,,,,,,,,,,,,,187,,,,187,187', -',,187,187,187,187,187,187,,187,187,11,,11,,,187,,,,11,11,11,11,11,11', -',11,,11,,187,11,11,11,11,,,,,,,,,,,,,,,,11,,,,11,11,,,11,11,11,11,11', -'11,,11,11,,182,,,,11,182,,,,182,182,182,182,182,182,,182,169,182,11', -',182,182,182,182,,,,,,,169,,169,,169,,,,,182,,,,182,182,,,182,182,182', -'182,182,182,,182,182,169,,,170,,182,,,169,169,169,169,,,,169,169,170', -',170,182,170,169,,,,171,,,,,,,,,,,169,,,171,,171,170,171,,172,,,,,170', -'170,170,170,,,,170,170,172,,172,,172,170,171,,,,,,,,171,171,171,171', -'171,171,170,171,171,,,,172,,171,,,173,,,172,172,172,172,172,172,,172', -'172,171,173,173,,173,172,173,,174,173,,,,,,,,,,,172,174,174,,174,,174', -',173,174,,,,,,,173,173,173,173,173,173,,173,173,,,,174,,173,,,175,,', -'174,174,174,174,174,174,,174,174,173,175,175,,175,174,175,,176,175,', -',,,,,,,,,174,176,176,,176,,176,,175,176,,,,,,,175,175,175,175,175,175', -',175,175,,,,176,,175,,,,,177,176,176,176,176,176,176,,176,176,175,,177', -'177,177,176,177,,177,,178,177,177,177,177,,,,,,176,,178,178,178,,178', -',178,,177,178,178,178,178,,,,177,177,177,177,177,177,,177,177,,,,178', -',177,,,178,,,178,178,178,178,178,178,,178,178,177,181,,,,178,,,,,181', -'181,181,181,181,181,,181,,181,178,,181,181,181,181,,,,,,,,,,,,,,,,181', -',,,181,181,,,181,181,181,181,181,181,,181,181,180,,,,,181,,,,180,180', -'180,180,180,180,,180,,180,,181,180,180,180,180,,,,,,,,,,,,,,,,180,,', -',180,180,,,180,180,180,180,180,180,,180,180,179,,,,,180,,,,179,179,179', -'179,179,179,,179,,179,,180,179,179,179,179,,,,,,,,,,,,,,,,179,,,,179', -'179,,,179,179,179,179,179,179,,179,179,,,,,,179,268,268,268,268,,268', -'268,268,268,268,,268,268,,179,,,,,268,268,268,210,210,210,210,,210,210', -'210,210,210,,210,210,,,268,268,,,210,210,210,263,263,263,263,,263,263', -'263,263,263,,263,263,,,210,210,,,263,263,263,,,,,,,,,,,,,,,,263,263' ] - racc_action_check = arr = ::Array.new(6060, nil) +',106,106,,,,,106,106,,,106,,,106,287,287,,,287,,287,106,,,,,,106,,,', +'106,106,,106,106,,287,,,106,,,287,,287,,287,287,,287,287,287,,287,287', +',,,,287,287,,,287,,,287,273,273,,,273,,273,287,,,,,,287,,,,287,287,', +'287,287,,273,,,287,,,273,,273,,273,273,,273,273,273,,273,273,,,,,273', +'273,,,273,,,273,272,272,,,272,,272,273,,,,,,273,167,,,273,273,,273,273', +',272,,,273,,167,272,167,272,167,272,272,,272,272,272,,272,272,,,,,272', +'272,,,272,,,272,167,,228,228,,,228,272,228,228,,,,272,,167,167,272,272', +',272,272,167,,,228,272,,,,,228,,228,,228,228,,228,228,228,,228,228,228', +'228,,,228,228,,,228,,,228,111,111,,,111,,111,228,,,,,,228,,,,228,228', +',228,228,,111,111,,228,,,111,,111,,111,111,,111,111,111,,111,111,,,', +',111,111,,,111,,,111,269,269,,,269,,269,111,,,,,,111,,,,111,111,,111', +'111,,269,,,111,,,269,,269,,269,269,,269,269,269,,269,269,,,,,269,269', +',,269,,,269,263,263,,,263,,263,269,,,,,,269,,,,269,269,,269,269,,263', +',,269,,,263,,263,,263,263,,263,263,263,,263,263,,,,,263,263,,,263,,', +'263,115,115,,,115,,115,263,,,,,,263,,,,263,263,,263,263,,115,115,,263', +',,115,,115,,115,115,,115,115,115,,115,115,,,,,115,115,,,115,,,115,150', +'150,,,150,,150,115,,,,,,115,,,,115,115,,115,115,,150,,,115,,,150,,150', +',150,150,,150,150,150,,150,150,150,150,,,150,150,,,150,,,150,229,229', +',,229,,229,150,,,,,,150,,,,150,150,,150,150,,229,,,150,,,229,,229,,229', +'229,,229,229,229,,229,229,,,,,229,229,,,229,,,229,244,244,,,244,244', +'244,229,,,,,,229,,,,229,229,,229,229,,244,,,229,,,244,,244,,244,244', +',244,244,244,,244,244,,,,,244,244,,,244,,,244,231,231,,,231,,231,244', +',,,,,244,,,,244,244,,244,244,,231,,,244,,,231,,231,,231,231,,231,231', +'231,,231,231,,,,,231,231,,,231,,,231,262,262,,,262,,262,231,,,,,,231', +',,,231,231,,231,231,,262,,,231,,,262,,262,,262,262,,262,262,262,,262', +'262,,,,,262,262,,,262,,,262,122,122,,,122,,122,262,,,,,,262,,,,262,262', +',262,262,,122,,,262,,,122,,122,,122,122,,122,122,122,,122,122,,,,,122', +'122,,,122,,,122,242,242,,,242,242,242,122,,,,,,122,,,,122,122,,122,122', +',242,,,122,,,242,,242,,242,242,,242,242,242,,242,242,,,,,242,242,,,242', +',,242,253,253,,,253,,253,242,,,,,,242,169,,,242,242,,242,242,,253,,', +'242,,169,253,169,253,169,253,253,,253,253,253,,253,253,,,,,253,253,', +',253,,,253,169,,248,248,,,248,253,248,248,,,,253,,169,169,253,253,,253', +'253,169,,,248,253,,,,,248,,248,,248,248,,248,248,248,,248,248,,,,,248', +'248,,,248,,,248,246,246,,,246,,246,248,,,,,,248,,,,248,248,,248,248', +',246,,,248,,,246,,246,,246,246,,246,246,246,,246,246,,,,,246,246,,,246', +',,246,230,230,,,230,,230,246,,,,,,246,,,,246,246,,246,246,,230,,,246', +',,230,230,230,230,230,230,230,230,230,230,,230,230,,,,,230,230,230,230', +'230,,,230,,,,,,,,230,,,,,230,230,,,,230,230,136,230,230,,,,,230,,136', +'136,136,136,136,136,,136,,136,,,136,136,136,136,,,,,,,,,,,,,,,,136,', +',,136,136,,,136,136,136,136,136,136,,136,136,,259,,259,,136,259,,,,259', +'259,259,259,259,259,,259,,259,136,,259,259,259,259,,,,,,,,,,,,,,,,259', +',,,259,259,,,259,259,259,259,259,259,,259,259,,141,,,,259,141,,,,141', +'141,141,141,141,141,,141,,141,259,,141,141,141,141,,,,,,,,,,,,,,,,141', +',,,141,141,,,141,141,141,141,141,141,,141,141,120,,120,,,141,,,,120', +'120,120,120,120,120,,120,,120,,141,120,120,120,120,,,,,,,,,,,,,,,,120', +',,,120,120,,,120,120,120,120,120,120,,120,120,145,,,,,120,,,,145,145', +'145,145,145,145,,145,,145,,120,145,145,145,145,,,,,,,,,,,,,,,,145,,', +',145,145,,,145,145,145,145,145,145,,145,145,119,,119,,,145,,,,119,119', +'119,119,119,119,,119,,119,,145,119,119,119,119,,,,,,,,,,,,,,,,119,,', +',119,119,,,119,119,119,119,119,119,,119,119,118,,118,,,119,,,,118,118', +'118,118,118,118,,118,,118,,119,118,118,118,118,,,,,,,,,,,,,,,,118,,', +',118,118,,,118,118,118,118,118,118,,118,118,116,,116,,,118,,,,116,116', +'116,116,116,116,,116,,116,,118,116,116,116,116,,,,,,,,,,,,,,,,116,,', +',116,116,,,116,116,116,116,116,116,,116,116,,110,,,,116,,,,,110,110', +'110,110,110,110,,110,,110,116,110,110,110,110,110,,,,,,,,,,,,,,,,110', +',,,110,110,,,110,110,110,110,110,110,,110,110,152,,,,,110,,,,152,152', +'152,152,152,152,,152,,152,,110,152,152,152,152,,,,,,,,,,,,,,,,152,,', +',152,152,,,152,152,152,152,152,152,,152,152,317,,,,,152,,,,317,317,317', +'317,317,317,,317,,317,,152,317,317,317,317,,,,,,,,,,,,,,,,317,,,,317', +'317,,,317,317,317,317,317,317,,317,317,320,,,,,317,,,,320,320,320,320', +'320,320,,320,,320,,317,320,320,320,320,,,,,,,,,,,,,,,,320,,,,320,320', +',,320,320,320,320,320,320,,320,320,326,,,,,320,,,,326,326,326,326,326', +'326,,326,,326,,320,326,326,326,326,,,,,,,,,,,,,,,,326,,,,326,326,,,326', +'326,326,326,326,326,,326,326,212,,,,,326,,,,212,212,212,212,212,212', +',212,,212,,326,212,212,212,212,,,,,,,,,,,,,,,,212,,,,212,212,,,212,212', +'212,212,212,212,,212,212,334,,,,,212,,,,334,334,334,334,334,334,,334', +',334,,212,334,334,334,334,,,,,,,,,,,,,,,,334,,,,334,334,,,334,334,334', +'334,334,334,,334,334,335,,,,,334,,,,335,335,335,335,335,335,,335,,335', +',334,335,335,335,335,,,,,,,,,,,,,,,,335,,,,335,335,,,335,335,335,335', +'335,335,,335,335,341,,,,,335,,,,341,341,341,341,341,341,,341,,341,,335', +'341,341,341,341,,,,,,,,,,,,,,,,341,,,,341,341,,,341,341,341,341,341', +'341,,341,341,188,,,,,341,,,,188,188,188,188,188,188,188,188,,188,,341', +'188,188,188,188,,,,,,,,,,,,,,,,188,,,,188,188,,,188,188,188,188,188', +'188,,188,188,11,,11,,,188,,,,11,11,11,11,11,11,,11,170,11,,188,11,11', +'11,11,,,,,,,170,,170,,170,,,,,11,,,,11,11,,,11,11,11,11,11,11,,11,11', +'170,,,171,,11,,,170,170,170,170,,,,170,170,171,,171,11,171,170,,,,172', +',,,,,,,,,,170,,,172,,172,171,172,,173,,,,,171,171,171,171,,,,171,171', +'173,,173,,173,171,172,,,,,,,,172,172,172,172,172,172,171,172,172,,,', +'173,,172,,,174,,,173,173,173,173,173,173,,173,173,172,174,174,,174,173', +'174,,175,174,,,,,,,,,,,173,175,175,,175,,175,,174,175,,,,,,,174,174', +'174,174,174,174,,174,174,,,,175,,174,,,176,,,175,175,175,175,175,175', +',175,175,174,176,176,,176,175,176,,177,176,,,,,,,,,,,175,177,177,,177', +',177,,176,177,,,,,,,176,176,176,176,176,176,,176,176,,,,177,,176,,,', +',178,177,177,177,177,177,177,,177,177,176,,178,178,178,177,178,,178', +',179,178,178,178,178,,,,,,177,,179,179,179,,179,,179,,178,179,179,179', +'179,,,,178,178,178,178,178,178,,178,178,,,,179,,178,,,179,,,179,179', +'179,179,179,179,,179,179,178,183,,,,179,183,,,,183,183,183,183,183,183', +',183,,183,179,,183,183,183,183,,,,,,,,,,,,,,,,183,,,,183,183,,,183,183', +'183,183,183,183,,183,183,181,,,,,183,,,,181,181,181,181,181,181,,181', +',181,,183,181,181,181,181,,,,,,,,,,,,,,,,181,,,,181,181,,,181,181,181', +'181,181,181,,181,181,182,,,,,181,,,,182,182,182,182,182,182,,182,,182', +',181,182,182,182,182,,,,,,,,,,,,,,,,182,,,,182,182,,,182,182,182,182', +'182,182,,182,182,180,,,,,182,,,,180,180,180,180,180,180,,180,,180,,182', +'180,180,180,180,,,,,,,,,,,,,,,,180,,,,180,180,,,180,180,180,180,180', +'180,,180,180,,,,,,180,211,211,211,211,,211,211,211,211,211,,211,211', +',180,,,,,211,211,211,271,271,271,271,,271,271,271,271,271,,271,271,', +',211,211,,,271,271,271,266,266,266,266,,266,266,266,266,266,,266,266', +',,271,271,,,266,266,266,,,,,,,,,,,,,,,,266,266' ] + racc_action_check = arr = ::Array.new(6173, nil) idx = 0 clist.each do |str| str.split(',', -1).each do |i| @@ -442,189 +450,189 @@ clist = [ end racc_action_pointer = [ - -2, 301, nil, nil, nil, 108, 288, nil, 274, nil, - nil, 5378, 328, 382, 436, nil, nil, nil, nil, nil, + -2, 303, nil, nil, nil, 108, 289, nil, 274, nil, + nil, 5492, 328, 382, 436, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, 265, 200, - 241, 652, 706, 760, 814, 65, 208, nil, 79, 241, + nil, nil, nil, nil, nil, nil, nil, nil, 266, 201, + 242, 652, 706, 760, 814, 65, 210, nil, 41, 245, nil, 1086, 1140, 1196, nil, nil, nil, nil, 1250, nil, - 160, 164, nil, 1414, nil, nil, nil, nil, nil, nil, - nil, 232, 1524, 219, 1632, 1686, 1740, 1794, 1848, 1902, - 1956, 2010, 2064, 2118, 2172, 2226, 2280, 2334, 2388, 2442, - 2496, 2550, 2604, 2658, 2712, 2766, 2820, 2874, 2928, 2982, - 3036, 3090, 174, 3200, 183, 3308, 3362, 62, 41, 172, - 4808, 3634, nil, 129, -15, 3796, 4750, nil, 4636, 4521, - 4464, 118, 4120, 10, nil, nil, nil, nil, 2, 27, - nil, 84, nil, nil, nil, nil, 4407, 71, nil, 100, + 161, 165, nil, 1416, nil, nil, nil, nil, nil, nil, + nil, 234, 1526, 220, 1634, 1688, 1742, 1796, 1850, 1904, + 1958, 2012, 2066, 2120, 2174, 2228, 2282, 2336, 2390, 2444, + 2498, 2552, 2606, 2660, 2714, 2768, 2822, 2876, 2930, 2984, + 3038, 3092, 175, 3202, 185, 3310, 3364, 62, -23, 8, + 4922, 3636, nil, 137, 30, 3798, 4864, nil, 4807, 4750, + 4636, 118, 4122, 63, nil, nil, nil, nil, 2, 27, + nil, 101, nil, nil, nil, nil, 4463, 71, nil, 100, nil, 4579, 57, nil, nil, 4693, nil, 54, nil, 159, - 1360, 30, 4979, nil, 199, nil, nil, nil, nil, nil, - 106, 8, 1424, 2, 118, 986, 4184, 3534, 1260, 5453, - 5496, 5519, 5539, 5584, 5604, 5649, 5669, 5716, 5736, 5908, - 5851, 5794, 5436, nil, nil, 274, nil, 5321, 598, 868, - 922, 257, 255, nil, nil, -4, nil, -9, -8, 55, - -23, 1134, -1, -2, nil, nil, nil, nil, nil, nil, - 5968, 5093, 207, nil, 226, nil, 227, 155, nil, 1032, - nil, 186, nil, 154, -7, nil, 1306, 3580, 3850, 4338, - 3958, 124, 122, nil, -10, 147, 121, 1057, nil, 245, - 81, 4012, nil, 3904, nil, 4284, nil, 4230, nil, nil, - nil, nil, 4174, nil, nil, nil, 76, nil, nil, 4066, - 3742, 113, nil, 5990, nil, 126, 3688, 136, 5946, 3524, - 3470, 156, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, 3416, 150, nil, 174, nil, 117, - 153, 3254, nil, 184, 100, 196, 178, 0, 3146, nil, - 174, 205, 179, 212, 216, nil, -25, nil, 218, 1578, - 1470, nil, nil, nil, 4865, nil, nil, 4922, nil, nil, - nil, 166, 5036, 233, 976, 238, nil, nil, nil, nil, - 5150, 5207, 248, 191, nil, nil, nil, 5264, 88, nil, - 544, 263, 241, nil, 266, 270, nil, nil, nil, 270, - 277, 278, nil, 490, nil, nil, nil, 265, 283, nil, - nil, 286, nil, nil, nil, nil, nil, nil, nil, nil, - nil, 220, nil, 164, 54, nil, nil, nil, 294, nil, - nil, nil, 297, nil, 302, nil, 309, nil, nil, nil, - nil, nil ] + 3852, -15, 4979, 1362, nil, 164, nil, nil, nil, nil, + nil, 106, 172, 2, 1426, 118, 986, 3536, 1260, 4240, + 5509, 5552, 5575, 5595, 5640, 5660, 5705, 5725, 5772, 5792, + 6021, 5907, 5964, 5850, nil, nil, 274, nil, 5435, 598, + 868, 922, 259, 258, nil, nil, -8, nil, -2, -1, + 55, 79, 241, -9, -4, nil, nil, nil, nil, nil, + nil, 6059, 5207, 210, nil, 227, nil, 230, 160, nil, + 1032, nil, 202, nil, 190, -7, nil, 1306, 3582, 3906, + 4394, 4014, 127, 128, nil, -10, 151, 127, 1057, nil, + 1134, 85, 4176, nil, 3960, nil, 4340, nil, 4286, nil, + nil, nil, nil, 4230, nil, nil, nil, 23, nil, 4521, + 95, nil, 4068, 3744, 117, nil, 6103, nil, 128, 3690, + 146, 6081, 3526, 3472, 157, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, 3418, 157, nil, + 178, nil, 118, 156, 3256, nil, 189, 100, 201, 179, + 0, 3148, nil, 176, 208, 180, 215, 217, nil, -25, + nil, 219, 1580, 1472, nil, nil, nil, 5036, nil, nil, + 5093, nil, nil, nil, 169, nil, 5150, 240, 976, 240, + nil, nil, nil, nil, 5264, 5321, 252, 196, nil, nil, + nil, 5378, 88, nil, 544, 265, 242, nil, 270, 271, + nil, nil, nil, 277, 278, 281, nil, 490, nil, nil, + nil, 266, 286, nil, nil, 289, nil, nil, nil, nil, + nil, nil, nil, nil, nil, 220, nil, 164, 54, nil, + nil, nil, 297, nil, nil, nil, 302, nil, 309, nil, + 311, nil, nil, nil, nil, nil ] racc_action_default = [ - -224, -225, -1, -2, -3, -4, -5, -8, -10, -11, - -16, -107, -225, -225, -225, -45, -46, -47, -48, -49, + -225, -226, -1, -2, -3, -4, -5, -8, -10, -11, + -16, -107, -226, -226, -226, -45, -46, -47, -48, -49, -50, -51, -52, -53, -54, -55, -56, -57, -58, -59, -60, -61, -62, -63, -64, -65, -66, -67, -72, -73, - -77, -225, -225, -225, -225, -225, -118, -120, -225, -225, - -165, -225, -225, -225, -178, -179, -180, -181, -225, -183, - -225, -194, -197, -225, -199, -200, -201, -202, -203, -204, - -205, -225, -225, -7, -225, -225, -225, -225, -225, -225, - -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, - -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, - -225, -225, -225, -127, -122, -224, -224, -28, -225, -35, - -225, -225, -74, -225, -225, -225, -225, -84, -225, -225, - -225, -225, -225, -224, -137, -156, -157, -119, -224, -224, - -146, -148, -149, -150, -151, -152, -43, -225, -168, -225, - -171, -225, -225, -174, -175, -187, -182, -225, -190, -225, - -225, -225, -198, 392, -6, -9, -12, -13, -14, -15, - -225, -18, -19, -20, -21, -22, -23, -24, -25, -26, - -27, -29, -30, -31, -32, -33, -34, -36, -37, -38, - -39, -40, -225, -41, -102, -225, -78, -225, -217, -223, - -211, -208, -206, -116, -128, -200, -131, -204, -225, -214, - -212, -220, -202, -203, -210, -215, -216, -218, -219, -221, - -127, -126, -225, -125, -225, -42, -206, -69, -79, -225, - -82, -206, -161, -164, -225, -76, -225, -225, -225, -127, - -225, -208, -224, -158, -225, -225, -225, -225, -154, -225, - -225, -225, -166, -225, -169, -225, -172, -225, -184, -185, - -186, -188, -225, -191, -192, -193, -206, -195, -17, -225, - -225, -206, -104, -127, -115, -225, -209, -225, -207, -225, - -225, -206, -130, -132, -211, -212, -213, -214, -217, -220, - -222, -223, -123, -124, -207, -225, -71, -225, -81, -225, - -207, -225, -75, -225, -87, -225, -93, -225, -225, -97, - -208, -206, -208, -225, -225, -140, -225, -159, -206, -224, - -225, -147, -155, -153, -44, -167, -170, -177, -173, -176, - -189, -225, -106, -225, -207, -206, -110, -117, -111, -129, - -133, -134, -225, -68, -80, -83, -162, -163, -87, -86, - -225, -225, -93, -92, -225, -225, -101, -96, -98, -225, - -225, -225, -113, -224, -141, -142, -143, -225, -225, -138, - -139, -225, -145, -196, -103, -105, -114, -121, -70, -85, - -88, -225, -91, -225, -225, -108, -109, -112, -225, -160, - -135, -144, -225, -90, -225, -95, -225, -100, -136, -89, - -94, -99 ] + -77, -226, -226, -226, -226, -226, -118, -120, -226, -226, + -165, -226, -226, -226, -178, -179, -180, -181, -226, -183, + -226, -194, -197, -226, -200, -201, -202, -203, -204, -205, + -206, -226, -226, -7, -226, -226, -226, -226, -226, -226, + -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, + -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, + -226, -226, -226, -127, -122, -225, -225, -28, -226, -35, + -226, -226, -74, -226, -226, -226, -226, -84, -226, -226, + -226, -226, -226, -225, -137, -156, -157, -119, -225, -225, + -146, -148, -149, -150, -151, -152, -43, -226, -168, -226, + -171, -226, -226, -174, -175, -187, -182, -226, -190, -226, + -226, -226, -198, -226, 396, -6, -9, -12, -13, -14, + -15, -226, -18, -19, -20, -21, -22, -23, -24, -25, + -26, -27, -29, -30, -31, -32, -33, -34, -36, -37, + -38, -39, -40, -226, -41, -102, -226, -78, -226, -218, + -224, -212, -209, -207, -116, -128, -201, -131, -205, -226, + -215, -213, -221, -203, -204, -211, -216, -217, -219, -220, + -222, -127, -126, -226, -125, -226, -42, -207, -69, -79, + -226, -82, -207, -161, -164, -226, -76, -226, -226, -226, + -127, -226, -209, -225, -158, -226, -226, -226, -226, -154, + -226, -226, -226, -166, -226, -169, -226, -172, -226, -184, + -185, -186, -188, -226, -191, -192, -193, -207, -195, -107, + -226, -17, -226, -226, -207, -104, -127, -115, -226, -210, + -226, -208, -226, -226, -207, -130, -132, -212, -213, -214, + -215, -218, -221, -223, -224, -123, -124, -208, -226, -71, + -226, -81, -226, -208, -226, -75, -226, -87, -226, -93, + -226, -226, -97, -209, -207, -209, -226, -226, -140, -226, + -159, -207, -225, -226, -147, -155, -153, -44, -167, -170, + -177, -173, -176, -189, -226, -199, -106, -226, -208, -207, + -110, -117, -111, -129, -133, -134, -226, -68, -80, -83, + -162, -163, -87, -86, -226, -226, -93, -92, -226, -226, + -101, -96, -98, -226, -226, -226, -113, -225, -141, -142, + -143, -226, -226, -138, -139, -226, -145, -196, -103, -105, + -114, -121, -70, -85, -88, -226, -91, -226, -226, -108, + -109, -112, -226, -160, -135, -144, -226, -90, -226, -95, + -226, -100, -136, -89, -94, -99 ] racc_goto_table = [ - 2, 112, 4, 144, 107, 109, 110, 128, 146, 192, - 134, 132, 191, 265, 221, 184, 343, 232, 1, 339, - 358, 311, 235, 312, 299, 156, 157, 158, 159, 212, - 214, 231, 267, 116, 118, 119, 120, 73, 261, 327, - 263, 345, 329, 136, 136, 141, 298, 370, 218, 304, - 145, 256, 354, 303, 236, 152, 285, 183, 336, 142, - 155, 289, 372, 369, 378, 253, 254, 3, 251, 252, - 250, 136, 161, 162, 163, 164, 165, 166, 167, 168, - 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, - 179, 180, 181, 182, 348, 187, 321, 211, 211, 262, - 148, 323, 150, 136, 154, nil, nil, 136, 137, 139, - nil, 332, nil, nil, 187, nil, 271, nil, nil, nil, - nil, nil, 349, nil, 351, 233, nil, nil, nil, nil, - 233, 238, nil, nil, 308, 301, 160, nil, 300, 302, - nil, 350, nil, nil, nil, nil, nil, nil, 357, nil, - 255, nil, nil, nil, nil, nil, nil, nil, 128, nil, - nil, nil, 134, 132, nil, 366, nil, nil, 216, 325, - nil, nil, 224, nil, nil, nil, nil, 182, nil, 286, - 116, 118, 119, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, 319, 134, 132, - 134, 132, 320, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, 287, - 136, 187, 187, nil, nil, nil, 293, 295, nil, nil, - nil, nil, nil, 314, 305, 314, nil, 317, 365, 141, - nil, nil, nil, nil, 145, nil, nil, nil, nil, nil, - nil, 314, 322, nil, nil, nil, nil, nil, 187, nil, - nil, 330, 331, nil, nil, 355, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, 314, nil, nil, nil, - nil, nil, nil, 337, nil, nil, nil, nil, nil, nil, - 136, nil, nil, nil, nil, 368, nil, nil, nil, nil, + 2, 112, 4, 146, 107, 109, 110, 128, 132, 193, + 192, 134, 222, 268, 185, 347, 343, 233, 362, 314, + 1, 315, 236, 264, 302, 157, 158, 159, 160, 232, + 270, 213, 215, 116, 118, 119, 120, 73, 349, 331, + 266, 301, 333, 136, 136, 141, 374, 219, 184, 257, + 145, 307, 358, 306, 288, 152, 237, 156, 340, 292, + 322, 373, 376, 382, 254, 255, 3, 252, 253, 251, + 148, 136, 162, 163, 164, 165, 166, 167, 168, 169, + 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 324, 188, 352, 212, 212, 265, + 150, 327, nil, 136, 155, nil, nil, 136, nil, nil, + nil, 336, nil, nil, 188, 137, 139, 274, nil, nil, + nil, nil, nil, nil, 353, 234, 355, nil, nil, nil, + 234, 239, nil, 311, nil, nil, 304, 303, 305, nil, + nil, 354, nil, 161, nil, 259, nil, nil, 361, nil, + 256, nil, nil, 260, nil, nil, nil, nil, nil, 128, + nil, 132, nil, nil, 134, nil, 370, nil, nil, nil, + nil, nil, 329, nil, nil, 217, nil, nil, 183, 225, + 289, 116, 118, 119, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, 132, 323, 132, + 134, nil, 134, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - 361, 360, nil, nil, nil, nil, 182, nil, nil, nil, + 290, 136, 188, 188, nil, nil, nil, 296, 298, nil, + nil, nil, nil, nil, 317, 308, 317, nil, 320, nil, + 141, 369, nil, nil, nil, 145, nil, nil, nil, nil, + nil, nil, nil, nil, 317, 326, nil, nil, nil, nil, + nil, 188, nil, nil, 334, 335, nil, nil, 359, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, 317, + nil, nil, nil, nil, nil, nil, 341, nil, nil, nil, + nil, nil, nil, 136, nil, nil, nil, nil, nil, 372, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, 116, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, 365, 364, nil, nil, nil, nil, nil, + 183, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, 116, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, 360, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, 364, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, 382, nil, 384, 386 ] + nil, nil, nil, nil, nil, 386, nil, 388, 390 ] racc_goto_check = [ - 2, 39, 4, 76, 10, 10, 10, 64, 81, 56, - 31, 37, 54, 55, 44, 51, 47, 65, 1, 46, - 66, 72, 65, 72, 49, 8, 8, 8, 8, 60, - 60, 54, 38, 10, 10, 10, 10, 6, 52, 57, - 58, 50, 61, 10, 10, 10, 48, 45, 43, 68, - 10, 44, 69, 55, 71, 10, 38, 13, 74, 75, - 7, 38, 47, 46, 66, 77, 78, 3, 82, 83, - 85, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 2, 39, 4, 81, 10, 10, 10, 64, 37, 56, + 54, 31, 44, 55, 51, 47, 46, 65, 66, 72, + 1, 72, 65, 52, 49, 8, 8, 8, 8, 54, + 38, 60, 60, 10, 10, 10, 10, 6, 50, 57, + 58, 48, 61, 10, 10, 10, 45, 43, 13, 44, + 10, 68, 69, 55, 38, 10, 71, 7, 74, 38, + 76, 46, 47, 66, 77, 78, 3, 82, 83, 85, + 86, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, - 10, 10, 10, 10, 49, 10, 38, 10, 10, 51, - 86, 38, 87, 10, 6, nil, nil, 10, 12, 12, - nil, 38, nil, nil, 10, nil, 56, nil, nil, nil, - nil, nil, 55, nil, 55, 4, nil, nil, nil, nil, - 4, 4, nil, nil, 44, 56, 12, nil, 54, 54, - nil, 38, nil, nil, nil, nil, nil, nil, 38, nil, - 2, nil, nil, nil, nil, nil, nil, nil, 64, nil, - nil, nil, 31, 37, nil, 38, nil, nil, 12, 56, - nil, nil, 12, nil, nil, nil, nil, 10, nil, 39, - 10, 10, 10, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, 76, 31, 37, - 31, 37, 81, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, 2, - 10, 10, 10, nil, nil, nil, 2, 2, nil, nil, - nil, nil, nil, 10, 4, 10, nil, 10, 51, 10, - nil, nil, nil, nil, 10, nil, nil, nil, nil, nil, - nil, 10, 10, nil, nil, nil, nil, nil, 10, nil, - nil, 10, 10, nil, nil, 64, nil, nil, nil, nil, + 10, 10, 10, 10, 38, 10, 49, 10, 10, 51, + 87, 38, nil, 10, 6, nil, nil, 10, nil, nil, + nil, 38, nil, nil, 10, 12, 12, 56, nil, nil, + nil, nil, nil, nil, 55, 4, 55, nil, nil, nil, + 4, 4, nil, 44, nil, nil, 56, 54, 54, nil, + nil, 38, nil, 12, nil, 10, nil, nil, 38, nil, + 2, nil, nil, 2, nil, nil, nil, nil, nil, 64, + nil, 37, nil, nil, 31, nil, 38, nil, nil, nil, + nil, nil, 56, nil, nil, 12, nil, nil, 10, 12, + 39, 10, 10, 10, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, 37, 81, 37, + 31, nil, 31, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + 2, 10, 10, 10, nil, nil, nil, 2, 2, nil, + nil, nil, nil, nil, 10, 4, 10, nil, 10, nil, + 10, 51, nil, nil, nil, 10, nil, nil, nil, nil, + nil, nil, nil, nil, 10, 10, nil, nil, nil, nil, + nil, 10, nil, nil, 10, 10, nil, nil, 64, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, 10, nil, nil, nil, nil, nil, nil, 10, nil, nil, nil, - nil, nil, nil, 10, nil, nil, nil, nil, nil, nil, - 10, nil, nil, nil, nil, 39, nil, nil, nil, nil, + nil, nil, nil, 10, nil, nil, nil, nil, nil, 39, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - 2, 4, nil, nil, nil, nil, 10, nil, nil, nil, + nil, nil, nil, 2, 4, nil, nil, nil, nil, nil, + 10, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, 10, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, 10, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, 4, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, 4, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, 2, nil, 2, 2 ] + nil, nil, nil, nil, nil, 2, nil, 2, 2 ] racc_goto_pointer = [ - nil, 18, 0, 67, 2, nil, 32, -14, -50, nil, - -8, nil, 57, -44, nil, nil, nil, nil, nil, nil, + nil, 20, 0, 66, 2, nil, 32, -17, -50, nil, + -8, nil, 64, -53, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, -39, nil, nil, nil, nil, nil, -38, -160, -38, - nil, nil, nil, -65, -100, -293, -275, -280, -182, -204, - -256, -86, -147, nil, -91, -178, -94, -227, -147, nil, - -76, -226, nil, nil, -41, -106, -289, nil, -183, -254, - nil, -75, -216, nil, -232, 6, -50, -84, -83, nil, - nil, -50, -79, -78, nil, -77, 40, 41 ] + nil, -38, nil, nil, nil, nil, nil, -41, -163, -38, + nil, nil, nil, -66, -102, -298, -281, -284, -188, -205, + -262, -87, -163, nil, -93, -179, -94, -230, -148, nil, + -74, -229, nil, nil, -41, -106, -294, nil, -182, -257, + nil, -73, -219, nil, -235, nil, -188, -85, -84, nil, + nil, -55, -80, -79, nil, -78, 10, 39 ] racc_goto_default = [ - nil, nil, 359, nil, 213, 5, 6, 7, 8, 9, - 11, 10, 297, nil, 15, 38, 16, 17, 18, 19, + nil, nil, 363, nil, 214, 5, 6, 7, 8, 9, + 11, 10, 300, nil, 15, 38, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, nil, nil, 39, 40, 113, nil, nil, 117, nil, nil, nil, nil, - nil, nil, nil, 44, nil, nil, nil, 193, nil, 104, - nil, 194, 198, 196, 124, nil, nil, 123, nil, nil, - 129, nil, 130, 131, 222, nil, nil, 54, 55, 56, + nil, nil, nil, 44, nil, nil, nil, 194, nil, 104, + nil, 195, 199, 197, 124, nil, nil, 123, nil, nil, + 129, nil, 130, 131, 223, 142, 144, 54, 55, 56, 58, nil, nil, nil, 147, nil, nil, nil ] racc_reduce_table = [ @@ -827,15 +835,16 @@ racc_reduce_table = [ 4, 173, :_reduce_196, 1, 112, :_reduce_197, 2, 112, :_reduce_198, - 1, 119, :_reduce_199, - 1, 122, :_reduce_200, - 1, 120, :_reduce_201, - 1, 121, :_reduce_202, - 1, 115, :_reduce_203, - 1, 114, :_reduce_204, - 1, 117, :_reduce_205, + 4, 112, :_reduce_199, + 1, 119, :_reduce_200, + 1, 122, :_reduce_201, + 1, 120, :_reduce_202, + 1, 121, :_reduce_203, + 1, 115, :_reduce_204, + 1, 114, :_reduce_205, + 1, 117, :_reduce_206, 0, 124, :_reduce_none, - 1, 124, :_reduce_207, + 1, 124, :_reduce_208, 0, 141, :_reduce_none, 1, 141, :_reduce_none, 1, 149, :_reduce_none, @@ -852,11 +861,11 @@ racc_reduce_table = [ 1, 149, :_reduce_none, 1, 149, :_reduce_none, 1, 149, :_reduce_none, - 0, 90, :_reduce_224 ] + 0, 90, :_reduce_225 ] -racc_reduce_n = 225 +racc_reduce_n = 226 -racc_shift_n = 392 +racc_shift_n = 396 racc_token_table = { false => 0, @@ -2434,66 +2443,71 @@ module_eval(<<'.,.,', 'egrammar.ra', 678) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 680) +module_eval(<<'.,.,', 'egrammar.ra', 679) def _reduce_199(val, _values, result) - result = Factory.NUMBER(val[0][:value]) ; loc result, val[0] + result = Factory.RENDER_EXPR(Factory.block_or_expression(*val[2])); loc result, val[0], val[3] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 681) def _reduce_200(val, _values, result) - result = Factory.QNAME_OR_NUMBER(val[0][:value]) ; loc result, val[0] + result = Factory.NUMBER(val[0][:value]) ; loc result, val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 682) def _reduce_201(val, _values, result) - result = Factory.QREF(val[0][:value]) ; loc result, val[0] + result = Factory.QNAME_OR_NUMBER(val[0][:value]) ; loc result, val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 683) def _reduce_202(val, _values, result) - result = Factory.literal(:undef); loc result, val[0] + result = Factory.QREF(val[0][:value]) ; loc result, val[0] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 684) def _reduce_203(val, _values, result) + result = Factory.literal(:undef); loc result, val[0] + result + end +.,., + +module_eval(<<'.,.,', 'egrammar.ra', 685) + def _reduce_204(val, _values, result) result = Factory.literal(:default); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 689) - def _reduce_204(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 690) + def _reduce_205(val, _values, result) result = Factory.literal(val[0][:value]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 692) - def _reduce_205(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 693) + def _reduce_206(val, _values, result) result = Factory.literal(val[0][:value]); loc result, val[0] result end .,., -# reduce 206 omitted +# reduce 207 omitted -module_eval(<<'.,.,', 'egrammar.ra', 698) - def _reduce_207(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 699) + def _reduce_208(val, _values, result) result = nil result end .,., -# reduce 208 omitted - # reduce 209 omitted # reduce 210 omitted @@ -2524,8 +2538,10 @@ module_eval(<<'.,.,', 'egrammar.ra', 698) # reduce 223 omitted -module_eval(<<'.,.,', 'egrammar.ra', 721) - def _reduce_224(val, _values, result) +# reduce 224 omitted + +module_eval(<<'.,.,', 'egrammar.ra', 722) + def _reduce_225(val, _values, result) result = nil result end diff --git a/lib/puppet/pops/validation/checker4_0.rb b/lib/puppet/pops/validation/checker4_0.rb index df19b2ef4..cb2c632a1 100644 --- a/lib/puppet/pops/validation/checker4_0.rb +++ b/lib/puppet/pops/validation/checker4_0.rb @@ -452,8 +452,6 @@ class Puppet::Pops::Validation::Checker4_0 # def rvalue_Expression(o); end - def rvalue_BlockExpression(o) ; acceptor.accept(Issues::NOT_RVALUE, o) ; end - def rvalue_ResourceDefaultsExpression(o); acceptor.accept(Issues::NOT_RVALUE, o) ; end def rvalue_ResourceOverrideExpression(o); acceptor.accept(Issues::NOT_RVALUE, o) ; end diff --git a/spec/unit/parser/functions/inline_epp_spec.rb b/spec/unit/parser/functions/inline_epp_spec.rb index bbd7e8a33..44b24528b 100644 --- a/spec/unit/parser/functions/inline_epp_spec.rb +++ b/spec/unit/parser/functions/inline_epp_spec.rb @@ -56,6 +56,10 @@ describe "the inline_epp function" do end end + it "renders a block expression" do + eval_template_with_args("<%= {($x) $x + 1} %>", 'x' => 2).should == "3" + end + # although never a problem with epp it "is not interfered with by having a variable named 'string' (#14093)" do scope['string'] = "this output should not be seen" From 8a4d1b3c0ab7cd4b6afb51b531527fb6ef8ab262 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Wed, 5 Mar 2014 12:54:36 -0800 Subject: [PATCH 795/800] (PUP-979) Fix unparenthesized calls with hash to not give strange error Previously, an unparenthesized call followed by a hash would always result in an error that the hash was a resource body without title. The change now treats the resource body without title as an opportunity to transform the expression into a hash (and subsequently into a call with a hash as an argument). At the same time, the set of functions this is available for has been reduced to realize, require, contain, include, and the logging functions notice, info, warning, error, and the fail function. --- lib/puppet/pops/model/factory.rb | 41 ++++- lib/puppet/pops/parser/egrammar.ra | 8 +- lib/puppet/pops/parser/eparser.rb | 174 +++++++++--------- lib/puppet/pops/parser/parser_support.rb | 5 + spec/unit/pops/parser/parse_calls_spec.rb | 16 +- .../pops/transformer/transform_calls_spec.rb | 43 ++++- 6 files changed, 189 insertions(+), 98 deletions(-) diff --git a/lib/puppet/pops/model/factory.rb b/lib/puppet/pops/model/factory.rb index 452f6a654..83af10242 100644 --- a/lib/puppet/pops/model/factory.rb +++ b/lib/puppet/pops/model/factory.rb @@ -147,8 +147,8 @@ class Puppet::Pops::Model::Factory end def build_KeyedEntry(o, k, v) - o.key = build(k) - o.value = build(v) + o.key = to_ops(k) + o.value = to_ops(v) o end @@ -703,6 +703,27 @@ class Puppet::Pops::Model::Factory o.nil? || o.is_a?(Puppet::Pops::Model::Nop) end + STATEMENT_CALLS = { + 'require' => true, + 'realize' => true, + 'include' => true, + 'contain' => true, + + 'debug' => true, + 'info' => true, + 'notice' => true, + 'warning' => true, + 'error' => true, + + 'fail' => true, + } + # Returns true if the given name is a "statement keyword" (require, include, contain, + # error, notice, info, debug + # + def name_is_statement(name) + STATEMENT_CALLS[name] + end + # Transforms an array of expressions containing literal name expressions to calls if followed by an # expression, or expression list. # @@ -710,7 +731,7 @@ class Puppet::Pops::Model::Factory expressions.reduce([]) do |memo, expr| expr = expr.current if expr.is_a?(Puppet::Pops::Model::Factory) name = memo[-1] - if name.is_a? Model::QualifiedName + if name.is_a?(Model::QualifiedName) && STATEMENT_CALLS[name.value] memo[-1] = Puppet::Pops::Model::Factory.CALL_NAMED(name, false, expr.is_a?(Array) ? expr : [expr]) if expr.is_a?(Model::CallNamedFunctionExpression) # Patch statement function call to expression style @@ -731,6 +752,20 @@ class Puppet::Pops::Model::Factory end + # Transforms a left expression followed by an untitled resource (in the form of attribute_operations) + # @param left [Factory, Expression] the lhs followed what may be a hash + def self.transform_resource_wo_title(left, attribute_ops) + return nil unless attribute_ops.is_a? Array +# return nil if attribute_ops.find { |ao| ao.operator == :'+>' } + keyed_entries = attribute_ops.map do |ao| + return nil if ao.operator == :'+>' + KEY_ENTRY(ao.attribute_name, ao.value_expr) + end + result = block_or_expression(*transform_calls([left, HASH(keyed_entries)])) + result + end + + # Building model equivalences of Ruby objects # Allows passing regular ruby objects to the factory to produce instructions # that when evaluated produce the same thing. diff --git a/lib/puppet/pops/parser/egrammar.ra b/lib/puppet/pops/parser/egrammar.ra index 962559647..e72c28658 100644 --- a/lib/puppet/pops/parser/egrammar.ra +++ b/lib/puppet/pops/parser/egrammar.ra @@ -397,7 +397,13 @@ resource_expression result = case Factory.resource_shape(val[0]) when :resource, :class # This catches deprecated syntax. - error val[1], "All resource specifications require names" + # If the attribute operations does not include +>, then the found expression + # is actually a LEFT followed by LITERAL_HASH + # + unless tmp = transform_resource_wo_title(val[0], val[2]) + error val[1], "Syntax error resource body without title or hash with +>" + end + tmp when :defaults Factory.RESOURCE_DEFAULTS(val[0], val[2]) when :override diff --git a/lib/puppet/pops/parser/eparser.rb b/lib/puppet/pops/parser/eparser.rb index 55acc6498..c56cc0d11 100644 --- a/lib/puppet/pops/parser/eparser.rb +++ b/lib/puppet/pops/parser/eparser.rb @@ -20,7 +20,7 @@ module Puppet module Parser class Parser < Racc::Parser -module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 738) +module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 744) # Make emacs happy # Local Variables: @@ -1867,7 +1867,13 @@ module_eval(<<'.,.,', 'egrammar.ra', 396) result = case Factory.resource_shape(val[0]) when :resource, :class # This catches deprecated syntax. - error val[1], "All resource specifications require names" + # If the attribute operations does not include +>, then the found expression + # is actually a LEFT followed by LITERAL_HASH + # + unless tmp = transform_resource_wo_title(val[0], val[2]) + error val[1], "Syntax error resource body without title or hash with +>" + end + tmp when :defaults Factory.RESOURCE_DEFAULTS(val[0], val[2]) when :override @@ -1882,7 +1888,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 396) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 411) +module_eval(<<'.,.,', 'egrammar.ra', 417) def _reduce_112(val, _values, result) result = Factory.RESOURCE(Factory.fqn(token_text(val[1])), val[3]) result.form = val[0] @@ -1892,7 +1898,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 411) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 416) +module_eval(<<'.,.,', 'egrammar.ra', 422) def _reduce_113(val, _values, result) result = Factory.RESOURCE(Factory.fqn(token_text(val[0])), val[2]) loc result, val[0], val[4] @@ -1901,56 +1907,56 @@ module_eval(<<'.,.,', 'egrammar.ra', 416) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 421) +module_eval(<<'.,.,', 'egrammar.ra', 427) def _reduce_114(val, _values, result) result = Factory.RESOURCE_BODY(val[0], val[2]) result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 423) +module_eval(<<'.,.,', 'egrammar.ra', 429) def _reduce_115(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 426) +module_eval(<<'.,.,', 'egrammar.ra', 432) def _reduce_116(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 427) +module_eval(<<'.,.,', 'egrammar.ra', 433) def _reduce_117(val, _values, result) result = val[0].push val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 432) +module_eval(<<'.,.,', 'egrammar.ra', 438) def _reduce_118(val, _values, result) result = :virtual result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 433) +module_eval(<<'.,.,', 'egrammar.ra', 439) def _reduce_119(val, _values, result) result = :exported result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 434) +module_eval(<<'.,.,', 'egrammar.ra', 440) def _reduce_120(val, _values, result) result = :exported result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 446) +module_eval(<<'.,.,', 'egrammar.ra', 452) def _reduce_121(val, _values, result) result = Factory.COLLECT(val[0], val[1], val[3]) loc result, val[0], val[5] @@ -1959,7 +1965,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 446) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 450) +module_eval(<<'.,.,', 'egrammar.ra', 456) def _reduce_122(val, _values, result) result = Factory.COLLECT(val[0], val[1], []) loc result, val[0], val[1] @@ -1968,14 +1974,14 @@ module_eval(<<'.,.,', 'egrammar.ra', 450) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 455) +module_eval(<<'.,.,', 'egrammar.ra', 461) def _reduce_123(val, _values, result) result = Factory.VIRTUAL_QUERY(val[1]) ; loc result, val[0], val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 456) +module_eval(<<'.,.,', 'egrammar.ra', 462) def _reduce_124(val, _values, result) result = Factory.EXPORTED_QUERY(val[1]) ; loc result, val[0], val[2] result @@ -1986,21 +1992,21 @@ module_eval(<<'.,.,', 'egrammar.ra', 456) # reduce 126 omitted -module_eval(<<'.,.,', 'egrammar.ra', 469) +module_eval(<<'.,.,', 'egrammar.ra', 475) def _reduce_127(val, _values, result) result = [] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 470) +module_eval(<<'.,.,', 'egrammar.ra', 476) def _reduce_128(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 471) +module_eval(<<'.,.,', 'egrammar.ra', 477) def _reduce_129(val, _values, result) result = val[0].push(val[2]) result @@ -2013,7 +2019,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 471) # reduce 132 omitted -module_eval(<<'.,.,', 'egrammar.ra', 487) +module_eval(<<'.,.,', 'egrammar.ra', 493) def _reduce_133(val, _values, result) result = Factory.ATTRIBUTE_OP(val[0][:value], :'=>', val[2]) loc result, val[0], val[2] @@ -2022,7 +2028,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 487) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 491) +module_eval(<<'.,.,', 'egrammar.ra', 497) def _reduce_134(val, _values, result) result = Factory.ATTRIBUTE_OP(val[0][:value], :'+>', val[2]) loc result, val[0], val[2] @@ -2031,7 +2037,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 491) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 501) +module_eval(<<'.,.,', 'egrammar.ra', 507) def _reduce_135(val, _values, result) result = add_definition(Factory.DEFINITION(classname(val[1][:value]), val[2], val[4])) loc result, val[0], val[5] @@ -2044,7 +2050,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 501) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 515) +module_eval(<<'.,.,', 'egrammar.ra', 521) def _reduce_136(val, _values, result) # Remove this class' name from the namestack as all nested classes have been parsed namepop @@ -2055,7 +2061,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 515) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 525) +module_eval(<<'.,.,', 'egrammar.ra', 531) def _reduce_137(val, _values, result) namestack(val[0][:value]) ; result = val[0] result @@ -2068,7 +2074,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 525) # reduce 140 omitted -module_eval(<<'.,.,', 'egrammar.ra', 534) +module_eval(<<'.,.,', 'egrammar.ra', 540) def _reduce_141(val, _values, result) result = val[1] result @@ -2079,7 +2085,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 534) # reduce 143 omitted -module_eval(<<'.,.,', 'egrammar.ra', 551) +module_eval(<<'.,.,', 'egrammar.ra', 557) def _reduce_144(val, _values, result) result = add_definition(Factory.NODE(val[1], val[2], val[4])) loc result, val[0], val[5] @@ -2088,7 +2094,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 551) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 555) +module_eval(<<'.,.,', 'egrammar.ra', 561) def _reduce_145(val, _values, result) result = add_definition(Factory.NODE(val[1], val[2], nil)) loc result, val[0], val[4] @@ -2097,35 +2103,35 @@ module_eval(<<'.,.,', 'egrammar.ra', 555) end .,., -module_eval(<<'.,.,', 'egrammar.ra', 565) +module_eval(<<'.,.,', 'egrammar.ra', 571) def _reduce_146(val, _values, result) result = [result] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 566) +module_eval(<<'.,.,', 'egrammar.ra', 572) def _reduce_147(val, _values, result) result = val[0].push(val[2]) result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 571) +module_eval(<<'.,.,', 'egrammar.ra', 577) def _reduce_148(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 572) +module_eval(<<'.,.,', 'egrammar.ra', 578) def _reduce_149(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 573) +module_eval(<<'.,.,', 'egrammar.ra', 579) def _reduce_150(val, _values, result) result = Factory.literal(:default); loc result, val[0] result @@ -2134,14 +2140,14 @@ module_eval(<<'.,.,', 'egrammar.ra', 573) # reduce 151 omitted -module_eval(<<'.,.,', 'egrammar.ra', 577) +module_eval(<<'.,.,', 'egrammar.ra', 583) def _reduce_152(val, _values, result) result = Factory.literal(val[0][:value]); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 578) +module_eval(<<'.,.,', 'egrammar.ra', 584) def _reduce_153(val, _values, result) result = Factory.concat(val[0], '.', val[2][:value]); loc result, val[0], val[2] result @@ -2150,161 +2156,161 @@ module_eval(<<'.,.,', 'egrammar.ra', 578) # reduce 154 omitted -module_eval(<<'.,.,', 'egrammar.ra', 583) +module_eval(<<'.,.,', 'egrammar.ra', 589) def _reduce_155(val, _values, result) result = val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 589) +module_eval(<<'.,.,', 'egrammar.ra', 595) def _reduce_156(val, _values, result) result = val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 590) +module_eval(<<'.,.,', 'egrammar.ra', 596) def _reduce_157(val, _values, result) error val[0], "'class' is not a valid classname" result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 594) +module_eval(<<'.,.,', 'egrammar.ra', 600) def _reduce_158(val, _values, result) result = [] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 595) +module_eval(<<'.,.,', 'egrammar.ra', 601) def _reduce_159(val, _values, result) result = [] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 596) +module_eval(<<'.,.,', 'egrammar.ra', 602) def _reduce_160(val, _values, result) result = val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 600) +module_eval(<<'.,.,', 'egrammar.ra', 606) def _reduce_161(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 601) +module_eval(<<'.,.,', 'egrammar.ra', 607) def _reduce_162(val, _values, result) result = val[0].push(val[2]) result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 605) +module_eval(<<'.,.,', 'egrammar.ra', 611) def _reduce_163(val, _values, result) result = Factory.PARAM(val[0][:value], val[2]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 606) +module_eval(<<'.,.,', 'egrammar.ra', 612) def _reduce_164(val, _values, result) result = Factory.PARAM(val[0][:value]); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 619) +module_eval(<<'.,.,', 'egrammar.ra', 625) def _reduce_165(val, _values, result) result = Factory.fqn(val[0][:value]).var ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 625) +module_eval(<<'.,.,', 'egrammar.ra', 631) def _reduce_166(val, _values, result) result = Factory.LIST(val[1]); loc result, val[0], val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 626) +module_eval(<<'.,.,', 'egrammar.ra', 632) def _reduce_167(val, _values, result) result = Factory.LIST(val[1]); loc result, val[0], val[3] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 627) +module_eval(<<'.,.,', 'egrammar.ra', 633) def _reduce_168(val, _values, result) result = Factory.literal([]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 628) +module_eval(<<'.,.,', 'egrammar.ra', 634) def _reduce_169(val, _values, result) result = Factory.LIST(val[1]); loc result, val[0], val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 629) +module_eval(<<'.,.,', 'egrammar.ra', 635) def _reduce_170(val, _values, result) result = Factory.LIST(val[1]); loc result, val[0], val[3] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 630) +module_eval(<<'.,.,', 'egrammar.ra', 636) def _reduce_171(val, _values, result) result = Factory.literal([]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 633) +module_eval(<<'.,.,', 'egrammar.ra', 639) def _reduce_172(val, _values, result) result = Factory.HASH(val[1]); loc result, val[0], val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 634) +module_eval(<<'.,.,', 'egrammar.ra', 640) def _reduce_173(val, _values, result) result = Factory.HASH(val[1]); loc result, val[0], val[3] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 635) +module_eval(<<'.,.,', 'egrammar.ra', 641) def _reduce_174(val, _values, result) result = Factory.literal({}) ; loc result, val[0], val[3] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 638) +module_eval(<<'.,.,', 'egrammar.ra', 644) def _reduce_175(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 639) +module_eval(<<'.,.,', 'egrammar.ra', 645) def _reduce_176(val, _values, result) result = val[0].push val[2] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 642) +module_eval(<<'.,.,', 'egrammar.ra', 648) def _reduce_177(val, _values, result) result = Factory.KEY_ENTRY(val[0], val[2]); loc result, val[1] result @@ -2317,182 +2323,182 @@ module_eval(<<'.,.,', 'egrammar.ra', 642) # reduce 180 omitted -module_eval(<<'.,.,', 'egrammar.ra', 649) +module_eval(<<'.,.,', 'egrammar.ra', 655) def _reduce_181(val, _values, result) result = Factory.literal(val[0][:value]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 650) +module_eval(<<'.,.,', 'egrammar.ra', 656) def _reduce_182(val, _values, result) result = Factory.string(val[0], *val[1]) ; loc result, val[0], val[1][-1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 651) +module_eval(<<'.,.,', 'egrammar.ra', 657) def _reduce_183(val, _values, result) result = Factory.literal(val[0][:value]); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 652) +module_eval(<<'.,.,', 'egrammar.ra', 658) def _reduce_184(val, _values, result) result = Factory.literal(val[0][:value]); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 653) +module_eval(<<'.,.,', 'egrammar.ra', 659) def _reduce_185(val, _values, result) result = Factory.literal(val[0][:value]); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 654) +module_eval(<<'.,.,', 'egrammar.ra', 660) def _reduce_186(val, _values, result) result = [val[0]] + val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 655) +module_eval(<<'.,.,', 'egrammar.ra', 661) def _reduce_187(val, _values, result) result = Factory.TEXT(val[0]) result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 658) +module_eval(<<'.,.,', 'egrammar.ra', 664) def _reduce_188(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 659) +module_eval(<<'.,.,', 'egrammar.ra', 665) def _reduce_189(val, _values, result) result = [val[0]] + val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 662) +module_eval(<<'.,.,', 'egrammar.ra', 668) def _reduce_190(val, _values, result) result = Factory.HEREDOC(val[0][:value], val[1]); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 665) +module_eval(<<'.,.,', 'egrammar.ra', 671) def _reduce_191(val, _values, result) result = Factory.SUBLOCATE(val[0], val[1]); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 666) +module_eval(<<'.,.,', 'egrammar.ra', 672) def _reduce_192(val, _values, result) result = Factory.SUBLOCATE(val[0], val[1]); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 669) +module_eval(<<'.,.,', 'egrammar.ra', 675) def _reduce_193(val, _values, result) result = Factory.EPP(val[1], val[2]); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 672) +module_eval(<<'.,.,', 'egrammar.ra', 678) def _reduce_194(val, _values, result) result = nil result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 673) +module_eval(<<'.,.,', 'egrammar.ra', 679) def _reduce_195(val, _values, result) result = [] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 674) +module_eval(<<'.,.,', 'egrammar.ra', 680) def _reduce_196(val, _values, result) result = val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 677) +module_eval(<<'.,.,', 'egrammar.ra', 683) def _reduce_197(val, _values, result) result = Factory.RENDER_STRING(val[0][:value]); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 678) +module_eval(<<'.,.,', 'egrammar.ra', 684) def _reduce_198(val, _values, result) result = Factory.RENDER_EXPR(val[1]); loc result, val[0], val[1] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 679) +module_eval(<<'.,.,', 'egrammar.ra', 685) def _reduce_199(val, _values, result) result = Factory.RENDER_EXPR(Factory.block_or_expression(*val[2])); loc result, val[0], val[3] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 681) +module_eval(<<'.,.,', 'egrammar.ra', 687) def _reduce_200(val, _values, result) result = Factory.NUMBER(val[0][:value]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 682) +module_eval(<<'.,.,', 'egrammar.ra', 688) def _reduce_201(val, _values, result) result = Factory.QNAME_OR_NUMBER(val[0][:value]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 683) +module_eval(<<'.,.,', 'egrammar.ra', 689) def _reduce_202(val, _values, result) result = Factory.QREF(val[0][:value]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 684) +module_eval(<<'.,.,', 'egrammar.ra', 690) def _reduce_203(val, _values, result) result = Factory.literal(:undef); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 685) +module_eval(<<'.,.,', 'egrammar.ra', 691) def _reduce_204(val, _values, result) result = Factory.literal(:default); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 690) +module_eval(<<'.,.,', 'egrammar.ra', 696) def _reduce_205(val, _values, result) result = Factory.literal(val[0][:value]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 693) +module_eval(<<'.,.,', 'egrammar.ra', 699) def _reduce_206(val, _values, result) result = Factory.literal(val[0][:value]); loc result, val[0] result @@ -2501,7 +2507,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 693) # reduce 207 omitted -module_eval(<<'.,.,', 'egrammar.ra', 699) +module_eval(<<'.,.,', 'egrammar.ra', 705) def _reduce_208(val, _values, result) result = nil result @@ -2540,7 +2546,7 @@ module_eval(<<'.,.,', 'egrammar.ra', 699) # reduce 224 omitted -module_eval(<<'.,.,', 'egrammar.ra', 722) +module_eval(<<'.,.,', 'egrammar.ra', 728) def _reduce_225(val, _values, result) result = nil result diff --git a/lib/puppet/pops/parser/parser_support.rb b/lib/puppet/pops/parser/parser_support.rb index 2a540a0c2..a351048d3 100644 --- a/lib/puppet/pops/parser/parser_support.rb +++ b/lib/puppet/pops/parser/parser_support.rb @@ -186,6 +186,11 @@ class Puppet::Pops::Parser::Parser Factory.transform_calls(expressions) end + # Transforms a LEFT followed by the result of attribute_operations, this may be a call or an invalid sequence + def transform_resource_wo_title(left, resource) + Factory.transform_resource_wo_title(left, resource) + end + # If there are definitions that require initialization a Program is produced, else the body def create_program(body) locator = @lexer.locator diff --git a/spec/unit/pops/parser/parse_calls_spec.rb b/spec/unit/pops/parser/parse_calls_spec.rb index 800a15bec..115c160d6 100644 --- a/spec/unit/pops/parser/parse_calls_spec.rb +++ b/spec/unit/pops/parser/parse_calls_spec.rb @@ -14,8 +14,12 @@ describe "egrammar parsing function calls" do dump(parse("foo()")).should == "(invoke foo)" end - it "foo bar" do - dump(parse("foo bar")).should == "(invoke foo bar)" + it "notice bar" do + dump(parse("notice bar")).should == "(invoke notice bar)" + end + + it "notice(bar)" do + dump(parse("notice bar")).should == "(invoke notice bar)" end it "foo(bar)" do @@ -30,8 +34,8 @@ describe "egrammar parsing function calls" do dump(parse("foo(bar,fum,)")).should == "(invoke foo bar fum)" end - it "foo fqdn_rand(30)" do - dump(parse("foo fqdn_rand(30)")).should == '(invoke foo (call fqdn_rand 30))' + it "notice fqdn_rand(30)" do + dump(parse("notice fqdn_rand(30)")).should == '(invoke notice (call fqdn_rand 30))' end end @@ -40,8 +44,8 @@ describe "egrammar parsing function calls" do dump(parse("if true {foo()}")).should == "(if true\n (then (invoke foo)))" end - it "if true { foo bar}" do - dump(parse("if true {foo bar}")).should == "(if true\n (then (invoke foo bar)))" + it "if true { notice bar}" do + dump(parse("if true {notice bar}")).should == "(if true\n (then (invoke notice bar)))" end end end diff --git a/spec/unit/pops/transformer/transform_calls_spec.rb b/spec/unit/pops/transformer/transform_calls_spec.rb index 735ba771e..f58c79e1e 100644 --- a/spec/unit/pops/transformer/transform_calls_spec.rb +++ b/spec/unit/pops/transformer/transform_calls_spec.rb @@ -14,8 +14,8 @@ describe "transformation to Puppet AST for function calls" do astdump(parse("foo()")).should == "(invoke foo)" end - it "foo bar" do - astdump(parse("foo bar")).should == "(invoke foo bar)" + it "notice bar" do + astdump(parse("notice bar")).should == "(invoke notice bar)" end end @@ -24,10 +24,45 @@ describe "transformation to Puppet AST for function calls" do astdump(parse("if true {foo()}")).should == "(if true\n (then (invoke foo)))" end - it "if true { foo bar}" do - astdump(parse("if true {foo bar}")).should == "(if true\n (then (invoke foo bar)))" + it "if true { notice bar}" do + astdump(parse("if true {notice bar}")).should == "(if true\n (then (invoke notice bar)))" end end + context "in general" do + { + "require bar" => '(invoke require bar)', + "realize bar" => '(invoke realize bar)', + "contain bar" => '(invoke contain bar)', + "include bar" => '(invoke include bar)', + + "info bar" => '(invoke info bar)', + "notice bar" => '(invoke notice bar)', + "error bar" => '(invoke error bar)', + "warning bar" => '(invoke warning bar)', + "debug bar" => '(invoke debug bar)', + + "fail bar" => '(invoke fail bar)', + + "notice {a => 1}" => "(invoke notice ({} ('a' 1)))", + "notice 1,2,3" => "(invoke notice 1 2 3)", + "notice(1,2,3)" => "(invoke notice 1 2 3)", + }.each do |source, result| + it "should transform #{source} to #{result}" do + astdump(parse(source)).should == result + end + end + + { + "foo bar" => '(block foo bar)', + "tag bar" => '(block tag bar)', + "tag" => 'tag', + }.each do |source, result| + it "should not transform #{source}, and instead produce #{result}" do + astdump(parse(source)).should == result + end + end + + end end context "When transforming calls as expressions" do From a916a53c95aabc851fec3c731dae0cb11875ca86 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 8 Mar 2014 19:36:38 -0800 Subject: [PATCH 796/800] (PUP-1897) Change epp lexer/grammar to only accept single rendered expr It was confusing to allow more than a single expression in an epp render expression - e.g. <%= 1 2 3 %> where only the first expression was rendered, and the subsequent expressions only evaluated for side effect. This change restricts the <%= tag to only accept a single expression. --- lib/puppet/pops/parser/egrammar.ra | 12 +- lib/puppet/pops/parser/eparser.rb | 1580 ++++++++++++++-------------- lib/puppet/pops/parser/lexer2.rb | 8 + 3 files changed, 814 insertions(+), 786 deletions(-) diff --git a/lib/puppet/pops/parser/egrammar.ra b/lib/puppet/pops/parser/egrammar.ra index e72c28658..1660b47a1 100644 --- a/lib/puppet/pops/parser/egrammar.ra +++ b/lib/puppet/pops/parser/egrammar.ra @@ -17,7 +17,7 @@ token IN UNLESS PIPE token LAMBDA SELBRACE token NUMBER token HEREDOC SUBLOCATE -token RENDER_STRING RENDER_EXPR EPP_START +token RENDER_STRING RENDER_EXPR EPP_START EPP_END EPP_END_TRIM token LOW prechigh @@ -681,9 +681,13 @@ epp_parameters_list | PIPE parameters endcomma PIPE { result = val[1] } epp_render_expression - : RENDER_STRING { result = Factory.RENDER_STRING(val[0][:value]); loc result, val[0] } - | RENDER_EXPR expression { result = Factory.RENDER_EXPR(val[1]); loc result, val[0], val[1] } - | RENDER_EXPR LBRACE statements RBRACE { result = Factory.RENDER_EXPR(Factory.block_or_expression(*val[2])); loc result, val[0], val[3] } + : RENDER_STRING { result = Factory.RENDER_STRING(val[0][:value]); loc result, val[0] } + | RENDER_EXPR expression epp_end { result = Factory.RENDER_EXPR(val[1]); loc result, val[0], val[2] } + | RENDER_EXPR LBRACE statements RBRACE epp_end { result = Factory.RENDER_EXPR(Factory.block_or_expression(*val[2])); loc result, val[0], val[4] } + +epp_end + : EPP_END + | EPP_END_TRIM number : NUMBER { result = Factory.NUMBER(val[0][:value]) ; loc result, val[0] } name : NAME { result = Factory.QNAME_OR_NUMBER(val[0][:value]) ; loc result, val[0] } diff --git a/lib/puppet/pops/parser/eparser.rb b/lib/puppet/pops/parser/eparser.rb index c56cc0d11..6d1a3668d 100644 --- a/lib/puppet/pops/parser/eparser.rb +++ b/lib/puppet/pops/parser/eparser.rb @@ -20,7 +20,7 @@ module Puppet module Parser class Parser < Racc::Parser -module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 744) +module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 748) # Make emacs happy # Local Variables: @@ -30,197 +30,198 @@ module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 744) ##### State transition tables begin ### clist = [ -'57,59,-223,-130,51,262,53,-214,79,-132,272,126,262,126,79,125,310,125', -'360,295,224,350,102,14,106,224,101,235,102,41,106,48,101,50,45,293,49', -'69,65,238,43,68,46,47,-223,-130,66,13,105,-214,67,-132,273,12,105,258', -'57,59,250,249,51,70,53,391,240,224,247,42,79,248,80,64,60,122,62,63', -'61,126,243,14,52,125,102,242,106,41,101,48,235,50,45,126,49,69,65,125', -'43,68,46,47,221,126,66,13,325,125,67,245,105,12,57,59,244,261,51,126', -'53,70,262,125,345,241,344,42,79,316,231,64,60,328,62,63,345,14,344,313', -'52,330,102,41,106,48,101,50,45,220,49,69,65,72,43,68,46,47,126,332,66', -'13,125,312,67,57,59,12,105,309,57,59,269,271,51,70,53,389,86,85,74,42', -'79,81,82,64,60,337,62,63,80,338,339,14,52,224,102,211,106,41,101,48', -'342,50,45,87,49,69,65,294,43,68,46,47,346,348,66,13,293,187,67,269,105', -'12,271,269,57,59,356,357,51,70,53,387,114,293,74,42,154,151,149,64,60', -'367,62,63,287,57,59,14,52,57,59,368,286,41,271,48,127,50,45,285,49,69', -'65,371,43,68,46,47,114,115,66,13,271,114,67,375,348,12,57,59,377,378', -'51,135,53,70,133,135,379,380,133,42,381,111,383,64,60,384,62,63,385', -'14,269,74,52,71,70,41,392,48,70,50,108,393,49,69,65,60,43,68,394,60', -'395,,66,13,,,67,,,12,57,59,,,51,,53,70,75,77,76,78,,42,,,,64,60,,62', -'63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57', -'59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49', -'69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60', -',62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12', -'57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,45,', -'49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,', -',64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,', -',67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48', -',50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,', -'42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66', -'13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41', -',48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,', -',,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,', -',,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52', -',,41,,48,,50,121,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53', -'70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68', +'57,59,275,-130,51,265,53,-216,79,-132,-225,126,313,126,79,125,265,125', +'363,298,224,224,102,14,106,353,101,360,102,41,106,48,101,50,45,235,49', +'69,65,238,43,68,46,47,276,-130,66,13,105,-216,67,-132,-225,12,105,258', +'57,59,260,261,51,70,53,395,240,224,247,42,245,248,80,64,60,244,62,63', +'61,328,243,14,231,264,52,242,126,41,265,48,125,50,45,126,49,69,65,125', +'43,68,46,47,221,316,66,13,57,59,67,235,126,12,57,59,125,331,51,126,53', +'70,79,125,348,272,347,42,348,333,347,64,60,220,62,63,102,14,106,335', +'101,74,52,41,297,48,135,50,45,133,49,69,65,72,43,68,46,47,296,122,66', +'13,105,274,67,57,59,12,114,70,57,59,250,249,51,70,53,393,340,341,60', +'42,342,224,79,64,60,126,62,63,211,125,345,14,241,349,52,351,102,41,106', +'48,101,50,45,290,49,69,65,187,43,68,46,47,272,274,66,13,272,359,67,296', +'289,12,105,296,57,59,74,154,51,70,53,391,151,149,370,42,312,81,82,64', +'60,288,62,63,80,372,274,14,274,127,52,272,375,41,114,48,115,50,45,315', +'49,69,65,114,43,68,46,47,379,351,66,13,381,382,67,383,384,12,57,59,385', +'111,51,387,53,70,75,77,76,78,388,42,389,319,74,64,60,71,62,63,396,14', +'397,57,59,398,52,41,399,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,', +',12,57,59,,,51,,53,70,,135,,,133,42,,,,64,60,,62,63,,14,,57,59,,52,41', +',48,70,50,108,,49,69,65,,43,68,,60,,,66,13,,,67,,,12,57,59,,,51,,53', +'70,,135,,,133,42,,,,64,60,,62,63,,14,,57,59,,52,41,,48,70,50,108,,49', +'69,65,,43,68,,60,,,66,13,,,67,,,12,57,59,,,51,,53,70,79,135,,,133,42', +',,,64,60,,62,63,102,14,106,,101,,52,41,,48,70,50,108,,49,69,65,,43,68', +',60,,,66,13,105,,67,,,12,57,59,,,51,,53,70,79,,,,,42,,,80,64,60,,62', +'63,102,14,106,,101,,52,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13', +'105,,67,,,12,57,59,,,51,,53,70,79,81,82,,,42,,,80,64,60,,62,63,102,14', +'106,,101,,52,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,105,,67,,,12', +'57,59,,,51,,53,70,79,81,82,,,42,,,80,64,60,,62,63,102,14,106,,101,,52', +'41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,105,,67,,,12,57,59,,,51,,53', +'70,,,,,,42,,,,64,60,,62,63,,14,,,,,52,41,,48,,50,108,,49,69,65,,43,68', ',,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,', -'52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51', -',53,70,,,,,,42,79,,,64,60,,62,63,,14,,,52,,102,41,106,48,101,50,108', -',49,69,65,,43,68,,,,,66,13,,,67,,,12,105,,57,59,,,51,70,53,291,86,85', -',42,,81,82,64,60,,62,63,80,,,14,52,57,59,,,41,,48,,50,45,87,49,69,65', -',43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,138,53,70,,135,,,133,42,,', -',64,60,,62,63,,14,,,52,,,41,,48,70,50,108,,49,69,65,,43,68,,60,,,66', -'13,57,59,67,,,12,57,59,,,51,140,53,70,,,,,,42,,,,64,60,,62,63,,14,,', -'52,,,41,,48,135,50,108,133,49,69,65,,43,68,,,,,66,13,,,67,,,12,,70,57', -'59,,,51,70,53,143,,,60,42,,,,64,60,,62,63,,,,14,52,,,,,41,,48,,50,108', -',49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,79,,', -'64,60,,62,63,,14,,,52,,102,41,106,48,101,50,108,,49,69,65,,43,68,,,', -',66,13,,,67,,,12,105,,57,59,,,51,70,53,297,,,,42,,81,82,64,60,,62,63', -'80,,,14,52,,,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12', -',,57,59,,,51,70,53,143,,,,42,,,,64,60,,62,63,,,,14,52,,,,,41,,48,,50', -'45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,153,70,,,,,', -'42,79,,,64,60,,62,63,,14,,,52,,102,41,106,48,101,50,108,,49,69,65,,43', -'68,,,,,66,13,,,67,,,12,105,,57,59,,,51,70,53,366,,,,42,,,,64,60,,62', -'63,80,,,14,52,,,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67', -',,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50', -'45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42', -',,,64,60,,62,63,,14,,,52,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66', -'13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41', -',48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70', -',,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,45,,49,69,65,,43,68,46', -'47,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,', -',52,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,', -',51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,45,,49,69,65', -',43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62', -'63,,14,,,52,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12', -'57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108', -',49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64', -'60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67', -',,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50', -'108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,', -',,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13', -',,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48', -',50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,', -'42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66', -'13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41', -',48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,', -',,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,', -',,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52', -',,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53', -'70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68', -',,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,', -'52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51', -',53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,', +',,52,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51', +',53,70,,,,,,42,,,,64,60,,62,63,,14,,,,,52,41,,48,,50,108,,49,69,65,', '43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63', -',14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59', -',,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69', +',14,,,,,52,41,,48,,50,121,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59', +',,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,,,52,41,,48,,50,108,,49,69', '65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62', -'63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57', -'59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49', +'63,,14,,,,,52,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57', +'59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,,,52,41,,48,,50,108,,49', +'69,65,,43,68,,,,,66,13,,,67,,,12,,,57,59,,,51,70,53,294,,,,42,,,,64', +'60,,62,63,,,,14,,,52,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13', +',,67,,,12,57,59,,,51,138,53,70,,,,,,42,,,,64,60,,62,63,,14,,,,,52,41', +',48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,140,53,70', +',,,,,42,,,,64,60,,62,63,,14,,,,,52,41,,48,,50,108,,49,69,65,,43,68,', +',,,66,13,,,67,,,12,,,57,59,,,51,70,53,143,,,,42,,,,64,60,,62,63,,,,14', +',,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51', +',53,70,,,,,,42,,,,64,60,,62,63,,14,,,,,52,41,,48,,50,108,,49,69,65,', +'43,68,,,,,66,13,,,67,,,12,,,57,59,,,51,70,53,300,,,,42,,,,64,60,,62', +'63,,,,14,,,52,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,', +'12,,,57,59,,,51,70,53,143,,,,42,,,,64,60,,62,63,,,,14,,,52,,,41,,48', +',50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,153,70,', +',,,,42,,,,64,60,,62,63,,14,,,,,52,41,,48,,50,108,,49,69,65,,43,68,,', +',,66,13,,,67,,,12,,,57,59,,,51,70,53,369,,,,42,,,,64,60,,62,63,,,,14', +',,52,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59', +',,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,,,52,41,,48,,50,45,,49,69', +'65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60', +',62,63,,14,,,,,52,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67', +',,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,,,52,41,,48,,50', +'45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42', +',,,64,60,,62,63,,14,,,,,52,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66', +'13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,,,52,41', +',48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70', +',,,,,42,,,,64,60,,62,63,,14,,,,,52,41,,48,,50,45,,49,69,65,,43,68,46', +'47,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,', +',,,52,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,', +',51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,,,52,41,,48,,50,108,,49,69', +'65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62', +'63,,14,,,,,52,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57', +'59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,,,52,41,,48,,50,108,,49', '69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60', -',62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12', -'57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108', +',62,63,,14,,,,,52,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12', +'57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,,,52,41,,48,,50,108', ',49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64', -'60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67', -',,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50', +'60,,62,63,,14,,,,,52,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67', +',,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,,,52,41,,48,,50', '108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,', -',,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13', -',,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48', +',,64,60,,62,63,,14,,,,,52,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13', +',,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,,,52,41,,48', ',50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,', -'42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66', -'13,,,67,,,12,,,57,59,,,51,70,53,351,,,,42,,,186,64,60,,62,63,,,,14,52', -',,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,', -'53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,189,206,200,207,50,201,209', -'202,198,196,,191,204,,,,,66,13,210,205,203,,,12,57,59,,,51,,53,70,,', -',,208,190,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68', +'42,,,,64,60,,62,63,,14,,,,,52,41,,48,,50,108,,49,69,65,,43,68,,,,,66', +'13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,,,52,41', +',48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,', +',,,,42,,,,64,60,,62,63,,14,,,,,52,41,,48,,50,108,,49,69,65,,43,68,,', +',,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,,', +'52,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53', +'70,,,,,,42,,,,64,60,,62,63,,14,,,,,52,41,,48,,50,108,,49,69,65,,43,68', ',,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,', -'52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51', -',53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,', +',,52,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51', +',53,70,,,,,,42,,,,64,60,,62,63,,14,,,,,52,41,,48,,50,108,,49,69,65,', '43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63', -',14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59', -',,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69', -'65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,79,,,64,60,', -'62,63,,14,,,52,,102,41,106,48,101,50,108,,49,69,65,,43,68,,,,,66,13', -',,67,,,12,105,,57,59,,,51,70,53,299,,,,42,,81,82,64,60,,62,63,80,,,14', -'52,,,,,41,,48,,50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59', -',,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,218,,52,,,41,,48,,50,108,,49', +',14,,,,,52,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59', +',,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,,,52,41,,48,,50,108,,49,69', +'65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62', +'63,,14,,,,,52,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57', +'59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,,,52,41,,48,,50,108,,49', '69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60', -',62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12', -'57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108', +',62,63,,14,,,,,52,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12', +'57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,,,52,41,,48,,50,108', ',49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64', -'60,,62,63,,14,226,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,', -'67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48', -',50,45,,49,69,65,,43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70,,', -',,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,', -',66,13,,,67,,,12,57,59,,,51,319,53,70,,,,,,42,,,,64,60,,62,63,,14,,', -'52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51', -',53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,', +'60,,62,63,,14,,,,,52,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67', +',,12,,,57,59,,,51,70,53,354,,,,42,,,186,64,60,,62,63,,,,14,,,52,,,41', +',48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,', +',,,,42,,,,64,60,,62,63,,14,,,,,52,189,206,200,207,50,201,209,202,198', +'196,,191,204,,,,,66,13,210,205,203,,,12,57,59,,,51,,53,70,,,,,208,190', +',,,64,60,,62,63,,14,,,,,52,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13', +',,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,,,52,41,,48', +',50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,', +'42,,,,64,60,,62,63,,14,,,,,52,41,,48,,50,108,,49,69,65,,43,68,,,,,66', +'13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,,,52,41', +',48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,', +',,,,42,,,,64,60,,62,63,,14,,,,,52,41,,48,,50,108,,49,69,65,,43,68,,', +',,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,,', +'52,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,,,57,59,,,51', +'70,53,302,,,,42,,,,64,60,,62,63,,,,14,,,52,,,41,,48,,50,45,,49,69,65', +',43,68,46,47,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62', +'63,,14,218,,,,52,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12', +'57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,,,52,41,,48,,50,108', +',49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64', +'60,,62,63,,14,,,,,52,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67', +',,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,226,,,,52,41,,48', +',50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,', +'42,,,,64,60,,62,63,,14,,,,,52,41,,48,,50,45,,49,69,65,,43,68,46,47,', +',66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,,,52', +'41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,322,53', +'70,,,,,,42,,,,64,60,,62,63,,14,,,,,52,41,,48,,50,108,,49,69,65,,43,68', +',,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,', +',,52,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51', +',53,70,,,,,,42,,,,64,60,,62,63,,14,,,,,52,41,,48,,50,108,,49,69,65,', '43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63', -',14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59', -',,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,41,,48,,50,108,,49,69', -'65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,318,53,70,,,,,,42,,,,64,60', -',62,63,,14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12', -'57,59,,,51,,53,70,,,,,,42,79,,,64,60,,62,63,,14,,,52,,102,41,106,48', -'101,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,105,,57,59,,,51,70,53', -'321,,,,42,,81,82,64,60,,62,63,80,,,14,52,,,,,41,,48,,50,108,,49,69,65', -',43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63', -',14,,,52,,,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59', -',,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,52,,,189,206,200,207,50,201', -'209,202,198,196,,191,204,,,,,66,13,210,205,203,,,12,,,,,,,,70,,,,,208', -'190,,,,64,60,79,62,63,,,,,52,,98,99,100,95,90,102,,106,,101,,,91,93', -'92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,,79,,103', -',80,246,,,,98,99,100,95,90,102,,106,,101,87,,91,93,92,94,,,,,,,,,,,', -',,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,,79,,,,80,246,,,,98,99,100', -'95,90,102,,106,,101,87,,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83', -'84,86,85,88,89,,81,82,79,,230,,,80,,,,98,99,100,95,90,102,,106,,101', -',87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81', -'82,79,,,,,80,,,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,', -',,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,229,,,80,,,,98', -'99,100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97', -'96,,,83,84,86,85,88,89,,81,82,79,,228,,,80,,,,98,99,100,95,90,102,,106', +',14,,,,,52,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59', +',,51,321,53,70,,,,,,42,,,,64,60,,62,63,,14,,,,,52,41,,48,,50,108,,49', +'69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60', +',62,63,,14,,,,,52,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13,,,67,,,12', +',,57,59,,,51,70,53,324,,,,42,,,,64,60,,62,63,,,,14,,,52,,,41,,48,,50', +'108,,49,69,65,,43,68,,,,,66,13,,,67,,,12,57,59,,,51,,53,70,,,,,,42,', +',,64,60,,62,63,,14,,,,,52,41,,48,,50,108,,49,69,65,,43,68,,,,,66,13', +',,67,,,12,57,59,,,51,,53,70,,,,,,42,,,,64,60,,62,63,,14,,,,,52,189,206', +'200,207,50,201,209,202,198,196,,191,204,,,,,66,13,210,205,203,,,12,', +',,,,,,70,,,,,208,190,,,,64,60,,62,63,79,,,,,,52,,,98,99,100,95,90,102', +',106,,101,,,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88', +'89,,81,82,,,79,,103,80,,246,,,,98,99,100,95,90,102,,106,,101,,87,91', +'93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,79,81,82,,', +'246,,,80,98,99,100,95,90,102,,106,,101,,,91,93,92,94,87,,,,,,,,,,,,', +',,105,,,,97,96,,,83,84,86,85,88,89,,81,82,,,79,,230,80,,,,,,98,99,100', +'95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83', +'84,86,85,88,89,79,81,82,,,,,,80,98,99,100,95,90,102,,106,,101,,,91,93', +'92,94,87,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,,,79', +',229,80,,,,,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,', +',,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,,,79,,228,80,,,,,,98,99', +'100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96', +',,83,84,86,85,88,89,,81,82,,,79,,227,80,,,,,,98,99,100,95,90,102,,106', ',101,,87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89', -',81,82,79,,227,,,80,,,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94', -',,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,,79,,,,80,,,', -',98,99,100,95,90,102,,106,,101,87,216,91,93,92,94,,,,,,,,,,,,,,,,105', -',,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102', -',106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85', -'88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102,,106,,101,,87,91,93,92', -'94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80', -',,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105', -',,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102', -',106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85', -'88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102,,106,,101,,87,91,93,92', -'94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80', -',,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105', -',,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102', -',106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85', -'88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102,,106,,101,,87,91,93,92', -'94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80', -',,,98,99,100,95,90,102,267,106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105', -',,,97,96,,,83,84,86,85,88,89,,81,82,79,,103,,,80,,,,98,99,100,95,90', -'102,,106,79,101,,87,91,93,92,94,,,,,,,102,,106,,101,,,,,105,,,,97,96', -',,83,84,86,85,88,89,,81,82,105,,,79,,80,,,83,84,86,85,,,,81,82,102,', -'106,87,101,80,,,,79,,,,,,,,,,,87,,,102,,106,105,101,,79,,,,,83,84,86', -'85,,,,81,82,102,,106,,101,80,105,,,,,,,,83,84,86,85,88,89,87,81,82,', -',,105,,80,,,79,,,83,84,86,85,88,89,,81,82,87,90,102,,106,80,101,,79', -'91,,,,,,,,,,,87,90,102,,106,,101,,105,91,,,,,,,83,84,86,85,88,89,,81', -'82,,,,105,,80,,,79,,,83,84,86,85,88,89,,81,82,87,90,102,,106,80,101', -',79,91,,,,,,,,,,,87,90,102,,106,,101,,105,91,,,,,,,83,84,86,85,88,89', -',81,82,,,,105,,80,,,,,79,83,84,86,85,88,89,,81,82,87,,95,90,102,80,106', -',101,,79,91,93,92,94,,,,,,87,,95,90,102,,106,,101,,105,91,93,92,94,', -',,83,84,86,85,88,89,,81,82,,,,105,,80,,,96,,,83,84,86,85,88,89,,81,82', -'87,79,,,,80,263,,,,98,99,100,95,90,102,,106,,101,87,,91,93,92,94,,,', -',,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98', -'99,100,95,90,102,,106,,101,,87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97', -'96,,,83,84,86,85,88,89,,81,82,79,,,,,80,,,,98,99,100,95,90,102,,106', -',101,,87,91,93,92,94,,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89', -',81,82,79,,,,,80,,,,98,99,100,95,90,102,,106,,101,,87,91,93,92,94,,', -',,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,,,,,,80,281,206', -'280,207,,278,209,282,276,275,,277,279,,87,,,,,210,205,283,281,206,280', -'207,,278,209,282,276,275,,277,279,,,208,284,,,210,205,283,281,206,280', -'207,,278,209,282,276,275,,277,279,,,208,284,,,210,205,283,,,,,,,,,,', -',,,,,208,284' ] - racc_action_table = arr = ::Array.new(6173, nil) +'79,81,82,,,,,,80,98,99,100,95,90,102,,106,,101,,216,91,93,92,94,87,', +',,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,79,81,82,,,,,,80,98,99', +'100,95,90,102,,106,,101,,,91,93,92,94,87,,,,,,,,,,,,,,,105,,,,97,96', +',,83,84,86,85,88,89,79,81,82,,,,,,80,98,99,100,95,90,102,,106,,101,260', +'261,91,93,92,94,87,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,79', +'81,82,,,,,,80,98,99,100,95,90,102,,106,,101,,,91,93,92,94,87,,,,,,,', +',,,,,,,105,,,,97,96,,,83,84,86,85,88,89,79,81,82,,,,,,80,98,99,100,95', +'90,102,,106,,101,,,91,93,92,94,87,,,,,,,,,,,,,,,105,,,,97,96,,,83,84', +'86,85,88,89,79,81,82,,,,,,80,98,99,100,95,90,102,,106,,101,,,91,93,92', +'94,87,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,79,81,82,,,,,,80', +'98,99,100,95,90,102,,106,,101,,,91,93,92,94,87,,,,,,,,,,,,,,,105,,,', +'97,96,,,83,84,86,85,88,89,79,81,82,,,,,,80,98,99,100,95,90,102,,106', +',101,79,,91,93,92,94,87,,,,,,,,102,,106,,101,,,105,,,,97,96,,,83,84', +'86,85,88,89,,81,82,,,105,,,80,,79,,,,,86,85,,,,81,82,,,102,87,106,80', +'101,,,,,,,,,,,,,,,,87,,,,,,105,,,,,,,,,,86,85,,,79,81,82,,,,,,80,98', +'99,100,95,90,102,,106,,101,,,91,93,92,94,87,,,,,,,,,,,,,,,105,,,,97', +'96,,,83,84,86,85,88,89,79,81,82,,,,,,80,98,99,100,95,90,102,270,106', +',101,,,91,93,92,94,87,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89', +',81,82,,,79,,103,80,,,,,,98,99,100,95,90,102,,106,,101,79,87,91,93,92', +'94,,,,,,,,,102,,106,,101,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,', +',105,,,80,,79,,,83,84,86,85,,,,81,82,,,102,87,106,80,101,79,,,,,,,,', +',,,,,102,87,106,,101,,,105,,,,,,,,83,84,86,85,,,,81,82,,,105,,,80,,79', +',,83,84,86,85,88,89,,81,82,,,102,87,106,80,101,,,,,,,79,,,,,,,,,87,', +',,90,102,105,106,,101,,79,91,,83,84,86,85,88,89,,81,82,,90,102,,106', +'80,101,,105,91,,,,79,,,83,84,86,85,88,89,87,81,82,,90,102,105,106,80', +'101,,,91,,83,84,86,85,88,89,,81,82,,,87,,,80,,,105,,,,,79,,,83,84,86', +'85,88,89,87,81,82,,90,102,,106,80,101,,,91,,,,,,,,,,,,,87,,,,,,105,', +',,,79,,,83,84,86,85,88,89,,81,82,95,90,102,,106,80,101,,,91,93,92,94', +',,,,,,,,,87,,,,,,105,,,,,79,,,83,84,86,85,88,89,,81,82,95,90,102,,106', +'80,101,,,91,93,92,94,,,,,,,,,,87,,,,,,105,,,,,96,,,83,84,86,85,88,89', +'79,81,82,,,,,,80,98,99,100,95,90,102,,106,,101,,,91,93,92,94,87,,,,', +',,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,79,81,82,,,266,,,80,98,99', +'100,95,90,102,,106,,101,,,91,93,92,94,87,,,,,,,,,,,,,,,105,,,,97,96', +',,83,84,86,85,88,89,79,81,82,,,,,,80,98,99,100,95,90,102,,106,,101,', +',91,93,92,94,87,,,,,,,,,,,,,,,105,,,,97,96,,,83,84,86,85,88,89,79,81', +'82,,,,,,80,98,99,100,95,90,102,,106,,101,,,91,93,92,94,87,,,,,,,,,,', +',,,,105,,,,97,96,,,83,84,86,85,88,89,,81,82,,,,,,80,284,206,283,207', +',281,209,285,279,278,,280,282,,,,87,,,210,205,286,284,206,283,207,,281', +'209,285,279,278,,280,282,,,208,287,,,210,205,286,284,206,283,207,,281', +'209,285,279,278,,280,282,,,208,287,,,210,205,286,,,,,,,,,,,,,,,,208', +'287' ] + racc_action_table = arr = ::Array.new(6233, nil) idx = 0 clist.each do |str| str.split(',', -1).each do |i| @@ -230,217 +231,219 @@ clist = [ end clist = [ -'0,0,203,196,0,225,0,204,163,198,199,309,300,108,109,309,235,108,309', -'225,151,300,163,0,163,235,163,128,109,0,109,0,109,0,0,257,0,0,0,129', -'0,0,0,0,203,196,0,0,163,204,0,198,199,0,109,151,378,378,147,147,378', -'0,378,378,129,114,142,0,107,142,163,0,0,45,0,0,0,48,137,378,0,48,107', -'137,107,378,107,378,123,378,378,200,378,378,378,200,378,378,378,378', -'114,45,378,378,260,45,378,139,107,378,5,5,139,161,5,201,5,378,161,201', -'342,131,342,378,165,241,121,378,378,264,378,378,297,5,297,237,378,268', -'165,5,165,5,165,5,5,113,5,5,5,5,5,5,5,5,121,270,5,5,121,236,5,149,149', -'5,165,233,377,377,232,274,377,5,377,377,165,165,155,5,162,165,165,5', -'5,288,5,5,165,290,292,377,5,293,162,104,162,377,162,377,296,377,377', -'165,377,377,377,224,377,377,377,377,298,299,377,377,222,102,377,303', -'162,377,304,305,375,375,306,307,375,377,375,375,218,311,73,377,71,61', -'60,377,377,324,377,377,217,202,202,375,377,49,49,327,215,375,329,375', -'46,375,375,213,375,375,375,336,375,375,375,375,337,40,375,375,193,39', -'375,345,346,375,186,186,348,349,186,202,186,375,202,49,353,354,49,375', -'355,38,361,375,375,362,375,375,365,186,192,6,375,1,202,186,382,186,49', -'186,186,386,186,186,186,202,186,186,388,49,390,,186,186,,,186,,,186', -'12,12,,,12,,12,186,8,8,8,8,,186,,,,186,186,,186,186,,12,,,186,,,12,', -'12,,12,12,,12,12,12,,12,12,,,,,12,12,,,12,,,12,13,13,,,13,,13,12,,,', -',,12,,,,12,12,,12,12,,13,,,12,,,13,,13,,13,13,,13,13,13,,13,13,,,,,13', -'13,,,13,,,13,14,14,,,14,,14,13,,,,,,13,,,,13,13,,13,13,,14,,,13,,,14', -',14,,14,14,,14,14,14,,14,14,,,,,14,14,,,14,,,14,357,357,,,357,,357,14', -',,,,,14,,,,14,14,,14,14,,357,,,14,,,357,,357,,357,357,,357,357,357,', -'357,357,357,357,,,357,357,,,357,,,357,344,344,,,344,,344,357,,,,,,357', -',,,357,357,,357,357,,344,,,357,,,344,,344,,344,344,,344,344,344,,344', -'344,,,,,344,344,,,344,,,344,189,189,,,189,,189,344,,,,,,344,,,,344,344', -',344,344,,189,,,344,,,189,,189,,189,189,,189,189,189,,189,189,,,,,189', -'189,,,189,,,189,41,41,,,41,,41,189,,,,,,189,,,,189,189,,189,189,,41', -',,189,,,41,,41,,41,41,,41,41,41,,41,41,,,,,41,41,,,41,,,41,42,42,,,42', -',42,41,,,,,,41,,,,41,41,,41,41,,42,,,41,,,42,,42,,42,42,,42,42,42,,42', -'42,,,,,42,42,,,42,,,42,43,43,,,43,,43,42,,,,,,42,,,,42,42,,42,42,,43', -',,42,,,43,,43,,43,43,,43,43,43,,43,43,,,,,43,43,,,43,,,43,44,44,,,44', -',44,43,,,,,,43,,,,43,43,,43,43,,44,,,43,,,44,,44,,44,44,,44,44,44,,44', -'44,,,,,44,44,,,44,,,44,190,190,,,190,,190,44,,,,,,44,,,,44,44,,44,44', -',190,,,44,,,190,,190,,190,190,,190,190,190,,190,190,,,,,190,190,,,190', -',,190,191,191,,,191,,191,190,,,,,,190,,,,190,190,,190,190,,191,,,190', -',,191,,191,,191,191,,191,191,191,,191,191,,,,,191,191,,,191,,,191,328', -'328,,,328,,328,191,,,,,,191,166,,,191,191,,191,191,,328,,,191,,166,328', -'166,328,166,328,328,,328,328,328,,328,328,,,,,328,328,,,328,,,328,166', -',220,220,,,220,328,220,220,166,166,,328,,166,166,328,328,,328,328,166', -',,220,328,238,238,,,220,,220,,220,220,166,220,220,220,,220,220,220,220', -',,220,220,,,220,,,220,51,51,,,51,51,51,220,,238,,,238,220,,,,220,220', -',220,220,,51,,,220,,,51,,51,238,51,51,,51,51,51,,51,51,,238,,,51,51', -'240,240,51,,,51,52,52,,,52,52,52,51,,,,,,51,,,,51,51,,51,51,,52,,,51', -',,52,,52,240,52,52,240,52,52,52,,52,52,,,,,52,52,,,52,,,52,,240,53,53', -',,53,52,53,53,,,240,52,,,,52,52,,52,52,,,,53,52,,,,,53,,53,,53,53,,53', -'53,53,,53,53,,,,,53,53,,,53,,,53,58,58,,,58,,58,53,,,,,,53,168,,,53', -'53,,53,53,,58,,,53,,168,58,168,58,168,58,58,,58,58,58,,58,58,,,,,58', -'58,,,58,,,58,168,,227,227,,,227,58,227,227,,,,58,,168,168,58,58,,58', -'58,168,,,227,58,,,,,227,,227,,227,227,,227,227,227,,227,227,227,227', -',,227,227,,,227,,,227,,,153,153,,,153,227,153,153,,,,227,,,,227,227', -',227,227,,,,153,227,,,,,153,,153,,153,153,,153,153,153,,153,153,153', -'153,,,153,153,,,153,,,153,63,63,,,63,,63,153,,,,,,153,164,,,153,153', -',153,153,,63,,,153,,164,63,164,63,164,63,63,,63,63,63,,63,63,,,,,63', -'63,,,63,,,63,164,,313,313,,,313,63,313,313,,,,63,,,,63,63,,63,63,164', -',,313,63,,,,,313,,313,,313,313,,313,313,313,,313,313,313,313,,,313,313', -',,313,,,313,72,72,,,72,,72,313,,,,,,313,,,,313,313,,313,313,,72,,,313', -',,72,,72,,72,72,,72,72,72,,72,72,72,72,,,72,72,,,72,,,72,312,312,,,312', -',312,72,,,,,,72,,,,72,72,,72,72,,312,,,72,,,312,,312,,312,312,,312,312', -'312,,312,312,312,312,,,312,312,,,312,,,312,74,74,,,74,,74,312,,,,,,312', -',,,312,312,,312,312,,74,,,312,,,74,,74,,74,74,,74,74,74,,74,74,74,74', -',,74,74,,,74,,,74,75,75,,,75,,75,74,,,,,,74,,,,74,74,,74,74,,75,,,74', -',,75,,75,,75,75,,75,75,75,,75,75,75,75,,,75,75,,,75,,,75,76,76,,,76', -',76,75,,,,,,75,,,,75,75,,75,75,,76,,,75,,,76,,76,,76,76,,76,76,76,,76', +'0,0,199,196,0,225,0,204,163,198,203,312,235,201,162,312,303,201,312', +'225,151,235,163,0,163,303,163,310,162,0,162,0,162,0,0,123,0,0,0,129', +'0,0,0,0,199,196,0,0,163,204,0,198,203,0,162,151,382,382,328,328,382', +'0,382,382,129,114,142,0,139,142,163,0,0,139,0,0,0,263,137,382,121,161', +'0,137,48,382,161,382,48,382,382,200,382,382,382,200,382,382,382,382', +'114,237,382,382,240,240,382,128,121,382,5,5,121,267,5,108,5,382,109', +'108,345,232,345,382,300,271,300,382,382,113,382,382,109,5,109,273,109', +'155,382,5,224,5,240,5,5,240,5,5,5,5,5,5,5,5,222,45,5,5,109,277,5,149', +'149,5,218,240,381,381,147,147,381,5,381,381,291,293,240,5,295,296,168', +'5,5,45,5,5,104,45,299,381,131,301,5,302,168,381,168,381,168,381,381', +'217,381,381,381,102,381,381,381,381,306,307,381,381,308,309,381,257', +'215,381,168,314,379,379,73,71,379,381,379,379,61,60,327,381,233,168', +'168,381,381,213,381,381,168,330,193,379,332,46,381,192,339,379,340,379', +'40,379,379,236,379,379,379,39,379,379,379,379,348,349,379,379,351,352', +'379,356,357,379,186,186,358,38,186,364,186,379,8,8,8,8,365,379,368,241', +'6,379,379,1,379,379,386,186,390,238,238,392,379,186,394,186,,186,186', +',186,186,186,,186,186,,,,,186,186,,,186,,,186,12,12,,,12,,12,186,,238', +',,238,186,,,,186,186,,186,186,,12,,202,202,,186,12,,12,238,12,12,,12', +'12,12,,12,12,,238,,,12,12,,,12,,,12,13,13,,,13,,13,12,,202,,,202,12', +',,,12,12,,12,12,,13,,49,49,,12,13,,13,202,13,13,,13,13,13,,13,13,,202', +',,13,13,,,13,,,13,14,14,,,14,,14,13,164,49,,,49,13,,,,13,13,,13,13,164', +'14,164,,164,,13,14,,14,49,14,14,,14,14,14,,14,14,,49,,,14,14,164,,14', +',,14,360,360,,,360,,360,14,167,,,,,14,,,164,14,14,,14,14,167,360,167', +',167,,14,360,,360,,360,360,,360,360,360,,360,360,360,360,,,360,360,167', +',360,,,360,347,347,,,347,,347,360,169,167,167,,,360,,,167,360,360,,360', +'360,169,347,169,,169,,360,347,,347,,347,347,,347,347,347,,347,347,,', +',,347,347,169,,347,,,347,189,189,,,189,,189,347,107,169,169,,,347,,', +'169,347,347,,347,347,107,189,107,,107,,347,189,,189,,189,189,,189,189', +'189,,189,189,,,,,189,189,107,,189,,,189,41,41,,,41,,41,189,,,,,,189', +',,,189,189,,189,189,,41,,,,,189,41,,41,,41,41,,41,41,41,,41,41,,,,,41', +'41,,,41,,,41,42,42,,,42,,42,41,,,,,,41,,,,41,41,,41,41,,42,,,,,41,42', +',42,,42,42,,42,42,42,,42,42,,,,,42,42,,,42,,,42,43,43,,,43,,43,42,,', +',,,42,,,,42,42,,42,42,,43,,,,,42,43,,43,,43,43,,43,43,43,,43,43,,,,', +'43,43,,,43,,,43,44,44,,,44,,44,43,,,,,,43,,,,43,43,,43,43,,44,,,,,43', +'44,,44,,44,44,,44,44,44,,44,44,,,,,44,44,,,44,,,44,190,190,,,190,,190', +'44,,,,,,44,,,,44,44,,44,44,,190,,,,,44,190,,190,,190,190,,190,190,190', +',190,190,,,,,190,190,,,190,,,190,191,191,,,191,,191,190,,,,,,190,,,', +'190,190,,190,190,,191,,,,,190,191,,191,,191,191,,191,191,191,,191,191', +',,,,191,191,,,191,,,191,331,331,,,331,,331,191,,,,,,191,,,,191,191,', +'191,191,,331,,,,,191,331,,331,,331,331,,331,331,331,,331,331,,,,,331', +'331,,,331,,,331,,,220,220,,,220,331,220,220,,,,331,,,,331,331,,331,331', +',,,220,,,331,,,220,,220,,220,220,,220,220,220,,220,220,220,220,,,220', +'220,,,220,,,220,51,51,,,51,51,51,220,,,,,,220,,,,220,220,,220,220,,51', +',,,,220,51,,51,,51,51,,51,51,51,,51,51,,,,,51,51,,,51,,,51,52,52,,,52', +'52,52,51,,,,,,51,,,,51,51,,51,51,,52,,,,,51,52,,52,,52,52,,52,52,52', +',52,52,,,,,52,52,,,52,,,52,,,53,53,,,53,52,53,53,,,,52,,,,52,52,,52', +'52,,,,53,,,52,,,53,,53,,53,53,,53,53,53,,53,53,,,,,53,53,,,53,,,53,58', +'58,,,58,,58,53,,,,,,53,,,,53,53,,53,53,,58,,,,,53,58,,58,,58,58,,58', +'58,58,,58,58,,,,,58,58,,,58,,,58,,,227,227,,,227,58,227,227,,,,58,,', +',58,58,,58,58,,,,227,,,58,,,227,,227,,227,227,,227,227,227,,227,227', +'227,227,,,227,227,,,227,,,227,,,153,153,,,153,227,153,153,,,,227,,,', +'227,227,,227,227,,,,153,,,227,,,153,,153,,153,153,,153,153,153,,153', +'153,153,153,,,153,153,,,153,,,153,63,63,,,63,,63,153,,,,,,153,,,,153', +'153,,153,153,,63,,,,,153,63,,63,,63,63,,63,63,63,,63,63,,,,,63,63,,', +'63,,,63,,,316,316,,,316,63,316,316,,,,63,,,,63,63,,63,63,,,,316,,,63', +',,316,,316,,316,316,,316,316,316,,316,316,316,316,,,316,316,,,316,,', +'316,72,72,,,72,,72,316,,,,,,316,,,,316,316,,316,316,,72,,,,,316,72,', +'72,,72,72,,72,72,72,,72,72,72,72,,,72,72,,,72,,,72,315,315,,,315,,315', +'72,,,,,,72,,,,72,72,,72,72,,315,,,,,72,315,,315,,315,315,,315,315,315', +',315,315,315,315,,,315,315,,,315,,,315,74,74,,,74,,74,315,,,,,,315,', +',,315,315,,315,315,,74,,,,,315,74,,74,,74,74,,74,74,74,,74,74,74,74', +',,74,74,,,74,,,74,75,75,,,75,,75,74,,,,,,74,,,,74,74,,74,74,,75,,,,', +'74,75,,75,,75,75,,75,75,75,,75,75,75,75,,,75,75,,,75,,,75,76,76,,,76', +',76,75,,,,,,75,,,,75,75,,75,75,,76,,,,,75,76,,76,,76,76,,76,76,76,,76', '76,76,76,,,76,76,,,76,,,76,77,77,,,77,,77,76,,,,,,76,,,,76,76,,76,76', -',77,,,76,,,77,,77,,77,77,,77,77,77,,77,77,77,77,,,77,77,,,77,,,77,78', -'78,,,78,,78,77,,,,,,77,,,,77,77,,77,77,,78,,,77,,,78,,78,,78,78,,78', +',77,,,,,76,77,,77,,77,77,,77,77,77,,77,77,77,77,,,77,77,,,77,,,77,78', +'78,,,78,,78,77,,,,,,77,,,,77,77,,77,77,,78,,,,,77,78,,78,,78,78,,78', '78,78,,78,78,78,78,,,78,78,,,78,,,78,79,79,,,79,,79,78,,,,,,78,,,,78', -'78,,78,78,,79,,,78,,,79,,79,,79,79,,79,79,79,,79,79,,,,,79,79,,,79,', -',79,80,80,,,80,,80,79,,,,,,79,,,,79,79,,79,79,,80,,,79,,,80,,80,,80', +'78,,78,78,,79,,,,,78,79,,79,,79,79,,79,79,79,,79,79,,,,,79,79,,,79,', +',79,80,80,,,80,,80,79,,,,,,79,,,,79,79,,79,79,,80,,,,,79,80,,80,,80', '80,,80,80,80,,80,80,,,,,80,80,,,80,,,80,81,81,,,81,,81,80,,,,,,80,,', -',80,80,,80,80,,81,,,80,,,81,,81,,81,81,,81,81,81,,81,81,,,,,81,81,,', -'81,,,81,82,82,,,82,,82,81,,,,,,81,,,,81,81,,81,81,,82,,,81,,,82,,82', +',80,80,,80,80,,81,,,,,80,81,,81,,81,81,,81,81,81,,81,81,,,,,81,81,,', +'81,,,81,82,82,,,82,,82,81,,,,,,81,,,,81,81,,81,81,,82,,,,,81,82,,82', ',82,82,,82,82,82,,82,82,,,,,82,82,,,82,,,82,83,83,,,83,,83,82,,,,,,82', -',,,82,82,,82,82,,83,,,82,,,83,,83,,83,83,,83,83,83,,83,83,,,,,83,83', -',,83,,,83,84,84,,,84,,84,83,,,,,,83,,,,83,83,,83,83,,84,,,83,,,84,,84', +',,,82,82,,82,82,,83,,,,,82,83,,83,,83,83,,83,83,83,,83,83,,,,,83,83', +',,83,,,83,84,84,,,84,,84,83,,,,,,83,,,,83,83,,83,83,,84,,,,,83,84,,84', ',84,84,,84,84,84,,84,84,,,,,84,84,,,84,,,84,85,85,,,85,,85,84,,,,,,84', -',,,84,84,,84,84,,85,,,84,,,85,,85,,85,85,,85,85,85,,85,85,,,,,85,85', -',,85,,,85,86,86,,,86,,86,85,,,,,,85,,,,85,85,,85,85,,86,,,85,,,86,,86', +',,,84,84,,84,84,,85,,,,,84,85,,85,,85,85,,85,85,85,,85,85,,,,,85,85', +',,85,,,85,86,86,,,86,,86,85,,,,,,85,,,,85,85,,85,85,,86,,,,,85,86,,86', ',86,86,,86,86,86,,86,86,,,,,86,86,,,86,,,86,87,87,,,87,,87,86,,,,,,86', -',,,86,86,,86,86,,87,,,86,,,87,,87,,87,87,,87,87,87,,87,87,,,,,87,87', -',,87,,,87,88,88,,,88,,88,87,,,,,,87,,,,87,87,,87,87,,88,,,87,,,88,,88', +',,,86,86,,86,86,,87,,,,,86,87,,87,,87,87,,87,87,87,,87,87,,,,,87,87', +',,87,,,87,88,88,,,88,,88,87,,,,,,87,,,,87,87,,87,87,,88,,,,,87,88,,88', ',88,88,,88,88,88,,88,88,,,,,88,88,,,88,,,88,89,89,,,89,,89,88,,,,,,88', -',,,88,88,,88,88,,89,,,88,,,89,,89,,89,89,,89,89,89,,89,89,,,,,89,89', -',,89,,,89,90,90,,,90,,90,89,,,,,,89,,,,89,89,,89,89,,90,,,89,,,90,,90', +',,,88,88,,88,88,,89,,,,,88,89,,89,,89,89,,89,89,89,,89,89,,,,,89,89', +',,89,,,89,90,90,,,90,,90,89,,,,,,89,,,,89,89,,89,89,,90,,,,,89,90,,90', ',90,90,,90,90,90,,90,90,,,,,90,90,,,90,,,90,91,91,,,91,,91,90,,,,,,90', -',,,90,90,,90,90,,91,,,90,,,91,,91,,91,91,,91,91,91,,91,91,,,,,91,91', -',,91,,,91,92,92,,,92,,92,91,,,,,,91,,,,91,91,,91,91,,92,,,91,,,92,,92', +',,,90,90,,90,90,,91,,,,,90,91,,91,,91,91,,91,91,91,,91,91,,,,,91,91', +',,91,,,91,92,92,,,92,,92,91,,,,,,91,,,,91,91,,91,91,,92,,,,,91,92,,92', ',92,92,,92,92,92,,92,92,,,,,92,92,,,92,,,92,93,93,,,93,,93,92,,,,,,92', -',,,92,92,,92,92,,93,,,92,,,93,,93,,93,93,,93,93,93,,93,93,,,,,93,93', -',,93,,,93,94,94,,,94,,94,93,,,,,,93,,,,93,93,,93,93,,94,,,93,,,94,,94', +',,,92,92,,92,92,,93,,,,,92,93,,93,,93,93,,93,93,93,,93,93,,,,,93,93', +',,93,,,93,94,94,,,94,,94,93,,,,,,93,,,,93,93,,93,93,,94,,,,,93,94,,94', ',94,94,,94,94,94,,94,94,,,,,94,94,,,94,,,94,95,95,,,95,,95,94,,,,,,94', -',,,94,94,,94,94,,95,,,94,,,95,,95,,95,95,,95,95,95,,95,95,,,,,95,95', -',,95,,,95,96,96,,,96,,96,95,,,,,,95,,,,95,95,,95,95,,96,,,95,,,96,,96', +',,,94,94,,94,94,,95,,,,,94,95,,95,,95,95,,95,95,95,,95,95,,,,,95,95', +',,95,,,95,96,96,,,96,,96,95,,,,,,95,,,,95,95,,95,95,,96,,,,,95,96,,96', ',96,96,,96,96,96,,96,96,,,,,96,96,,,96,,,96,97,97,,,97,,97,96,,,,,,96', -',,,96,96,,96,96,,97,,,96,,,97,,97,,97,97,,97,97,97,,97,97,,,,,97,97', -',,97,,,97,98,98,,,98,,98,97,,,,,,97,,,,97,97,,97,97,,98,,,97,,,98,,98', +',,,96,96,,96,96,,97,,,,,96,97,,97,,97,97,,97,97,97,,97,97,,,,,97,97', +',,97,,,97,98,98,,,98,,98,97,,,,,,97,,,,97,97,,97,97,,98,,,,,97,98,,98', ',98,98,,98,98,98,,98,98,,,,,98,98,,,98,,,98,99,99,,,99,,99,98,,,,,,98', -',,,98,98,,98,98,,99,,,98,,,99,,99,,99,99,,99,99,99,,99,99,,,,,99,99', -',,99,,,99,100,100,,,100,,100,99,,,,,,99,,,,99,99,,99,99,,100,,,99,,', +',,,98,98,,98,98,,99,,,,,98,99,,99,,99,99,,99,99,99,,99,99,,,,,99,99', +',,99,,,99,100,100,,,100,,100,99,,,,,,99,,,,99,99,,99,99,,100,,,,,99', '100,,100,,100,100,,100,100,100,,100,100,,,,,100,100,,,100,,,100,101', -'101,,,101,,101,100,,,,,,100,,,,100,100,,100,100,,101,,,100,,,101,,101', -',101,101,,101,101,101,,101,101,,,,,101,101,,,101,,,101,,,301,301,,,301', -'101,301,301,,,,101,,,101,101,101,,101,101,,,,301,101,,,,,301,,301,,301', -'301,,301,301,301,,301,301,,,,,301,301,,,301,,,301,103,103,,,103,,103', -'301,,,,,,301,,,,301,301,,301,301,,103,,,301,,,103,103,103,103,103,103', -'103,103,103,103,,103,103,,,,,103,103,103,103,103,,,103,294,294,,,294', -',294,103,,,,,103,103,,,,103,103,,103,103,,294,,,103,,,294,,294,,294', -'294,,294,294,294,,294,294,,,,,294,294,,,294,,,294,105,105,,,105,,105', -'294,,,,,,294,,,,294,294,,294,294,,105,,,294,,,105,,105,,105,105,,105', +'101,,,101,,101,100,,,,,,100,,,,100,100,,100,100,,101,,,,,100,101,,101', +',101,101,,101,101,101,,101,101,,,,,101,101,,,101,,,101,,,304,304,,,304', +'101,304,304,,,,101,,,101,101,101,,101,101,,,,304,,,101,,,304,,304,,304', +'304,,304,304,304,,304,304,,,,,304,304,,,304,,,304,103,103,,,103,,103', +'304,,,,,,304,,,,304,304,,304,304,,103,,,,,304,103,103,103,103,103,103', +'103,103,103,103,,103,103,,,,,103,103,103,103,103,,,103,297,297,,,297', +',297,103,,,,,103,103,,,,103,103,,103,103,,297,,,,,103,297,,297,,297', +'297,,297,297,297,,297,297,,,,,297,297,,,297,,,297,105,105,,,105,,105', +'297,,,,,,297,,,,297,297,,297,297,,105,,,,,297,105,,105,,105,105,,105', '105,105,,105,105,,,,,105,105,,,105,,,105,106,106,,,106,,106,105,,,,', -',105,,,,105,105,,105,105,,106,,,105,,,106,,106,,106,106,,106,106,106', -',106,106,,,,,106,106,,,106,,,106,287,287,,,287,,287,106,,,,,,106,,,', -'106,106,,106,106,,287,,,106,,,287,,287,,287,287,,287,287,287,,287,287', -',,,,287,287,,,287,,,287,273,273,,,273,,273,287,,,,,,287,,,,287,287,', -'287,287,,273,,,287,,,273,,273,,273,273,,273,273,273,,273,273,,,,,273', -'273,,,273,,,273,272,272,,,272,,272,273,,,,,,273,167,,,273,273,,273,273', -',272,,,273,,167,272,167,272,167,272,272,,272,272,272,,272,272,,,,,272', -'272,,,272,,,272,167,,228,228,,,228,272,228,228,,,,272,,167,167,272,272', -',272,272,167,,,228,272,,,,,228,,228,,228,228,,228,228,228,,228,228,228', -'228,,,228,228,,,228,,,228,111,111,,,111,,111,228,,,,,,228,,,,228,228', -',228,228,,111,111,,228,,,111,,111,,111,111,,111,111,111,,111,111,,,', -',111,111,,,111,,,111,269,269,,,269,,269,111,,,,,,111,,,,111,111,,111', -'111,,269,,,111,,,269,,269,,269,269,,269,269,269,,269,269,,,,,269,269', -',,269,,,269,263,263,,,263,,263,269,,,,,,269,,,,269,269,,269,269,,263', -',,269,,,263,,263,,263,263,,263,263,263,,263,263,,,,,263,263,,,263,,', -'263,115,115,,,115,,115,263,,,,,,263,,,,263,263,,263,263,,115,115,,263', -',,115,,115,,115,115,,115,115,115,,115,115,,,,,115,115,,,115,,,115,150', -'150,,,150,,150,115,,,,,,115,,,,115,115,,115,115,,150,,,115,,,150,,150', -',150,150,,150,150,150,,150,150,150,150,,,150,150,,,150,,,150,229,229', -',,229,,229,150,,,,,,150,,,,150,150,,150,150,,229,,,150,,,229,,229,,229', -'229,,229,229,229,,229,229,,,,,229,229,,,229,,,229,244,244,,,244,244', -'244,229,,,,,,229,,,,229,229,,229,229,,244,,,229,,,244,,244,,244,244', -',244,244,244,,244,244,,,,,244,244,,,244,,,244,231,231,,,231,,231,244', -',,,,,244,,,,244,244,,244,244,,231,,,244,,,231,,231,,231,231,,231,231', -'231,,231,231,,,,,231,231,,,231,,,231,262,262,,,262,,262,231,,,,,,231', -',,,231,231,,231,231,,262,,,231,,,262,,262,,262,262,,262,262,262,,262', -'262,,,,,262,262,,,262,,,262,122,122,,,122,,122,262,,,,,,262,,,,262,262', -',262,262,,122,,,262,,,122,,122,,122,122,,122,122,122,,122,122,,,,,122', -'122,,,122,,,122,242,242,,,242,242,242,122,,,,,,122,,,,122,122,,122,122', -',242,,,122,,,242,,242,,242,242,,242,242,242,,242,242,,,,,242,242,,,242', -',,242,253,253,,,253,,253,242,,,,,,242,169,,,242,242,,242,242,,253,,', -'242,,169,253,169,253,169,253,253,,253,253,253,,253,253,,,,,253,253,', -',253,,,253,169,,248,248,,,248,253,248,248,,,,253,,169,169,253,253,,253', -'253,169,,,248,253,,,,,248,,248,,248,248,,248,248,248,,248,248,,,,,248', -'248,,,248,,,248,246,246,,,246,,246,248,,,,,,248,,,,248,248,,248,248', -',246,,,248,,,246,,246,,246,246,,246,246,246,,246,246,,,,,246,246,,,246', -',,246,230,230,,,230,,230,246,,,,,,246,,,,246,246,,246,246,,230,,,246', -',,230,230,230,230,230,230,230,230,230,230,,230,230,,,,,230,230,230,230', -'230,,,230,,,,,,,,230,,,,,230,230,,,,230,230,136,230,230,,,,,230,,136', -'136,136,136,136,136,,136,,136,,,136,136,136,136,,,,,,,,,,,,,,,,136,', -',,136,136,,,136,136,136,136,136,136,,136,136,,259,,259,,136,259,,,,259', -'259,259,259,259,259,,259,,259,136,,259,259,259,259,,,,,,,,,,,,,,,,259', -',,,259,259,,,259,259,259,259,259,259,,259,259,,141,,,,259,141,,,,141', -'141,141,141,141,141,,141,,141,259,,141,141,141,141,,,,,,,,,,,,,,,,141', -',,,141,141,,,141,141,141,141,141,141,,141,141,120,,120,,,141,,,,120', -'120,120,120,120,120,,120,,120,,141,120,120,120,120,,,,,,,,,,,,,,,,120', -',,,120,120,,,120,120,120,120,120,120,,120,120,145,,,,,120,,,,145,145', -'145,145,145,145,,145,,145,,120,145,145,145,145,,,,,,,,,,,,,,,,145,,', -',145,145,,,145,145,145,145,145,145,,145,145,119,,119,,,145,,,,119,119', -'119,119,119,119,,119,,119,,145,119,119,119,119,,,,,,,,,,,,,,,,119,,', -',119,119,,,119,119,119,119,119,119,,119,119,118,,118,,,119,,,,118,118', -'118,118,118,118,,118,,118,,119,118,118,118,118,,,,,,,,,,,,,,,,118,,', -',118,118,,,118,118,118,118,118,118,,118,118,116,,116,,,118,,,,116,116', -'116,116,116,116,,116,,116,,118,116,116,116,116,,,,,,,,,,,,,,,,116,,', -',116,116,,,116,116,116,116,116,116,,116,116,,110,,,,116,,,,,110,110', -'110,110,110,110,,110,,110,116,110,110,110,110,110,,,,,,,,,,,,,,,,110', -',,,110,110,,,110,110,110,110,110,110,,110,110,152,,,,,110,,,,152,152', -'152,152,152,152,,152,,152,,110,152,152,152,152,,,,,,,,,,,,,,,,152,,', -',152,152,,,152,152,152,152,152,152,,152,152,317,,,,,152,,,,317,317,317', -'317,317,317,,317,,317,,152,317,317,317,317,,,,,,,,,,,,,,,,317,,,,317', -'317,,,317,317,317,317,317,317,,317,317,320,,,,,317,,,,320,320,320,320', -'320,320,,320,,320,,317,320,320,320,320,,,,,,,,,,,,,,,,320,,,,320,320', -',,320,320,320,320,320,320,,320,320,326,,,,,320,,,,326,326,326,326,326', -'326,,326,,326,,320,326,326,326,326,,,,,,,,,,,,,,,,326,,,,326,326,,,326', -'326,326,326,326,326,,326,326,212,,,,,326,,,,212,212,212,212,212,212', -',212,,212,,326,212,212,212,212,,,,,,,,,,,,,,,,212,,,,212,212,,,212,212', -'212,212,212,212,,212,212,334,,,,,212,,,,334,334,334,334,334,334,,334', -',334,,212,334,334,334,334,,,,,,,,,,,,,,,,334,,,,334,334,,,334,334,334', -'334,334,334,,334,334,335,,,,,334,,,,335,335,335,335,335,335,,335,,335', -',334,335,335,335,335,,,,,,,,,,,,,,,,335,,,,335,335,,,335,335,335,335', -'335,335,,335,335,341,,,,,335,,,,341,341,341,341,341,341,,341,,341,,335', -'341,341,341,341,,,,,,,,,,,,,,,,341,,,,341,341,,,341,341,341,341,341', -'341,,341,341,188,,,,,341,,,,188,188,188,188,188,188,188,188,,188,,341', -'188,188,188,188,,,,,,,,,,,,,,,,188,,,,188,188,,,188,188,188,188,188', -'188,,188,188,11,,11,,,188,,,,11,11,11,11,11,11,,11,170,11,,188,11,11', -'11,11,,,,,,,170,,170,,170,,,,,11,,,,11,11,,,11,11,11,11,11,11,,11,11', -'170,,,171,,11,,,170,170,170,170,,,,170,170,171,,171,11,171,170,,,,172', -',,,,,,,,,,170,,,172,,172,171,172,,173,,,,,171,171,171,171,,,,171,171', -'173,,173,,173,171,172,,,,,,,,172,172,172,172,172,172,171,172,172,,,', -'173,,172,,,174,,,173,173,173,173,173,173,,173,173,172,174,174,,174,173', -'174,,175,174,,,,,,,,,,,173,175,175,,175,,175,,174,175,,,,,,,174,174', -'174,174,174,174,,174,174,,,,175,,174,,,176,,,175,175,175,175,175,175', -',175,175,174,176,176,,176,175,176,,177,176,,,,,,,,,,,175,177,177,,177', -',177,,176,177,,,,,,,176,176,176,176,176,176,,176,176,,,,177,,176,,,', -',178,177,177,177,177,177,177,,177,177,176,,178,178,178,177,178,,178', -',179,178,178,178,178,,,,,,177,,179,179,179,,179,,179,,178,179,179,179', -'179,,,,178,178,178,178,178,178,,178,178,,,,179,,178,,,179,,,179,179', -'179,179,179,179,,179,179,178,183,,,,179,183,,,,183,183,183,183,183,183', -',183,,183,179,,183,183,183,183,,,,,,,,,,,,,,,,183,,,,183,183,,,183,183', -'183,183,183,183,,183,183,181,,,,,183,,,,181,181,181,181,181,181,,181', -',181,,183,181,181,181,181,,,,,,,,,,,,,,,,181,,,,181,181,,,181,181,181', -'181,181,181,,181,181,182,,,,,181,,,,182,182,182,182,182,182,,182,,182', -',181,182,182,182,182,,,,,,,,,,,,,,,,182,,,,182,182,,,182,182,182,182', -'182,182,,182,182,180,,,,,182,,,,180,180,180,180,180,180,,180,,180,,182', -'180,180,180,180,,,,,,,,,,,,,,,,180,,,,180,180,,,180,180,180,180,180', -'180,,180,180,,,,,,180,211,211,211,211,,211,211,211,211,211,,211,211', -',180,,,,,211,211,211,271,271,271,271,,271,271,271,271,271,,271,271,', -',211,211,,,271,271,271,266,266,266,266,,266,266,266,266,266,,266,266', -',,271,271,,,266,266,266,,,,,,,,,,,,,,,,266,266' ] - racc_action_check = arr = ::Array.new(6173, nil) +',105,,,,105,105,,105,105,,106,,,,,105,106,,106,,106,106,,106,106,106', +',106,106,,,,,106,106,,,106,,,106,290,290,,,290,,290,106,,,,,,106,,,', +'106,106,,106,106,,290,,,,,106,290,,290,,290,290,,290,290,290,,290,290', +',,,,290,290,,,290,,,290,276,276,,,276,,276,290,,,,,,290,,,,290,290,', +'290,290,,276,,,,,290,276,,276,,276,276,,276,276,276,,276,276,,,,,276', +'276,,,276,,,276,275,275,,,275,,275,276,,,,,,276,,,,276,276,,276,276', +',275,,,,,276,275,,275,,275,275,,275,275,275,,275,275,,,,,275,275,,,275', +',,275,,,228,228,,,228,275,228,228,,,,275,,,,275,275,,275,275,,,,228', +',,275,,,228,,228,,228,228,,228,228,228,,228,228,228,228,,,228,228,,', +'228,,,228,111,111,,,111,,111,228,,,,,,228,,,,228,228,,228,228,,111,111', +',,,228,111,,111,,111,111,,111,111,111,,111,111,,,,,111,111,,,111,,,111', +'272,272,,,272,,272,111,,,,,,111,,,,111,111,,111,111,,272,,,,,111,272', +',272,,272,272,,272,272,272,,272,272,,,,,272,272,,,272,,,272,266,266', +',,266,,266,272,,,,,,272,,,,272,272,,272,272,,266,,,,,272,266,,266,,266', +'266,,266,266,266,,266,266,,,,,266,266,,,266,,,266,115,115,,,115,,115', +'266,,,,,,266,,,,266,266,,266,266,,115,115,,,,266,115,,115,,115,115,', +'115,115,115,,115,115,,,,,115,115,,,115,,,115,150,150,,,150,,150,115', +',,,,,115,,,,115,115,,115,115,,150,,,,,115,150,,150,,150,150,,150,150', +'150,,150,150,150,150,,,150,150,,,150,,,150,229,229,,,229,,229,150,,', +',,,150,,,,150,150,,150,150,,229,,,,,150,229,,229,,229,229,,229,229,229', +',229,229,,,,,229,229,,,229,,,229,244,244,,,244,244,244,229,,,,,,229', +',,,229,229,,229,229,,244,,,,,229,244,,244,,244,244,,244,244,244,,244', +'244,,,,,244,244,,,244,,,244,231,231,,,231,,231,244,,,,,,244,,,,244,244', +',244,244,,231,,,,,244,231,,231,,231,231,,231,231,231,,231,231,,,,,231', +'231,,,231,,,231,265,265,,,265,,265,231,,,,,,231,,,,231,231,,231,231', +',265,,,,,231,265,,265,,265,265,,265,265,265,,265,265,,,,,265,265,,,265', +',,265,122,122,,,122,,122,265,,,,,,265,,,,265,265,,265,265,,122,,,,,265', +'122,,122,,122,122,,122,122,122,,122,122,,,,,122,122,,,122,,,122,242', +'242,,,242,242,242,122,,,,,,122,,,,122,122,,122,122,,242,,,,,122,242', +',242,,242,242,,242,242,242,,242,242,,,,,242,242,,,242,,,242,253,253', +',,253,,253,242,,,,,,242,,,,242,242,,242,242,,253,,,,,242,253,,253,,253', +'253,,253,253,253,,253,253,,,,,253,253,,,253,,,253,,,248,248,,,248,253', +'248,248,,,,253,,,,253,253,,253,253,,,,248,,,253,,,248,,248,,248,248', +',248,248,248,,248,248,,,,,248,248,,,248,,,248,246,246,,,246,,246,248', +',,,,,248,,,,248,248,,248,248,,246,,,,,248,246,,246,,246,246,,246,246', +'246,,246,246,,,,,246,246,,,246,,,246,230,230,,,230,,230,246,,,,,,246', +',,,246,246,,246,246,,230,,,,,246,230,230,230,230,230,230,230,230,230', +'230,,230,230,,,,,230,230,230,230,230,,,230,,,,,,,,230,,,,,230,230,,', +',230,230,,230,230,136,,,,,,230,,,136,136,136,136,136,136,,136,,136,', +',136,136,136,136,,,,,,,,,,,,,,,,136,,,,136,136,,,136,136,136,136,136', +'136,,136,136,,,262,,262,136,,262,,,,262,262,262,262,262,262,,262,,262', +',136,262,262,262,262,,,,,,,,,,,,,,,,262,,,,262,262,,,262,262,262,262', +'262,262,141,262,262,,,141,,,262,141,141,141,141,141,141,,141,,141,,', +'141,141,141,141,262,,,,,,,,,,,,,,,141,,,,141,141,,,141,141,141,141,141', +'141,,141,141,,,120,,120,141,,,,,,120,120,120,120,120,120,,120,,120,', +'141,120,120,120,120,,,,,,,,,,,,,,,,120,,,,120,120,,,120,120,120,120', +'120,120,145,120,120,,,,,,120,145,145,145,145,145,145,,145,,145,,,145', +'145,145,145,120,,,,,,,,,,,,,,,145,,,,145,145,,,145,145,145,145,145,145', +',145,145,,,119,,119,145,,,,,,119,119,119,119,119,119,,119,,119,,145', +'119,119,119,119,,,,,,,,,,,,,,,,119,,,,119,119,,,119,119,119,119,119', +'119,,119,119,,,118,,118,119,,,,,,118,118,118,118,118,118,,118,,118,', +'119,118,118,118,118,,,,,,,,,,,,,,,,118,,,,118,118,,,118,118,118,118', +'118,118,,118,118,,,116,,116,118,,,,,,116,116,116,116,116,116,,116,,116', +',118,116,116,116,116,,,,,,,,,,,,,,,,116,,,,116,116,,,116,116,116,116', +'116,116,110,116,116,,,,,,116,110,110,110,110,110,110,,110,,110,,110', +'110,110,110,110,116,,,,,,,,,,,,,,,110,,,,110,110,,,110,110,110,110,110', +'110,152,110,110,,,,,,110,152,152,152,152,152,152,,152,,152,,,152,152', +'152,152,110,,,,,,,,,,,,,,,152,,,,152,152,,,152,152,152,152,152,152,320', +'152,152,,,,,,152,320,320,320,320,320,320,,320,,320,152,152,320,320,320', +'320,152,,,,,,,,,,,,,,,320,,,,320,320,,,320,320,320,320,320,320,323,320', +'320,,,,,,320,323,323,323,323,323,323,,323,,323,,,323,323,323,323,320', +',,,,,,,,,,,,,,323,,,,323,323,,,323,323,323,323,323,323,329,323,323,', +',,,,323,329,329,329,329,329,329,,329,,329,,,329,329,329,329,323,,,,', +',,,,,,,,,,329,,,,329,329,,,329,329,329,329,329,329,212,329,329,,,,,', +'329,212,212,212,212,212,212,,212,,212,,,212,212,212,212,329,,,,,,,,', +',,,,,,212,,,,212,212,,,212,212,212,212,212,212,337,212,212,,,,,,212', +'337,337,337,337,337,337,,337,,337,,,337,337,337,337,212,,,,,,,,,,,,', +',,337,,,,337,337,,,337,337,337,337,337,337,338,337,337,,,,,,337,338', +'338,338,338,338,338,,338,,338,165,,338,338,338,338,337,,,,,,,,165,,165', +',165,,,338,,,,338,338,,,338,338,338,338,338,338,,338,338,,,165,,,338', +',166,,,,,165,165,,,,165,165,,,166,338,166,165,166,,,,,,,,,,,,,,,,165', +',,,,,166,,,,,,,,,,166,166,,,344,166,166,,,,,,166,344,344,344,344,344', +'344,,344,,344,,,344,344,344,344,166,,,,,,,,,,,,,,,344,,,,344,344,,,344', +'344,344,344,344,344,188,344,344,,,,,,344,188,188,188,188,188,188,188', +'188,,188,,,188,188,188,188,344,,,,,,,,,,,,,,,188,,,,188,188,,,188,188', +'188,188,188,188,,188,188,,,11,,11,188,,,,,,11,11,11,11,11,11,,11,,11', +'170,188,11,11,11,11,,,,,,,,,170,,170,,170,,,11,,,,11,11,,,11,11,11,11', +'11,11,,11,11,,,170,,,11,,171,,,170,170,170,170,,,,170,170,,,171,11,171', +'170,171,172,,,,,,,,,,,,,,172,170,172,,172,,,171,,,,,,,,171,171,171,171', +',,,171,171,,,172,,,171,,173,,,172,172,172,172,172,172,,172,172,,,173', +'171,173,172,173,,,,,,,174,,,,,,,,,172,,,,174,174,173,174,,174,,175,174', +',173,173,173,173,173,173,,173,173,,175,175,,175,173,175,,174,175,,,', +'176,,,174,174,174,174,174,174,173,174,174,,176,176,175,176,174,176,', +',176,,175,175,175,175,175,175,,175,175,,,174,,,175,,,176,,,,,177,,,176', +'176,176,176,176,176,175,176,176,,177,177,,177,176,177,,,177,,,,,,,,', +',,,,176,,,,,,177,,,,,178,,,177,177,177,177,177,177,,177,177,178,178', +'178,,178,177,178,,,178,178,178,178,,,,,,,,,,177,,,,,,178,,,,,179,,,178', +'178,178,178,178,178,,178,178,179,179,179,,179,178,179,,,179,179,179', +'179,,,,,,,,,,178,,,,,,179,,,,,179,,,179,179,179,179,179,179,180,179', +'179,,,,,,179,180,180,180,180,180,180,,180,,180,,,180,180,180,180,179', +',,,,,,,,,,,,,,180,,,,180,180,,,180,180,180,180,180,180,183,180,180,', +',183,,,180,183,183,183,183,183,183,,183,,183,,,183,183,183,183,180,', +',,,,,,,,,,,,,183,,,,183,183,,,183,183,183,183,183,183,182,183,183,,', +',,,183,182,182,182,182,182,182,,182,,182,,,182,182,182,182,183,,,,,', +',,,,,,,,,182,,,,182,182,,,182,182,182,182,182,182,181,182,182,,,,,,182', +'181,181,181,181,181,181,,181,,181,,,181,181,181,181,182,,,,,,,,,,,,', +',,181,,,,181,181,,,181,181,181,181,181,181,,181,181,,,,,,181,274,274', +'274,274,,274,274,274,274,274,,274,274,,,,181,,,274,274,274,269,269,269', +'269,,269,269,269,269,269,,269,269,,,274,274,,,269,269,269,211,211,211', +'211,,211,211,211,211,211,,211,211,,,269,269,,,211,211,211,,,,,,,,,,', +',,,,,211,211' ] + racc_action_check = arr = ::Array.new(6233, nil) idx = 0 clist.each do |str| str.split(',', -1).each do |i| @@ -450,422 +453,426 @@ clist = [ end racc_action_pointer = [ - -2, 303, nil, nil, nil, 108, 289, nil, 274, nil, - nil, 5492, 328, 382, 436, nil, nil, nil, nil, nil, + -2, 295, nil, nil, nil, 108, 280, nil, 220, nil, + nil, 5532, 328, 382, 436, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, 266, 201, - 242, 652, 706, 760, 814, 65, 210, nil, 41, 245, + nil, nil, nil, nil, nil, nil, nil, nil, 254, 191, + 229, 652, 706, 760, 814, 147, 203, nil, 48, 407, nil, 1086, 1140, 1196, nil, nil, nil, nil, 1250, nil, - 161, 165, nil, 1416, nil, nil, nil, nil, nil, nil, - nil, 234, 1526, 220, 1634, 1688, 1742, 1796, 1850, 1904, + 156, 160, nil, 1416, nil, nil, nil, nil, nil, nil, + nil, 225, 1526, 212, 1634, 1688, 1742, 1796, 1850, 1904, 1958, 2012, 2066, 2120, 2174, 2228, 2282, 2336, 2390, 2444, 2498, 2552, 2606, 2660, 2714, 2768, 2822, 2876, 2930, 2984, - 3038, 3092, 175, 3202, 185, 3310, 3364, 62, -23, 8, - 4922, 3636, nil, 137, 30, 3798, 4864, nil, 4807, 4750, - 4636, 118, 4122, 63, nil, nil, nil, nil, 2, 27, - nil, 101, nil, nil, nil, nil, 4463, 71, nil, 100, - nil, 4579, 57, nil, nil, 4693, nil, 54, nil, 159, - 3852, -15, 4979, 1362, nil, 164, nil, nil, nil, nil, - nil, 106, 172, 2, 1426, 118, 986, 3536, 1260, 4240, - 5509, 5552, 5575, 5595, 5640, 5660, 5705, 5725, 5772, 5792, - 6021, 5907, 5964, 5850, nil, nil, 274, nil, 5435, 598, - 868, 922, 259, 258, nil, nil, -8, nil, -2, -1, - 55, 79, 241, -9, -4, nil, nil, nil, nil, nil, - nil, 6059, 5207, 210, nil, 227, nil, 230, 160, nil, - 1032, nil, 202, nil, 190, -7, nil, 1306, 3582, 3906, - 4394, 4014, 127, 128, nil, -10, 151, 127, 1057, nil, - 1134, 85, 4176, nil, 3960, nil, 4340, nil, 4286, nil, - nil, nil, nil, 4230, nil, nil, nil, 23, nil, 4521, - 95, nil, 4068, 3744, 117, nil, 6103, nil, 128, 3690, - 146, 6081, 3526, 3472, 157, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, 3418, 157, nil, - 178, nil, 118, 156, 3256, nil, 189, 100, 201, 179, - 0, 3148, nil, 176, 208, 180, 215, 217, nil, -25, - nil, 219, 1580, 1472, nil, nil, nil, 5036, nil, nil, - 5093, nil, nil, nil, 169, nil, 5150, 240, 976, 240, - nil, nil, nil, nil, 5264, 5321, 252, 196, nil, nil, - nil, 5378, 88, nil, 544, 265, 242, nil, 270, 271, - nil, nil, nil, 277, 278, 281, nil, 490, nil, nil, - nil, 266, 286, nil, nil, 289, nil, nil, nil, nil, - nil, nil, nil, nil, nil, 220, nil, 164, 54, nil, - nil, nil, 297, nil, nil, nil, 302, nil, 309, nil, - 311, nil, nil, nil, nil, nil ] + 3038, 3092, 165, 3202, 178, 3310, 3364, 602, 79, 112, + 4923, 3636, nil, 121, 30, 3798, 4869, nil, 4810, 4751, + 4638, 72, 4122, 10, nil, nil, nil, nil, 82, 27, + nil, 170, nil, nil, nil, nil, 4466, 71, nil, 61, + nil, 4579, 57, nil, nil, 4692, nil, 164, nil, 159, + 3852, -15, 4977, 1362, nil, 125, nil, nil, nil, nil, + nil, 74, 8, 2, 440, 5320, 5365, 494, 174, 548, + 5551, 5596, 5615, 5660, 5685, 5705, 5730, 5775, 5820, 5865, + 5919, 6081, 6027, 5973, nil, nil, 274, nil, 5473, 598, + 868, 922, 208, 232, nil, nil, -8, nil, -2, -9, + 55, -23, 353, -1, -4, nil, nil, nil, nil, nil, + nil, 6163, 5193, 192, nil, 195, nil, 189, 94, nil, + 1032, nil, 142, nil, 125, -7, nil, 1306, 3582, 3906, + 4394, 4014, 80, 197, nil, -14, 249, 93, 299, nil, + 102, 251, 4176, nil, 3960, nil, 4340, nil, 4286, nil, + nil, nil, nil, 4230, nil, nil, nil, 205, nil, nil, + nil, nil, 4525, 68, nil, 4068, 3744, 101, nil, 6141, + nil, 116, 3690, 126, 6119, 3526, 3472, 147, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + 3418, 148, nil, 166, nil, 108, 144, 3256, nil, 179, + 92, 182, 161, 4, 3148, nil, 169, 199, 173, 206, + 19, nil, -25, nil, 209, 1580, 1472, nil, nil, nil, + 5031, nil, nil, 5085, nil, nil, nil, 162, -21, 5139, + 234, 976, 234, nil, nil, nil, nil, 5247, 5301, 241, + 182, nil, nil, nil, 5419, 88, nil, 544, 258, 235, + nil, 262, 263, nil, nil, nil, 264, 265, 269, nil, + 490, nil, nil, nil, 255, 279, nil, nil, 281, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, 220, + nil, 164, 54, nil, nil, nil, 289, nil, nil, nil, + 291, nil, 294, nil, 297, nil, nil, nil, nil, nil ] racc_action_default = [ - -225, -226, -1, -2, -3, -4, -5, -8, -10, -11, - -16, -107, -226, -226, -226, -45, -46, -47, -48, -49, + -227, -228, -1, -2, -3, -4, -5, -8, -10, -11, + -16, -107, -228, -228, -228, -45, -46, -47, -48, -49, -50, -51, -52, -53, -54, -55, -56, -57, -58, -59, -60, -61, -62, -63, -64, -65, -66, -67, -72, -73, - -77, -226, -226, -226, -226, -226, -118, -120, -226, -226, - -165, -226, -226, -226, -178, -179, -180, -181, -226, -183, - -226, -194, -197, -226, -200, -201, -202, -203, -204, -205, - -206, -226, -226, -7, -226, -226, -226, -226, -226, -226, - -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, - -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, - -226, -226, -226, -127, -122, -225, -225, -28, -226, -35, - -226, -226, -74, -226, -226, -226, -226, -84, -226, -226, - -226, -226, -226, -225, -137, -156, -157, -119, -225, -225, - -146, -148, -149, -150, -151, -152, -43, -226, -168, -226, - -171, -226, -226, -174, -175, -187, -182, -226, -190, -226, - -226, -226, -198, -226, 396, -6, -9, -12, -13, -14, - -15, -226, -18, -19, -20, -21, -22, -23, -24, -25, + -77, -228, -228, -228, -228, -228, -118, -120, -228, -228, + -165, -228, -228, -228, -178, -179, -180, -181, -228, -183, + -228, -194, -197, -228, -202, -203, -204, -205, -206, -207, + -208, -228, -228, -7, -228, -228, -228, -228, -228, -228, + -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, + -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, + -228, -228, -228, -127, -122, -227, -227, -28, -228, -35, + -228, -228, -74, -228, -228, -228, -228, -84, -228, -228, + -228, -228, -228, -227, -137, -156, -157, -119, -227, -227, + -146, -148, -149, -150, -151, -152, -43, -228, -168, -228, + -171, -228, -228, -174, -175, -187, -182, -228, -190, -228, + -228, -228, -228, -228, 400, -6, -9, -12, -13, -14, + -15, -228, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -29, -30, -31, -32, -33, -34, -36, -37, - -38, -39, -40, -226, -41, -102, -226, -78, -226, -218, - -224, -212, -209, -207, -116, -128, -201, -131, -205, -226, - -215, -213, -221, -203, -204, -211, -216, -217, -219, -220, - -222, -127, -126, -226, -125, -226, -42, -207, -69, -79, - -226, -82, -207, -161, -164, -226, -76, -226, -226, -226, - -127, -226, -209, -225, -158, -226, -226, -226, -226, -154, - -226, -226, -226, -166, -226, -169, -226, -172, -226, -184, - -185, -186, -188, -226, -191, -192, -193, -207, -195, -107, - -226, -17, -226, -226, -207, -104, -127, -115, -226, -210, - -226, -208, -226, -226, -207, -130, -132, -212, -213, -214, - -215, -218, -221, -223, -224, -123, -124, -208, -226, -71, - -226, -81, -226, -208, -226, -75, -226, -87, -226, -93, - -226, -226, -97, -209, -207, -209, -226, -226, -140, -226, - -159, -207, -225, -226, -147, -155, -153, -44, -167, -170, - -177, -173, -176, -189, -226, -199, -106, -226, -208, -207, - -110, -117, -111, -129, -133, -134, -226, -68, -80, -83, - -162, -163, -87, -86, -226, -226, -93, -92, -226, -226, - -101, -96, -98, -226, -226, -226, -113, -225, -141, -142, - -143, -226, -226, -138, -139, -226, -145, -196, -103, -105, - -114, -121, -70, -85, -88, -226, -91, -226, -226, -108, - -109, -112, -226, -160, -135, -144, -226, -90, -226, -95, - -226, -100, -136, -89, -94, -99 ] + -38, -39, -40, -228, -41, -102, -228, -78, -228, -220, + -226, -214, -211, -209, -116, -128, -203, -131, -207, -228, + -217, -215, -223, -205, -206, -213, -218, -219, -221, -222, + -224, -127, -126, -228, -125, -228, -42, -209, -69, -79, + -228, -82, -209, -161, -164, -228, -76, -228, -228, -228, + -127, -228, -211, -227, -158, -228, -228, -228, -228, -154, + -228, -228, -228, -166, -228, -169, -228, -172, -228, -184, + -185, -186, -188, -228, -191, -192, -193, -209, -195, -198, + -200, -201, -107, -228, -17, -228, -228, -209, -104, -127, + -115, -228, -212, -228, -210, -228, -228, -209, -130, -132, + -214, -215, -216, -217, -220, -223, -225, -226, -123, -124, + -210, -228, -71, -228, -81, -228, -210, -228, -75, -228, + -87, -228, -93, -228, -228, -97, -211, -209, -211, -228, + -228, -140, -228, -159, -209, -227, -228, -147, -155, -153, + -44, -167, -170, -177, -173, -176, -189, -228, -228, -106, + -228, -210, -209, -110, -117, -111, -129, -133, -134, -228, + -68, -80, -83, -162, -163, -87, -86, -228, -228, -93, + -92, -228, -228, -101, -96, -98, -228, -228, -228, -113, + -227, -141, -142, -143, -228, -228, -138, -139, -228, -145, + -196, -199, -103, -105, -114, -121, -70, -85, -88, -228, + -91, -228, -228, -108, -109, -112, -228, -160, -135, -144, + -228, -90, -228, -95, -228, -100, -136, -89, -94, -99 ] racc_goto_table = [ - 2, 112, 4, 146, 107, 109, 110, 128, 132, 193, - 192, 134, 222, 268, 185, 347, 343, 233, 362, 314, - 1, 315, 236, 264, 302, 157, 158, 159, 160, 232, - 270, 213, 215, 116, 118, 119, 120, 73, 349, 331, - 266, 301, 333, 136, 136, 141, 374, 219, 184, 257, - 145, 307, 358, 306, 288, 152, 237, 156, 340, 292, - 322, 373, 376, 382, 254, 255, 3, 252, 253, 251, + 2, 112, 4, 146, 107, 109, 110, 128, 134, 185, + 259, 132, 222, 193, 365, 350, 184, 233, 346, 192, + 156, 271, 236, 334, 305, 157, 158, 159, 160, 73, + 317, 269, 318, 116, 118, 119, 120, 352, 232, 336, + 137, 139, 267, 136, 136, 141, 213, 215, 304, 257, + 145, 378, 310, 361, 237, 152, 219, 343, 325, 386, + 254, 309, 380, 377, 255, 3, 252, 253, 161, 251, 148, 136, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, - 180, 181, 182, 183, 324, 188, 352, 212, 212, 265, - 150, 327, nil, 136, 155, nil, nil, 136, nil, nil, - nil, 336, nil, nil, 188, 137, 139, 274, nil, nil, - nil, nil, nil, nil, 353, 234, 355, nil, nil, nil, - 234, 239, nil, 311, nil, nil, 304, 303, 305, nil, - nil, 354, nil, 161, nil, 259, nil, nil, 361, nil, - 256, nil, nil, 260, nil, nil, nil, nil, nil, 128, - nil, 132, nil, nil, 134, nil, 370, nil, nil, nil, - nil, nil, 329, nil, nil, 217, nil, nil, 183, 225, - 289, 116, 118, 119, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, 132, 323, 132, - 134, nil, 134, nil, nil, nil, nil, nil, nil, nil, + 180, 181, 182, 183, 268, 188, 155, 212, 212, 355, + 217, 150, 1, 136, 225, nil, nil, 136, nil, nil, + 273, nil, nil, nil, 188, nil, nil, nil, nil, nil, + nil, 277, nil, nil, nil, 234, nil, nil, nil, nil, + 234, 239, nil, 314, 291, 356, nil, 358, nil, 295, + 307, nil, nil, nil, nil, 262, 306, 308, nil, nil, + 256, nil, nil, 263, nil, nil, nil, nil, nil, 128, + nil, 134, nil, nil, 132, nil, nil, nil, nil, nil, + nil, nil, nil, nil, 327, nil, nil, nil, 183, 332, + 292, 116, 118, 119, 330, nil, 371, nil, nil, nil, + nil, nil, nil, nil, 339, nil, nil, 134, 326, 134, + 132, nil, 132, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - 290, 136, 188, 188, nil, nil, nil, 296, 298, nil, - nil, nil, nil, nil, 317, 308, 317, nil, 320, nil, - 141, 369, nil, nil, nil, 145, nil, nil, nil, nil, - nil, nil, nil, nil, 317, 326, nil, nil, nil, nil, - nil, 188, nil, nil, 334, 335, nil, nil, 359, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, 317, - nil, nil, nil, nil, nil, nil, 341, nil, nil, nil, - nil, nil, nil, 136, nil, nil, nil, nil, nil, 372, + 293, 136, 188, 188, 357, nil, nil, 299, 301, nil, + nil, 364, nil, nil, 320, 311, 320, nil, 323, 373, + 141, nil, nil, nil, nil, 145, nil, nil, nil, 374, + nil, nil, nil, nil, nil, nil, nil, 320, 329, nil, + nil, nil, nil, nil, 188, nil, nil, 337, 338, nil, + nil, 362, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, 320, nil, nil, nil, nil, nil, nil, 344, + nil, nil, nil, nil, nil, nil, 136, nil, nil, nil, + nil, nil, 376, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, 368, 367, nil, nil, + nil, nil, nil, 183, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, 116, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, 365, 364, nil, nil, nil, nil, nil, - 183, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, 116, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, 364, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, 386, nil, 388, 390 ] + nil, nil, 367, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, 390, + nil, 392, 394 ] racc_goto_check = [ - 2, 39, 4, 81, 10, 10, 10, 64, 37, 56, - 54, 31, 44, 55, 51, 47, 46, 65, 66, 72, - 1, 72, 65, 52, 49, 8, 8, 8, 8, 54, - 38, 60, 60, 10, 10, 10, 10, 6, 50, 57, - 58, 48, 61, 10, 10, 10, 45, 43, 13, 44, - 10, 68, 69, 55, 38, 10, 71, 7, 74, 38, - 76, 46, 47, 66, 77, 78, 3, 82, 83, 85, + 2, 39, 4, 81, 10, 10, 10, 64, 31, 51, + 88, 37, 44, 56, 66, 47, 13, 65, 46, 54, + 7, 55, 65, 57, 49, 8, 8, 8, 8, 6, + 72, 58, 72, 10, 10, 10, 10, 50, 54, 61, + 12, 12, 52, 10, 10, 10, 60, 60, 48, 44, + 10, 45, 68, 69, 71, 10, 43, 74, 76, 66, + 77, 55, 47, 46, 78, 3, 82, 83, 12, 85, 86, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, - 10, 10, 10, 10, 38, 10, 49, 10, 10, 51, - 87, 38, nil, 10, 6, nil, nil, 10, nil, nil, - nil, 38, nil, nil, 10, 12, 12, 56, nil, nil, - nil, nil, nil, nil, 55, 4, 55, nil, nil, nil, - 4, 4, nil, 44, nil, nil, 56, 54, 54, nil, - nil, 38, nil, 12, nil, 10, nil, nil, 38, nil, + 10, 10, 10, 10, 51, 10, 6, 10, 10, 49, + 12, 87, 1, 10, 12, nil, nil, 10, nil, nil, + 38, nil, nil, nil, 10, nil, nil, nil, nil, nil, + nil, 56, nil, nil, nil, 4, nil, nil, nil, nil, + 4, 4, nil, 44, 38, 55, nil, 55, nil, 38, + 56, nil, nil, nil, nil, 10, 54, 54, nil, nil, 2, nil, nil, 2, nil, nil, nil, nil, nil, 64, - nil, 37, nil, nil, 31, nil, 38, nil, nil, nil, - nil, nil, 56, nil, nil, 12, nil, nil, 10, 12, - 39, 10, 10, 10, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, 37, 81, 37, - 31, nil, 31, nil, nil, nil, nil, nil, nil, nil, + nil, 31, nil, nil, 37, nil, nil, nil, nil, nil, + nil, nil, nil, nil, 38, nil, nil, nil, 10, 56, + 39, 10, 10, 10, 38, nil, 88, nil, nil, nil, + nil, nil, nil, nil, 38, nil, nil, 31, 81, 31, + 37, nil, 37, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - 2, 10, 10, 10, nil, nil, nil, 2, 2, nil, - nil, nil, nil, nil, 10, 4, 10, nil, 10, nil, - 10, 51, nil, nil, nil, 10, nil, nil, nil, nil, - nil, nil, nil, nil, 10, 10, nil, nil, nil, nil, - nil, 10, nil, nil, 10, 10, nil, nil, 64, nil, + 2, 10, 10, 10, 38, nil, nil, 2, 2, nil, + nil, 38, nil, nil, 10, 4, 10, nil, 10, 51, + 10, nil, nil, nil, nil, 10, nil, nil, nil, 38, + nil, nil, nil, nil, nil, nil, nil, 10, 10, nil, + nil, nil, nil, nil, 10, nil, nil, 10, 10, nil, + nil, 64, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, 10, nil, nil, nil, nil, nil, nil, 10, + nil, nil, nil, nil, nil, nil, 10, nil, nil, nil, + nil, nil, 39, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, 2, 4, nil, nil, + nil, nil, nil, 10, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 10, - nil, nil, nil, nil, nil, nil, 10, nil, nil, nil, - nil, nil, nil, 10, nil, nil, nil, nil, nil, 39, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, 2, 4, nil, nil, nil, nil, nil, - 10, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, 10, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, 4, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, 2, nil, 2, 2 ] + nil, nil, 4, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, 2, + nil, 2, 2 ] racc_goto_pointer = [ - nil, 20, 0, 66, 2, nil, 32, -17, -50, nil, - -8, nil, 64, -53, nil, nil, nil, nil, nil, nil, + nil, 102, 0, 65, 2, nil, 24, -54, -50, nil, + -8, nil, -11, -85, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, -38, nil, nil, nil, nil, nil, -41, -163, -38, - nil, nil, nil, -66, -102, -298, -281, -284, -188, -205, - -262, -87, -163, nil, -93, -179, -94, -230, -148, nil, - -74, -229, nil, nil, -41, -106, -294, nil, -182, -257, - nil, -73, -219, nil, -235, nil, -188, -85, -84, nil, - nil, -55, -80, -79, nil, -78, 10, 39 ] + nil, -41, nil, nil, nil, nil, nil, -38, -83, -38, + nil, nil, nil, -57, -102, -296, -282, -287, -181, -205, + -266, -92, -144, nil, -84, -171, -90, -249, -157, nil, + -59, -235, nil, nil, -41, -106, -301, nil, -181, -259, + nil, -75, -208, nil, -239, nil, -190, -89, -85, nil, + nil, -55, -81, -80, nil, -78, 10, 40, -142 ] racc_goto_default = [ - nil, nil, 363, nil, 214, 5, 6, 7, 8, 9, - 11, 10, 300, nil, 15, 38, 16, 17, 18, 19, + nil, nil, 366, nil, 214, 5, 6, 7, 8, 9, + 11, 10, 303, nil, 15, 38, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, nil, nil, 39, 40, 113, nil, nil, 117, nil, nil, nil, nil, nil, nil, nil, 44, nil, nil, nil, 194, nil, 104, nil, 195, 199, 197, 124, nil, nil, 123, nil, nil, 129, nil, 130, 131, 223, 142, 144, 54, 55, 56, - 58, nil, nil, nil, 147, nil, nil, nil ] + 58, nil, nil, nil, 147, nil, nil, nil, nil ] racc_reduce_table = [ 0, 0, :racc_error, - 1, 87, :_reduce_1, - 1, 87, :_reduce_2, - 1, 87, :_reduce_none, - 1, 88, :_reduce_4, - 1, 91, :_reduce_5, - 3, 91, :_reduce_6, - 2, 91, :_reduce_7, - 1, 92, :_reduce_8, - 3, 92, :_reduce_9, - 1, 93, :_reduce_none, - 1, 94, :_reduce_11, - 3, 94, :_reduce_12, - 3, 94, :_reduce_13, - 3, 94, :_reduce_14, - 3, 94, :_reduce_15, - 1, 96, :_reduce_none, - 4, 96, :_reduce_17, - 3, 96, :_reduce_18, - 3, 96, :_reduce_19, - 3, 96, :_reduce_20, - 3, 96, :_reduce_21, - 3, 96, :_reduce_22, - 3, 96, :_reduce_23, - 3, 96, :_reduce_24, - 3, 96, :_reduce_25, - 3, 96, :_reduce_26, - 3, 96, :_reduce_27, - 2, 96, :_reduce_28, - 3, 96, :_reduce_29, - 3, 96, :_reduce_30, - 3, 96, :_reduce_31, - 3, 96, :_reduce_32, - 3, 96, :_reduce_33, - 3, 96, :_reduce_34, - 2, 96, :_reduce_35, - 3, 96, :_reduce_36, - 3, 96, :_reduce_37, - 3, 96, :_reduce_38, - 3, 96, :_reduce_39, - 3, 96, :_reduce_40, - 3, 96, :_reduce_41, - 3, 96, :_reduce_42, - 1, 98, :_reduce_43, - 3, 98, :_reduce_44, - 1, 97, :_reduce_none, - 1, 101, :_reduce_none, - 1, 101, :_reduce_none, - 1, 101, :_reduce_none, - 1, 101, :_reduce_none, - 1, 101, :_reduce_none, - 1, 101, :_reduce_none, - 1, 101, :_reduce_none, - 1, 101, :_reduce_none, - 1, 101, :_reduce_none, - 1, 101, :_reduce_none, - 1, 101, :_reduce_none, - 1, 102, :_reduce_none, - 1, 102, :_reduce_none, - 1, 102, :_reduce_none, - 1, 102, :_reduce_none, - 1, 102, :_reduce_none, - 1, 102, :_reduce_none, - 1, 102, :_reduce_none, - 1, 102, :_reduce_none, - 1, 102, :_reduce_none, - 1, 118, :_reduce_66, - 1, 118, :_reduce_67, - 5, 100, :_reduce_68, - 3, 100, :_reduce_69, - 6, 100, :_reduce_70, - 4, 100, :_reduce_71, - 1, 100, :_reduce_72, - 1, 104, :_reduce_73, - 2, 104, :_reduce_74, - 4, 126, :_reduce_75, - 3, 126, :_reduce_76, - 1, 126, :_reduce_77, - 3, 127, :_reduce_78, - 2, 125, :_reduce_79, - 3, 129, :_reduce_80, - 2, 129, :_reduce_81, - 2, 128, :_reduce_82, - 4, 128, :_reduce_83, - 2, 107, :_reduce_84, - 5, 131, :_reduce_85, - 4, 131, :_reduce_86, - 0, 132, :_reduce_none, - 2, 132, :_reduce_88, - 4, 132, :_reduce_89, - 3, 132, :_reduce_90, - 6, 108, :_reduce_91, - 5, 108, :_reduce_92, - 0, 133, :_reduce_none, - 4, 133, :_reduce_94, - 3, 133, :_reduce_95, - 5, 106, :_reduce_96, - 1, 134, :_reduce_97, - 2, 134, :_reduce_98, - 5, 135, :_reduce_99, - 4, 135, :_reduce_100, - 1, 136, :_reduce_101, + 1, 89, :_reduce_1, + 1, 89, :_reduce_2, + 1, 89, :_reduce_none, + 1, 90, :_reduce_4, + 1, 93, :_reduce_5, + 3, 93, :_reduce_6, + 2, 93, :_reduce_7, + 1, 94, :_reduce_8, + 3, 94, :_reduce_9, + 1, 95, :_reduce_none, + 1, 96, :_reduce_11, + 3, 96, :_reduce_12, + 3, 96, :_reduce_13, + 3, 96, :_reduce_14, + 3, 96, :_reduce_15, + 1, 98, :_reduce_none, + 4, 98, :_reduce_17, + 3, 98, :_reduce_18, + 3, 98, :_reduce_19, + 3, 98, :_reduce_20, + 3, 98, :_reduce_21, + 3, 98, :_reduce_22, + 3, 98, :_reduce_23, + 3, 98, :_reduce_24, + 3, 98, :_reduce_25, + 3, 98, :_reduce_26, + 3, 98, :_reduce_27, + 2, 98, :_reduce_28, + 3, 98, :_reduce_29, + 3, 98, :_reduce_30, + 3, 98, :_reduce_31, + 3, 98, :_reduce_32, + 3, 98, :_reduce_33, + 3, 98, :_reduce_34, + 2, 98, :_reduce_35, + 3, 98, :_reduce_36, + 3, 98, :_reduce_37, + 3, 98, :_reduce_38, + 3, 98, :_reduce_39, + 3, 98, :_reduce_40, + 3, 98, :_reduce_41, + 3, 98, :_reduce_42, + 1, 100, :_reduce_43, + 3, 100, :_reduce_44, 1, 99, :_reduce_none, - 4, 99, :_reduce_103, - 1, 138, :_reduce_104, - 3, 138, :_reduce_105, - 3, 137, :_reduce_106, - 1, 95, :_reduce_107, - 6, 95, :_reduce_108, - 6, 95, :_reduce_109, - 5, 95, :_reduce_110, - 5, 95, :_reduce_111, - 6, 95, :_reduce_112, - 5, 95, :_reduce_113, - 4, 143, :_reduce_114, - 1, 144, :_reduce_115, - 1, 140, :_reduce_116, - 3, 140, :_reduce_117, - 1, 139, :_reduce_118, - 2, 139, :_reduce_119, - 1, 139, :_reduce_120, - 6, 105, :_reduce_121, - 2, 105, :_reduce_122, - 3, 145, :_reduce_123, - 3, 145, :_reduce_124, - 1, 146, :_reduce_none, - 1, 146, :_reduce_none, - 0, 142, :_reduce_127, - 1, 142, :_reduce_128, - 3, 142, :_reduce_129, + 1, 103, :_reduce_none, + 1, 103, :_reduce_none, + 1, 103, :_reduce_none, + 1, 103, :_reduce_none, + 1, 103, :_reduce_none, + 1, 103, :_reduce_none, + 1, 103, :_reduce_none, + 1, 103, :_reduce_none, + 1, 103, :_reduce_none, + 1, 103, :_reduce_none, + 1, 103, :_reduce_none, + 1, 104, :_reduce_none, + 1, 104, :_reduce_none, + 1, 104, :_reduce_none, + 1, 104, :_reduce_none, + 1, 104, :_reduce_none, + 1, 104, :_reduce_none, + 1, 104, :_reduce_none, + 1, 104, :_reduce_none, + 1, 104, :_reduce_none, + 1, 120, :_reduce_66, + 1, 120, :_reduce_67, + 5, 102, :_reduce_68, + 3, 102, :_reduce_69, + 6, 102, :_reduce_70, + 4, 102, :_reduce_71, + 1, 102, :_reduce_72, + 1, 106, :_reduce_73, + 2, 106, :_reduce_74, + 4, 128, :_reduce_75, + 3, 128, :_reduce_76, + 1, 128, :_reduce_77, + 3, 129, :_reduce_78, + 2, 127, :_reduce_79, + 3, 131, :_reduce_80, + 2, 131, :_reduce_81, + 2, 130, :_reduce_82, + 4, 130, :_reduce_83, + 2, 109, :_reduce_84, + 5, 133, :_reduce_85, + 4, 133, :_reduce_86, + 0, 134, :_reduce_none, + 2, 134, :_reduce_88, + 4, 134, :_reduce_89, + 3, 134, :_reduce_90, + 6, 110, :_reduce_91, + 5, 110, :_reduce_92, + 0, 135, :_reduce_none, + 4, 135, :_reduce_94, + 3, 135, :_reduce_95, + 5, 108, :_reduce_96, + 1, 136, :_reduce_97, + 2, 136, :_reduce_98, + 5, 137, :_reduce_99, + 4, 137, :_reduce_100, + 1, 138, :_reduce_101, + 1, 101, :_reduce_none, + 4, 101, :_reduce_103, + 1, 140, :_reduce_104, + 3, 140, :_reduce_105, + 3, 139, :_reduce_106, + 1, 97, :_reduce_107, + 6, 97, :_reduce_108, + 6, 97, :_reduce_109, + 5, 97, :_reduce_110, + 5, 97, :_reduce_111, + 6, 97, :_reduce_112, + 5, 97, :_reduce_113, + 4, 145, :_reduce_114, + 1, 146, :_reduce_115, + 1, 142, :_reduce_116, + 3, 142, :_reduce_117, + 1, 141, :_reduce_118, + 2, 141, :_reduce_119, + 1, 141, :_reduce_120, + 6, 107, :_reduce_121, + 2, 107, :_reduce_122, + 3, 147, :_reduce_123, + 3, 147, :_reduce_124, 1, 148, :_reduce_none, 1, 148, :_reduce_none, - 1, 148, :_reduce_none, - 3, 147, :_reduce_133, - 3, 147, :_reduce_134, - 6, 109, :_reduce_135, - 7, 110, :_reduce_136, - 1, 153, :_reduce_137, - 1, 152, :_reduce_none, - 1, 152, :_reduce_none, + 0, 144, :_reduce_127, + 1, 144, :_reduce_128, + 3, 144, :_reduce_129, + 1, 150, :_reduce_none, + 1, 150, :_reduce_none, + 1, 150, :_reduce_none, + 3, 149, :_reduce_133, + 3, 149, :_reduce_134, + 6, 111, :_reduce_135, + 7, 112, :_reduce_136, + 1, 155, :_reduce_137, 1, 154, :_reduce_none, - 2, 154, :_reduce_141, - 1, 155, :_reduce_none, - 1, 155, :_reduce_none, - 6, 111, :_reduce_144, - 5, 111, :_reduce_145, - 1, 156, :_reduce_146, - 3, 156, :_reduce_147, - 1, 158, :_reduce_148, - 1, 158, :_reduce_149, - 1, 158, :_reduce_150, - 1, 158, :_reduce_none, - 1, 159, :_reduce_152, - 3, 159, :_reduce_153, + 1, 154, :_reduce_none, + 1, 156, :_reduce_none, + 2, 156, :_reduce_141, 1, 157, :_reduce_none, - 2, 157, :_reduce_155, - 1, 150, :_reduce_156, - 1, 150, :_reduce_157, - 1, 151, :_reduce_158, - 2, 151, :_reduce_159, - 4, 151, :_reduce_160, - 1, 130, :_reduce_161, - 3, 130, :_reduce_162, - 3, 160, :_reduce_163, - 1, 160, :_reduce_164, - 1, 103, :_reduce_165, - 3, 113, :_reduce_166, - 4, 113, :_reduce_167, - 2, 113, :_reduce_168, - 3, 113, :_reduce_169, - 4, 113, :_reduce_170, - 2, 113, :_reduce_171, - 3, 116, :_reduce_172, - 4, 116, :_reduce_173, - 2, 116, :_reduce_174, - 1, 161, :_reduce_175, - 3, 161, :_reduce_176, - 3, 162, :_reduce_177, - 1, 123, :_reduce_none, - 1, 123, :_reduce_none, - 1, 123, :_reduce_none, - 1, 163, :_reduce_181, - 2, 164, :_reduce_182, - 1, 166, :_reduce_183, - 1, 168, :_reduce_184, - 1, 169, :_reduce_185, - 2, 167, :_reduce_186, - 1, 170, :_reduce_187, - 1, 171, :_reduce_188, - 2, 171, :_reduce_189, - 2, 165, :_reduce_190, - 2, 172, :_reduce_191, - 2, 172, :_reduce_192, - 3, 89, :_reduce_193, - 0, 173, :_reduce_194, - 2, 173, :_reduce_195, - 4, 173, :_reduce_196, - 1, 112, :_reduce_197, - 2, 112, :_reduce_198, - 4, 112, :_reduce_199, - 1, 119, :_reduce_200, - 1, 122, :_reduce_201, - 1, 120, :_reduce_202, - 1, 121, :_reduce_203, - 1, 115, :_reduce_204, - 1, 114, :_reduce_205, + 1, 157, :_reduce_none, + 6, 113, :_reduce_144, + 5, 113, :_reduce_145, + 1, 158, :_reduce_146, + 3, 158, :_reduce_147, + 1, 160, :_reduce_148, + 1, 160, :_reduce_149, + 1, 160, :_reduce_150, + 1, 160, :_reduce_none, + 1, 161, :_reduce_152, + 3, 161, :_reduce_153, + 1, 159, :_reduce_none, + 2, 159, :_reduce_155, + 1, 152, :_reduce_156, + 1, 152, :_reduce_157, + 1, 153, :_reduce_158, + 2, 153, :_reduce_159, + 4, 153, :_reduce_160, + 1, 132, :_reduce_161, + 3, 132, :_reduce_162, + 3, 162, :_reduce_163, + 1, 162, :_reduce_164, + 1, 105, :_reduce_165, + 3, 115, :_reduce_166, + 4, 115, :_reduce_167, + 2, 115, :_reduce_168, + 3, 115, :_reduce_169, + 4, 115, :_reduce_170, + 2, 115, :_reduce_171, + 3, 118, :_reduce_172, + 4, 118, :_reduce_173, + 2, 118, :_reduce_174, + 1, 163, :_reduce_175, + 3, 163, :_reduce_176, + 3, 164, :_reduce_177, + 1, 125, :_reduce_none, + 1, 125, :_reduce_none, + 1, 125, :_reduce_none, + 1, 165, :_reduce_181, + 2, 166, :_reduce_182, + 1, 168, :_reduce_183, + 1, 170, :_reduce_184, + 1, 171, :_reduce_185, + 2, 169, :_reduce_186, + 1, 172, :_reduce_187, + 1, 173, :_reduce_188, + 2, 173, :_reduce_189, + 2, 167, :_reduce_190, + 2, 174, :_reduce_191, + 2, 174, :_reduce_192, + 3, 91, :_reduce_193, + 0, 175, :_reduce_194, + 2, 175, :_reduce_195, + 4, 175, :_reduce_196, + 1, 114, :_reduce_197, + 3, 114, :_reduce_198, + 5, 114, :_reduce_199, + 1, 176, :_reduce_none, + 1, 176, :_reduce_none, + 1, 121, :_reduce_202, + 1, 124, :_reduce_203, + 1, 122, :_reduce_204, + 1, 123, :_reduce_205, 1, 117, :_reduce_206, - 0, 124, :_reduce_none, - 1, 124, :_reduce_208, - 0, 141, :_reduce_none, - 1, 141, :_reduce_none, - 1, 149, :_reduce_none, - 1, 149, :_reduce_none, - 1, 149, :_reduce_none, - 1, 149, :_reduce_none, - 1, 149, :_reduce_none, - 1, 149, :_reduce_none, - 1, 149, :_reduce_none, - 1, 149, :_reduce_none, - 1, 149, :_reduce_none, - 1, 149, :_reduce_none, - 1, 149, :_reduce_none, - 1, 149, :_reduce_none, - 1, 149, :_reduce_none, - 1, 149, :_reduce_none, - 0, 90, :_reduce_225 ] + 1, 116, :_reduce_207, + 1, 119, :_reduce_208, + 0, 126, :_reduce_none, + 1, 126, :_reduce_210, + 0, 143, :_reduce_none, + 1, 143, :_reduce_none, + 1, 151, :_reduce_none, + 1, 151, :_reduce_none, + 1, 151, :_reduce_none, + 1, 151, :_reduce_none, + 1, 151, :_reduce_none, + 1, 151, :_reduce_none, + 1, 151, :_reduce_none, + 1, 151, :_reduce_none, + 1, 151, :_reduce_none, + 1, 151, :_reduce_none, + 1, 151, :_reduce_none, + 1, 151, :_reduce_none, + 1, 151, :_reduce_none, + 1, 151, :_reduce_none, + 0, 92, :_reduce_227 ] -racc_reduce_n = 226 +racc_reduce_n = 228 -racc_shift_n = 396 +racc_shift_n = 400 racc_token_table = { false => 0, @@ -947,15 +954,17 @@ racc_token_table = { :RENDER_STRING => 76, :RENDER_EXPR => 77, :EPP_START => 78, - :LOW => 79, - :HIGH => 80, - :CALL => 81, - :LISTSTART => 82, - :MODULO => 83, - :TITLE_COLON => 84, - :CASE_COLON => 85 } + :EPP_END => 79, + :EPP_END_TRIM => 80, + :LOW => 81, + :HIGH => 82, + :CALL => 83, + :LISTSTART => 84, + :MODULO => 85, + :TITLE_COLON => 86, + :CASE_COLON => 87 } -racc_nt_base = 86 +racc_nt_base = 88 racc_use_result_var = true @@ -1055,6 +1064,8 @@ Racc_token_to_s_table = [ "RENDER_STRING", "RENDER_EXPR", "EPP_START", + "EPP_END", + "EPP_END_TRIM", "LOW", "HIGH", "CALL", @@ -1149,7 +1160,8 @@ Racc_token_to_s_table = [ "text_expression", "dqtail", "sublocated_text", - "epp_parameters_list" ] + "epp_parameters_list", + "epp_end" ] Racc_debug_parser = false @@ -2444,79 +2456,79 @@ module_eval(<<'.,.,', 'egrammar.ra', 683) module_eval(<<'.,.,', 'egrammar.ra', 684) def _reduce_198(val, _values, result) - result = Factory.RENDER_EXPR(val[1]); loc result, val[0], val[1] + result = Factory.RENDER_EXPR(val[1]); loc result, val[0], val[2] result end .,., module_eval(<<'.,.,', 'egrammar.ra', 685) def _reduce_199(val, _values, result) - result = Factory.RENDER_EXPR(Factory.block_or_expression(*val[2])); loc result, val[0], val[3] + result = Factory.RENDER_EXPR(Factory.block_or_expression(*val[2])); loc result, val[0], val[4] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 687) - def _reduce_200(val, _values, result) +# reduce 200 omitted + +# reduce 201 omitted + +module_eval(<<'.,.,', 'egrammar.ra', 691) + def _reduce_202(val, _values, result) result = Factory.NUMBER(val[0][:value]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 688) - def _reduce_201(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 692) + def _reduce_203(val, _values, result) result = Factory.QNAME_OR_NUMBER(val[0][:value]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 689) - def _reduce_202(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 693) + def _reduce_204(val, _values, result) result = Factory.QREF(val[0][:value]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 690) - def _reduce_203(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 694) + def _reduce_205(val, _values, result) result = Factory.literal(:undef); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 691) - def _reduce_204(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 695) + def _reduce_206(val, _values, result) result = Factory.literal(:default); loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 696) - def _reduce_205(val, _values, result) +module_eval(<<'.,.,', 'egrammar.ra', 700) + def _reduce_207(val, _values, result) result = Factory.literal(val[0][:value]) ; loc result, val[0] result end .,., -module_eval(<<'.,.,', 'egrammar.ra', 699) - def _reduce_206(val, _values, result) - result = Factory.literal(val[0][:value]); loc result, val[0] - result - end -.,., - -# reduce 207 omitted - -module_eval(<<'.,.,', 'egrammar.ra', 705) +module_eval(<<'.,.,', 'egrammar.ra', 703) def _reduce_208(val, _values, result) - result = nil + result = Factory.literal(val[0][:value]); loc result, val[0] result end .,., # reduce 209 omitted -# reduce 210 omitted +module_eval(<<'.,.,', 'egrammar.ra', 709) + def _reduce_210(val, _values, result) + result = nil + result + end +.,., # reduce 211 omitted @@ -2546,8 +2558,12 @@ module_eval(<<'.,.,', 'egrammar.ra', 705) # reduce 224 omitted -module_eval(<<'.,.,', 'egrammar.ra', 728) - def _reduce_225(val, _values, result) +# reduce 225 omitted + +# reduce 226 omitted + +module_eval(<<'.,.,', 'egrammar.ra', 732) + def _reduce_227(val, _values, result) result = nil result end diff --git a/lib/puppet/pops/parser/lexer2.rb b/lib/puppet/pops/parser/lexer2.rb index 8f24e2b33..d9cec9352 100644 --- a/lib/puppet/pops/parser/lexer2.rb +++ b/lib/puppet/pops/parser/lexer2.rb @@ -99,6 +99,8 @@ class Puppet::Pops::Parser::Lexer2 # EPP_START is currently a marker token, may later get syntax TOKEN_EPPSTART = [:EPP_START, nil, 0].freeze + TOKEN_EPPEND = [:EPP_END, '%>', 2].freeze + TOKEN_EPPEND_TRIM = [:EPP_END_TRIM, '-%>', 3].freeze # This is used for unrecognized tokens, will always be a single character. This particular instance # is not used, but is kept here for documentation purposes. @@ -347,6 +349,9 @@ class Puppet::Pops::Parser::Lexer2 when '%' if la1 == '>' && ctx[:epp_mode] scn.pos += 2 + if ctx[:epp_mode] == :expr + enqueue_completed(TOKEN_EPPEND, before) + end ctx[:epp_mode] = :text interpolate_epp else @@ -417,6 +422,9 @@ class Puppet::Pops::Parser::Lexer2 when '-' if ctx[:epp_mode] && la1 == '%' && la2 == '>' scn.pos += 3 + if ctx[:epp_mode] == :expr + enqueue_completed(TOKEN_EPPEND_TRIM, before) + end interpolate_epp(:with_trim) else emit(case la1 From 323f2286d87f616bef8349af5079ef2b002b08e1 Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 8 Mar 2014 19:43:22 -0800 Subject: [PATCH 797/800] (PUP-1897) Fix failing lexer2 test, and update with additional tests Tests were failing due to additional token marking the end of an EPP render expression. --- spec/unit/pops/parser/lexer2_spec.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/spec/unit/pops/parser/lexer2_spec.rb b/spec/unit/pops/parser/lexer2_spec.rb index ae6099223..3e5a2c20b 100644 --- a/spec/unit/pops/parser/lexer2_spec.rb +++ b/spec/unit/pops/parser/lexer2_spec.rb @@ -345,10 +345,25 @@ describe 'Lexer2' do [:RENDER_STRING, " This is "], [:RENDER_EXPR, nil], [:VARIABLE, "x"], + [:EPP_END, "%>"], [:RENDER_STRING, " just text\n"] ) end + it 'epp can contain text with trimmed interpolated rendered expressions' do + code = <<-CODE + This is <%= $x -%> just text + CODE + epp_tokens_scanned_from(code).should match_tokens2( + :EPP_START, + [:RENDER_STRING, " This is "], + [:RENDER_EXPR, nil], + [:VARIABLE, "x"], + [:EPP_END_TRIM, "-%>"], + [:RENDER_STRING, "just text\n"] + ) + end + it 'epp can contain text with expressions that are not rendered' do code = <<-CODE This is <% $x=10 %> just text From ab1ae3c62ff87dd852c10ac89d3cc9ab440cbefd Mon Sep 17 00:00:00 2001 From: Henrik Lindberg Date: Sat, 8 Mar 2014 19:57:22 -0800 Subject: [PATCH 798/800] (PUP-1897) Improve test of EPP trim, and expr after params This adds tests for render single expression with trim, and for sequence of expressions after opening parameters. --- spec/unit/pops/parser/epp_parser_spec.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spec/unit/pops/parser/epp_parser_spec.rb b/spec/unit/pops/parser/epp_parser_spec.rb index 8304f4120..0db4ba7d9 100644 --- a/spec/unit/pops/parser/epp_parser_spec.rb +++ b/spec/unit/pops/parser/epp_parser_spec.rb @@ -43,6 +43,10 @@ describe "epp parser" do it "nested epp expression tags" do expect { dump(parse("<%= 1+1 <%= 2+2 %>%>")) }.to raise_error(/Syntax error/) end + + it "rendering sequence of expressions" do + expect { dump(parse("<%= 1 2 3 %>")) }.to raise_error(/Syntax error/) + end end context "handles parsing of" do @@ -62,6 +66,10 @@ describe "epp parser" do dump(parse("<%|$x='cigar', $y|%>Hello World")).should == "(lambda (parameters (= x 'cigar') y) (epp (block (render-s 'Hello World'))))" end + it "template parameters + additional setup" do + dump(parse("<%|$x| $y = 10 %>Hello World")).should == "(lambda (parameters x) (epp (block (= $y 10) (render-s 'Hello World'))))" + end + it "comments" do dump(parse("<%#($x='cigar', $y)%>Hello World")).should == "(lambda (epp (block (render-s 'Hello World'))))" end From 0a09175f4dab9e733cf25fecdcbc3346e048ba8c Mon Sep 17 00:00:00 2001 From: Dominic Cleal Date: Mon, 10 Mar 2014 09:18:54 +0000 Subject: [PATCH 799/800] (PUP-1905) Split modulepath when supplied as an override from CLI When a --modulepath option is supplied from the CLI to override the node environment's modulepath, it can be made up of multiple directories with a path separator. The path is now split so multiple paths are correctly searched. --- lib/puppet/node/environment.rb | 2 +- spec/integration/application/apply_spec.rb | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb index 017eab560..84a046922 100644 --- a/lib/puppet/node/environment.rb +++ b/lib/puppet/node/environment.rb @@ -119,7 +119,7 @@ class Puppet::Node::Environment # there are no commandline changes from settings. def override_from_commandline(settings) overrides = {} - overrides[:modulepath] = [settings[:modulepath]] if settings.set_by_cli?(:modulepath) + overrides[:modulepath] = self.class.split_path(settings[:modulepath]) if settings.set_by_cli?(:modulepath) if settings.set_by_cli?(:manifest) || (settings.set_by_cli?(:manifestdir) && settings[:manifest].start_with?(settings[:manifestdir])) overrides[:manifest] = settings[:manifest] diff --git a/spec/integration/application/apply_spec.rb b/spec/integration/application/apply_spec.rb index f464c80ac..9847a14dc 100755 --- a/spec/integration/application/apply_spec.rb +++ b/spec/integration/application/apply_spec.rb @@ -94,6 +94,15 @@ describe "apply" do expect { apply.run }.to exit_with(0) end.to have_printed('amod class included') end + + it "looks in --modulepath when given multiple paths in --modulepath" do + args = ['-e', execute, '--modulepath', [tmpdir('notmodulepath'), modulepath].join(File::PATH_SEPARATOR)] + apply = init_cli_args_and_apply_app(args, execute) + + expect do + expect { apply.run }.to exit_with(0) + end.to have_printed('amod class included') + end end end From 82cdf1678fb119b7410bde64a84e3f56e7ec494d Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sun, 2 Mar 2014 19:01:10 +0100 Subject: [PATCH 800/800] (PUP-1085) Add and fix pacman provider tests - Added a test for the installedgroups function - Fixed behaviour of instances function - Pointed a test for code that has moved at the new function --- lib/puppet/provider/package/pacman.rb | 12 +++++++-- spec/unit/provider/package/pacman_spec.rb | 30 +++++++++++++++++++---- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/lib/puppet/provider/package/pacman.rb b/lib/puppet/provider/package/pacman.rb index 4fd71ffe3..0c721c9d4 100644 --- a/lib/puppet/provider/package/pacman.rb +++ b/lib/puppet/provider/package/pacman.rb @@ -139,8 +139,16 @@ Puppet::Type.type(:package).provide :pacman, :parent => Puppet::Provider::Packag # Fetch the list of packages currently installed on the system. def self.instances packages = self.installedpkgs - packages.concat(self.installedgroups) - packages + groups = self.installedgroups + result = nil + + if (!packages && !groups) + nil + elsif (packages && groups) + packages.concat(groups) + else + packages + end end diff --git a/spec/unit/provider/package/pacman_spec.rb b/spec/unit/provider/package/pacman_spec.rb index 07d114355..789fd88fb 100755 --- a/spec/unit/provider/package/pacman_spec.rb +++ b/spec/unit/provider/package/pacman_spec.rb @@ -195,15 +195,22 @@ EOF end end + + describe "when fetching a package list" do - it "should query pacman" do + it "should retrieve installed packages" do provider.expects(:execpipe).with(["/usr/bin/pacman", '-Q']) - provider.instances + provider.installedpkgs + end + + it "should retrieve installed package groups" do + provider.expects(:execpipe).with(["/usr/bin/pacman", '-Qg']) + provider.installedgroups end it "should return installed packages with their versions" do provider.expects(:execpipe).yields(StringIO.new("package1 1.23-4\npackage2 2.00\n")) - packages = provider.instances + packages = provider.installedpkgs packages.length.should == 2 @@ -220,15 +227,28 @@ EOF } end + it "should return installed groups with a dummy version" do + provider.expects(:execpipe).yields(StringIO.new("group1 pkg1\ngroup1 pkg2")) + groups = provider.installedgroups + + groups.length.should == 1 + + groups[0].properties.should == { + :provider => :pacman, + :ensure => '1', + :name => 'group1' + } + end + it "should return nil on error" do - provider.expects(:execpipe).raises(Puppet::ExecutionFailure.new("ERROR!")) + provider.expects(:execpipe).twice.raises(Puppet::ExecutionFailure.new("ERROR!")) provider.instances.should be_nil end it "should warn on invalid input" do provider.expects(:execpipe).yields(StringIO.new("blah")) provider.expects(:warning).with("Failed to match line blah") - provider.instances.should == [] + provider.installedpkgs == [] end end