From 29ae8792321e083b2263e34d273addfc4818f859 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Wed, 27 Aug 2008 15:30:24 +1000 Subject: Fixes tests broken by 95aa085 --- test/language/functions.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/language/functions.rb b/test/language/functions.rb index 97429802b..50a61a148 100755 --- a/test/language/functions.rb +++ b/test/language/functions.rb @@ -379,6 +379,8 @@ class TestLangFunctions < Test::Unit::TestCase scope = mkscope parser = scope.compiler.parser + realize = Puppet::Parser::Functions.function(:realize) + # Make a definition parser.newdefine("mytype") @@ -434,6 +436,8 @@ class TestLangFunctions < Test::Unit::TestCase def test_defined scope = mkscope parser = scope.compiler.parser + + defined = Puppet::Parser::Functions.function(:defined) parser.newclass("yayness") parser.newdefine("rahness") @@ -491,6 +495,8 @@ class TestLangFunctions < Test::Unit::TestCase scope = mkscope parser = scope.compiler.parser + include = Puppet::Parser::Functions.function(:include) + assert_raise(Puppet::ParseError, "did not throw error on missing class") do scope.function_include("nosuchclass") end @@ -508,6 +514,8 @@ class TestLangFunctions < Test::Unit::TestCase parser = mkparser scope = mkscope(:parser => parser) + file = Puppet::Parser::Functions.function(:file) + file1 = tempfile file2 = tempfile file3 = tempfile @@ -548,6 +556,8 @@ class TestLangFunctions < Test::Unit::TestCase assert_equal("yay\n", %x{#{command}}, "command did not work") assert_equal("yay-foo\n", %x{#{command} foo}, "command did not work") + generate = Puppet::Parser::Functions.function(:generate) + scope = mkscope parser = scope.compiler.parser -- cgit From aae0793eb3632c6dea56f5d573d201c475786f8c Mon Sep 17 00:00:00 2001 From: Brice Figureau Date: Tue, 26 Aug 2008 20:47:10 +0200 Subject: Daemontools service provider This provider manages daemons running supervised under D.J.Bernstein daemontools. It tries to detect the service directory, with by order of preference: * /service * /etc/service * /var/lib/svscan The daemon directory should be placed in a directory that can be by default in: * /var/lib/service * /etc or this can be overriden in the service resource parameters: service { "myservice": provider => "daemontools", path => "/path/to/daemons"; } This provider supports out of the box: * start/stop (mapped to enable/disable) * enable/disable * restart * status Signed-off-by: Brice Figureau --- lib/puppet/provider/service/daemontools.rb | 154 +++++++++++++++++++++++++++++ spec/unit/provider/service/daemontools.rb | 124 +++++++++++++++++++++++ 2 files changed, 278 insertions(+) create mode 100644 lib/puppet/provider/service/daemontools.rb create mode 100644 spec/unit/provider/service/daemontools.rb diff --git a/lib/puppet/provider/service/daemontools.rb b/lib/puppet/provider/service/daemontools.rb new file mode 100644 index 000000000..52d8c6b6c --- /dev/null +++ b/lib/puppet/provider/service/daemontools.rb @@ -0,0 +1,154 @@ +# Daemontools service management +# +# author Brice Figureau +Puppet::Type.type(:service).provide :daemontools, :parent => :base do + desc "Daemontools service management. + This provider manages daemons running supervised by D.J.Bernstein daemontools. + It tries to detect the service directory, with by order of preference: + * /service + * /etc/service + * /var/lib/svscan + The daemon directory should be placed in a directory that can be + by default in: + * /var/lib/service + * /etc + or this can be overriden in the service resource parameters: + service { + \"myservice\": + provider => \"daemontools\", path => \"/path/to/daemons\"; + } + + This provider supports out of the box: + * start/stop (mapped to enable/disable) + * enable/disable + * restart + * status" + + commands :svc => "/usr/bin/svc" + commands :svstat => "/usr/bin/svstat" + + class << self + attr_writer :defpath + + # this is necessary to autodetect a valid resource + # default path, since there is no standard for such directory. + def defpath + unless defined?(@defpath) and @defpath + ["/var/lib/service", "/etc"].each do |path| + if FileTest.exist?(path) + @defpath = path + break + end + end + raise "Could not find the daemon directory (tested [/var/lib/service,/etc])" unless @defpath + end + @defpath + end + end + + attr_writer :servicedir + + # returns all providers for all existing services in @defpath + # ie enabled or not + def self.instances + path = self.defpath + unless FileTest.directory?(path) + Puppet.notice "Service path %s does not exist" % path + next + end + + # reject entries that aren't either a directory + # or don't contain a run file + Dir.entries(path).reject { |e| + fullpath = File.join(path, e) + e =~ /^\./ or ! FileTest.directory?(fullpath) or ! FileTest.exist?(File.join(fullpath,"run")) + }.collect do |name| + new(:name => name, :path => path) + end + end + + # returns the daemon dir on this node + def self.daemondir + self.defpath + end + + # find the service dir on this node + def servicedir + unless defined?(@servicedir) and @servicedir + ["/service", "/etc/service","/var/lib/svscan"].each do |path| + if FileTest.exist?(path) + @servicedir = path + break + end + end + raise "Could not find service directory" unless @servicedir + end + @servicedir + end + + # returns the full path of this service when enabled + # (ie in the service directory) + def service + File.join(self.servicedir, resource[:name]) + end + + # returns the full path to the current daemon directory + # note that this path can be overriden in the resource + # definition + def daemon + File.join(resource[:path], resource[:name]) + end + + def restartcmd + [ command(:svc), "-t", self.service] + end + + # The start command does nothing, service are automatically started + # when enabled by svscan. But, forces an enable if necessary + def start + # to start make sure the sevice is enabled + self.enable + # start is then automatic + end + + def status + begin + output = svstat self.service + return :running if output =~ /\bup\b/ + rescue Puppet::ExecutionFailure => detail + raise Puppet::Error.new( "Could not get status for service %s: %s" % [ resource.ref, detail] ) + end + return :stopped + end + + # unfortunately it is not possible + # to stop without disabling the service + def stop + self.disable + end + + # disable by stopping the service + # and removing the symlink so that svscan + # doesn't restart our service behind our back + def disable + # should stop the service + # stop the log subservice if any + log = File.join(self.service, "log") + texecute("stop log", [ command(:svc) , '-dx', log] ) if FileTest.directory?(log) + + # stop the main resource + texecute("stop", [command(:svc), '-dx', self.service] ) + + # unlink the daemon symlink to disable it + File.unlink(self.service) if FileTest.symlink?(self.service) + end + + def enabled? + FileTest.symlink?(self.service) + end + + def enable + File.symlink(self.daemon, self.service) if ! FileTest.symlink?(self.service) + end +end + diff --git a/spec/unit/provider/service/daemontools.rb b/spec/unit/provider/service/daemontools.rb new file mode 100644 index 000000000..29e9dd5be --- /dev/null +++ b/spec/unit/provider/service/daemontools.rb @@ -0,0 +1,124 @@ +#!/usr/bin/env ruby +# +# Unit testing for the Daemontools service Provider +# +# author Brice Figureau +# +require File.dirname(__FILE__) + '/../../../spec_helper' + +provider_class = Puppet::Type.type(:service).provider(:daemontools) + +describe provider_class do + + before(:each) do + # Create a mock resource + @resource = stub 'resource' + + @provider = provider_class.new + @servicedir = "/etc/service" + @provider.servicedir=@servicedir + @daemondir = "/var/lib/service" + @provider.class.defpath=@daemondir + + # A catch all; no parameters set + @resource.stubs(:[]).returns(nil) + + # But set name, source and path (because we won't run + # the thing that will fetch the resource path from the provider) + @resource.stubs(:[]).with(:name).returns "myservice" + @resource.stubs(:[]).with(:ensure).returns :enabled + @resource.stubs(:[]).with(:path).returns @daemondir + @resource.stubs(:ref).returns "Service[myservice]" + + @provider.stubs(:resource).returns @resource + end + + it "should have a restartcmd method" do + @provider.should respond_to(:restartcmd) + end + + it "should have a start method" do + @provider.should respond_to(:start) + end + + it "should have a stop method" do + @provider.should respond_to(:stop) + end + + it "should have an enabled? method" do + @provider.should respond_to(:enabled?) + end + + it "should have an enable method" do + @provider.should respond_to(:enable) + end + + it "should have a disable method" do + @provider.should respond_to(:disable) + end + + describe "when starting" do + it "should call enable" do + @provider.expects(:enable) + @provider.start + end + end + + describe "when stopping" do + it "should call disable" do + @provider.expects(:disable) + @provider.stop + end + end + + describe "when enabling" do + it "should create a symlink between daemon dir and service dir" do + FileTest.stubs(:symlink?).returns(false) + File.expects(:symlink).with(File.join(@daemondir,"myservice"), File.join(@servicedir,"myservice")).returns(0) + @provider.enable + end + end + + describe "when disabling" do + it "should stop and then remove the symlink between daemon dir and service dir" do + FileTest.stubs(:directory?).returns(false) + FileTest.stubs(:symlink?).returns(true) + File.expects(:unlink).with(File.join(@servicedir,"myservice")).returns(0) + @provider.stubs(:texecute).returns("") + @provider.disable + end + end + + describe "when disabling" do + it "should also call 'svc -dx /etc/service/myservice'" do + FileTest.stubs(:directory?).returns(false) + FileTest.stubs(:symlink?).returns(true) + File.expects(:unlink).with(File.join(@servicedir,"myservice")).returns(0) + @provider.expects(:texecute).with("stop", [nil, '-dx', File.join(@servicedir,"myservice")]).returns "" + @provider.disable + end + end + + describe "when checking status" do + it "should call the external command 'svstat /etc/service/myservice'" do + @provider.expects(:svstat).with(File.join(@servicedir,"myservice")) + @provider.status + end + end + + describe "when checking status" do + it "and svstat fails, properly raise a Puppet::Error" do + @provider.expects(:svstat).with(File.join(@servicedir,"myservice")).raises(Puppet::ExecutionFailure, "failure") + lambda { @provider.status }.should raise_error(Puppet::Error, 'Could not get status for service Service[myservice]: failure') + end + it "and svstat returns up, then return :running" do + @provider.expects(:svstat).with(File.join(@servicedir,"myservice")).returns("/etc/service/myservice: up (pid 454) 954326 seconds") + @provider.status.should == :running + end + it "and svstat returns not running, then return :stopped" do + @provider.expects(:svstat).with(File.join(@servicedir,"myservice")).returns("/etc/service/myservice: supervise not running") + @provider.status.should == :stopped + end + end + + end -- cgit From 41dc1fac33cbb3938a5dc5f42f5b841a0a734c27 Mon Sep 17 00:00:00 2001 From: Brice Figureau Date: Wed, 27 Aug 2008 20:31:38 +0200 Subject: Runit service provider This provider manages daemons running supervised by Runit[1]. It tries to detect the service directory, with by order of preference: * /service * /var/service * /etc/service The daemon directory should be placed in a directory that can be by default in: * /etc/sv * /var/lib/service or this can be overriden in the service resource parameters: service { "myservice": provider => "runit", path => "/path/to/daemons"; } This provider supports out of the box: * start/stop * enable/disable * restart * status [1]: http://smarden.sunsite.dk/runit/ --- CHANGELOG | 2 + lib/puppet/provider/service/runit.rb | 93 ++++++++++++++++++++++++++++ spec/unit/provider/service/runit.rb | 117 +++++++++++++++++++++++++++++++++++ 3 files changed, 212 insertions(+) create mode 100644 lib/puppet/provider/service/runit.rb create mode 100644 spec/unit/provider/service/runit.rb diff --git a/CHANGELOG b/CHANGELOG index 11107ebfb..8ce2c3a8d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,6 @@ 0.24.x + Added daemontools and runit providers for service type + Added simple rake task for running unit tests Added spec Rake task diff --git a/lib/puppet/provider/service/runit.rb b/lib/puppet/provider/service/runit.rb new file mode 100644 index 000000000..230fa75d9 --- /dev/null +++ b/lib/puppet/provider/service/runit.rb @@ -0,0 +1,93 @@ +# Daemontools service management +# +# author Brice Figureau +Puppet::Type.type(:service).provide :runit, :parent => :daemontools do + desc "Runit service management. + This provider manages daemons running supervised by Runit. + It tries to detect the service directory, with by order of preference: + * /service + * /var/service + * /etc/service + The daemon directory should be placed in a directory that can be + by default in: + * /etc/sv + or this can be overriden in the service resource parameters: + service { + \"myservice\": + provider => \"runit\", path => \"/path/to/daemons\"; + } + + This provider supports out of the box: + * start/stop + * enable/disable + * restart + * status" + + commands :sv => "/usr/bin/sv" + + class << self + # this is necessary to autodetect a valid resource + # default path, since there is no standard for such directory. + def defpath + unless defined?(@defpath) and @defpath + ["/etc/sv", "/var/lib/service"].each do |path| + if FileTest.exist?(path) + @defpath = path + break + end + end + raise "Could not find the daemon directory (tested [/var/lib/service,/etc])" unless @defpath + end + @defpath + end + end + + # find the service dir on this node + def servicedir + unless defined?(@servicedir) and @servicedir + ["/service", "/etc/service","/var/service"].each do |path| + if FileTest.exist?(path) + @servicedir = path + break + end + end + raise "Could not find service directory" unless @servicedir + end + @servicedir + end + + def restartcmd + [ command(:sv), "restart", self.service] + end + + def status + begin + output = sv "status", self.daemon + 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 %s: %s" % [ resource.ref, detail] ) + end + end + return :stopped + end + + # relay to the stopcmd + def stop + ucommand( :stop ) + end + + def stopcmd + [ command(:sv), "stop", self.service] + end + + # disable by removing the symlink so that runit + # doesn't restart our service behind our back + # note that runit doesn't need to perform a stop + # before a disable + def disable + # unlink the daemon symlink to disable it + File.unlink(self.service) if FileTest.symlink?(self.service) + end +end + diff --git a/spec/unit/provider/service/runit.rb b/spec/unit/provider/service/runit.rb new file mode 100644 index 000000000..8eb53849b --- /dev/null +++ b/spec/unit/provider/service/runit.rb @@ -0,0 +1,117 @@ +#!/usr/bin/env ruby +# +# Unit testing for the Runit service Provider +# +# author Brice Figureau +# +require File.dirname(__FILE__) + '/../../../spec_helper' + +provider_class = Puppet::Type.type(:service).provider(:runit) + +describe provider_class do + + before(:each) do + # Create a mock resource + @resource = stub 'resource' + + @provider = provider_class.new + @servicedir = "/etc/service" + @provider.servicedir=@servicedir + @daemondir = "/etc/sv" + @provider.class.defpath=@daemondir + + # A catch all; no parameters set + @resource.stubs(:[]).returns(nil) + + # But set name, source and path (because we won't run + # the thing that will fetch the resource path from the provider) + @resource.stubs(:[]).with(:name).returns "myservice" + @resource.stubs(:[]).with(:ensure).returns :enabled + @resource.stubs(:[]).with(:path).returns @daemondir + @resource.stubs(:ref).returns "Service[myservice]" + + @provider.stubs(:resource).returns @resource + end + + it "should have a restartcmd method" do + @provider.should respond_to(:restartcmd) + end + + it "should have a start method" do + @provider.should respond_to(:start) + end + + it "should have a stop method" do + @provider.should respond_to(:stop) + end + + it "should have an enabled? method" do + @provider.should respond_to(:enabled?) + end + + it "should have an enable method" do + @provider.should respond_to(:enable) + end + + it "should have a disable method" do + @provider.should respond_to(:disable) + end + + describe "when starting" do + it "should call enable" do + @provider.expects(:enable) + @provider.start + end + end + + describe "when stopping" do + it "should execute external command 'sv stop /etc/service/myservice'" do + @provider.expects(:ucommand).with(:stop).returns("") + @provider.stop + end + end + + describe "when enabling" do + it "should create a symlink between daemon dir and service dir" do + FileTest.stubs(:symlink?).returns(false) + File.expects(:symlink).with(File.join(@daemondir,"myservice"), File.join(@servicedir,"myservice")).returns(0) + @provider.enable + end + end + + describe "when disabling" do + it "should remove the '/etc/service/myservice' symlink" do + FileTest.stubs(:directory?).returns(false) + FileTest.stubs(:symlink?).returns(true) + File.expects(:unlink).with(File.join(@servicedir,"myservice")).returns(0) + @provider.disable + end + end + + describe "when checking status" do + it "should call the external command 'sv status /etc/sv/myservice'" do + @provider.expects(:sv).with('status',File.join(@daemondir,"myservice")) + @provider.status + end + end + + describe "when checking status" do + it "and sv status fails, properly raise a Puppet::Error" do + @provider.expects(:sv).with('status',File.join(@daemondir,"myservice")).raises(Puppet::ExecutionFailure, "fail: /etc/sv/myservice: file not found") + lambda { @provider.status }.should raise_error(Puppet::Error, 'Could not get status for service Service[myservice]: fail: /etc/sv/myservice: file not found') + end + it "and sv status returns up, then return :running" do + @provider.expects(:sv).with('status',File.join(@daemondir,"myservice")).returns("run: /etc/sv/myservice: (pid 9029) 6s") + @provider.status.should == :running + end + it "and sv status returns not running, then return :stopped" do + @provider.expects(:sv).with('status',File.join(@daemondir,"myservice")).returns("fail: /etc/sv/myservice: runsv not running") + @provider.status.should == :stopped + end + it "and sv status returns a warning, then return :stopped" do + @provider.expects(:sv).with('status',File.join(@daemondir,"myservice")).returns("warning: /etc/sv/myservice: unable to open supervise/ok: file does not exist") + @provider.status.should == :stopped + end + end + + end -- cgit From 81cc9bf4892170e2e36a7b0b302a74f88c6825d1 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Fri, 29 Aug 2008 12:17:04 +1000 Subject: Fixed #1533 - changed permissions for man directory --- CHANGELOG | 2 ++ install.rb | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 8ce2c3a8d..1ba297e97 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,6 @@ 0.24.x + Fixed #1533 - changed permissions for man directory + Added daemontools and runit providers for service type Added simple rake task for running unit tests diff --git a/install.rb b/install.rb index b5aba4215..73bcc6db5 100755 --- a/install.rb +++ b/install.rb @@ -102,7 +102,7 @@ def do_man(man, strip = 'man/') omf = File.join(InstallOptions.man_dir, mf.gsub(/#{strip}/, '')) om = File.dirname(omf) File.makedirs(om, true) - File.chmod(0644, om) + File.chmod(0755, om) File.install(mf, omf, 0644, true) gzip = %x{which gzip} gzip.chomp! -- cgit From 8fe033820875966106ab3807aa34a1cafc85cbd2 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Fri, 5 Sep 2008 11:39:57 +1000 Subject: Fixes #1554 - Fix exception for undefined hostname --- CHANGELOG | 2 ++ lib/puppet/node.rb | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 1ba297e97..5b9ad4c32 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,6 @@ 0.24.x + Fixes #1554 - Fix exception for undefined hostname + Fixed #1533 - changed permissions for man directory Added daemontools and runit providers for service type diff --git a/lib/puppet/node.rb b/lib/puppet/node.rb index afa18f565..341ec77ba 100644 --- a/lib/puppet/node.rb +++ b/lib/puppet/node.rb @@ -94,8 +94,10 @@ class Puppet::Node # First, get the fqdn unless fqdn = parameters["fqdn"] - if domain = parameters["domain"] - fqdn = parameters["hostname"] + "." + parameters["domain"] + if parameters["hostname"] and parameters["domain"] + fqdn = parameters["hostname"] + "." + parameters["domain"] + else + Puppet.warning "Host is missing hostname and/or domain: %s" % name end end -- cgit From e15d316cbb9ee33bfbed4b29649f36a97490e985 Mon Sep 17 00:00:00 2001 From: Nigel Kersten Date: Wed, 3 Sep 2008 10:28:17 -0700 Subject: Fixes #1551 puppetmaster.freshness xmlrpc call returns incorrect type --- CHANGELOG | 2 ++ lib/puppet/network/handler/master.rb | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 5b9ad4c32..15f681e77 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,6 @@ 0.24.x + Fixes #1551 puppetmaster.freshness xmlrpc call returns incorrect type + Fixes #1554 - Fix exception for undefined hostname Fixed #1533 - changed permissions for man directory diff --git a/lib/puppet/network/handler/master.rb b/lib/puppet/network/handler/master.rb index 9682c460e..05ae7b9a2 100644 --- a/lib/puppet/network/handler/master.rb +++ b/lib/puppet/network/handler/master.rb @@ -24,7 +24,7 @@ class Puppet::Network::Handler # Tell a client whether there's a fresh config for it def freshness(client = nil, clientip = nil) # Always force a recompile. Newer clients shouldn't do this (as of April 2008). - Time.now + return 0 end def initialize(hash = {}) -- cgit From 0705dfb462b145ac9e4faafc517bbb833f2c81b9 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Sat, 6 Sep 2008 09:52:26 +1000 Subject: Fixes #1455 - Adds HP-UX support for user type --- CHANGELOG | 2 ++ lib/puppet/provider/user/hpux.rb | 29 +++++++++++++++++++++++++++++ spec/unit/provider/user/hpux.rb | 29 +++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 lib/puppet/provider/user/hpux.rb create mode 100755 spec/unit/provider/user/hpux.rb diff --git a/CHANGELOG b/CHANGELOG index 15f681e77..2df61d8fa 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,6 @@ 0.24.x + Fixes #1455 - Adds HP-UX support for user type + Fixes #1551 puppetmaster.freshness xmlrpc call returns incorrect type Fixes #1554 - Fix exception for undefined hostname diff --git a/lib/puppet/provider/user/hpux.rb b/lib/puppet/provider/user/hpux.rb new file mode 100644 index 000000000..d50924c72 --- /dev/null +++ b/lib/puppet/provider/user/hpux.rb @@ -0,0 +1,29 @@ +Puppet::Type.type(:user).provide :hpuxuseradd, :parent => :useradd do + desc "User management for hp-ux! Undocumented switch to special usermod because HP-UX regular usermod is TOO STUPID to change stuff while the user is logged in." + + defaultfor :operatingsystem => :"hp-ux" + + commands :modify => "/usr/sbin/usermod", :delete => "/usr/sbin/userdel", :add => "/usr/sbin/useradd" + options :comment, :method => :gecos + options :groups, :flag => "-G" + options :home, :flag => "-d", :method => :dir + + verify :gid, "GID must be an integer" do |value| + value.is_a? Integer + end + + verify :groups, "Groups must be comma-separated" do |value| + value !~ /\s/ + end + + has_features :manages_homedir, :allows_duplicates + + def deletecmd + super.insert(1,"-F") + end + + def modifycmd(param,value) + super.insert(1,"-F") + end + +end diff --git a/spec/unit/provider/user/hpux.rb b/spec/unit/provider/user/hpux.rb new file mode 100755 index 000000000..8b31658b8 --- /dev/null +++ b/spec/unit/provider/user/hpux.rb @@ -0,0 +1,29 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +provider_class = Puppet::Type.type(:user).provider(:hpuxuseradd) + +describe provider_class do + # left from the useradd test... I have no clue what I'm doing. + before do + @resource = stub("resource", :name => "myuser", :managehome? => nil) + @provider = provider_class.new(@resource) + end + + it "should add -F when modifying a user" do + @resource.stubs(:should).returns "fakeval" + @resource.stubs(:[]).returns "fakeval" + @provider.expects(:execute).with { |args| args.include?("-F") } + + @provider.modify + end + + it "should add -F when deleting a user" do + @resource.stubs(:should).returns "fakeval" + @resource.stubs(:[]).returns "fakeval" + @provider.expects(:execute).with { |args| args.include?("-F") } + + @provider.delete + end +end -- cgit From b88df5ab32cc3e58b2e06d06c1f1694cfca1cba6 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Tue, 9 Sep 2008 07:30:49 +1000 Subject: Sync with latest Fedora/EPEL specfile --- CHANGELOG | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 2df61d8fa..f63fe4ccc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,6 @@ 0.24.x + Updated Red Hat spec file + Fixes #1455 - Adds HP-UX support for user type Fixes #1551 puppetmaster.freshness xmlrpc call returns incorrect type -- cgit From d4d3213a4a8bc7be93a0adb85bf29b5739c7a81d Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Wed, 10 Sep 2008 13:48:52 +1000 Subject: Fixed debug messages in package type - thanks to Todd Zullinger for this fix --- CHANGELOG | 2 ++ lib/puppet/type/package.rb | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index f63fe4ccc..7cd7d0599 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,6 @@ 0.24.x + Fixed debug messages in package type - thanks to Todd Zullinger for this fix + Updated Red Hat spec file Fixes #1455 - Adds HP-UX support for user type diff --git a/lib/puppet/type/package.rb b/lib/puppet/type/package.rb index f004f7c42..0cea39197 100644 --- a/lib/puppet/type/package.rb +++ b/lib/puppet/type/package.rb @@ -138,8 +138,8 @@ module Puppet # that can't query versions. return true else - self.debug "is is %s, latest %s is %s" % - [is.inspect, @resource.name, @latest.inspect] + self.debug "%s %s is installed, latest is %s" % + [@resource.name, is.inspect, @latest.inspect] end when :absent return true if is == :absent or is == :purged -- cgit From 8f1336f94e4f566e229efb64be168530e402741b Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Fri, 12 Sep 2008 08:44:41 +1000 Subject: Fixed #1566 - changed password property of the user type --- CHANGELOG | 4 ++++ lib/puppet/type/user.rb | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 7cd7d0599..056e8f5bc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,9 @@ 0.24.x Fixed debug messages in package type - thanks to Todd Zullinger for this fix + + Fixed #1566 - changed password property of the user type + + Fixed debug messages in package type Updated Red Hat spec file diff --git a/lib/puppet/type/user.rb b/lib/puppet/type/user.rb index 71507d172..039bcb7cb 100755 --- a/lib/puppet/type/user.rb +++ b/lib/puppet/type/user.rb @@ -158,6 +158,14 @@ module Puppet newproperty(:password, :required_features => :manages_passwords) do desc "The user's password, in whatever encrypted format the local machine requires. Be sure to enclose any value that includes a dollar sign ($) in single quotes (\')." + + def change_to_s(currentvalue, newvalue) + if currentvalue == :absent + return "created password" + else + return "changed password" + end + end end newproperty(:groups) do -- cgit From 7ce902df27393821163b4200916baffb712f0d03 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Fri, 12 Sep 2008 09:13:50 +1000 Subject: Adjusted hpuxuseradd user provider to confine to HP-UX and fixed HP-UX user provider path regression --- CHANGELOG | 2 ++ lib/puppet/provider/user/hpux.rb | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 056e8f5bc..fd65d05dd 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,8 @@ 0.24.x Fixed debug messages in package type - thanks to Todd Zullinger for this fix + Adjusted hpuxuseradd user provider to confine to HP-UX + Fixed #1566 - changed password property of the user type Fixed debug messages in package type diff --git a/lib/puppet/provider/user/hpux.rb b/lib/puppet/provider/user/hpux.rb index d50924c72..672b77da6 100644 --- a/lib/puppet/provider/user/hpux.rb +++ b/lib/puppet/provider/user/hpux.rb @@ -1,9 +1,10 @@ Puppet::Type.type(:user).provide :hpuxuseradd, :parent => :useradd do desc "User management for hp-ux! Undocumented switch to special usermod because HP-UX regular usermod is TOO STUPID to change stuff while the user is logged in." - defaultfor :operatingsystem => :"hp-ux" - - commands :modify => "/usr/sbin/usermod", :delete => "/usr/sbin/userdel", :add => "/usr/sbin/useradd" + defaultfor :operatingsystem => "hp-ux" + confine :operatingsystem => "hp-ux" + + commands :modify => "/usr/sam/lbin/usermod.sam", :delete => "/usr/sam/lbin/userdel.sam", :add => "/usr/sbin/useradd" options :comment, :method => :gecos options :groups, :flag => "-G" options :home, :flag => "-d", :method => :dir -- cgit From 758505b9e3bcb2c45da30d62350534c232f1bf98 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Sat, 13 Sep 2008 14:16:01 +1000 Subject: Fixed #1568 - createpackage.sh --- conf/osx/createpackage.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/osx/createpackage.sh b/conf/osx/createpackage.sh index 3ac4d09de..32cbebfc5 100755 --- a/conf/osx/createpackage.sh +++ b/conf/osx/createpackage.sh @@ -49,7 +49,7 @@ function find_puppet_root() { function install_puppet() { echo "Installing Puppet to ${pkgroot}" - "${installer}" --destdir="${pkgroot}" --bindir="${BINDIR}" --sitelibdir="${SITELIBDIR}" &> /dev/null + "${installer}" --destdir="${pkgroot}" --bindir="${BINDIR}" --sitelibdir="${SITELIBDIR}" chown -R root:admin "${pkgroot}" } -- cgit From a7306e14b9aa064218d051602715c987aebb8417 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Sat, 13 Sep 2008 14:21:06 +1000 Subject: Fixed #1553 - Puppet and Facter cannot both install the plist module into two different locations --- install.rb | 10 ++ lib/puppet/provider/package/appdmg.rb | 2 +- lib/puppet/provider/package/pkgdmg.rb | 2 +- lib/puppet/util/plist.rb | 23 ---- lib/puppet/util/plist/generator.rb | 225 --------------------------------- lib/puppet/util/plist/parser.rb | 226 ---------------------------------- 6 files changed, 12 insertions(+), 476 deletions(-) delete mode 100644 lib/puppet/util/plist.rb delete mode 100644 lib/puppet/util/plist/generator.rb delete mode 100644 lib/puppet/util/plist/parser.rb diff --git a/install.rb b/install.rb index 73bcc6db5..3fa3822c5 100755 --- a/install.rb +++ b/install.rb @@ -60,6 +60,7 @@ rescue end PREREQS = %w{openssl facter xmlrpc/client xmlrpc/server cgi} +MIN_FACTER_VERSION = 1.5 InstallOptions = OpenStruct.new @@ -115,6 +116,15 @@ def check_prereqs PREREQS.each { |pre| begin require pre + if pre == "facter" + # to_f isn't quite exact for strings like "1.5.1" but is good + # enough for this purpose. + facter_version = Facter.version.to_f + if facter_version < MIN_FACTER_VERSION + puts "Facter version: %s; minimum required: %s; cannot install" % [facter_version, MIN_FACTER_VERSION] + exit -1 + end + end rescue LoadError puts "Could not load %s; cannot install" % pre exit -1 diff --git a/lib/puppet/provider/package/appdmg.rb b/lib/puppet/provider/package/appdmg.rb index e822be14c..2ee82a95d 100644 --- a/lib/puppet/provider/package/appdmg.rb +++ b/lib/puppet/provider/package/appdmg.rb @@ -55,7 +55,7 @@ Puppet::Type.type(:package).provide(:appdmg, :parent => Puppet::Provider::Packag self.fail "Mac OS X PKG DMG's must specificy a source string ending in .dmg" end require 'open-uri' - require 'puppet/util/plist' + require 'facter/util/plist' cached_source = source if %r{\A[A-Za-z][A-Za-z0-9+\-\.]*://} =~ cached_source cached_source = "/tmp/#{name}" diff --git a/lib/puppet/provider/package/pkgdmg.rb b/lib/puppet/provider/package/pkgdmg.rb index 134be5a65..fa546c61f 100644 --- a/lib/puppet/provider/package/pkgdmg.rb +++ b/lib/puppet/provider/package/pkgdmg.rb @@ -70,7 +70,7 @@ Puppet::Type.type(:package).provide :pkgdmg, :parent => Puppet::Provider::Packag self.fail "Mac OS X PKG DMG's must specificy a source string ending in .dmg" end require 'open-uri' - require 'puppet/util/plist' + require 'facter/util/plist' cached_source = source if %r{\A[A-Za-z][A-Za-z0-9+\-\.]*://} =~ cached_source cached_source = "/tmp/#{name}" diff --git a/lib/puppet/util/plist.rb b/lib/puppet/util/plist.rb deleted file mode 100644 index d7a95da23..000000000 --- a/lib/puppet/util/plist.rb +++ /dev/null @@ -1,23 +0,0 @@ -#-- -############################################################## -# Copyright 2006, Ben Bleything and # -# Patrick May # -# # -# Distributed under the MIT license. # -############################################################## -#++ -# = Plist -# -# This is the main file for plist. Everything interesting happens in Plist and Plist::Emit. - -require 'base64' -require 'cgi' -require 'stringio' - -require 'puppet/util/plist/generator' -require 'puppet/util/plist/parser' - -module Plist - VERSION = '3.0.0' -end - diff --git a/lib/puppet/util/plist/generator.rb b/lib/puppet/util/plist/generator.rb deleted file mode 100644 index c615ac43b..000000000 --- a/lib/puppet/util/plist/generator.rb +++ /dev/null @@ -1,225 +0,0 @@ -#--########################################################### -# Copyright 2006, Ben Bleything and # -# Patrick May # -# # -# Distributed under the MIT license. # -############################################################## -#++ -# See Plist::Emit. -module Plist - # === Create a plist - # You can dump an object to a plist in one of two ways: - # - # * Plist::Emit.dump(obj) - # * obj.to_plist - # * This requires that you mixin the Plist::Emit module, which is already done for +Array+ and +Hash+. - # - # The following Ruby classes are converted into native plist types: - # Array, Bignum, Date, DateTime, Fixnum, Float, Hash, Integer, String, Symbol, Time, true, false - # * +Array+ and +Hash+ are both recursive; their elements will be converted into plist nodes inside the and containers (respectively). - # * +IO+ (and its descendants) and +StringIO+ objects are read from and their contents placed in a element. - # * User classes may implement +to_plist_node+ to dictate how they should be serialized; otherwise the object will be passed to Marshal.dump and the result placed in a element. - # - # For detailed usage instructions, refer to USAGE[link:files/docs/USAGE.html] and the methods documented below. - module Emit - # Helper method for injecting into classes. Calls Plist::Emit.dump with +self+. - def to_plist(envelope = true) - return Plist::Emit.dump(self, envelope) - end - - # Helper method for injecting into classes. Calls Plist::Emit.save_plist with +self+. - def save_plist(filename) - Plist::Emit.save_plist(self, filename) - end - - # The following Ruby classes are converted into native plist types: - # Array, Bignum, Date, DateTime, Fixnum, Float, Hash, Integer, String, Symbol, Time - # - # Write us (via RubyForge) if you think another class can be coerced safely into one of the expected plist classes. - # - # +IO+ and +StringIO+ objects are encoded and placed in elements; other objects are Marshal.dump'ed unless they implement +to_plist_node+. - # - # The +envelope+ parameters dictates whether or not the resultant plist fragment is wrapped in the normal XML/plist header and footer. Set it to false if you only want the fragment. - def self.dump(obj, envelope = true) - output = plist_node(obj) - - output = wrap(output) if envelope - - return output - end - - # Writes the serialized object's plist to the specified filename. - def self.save_plist(obj, filename) - File.open(filename, 'wb') do |f| - f.write(obj.to_plist) - end - end - - private - def self.plist_node(element) - output = '' - - if element.respond_to? :to_plist_node - output << element.to_plist_node - else - case element - when Array - if element.empty? - output << "\n" - else - output << tag('array') { - element.collect {|e| plist_node(e)} - } - end - when Hash - if element.empty? - output << "\n" - else - inner_tags = [] - - element.keys.sort.each do |k| - v = element[k] - inner_tags << tag('key', CGI::escapeHTML(k.to_s)) - inner_tags << plist_node(v) - end - - output << tag('dict') { - inner_tags - } - end - when true, false - output << "<#{element}/>\n" - when Time - output << tag('date', element.utc.strftime('%Y-%m-%dT%H:%M:%SZ')) - when Date # also catches DateTime - output << tag('date', element.strftime('%Y-%m-%dT%H:%M:%SZ')) - when String, Symbol, Fixnum, Bignum, Integer, Float - output << tag(element_type(element), CGI::escapeHTML(element.to_s)) - when IO, StringIO - element.rewind - contents = element.read - # note that apple plists are wrapped at a different length then - # what ruby's base64 wraps by default. - # I used #encode64 instead of #b64encode (which allows a length arg) - # because b64encode is b0rked and ignores the length arg. - data = "\n" - Base64::encode64(contents).gsub(/\s+/, '').scan(/.{1,68}/o) { data << $& << "\n" } - output << tag('data', data) - else - output << comment( 'The element below contains a Ruby object which has been serialized with Marshal.dump.' ) - data = "\n" - Base64::encode64(Marshal.dump(element)).gsub(/\s+/, '').scan(/.{1,68}/o) { data << $& << "\n" } - output << tag('data', data ) - end - end - - return output - end - - def self.comment(content) - return "\n" - end - - def self.tag(type, contents = '', &block) - out = nil - - if block_given? - out = IndentedString.new - out << "<#{type}>" - out.raise_indent - - out << block.call - - out.lower_indent - out << "" - else - out = "<#{type}>#{contents.to_s}\n" - end - - return out.to_s - end - - def self.wrap(contents) - output = '' - - output << '' + "\n" - output << '' + "\n" - output << '' + "\n" - - output << contents - - output << '' + "\n" - - return output - end - - def self.element_type(item) - return case item - when String, Symbol: 'string' - when Fixnum, Bignum, Integer: 'integer' - when Float: 'real' - else - raise "Don't know about this data type... something must be wrong!" - end - end - private - class IndentedString #:nodoc: - attr_accessor :indent_string - - @@indent_level = 0 - - def initialize(str = "\t") - @indent_string = str - @contents = '' - end - - def to_s - return @contents - end - - def raise_indent - @@indent_level += 1 - end - - def lower_indent - @@indent_level -= 1 if @@indent_level > 0 - end - - def <<(val) - if val.is_a? Array - val.each do |f| - self << f - end - else - # if it's already indented, don't bother indenting further - unless val =~ /\A#{@indent_string}/ - indent = @indent_string * @@indent_level - - @contents << val.gsub(/^/, indent) - else - @contents << val - end - - # it already has a newline, don't add another - @contents << "\n" unless val =~ /\n$/ - end - end - end - end -end - -# we need to add this so sorting hash keys works properly -class Symbol #:nodoc: - def <=> (other) - self.to_s <=> other.to_s - end -end - -class Array #:nodoc: - include Plist::Emit -end - -class Hash #:nodoc: - include Plist::Emit -end - diff --git a/lib/puppet/util/plist/parser.rb b/lib/puppet/util/plist/parser.rb deleted file mode 100644 index 7308bfb9b..000000000 --- a/lib/puppet/util/plist/parser.rb +++ /dev/null @@ -1,226 +0,0 @@ -#--########################################################### -# Copyright 2006, Ben Bleything and # -# Patrick May # -# # -# Distributed under the MIT license. # -############################################################## -#++ -# Plist parses Mac OS X xml property list files into ruby data structures. -# -# === Load a plist file -# This is the main point of the library: -# -# r = Plist::parse_xml( filename_or_xml ) -module Plist -# Note that I don't use these two elements much: -# -# + Date elements are returned as DateTime objects. -# + Data elements are implemented as Tempfiles -# -# Plist::parse_xml will blow up if it encounters a data element. -# If you encounter such an error, or if you have a Date element which -# can't be parsed into a Time object, please send your plist file to -# plist@hexane.org so that I can implement the proper support. - def Plist::parse_xml( filename_or_xml ) - listener = Listener.new - #parser = REXML::Parsers::StreamParser.new(File.new(filename), listener) - parser = StreamParser.new(filename_or_xml, listener) - parser.parse - listener.result - end - - class Listener - #include REXML::StreamListener - - attr_accessor :result, :open - - def initialize - @result = nil - @open = Array.new - end - - - def tag_start(name, attributes) - @open.push PTag::mappings[name].new - end - - def text( contents ) - @open.last.text = contents if @open.last - end - - def tag_end(name) - last = @open.pop - if @open.empty? - @result = last.to_ruby - else - @open.last.children.push last - end - end - end - - class StreamParser - def initialize( filename_or_xml, listener ) - @filename_or_xml = filename_or_xml - @listener = listener - end - - TEXT = /([^<]+)/ - XMLDECL_PATTERN = /<\?xml\s+(.*?)\?>*/um - DOCTYPE_PATTERN = /\s*)/um - COMMENT_START = /\A/um - - - def parse - plist_tags = PTag::mappings.keys.join('|') - start_tag = /<(#{plist_tags})([^>]*)>/i - end_tag = /<\/(#{plist_tags})[^>]*>/i - - require 'strscan' - - contents = ( - if (File.exists? @filename_or_xml) - File.open(@filename_or_xml) {|f| f.read} - else - @filename_or_xml - end - ) - - @scanner = StringScanner.new( contents ) - until @scanner.eos? - if @scanner.scan(COMMENT_START) - @scanner.scan(COMMENT_END) - elsif @scanner.scan(XMLDECL_PATTERN) - elsif @scanner.scan(DOCTYPE_PATTERN) - elsif @scanner.scan(start_tag) - @listener.tag_start(@scanner[1], nil) - if (@scanner[2] =~ /\/$/) - @listener.tag_end(@scanner[1]) - end - elsif @scanner.scan(TEXT) - @listener.text(@scanner[1]) - elsif @scanner.scan(end_tag) - @listener.tag_end(@scanner[1]) - else - raise "Unimplemented element" - end - end - end - end - - class PTag - @@mappings = { } - def PTag::mappings - @@mappings - end - - def PTag::inherited( sub_class ) - key = sub_class.to_s.downcase - key.gsub!(/^plist::/, '' ) - key.gsub!(/^p/, '') unless key == "plist" - - @@mappings[key] = sub_class - end - - attr_accessor :text, :children - def initialize - @children = Array.new - end - - def to_ruby - raise "Unimplemented: " + self.class.to_s + "#to_ruby on #{self.inspect}" - end - end - - class PList < PTag - def to_ruby - children.first.to_ruby if children.first - end - end - - class PDict < PTag - def to_ruby - dict = Hash.new - key = nil - - children.each do |c| - if key.nil? - key = c.to_ruby - else - dict[key] = c.to_ruby - key = nil - end - end - - dict - end - end - - class PKey < PTag - def to_ruby - CGI::unescapeHTML(text || '') - end - end - - class PString < PTag - def to_ruby - CGI::unescapeHTML(text || '') - end - end - - class PArray < PTag - def to_ruby - children.collect do |c| - c.to_ruby - end - end - end - - class PInteger < PTag - def to_ruby - text.to_i - end - end - - class PTrue < PTag - def to_ruby - true - end - end - - class PFalse < PTag - def to_ruby - false - end - end - - class PReal < PTag - def to_ruby - text.to_f - end - end - - require 'date' - class PDate < PTag - def to_ruby - DateTime.parse(text) - end - end - - require 'base64' - class PData < PTag - def to_ruby - data = Base64.decode64(text.gsub(/\s+/, '')) - - begin - return Marshal.load(data) - rescue Exception => e - io = StringIO.new - io.write data - io.rewind - return io - end - end - end -end - -- cgit From cab5d85dea17f3ea09343955f29eb47c8b32a05d Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Sat, 13 Sep 2008 15:01:57 +1000 Subject: Fixed #1571 - Puppet::Util::binary returns incorrect results --- lib/puppet/util.rb | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/lib/puppet/util.rb b/lib/puppet/util.rb index 94c96db0c..ff9858ed0 100644 --- a/lib/puppet/util.rb +++ b/lib/puppet/util.rb @@ -220,19 +220,14 @@ module Util def binary(bin) if bin =~ /^\// - if FileTest.exists? bin + if FileTest.file? bin and FileTest.executable? bin return bin else return nil end else - # LAK:NOTE See http://snurl.com/21zf8 [groups_google_com] - x = ENV['PATH'].split(":").each do |dir| - if FileTest.exists? File.join(dir, bin) - return File.join(dir, bin) - end - end - return nil + x = %x{which #{bin} 2>/dev/null}.chomp + return x end end module_function :binary -- cgit From 923fd89a2a5d52a6ec9abeb02f6edc01c721fd91 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Sat, 13 Sep 2008 15:11:17 +1000 Subject: Fixed issues with file descriptors leaking into subprocesses --- CHANGELOG | 12 ++++++++++-- lib/puppet/util.rb | 1 + 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index fd65d05dd..441e68ec1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,8 +1,16 @@ 0.24.x + Fixed issues with file descriptors leaking into subprocesses + + Fixed #1568 - createpackage.sh + + Fixed #1571 - Puppet::Util::binary returns incorrect results + + Fixed #1553 - Puppet and Facter cannot both install the plist module into two different locations + + Adjusted hpuxuseradd user provider to confine to HP-UX and fixed HP-UX user provider path regression + Fixed debug messages in package type - thanks to Todd Zullinger for this fix - Adjusted hpuxuseradd user provider to confine to HP-UX - Fixed #1566 - changed password property of the user type Fixed debug messages in package type diff --git a/lib/puppet/util.rb b/lib/puppet/util.rb index ff9858ed0..d6de3e2e1 100644 --- a/lib/puppet/util.rb +++ b/lib/puppet/util.rb @@ -316,6 +316,7 @@ module Util $stdin.reopen("/dev/null") $stdout.reopen(output_file) $stderr.reopen(output_file) + 3.upto(256){|fd| IO::new(fd).close rescue nil} if arguments[:gid] Process.egid = arguments[:gid] Process.gid = arguments[:gid] unless @@os == "Darwin" -- cgit From dd4f65478c23eaeb56dcef588f96bfdd31557080 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 15 Sep 2008 23:10:13 -0500 Subject: Fixing #1576 - moving all of the Puppet::Type code back into type.rb. Signed-off-by: Luke Kanies --- lib/puppet/metatype/attributes.rb | 685 ----------- lib/puppet/metatype/closure.rb | 49 - lib/puppet/metatype/container.rb | 50 - lib/puppet/metatype/evaluation.rb | 163 --- lib/puppet/metatype/instances.rb | 305 ----- lib/puppet/metatype/metaparams.rb | 424 ------- lib/puppet/metatype/providers.rb | 247 ---- lib/puppet/metatype/relationships.rb | 115 -- lib/puppet/metatype/schedules.rb | 33 - lib/puppet/metatype/tags.rb | 38 - lib/puppet/type.rb | 2114 +++++++++++++++++++++++++++++++++- spec/unit/type/noop_metaparam.rb | 2 +- 12 files changed, 2101 insertions(+), 2124 deletions(-) delete mode 100644 lib/puppet/metatype/attributes.rb delete mode 100644 lib/puppet/metatype/closure.rb delete mode 100644 lib/puppet/metatype/container.rb delete mode 100644 lib/puppet/metatype/evaluation.rb delete mode 100644 lib/puppet/metatype/instances.rb delete mode 100644 lib/puppet/metatype/metaparams.rb delete mode 100644 lib/puppet/metatype/providers.rb delete mode 100644 lib/puppet/metatype/relationships.rb delete mode 100644 lib/puppet/metatype/schedules.rb delete mode 100644 lib/puppet/metatype/tags.rb diff --git a/lib/puppet/metatype/attributes.rb b/lib/puppet/metatype/attributes.rb deleted file mode 100644 index 3f48f22ff..000000000 --- a/lib/puppet/metatype/attributes.rb +++ /dev/null @@ -1,685 +0,0 @@ -require 'puppet' -require 'puppet/type' - -class Puppet::Type - class << self - include Puppet::Util::ClassGen - include Puppet::Util::Warnings - attr_reader :properties - end - - def self.states - warnonce "The states method is deprecated; use properties" - properties() - end - - # All parameters, in the appropriate order. The namevar comes first, - # then the properties, then the params and metaparams in the order they - # were specified in the files. - def self.allattrs - # now get all of the arguments, in a specific order - # Cache this, since it gets called so many times - namevar = self.namevar - - order = [namevar] - if self.parameters.include?(:provider) - order << :provider - end - order << [self.properties.collect { |property| property.name }, - self.parameters - [:provider], - self.metaparams].flatten.reject { |param| - # we don't want our namevar in there multiple times - param == namevar - } - - order.flatten! - - return order - end - - # Retrieve an attribute alias, if there is one. - def self.attr_alias(param) - @attr_aliases[symbolize(param)] - end - - # Create an alias to an existing attribute. This will cause the aliased - # attribute to be valid when setting and retrieving values on the instance. - def self.set_attr_alias(hash) - hash.each do |new, old| - @attr_aliases[symbolize(new)] = symbolize(old) - end - end - - # Find the class associated with any given attribute. - def self.attrclass(name) - @attrclasses ||= {} - - # We cache the value, since this method gets called such a huge number - # of times (as in, hundreds of thousands in a given run). - unless @attrclasses.include?(name) - @attrclasses[name] = case self.attrtype(name) - when :property: @validproperties[name] - when :meta: @@metaparamhash[name] - when :param: @paramhash[name] - end - end - @attrclasses[name] - end - - # What type of parameter are we dealing with? Cache the results, because - # this method gets called so many times. - def self.attrtype(attr) - @attrtypes ||= {} - unless @attrtypes.include?(attr) - @attrtypes[attr] = case - when @validproperties.include?(attr): :property - when @paramhash.include?(attr): :param - when @@metaparamhash.include?(attr): :meta - else - raise Puppet::DevError, - "Invalid attribute '%s' for class '%s'" % - [attr, self.name] - end - end - - @attrtypes[attr] - end - - # Copy an existing class parameter. This allows other types to avoid - # duplicating a parameter definition, and is mostly used by subclasses - # of the File class. - def self.copyparam(klass, name) - param = klass.attrclass(name) - - unless param - raise Puppet::DevError, "Class %s has no param %s" % [klass, name] - end - @parameters << param - @parameters.each { |p| @paramhash[name] = p } - - if param.isnamevar? - @namevar = param.name - end - end - - # A similar function but one that yields the class and type. - # This is mainly so that setdefaults doesn't call quite so many functions. - def self.eachattr(*ary) - if ary.empty? - ary = nil - end - - # We have to do this in a specific order, so that defaults are - # created in that order (e.g., providers should be set up before - # anything else). - allattrs.each do |name| - next unless ary.nil? or ary.include?(name) - if obj = @properties.find { |p| p.name == name } - yield obj, :property - elsif obj = @parameters.find { |p| p.name == name } - yield obj, :param - elsif obj = @@metaparams.find { |p| p.name == name } - yield obj, :meta - else - raise Puppet::DevError, "Could not find parameter %s" % name - end - end - end - - def self.eachmetaparam - @@metaparams.each { |p| yield p.name } - end - - # Create the 'ensure' class. This is a separate method so other types - # can easily call it and create their own 'ensure' values. - def self.ensurable(&block) - if block_given? - self.newproperty(:ensure, :parent => Puppet::Property::Ensure, &block) - else - self.newproperty(:ensure, :parent => Puppet::Property::Ensure) do - self.defaultvalues - end - end - end - - # Should we add the 'ensure' property to this class? - def self.ensurable? - # If the class has all three of these methods defined, then it's - # ensurable. - ens = [:exists?, :create, :destroy].inject { |set, method| - set &&= self.public_method_defined?(method) - } - - return ens - end - - # Deal with any options passed into parameters. - def self.handle_param_options(name, options) - # If it's a boolean parameter, create a method to test the value easily - if options[:boolean] - define_method(name.to_s + "?") do - val = self[name] - if val == :true or val == true - return true - end - end - end - - # If this param handles relationships, store that information - end - - # Is the parameter in question a meta-parameter? - def self.metaparam?(param) - @@metaparamhash.include?(symbolize(param)) - end - - # Find the metaparameter class associated with a given metaparameter name. - def self.metaparamclass(name) - @@metaparamhash[symbolize(name)] - end - - def self.metaparams - @@metaparams.collect { |param| param.name } - end - - def self.metaparamdoc(metaparam) - @@metaparamhash[metaparam].doc - end - - # Create a new metaparam. Requires a block and a name, stores it in the - # @parameters array, and does some basic checking on it. - def self.newmetaparam(name, options = {}, &block) - @@metaparams ||= [] - @@metaparamhash ||= {} - name = symbolize(name) - - param = genclass(name, - :parent => options[:parent] || Puppet::Parameter, - :prefix => "MetaParam", - :hash => @@metaparamhash, - :array => @@metaparams, - :attributes => options[:attributes], - &block - ) - - # Grr. - if options[:required_features] - param.required_features = options[:required_features] - end - - handle_param_options(name, options) - - param.metaparam = true - - return param - end - - # Find the namevar - def self.namevar - unless defined? @namevar - params = @parameters.find_all { |param| - param.isnamevar? or param.name == :name - } - - if params.length > 1 - raise Puppet::DevError, "Found multiple namevars for %s" % self.name - elsif params.length == 1 - @namevar = params[0].name - else - raise Puppet::DevError, "No namevar for %s" % self.name - end - end - @namevar - end - - # Create a new parameter. Requires a block and a name, stores it in the - # @parameters array, and does some basic checking on it. - def self.newparam(name, options = {}, &block) - options[:attributes] ||= {} - param = genclass(name, - :parent => options[:parent] || Puppet::Parameter, - :attributes => options[:attributes], - :block => block, - :prefix => "Parameter", - :array => @parameters, - :hash => @paramhash - ) - - handle_param_options(name, options) - - # Grr. - if options[:required_features] - param.required_features = options[:required_features] - end - - param.isnamevar if options[:namevar] - - # These might be enabled later. -# define_method(name) do -# @parameters[name].value -# end -# -# define_method(name.to_s + "=") do |value| -# newparam(param, value) -# end - - if param.isnamevar? - @namevar = param.name - end - - return param - end - - def self.newstate(name, options = {}, &block) - Puppet.warning "newstate() has been deprecrated; use newproperty(%s)" % - name - newproperty(name, options, &block) - end - - # Create a new property. The first parameter must be the name of the property; - # this is how users will refer to the property when creating new instances. - # The second parameter is a hash of options; the options are: - # * :parent: The parent class for the property. Defaults to Puppet::Property. - # * :retrieve: The method to call on the provider or @parent object (if - # the provider is not set) to retrieve the current value. - def self.newproperty(name, options = {}, &block) - name = symbolize(name) - - # This is here for types that might still have the old method of defining - # a parent class. - unless options.is_a? Hash - raise Puppet::DevError, - "Options must be a hash, not %s" % options.inspect - end - - if @validproperties.include?(name) - raise Puppet::DevError, "Class %s already has a property named %s" % - [self.name, name] - end - - if parent = options[:parent] - options.delete(:parent) - else - parent = Puppet::Property - end - - # We have to create our own, new block here because we want to define - # an initial :retrieve method, if told to, and then eval the passed - # block if available. - prop = genclass(name, :parent => parent, :hash => @validproperties, :attributes => options) do - # If they've passed a retrieve method, then override the retrieve - # method on the class. - if options[:retrieve] - define_method(:retrieve) do - provider.send(options[:retrieve]) - end - end - - if block - class_eval(&block) - end - end - - # If it's the 'ensure' property, always put it first. - if name == :ensure - @properties.unshift prop - else - @properties << prop - end - -# define_method(name) do -# @parameters[name].should -# end -# -# define_method(name.to_s + "=") do |value| -# newproperty(name, :should => value) -# end - - return prop - end - - def self.paramdoc(param) - @paramhash[param].doc - end - - # Return the parameter names - def self.parameters - return [] unless defined? @parameters - @parameters.collect { |klass| klass.name } - end - - # Find the parameter class associated with a given parameter name. - def self.paramclass(name) - @paramhash[name] - end - - # Return the property class associated with a name - def self.propertybyname(name) - @validproperties[name] - end - - def self.validattr?(name) - name = symbolize(name) - return true if name == :name - @validattrs ||= {} - - unless @validattrs.include?(name) - if self.validproperty?(name) or self.validparameter?(name) or self.metaparam?(name) - @validattrs[name] = true - else - @validattrs[name] = false - end - end - - @validattrs[name] - end - - # does the name reflect a valid property? - def self.validproperty?(name) - name = symbolize(name) - if @validproperties.include?(name) - return @validproperties[name] - else - return false - end - end - - # Return the list of validproperties - def self.validproperties - return {} unless defined? @parameters - - return @validproperties.keys - end - - # does the name reflect a valid parameter? - def self.validparameter?(name) - unless defined? @parameters - raise Puppet::DevError, "Class %s has not defined parameters" % self - end - if @paramhash.include?(name) or @@metaparamhash.include?(name) - return true - else - return false - end - end - - # fix any namevar => param translations - def argclean(oldhash) - # This duplication is here because it might be a transobject. - hash = oldhash.dup.to_hash - - if hash.include?(:resource) - hash.delete(:resource) - end - namevar = self.class.namevar - - # Do a simple translation for those cases where they've passed :name - # but that's not our namevar - if hash.include? :name and namevar != :name - if hash.include? namevar - raise ArgumentError, "Cannot provide both name and %s" % namevar - end - hash[namevar] = hash[:name] - hash.delete(:name) - end - - # Make sure we have a name, one way or another - unless hash.include? namevar - if defined? @title and @title - hash[namevar] = @title - else - raise Puppet::Error, "Was not passed a namevar or title" - end - end - - return hash - end - - # Return either the attribute alias or the attribute. - def attr_alias(name) - name = symbolize(name) - if synonym = self.class.attr_alias(name) - return synonym - else - return name - end - end - - # Are we deleting this resource? - def deleting? - obj = @parameters[:ensure] and obj.should == :absent - end - - # Create a new property if it is valid but doesn't exist - # Returns: true if a new parameter was added, false otherwise - def add_property_parameter(prop_name) - if self.class.validproperty?(prop_name) && !@parameters[prop_name] - self.newattr(prop_name) - return true - end - return false - end - - # abstract accessing parameters and properties, and normalize - # access to always be symbols, not strings - # This returns a value, not an object. It returns the 'is' - # value, but you can also specifically return 'is' and 'should' - # values using 'object.is(:property)' or 'object.should(:property)'. - def [](name) - name = attr_alias(name) - - unless self.class.validattr?(name) - raise TypeError.new("Invalid parameter %s(%s)" % [name, name.inspect]) - end - - if name == :name - name = self.class.namevar - end - - if obj = @parameters[name] - # Note that if this is a property, then the value is the "should" value, - # not the current value. - obj.value - else - return nil - end - end - - # Abstract setting parameters and properties, and normalize - # access to always be symbols, not strings. This sets the 'should' - # value on properties, and otherwise just sets the appropriate parameter. - def []=(name,value) - name = attr_alias(name) - - unless self.class.validattr?(name) - raise TypeError.new("Invalid parameter %s" % [name]) - end - - if name == :name - name = self.class.namevar - end - if value.nil? - raise Puppet::Error.new("Got nil value for %s" % name) - end - - if obj = @parameters[name] - obj.value = value - return nil - else - self.newattr(name, :value => value) - end - - nil - end - - # remove a property from the object; useful in testing or in cleanup - # when an error has been encountered - def delete(attr) - attr = symbolize(attr) - if @parameters.has_key?(attr) - @parameters.delete(attr) - else - raise Puppet::DevError.new("Undefined attribute '#{attr}' in #{self}") - end - end - - # iterate across the existing properties - def eachproperty - # properties() is a private method - properties().each { |property| - yield property - } - end - - # retrieve the 'should' value for a specified property - def should(name) - name = attr_alias(name) - if prop = @parameters[name] and prop.is_a?(Puppet::Property) - return prop.should - else - return nil - end - end - - # Create the actual attribute instance. Requires either the attribute - # name or class as the first argument, then an optional hash of - # attributes to set during initialization. - def newattr(name, options = {}) - if name.is_a?(Class) - klass = name - name = klass.name - end - - unless klass = self.class.attrclass(name) - raise Puppet::Error, "Resource type %s does not support parameter %s" % [self.class.name, name] - end - - if @parameters.include?(name) - raise Puppet::Error, "Parameter '%s' is already defined in %s" % - [name, self.ref] - end - - if provider and ! provider.class.supports_parameter?(klass) - missing = klass.required_features.find_all { |f| ! provider.class.feature?(f) } - info "Provider %s does not support features %s; not managing attribute %s" % [provider.class.name, missing.join(", "), name] - return nil - end - - # Add resource information at creation time, so it's available - # during validation. - options[:resource] = self - begin - # make sure the parameter doesn't have any errors - return @parameters[name] = klass.new(options) - rescue => detail - error = Puppet::Error.new("Parameter %s failed: %s" % - [name, detail]) - error.set_backtrace(detail.backtrace) - raise error - end - end - - # return the value of a parameter - def parameter(name) - unless name.is_a? Symbol - name = name.intern - end - return @parameters[name].value - end - - # Is the named property defined? - def propertydefined?(name) - unless name.is_a? Symbol - name = name.intern - end - return @parameters.include?(name) - end - - # return an actual type by name; to return the value, use 'inst[name]' - # FIXME this method should go away - def property(name) - if obj = @parameters[symbolize(name)] and obj.is_a?(Puppet::Property) - return obj - else - return nil - end - end - -# def set(name, value) -# send(name.to_s + "=", value) -# end -# -# def get(name) -# send(name) -# end - - # For any parameters or properties that have defaults and have not yet been - # set, set them now. This method can be handed a list of attributes, - # and if so it will only set defaults for those attributes. - def setdefaults(*ary) - #self.class.eachattr(*ary) { |klass, type| - self.class.eachattr(*ary) { |klass, type| - # not many attributes will have defaults defined, so we short-circuit - # those away - next unless klass.method_defined?(:default) - next if @parameters[klass.name] - - next unless obj = self.newattr(klass) - - # We have to check for nil values, not "truth", so we allow defaults - # to false. - value = obj.default and ! value.nil? - if ! value.nil? - obj.value = value - else - @parameters.delete(obj.name) - end - } - end - - # Convert our object to a hash. This just includes properties. - def to_hash - rethash = {} - - @parameters.each do |name, obj| - rethash[name] = obj.value - end - - rethash - end - - # Return a specific value for an attribute. - def value(name) - name = attr_alias(name) - - if obj = @parameters[name] and obj.respond_to?(:value) - return obj.value - else - return nil - end - end - - # Meta-parameter methods: These methods deal with the results - # of specifying metaparameters - - private - - # Return all of the property objects, in the order specified in the - # class. - def properties - #debug "%s has %s properties" % [self,@parameters.length] - props = self.class.properties.collect { |prop| - @parameters[prop.name] - }.find_all { |p| - ! p.nil? - }.each do |prop| - unless prop.is_a?(Puppet::Property) - raise Puppet::DevError, "got a non-property %s(%s)" % - [prop.class, prop.class.name] - end - end - - props - end -end - diff --git a/lib/puppet/metatype/closure.rb b/lib/puppet/metatype/closure.rb deleted file mode 100644 index 673a2359d..000000000 --- a/lib/puppet/metatype/closure.rb +++ /dev/null @@ -1,49 +0,0 @@ -class Puppet::Type - attr_writer :implicit - - # Is this type's name isomorphic with the object? That is, if the - # name conflicts, does it necessarily mean that the objects conflict? - # Defaults to true. - def self.isomorphic? - if defined? @isomorphic - return @isomorphic - else - return true - end - end - - def implicit? - if defined? @implicit and @implicit - return true - else - return false - end - end - - def isomorphic? - self.class.isomorphic? - end - - # is the instance a managed instance? A 'yes' here means that - # the instance was created from the language, vs. being created - # in order resolve other questions, such as finding a package - # in a list - def managed? - # Once an object is managed, it always stays managed; but an object - # that is listed as unmanaged might become managed later in the process, - # so we have to check that every time - if defined? @managed and @managed - return @managed - else - @managed = false - properties.each { |property| - s = property.should - if s and ! property.class.unmanaged - @managed = true - break - end - } - return @managed - end - end -end diff --git a/lib/puppet/metatype/container.rb b/lib/puppet/metatype/container.rb deleted file mode 100644 index 2bbe3f546..000000000 --- a/lib/puppet/metatype/container.rb +++ /dev/null @@ -1,50 +0,0 @@ -class Puppet::Type - - # this is a retarded hack method to get around the difference between - # component children and file children - def self.depthfirst? - if defined? @depthfirst - return @depthfirst - else - return false - end - end - - def depthfirst? - self.class.depthfirst? - end - - # Add a hook for testing for recursion. - def parentof?(child) - if (self == child) - debug "parent is equal to child" - return true - elsif defined? @parent and @parent.parentof?(child) - debug "My parent is parent of child" - return true - else - return false - end - end - - # Remove an object. The argument determines whether the object's - # subscriptions get eliminated, too. - def remove(rmdeps = true) - # This is hackish (mmm, cut and paste), but it works for now, and it's - # better than warnings. - @parameters.each do |name, obj| - obj.remove - end - @parameters.clear - self.class.delete(self) - - @parent = nil - - # Remove the reference to the provider. - if self.provider - @provider.clear - @provider = nil - end - end -end - diff --git a/lib/puppet/metatype/evaluation.rb b/lib/puppet/metatype/evaluation.rb deleted file mode 100644 index 18bbb812f..000000000 --- a/lib/puppet/metatype/evaluation.rb +++ /dev/null @@ -1,163 +0,0 @@ -class Puppet::Type - # This method is responsible for collecting property changes we always - # descend into the children before we evaluate our current properties. - # This returns any changes resulting from testing, thus 'collect' rather - # than 'each'. - def evaluate - if self.provider.is_a?(Puppet::Provider) - unless provider.class.suitable? - raise Puppet::Error, "Provider %s is not functional on this platform" % provider.class.name - end - end - #Puppet.err "Evaluating %s" % self.path.join(":") - unless defined? @evalcount - self.err "No evalcount defined on '%s' of type '%s'" % - [self.title,self.class] - @evalcount = 0 - end - @evalcount += 1 - - if p = self.provider and p.respond_to?(:prefetch) - p.prefetch - end - - # this only operates on properties, not properties + children - # it's important that we call retrieve() on the type instance, - # not directly on the property, because it allows the type to override - # the method, like pfile does - currentvalues = self.retrieve - - changes = propertychanges(currentvalues).flatten - - # now record how many changes we've resulted in - if changes.length > 0 - self.debug "%s change(s)" % - [changes.length] - end - - # If we're in noop mode, we don't want to store the checked time, - # because it will result in the resource not getting scheduled if - # someone were to apply the catalog in non-noop mode. - # We're going to go ahead and record that we checked if there were - # no changes, since it's unlikely it will affect the scheduling. - noop = noop? - if ! noop or (noop && changes.length == 0) - self.cache(:checked, Time.now) - end - return changes.flatten - end - - # Flush the provider, if it supports it. This is called by the - # transaction. - def flush - if self.provider and self.provider.respond_to?(:flush) - self.provider.flush - end - end - - # if all contained objects are in sync, then we're in sync - # FIXME I don't think this is used on the type instances any more, - # it's really only used for testing - def insync?(is) - insync = true - - if property = @parameters[:ensure] - unless is.include? property - raise Puppet::DevError, - "The is value is not in the is array for '%s'" % - [property.name] - end - ensureis = is[property] - if property.insync?(ensureis) and property.should == :absent - return true - end - end - - properties.each { |property| - unless is.include? property - raise Puppet::DevError, - "The is value is not in the is array for '%s'" % - [property.name] - end - - propis = is[property] - unless property.insync?(propis) - property.debug("Not in sync: %s vs %s" % - [propis.inspect, property.should.inspect]) - insync = false - #else - # property.debug("In sync") - end - } - - #self.debug("%s sync status is %s" % [self,insync]) - return insync - end - - # retrieve the current value of all contained properties - def retrieve - return currentpropvalues - end - - # get a hash of the current properties. - def currentpropvalues(override_value = nil) - # it's important to use the method here, as it follows the order - # in which they're defined in the object - return properties().inject({}) { | prophash, property| - prophash[property] = override_value.nil? ? - property.retrieve : - override_value - prophash - } - end - - # Are we running in noop mode? - def noop? - if defined?(@noop) - @noop - else - Puppet[:noop] - end - end - - def noop - noop? - end - - # Retrieve the changes associated with all of the properties. - def propertychanges(currentvalues) - # If we are changing the existence of the object, then none of - # the other properties matter. - changes = [] - ensureparam = @parameters[:ensure] - - # This allows resource types to have 'ensure' be a parameter, which allows them to - # just pass the parameter on to other generated resources. - ensureparam = nil unless ensureparam.is_a?(Puppet::Property) - if ensureparam && !currentvalues.include?(ensureparam) - raise Puppet::DevError, "Parameter ensure defined but missing from current values" - end - - if ensureparam and ! ensureparam.insync?(currentvalues[ensureparam]) - changes << Puppet::Transaction::Change.new(ensureparam, currentvalues[ensureparam]) - # Else, if the 'ensure' property is correctly absent, then do - # nothing - elsif ensureparam and currentvalues[ensureparam] == :absent - return [] - else - changes = properties().find_all { |property| - currentvalues[property] ||= :absent - ! property.insync?(currentvalues[property]) - }.collect { |property| - Puppet::Transaction::Change.new(property, currentvalues[property]) - } - end - - if Puppet[:debug] and changes.length > 0 - self.debug("Changing " + changes.collect { |ch| ch.property.name }.join(",")) - end - - changes - end -end - diff --git a/lib/puppet/metatype/instances.rb b/lib/puppet/metatype/instances.rb deleted file mode 100644 index 3f44413f8..000000000 --- a/lib/puppet/metatype/instances.rb +++ /dev/null @@ -1,305 +0,0 @@ -require 'puppet/transportable' - -class Puppet::Type - # Make 'new' private, so people have to use create instead. - class << self - private :new - end - - # retrieve a named instance of the current type - def self.[](name) - @objects[name] || @aliases[name] - end - - # add an instance by name to the class list of instances - def self.[]=(name,object) - newobj = nil - if object.is_a?(Puppet::Type) - newobj = object - else - raise Puppet::DevError, "must pass a Puppet::Type object" - end - - if exobj = @objects[name] and self.isomorphic? - msg = "Object '%s[%s]' already exists" % - [newobj.class.name, name] - - if exobj.file and exobj.line - msg += ("in file %s at line %s" % - [object.file, object.line]) - end - if object.file and object.line - msg += ("and cannot be redefined in file %s at line %s" % - [object.file, object.line]) - end - error = Puppet::Error.new(msg) - raise error - else - #Puppet.info("adding %s of type %s to class list" % - # [name,object.class]) - @objects[name] = newobj - end - end - - # Create an alias. We keep these in a separate hash so that we don't encounter - # the objects multiple times when iterating over them. - def self.alias(name, obj) - if @objects.include?(name) - unless @objects[name] == obj - raise Puppet::Error.new( - "Cannot create alias %s: object already exists" % - [name] - ) - end - end - - if @aliases.include?(name) - unless @aliases[name] == obj - raise Puppet::Error.new( - "Object %s already has alias %s" % - [@aliases[name].name, name] - ) - end - end - - @aliases[name] = obj - end - - # remove all of the instances of a single type - def self.clear - if defined? @objects - @objects.each do |name, obj| - obj.remove(true) - end - @objects.clear - end - if defined? @aliases - @aliases.clear - end - end - - # Force users to call this, so that we can merge objects if - # necessary. - def self.create(args) - # Don't modify the original hash; instead, create a duplicate and modify it. - # We have to dup and use the ! so that it stays a TransObject if it is - # one. - hash = args.dup - symbolizehash!(hash) - - # If we're the base class, then pass the info on appropriately - if self == Puppet::Type - type = nil - if hash.is_a? Puppet::TransObject - type = hash.type - else - # If we're using the type to determine object type, then delete it - if type = hash[:type] - hash.delete(:type) - end - end - - # If they've specified a type and called on the base, then - # delegate to the subclass. - if type - if typeklass = self.type(type) - return typeklass.create(hash) - else - raise Puppet::Error, "Unknown type %s" % type - end - else - raise Puppet::Error, "No type found for %s" % hash.inspect - end - end - - # Handle this new object being implicit - implicit = hash[:implicit] || false - if hash.include?(:implicit) - hash.delete(:implicit) - end - - name = nil - unless hash.is_a? Puppet::TransObject - hash = self.hash2trans(hash) - end - - # XXX This will have to change when transobjects change to using titles - title = hash.name - - # if the object already exists - if self.isomorphic? and retobj = self[title] - # if only one of our objects is implicit, then it's easy to see - # who wins -- the non-implicit one. - if retobj.implicit? and ! implicit - Puppet.notice "Removing implicit %s" % retobj.title - # Remove all of the objects, but do not remove their subscriptions. - retobj.remove(false) - - # now pass through and create the new object - elsif implicit - Puppet.debug "Ignoring implicit %s[%s]" % [self.name, title] - return nil - else - raise Puppet::Error, "%s is already being managed" % retobj.ref - end - end - - # create it anew - # if there's a failure, destroy the object if it got that far, but raise - # the error. - begin - obj = new(hash) - rescue => detail - Puppet.err "Could not create %s: %s" % [title, detail.to_s] - if obj - obj.remove(true) - elsif obj = self[title] - obj.remove(true) - end - raise - end - - if implicit - obj.implicit = true - end - - # Store the object by title - self[obj.title] = obj - - return obj - end - - # remove a specified object - def self.delete(resource) - return unless defined? @objects - if @objects.include?(resource.title) - @objects.delete(resource.title) - end - if @aliases.include?(resource.title) - @aliases.delete(resource.title) - end - if @aliases.has_value?(resource) - names = [] - @aliases.each do |name, otherres| - if otherres == resource - names << name - end - end - names.each { |name| @aliases.delete(name) } - end - end - - # iterate across each of the type's instances - def self.each - return unless defined? @objects - @objects.each { |name,instance| - yield instance - } - end - - # does the type have an object with the given name? - def self.has_key?(name) - return @objects.has_key?(name) - end - - # Convert a hash to a TransObject. - def self.hash2trans(hash) - title = nil - if hash.include? :title - title = hash[:title] - hash.delete(:title) - elsif hash.include? self.namevar - title = hash[self.namevar] - hash.delete(self.namevar) - - if hash.include? :name - raise ArgumentError, "Cannot provide both name and %s to %s" % - [self.namevar, self.name] - end - elsif hash[:name] - title = hash[:name] - hash.delete :name - end - - if catalog = hash[:catalog] - hash.delete(:catalog) - end - - raise(Puppet::Error, "You must specify a title for objects of type %s" % self.to_s) unless title - - if hash.include? :type - unless self.validattr? :type - hash.delete :type - end - end - - # okay, now make a transobject out of hash - begin - trans = Puppet::TransObject.new(title, self.name.to_s) - trans.catalog = catalog if catalog - hash.each { |param, value| - trans[param] = value - } - rescue => detail - raise Puppet::Error, "Could not create %s: %s" % - [name, detail] - end - - return trans - end - - # Retrieve all known instances. Either requires providers or must be overridden. - def self.instances - unless defined?(@providers) and ! @providers.empty? - raise Puppet::DevError, "%s has no providers and has not overridden 'instances'" % self.name - end - - # Put the default provider first, then the rest of the suitable providers. - provider_instances = {} - providers_by_source.collect do |provider| - provider.instances.collect do |instance| - # First try to get the resource if it already exists - # Skip instances that map to a managed resource with a different provider - next if resource = self[instance.name] and resource.provider.class != instance.class - - # We always want to use the "first" provider instance we find, unless the resource - # is already managed and has a different provider set - if other = provider_instances[instance.name] - Puppet.warning "%s %s found in both %s and %s; skipping the %s version" % - [self.name.to_s.capitalize, instance.name, other.class.name, instance.class.name, instance.class.name] - next - end - provider_instances[instance.name] = instance - - if resource - resource.provider = instance - resource - else - create(:name => instance.name, :provider => instance, :check => :all) - end - end - end.flatten.compact - end - - # Return a list of one suitable provider per source, with the default provider first. - def self.providers_by_source - # Put the default provider first, then the rest of the suitable providers. - sources = [] - [defaultprovider, suitableprovider].flatten.uniq.collect do |provider| - next if sources.include?(provider.source) - - sources << provider.source - provider - end.compact - end - - # Create the path for logging and such. - def pathbuilder - if p = parent - [p.pathbuilder, self.ref].flatten - else - [self.ref] - end - end -end - diff --git a/lib/puppet/metatype/metaparams.rb b/lib/puppet/metatype/metaparams.rb deleted file mode 100644 index df96733c2..000000000 --- a/lib/puppet/metatype/metaparams.rb +++ /dev/null @@ -1,424 +0,0 @@ -require 'puppet' -require 'puppet/type' - -class Puppet::Type - # Add all of the meta parameters. - #newmetaparam(:onerror) do - # desc "How to handle errors -- roll back innermost - # transaction, roll back entire transaction, ignore, etc. Currently - # non-functional." - #end - - newmetaparam(:noop) do - desc "Boolean flag indicating whether work should actually - be done." - - newvalues(:true, :false) - munge do |value| - case value - when true, :true, "true": @resource.noop = true - when false, :false, "false": @resource.noop = false - end - end - end - - newmetaparam(:schedule) do - desc "On what schedule the object should be managed. You must create a - schedule object, and then reference the name of that object to use - that for your schedule:: - - schedule { daily: - period => daily, - range => \"2-4\" - } - - exec { \"/usr/bin/apt-get update\": - schedule => daily - } - - The creation of the schedule object does not need to appear in the - configuration before objects that use it." - end - - newmetaparam(:check) do - desc "Propertys which should have their values retrieved - but which should not actually be modified. This is currently used - internally, but will eventually be used for querying, so that you - could specify that you wanted to check the install state of all - packages, and then query the Puppet client daemon to get reports - on all packages." - - munge do |args| - # If they've specified all, collect all known properties - if args == :all - args = @resource.class.properties.find_all do |property| - # Only get properties supported by our provider - if @resource.provider - @resource.provider.class.supports_parameter?(property) - else - true - end - end.collect do |property| - property.name - end - end - - unless args.is_a?(Array) - args = [args] - end - - unless defined? @resource - self.devfail "No parent for %s, %s?" % - [self.class, self.name] - end - - args.each { |property| - unless property.is_a?(Symbol) - property = property.intern - end - next if @resource.propertydefined?(property) - - unless propertyklass = @resource.class.validproperty?(property) - if @resource.class.validattr?(property) - next - else - raise Puppet::Error, "%s is not a valid attribute for %s" % - [property, self.class.name] - end - end - next unless propertyklass.checkable? - @resource.newattr(property) - } - end - end - - # We've got four relationship metaparameters, so this method is used - # to reduce code duplication between them. - def munge_relationship(param, values) - # We need to support values passed in as an array or as a - # resource reference. - result = [] - - # 'values' could be an array or a reference. If it's an array, - # it could be an array of references or an array of arrays. - if values.is_a?(Puppet::Type) - result << [values.class.name, values.title] - else - unless values.is_a?(Array) - devfail "Relationships must be resource references" - end - if values[0].is_a?(String) or values[0].is_a?(Symbol) - # we're a type/title array reference - values[0] = symbolize(values[0]) - result << values - else - # we're an array of stuff - values.each do |value| - if value.is_a?(Puppet::Type) - result << [value.class.name, value.title] - elsif value.is_a?(Array) - value[0] = symbolize(value[0]) - result << value - else - devfail "Invalid relationship %s" % value.inspect - end - end - end - end - - if existing = self[param] - result = existing + result - end - - result - end - - newmetaparam(:loglevel) do - desc "Sets the level that information will be logged. - The log levels have the biggest impact when logs are sent to - syslog (which is currently the default)." - defaultto :notice - - newvalues(*Puppet::Util::Log.levels) - newvalues(:verbose) - - munge do |loglevel| - val = super(loglevel) - if val == :verbose - val = :info - end - val - end - end - - newmetaparam(:alias) do - desc "Creates an alias for the object. Puppet uses this internally when you - provide a symbolic name:: - - file { sshdconfig: - path => $operatingsystem ? { - solaris => \"/usr/local/etc/ssh/sshd_config\", - default => \"/etc/ssh/sshd_config\" - }, - source => \"...\" - } - - service { sshd: - subscribe => file[sshdconfig] - } - - When you use this feature, the parser sets ``sshdconfig`` as the name, - and the library sets that as an alias for the file so the dependency - lookup for ``sshd`` works. You can use this parameter yourself, - but note that only the library can use these aliases; for instance, - the following code will not work:: - - file { \"/etc/ssh/sshd_config\": - owner => root, - group => root, - alias => sshdconfig - } - - file { sshdconfig: - mode => 644 - } - - There's no way here for the Puppet parser to know that these two stanzas - should be affecting the same file. - - See the `LanguageTutorial language tutorial`:trac: for more information. - - " - - munge do |aliases| - unless aliases.is_a?(Array) - aliases = [aliases] - end - - raise(ArgumentError, "Cannot add aliases without a catalog") unless @resource.catalog - - @resource.info "Adding aliases %s" % aliases.collect { |a| a.inspect }.join(", ") - - aliases.each do |other| - if obj = @resource.catalog.resource(@resource.class.name, other) - unless obj.object_id == @resource.object_id - self.fail("%s can not create alias %s: object already exists" % [@resource.title, other]) - end - next - end - - # LAK:FIXME Old-school, add the alias to the class. - @resource.class.alias(other, @resource) - - # Newschool, add it to the catalog. - @resource.catalog.alias(@resource, other) - end - end - end - - newmetaparam(:tag) do - desc "Add the specified tags to the associated resource. While all resources - are automatically tagged with as much information as possible - (e.g., each class and definition containing the resource), it can - be useful to add your own tags to a given resource. - - Tags are currently useful for things like applying a subset of a - host's configuration:: - - puppetd --test --tags mytag - - This way, when you're testing a configuration you can run just the - portion you're testing." - - munge do |tags| - tags = [tags] unless tags.is_a? Array - - tags.each do |tag| - @resource.tag(tag) - end - end - end - - class RelationshipMetaparam < Puppet::Parameter - class << self - attr_accessor :direction, :events, :callback, :subclasses - end - - @subclasses = [] - - def self.inherited(sub) - @subclasses << sub - end - - def munge(rels) - @resource.munge_relationship(self.class.name, rels) - end - - def validate_relationship - @value.each do |value| - unless @resource.catalog.resource(*value) - description = self.class.direction == :in ? "dependency" : "dependent" - fail Puppet::Error, "Could not find %s %s[%s] for %s" % - [description, value[0].to_s.capitalize, value[1], resource.ref] - end - end - end - - # Create edges from each of our relationships. :in - # relationships are specified by the event-receivers, and :out - # relationships are specified by the event generator. This - # way 'source' and 'target' are consistent terms in both edges - # and events -- that is, an event targets edges whose source matches - # the event's source. The direction of the relationship determines - # which resource is applied first and which resource is considered - # to be the event generator. - def to_edges - @value.collect do |value| - # we just have a name and a type, and we need to convert it - # to an object... - tname, name = value - reference = Puppet::ResourceReference.new(tname, name) - - # Either of the two retrieval attempts could have returned - # nil. - unless object = reference.resolve - self.fail "Could not retrieve dependency '%s' of %s" % [reference, @resource.ref] - end - - # Are we requiring them, or vice versa? See the method docs - # for futher info on this. - if self.class.direction == :in - source = object - target = @resource - else - source = @resource - target = object - end - - if method = self.class.callback - subargs = { - :event => self.class.events, - :callback => method - } - self.debug("subscribes to %s" % [object.ref]) - else - # If there's no callback, there's no point in even adding - # a label. - subargs = nil - self.debug("requires %s" % [object.ref]) - end - - rel = Puppet::Relationship.new(source, target, subargs) - end - end - end - - def self.relationship_params - RelationshipMetaparam.subclasses - end - - - # Note that the order in which the relationships params is defined - # matters. The labelled params (notify and subcribe) must be later, - # so that if both params are used, those ones win. It's a hackish - # solution, but it works. - - newmetaparam(:require, :parent => RelationshipMetaparam, :attributes => {:direction => :in, :events => :NONE}) do - desc "One or more objects that this object depends on. - This is used purely for guaranteeing that changes to required objects - happen before the dependent object. For instance:: - - # Create the destination directory before you copy things down - file { \"/usr/local/scripts\": - ensure => directory - } - - file { \"/usr/local/scripts/myscript\": - source => \"puppet://server/module/myscript\", - mode => 755, - require => File[\"/usr/local/scripts\"] - } - - Multiple dependencies can be specified by providing a comma-seperated list - of resources, enclosed in square brackets:: - - require => [ File[\"/usr/local\"], File[\"/usr/local/scripts\"] ] - - Note that Puppet will autorequire everything that it can, and - there are hooks in place so that it's easy for resources to add new - ways to autorequire objects, so if you think Puppet could be - smarter here, let us know. - - In fact, the above code was redundant -- Puppet will autorequire - any parent directories that are being managed; it will - automatically realize that the parent directory should be created - before the script is pulled down. - - Currently, exec resources will autorequire their CWD (if it is - specified) plus any fully qualified paths that appear in the - command. For instance, if you had an ``exec`` command that ran - the ``myscript`` mentioned above, the above code that pulls the - file down would be automatically listed as a requirement to the - ``exec`` code, so that you would always be running againts the - most recent version. - " - end - - newmetaparam(:subscribe, :parent => RelationshipMetaparam, :attributes => {:direction => :in, :events => :ALL_EVENTS, :callback => :refresh}) do - desc "One or more objects that this object depends on. Changes in the - subscribed to objects result in the dependent objects being - refreshed (e.g., a service will get restarted). For instance:: - - class nagios { - file { \"/etc/nagios/nagios.conf\": - source => \"puppet://server/module/nagios.conf\", - alias => nagconf # just to make things easier for me - } - service { nagios: - running => true, - subscribe => File[nagconf] - } - } - - Currently the ``exec``, ``mount`` and ``service`` type support - refreshing. - " - end - - newmetaparam(:before, :parent => RelationshipMetaparam, :attributes => {:direction => :out, :events => :NONE}) do - desc %{This parameter is the opposite of **require** -- it guarantees - that the specified object is applied later than the specifying - object:: - - file { "/var/nagios/configuration": - source => "...", - recurse => true, - before => Exec["nagios-rebuid"] - } - - exec { "nagios-rebuild": - command => "/usr/bin/make", - cwd => "/var/nagios/configuration" - } - - This will make sure all of the files are up to date before the - make command is run.} - end - - newmetaparam(:notify, :parent => RelationshipMetaparam, :attributes => {:direction => :out, :events => :ALL_EVENTS, :callback => :refresh}) do - desc %{This parameter is the opposite of **subscribe** -- it sends events - to the specified object:: - - file { "/etc/sshd_config": - source => "....", - notify => Service[sshd] - } - - service { sshd: - ensure => running - } - - This will restart the sshd service if the sshd config file changes.} - end -end # Puppet::Type - diff --git a/lib/puppet/metatype/providers.rb b/lib/puppet/metatype/providers.rb deleted file mode 100644 index 6308f7e54..000000000 --- a/lib/puppet/metatype/providers.rb +++ /dev/null @@ -1,247 +0,0 @@ -require 'puppet/provider' -require 'puppet/util/provider_features' -class Puppet::Type - # Add the feature handling module. - extend Puppet::Util::ProviderFeatures - - attr_reader :provider - - # the Type class attribute accessors - class << self - attr_accessor :providerloader - attr_writer :defaultprovider - end - - # Find the default provider. - def self.defaultprovider - unless defined? @defaultprovider and @defaultprovider - suitable = suitableprovider() - - # Find which providers are a default for this system. - defaults = suitable.find_all { |provider| provider.default? } - - # If we don't have any default we use suitable providers - defaults = suitable if defaults.empty? - max = defaults.collect { |provider| provider.defaultnum }.max - defaults = defaults.find_all { |provider| provider.defaultnum == max } - - retval = nil - if defaults.length > 1 - Puppet.warning( - "Found multiple default providers for %s: %s; using %s" % - [self.name, defaults.collect { |i| i.name.to_s }.join(", "), - defaults[0].name] - ) - retval = defaults.shift - elsif defaults.length == 1 - retval = defaults.shift - else - raise Puppet::DevError, "Could not find a default provider for %s" % - self.name - end - - @defaultprovider = retval - end - - return @defaultprovider - end - - # Convert a hash, as provided by, um, a provider, into an instance of self. - def self.hash2obj(hash) - obj = nil - - namevar = self.namevar - unless hash.include?(namevar) and hash[namevar] - raise Puppet::DevError, "Hash was not passed with namevar" - end - - # if the obj already exists with that name... - if obj = self[hash[namevar]] - # We're assuming here that objects with the same name - # are the same object, which *should* be the case, assuming - # we've set up our naming stuff correctly everywhere. - - # Mark found objects as present - hash.each { |param, value| - if property = obj.property(param) - elsif val = obj[param] - obj[param] = val - else - # There is a value on disk, but it should go away - obj[param] = :absent - end - } - else - # create a new obj, since no existing one seems to - # match - obj = self.create(namevar => hash[namevar]) - - # We can't just pass the hash in at object creation time, - # because it sets the should value, not the is value. - hash.delete(namevar) - hash.each { |param, value| - obj[param] = value unless obj.add_property_parameter(param) - } - end - - return obj - end - - # Retrieve a provider by name. - def self.provider(name) - name = Puppet::Util.symbolize(name) - - # If we don't have it yet, try loading it. - unless @providers.has_key?(name) - @providerloader.load(name) - end - return @providers[name] - end - - # Just list all of the providers. - def self.providers - @providers.keys - end - - def self.validprovider?(name) - name = Puppet::Util.symbolize(name) - - return (@providers.has_key?(name) && @providers[name].suitable?) - end - - # Create a new provider of a type. This method must be called - # directly on the type that it's implementing. - def self.provide(name, options = {}, &block) - name = Puppet::Util.symbolize(name) - - if obj = @providers[name] - Puppet.debug "Reloading %s %s provider" % [name, self.name] - unprovide(name) - end - - parent = if pname = options[:parent] - options.delete(:parent) - if pname.is_a? Class - pname - else - if provider = self.provider(pname) - provider - else - raise Puppet::DevError, - "Could not find parent provider %s of %s" % - [pname, name] - end - end - else - Puppet::Provider - end - - options[:resource_type] ||= self - - self.providify - - provider = genclass(name, - :parent => parent, - :hash => @providers, - :prefix => "Provider", - :block => block, - :include => feature_module, - :extend => feature_module, - :attributes => options - ) - - return provider - end - - # Make sure we have a :provider parameter defined. Only gets called if there - # are providers. - def self.providify - return if @paramhash.has_key? :provider - - newparam(:provider) do - desc "The specific backend for #{self.name.to_s} to use. You will - seldom need to specify this -- Puppet will usually discover the - appropriate provider for your platform." - - # This is so we can refer back to the type to get a list of - # providers for documentation. - class << self - attr_accessor :parenttype - end - - # We need to add documentation for each provider. - def self.doc - @doc + " Available providers are:\n\n" + parenttype().providers.sort { |a,b| - a.to_s <=> b.to_s - }.collect { |i| - "* **%s**: %s" % [i, parenttype().provider(i).doc] - }.join("\n") - end - - defaultto { - @resource.class.defaultprovider.name - } - - validate do |provider_class| - provider_class = provider_class[0] if provider_class.is_a? Array - if provider_class.is_a?(Puppet::Provider) - provider_class = provider_class.class.name - end - - unless provider = @resource.class.provider(provider_class) - raise ArgumentError, "Invalid %s provider '%s'" % [@resource.class.name, provider_class] - end - end - - munge do |provider| - provider = provider[0] if provider.is_a? Array - if provider.is_a? String - provider = provider.intern - end - @resource.provider = provider - - if provider.is_a?(Puppet::Provider) - provider.class.name - else - provider - end - end - end.parenttype = self - end - - def self.unprovide(name) - if @providers.has_key? name - rmclass(name, - :hash => @providers, - :prefix => "Provider" - ) - if @defaultprovider and @defaultprovider.name == name - @defaultprovider = nil - end - end - end - - # Return an array of all of the suitable providers. - def self.suitableprovider - if @providers.empty? - providerloader.loadall - end - @providers.find_all { |name, provider| - provider.suitable? - }.collect { |name, provider| - provider - }.reject { |p| p.name == :fake } # For testing - end - - def provider=(name) - if name.is_a?(Puppet::Provider) - @provider = name - @provider.resource = self - elsif klass = self.class.provider(name) - @provider = klass.new(self) - else - raise ArgumentError, "Could not find %s provider of %s" % - [name, self.class.name] - end - end -end diff --git a/lib/puppet/metatype/relationships.rb b/lib/puppet/metatype/relationships.rb deleted file mode 100644 index 4fb78ae56..000000000 --- a/lib/puppet/metatype/relationships.rb +++ /dev/null @@ -1,115 +0,0 @@ -class Puppet::Type - # Specify a block for generating a list of objects to autorequire. This - # makes it so that you don't have to manually specify things that you clearly - # require. - def self.autorequire(name, &block) - @autorequires ||= {} - @autorequires[name] = block - end - - # Yield each of those autorequires in turn, yo. - def self.eachautorequire - @autorequires ||= {} - @autorequires.each { |type, block| - yield(type, block) - } - end - - # Figure out of there are any objects we can automatically add as - # dependencies. - def autorequire - reqs = [] - self.class.eachautorequire { |type, block| - # Ignore any types we can't find, although that would be a bit odd. - next unless typeobj = Puppet.type(type) - - # Retrieve the list of names from the block. - next unless list = self.instance_eval(&block) - unless list.is_a?(Array) - list = [list] - end - - # Collect the current prereqs - list.each { |dep| - obj = nil - # Support them passing objects directly, to save some effort. - unless dep.is_a? Puppet::Type - # Skip autorequires that we aren't managing - unless dep = typeobj[dep] - next - end - end - - reqs << Puppet::Relationship.new(dep, self) - } - } - - return reqs - end - - # Build the dependencies associated with an individual object. - def builddepends - # Handle the requires - self.class.relationship_params.collect do |klass| - if param = @parameters[klass.name] - param.to_edges - end - end.flatten.reject { |r| r.nil? } - end - - # Does this resource have a relationship with the other? We have to - # check each object for both directions of relationship. - def requires?(other) - them = [other.class.name, other.title] - me = [self.class.name, self.title] - self.class.relationship_params.each do |param| - case param.direction - when :in: return true if v = self[param.name] and v.include?(them) - when :out: return true if v = other[param.name] and v.include?(me) - end - end - return false - end - - # we've received an event - # we only support local events right now, so we can pass actual - # objects around, including the transaction object - # the assumption here is that container objects will pass received - # methods on to contained objects - # i.e., we don't trigger our children, our refresh() method calls - # refresh() on our children - def trigger(event, source) - trans = event.transaction - if @callbacks.include?(source) - [:ALL_EVENTS, event.event].each { |eventname| - if method = @callbacks[source][eventname] - if trans.triggered?(self, method) > 0 - next - end - if self.respond_to?(method) - self.send(method) - end - - trans.triggered(self, method) - end - } - end - end - - # Unsubscribe from a given object, possibly with a specific event. - def unsubscribe(object, event = nil) - # First look through our own relationship params - [:require, :subscribe].each do |param| - if values = self[param] - newvals = values.reject { |d| - d == [object.class.name, object.title] - } - if newvals.length != values.length - self.delete(param) - self[param] = newvals - end - end - end - end -end - diff --git a/lib/puppet/metatype/schedules.rb b/lib/puppet/metatype/schedules.rb deleted file mode 100644 index 96ebce0ab..000000000 --- a/lib/puppet/metatype/schedules.rb +++ /dev/null @@ -1,33 +0,0 @@ -class Puppet::Type - # Look up the schedule and set it appropriately. This is done after - # the instantiation phase, so that the schedule can be anywhere in the - # file. - def schedule - unless defined? @schedule - if name = self[:schedule] - if sched = Puppet.type(:schedule)[name] - @schedule = sched - else - self.fail "Could not find schedule %s" % name - end - else - @schedule = nil - end - end - @schedule - end - - # Check whether we are scheduled to run right now or not. - def scheduled? - return true if Puppet[:ignoreschedules] - return true unless schedule = self.schedule - - # We use 'checked' here instead of 'synced' because otherwise we'll - # end up checking most resources most times, because they will generally - # have been synced a long time ago (e.g., a file only gets updated - # once a month on the server and its schedule is daily; the last sync time - # will have been a month ago, so we'd end up checking every run). - return schedule.match?(self.cached(:checked).to_i) - end -end - diff --git a/lib/puppet/metatype/tags.rb b/lib/puppet/metatype/tags.rb deleted file mode 100644 index 1d96306dd..000000000 --- a/lib/puppet/metatype/tags.rb +++ /dev/null @@ -1,38 +0,0 @@ -class Puppet::Type - attr_reader :tags - - # Add a new tag. - def tag(tag) - tag = tag.intern if tag.is_a? String - unless @tags.include? tag - @tags << tag - end - end - - # Define the initial list of tags. - def tags=(list) - list = [list] unless list.is_a? Array - - @tags = list.collect do |t| - case t - when String: t.intern - when Symbol: t - else - self.warning "Ignoring tag %s of type %s" % [tag.inspect, tag.class] - end - end - - @tags << self.class.name unless @tags.include?(self.class.name) - end - - # Figure out of any of the specified tags apply to this object. This is an - # OR operation. - def tagged?(tags) - tags = [tags] unless tags.is_a? Array - - tags = tags.collect { |t| t.intern } - - return tags.find { |tag| @tags.include? tag } - end -end - diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb index 45dd7f5b5..c7a866e2c 100644 --- a/lib/puppet/type.rb +++ b/lib/puppet/type.rb @@ -20,20 +20,2106 @@ class Type include Puppet::Util::LogPaths include Puppet::Util::Logging - # Nearly all of the code in this class is stored in files in the - # metatype/ directory. This is a temporary measure until I get a chance - # to refactor this class entirely. There's still more simplification to - # do, but this works for now. - require 'puppet/metatype/attributes' - require 'puppet/metatype/closure' - require 'puppet/metatype/container' - require 'puppet/metatype/evaluation' - require 'puppet/metatype/instances' - require 'puppet/metatype/metaparams' - require 'puppet/metatype/providers' - require 'puppet/metatype/relationships' - require 'puppet/metatype/schedules' - require 'puppet/metatype/tags' + ############################### + # Code related to resource type attributes. + class << self + include Puppet::Util::ClassGen + include Puppet::Util::Warnings + attr_reader :properties + end + + def self.states + warnonce "The states method is deprecated; use properties" + properties() + end + + # All parameters, in the appropriate order. The namevar comes first, + # then the properties, then the params and metaparams in the order they + # were specified in the files. + def self.allattrs + # now get all of the arguments, in a specific order + # Cache this, since it gets called so many times + namevar = self.namevar + + order = [namevar] + if self.parameters.include?(:provider) + order << :provider + end + order << [self.properties.collect { |property| property.name }, + self.parameters - [:provider], + self.metaparams].flatten.reject { |param| + # we don't want our namevar in there multiple times + param == namevar + } + + order.flatten! + + return order + end + + # Retrieve an attribute alias, if there is one. + def self.attr_alias(param) + @attr_aliases[symbolize(param)] + end + + # Create an alias to an existing attribute. This will cause the aliased + # attribute to be valid when setting and retrieving values on the instance. + def self.set_attr_alias(hash) + hash.each do |new, old| + @attr_aliases[symbolize(new)] = symbolize(old) + end + end + + # Find the class associated with any given attribute. + def self.attrclass(name) + @attrclasses ||= {} + + # We cache the value, since this method gets called such a huge number + # of times (as in, hundreds of thousands in a given run). + unless @attrclasses.include?(name) + @attrclasses[name] = case self.attrtype(name) + when :property: @validproperties[name] + when :meta: @@metaparamhash[name] + when :param: @paramhash[name] + end + end + @attrclasses[name] + end + + # What type of parameter are we dealing with? Cache the results, because + # this method gets called so many times. + def self.attrtype(attr) + @attrtypes ||= {} + unless @attrtypes.include?(attr) + @attrtypes[attr] = case + when @validproperties.include?(attr): :property + when @paramhash.include?(attr): :param + when @@metaparamhash.include?(attr): :meta + else + raise Puppet::DevError, + "Invalid attribute '%s' for class '%s'" % + [attr, self.name] + end + end + + @attrtypes[attr] + end + + # Copy an existing class parameter. This allows other types to avoid + # duplicating a parameter definition, and is mostly used by subclasses + # of the File class. + def self.copyparam(klass, name) + param = klass.attrclass(name) + + unless param + raise Puppet::DevError, "Class %s has no param %s" % [klass, name] + end + @parameters << param + @parameters.each { |p| @paramhash[name] = p } + + if param.isnamevar? + @namevar = param.name + end + end + + # A similar function but one that yields the class and type. + # This is mainly so that setdefaults doesn't call quite so many functions. + def self.eachattr(*ary) + if ary.empty? + ary = nil + end + + # We have to do this in a specific order, so that defaults are + # created in that order (e.g., providers should be set up before + # anything else). + allattrs.each do |name| + next unless ary.nil? or ary.include?(name) + if obj = @properties.find { |p| p.name == name } + yield obj, :property + elsif obj = @parameters.find { |p| p.name == name } + yield obj, :param + elsif obj = @@metaparams.find { |p| p.name == name } + yield obj, :meta + else + raise Puppet::DevError, "Could not find parameter %s" % name + end + end + end + + def self.eachmetaparam + @@metaparams.each { |p| yield p.name } + end + + # Create the 'ensure' class. This is a separate method so other types + # can easily call it and create their own 'ensure' values. + def self.ensurable(&block) + if block_given? + self.newproperty(:ensure, :parent => Puppet::Property::Ensure, &block) + else + self.newproperty(:ensure, :parent => Puppet::Property::Ensure) do + self.defaultvalues + end + end + end + + # Should we add the 'ensure' property to this class? + def self.ensurable? + # If the class has all three of these methods defined, then it's + # ensurable. + ens = [:exists?, :create, :destroy].inject { |set, method| + set &&= self.public_method_defined?(method) + } + + return ens + end + + # Deal with any options passed into parameters. + def self.handle_param_options(name, options) + # If it's a boolean parameter, create a method to test the value easily + if options[:boolean] + define_method(name.to_s + "?") do + val = self[name] + if val == :true or val == true + return true + end + end + end + + # If this param handles relationships, store that information + end + + # Is the parameter in question a meta-parameter? + def self.metaparam?(param) + @@metaparamhash.include?(symbolize(param)) + end + + # Find the metaparameter class associated with a given metaparameter name. + def self.metaparamclass(name) + @@metaparamhash[symbolize(name)] + end + + def self.metaparams + @@metaparams.collect { |param| param.name } + end + + def self.metaparamdoc(metaparam) + @@metaparamhash[metaparam].doc + end + + # Create a new metaparam. Requires a block and a name, stores it in the + # @parameters array, and does some basic checking on it. + def self.newmetaparam(name, options = {}, &block) + @@metaparams ||= [] + @@metaparamhash ||= {} + name = symbolize(name) + + param = genclass(name, + :parent => options[:parent] || Puppet::Parameter, + :prefix => "MetaParam", + :hash => @@metaparamhash, + :array => @@metaparams, + :attributes => options[:attributes], + &block + ) + + # Grr. + if options[:required_features] + param.required_features = options[:required_features] + end + + handle_param_options(name, options) + + param.metaparam = true + + return param + end + + # Find the namevar + def self.namevar + unless defined? @namevar + params = @parameters.find_all { |param| + param.isnamevar? or param.name == :name + } + + if params.length > 1 + raise Puppet::DevError, "Found multiple namevars for %s" % self.name + elsif params.length == 1 + @namevar = params[0].name + else + raise Puppet::DevError, "No namevar for %s" % self.name + end + end + @namevar + end + + # Create a new parameter. Requires a block and a name, stores it in the + # @parameters array, and does some basic checking on it. + def self.newparam(name, options = {}, &block) + options[:attributes] ||= {} + param = genclass(name, + :parent => options[:parent] || Puppet::Parameter, + :attributes => options[:attributes], + :block => block, + :prefix => "Parameter", + :array => @parameters, + :hash => @paramhash + ) + + handle_param_options(name, options) + + # Grr. + if options[:required_features] + param.required_features = options[:required_features] + end + + param.isnamevar if options[:namevar] + + # These might be enabled later. +# define_method(name) do +# @parameters[name].value +# end +# +# define_method(name.to_s + "=") do |value| +# newparam(param, value) +# end + + if param.isnamevar? + @namevar = param.name + end + + return param + end + + def self.newstate(name, options = {}, &block) + Puppet.warning "newstate() has been deprecrated; use newproperty(%s)" % + name + newproperty(name, options, &block) + end + + # Create a new property. The first parameter must be the name of the property; + # this is how users will refer to the property when creating new instances. + # The second parameter is a hash of options; the options are: + # * :parent: The parent class for the property. Defaults to Puppet::Property. + # * :retrieve: The method to call on the provider or @parent object (if + # the provider is not set) to retrieve the current value. + def self.newproperty(name, options = {}, &block) + name = symbolize(name) + + # This is here for types that might still have the old method of defining + # a parent class. + unless options.is_a? Hash + raise Puppet::DevError, + "Options must be a hash, not %s" % options.inspect + end + + if @validproperties.include?(name) + raise Puppet::DevError, "Class %s already has a property named %s" % + [self.name, name] + end + + if parent = options[:parent] + options.delete(:parent) + else + parent = Puppet::Property + end + + # We have to create our own, new block here because we want to define + # an initial :retrieve method, if told to, and then eval the passed + # block if available. + prop = genclass(name, :parent => parent, :hash => @validproperties, :attributes => options) do + # If they've passed a retrieve method, then override the retrieve + # method on the class. + if options[:retrieve] + define_method(:retrieve) do + provider.send(options[:retrieve]) + end + end + + if block + class_eval(&block) + end + end + + # If it's the 'ensure' property, always put it first. + if name == :ensure + @properties.unshift prop + else + @properties << prop + end + +# define_method(name) do +# @parameters[name].should +# end +# +# define_method(name.to_s + "=") do |value| +# newproperty(name, :should => value) +# end + + return prop + end + + def self.paramdoc(param) + @paramhash[param].doc + end + + # Return the parameter names + def self.parameters + return [] unless defined? @parameters + @parameters.collect { |klass| klass.name } + end + + # Find the parameter class associated with a given parameter name. + def self.paramclass(name) + @paramhash[name] + end + + # Return the property class associated with a name + def self.propertybyname(name) + @validproperties[name] + end + + def self.validattr?(name) + name = symbolize(name) + return true if name == :name + @validattrs ||= {} + + unless @validattrs.include?(name) + if self.validproperty?(name) or self.validparameter?(name) or self.metaparam?(name) + @validattrs[name] = true + else + @validattrs[name] = false + end + end + + @validattrs[name] + end + + # does the name reflect a valid property? + def self.validproperty?(name) + name = symbolize(name) + if @validproperties.include?(name) + return @validproperties[name] + else + return false + end + end + + # Return the list of validproperties + def self.validproperties + return {} unless defined? @parameters + + return @validproperties.keys + end + + # does the name reflect a valid parameter? + def self.validparameter?(name) + unless defined? @parameters + raise Puppet::DevError, "Class %s has not defined parameters" % self + end + if @paramhash.include?(name) or @@metaparamhash.include?(name) + return true + else + return false + end + end + + # fix any namevar => param translations + def argclean(oldhash) + # This duplication is here because it might be a transobject. + hash = oldhash.dup.to_hash + + if hash.include?(:resource) + hash.delete(:resource) + end + namevar = self.class.namevar + + # Do a simple translation for those cases where they've passed :name + # but that's not our namevar + if hash.include? :name and namevar != :name + if hash.include? namevar + raise ArgumentError, "Cannot provide both name and %s" % namevar + end + hash[namevar] = hash[:name] + hash.delete(:name) + end + + # Make sure we have a name, one way or another + unless hash.include? namevar + if defined? @title and @title + hash[namevar] = @title + else + raise Puppet::Error, "Was not passed a namevar or title" + end + end + + return hash + end + + # Return either the attribute alias or the attribute. + def attr_alias(name) + name = symbolize(name) + if synonym = self.class.attr_alias(name) + return synonym + else + return name + end + end + + # Are we deleting this resource? + def deleting? + obj = @parameters[:ensure] and obj.should == :absent + end + + # Create a new property if it is valid but doesn't exist + # Returns: true if a new parameter was added, false otherwise + def add_property_parameter(prop_name) + if self.class.validproperty?(prop_name) && !@parameters[prop_name] + self.newattr(prop_name) + return true + end + return false + end + + # abstract accessing parameters and properties, and normalize + # access to always be symbols, not strings + # This returns a value, not an object. It returns the 'is' + # value, but you can also specifically return 'is' and 'should' + # values using 'object.is(:property)' or 'object.should(:property)'. + def [](name) + name = attr_alias(name) + + unless self.class.validattr?(name) + raise TypeError.new("Invalid parameter %s(%s)" % [name, name.inspect]) + end + + if name == :name + name = self.class.namevar + end + + if obj = @parameters[name] + # Note that if this is a property, then the value is the "should" value, + # not the current value. + obj.value + else + return nil + end + end + + # Abstract setting parameters and properties, and normalize + # access to always be symbols, not strings. This sets the 'should' + # value on properties, and otherwise just sets the appropriate parameter. + def []=(name,value) + name = attr_alias(name) + + unless self.class.validattr?(name) + raise TypeError.new("Invalid parameter %s" % [name]) + end + + if name == :name + name = self.class.namevar + end + if value.nil? + raise Puppet::Error.new("Got nil value for %s" % name) + end + + if obj = @parameters[name] + obj.value = value + return nil + else + self.newattr(name, :value => value) + end + + nil + end + + # remove a property from the object; useful in testing or in cleanup + # when an error has been encountered + def delete(attr) + attr = symbolize(attr) + if @parameters.has_key?(attr) + @parameters.delete(attr) + else + raise Puppet::DevError.new("Undefined attribute '#{attr}' in #{self}") + end + end + + # iterate across the existing properties + def eachproperty + # properties() is a private method + properties().each { |property| + yield property + } + end + + # retrieve the 'should' value for a specified property + def should(name) + name = attr_alias(name) + if prop = @parameters[name] and prop.is_a?(Puppet::Property) + return prop.should + else + return nil + end + end + + # Create the actual attribute instance. Requires either the attribute + # name or class as the first argument, then an optional hash of + # attributes to set during initialization. + def newattr(name, options = {}) + if name.is_a?(Class) + klass = name + name = klass.name + end + + unless klass = self.class.attrclass(name) + raise Puppet::Error, "Resource type %s does not support parameter %s" % [self.class.name, name] + end + + if @parameters.include?(name) + raise Puppet::Error, "Parameter '%s' is already defined in %s" % + [name, self.ref] + end + + if provider and ! provider.class.supports_parameter?(klass) + missing = klass.required_features.find_all { |f| ! provider.class.feature?(f) } + info "Provider %s does not support features %s; not managing attribute %s" % [provider.class.name, missing.join(", "), name] + return nil + end + + # Add resource information at creation time, so it's available + # during validation. + options[:resource] = self + begin + # make sure the parameter doesn't have any errors + return @parameters[name] = klass.new(options) + rescue => detail + error = Puppet::Error.new("Parameter %s failed: %s" % + [name, detail]) + error.set_backtrace(detail.backtrace) + raise error + end + end + + # return the value of a parameter + def parameter(name) + unless name.is_a? Symbol + name = name.intern + end + return @parameters[name].value + end + + # Is the named property defined? + def propertydefined?(name) + unless name.is_a? Symbol + name = name.intern + end + return @parameters.include?(name) + end + + # return an actual type by name; to return the value, use 'inst[name]' + # FIXME this method should go away + def property(name) + if obj = @parameters[symbolize(name)] and obj.is_a?(Puppet::Property) + return obj + else + return nil + end + end + +# def set(name, value) +# send(name.to_s + "=", value) +# end +# +# def get(name) +# send(name) +# end + + # For any parameters or properties that have defaults and have not yet been + # set, set them now. This method can be handed a list of attributes, + # and if so it will only set defaults for those attributes. + def setdefaults(*ary) + #self.class.eachattr(*ary) { |klass, type| + self.class.eachattr(*ary) { |klass, type| + # not many attributes will have defaults defined, so we short-circuit + # those away + next unless klass.method_defined?(:default) + next if @parameters[klass.name] + + next unless obj = self.newattr(klass) + + # We have to check for nil values, not "truth", so we allow defaults + # to false. + value = obj.default and ! value.nil? + if ! value.nil? + obj.value = value + else + @parameters.delete(obj.name) + end + } + end + + # Convert our object to a hash. This just includes properties. + def to_hash + rethash = {} + + @parameters.each do |name, obj| + rethash[name] = obj.value + end + + rethash + end + + # Return a specific value for an attribute. + def value(name) + name = attr_alias(name) + + if obj = @parameters[name] and obj.respond_to?(:value) + return obj.value + else + return nil + end + end + + # Meta-parameter methods: These methods deal with the results + # of specifying metaparameters + + private + + # Return all of the property objects, in the order specified in the + # class. + def properties + #debug "%s has %s properties" % [self,@parameters.length] + props = self.class.properties.collect { |prop| + @parameters[prop.name] + }.find_all { |p| + ! p.nil? + }.each do |prop| + unless prop.is_a?(Puppet::Property) + raise Puppet::DevError, "got a non-property %s(%s)" % + [prop.class, prop.class.name] + end + end + + props + end + + public + + ############################### + # Code related to the closure-like behaviour of the resource classes. + attr_writer :implicit + + # Is this type's name isomorphic with the object? That is, if the + # name conflicts, does it necessarily mean that the objects conflict? + # Defaults to true. + def self.isomorphic? + if defined? @isomorphic + return @isomorphic + else + return true + end + end + + def implicit? + if defined? @implicit and @implicit + return true + else + return false + end + end + + def isomorphic? + self.class.isomorphic? + end + + # is the instance a managed instance? A 'yes' here means that + # the instance was created from the language, vs. being created + # in order resolve other questions, such as finding a package + # in a list + def managed? + # Once an object is managed, it always stays managed; but an object + # that is listed as unmanaged might become managed later in the process, + # so we have to check that every time + if defined? @managed and @managed + return @managed + else + @managed = false + properties.each { |property| + s = property.should + if s and ! property.class.unmanaged + @managed = true + break + end + } + return @managed + end + end + + ############################### + # Code related to the container behaviour. + def self.depthfirst? + if defined? @depthfirst + return @depthfirst + else + return false + end + end + + def depthfirst? + self.class.depthfirst? + end + + # Add a hook for testing for recursion. + def parentof?(child) + if (self == child) + debug "parent is equal to child" + return true + elsif defined? @parent and @parent.parentof?(child) + debug "My parent is parent of child" + return true + else + return false + end + end + + # Remove an object. The argument determines whether the object's + # subscriptions get eliminated, too. + def remove(rmdeps = true) + # This is hackish (mmm, cut and paste), but it works for now, and it's + # better than warnings. + @parameters.each do |name, obj| + obj.remove + end + @parameters.clear + self.class.delete(self) + + @parent = nil + + # Remove the reference to the provider. + if self.provider + @provider.clear + @provider = nil + end + end + + ############################### + # Code related to evaluating the resources. + + # This method is responsible for collecting property changes we always + # descend into the children before we evaluate our current properties. + # This returns any changes resulting from testing, thus 'collect' rather + # than 'each'. + def evaluate + if self.provider.is_a?(Puppet::Provider) + unless provider.class.suitable? + raise Puppet::Error, "Provider %s is not functional on this platform" % provider.class.name + end + end + #Puppet.err "Evaluating %s" % self.path.join(":") + unless defined? @evalcount + self.err "No evalcount defined on '%s' of type '%s'" % + [self.title,self.class] + @evalcount = 0 + end + @evalcount += 1 + + if p = self.provider and p.respond_to?(:prefetch) + p.prefetch + end + + # this only operates on properties, not properties + children + # it's important that we call retrieve() on the type instance, + # not directly on the property, because it allows the type to override + # the method, like pfile does + currentvalues = self.retrieve + + changes = propertychanges(currentvalues).flatten + + # now record how many changes we've resulted in + if changes.length > 0 + self.debug "%s change(s)" % + [changes.length] + end + + # If we're in noop mode, we don't want to store the checked time, + # because it will result in the resource not getting scheduled if + # someone were to apply the catalog in non-noop mode. + # We're going to go ahead and record that we checked if there were + # no changes, since it's unlikely it will affect the scheduling. + noop = noop? + if ! noop or (noop && changes.length == 0) + self.cache(:checked, Time.now) + end + return changes.flatten + end + + # Flush the provider, if it supports it. This is called by the + # transaction. + def flush + if self.provider and self.provider.respond_to?(:flush) + self.provider.flush + end + end + + # if all contained objects are in sync, then we're in sync + # FIXME I don't think this is used on the type instances any more, + # it's really only used for testing + def insync?(is) + insync = true + + if property = @parameters[:ensure] + unless is.include? property + raise Puppet::DevError, + "The is value is not in the is array for '%s'" % + [property.name] + end + ensureis = is[property] + if property.insync?(ensureis) and property.should == :absent + return true + end + end + + properties.each { |property| + unless is.include? property + raise Puppet::DevError, + "The is value is not in the is array for '%s'" % + [property.name] + end + + propis = is[property] + unless property.insync?(propis) + property.debug("Not in sync: %s vs %s" % + [propis.inspect, property.should.inspect]) + insync = false + #else + # property.debug("In sync") + end + } + + #self.debug("%s sync status is %s" % [self,insync]) + return insync + end + + # retrieve the current value of all contained properties + def retrieve + return currentpropvalues + end + + # get a hash of the current properties. + def currentpropvalues(override_value = nil) + # it's important to use the method here, as it follows the order + # in which they're defined in the object + return properties().inject({}) { | prophash, property| + prophash[property] = override_value.nil? ? + property.retrieve : + override_value + prophash + } + end + + # Are we running in noop mode? + def noop? + if defined?(@noop) + @noop + else + Puppet[:noop] + end + end + + def noop + noop? + end + + # Retrieve the changes associated with all of the properties. + def propertychanges(currentvalues) + # If we are changing the existence of the object, then none of + # the other properties matter. + changes = [] + ensureparam = @parameters[:ensure] + + # This allows resource types to have 'ensure' be a parameter, which allows them to + # just pass the parameter on to other generated resources. + ensureparam = nil unless ensureparam.is_a?(Puppet::Property) + if ensureparam && !currentvalues.include?(ensureparam) + raise Puppet::DevError, "Parameter ensure defined but missing from current values" + end + + if ensureparam and ! ensureparam.insync?(currentvalues[ensureparam]) + changes << Puppet::Transaction::Change.new(ensureparam, currentvalues[ensureparam]) + # Else, if the 'ensure' property is correctly absent, then do + # nothing + elsif ensureparam and currentvalues[ensureparam] == :absent + return [] + else + changes = properties().find_all { |property| + currentvalues[property] ||= :absent + ! property.insync?(currentvalues[property]) + }.collect { |property| + Puppet::Transaction::Change.new(property, currentvalues[property]) + } + end + + if Puppet[:debug] and changes.length > 0 + self.debug("Changing " + changes.collect { |ch| ch.property.name }.join(",")) + end + + changes + end + + ############################### + # Code related to managing resource instances. + require 'puppet/transportable' + + # Make 'new' private, so people have to use create instead. + class << self + private :new + end + + # retrieve a named instance of the current type + def self.[](name) + @objects[name] || @aliases[name] + end + + # add an instance by name to the class list of instances + def self.[]=(name,object) + newobj = nil + if object.is_a?(Puppet::Type) + newobj = object + else + raise Puppet::DevError, "must pass a Puppet::Type object" + end + + if exobj = @objects[name] and self.isomorphic? + msg = "Object '%s[%s]' already exists" % + [newobj.class.name, name] + + if exobj.file and exobj.line + msg += ("in file %s at line %s" % + [object.file, object.line]) + end + if object.file and object.line + msg += ("and cannot be redefined in file %s at line %s" % + [object.file, object.line]) + end + error = Puppet::Error.new(msg) + raise error + else + #Puppet.info("adding %s of type %s to class list" % + # [name,object.class]) + @objects[name] = newobj + end + end + + # Create an alias. We keep these in a separate hash so that we don't encounter + # the objects multiple times when iterating over them. + def self.alias(name, obj) + if @objects.include?(name) + unless @objects[name] == obj + raise Puppet::Error.new( + "Cannot create alias %s: object already exists" % + [name] + ) + end + end + + if @aliases.include?(name) + unless @aliases[name] == obj + raise Puppet::Error.new( + "Object %s already has alias %s" % + [@aliases[name].name, name] + ) + end + end + + @aliases[name] = obj + end + + # remove all of the instances of a single type + def self.clear + if defined? @objects + @objects.each do |name, obj| + obj.remove(true) + end + @objects.clear + end + if defined? @aliases + @aliases.clear + end + end + + # Force users to call this, so that we can merge objects if + # necessary. + def self.create(args) + # Don't modify the original hash; instead, create a duplicate and modify it. + # We have to dup and use the ! so that it stays a TransObject if it is + # one. + hash = args.dup + symbolizehash!(hash) + + # If we're the base class, then pass the info on appropriately + if self == Puppet::Type + type = nil + if hash.is_a? Puppet::TransObject + type = hash.type + else + # If we're using the type to determine object type, then delete it + if type = hash[:type] + hash.delete(:type) + end + end + + # If they've specified a type and called on the base, then + # delegate to the subclass. + if type + if typeklass = self.type(type) + return typeklass.create(hash) + else + raise Puppet::Error, "Unknown type %s" % type + end + else + raise Puppet::Error, "No type found for %s" % hash.inspect + end + end + + # Handle this new object being implicit + implicit = hash[:implicit] || false + if hash.include?(:implicit) + hash.delete(:implicit) + end + + name = nil + unless hash.is_a? Puppet::TransObject + hash = self.hash2trans(hash) + end + + # XXX This will have to change when transobjects change to using titles + title = hash.name + + # if the object already exists + if self.isomorphic? and retobj = self[title] + # if only one of our objects is implicit, then it's easy to see + # who wins -- the non-implicit one. + if retobj.implicit? and ! implicit + Puppet.notice "Removing implicit %s" % retobj.title + # Remove all of the objects, but do not remove their subscriptions. + retobj.remove(false) + + # now pass through and create the new object + elsif implicit + Puppet.debug "Ignoring implicit %s[%s]" % [self.name, title] + return nil + else + raise Puppet::Error, "%s is already being managed" % retobj.ref + end + end + + # create it anew + # if there's a failure, destroy the object if it got that far, but raise + # the error. + begin + obj = new(hash) + rescue => detail + Puppet.err "Could not create %s: %s" % [title, detail.to_s] + if obj + obj.remove(true) + elsif obj = self[title] + obj.remove(true) + end + raise + end + + if implicit + obj.implicit = true + end + + # Store the object by title + self[obj.title] = obj + + return obj + end + + # remove a specified object + def self.delete(resource) + return unless defined? @objects + if @objects.include?(resource.title) + @objects.delete(resource.title) + end + if @aliases.include?(resource.title) + @aliases.delete(resource.title) + end + if @aliases.has_value?(resource) + names = [] + @aliases.each do |name, otherres| + if otherres == resource + names << name + end + end + names.each { |name| @aliases.delete(name) } + end + end + + # iterate across each of the type's instances + def self.each + return unless defined? @objects + @objects.each { |name,instance| + yield instance + } + end + + # does the type have an object with the given name? + def self.has_key?(name) + return @objects.has_key?(name) + end + + # Convert a hash to a TransObject. + def self.hash2trans(hash) + title = nil + if hash.include? :title + title = hash[:title] + hash.delete(:title) + elsif hash.include? self.namevar + title = hash[self.namevar] + hash.delete(self.namevar) + + if hash.include? :name + raise ArgumentError, "Cannot provide both name and %s to %s" % + [self.namevar, self.name] + end + elsif hash[:name] + title = hash[:name] + hash.delete :name + end + + if catalog = hash[:catalog] + hash.delete(:catalog) + end + + raise(Puppet::Error, "You must specify a title for objects of type %s" % self.to_s) unless title + + if hash.include? :type + unless self.validattr? :type + hash.delete :type + end + end + + # okay, now make a transobject out of hash + begin + trans = Puppet::TransObject.new(title, self.name.to_s) + trans.catalog = catalog if catalog + hash.each { |param, value| + trans[param] = value + } + rescue => detail + raise Puppet::Error, "Could not create %s: %s" % + [name, detail] + end + + return trans + end + + # Retrieve all known instances. Either requires providers or must be overridden. + def self.instances + unless defined?(@providers) and ! @providers.empty? + raise Puppet::DevError, "%s has no providers and has not overridden 'instances'" % self.name + end + + # Put the default provider first, then the rest of the suitable providers. + provider_instances = {} + providers_by_source.collect do |provider| + provider.instances.collect do |instance| + # First try to get the resource if it already exists + # Skip instances that map to a managed resource with a different provider + next if resource = self[instance.name] and resource.provider.class != instance.class + + # We always want to use the "first" provider instance we find, unless the resource + # is already managed and has a different provider set + if other = provider_instances[instance.name] + Puppet.warning "%s %s found in both %s and %s; skipping the %s version" % + [self.name.to_s.capitalize, instance.name, other.class.name, instance.class.name, instance.class.name] + next + end + provider_instances[instance.name] = instance + + if resource + resource.provider = instance + resource + else + create(:name => instance.name, :provider => instance, :check => :all) + end + end + end.flatten.compact + end + + # Return a list of one suitable provider per source, with the default provider first. + def self.providers_by_source + # Put the default provider first, then the rest of the suitable providers. + sources = [] + [defaultprovider, suitableprovider].flatten.uniq.collect do |provider| + next if sources.include?(provider.source) + + sources << provider.source + provider + end.compact + end + + # Create the path for logging and such. + def pathbuilder + if p = parent + [p.pathbuilder, self.ref].flatten + else + [self.ref] + end + end + + ############################### + # Add all of the meta parameters. + newmetaparam(:noop) do + desc "Boolean flag indicating whether work should actually + be done." + + newvalues(:true, :false) + munge do |value| + case value + when true, :true, "true": @resource.noop = true + when false, :false, "false": @resource.noop = false + end + end + end + + newmetaparam(:schedule) do + desc "On what schedule the object should be managed. You must create a + schedule object, and then reference the name of that object to use + that for your schedule:: + + schedule { daily: + period => daily, + range => \"2-4\" + } + + exec { \"/usr/bin/apt-get update\": + schedule => daily + } + + The creation of the schedule object does not need to appear in the + configuration before objects that use it." + end + + newmetaparam(:check) do + desc "Propertys which should have their values retrieved + but which should not actually be modified. This is currently used + internally, but will eventually be used for querying, so that you + could specify that you wanted to check the install state of all + packages, and then query the Puppet client daemon to get reports + on all packages." + + munge do |args| + # If they've specified all, collect all known properties + if args == :all + args = @resource.class.properties.find_all do |property| + # Only get properties supported by our provider + if @resource.provider + @resource.provider.class.supports_parameter?(property) + else + true + end + end.collect do |property| + property.name + end + end + + unless args.is_a?(Array) + args = [args] + end + + unless defined? @resource + self.devfail "No parent for %s, %s?" % + [self.class, self.name] + end + + args.each { |property| + unless property.is_a?(Symbol) + property = property.intern + end + next if @resource.propertydefined?(property) + + unless propertyklass = @resource.class.validproperty?(property) + if @resource.class.validattr?(property) + next + else + raise Puppet::Error, "%s is not a valid attribute for %s" % + [property, self.class.name] + end + end + next unless propertyklass.checkable? + @resource.newattr(property) + } + end + end + + # We've got four relationship metaparameters, so this method is used + # to reduce code duplication between them. + def munge_relationship(param, values) + # We need to support values passed in as an array or as a + # resource reference. + result = [] + + # 'values' could be an array or a reference. If it's an array, + # it could be an array of references or an array of arrays. + if values.is_a?(Puppet::Type) + result << [values.class.name, values.title] + else + unless values.is_a?(Array) + devfail "Relationships must be resource references" + end + if values[0].is_a?(String) or values[0].is_a?(Symbol) + # we're a type/title array reference + values[0] = symbolize(values[0]) + result << values + else + # we're an array of stuff + values.each do |value| + if value.is_a?(Puppet::Type) + result << [value.class.name, value.title] + elsif value.is_a?(Array) + value[0] = symbolize(value[0]) + result << value + else + devfail "Invalid relationship %s" % value.inspect + end + end + end + end + + if existing = self[param] + result = existing + result + end + + result + end + + newmetaparam(:loglevel) do + desc "Sets the level that information will be logged. + The log levels have the biggest impact when logs are sent to + syslog (which is currently the default)." + defaultto :notice + + newvalues(*Puppet::Util::Log.levels) + newvalues(:verbose) + + munge do |loglevel| + val = super(loglevel) + if val == :verbose + val = :info + end + val + end + end + + newmetaparam(:alias) do + desc "Creates an alias for the object. Puppet uses this internally when you + provide a symbolic name:: + + file { sshdconfig: + path => $operatingsystem ? { + solaris => \"/usr/local/etc/ssh/sshd_config\", + default => \"/etc/ssh/sshd_config\" + }, + source => \"...\" + } + + service { sshd: + subscribe => file[sshdconfig] + } + + When you use this feature, the parser sets ``sshdconfig`` as the name, + and the library sets that as an alias for the file so the dependency + lookup for ``sshd`` works. You can use this parameter yourself, + but note that only the library can use these aliases; for instance, + the following code will not work:: + + file { \"/etc/ssh/sshd_config\": + owner => root, + group => root, + alias => sshdconfig + } + + file { sshdconfig: + mode => 644 + } + + There's no way here for the Puppet parser to know that these two stanzas + should be affecting the same file. + + See the `LanguageTutorial language tutorial`:trac: for more information. + + " + + munge do |aliases| + unless aliases.is_a?(Array) + aliases = [aliases] + end + + raise(ArgumentError, "Cannot add aliases without a catalog") unless @resource.catalog + + @resource.info "Adding aliases %s" % aliases.collect { |a| a.inspect }.join(", ") + + aliases.each do |other| + if obj = @resource.catalog.resource(@resource.class.name, other) + unless obj.object_id == @resource.object_id + self.fail("%s can not create alias %s: object already exists" % [@resource.title, other]) + end + next + end + + # LAK:FIXME Old-school, add the alias to the class. + @resource.class.alias(other, @resource) + + # Newschool, add it to the catalog. + @resource.catalog.alias(@resource, other) + end + end + end + + newmetaparam(:tag) do + desc "Add the specified tags to the associated resource. While all resources + are automatically tagged with as much information as possible + (e.g., each class and definition containing the resource), it can + be useful to add your own tags to a given resource. + + Tags are currently useful for things like applying a subset of a + host's configuration:: + + puppetd --test --tags mytag + + This way, when you're testing a configuration you can run just the + portion you're testing." + + munge do |tags| + tags = [tags] unless tags.is_a? Array + + tags.each do |tag| + @resource.tag(tag) + end + end + end + + class RelationshipMetaparam < Puppet::Parameter + class << self + attr_accessor :direction, :events, :callback, :subclasses + end + + @subclasses = [] + + def self.inherited(sub) + @subclasses << sub + end + + def munge(rels) + @resource.munge_relationship(self.class.name, rels) + end + + def validate_relationship + @value.each do |value| + unless @resource.catalog.resource(*value) + description = self.class.direction == :in ? "dependency" : "dependent" + fail Puppet::Error, "Could not find %s %s[%s] for %s" % + [description, value[0].to_s.capitalize, value[1], resource.ref] + end + end + end + + # Create edges from each of our relationships. :in + # relationships are specified by the event-receivers, and :out + # relationships are specified by the event generator. This + # way 'source' and 'target' are consistent terms in both edges + # and events -- that is, an event targets edges whose source matches + # the event's source. The direction of the relationship determines + # which resource is applied first and which resource is considered + # to be the event generator. + def to_edges + @value.collect do |value| + # we just have a name and a type, and we need to convert it + # to an object... + tname, name = value + reference = Puppet::ResourceReference.new(tname, name) + + # Either of the two retrieval attempts could have returned + # nil. + unless object = reference.resolve + self.fail "Could not retrieve dependency '%s' of %s" % [reference, @resource.ref] + end + + # Are we requiring them, or vice versa? See the method docs + # for futher info on this. + if self.class.direction == :in + source = object + target = @resource + else + source = @resource + target = object + end + + if method = self.class.callback + subargs = { + :event => self.class.events, + :callback => method + } + self.debug("subscribes to %s" % [object.ref]) + else + # If there's no callback, there's no point in even adding + # a label. + subargs = nil + self.debug("requires %s" % [object.ref]) + end + + rel = Puppet::Relationship.new(source, target, subargs) + end + end + end + + def self.relationship_params + RelationshipMetaparam.subclasses + end + + + # Note that the order in which the relationships params is defined + # matters. The labelled params (notify and subcribe) must be later, + # so that if both params are used, those ones win. It's a hackish + # solution, but it works. + + newmetaparam(:require, :parent => RelationshipMetaparam, :attributes => {:direction => :in, :events => :NONE}) do + desc "One or more objects that this object depends on. + This is used purely for guaranteeing that changes to required objects + happen before the dependent object. For instance:: + + # Create the destination directory before you copy things down + file { \"/usr/local/scripts\": + ensure => directory + } + + file { \"/usr/local/scripts/myscript\": + source => \"puppet://server/module/myscript\", + mode => 755, + require => File[\"/usr/local/scripts\"] + } + + Multiple dependencies can be specified by providing a comma-seperated list + of resources, enclosed in square brackets:: + + require => [ File[\"/usr/local\"], File[\"/usr/local/scripts\"] ] + + Note that Puppet will autorequire everything that it can, and + there are hooks in place so that it's easy for resources to add new + ways to autorequire objects, so if you think Puppet could be + smarter here, let us know. + + In fact, the above code was redundant -- Puppet will autorequire + any parent directories that are being managed; it will + automatically realize that the parent directory should be created + before the script is pulled down. + + Currently, exec resources will autorequire their CWD (if it is + specified) plus any fully qualified paths that appear in the + command. For instance, if you had an ``exec`` command that ran + the ``myscript`` mentioned above, the above code that pulls the + file down would be automatically listed as a requirement to the + ``exec`` code, so that you would always be running againts the + most recent version. + " + end + + newmetaparam(:subscribe, :parent => RelationshipMetaparam, :attributes => {:direction => :in, :events => :ALL_EVENTS, :callback => :refresh}) do + desc "One or more objects that this object depends on. Changes in the + subscribed to objects result in the dependent objects being + refreshed (e.g., a service will get restarted). For instance:: + + class nagios { + file { \"/etc/nagios/nagios.conf\": + source => \"puppet://server/module/nagios.conf\", + alias => nagconf # just to make things easier for me + } + service { nagios: + running => true, + subscribe => File[nagconf] + } + } + + Currently the ``exec``, ``mount`` and ``service`` type support + refreshing. + " + end + + newmetaparam(:before, :parent => RelationshipMetaparam, :attributes => {:direction => :out, :events => :NONE}) do + desc %{This parameter is the opposite of **require** -- it guarantees + that the specified object is applied later than the specifying + object:: + + file { "/var/nagios/configuration": + source => "...", + recurse => true, + before => Exec["nagios-rebuid"] + } + + exec { "nagios-rebuild": + command => "/usr/bin/make", + cwd => "/var/nagios/configuration" + } + + This will make sure all of the files are up to date before the + make command is run.} + end + + newmetaparam(:notify, :parent => RelationshipMetaparam, :attributes => {:direction => :out, :events => :ALL_EVENTS, :callback => :refresh}) do + desc %{This parameter is the opposite of **subscribe** -- it sends events + to the specified object:: + + file { "/etc/sshd_config": + source => "....", + notify => Service[sshd] + } + + service { sshd: + ensure => running + } + + This will restart the sshd service if the sshd config file changes.} + end + + ############################### + # All of the provider plumbing for the resource types. + require 'puppet/provider' + require 'puppet/util/provider_features' + + # Add the feature handling module. + extend Puppet::Util::ProviderFeatures + + attr_reader :provider + + # the Type class attribute accessors + class << self + attr_accessor :providerloader + attr_writer :defaultprovider + end + + # Find the default provider. + def self.defaultprovider + unless defined? @defaultprovider and @defaultprovider + suitable = suitableprovider() + + # Find which providers are a default for this system. + defaults = suitable.find_all { |provider| provider.default? } + + # If we don't have any default we use suitable providers + defaults = suitable if defaults.empty? + max = defaults.collect { |provider| provider.defaultnum }.max + defaults = defaults.find_all { |provider| provider.defaultnum == max } + + retval = nil + if defaults.length > 1 + Puppet.warning( + "Found multiple default providers for %s: %s; using %s" % + [self.name, defaults.collect { |i| i.name.to_s }.join(", "), + defaults[0].name] + ) + retval = defaults.shift + elsif defaults.length == 1 + retval = defaults.shift + else + raise Puppet::DevError, "Could not find a default provider for %s" % + self.name + end + + @defaultprovider = retval + end + + return @defaultprovider + end + + # Convert a hash, as provided by, um, a provider, into an instance of self. + def self.hash2obj(hash) + obj = nil + + namevar = self.namevar + unless hash.include?(namevar) and hash[namevar] + raise Puppet::DevError, "Hash was not passed with namevar" + end + + # if the obj already exists with that name... + if obj = self[hash[namevar]] + # We're assuming here that objects with the same name + # are the same object, which *should* be the case, assuming + # we've set up our naming stuff correctly everywhere. + + # Mark found objects as present + hash.each { |param, value| + if property = obj.property(param) + elsif val = obj[param] + obj[param] = val + else + # There is a value on disk, but it should go away + obj[param] = :absent + end + } + else + # create a new obj, since no existing one seems to + # match + obj = self.create(namevar => hash[namevar]) + + # We can't just pass the hash in at object creation time, + # because it sets the should value, not the is value. + hash.delete(namevar) + hash.each { |param, value| + obj[param] = value unless obj.add_property_parameter(param) + } + end + + return obj + end + + # Retrieve a provider by name. + def self.provider(name) + name = Puppet::Util.symbolize(name) + + # If we don't have it yet, try loading it. + unless @providers.has_key?(name) + @providerloader.load(name) + end + return @providers[name] + end + + # Just list all of the providers. + def self.providers + @providers.keys + end + + def self.validprovider?(name) + name = Puppet::Util.symbolize(name) + + return (@providers.has_key?(name) && @providers[name].suitable?) + end + + # Create a new provider of a type. This method must be called + # directly on the type that it's implementing. + def self.provide(name, options = {}, &block) + name = Puppet::Util.symbolize(name) + + if obj = @providers[name] + Puppet.debug "Reloading %s %s provider" % [name, self.name] + unprovide(name) + end + + parent = if pname = options[:parent] + options.delete(:parent) + if pname.is_a? Class + pname + else + if provider = self.provider(pname) + provider + else + raise Puppet::DevError, + "Could not find parent provider %s of %s" % + [pname, name] + end + end + else + Puppet::Provider + end + + options[:resource_type] ||= self + + self.providify + + provider = genclass(name, + :parent => parent, + :hash => @providers, + :prefix => "Provider", + :block => block, + :include => feature_module, + :extend => feature_module, + :attributes => options + ) + + return provider + end + + # Make sure we have a :provider parameter defined. Only gets called if there + # are providers. + def self.providify + return if @paramhash.has_key? :provider + + newparam(:provider) do + desc "The specific backend for #{self.name.to_s} to use. You will + seldom need to specify this -- Puppet will usually discover the + appropriate provider for your platform." + + # This is so we can refer back to the type to get a list of + # providers for documentation. + class << self + attr_accessor :parenttype + end + + # We need to add documentation for each provider. + def self.doc + @doc + " Available providers are:\n\n" + parenttype().providers.sort { |a,b| + a.to_s <=> b.to_s + }.collect { |i| + "* **%s**: %s" % [i, parenttype().provider(i).doc] + }.join("\n") + end + + defaultto { + @resource.class.defaultprovider.name + } + + validate do |provider_class| + provider_class = provider_class[0] if provider_class.is_a? Array + if provider_class.is_a?(Puppet::Provider) + provider_class = provider_class.class.name + end + + unless provider = @resource.class.provider(provider_class) + raise ArgumentError, "Invalid %s provider '%s'" % [@resource.class.name, provider_class] + end + end + + munge do |provider| + provider = provider[0] if provider.is_a? Array + if provider.is_a? String + provider = provider.intern + end + @resource.provider = provider + + if provider.is_a?(Puppet::Provider) + provider.class.name + else + provider + end + end + end.parenttype = self + end + + def self.unprovide(name) + if @providers.has_key? name + rmclass(name, + :hash => @providers, + :prefix => "Provider" + ) + if @defaultprovider and @defaultprovider.name == name + @defaultprovider = nil + end + end + end + + # Return an array of all of the suitable providers. + def self.suitableprovider + if @providers.empty? + providerloader.loadall + end + @providers.find_all { |name, provider| + provider.suitable? + }.collect { |name, provider| + provider + }.reject { |p| p.name == :fake } # For testing + end + + def provider=(name) + if name.is_a?(Puppet::Provider) + @provider = name + @provider.resource = self + elsif klass = self.class.provider(name) + @provider = klass.new(self) + else + raise ArgumentError, "Could not find %s provider of %s" % + [name, self.class.name] + end + end + + ############################### + # All of the relationship code. + + # Specify a block for generating a list of objects to autorequire. This + # makes it so that you don't have to manually specify things that you clearly + # require. + def self.autorequire(name, &block) + @autorequires ||= {} + @autorequires[name] = block + end + + # Yield each of those autorequires in turn, yo. + def self.eachautorequire + @autorequires ||= {} + @autorequires.each { |type, block| + yield(type, block) + } + end + + # Figure out of there are any objects we can automatically add as + # dependencies. + def autorequire + reqs = [] + self.class.eachautorequire { |type, block| + # Ignore any types we can't find, although that would be a bit odd. + next unless typeobj = Puppet.type(type) + + # Retrieve the list of names from the block. + next unless list = self.instance_eval(&block) + unless list.is_a?(Array) + list = [list] + end + + # Collect the current prereqs + list.each { |dep| + obj = nil + # Support them passing objects directly, to save some effort. + unless dep.is_a? Puppet::Type + # Skip autorequires that we aren't managing + unless dep = typeobj[dep] + next + end + end + + reqs << Puppet::Relationship.new(dep, self) + } + } + + return reqs + end + + # Build the dependencies associated with an individual object. + def builddepends + # Handle the requires + self.class.relationship_params.collect do |klass| + if param = @parameters[klass.name] + param.to_edges + end + end.flatten.reject { |r| r.nil? } + end + + # Does this resource have a relationship with the other? We have to + # check each object for both directions of relationship. + def requires?(other) + them = [other.class.name, other.title] + me = [self.class.name, self.title] + self.class.relationship_params.each do |param| + case param.direction + when :in: return true if v = self[param.name] and v.include?(them) + when :out: return true if v = other[param.name] and v.include?(me) + end + end + return false + end + + # we've received an event + # we only support local events right now, so we can pass actual + # objects around, including the transaction object + # the assumption here is that container objects will pass received + # methods on to contained objects + # i.e., we don't trigger our children, our refresh() method calls + # refresh() on our children + def trigger(event, source) + trans = event.transaction + if @callbacks.include?(source) + [:ALL_EVENTS, event.event].each { |eventname| + if method = @callbacks[source][eventname] + if trans.triggered?(self, method) > 0 + next + end + if self.respond_to?(method) + self.send(method) + end + + trans.triggered(self, method) + end + } + end + end + + # Unsubscribe from a given object, possibly with a specific event. + def unsubscribe(object, event = nil) + # First look through our own relationship params + [:require, :subscribe].each do |param| + if values = self[param] + newvals = values.reject { |d| + d == [object.class.name, object.title] + } + if newvals.length != values.length + self.delete(param) + self[param] = newvals + end + end + end + end + + ############################### + # All of the scheduling code. + + # Look up the schedule and set it appropriately. This is done after + # the instantiation phase, so that the schedule can be anywhere in the + # file. + def schedule + unless defined? @schedule + if name = self[:schedule] + if sched = Puppet.type(:schedule)[name] + @schedule = sched + else + self.fail "Could not find schedule %s" % name + end + else + @schedule = nil + end + end + @schedule + end + + # Check whether we are scheduled to run right now or not. + def scheduled? + return true if Puppet[:ignoreschedules] + return true unless schedule = self.schedule + + # We use 'checked' here instead of 'synced' because otherwise we'll + # end up checking most resources most times, because they will generally + # have been synced a long time ago (e.g., a file only gets updated + # once a month on the server and its schedule is daily; the last sync time + # will have been a month ago, so we'd end up checking every run). + return schedule.match?(self.cached(:checked).to_i) + end + + ############################### + # All of the tagging code. + attr_reader :tags + + # Add a new tag. + def tag(tag) + tag = tag.intern if tag.is_a? String + unless @tags.include? tag + @tags << tag + end + end + + # Define the initial list of tags. + def tags=(list) + list = [list] unless list.is_a? Array + + @tags = list.collect do |t| + case t + when String: t.intern + when Symbol: t + else + self.warning "Ignoring tag %s of type %s" % [tag.inspect, tag.class] + end + end + + @tags << self.class.name unless @tags.include?(self.class.name) + end + + # Figure out of any of the specified tags apply to this object. This is an + # OR operation. + def tagged?(tags) + tags = [tags] unless tags.is_a? Array + + tags = tags.collect { |t| t.intern } + + return tags.find { |tag| @tags.include? tag } + end # Types (which map to resources in the languages) are entirely composed of # attribute value pairs. Generally, Puppet calls any of these things an diff --git a/spec/unit/type/noop_metaparam.rb b/spec/unit/type/noop_metaparam.rb index 2a3e0160d..8510a1b6b 100755 --- a/spec/unit/type/noop_metaparam.rb +++ b/spec/unit/type/noop_metaparam.rb @@ -2,7 +2,7 @@ require File.dirname(__FILE__) + '/../../spec_helper' -require 'puppet/metatype/metaparams' +require 'puppet/type' describe Puppet::Type.type(:file).attrclass(:noop) do before do -- cgit From a1a670b305252b2f4b4b2b0020303143addc3eb8 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Tue, 16 Sep 2008 11:41:48 -0500 Subject: Fixed #1572 -- file purging now fails if remote sources do not exist. Signed-off-by: Luke Kanies --- CHANGELOG | 2 ++ lib/puppet/type/file.rb | 9 +++++++++ spec/unit/type/file.rb | 6 ++++++ 3 files changed, 17 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 441e68ec1..303b1155d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,6 @@ 0.24.x + Fixed #1572 -- file purging now fails if remote sources do not exist. + Fixed issues with file descriptors leaking into subprocesses Fixed #1568 - createpackage.sh diff --git a/lib/puppet/type/file.rb b/lib/puppet/type/file.rb index 3518e8bb3..875f8c370 100644 --- a/lib/puppet/type/file.rb +++ b/lib/puppet/type/file.rb @@ -849,6 +849,8 @@ module Puppet # Keep track of all the files we found in the source, so we can purge # appropriately. sourced = [] + + success = false @parameters[:source].should.each do |source| sourceobj, path = uri2obj(source) @@ -863,6 +865,8 @@ module Puppet if desc == "" next end + + success = true # Now create a new child for every file returned in the list. result += desc.split("\n").collect { |line| @@ -898,6 +902,11 @@ module Puppet return [result, sourced] end end + + unless success + raise Puppet::Error, "None of the provided sources exist" + end + return [result, sourced] end diff --git a/spec/unit/type/file.rb b/spec/unit/type/file.rb index 3ea4c3731..fd790d678 100755 --- a/spec/unit/type/file.rb +++ b/spec/unit/type/file.rb @@ -69,6 +69,12 @@ describe Puppet::Type.type(:file) do @filesource.server.stubs(:describe).raises(Puppet::Network::XMLRPCClientError.new("Testing")) lambda { @file.retrieve }.should raise_error(Puppet::Error) end + + it "should fail during eval_generate if no remote sources exist" do + file = Puppet::Type.type(:file).create :path => "/foobar", :source => "/this/file/does/not/exist", :recurse => true + + lambda { file.eval_generate }.should raise_error(Puppet::Error) + end end describe "when managing links" do -- cgit From 77f4fb67fc9efac8ab70c1fc83e86123401b8000 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Tue, 16 Sep 2008 11:24:57 -0500 Subject: Fixed #1521 -- ldap user and group are now used with the default connection when available. Signed-off-by: Luke Kanies --- CHANGELOG | 2 ++ lib/puppet/util/ldap/connection.rb | 12 +++++++++++- spec/unit/util/ldap/connection.rb | 13 +++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 303b1155d..404f7fb95 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,7 @@ 0.24.x Fixed #1572 -- file purging now fails if remote sources do not exist. + + Fixed #1521 -- ldap user and password are now used with the default connection. Fixed issues with file descriptors leaking into subprocesses diff --git a/lib/puppet/util/ldap/connection.rb b/lib/puppet/util/ldap/connection.rb index f6530f853..70fe303c5 100644 --- a/lib/puppet/util/ldap/connection.rb +++ b/lib/puppet/util/ldap/connection.rb @@ -17,7 +17,17 @@ class Puppet::Util::Ldap::Connection else false end - new(Puppet[:ldapserver], Puppet[:ldapport], :ssl => ssl) + + options = {} + options[:ssl] = ssl + if user = Puppet.settings[:ldapuser] and user != "" + options[:user] = user + if pass = Puppet.settings[:ldappassword] and pass != "" + options[:password] = pass + end + end + + new(Puppet[:ldapserver], Puppet[:ldapport], options) end def close diff --git a/spec/unit/util/ldap/connection.rb b/spec/unit/util/ldap/connection.rb index 9392466c7..8bc85a620 100755 --- a/spec/unit/util/ldap/connection.rb +++ b/spec/unit/util/ldap/connection.rb @@ -152,5 +152,18 @@ describe Puppet::Util::Ldap::Connection do Puppet::Util::Ldap::Connection.expects(:new).with { |host, port, options| options[:ssl] == false } Puppet::Util::Ldap::Connection.instance end + + it "should set the ldapuser if one is set" do + Puppet.settings.expects(:value).with(:ldapuser).returns "foo" + Puppet::Util::Ldap::Connection.expects(:new).with { |host, port, options| options[:user] == "foo" } + Puppet::Util::Ldap::Connection.instance + end + + it "should set the ldapuser and ldappassword if both is set" do + Puppet.settings.expects(:value).with(:ldapuser).returns "foo" + Puppet.settings.expects(:value).with(:ldappassword).returns "bar" + Puppet::Util::Ldap::Connection.expects(:new).with { |host, port, options| options[:user] == "foo" and options[:password] == "bar" } + Puppet::Util::Ldap::Connection.instance + end end end -- cgit From 3b1d6e25b4c1178554fb6df128e1cdf6dd9ef3b6 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Fri, 19 Sep 2008 09:49:18 +1000 Subject: Fixed #1579 and #1580 - errors in the Puppet RPM spec file --- CHANGELOG | 2 ++ conf/redhat/puppet.spec | 12 +++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 404f7fb95..b2858a939 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,6 @@ 0.24.x + Fixed #1579 and #1580 - errors in the Puppet RPM spec file + Fixed #1572 -- file purging now fails if remote sources do not exist. Fixed #1521 -- ldap user and password are now used with the default connection. diff --git a/conf/redhat/puppet.spec b/conf/redhat/puppet.spec index 92a42ce7b..b5090ed38 100644 --- a/conf/redhat/puppet.spec +++ b/conf/redhat/puppet.spec @@ -2,7 +2,7 @@ %define pbuild %{_builddir}/%{name}-%{version} %define confdir conf/redhat -%define has_ruby_abi 0%{?fedora:%fedora} >= 5 || 0%{?rhel:%rhel} >= 5 +%define has_ruby_abi 0%{?fedora:%fedora} >= 5 || 0%{?rhel:%rhel} >= 5 || 0%{?centos:%centos} >= 5 %define has_ruby_noarch %has_ruby_abi Summary: A network tool for managing many disparate systems @@ -51,8 +51,8 @@ done # Fix some rpmlint complaints for f in mac_dscl.pp mac_dscl_revert.pp \ mac_netinfo.pp mac_pkgdmg.pp ; do - sed -i -e'1d' examples/code/$f - chmod a-x examples/code/$f + sed -i -e'1d' examples/$f + chmod a-x examples/$f done find examples/ -type f -empty | xargs rm @@ -65,6 +65,7 @@ install -d -m0755 %{buildroot}%{_bindir} install -d -m0755 %{buildroot}%{ruby_sitelibdir} install -d -m0755 %{buildroot}%{_sysconfdir}/puppet/manifests install -d -m0755 %{buildroot}%{_docdir}/%{name}-%{version} +install -d -m0755 %{buildroot}%{_mandir}/man8 install -d -m0755 %{buildroot}%{_localstatedir}/lib/puppet install -d -m0755 %{buildroot}%{_localstatedir}/run/puppet install -d -m0755 %{buildroot}%{_localstatedir}/log/puppet @@ -84,6 +85,7 @@ install -Dp -m0755 %{confdir}/server.init %{buildroot}%{_initrddir}/puppetmaster install -Dp -m0644 %{confdir}/fileserver.conf %{buildroot}%{_sysconfdir}/puppet/fileserver.conf install -Dp -m0644 %{confdir}/puppet.conf %{buildroot}%{_sysconfdir}/puppet/puppet.conf install -Dp -m0644 %{confdir}/logrotate %{buildroot}%{_sysconfdir}/logrotate.d/puppet +install -Dp -m0644 man/man8/* %{buildroot}%{_mandir}/man8 # We need something for these ghosted files, otherwise rpmbuild # will complain loudly. They won't be included in the binary packages touch %{buildroot}%{_sysconfdir}/puppet/puppetmasterd.conf @@ -128,6 +130,10 @@ touch %{buildroot}%{_sysconfdir}/puppet/puppetd.conf %ghost %config(noreplace,missingok) %{_sysconfdir}/puppet/puppetca.conf %ghost %config(noreplace,missingok) %{_sysconfdir}/puppet/puppetmasterd.conf %{_sbindir}/puppetca +%doc %{_mandir}/man8/filebucket.8.gz +%doc %{_mandir}/man8/puppetca.8.gz +%doc %{_mandir}/man8/puppetmasterd.8.gz +%doc %{_mandir}/man8/puppetrun.8.gz %pre /usr/sbin/groupadd -r puppet 2>/dev/null || : -- cgit From 11b0848b8c6eaaded608f4a485990ddb5bbd5e80 Mon Sep 17 00:00:00 2001 From: Andrew Shafer Date: Thu, 18 Sep 2008 18:48:12 -0600 Subject: Fixed #1500 - puppetrun host regression --- CHANGELOG | 2 ++ bin/puppetrun | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index b2858a939..61e46915a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,6 @@ 0.24.x + Fixed #1500 - puppetrun not working + Fixed #1579 and #1580 - errors in the Puppet RPM spec file Fixed #1572 -- file purging now fails if remote sources do not exist. diff --git a/bin/puppetrun b/bin/puppetrun index f33c7f8a1..28f72d9c3 100755 --- a/bin/puppetrun +++ b/bin/puppetrun @@ -245,7 +245,7 @@ end # Now parse the config Puppet.parse_config -if Puppet[:node_terminus] = "ldap" +if Puppet[:node_terminus] == "ldap" and (options[:all] or classes) if options[:all] hosts = Puppet::Node.search("whatever").collect { |node| node.name } puts "all: %s" % hosts.join(", ") -- cgit From 09057346dd7207e36c81bae479d1343fb7cc8d0a Mon Sep 17 00:00:00 2001 From: Thom May Date: Tue, 16 Sep 2008 10:24:18 +0100 Subject: Allow a templatedir to be colon separated. Signed-off-by: Thom May Signed-off-by: Paul Nasrat --- lib/puppet/module.rb | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/puppet/module.rb b/lib/puppet/module.rb index 544d94ea9..4bdfab092 100644 --- a/lib/puppet/module.rb +++ b/lib/puppet/module.rb @@ -19,6 +19,15 @@ class Puppet::Module end end + # Return an array of paths by splitting the +templatedir+ config + # parameter. + def self.templatepath(environment = nil) + dirs = Puppet.settings.value(:templatedir, environment).split(":") + dirs.select do |p| + p =~ /^#{File::SEPARATOR}/ && File::directory?(p) + end + end + # Find and return the +module+ that +path+ belongs to. If +path+ is # absolute, or if there is no module whose name is the first component # of +path+, return +nil+ @@ -64,9 +73,12 @@ class Puppet::Module end # If we can find the template in :templatedir, we return that. - td_file = File.join(Puppet.settings.value(:templatedir, environment), template) - return td_file if File.exists?(td_file) - + td_file = templatepath(environment).collect { |path| + File::join(path, template) + }.find { |f| File.exists?(f) } + + return td_file unless td_file == nil + path, file = split_path(template) # Because templates don't have an assumed template name, like manifests do, -- cgit From 7f8abbd388ee3898c8505ca3bb884b75d8db2087 Mon Sep 17 00:00:00 2001 From: Paul Nasrat Date: Tue, 16 Sep 2008 10:24:19 +0100 Subject: Bug #1550 - Rework to avoid regressing rspec tests, add new rspec tests for templatedir as a path Signed-off-by: Paul Nasrat --- CHANGELOG | 2 ++ lib/puppet/defaults.rb | 3 ++- lib/puppet/module.rb | 24 +++++++++++++++--------- spec/unit/module.rb | 27 ++++++++++++++++++++++++--- 4 files changed, 43 insertions(+), 13 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 61e46915a..74c8a93d7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,6 @@ 0.24.x + Fixed #1554 - Added support for multiple template directories + Fixed #1500 - puppetrun not working Fixed #1579 and #1580 - errors in the Puppet RPM spec file diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index 87ccd62f6..a2900fd43 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -664,7 +664,8 @@ module Puppet setdefaults(:parser, :lexical => [false, "Whether to use lexical scoping (vs. dynamic)."], :templatedir => ["$vardir/templates", - "Where Puppet looks for template files." + "Where Puppet looks for template files. Can be a list of colon-seperated + directories." ] ) diff --git a/lib/puppet/module.rb b/lib/puppet/module.rb index 4bdfab092..b34f2f8b0 100644 --- a/lib/puppet/module.rb +++ b/lib/puppet/module.rb @@ -72,28 +72,33 @@ class Puppet::Module return template end + template_paths = templatepath(environment) + default_template_path = File::join(template_paths.first, template) + # If we can find the template in :templatedir, we return that. - td_file = templatepath(environment).collect { |path| + td_file = template_paths.collect { |path| File::join(path, template) }.find { |f| File.exists?(f) } return td_file unless td_file == nil + td_file = find_template_for_module(template, environment) + td_file ||= default_template_path + end + + def self.find_template_for_module(template, environment = nil) path, file = split_path(template) # Because templates don't have an assumed template name, like manifests do, # we treat templates with no name as being templates in the main template # directory. - if file.nil? - mod = nil - else + if not file.nil? mod = find(path, environment) + if mod + return mod.template(file) + end end - if mod - return mod.template(file) - else - return td_file # Return this anyway, since we're going to fail. - end + nil end # Return a list of manifests (as absolute filenames) that match +pat+ @@ -156,4 +161,5 @@ class Puppet::Module end private :initialize + private_class_method :find_template_for_module end diff --git a/spec/unit/module.rb b/spec/unit/module.rb index e79001ae1..4d66550b5 100755 --- a/spec/unit/module.rb +++ b/spec/unit/module.rb @@ -90,23 +90,44 @@ describe Puppet::Module, " when searching for templates" do end it "should use the main templatedir if no module is found" do - Puppet.settings.expects(:value).with(:templatedir, nil).returns("/my/templates") + Puppet::Module.stubs(:templatepath).with(nil).returns(["/my/templates"]) Puppet::Module.expects(:find).with("mymod", nil).returns(nil) Puppet::Module.find_template("mymod/mytemplate").should == "/my/templates/mymod/mytemplate" end it "should return unqualified templates directly in the template dir" do - Puppet.settings.expects(:value).with(:templatedir, nil).returns("/my/templates") + Puppet::Module.stubs(:templatepath).with(nil).returns(["/my/templates"]) Puppet::Module.expects(:find).never Puppet::Module.find_template("mytemplate").should == "/my/templates/mytemplate" end it "should use the environment templatedir if no module is found and an environment is specified" do - Puppet.settings.expects(:value).with(:templatedir, "myenv").returns("/myenv/templates") + Puppet::Module.stubs(:templatepath).with("myenv").returns(["/myenv/templates"]) + Puppet::Module.expects(:find).with("mymod", "myenv").returns(nil) + Puppet::Module.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::Module.stubs(:templatepath).with("myenv").returns(["/myenv/templates", "/two/templates"]) Puppet::Module.expects(:find).with("mymod", "myenv").returns(nil) Puppet::Module.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::Module.stubs(:templatepath).returns(["/one/templates", "/two/templates"]) + File.expects(:exists?).with("/one/templates/mytemplate").returns(true) + Puppet::Module.expects(:find).never + Puppet::Module.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::Module.stubs(:templatepath).returns(["/one/templates", "/two/templates"]) + File.expects(:exists?).with("/one/templates/mytemplate").returns(false) + File.expects(:exists?).with("/two/templates/mytemplate").returns(true) + Puppet::Module.expects(:find).never + Puppet::Module.find_template("mytemplate").should == "/two/templates/mytemplate" + end + it "should use the node environment if specified" do Puppet.settings.stubs(:value).returns.returns("/my/directory") Puppet.settings.expects(:value).with(:modulepath, "myenv").returns("/my/modules") -- cgit From 16793d221f95b2430260c38cd7c36bb8a5ef8d49 Mon Sep 17 00:00:00 2001 From: Brice Figureau Date: Sun, 14 Sep 2008 15:49:34 +0200 Subject: Add an append (+=) variable operator: The append variable operator can be used to append something to a variable defined in a parent scope, containing either a string or an array. The main use is to append array elements in classes to a variable globally defined in a node. Example: $ssh_users = ['brice', 'admin1'] class backup { $ssh_users += ['backup_operator'] ... } Signed-off-by: Brice Figureau --- lib/puppet/parser/ast/vardef.rb | 4 +- lib/puppet/parser/grammar.ra | 8 +- lib/puppet/parser/lexer.rb | 1 + lib/puppet/parser/parser.rb | 1437 ++++++++++++++++++++------------------- lib/puppet/parser/scope.rb | 26 +- 5 files changed, 760 insertions(+), 716 deletions(-) diff --git a/lib/puppet/parser/ast/vardef.rb b/lib/puppet/parser/ast/vardef.rb index ee79159d7..a3094ac6e 100644 --- a/lib/puppet/parser/ast/vardef.rb +++ b/lib/puppet/parser/ast/vardef.rb @@ -3,7 +3,7 @@ require 'puppet/parser/ast/branch' class Puppet::Parser::AST # Define a variable. Stores the value in the current scope. class VarDef < AST::Branch - attr_accessor :name, :value + attr_accessor :name, :value, :append @settor = true @@ -14,7 +14,7 @@ class Puppet::Parser::AST value = @value.safeevaluate(scope) parsewrap do - scope.setvar(name,value, @file, @line) + scope.setvar(name,value, @file, @line, @append) end end diff --git a/lib/puppet/parser/grammar.ra b/lib/puppet/parser/grammar.ra index a2f6729ee..07666acb4 100644 --- a/lib/puppet/parser/grammar.ra +++ b/lib/puppet/parser/grammar.ra @@ -5,7 +5,7 @@ class Puppet::Parser::Parser token LBRACK DQTEXT SQTEXT RBRACK LBRACE RBRACE SYMBOL FARROW COMMA TRUE -token FALSE EQUALS LESSEQUAL NOTEQUAL DOT COLON LLCOLLECT RRCOLLECT +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 CLASSNAME CLASSREF @@ -58,6 +58,7 @@ statement: resource | hostclass | nodedef | resourceoverride + | append fstatement: NAME LPAREN funcvalues RPAREN { args = aryfy(val[2]) @@ -291,6 +292,11 @@ assignment: VARIABLE EQUALS rvalue { result = ast AST::VarDef, :name => variable, :value => val[2] } +append: VARIABLE APPENDS rvalue { + variable = ast AST::Name, :value => val[0] + result = ast AST::VarDef, :name => variable, :value => val[2], :append => true +} + params: # nothing { result = ast AST::ASTArray diff --git a/lib/puppet/parser/lexer.rb b/lib/puppet/parser/lexer.rb index 2c5f66e5a..71210d919 100644 --- a/lib/puppet/parser/lexer.rb +++ b/lib/puppet/parser/lexer.rb @@ -105,6 +105,7 @@ class Puppet::Parser::Lexer '(' => :LPAREN, ')' => :RPAREN, '=' => :EQUALS, + '+=' => :APPENDS, '==' => :ISEQUAL, '>=' => :GREATEREQUAL, '>' => :GREATERTHAN, diff --git a/lib/puppet/parser/parser.rb b/lib/puppet/parser/parser.rb index 3d7f21dca..69225cc2b 100644 --- a/lib/puppet/parser/parser.rb +++ b/lib/puppet/parser/parser.rb @@ -29,7 +29,7 @@ module Puppet class Parser < Racc::Parser -module_eval <<'..end grammar.ra modeval..id9145566289', 'grammar.ra', 638 +module_eval <<'..end grammar.ra modeval..id7dbe8301d1', 'grammar.ra', 644 # It got too annoying having code in a file that needs to be compiled. require 'puppet/parser/parser_support' @@ -41,517 +41,525 @@ require 'puppet/parser/parser_support' # $Id$ -..end grammar.ra modeval..id9145566289 +..end grammar.ra modeval..id7dbe8301d1 ##### racc 1.4.5 generates ### racc_reduce_table = [ 0, 0, :racc_error, - 1, 52, :_reduce_1, - 1, 52, :_reduce_none, + 1, 53, :_reduce_1, 1, 53, :_reduce_none, - 2, 53, :_reduce_4, - 1, 55, :_reduce_none, - 1, 55, :_reduce_none, - 1, 55, :_reduce_none, - 1, 55, :_reduce_none, - 1, 55, :_reduce_none, - 1, 55, :_reduce_none, - 1, 55, :_reduce_none, - 1, 55, :_reduce_none, - 1, 55, :_reduce_none, - 1, 55, :_reduce_none, - 1, 55, :_reduce_none, - 1, 55, :_reduce_none, - 4, 63, :_reduce_17, - 3, 63, :_reduce_18, - 2, 63, :_reduce_19, - 1, 68, :_reduce_none, - 1, 68, :_reduce_none, - 1, 69, :_reduce_none, - 3, 69, :_reduce_23, - 1, 71, :_reduce_none, - 1, 71, :_reduce_none, - 1, 71, :_reduce_none, - 1, 71, :_reduce_none, - 1, 71, :_reduce_none, - 1, 71, :_reduce_none, - 1, 71, :_reduce_none, - 1, 71, :_reduce_31, + 1, 54, :_reduce_none, + 2, 54, :_reduce_4, + 1, 56, :_reduce_none, + 1, 56, :_reduce_none, + 1, 56, :_reduce_none, + 1, 56, :_reduce_none, + 1, 56, :_reduce_none, + 1, 56, :_reduce_none, + 1, 56, :_reduce_none, + 1, 56, :_reduce_none, + 1, 56, :_reduce_none, + 1, 56, :_reduce_none, + 1, 56, :_reduce_none, + 1, 56, :_reduce_none, + 1, 56, :_reduce_none, + 4, 64, :_reduce_18, + 3, 64, :_reduce_19, + 2, 64, :_reduce_20, 1, 70, :_reduce_none, - 3, 70, :_reduce_33, - 5, 56, :_reduce_34, - 5, 56, :_reduce_35, - 5, 56, :_reduce_36, - 5, 67, :_reduce_37, - 2, 57, :_reduce_38, - 1, 87, :_reduce_39, - 2, 87, :_reduce_40, - 2, 58, :_reduce_41, - 3, 88, :_reduce_42, - 3, 88, :_reduce_43, - 1, 89, :_reduce_none, - 1, 89, :_reduce_none, - 3, 89, :_reduce_46, - 1, 90, :_reduce_none, - 3, 90, :_reduce_48, + 1, 70, :_reduce_none, + 1, 71, :_reduce_none, + 3, 71, :_reduce_24, + 1, 73, :_reduce_none, + 1, 73, :_reduce_none, + 1, 73, :_reduce_none, + 1, 73, :_reduce_none, + 1, 73, :_reduce_none, + 1, 73, :_reduce_none, + 1, 73, :_reduce_none, + 1, 73, :_reduce_32, + 1, 72, :_reduce_none, + 3, 72, :_reduce_34, + 5, 57, :_reduce_35, + 5, 57, :_reduce_36, + 5, 57, :_reduce_37, + 5, 68, :_reduce_38, + 2, 58, :_reduce_39, + 1, 89, :_reduce_40, + 2, 89, :_reduce_41, + 2, 59, :_reduce_42, + 3, 90, :_reduce_43, + 3, 90, :_reduce_44, 1, 91, :_reduce_none, 1, 91, :_reduce_none, - 3, 92, :_reduce_51, - 3, 92, :_reduce_52, + 3, 91, :_reduce_47, + 1, 92, :_reduce_none, + 3, 92, :_reduce_49, 1, 93, :_reduce_none, 1, 93, :_reduce_none, - 4, 95, :_reduce_55, - 1, 81, :_reduce_none, - 3, 81, :_reduce_57, - 0, 82, :_reduce_none, - 1, 82, :_reduce_none, - 1, 97, :_reduce_60, - 1, 72, :_reduce_61, + 3, 94, :_reduce_52, + 3, 94, :_reduce_53, + 1, 95, :_reduce_none, + 1, 95, :_reduce_none, + 4, 97, :_reduce_56, + 1, 83, :_reduce_none, + 3, 83, :_reduce_58, + 0, 84, :_reduce_none, + 1, 84, :_reduce_none, + 1, 99, :_reduce_61, 1, 74, :_reduce_62, + 1, 76, :_reduce_63, + 1, 98, :_reduce_none, + 1, 98, :_reduce_none, + 1, 98, :_reduce_none, + 1, 98, :_reduce_none, + 1, 98, :_reduce_none, + 1, 98, :_reduce_none, + 3, 60, :_reduce_70, + 3, 69, :_reduce_71, + 0, 85, :_reduce_72, + 1, 85, :_reduce_73, + 3, 85, :_reduce_74, + 3, 102, :_reduce_75, + 3, 103, :_reduce_76, + 1, 104, :_reduce_none, + 1, 104, :_reduce_none, + 0, 88, :_reduce_79, + 1, 88, :_reduce_80, + 3, 88, :_reduce_81, + 1, 105, :_reduce_none, + 3, 105, :_reduce_83, 1, 96, :_reduce_none, 1, 96, :_reduce_none, 1, 96, :_reduce_none, 1, 96, :_reduce_none, 1, 96, :_reduce_none, 1, 96, :_reduce_none, - 3, 59, :_reduce_69, - 0, 83, :_reduce_70, - 1, 83, :_reduce_71, - 3, 83, :_reduce_72, - 3, 100, :_reduce_73, - 3, 101, :_reduce_74, - 1, 102, :_reduce_none, - 1, 102, :_reduce_none, - 0, 86, :_reduce_77, - 1, 86, :_reduce_78, - 3, 86, :_reduce_79, - 1, 103, :_reduce_none, - 3, 103, :_reduce_81, - 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, 99, :_reduce_none, - 1, 99, :_reduce_none, - 1, 99, :_reduce_none, - 1, 99, :_reduce_none, - 1, 99, :_reduce_none, - 1, 99, :_reduce_none, - 1, 99, :_reduce_none, - 1, 99, :_reduce_none, - 1, 99, :_reduce_none, - 1, 99, :_reduce_none, - 4, 76, :_reduce_98, - 3, 76, :_reduce_99, - 1, 78, :_reduce_100, - 1, 78, :_reduce_101, - 1, 75, :_reduce_102, - 4, 79, :_reduce_103, - 4, 79, :_reduce_104, - 6, 61, :_reduce_105, - 0, 106, :_reduce_none, - 4, 106, :_reduce_107, - 1, 105, :_reduce_none, - 5, 60, :_reduce_109, + 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, + 4, 78, :_reduce_100, + 3, 78, :_reduce_101, + 1, 80, :_reduce_102, + 1, 80, :_reduce_103, + 1, 77, :_reduce_104, + 4, 81, :_reduce_105, + 4, 81, :_reduce_106, + 6, 62, :_reduce_107, + 0, 108, :_reduce_none, + 4, 108, :_reduce_109, 1, 107, :_reduce_none, - 2, 107, :_reduce_111, - 5, 108, :_reduce_112, - 4, 108, :_reduce_113, + 5, 61, :_reduce_111, 1, 109, :_reduce_none, - 3, 109, :_reduce_115, - 3, 77, :_reduce_116, + 2, 109, :_reduce_113, + 5, 110, :_reduce_114, + 4, 110, :_reduce_115, 1, 111, :_reduce_none, - 4, 111, :_reduce_118, + 3, 111, :_reduce_117, + 3, 79, :_reduce_118, 1, 113, :_reduce_none, - 3, 113, :_reduce_120, - 3, 112, :_reduce_121, - 1, 110, :_reduce_none, - 1, 110, :_reduce_none, - 1, 110, :_reduce_none, - 1, 110, :_reduce_none, - 1, 110, :_reduce_none, - 1, 110, :_reduce_none, - 1, 110, :_reduce_none, - 1, 110, :_reduce_129, - 1, 114, :_reduce_130, - 3, 114, :_reduce_131, - 2, 62, :_reduce_132, - 6, 64, :_reduce_133, - 5, 64, :_reduce_134, + 4, 113, :_reduce_120, + 1, 115, :_reduce_none, + 3, 115, :_reduce_122, + 3, 114, :_reduce_123, + 1, 112, :_reduce_none, + 1, 112, :_reduce_none, + 1, 112, :_reduce_none, + 1, 112, :_reduce_none, + 1, 112, :_reduce_none, + 1, 112, :_reduce_none, + 1, 112, :_reduce_none, + 1, 112, :_reduce_131, + 1, 116, :_reduce_132, + 3, 116, :_reduce_133, + 2, 63, :_reduce_134, 6, 65, :_reduce_135, 5, 65, :_reduce_136, 6, 66, :_reduce_137, 5, 66, :_reduce_138, - 1, 85, :_reduce_none, - 1, 80, :_reduce_none, - 1, 80, :_reduce_none, - 1, 117, :_reduce_none, - 3, 117, :_reduce_143, - 1, 119, :_reduce_none, - 1, 119, :_reduce_none, - 1, 119, :_reduce_none, + 6, 67, :_reduce_139, + 5, 67, :_reduce_140, + 1, 87, :_reduce_none, + 1, 82, :_reduce_none, + 1, 82, :_reduce_none, 1, 119, :_reduce_none, - 0, 54, :_reduce_148, - 0, 120, :_reduce_149, - 1, 115, :_reduce_none, - 3, 115, :_reduce_151, - 3, 115, :_reduce_152, + 3, 119, :_reduce_145, 1, 121, :_reduce_none, - 3, 121, :_reduce_154, - 3, 122, :_reduce_155, - 1, 122, :_reduce_156, - 3, 122, :_reduce_157, - 1, 122, :_reduce_158, - 1, 118, :_reduce_none, - 2, 118, :_reduce_160, - 1, 116, :_reduce_none, - 2, 116, :_reduce_162, - 1, 123, :_reduce_none, + 1, 121, :_reduce_none, + 1, 121, :_reduce_none, + 1, 121, :_reduce_none, + 0, 55, :_reduce_150, + 0, 122, :_reduce_151, + 1, 117, :_reduce_none, + 3, 117, :_reduce_153, + 3, 117, :_reduce_154, 1, 123, :_reduce_none, - 1, 73, :_reduce_165, - 3, 98, :_reduce_166, - 2, 98, :_reduce_167, - 1, 104, :_reduce_none, - 1, 104, :_reduce_none, - 0, 84, :_reduce_none, - 1, 84, :_reduce_171 ] + 3, 123, :_reduce_156, + 3, 124, :_reduce_157, + 1, 124, :_reduce_158, + 3, 124, :_reduce_159, + 1, 124, :_reduce_160, + 1, 120, :_reduce_none, + 2, 120, :_reduce_162, + 1, 118, :_reduce_none, + 2, 118, :_reduce_164, + 1, 125, :_reduce_none, + 1, 125, :_reduce_none, + 1, 75, :_reduce_167, + 3, 100, :_reduce_168, + 2, 100, :_reduce_169, + 1, 106, :_reduce_none, + 1, 106, :_reduce_none, + 0, 86, :_reduce_none, + 1, 86, :_reduce_173 ] -racc_reduce_n = 172 +racc_reduce_n = 174 -racc_shift_n = 276 +racc_shift_n = 279 racc_action_table = [ - 71, 51, 53, 184, 162, 102, 71, 51, 53, 132, - 149, 5, 209, 47, -127, 87, 71, 51, 53, 88, - 208, 65, 71, 51, 53, 162, 144, 34, 35, 99, - 65, 89, 126, -127, 50, 54, 65, 126, 59, -123, - 50, 54, 207, 45, 59, 161, 65, 56, -126, 45, - 50, 54, 65, 56, 59, 90, 50, 54, 185, 45, - 59, 36, 169, 56, 37, 45, 71, 51, 53, 56, - 204, 206, 47, 51, 53, 200, 149, 51, 53, 33, - 199, -122, 71, 51, 53, 238, 1, 65, 71, 51, - 53, 67, 144, 34, 35, 87, 65, 237, 234, -123, - 50, 54, 65, 200, 59, 233, 50, 54, 199, 45, - 59, 252, 65, 56, 43, 45, 50, 54, 65, 56, - 59, 33, 50, 54, 156, 45, 59, 36, 1, 56, - 37, 45, 71, 51, 53, 56, 204, 206, 71, 51, - 53, 149, 149, 34, 35, 188, 259, 196, 71, 51, - 53, -125, 65, 65, 71, 51, 53, 144, 144, 51, - 53, 202, 65, 162, 33, 168, 50, 54, 65, 137, - 59, 1, 50, 54, 168, 45, 59, 36, 65, 56, - 37, 45, 50, 174, 65, 56, 59, -122, 50, 54, - 165, 111, 59, -125, -122, 56, 127, 45, 71, 51, - 53, 56, 33, 211, 71, 51, 53, 204, 206, 1, - 5, 135, 218, 186, 71, 51, 53, 188, 189, 106, - 125, 51, 53, 221, 222, 231, 140, 140, 65, 225, - 126, -124, 50, 54, 65, 228, 59, 47, 50, 54, - -139, 45, 59, 123, 65, 56, 106, 45, 50, 112, - 65, 56, 59, -124, 50, 174, 134, 111, 59, 51, - 53, 56, 164, 111, 239, 51, 53, 56, 240, 241, - 242, -124, 96, 140, -126, 51, 53, 93, 179, 168, - 91, 51, 53, 159, 181, 260, 262, 85, 65, 157, - 88, 133, 50, 174, 65, 41, 59, -124, 50, 54, - 128, 111, 59, -122, 65, 56, 43, 45, 50, 174, - 65, 56, 59, -125, 50, 54, -123, 111, 59, 51, - 53, 56, 43, 45, -127, 51, 53, 56, 176, 40, - 269, -171, 270, -128, -123, 51, 53, 152, -125, nil, - nil, 51, 53, nil, nil, nil, nil, nil, 65, nil, - nil, nil, 50, 174, 65, nil, 59, nil, 50, 174, - 43, 111, 59, nil, 65, 56, nil, 111, 50, 174, - 65, 56, 59, 254, 50, 174, nil, 111, 59, 51, - 53, 56, nil, 111, nil, 51, 53, 56, nil, nil, - nil, nil, nil, 15, nil, 19, 21, nil, 28, 2, - nil, 8, nil, 13, nil, 17, nil, 22, 65, nil, - 1, 5, 50, 174, 65, 264, 59, nil, 50, 174, - nil, 111, 59, nil, nil, 56, nil, 111, nil, nil, - nil, 56, nil, nil, 271, 15, nil, 19, 21, nil, - 28, 2, nil, 8, nil, 13, nil, 17, nil, 22, - nil, nil, 1, 5, 15, 212, 19, 21, nil, 28, - 2, nil, 8, nil, 13, nil, 17, nil, 22, nil, - nil, 1, 5, nil, 230, 15, nil, 19, 21, nil, - 28, 2, nil, 8, nil, 13, nil, 17, nil, 22, - nil, nil, 1, 5, 15, 253, 19, 21, nil, 28, - 2, nil, 8, nil, 13, nil, 17, nil, 22, nil, - nil, 1, 5, nil, 214, 15, nil, 19, 21, nil, - 28, 2, nil, 8, nil, 13, nil, 17, nil, 22, - nil, nil, 1, 5, 15, 274, 19, 21, nil, 28, - 2, nil, 8, nil, 13, nil, 17, nil, 22, nil, - nil, 1, 5, nil, 235, 15, nil, 19, 21, nil, - 28, 2, nil, 8, nil, 13, nil, 17, nil, 22, - nil, nil, 1, 5, 15, 275, 19, 21, nil, 28, - 2, nil, 8, nil, 13, nil, 17, nil, 22, nil, - nil, 1, 5, nil, nil, 15, nil, 19, 21, nil, - 28, 2, nil, 8, nil, 13, nil, 17, nil, 22, - nil, nil, 1, 5, 15, nil, 19, 21, nil, 28, - 2, nil, 8, nil, 13, nil, 17, nil, 22, nil, - nil, 1, 5, 15, nil, 19, 21, nil, 28, 2, - nil, 8, nil, 13, nil, 17, nil, 22, nil, nil, - 1, 5, 15, nil, 19, 21, nil, 28, 2, nil, - 8, nil, 13, nil, 17, nil, 22, nil, nil, 1, - 5, 15, nil, 19, 21, nil, 28, 2, nil, 8, - nil, 13, nil, 17, nil, 22, nil, nil, 1, 5 ] + 77, 57, 60, 246, 167, 105, 77, 57, 60, 101, + 199, 235, -124, 167, 241, 203, 77, 57, 60, 234, + 54, 92, 77, 57, 60, 94, 131, 240, 208, 209, + 104, 47, 77, 57, 60, 59, 63, 47, 95, 68, + 131, 59, 63, 137, 55, 68, 166, 47, 65, 211, + 55, 59, 178, 47, 65, 68, -125, 59, 63, 173, + 116, 68, 96, 47, 65, -126, 55, 59, 119, -128, + 65, 68, 77, 57, 60, 147, 116, 208, 209, 147, + 65, 213, 77, 57, 60, 147, 47, 40, 41, 212, + 47, 151, 54, 57, 60, 151, 47, 35, 77, 57, + 60, 151, 188, 47, 4, 9, 172, 59, 63, 199, + 9, 68, 50, 47, 203, -125, 55, 59, 63, 204, + 65, 68, 42, 47, 205, 43, 55, 59, 63, 47, + 65, 68, 35, 59, 63, 51, 55, 68, 167, 4, + 65, 147, 55, 77, 57, 60, 65, 33, 34, 77, + 57, 60, 47, 171, 40, 41, 35, 151, -127, 77, + 57, 60, -129, 4, 214, 77, 57, 60, 208, 209, + 40, 41, 57, 60, 47, 77, 57, 60, 59, 63, + 47, 171, 68, -124, 59, 63, -130, 55, 68, 42, + 47, 65, 43, 55, 59, 63, 47, 65, 68, -125, + 59, 63, 219, 55, 68, 42, 47, 65, 43, 55, + 59, 63, 111, 65, 68, 77, 57, 60, 92, 55, + 191, 262, -127, 65, -124, 77, 57, 60, 135, 57, + 60, 35, 57, 60, 159, 189, 224, 225, 4, 191, + 192, 145, 57, 60, 145, 228, 47, 131, 57, 60, + 59, 63, 127, 133, 68, 231, 47, 54, 132, 55, + 59, 63, 47, 65, 68, -126, 59, 63, 168, 55, + 68, -141, 47, 65, 51, 55, 59, 178, 47, 65, + 68, 238, 59, 178, 239, 116, 68, 57, 60, 65, + -126, 116, 242, 57, 60, 65, 129, 245, -128, 138, + -129, 111, 94, 57, 60, -127, 171, 91, 165, 263, + 265, 90, 163, 57, 60, 139, 140, 47, 46, 57, + 60, 59, 178, 47, -129, 68, -126, 59, 178, 51, + 116, 68, -124, 47, 65, -127, 116, 59, 178, 184, + 65, 68, -125, 47, 57, 60, 116, 59, 178, 47, + 65, 68, 45, 59, 63, 272, 116, 68, -173, 273, + 65, 51, 55, 160, 57, 60, 65, 179, 143, 145, + 183, 57, 60, nil, 47, 237, nil, nil, 59, 178, + nil, nil, 68, nil, nil, nil, nil, 116, nil, nil, + nil, 65, nil, nil, 47, nil, 278, nil, 59, 178, + nil, 47, 68, nil, nil, 59, 178, 116, nil, 68, + nil, 65, nil, nil, 116, 257, nil, 20, 65, 24, + 26, nil, 1, 5, nil, 12, nil, 18, nil, 22, + nil, 27, nil, nil, 4, 9, 20, 243, 24, 26, + nil, 1, 5, nil, 12, nil, 18, nil, 22, nil, + 27, nil, nil, 4, 9, nil, 270, nil, 20, nil, + 24, 26, nil, 1, 5, nil, 12, nil, 18, nil, + 22, nil, 27, nil, nil, 4, 9, 20, 233, 24, + 26, nil, 1, 5, nil, 12, nil, 18, nil, 22, + nil, 27, nil, nil, 4, 9, nil, 277, nil, 20, + nil, 24, 26, nil, 1, 5, nil, 12, nil, 18, + nil, 22, nil, 27, nil, nil, 4, 9, 20, 274, + 24, 26, nil, 1, 5, nil, 12, nil, 18, nil, + 22, nil, 27, nil, nil, 4, 9, nil, 217, nil, + 20, nil, 24, 26, nil, 1, 5, nil, 12, nil, + 18, nil, 22, nil, 27, nil, nil, 4, 9, 20, + 215, 24, 26, nil, 1, 5, nil, 12, nil, 18, + nil, 22, nil, 27, nil, nil, 4, 9, nil, 256, + nil, 20, nil, 24, 26, nil, 1, 5, nil, 12, + nil, 18, nil, 22, nil, 27, nil, nil, 4, 9, + 20, nil, 24, 26, nil, 1, 5, nil, 12, nil, + 18, nil, 22, nil, 27, nil, nil, 4, 9, 20, + nil, 24, 26, nil, 1, 5, nil, 12, nil, 18, + nil, 22, nil, 27, nil, nil, 4, 9, 20, nil, + 24, 26, nil, 1, 5, nil, 12, nil, 18, nil, + 22, nil, 27, nil, nil, 4, 9, 20, nil, 24, + 26, nil, 1, 5, nil, 12, nil, 18, nil, 22, + nil, 27, nil, nil, 4, 9, 20, nil, 24, 26, + nil, 1, 5, nil, 12, nil, 18, nil, 22, nil, + 27, nil, nil, 4, 9 ] racc_action_check = [ - 93, 93, 93, 127, 106, 38, 71, 71, 71, 71, - 90, 127, 146, 54, 78, 23, 87, 87, 87, 23, - 146, 90, 161, 161, 161, 112, 90, 102, 102, 38, - 93, 23, 54, 64, 93, 93, 71, 112, 93, 62, - 71, 71, 142, 93, 71, 106, 87, 93, 66, 71, - 87, 87, 161, 71, 87, 23, 161, 161, 129, 87, - 161, 102, 119, 87, 102, 161, 240, 240, 240, 161, - 142, 142, 13, 13, 13, 238, 149, 135, 135, 2, - 238, 74, 15, 15, 15, 197, 2, 149, 17, 17, - 17, 13, 149, 99, 99, 61, 240, 197, 194, 118, - 240, 240, 13, 137, 240, 194, 13, 13, 137, 240, - 13, 210, 15, 240, 13, 13, 15, 15, 17, 13, - 15, 96, 17, 17, 96, 15, 17, 99, 96, 15, - 99, 17, 239, 239, 239, 17, 210, 210, 162, 162, - 162, 89, 205, 8, 8, 227, 227, 136, 165, 165, - 165, 116, 89, 205, 47, 47, 47, 89, 205, 19, - 19, 139, 239, 140, 21, 141, 239, 239, 162, 84, - 239, 21, 162, 162, 115, 239, 162, 8, 165, 239, - 8, 162, 165, 165, 47, 162, 165, 114, 47, 47, - 113, 165, 47, 60, 58, 165, 55, 47, 225, 225, - 225, 47, 29, 151, 187, 187, 187, 151, 151, 29, - 29, 83, 163, 131, 41, 41, 41, 131, 131, 164, - 52, 192, 192, 166, 167, 192, 168, 169, 225, 170, - 174, 46, 225, 225, 187, 182, 225, 184, 187, 187, - 45, 225, 187, 42, 41, 225, 40, 187, 41, 41, - 192, 187, 41, 110, 192, 192, 81, 41, 192, 208, - 208, 41, 107, 192, 199, 126, 126, 192, 200, 201, - 203, 70, 32, 88, 79, 209, 209, 28, 124, 224, - 25, 67, 67, 100, 126, 230, 233, 22, 208, 97, - 95, 73, 208, 208, 126, 11, 208, 245, 126, 126, - 67, 208, 126, 246, 209, 208, 126, 126, 209, 209, - 67, 126, 209, 248, 67, 67, 249, 209, 67, 125, - 125, 209, 67, 67, 250, 123, 123, 67, 123, 9, - 258, 259, 260, 77, 76, 176, 176, 91, 75, nil, - nil, 134, 134, nil, nil, nil, nil, nil, 125, nil, - nil, nil, 125, 125, 123, nil, 125, nil, 123, 123, - 125, 125, 123, nil, 176, 125, nil, 123, 176, 176, - 134, 123, 176, 215, 134, 134, nil, 176, 134, 234, - 234, 176, nil, 134, nil, 257, 257, 134, nil, nil, - nil, nil, nil, 215, nil, 215, 215, nil, 215, 215, - nil, 215, nil, 215, nil, 215, nil, 215, 234, nil, - 215, 215, 234, 234, 257, 236, 234, nil, 257, 257, - nil, 234, 257, nil, nil, 234, nil, 257, nil, nil, - nil, 257, nil, nil, 262, 236, nil, 236, 236, nil, - 236, 236, nil, 236, nil, 236, nil, 236, nil, 236, - nil, nil, 236, 236, 262, 157, 262, 262, nil, 262, - 262, nil, 262, nil, 262, nil, 262, nil, 262, nil, - nil, 262, 262, nil, 190, 157, nil, 157, 157, nil, - 157, 157, nil, 157, nil, 157, nil, 157, nil, 157, - nil, nil, 157, 157, 190, 213, 190, 190, nil, 190, - 190, nil, 190, nil, 190, nil, 190, nil, 190, nil, - nil, 190, 190, nil, 159, 213, nil, 213, 213, nil, - 213, 213, nil, 213, nil, 213, nil, 213, nil, 213, - nil, nil, 213, 213, 159, 272, 159, 159, nil, 159, - 159, nil, 159, nil, 159, nil, 159, nil, 159, nil, - nil, 159, 159, nil, 196, 272, nil, 272, 272, nil, - 272, 272, nil, 272, nil, 272, nil, 272, nil, 272, - nil, nil, 272, 272, 196, 273, 196, 196, nil, 196, - 196, nil, 196, nil, 196, nil, 196, nil, 196, nil, - nil, 196, 196, nil, nil, 273, nil, 273, 273, nil, - 273, 273, nil, 273, nil, 273, nil, 273, nil, 273, - nil, nil, 273, 273, 133, nil, 133, 133, nil, 133, - 133, nil, 133, nil, 133, nil, 133, nil, 133, nil, - nil, 133, 133, 270, nil, 270, 270, nil, 270, 270, - nil, 270, nil, 270, nil, 270, nil, 270, nil, nil, - 270, 270, 27, nil, 27, 27, nil, 27, 27, nil, - 27, nil, 27, nil, 27, nil, 27, nil, nil, 27, - 27, 0, nil, 0, 0, nil, 0, 0, nil, 0, - nil, 0, nil, 0, nil, 0, nil, nil, 0, 0 ] + 228, 228, 228, 207, 111, 44, 54, 54, 54, 36, + 241, 195, 83, 119, 201, 241, 168, 168, 168, 195, + 63, 30, 92, 92, 92, 30, 119, 201, 207, 207, + 44, 228, 46, 46, 46, 228, 228, 54, 30, 228, + 63, 54, 54, 82, 228, 54, 111, 168, 228, 149, + 54, 168, 168, 92, 54, 168, 85, 92, 92, 128, + 168, 92, 30, 46, 168, 80, 92, 46, 46, 75, + 92, 46, 167, 167, 167, 95, 46, 149, 149, 147, + 46, 153, 166, 166, 166, 96, 95, 105, 105, 153, + 147, 95, 18, 18, 18, 147, 96, 6, 20, 20, + 20, 96, 133, 167, 6, 6, 126, 167, 167, 140, + 133, 167, 18, 166, 140, 125, 167, 166, 166, 142, + 167, 166, 105, 18, 144, 105, 166, 18, 18, 20, + 166, 18, 5, 20, 20, 18, 18, 20, 145, 5, + 18, 210, 20, 22, 22, 22, 20, 1, 1, 242, + 242, 242, 210, 146, 12, 12, 26, 210, 123, 238, + 238, 238, 74, 26, 156, 34, 34, 34, 156, 156, + 104, 104, 139, 139, 22, 33, 33, 33, 22, 22, + 242, 122, 22, 121, 242, 242, 73, 22, 242, 12, + 238, 22, 12, 242, 238, 238, 34, 242, 238, 72, + 34, 34, 164, 238, 34, 104, 33, 238, 104, 34, + 33, 33, 165, 34, 33, 190, 190, 190, 71, 33, + 230, 230, 70, 33, 69, 77, 77, 77, 77, 24, + 24, 101, 50, 50, 101, 136, 169, 170, 101, 136, + 136, 171, 235, 235, 172, 174, 190, 178, 138, 138, + 190, 190, 50, 66, 190, 185, 77, 188, 64, 190, + 77, 77, 50, 190, 77, 58, 50, 50, 120, 77, + 50, 55, 235, 77, 50, 50, 235, 235, 138, 50, + 235, 199, 138, 138, 200, 235, 138, 132, 132, 235, + 118, 138, 203, 260, 260, 138, 53, 206, 52, 86, + 49, 45, 38, 213, 213, 84, 227, 29, 107, 233, + 234, 27, 106, 212, 212, 88, 89, 132, 17, 131, + 131, 132, 132, 260, 248, 132, 250, 260, 260, 132, + 132, 260, 251, 213, 132, 253, 260, 213, 213, 131, + 260, 213, 254, 212, 179, 179, 213, 212, 212, 131, + 213, 212, 15, 131, 131, 261, 212, 131, 262, 263, + 212, 131, 131, 102, 129, 129, 131, 129, 91, 94, + 130, 197, 197, nil, 179, 197, nil, nil, 179, 179, + nil, nil, 179, nil, nil, nil, nil, 179, nil, nil, + nil, 179, nil, nil, 129, nil, 276, nil, 129, 129, + nil, 197, 129, nil, nil, 197, 197, 129, nil, 197, + nil, 129, nil, nil, 197, 218, nil, 276, 197, 276, + 276, nil, 276, 276, nil, 276, nil, 276, nil, 276, + nil, 276, nil, nil, 276, 276, 218, 204, 218, 218, + nil, 218, 218, nil, 218, nil, 218, nil, 218, nil, + 218, nil, nil, 218, 218, nil, 244, nil, 204, nil, + 204, 204, nil, 204, 204, nil, 204, nil, 204, nil, + 204, nil, 204, nil, nil, 204, 204, 244, 193, 244, + 244, nil, 244, 244, nil, 244, nil, 244, nil, 244, + nil, 244, nil, nil, 244, 244, nil, 275, nil, 193, + nil, 193, 193, nil, 193, 193, nil, 193, nil, 193, + nil, 193, nil, 193, nil, nil, 193, 193, 275, 265, + 275, 275, nil, 275, 275, nil, 275, nil, 275, nil, + 275, nil, 275, nil, nil, 275, 275, nil, 163, nil, + 265, nil, 265, 265, nil, 265, 265, nil, 265, nil, + 265, nil, 265, nil, 265, nil, nil, 265, 265, 163, + 160, 163, 163, nil, 163, 163, nil, 163, nil, 163, + nil, 163, nil, 163, nil, nil, 163, 163, nil, 216, + nil, 160, nil, 160, 160, nil, 160, 160, nil, 160, + nil, 160, nil, 160, nil, 160, nil, nil, 160, 160, + 216, nil, 216, 216, nil, 216, 216, nil, 216, nil, + 216, nil, 216, nil, 216, nil, nil, 216, 216, 273, + nil, 273, 273, nil, 273, 273, nil, 273, nil, 273, + nil, 273, nil, 273, nil, nil, 273, 273, 32, nil, + 32, 32, nil, 32, 32, nil, 32, nil, 32, nil, + 32, nil, 32, nil, nil, 32, 32, 137, nil, 137, + 137, nil, 137, 137, nil, 137, nil, 137, nil, 137, + nil, 137, nil, nil, 137, 137, 0, nil, 0, 0, + nil, 0, 0, nil, 0, nil, 0, nil, 0, nil, + 0, nil, nil, 0, 0 ] racc_action_pointer = [ - 644, nil, 42, nil, nil, nil, nil, nil, 140, 323, - nil, 289, nil, 70, nil, 80, nil, 86, nil, 156, - nil, 127, 246, 13, nil, 280, nil, 625, 264, 165, - nil, nil, 238, nil, nil, nil, nil, nil, -5, nil, - 209, 212, 223, nil, nil, 238, 211, 152, nil, nil, - nil, nil, 210, nil, 11, 186, nil, nil, 174, nil, - 173, 93, 19, nil, 13, nil, 28, 278, nil, nil, - 251, 4, nil, 285, 61, 318, 314, 313, -6, 254, - nil, 250, nil, 201, 148, nil, nil, 14, 236, 120, - -11, 337, nil, -2, nil, 284, 84, 283, nil, 90, - 277, nil, 24, nil, nil, nil, -5, 252, nil, nil, - 233, nil, 16, 152, 167, 164, 131, nil, 79, 45, - nil, nil, nil, 322, 273, 316, 262, -34, nil, 36, - nil, 208, nil, 587, 338, 74, 141, 71, nil, 156, - 154, 155, 23, nil, nil, nil, -3, nil, nil, 55, - nil, 160, nil, nil, nil, nil, nil, 448, nil, 507, - nil, 20, 136, 205, 182, 146, 216, 217, 189, 190, - 220, nil, nil, nil, 209, nil, 332, nil, nil, nil, - nil, nil, 213, nil, 235, nil, nil, 202, nil, nil, - 467, nil, 218, nil, 88, nil, 547, 75, nil, 251, - 255, 247, nil, 263, nil, 121, nil, nil, 256, 272, - 89, nil, nil, 488, nil, 366, nil, nil, nil, nil, - nil, nil, nil, nil, 269, 196, nil, 136, nil, nil, - 257, nil, nil, 280, 376, nil, 408, nil, 43, 130, - 64, nil, nil, nil, nil, 277, 283, nil, 293, 296, - 304, nil, nil, nil, nil, nil, nil, 382, 323, 324, - 326, nil, 427, nil, nil, nil, nil, nil, nil, nil, - 606, nil, 528, 568, nil, nil ] + 648, 134, nil, nil, nil, 94, 59, nil, nil, nil, + nil, nil, 151, nil, nil, 346, nil, 312, 90, nil, + 96, nil, 141, nil, 226, nil, 118, 269, nil, 307, + 19, nil, 610, 173, 163, nil, -26, nil, 296, nil, + nil, nil, nil, nil, -5, 263, 30, nil, nil, 279, + 229, nil, 277, 275, 4, 269, nil, nil, 244, nil, + nil, nil, nil, 18, 248, nil, 243, nil, nil, 203, + 201, 216, 178, 165, 141, 48, nil, 223, nil, nil, + 44, nil, 37, -9, 284, 35, 293, nil, 305, 294, + nil, 368, 20, nil, 331, 53, 63, nil, nil, nil, + nil, 193, 357, nil, 167, 84, 306, 298, nil, nil, + nil, -5, nil, nil, nil, nil, nil, nil, 269, 4, + 229, 162, 171, 137, nil, 94, 88, nil, 36, 361, + 365, 316, 284, 64, nil, nil, 230, 629, 245, 169, + 76, nil, 113, nil, 119, 129, 143, 57, nil, 29, + nil, nil, nil, 65, nil, nil, 120, nil, nil, nil, + 553, nil, nil, 531, 195, 174, 80, 70, 14, 229, + 230, 203, 206, nil, 236, nil, nil, nil, 225, 341, + nil, nil, nil, nil, nil, 232, nil, nil, 255, nil, + 213, nil, nil, 471, nil, 1, nil, 368, nil, 268, + 261, 4, nil, 279, 430, nil, 290, -20, nil, nil, + 119, nil, 310, 300, nil, nil, 572, nil, 408, nil, + nil, nil, nil, nil, nil, nil, nil, 296, -2, nil, + 211, nil, nil, 280, 304, 239, nil, nil, 157, nil, + nil, -23, 147, nil, 449, nil, nil, nil, 303, nil, + 305, 311, nil, 314, 321, nil, nil, nil, nil, nil, + 290, 348, 351, 353, nil, 512, nil, nil, nil, nil, + nil, nil, nil, 591, nil, 490, 389, nil, nil ] racc_action_default = [ - -148, -141, -172, -15, -3, -139, -16, -5, -172, -172, - -6, -172, -7, -140, -8, -172, -9, -172, -10, -172, - -11, -172, -39, -172, -12, -172, -13, -1, -172, -172, - -14, -2, -148, -140, -146, -145, -144, -147, -148, -142, - -77, -70, -172, -31, -29, -62, -30, -172, -32, -19, - -102, -100, -20, -101, -61, -21, -60, -22, -24, -129, - -25, -172, -26, -128, -27, -165, -28, -172, -108, -92, - -88, -172, -95, -172, -89, -93, -90, -97, -91, -96, - -94, -172, -130, -132, -148, -40, -41, -172, -70, -148, - -148, -172, -4, -172, -38, -172, -172, -172, -161, -172, - -172, -159, -172, -75, -76, -78, -172, -170, -66, -71, - -63, -62, -61, -58, -64, -170, -67, -56, -65, -172, - -127, -126, -68, -172, -172, -172, -172, -172, -18, -172, - -80, -172, -167, -172, -172, -172, -172, -149, -150, -172, - -172, -170, -172, -45, -61, -47, -172, -54, -53, -148, - -44, -172, 276, -69, -162, -163, -164, -172, -160, -172, - -143, -172, -172, -172, -171, -59, -172, -172, -171, -70, - -172, -124, -116, -117, -61, -122, -172, -125, -123, -103, - -23, -99, -172, -33, -172, -17, -166, -172, -168, -169, - -172, -114, -172, -110, -172, -131, -172, -172, -153, -156, - -158, -172, -104, -172, -50, -172, -49, -43, -172, -172, - -172, -42, -136, -172, -138, -172, -74, -73, -37, -79, - -57, -34, -35, -72, -170, -172, -119, -170, -98, -81, - -106, -109, -111, -172, -172, -134, -172, -152, -172, -172, - -172, -151, -36, -46, -86, -82, -83, -51, -87, -84, - -85, -52, -48, -135, -137, -55, -121, -172, -172, -169, - -172, -105, -172, -115, -133, -154, -155, -157, -120, -118, - -172, -113, -172, -172, -112, -107 ] + -150, -174, -14, -2, -143, -174, -174, -15, -3, -141, + -16, -5, -174, -17, -6, -174, -7, -174, -142, -8, + -174, -9, -174, -10, -174, -11, -174, -40, -12, -174, + -174, -13, -1, -174, -174, -142, -150, -39, -174, -144, + -148, -147, -146, -149, -150, -79, -72, -167, -130, -28, + -174, -32, -29, -174, -174, -63, -30, -102, -31, -104, + -103, -33, -20, -62, -21, -61, -22, -23, -131, -25, + -26, -174, -27, -99, -93, -98, -96, -174, -110, -94, + -90, -97, -174, -91, -95, -92, -174, -132, -134, -150, + -41, -174, -174, -42, -72, -150, -150, -4, -70, -71, + -163, -174, -174, -161, -174, -174, -174, -172, -77, -78, + -80, -174, -129, -128, -69, -67, -63, -73, -64, -62, + -59, -65, -172, -68, -57, -66, -174, -19, -174, -174, + -174, -174, -174, -174, -82, -169, -174, -174, -174, -174, + -151, -152, -174, 279, -174, -174, -172, -150, -45, -174, + -46, -62, -48, -174, -55, -54, -174, -164, -165, -166, + -174, -162, -145, -174, -174, -173, -174, -174, -60, -174, + -174, -173, -72, -18, -174, -126, -118, -119, -62, -174, + -124, -127, -125, -105, -101, -174, -24, -34, -174, -168, + -174, -170, -171, -174, -112, -174, -116, -174, -133, -160, + -174, -174, -155, -158, -174, -106, -174, -174, -51, -50, + -174, -44, -174, -174, -43, -138, -174, -140, -174, -38, + -81, -76, -75, -58, -35, -36, -74, -172, -174, -121, + -172, -100, -83, -108, -174, -174, -113, -111, -174, -153, + -154, -174, -174, -136, -174, -37, -49, -47, -87, -88, + -84, -85, -52, -89, -86, -53, -137, -139, -56, -123, + -174, -174, -171, -174, -107, -174, -117, -159, -156, -157, + -135, -122, -120, -174, -115, -174, -174, -114, -109 ] racc_goto_table = [ - 23, 27, 122, 92, 173, 105, 198, 49, 117, 46, - 187, 39, 31, 170, 32, 82, 193, 103, 115, 25, - 142, 151, 243, 44, 191, 247, 251, 23, 131, 95, - 64, 86, 73, 84, 261, 192, 107, 110, 166, 172, - 113, 227, 83, 62, 98, 163, 136, 97, 38, 100, - 101, 108, 180, 167, 201, 197, 94, 226, 120, 154, - nil, 129, nil, 46, 66, 141, 170, nil, nil, nil, - nil, 118, nil, nil, 232, nil, nil, 44, nil, 203, - 210, nil, 191, nil, 64, nil, nil, nil, nil, nil, - nil, nil, 121, nil, nil, nil, 138, 62, nil, nil, - nil, nil, 158, nil, nil, 160, 257, 265, 155, nil, - nil, nil, nil, nil, nil, nil, nil, nil, 66, 171, - 182, 46, 46, nil, 263, nil, 122, nil, nil, 219, - 171, 195, 220, 23, 190, 44, 44, nil, 268, nil, - 120, 103, 64, 64, nil, 223, 224, 170, nil, nil, - nil, 120, nil, 178, nil, 62, 62, 23, 213, 23, - 215, 110, 255, nil, 178, 258, 92, nil, nil, nil, - 60, nil, 171, nil, 121, 108, 66, 66, nil, nil, - 58, nil, 120, nil, nil, 121, nil, nil, 171, 92, - 23, 92, nil, 120, nil, 118, 23, 236, 116, nil, - nil, nil, nil, nil, 245, 245, 178, nil, 114, 120, - nil, nil, 92, 23, nil, 23, 121, nil, 244, 244, - nil, nil, 178, nil, 60, 250, 250, 121, nil, nil, - 171, nil, nil, nil, 58, nil, 23, nil, 249, 249, - nil, nil, nil, 121, nil, nil, 148, 148, 92, 92, - nil, 120, nil, 171, nil, nil, 147, 147, nil, 121, - 121, nil, 23, 272, 178, nil, nil, nil, nil, nil, - 23, 273, 23, 23, 120, 68, nil, 81, nil, 77, - 177, 77, 60, 60, 48, 121, 72, 178, 72, nil, - 175, 177, 58, 58, nil, nil, nil, nil, nil, nil, - nil, 175, nil, nil, nil, nil, 148, 124, 121, nil, - nil, 77, nil, nil, nil, nil, 147, nil, 72, nil, - nil, nil, 116, nil, nil, nil, nil, nil, nil, nil, - nil, 130, 114, 177, nil, 77, nil, nil, 48, nil, - nil, nil, 72, 175, nil, nil, nil, 139, nil, 177, - nil, 77, nil, 153, nil, nil, nil, 77, 72, 175, - nil, nil, 148, nil, 72, 248, 248, nil, nil, nil, - nil, nil, 147, nil, nil, 246, 246, nil, nil, nil, + 30, 32, 56, 108, 174, 97, 38, 110, 52, 39, + 122, 177, 62, 196, 190, 3, 202, 114, 194, 29, + 58, 124, 149, 156, 164, 247, 87, 136, 36, 93, + 115, 82, 30, 264, 56, 197, 113, 252, 255, 170, + 52, 107, 169, 176, 128, 120, 230, 88, 118, 89, + 142, 100, 58, 102, 174, 44, 106, 186, 146, 103, + 49, 229, 200, 206, 201, 37, 157, nil, nil, nil, + nil, 61, 196, 81, 207, 81, nil, 236, nil, nil, + nil, nil, nil, nil, nil, nil, 81, 81, 112, nil, + nil, nil, 49, nil, nil, nil, nil, nil, nil, nil, + nil, 161, 162, 61, 141, nil, nil, 81, 260, nil, + 266, nil, nil, nil, nil, 56, 56, 268, 72, 113, + nil, 52, 52, 108, 158, 185, nil, 220, 113, 226, + 81, 175, nil, 58, 58, 174, 227, 30, 193, 114, + 175, 198, 271, 223, 258, 81, 125, 261, nil, nil, + 72, nil, 115, nil, nil, nil, nil, nil, 113, nil, + 30, 216, nil, 30, 218, nil, 97, nil, nil, 113, + 118, 112, nil, 49, 49, nil, nil, nil, nil, 70, + 112, 175, nil, nil, 61, nil, 187, 113, nil, 97, + nil, 97, nil, 30, nil, nil, 249, 249, nil, 175, + nil, nil, 113, 113, 30, 244, nil, 123, nil, nil, + 112, 70, nil, nil, 250, 250, 30, 97, 30, 81, + 81, 112, nil, nil, nil, 113, nil, nil, nil, 182, + nil, 72, 72, nil, nil, nil, nil, 175, 182, 112, + nil, nil, nil, 81, 30, nil, nil, nil, 97, 97, + 113, nil, nil, nil, 248, 248, 155, 155, nil, nil, + nil, nil, 175, nil, nil, 30, 275, nil, 125, 78, + nil, 86, 69, 30, 276, 30, 30, 112, nil, 182, + nil, 81, 98, 99, nil, nil, nil, nil, nil, nil, + 181, 81, 70, 70, nil, 81, nil, 182, nil, 181, + 121, nil, 112, 130, 69, nil, nil, nil, 155, 73, + nil, 73, 254, 254, nil, nil, nil, nil, nil, nil, + nil, nil, 73, 73, nil, nil, 134, nil, nil, 123, + nil, nil, nil, nil, nil, 182, nil, nil, nil, nil, + 181, 144, nil, 73, nil, nil, nil, nil, nil, 154, + 154, nil, nil, nil, nil, nil, nil, nil, 181, nil, + 182, nil, nil, nil, nil, nil, 73, nil, nil, nil, + nil, 155, nil, 253, 253, nil, nil, nil, nil, nil, + nil, 73, nil, 180, nil, 69, 69, nil, nil, nil, + nil, nil, 180, nil, nil, nil, 181, nil, nil, nil, + nil, 154, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, 221, 222, nil, nil, nil, + nil, 181, 121, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, 180, nil, nil, nil, nil, nil, 232, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, 177, nil, nil, nil, nil, nil, 48, 183, nil, - nil, 175, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, 177, nil, nil, nil, nil, nil, - nil, 216, 217, nil, 175, 77, 77, nil, nil, nil, - nil, nil, 72, 72, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, 229, nil, nil, - nil, 77, nil, nil, nil, nil, nil, nil, 72, nil, + nil, 180, nil, nil, nil, 73, 73, nil, nil, nil, + nil, nil, nil, nil, 154, nil, 251, 251, nil, nil, + nil, nil, nil, nil, nil, nil, nil, 259, nil, 73, + nil, nil, nil, nil, nil, nil, nil, 267, nil, 180, + nil, 269, 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, nil, nil, - nil, nil, nil, nil, nil, 256, nil, nil, nil, 77, - nil, nil, nil, nil, nil, nil, 72, nil, nil, 266, - 267, nil, nil, 77, 77, nil, nil, nil, nil, nil, - 72, 72 ] + nil, nil, nil, nil, 180, nil, nil, 73, nil, nil, + nil, nil, nil, nil, nil, nil, nil, 73, nil, nil, + nil, 73 ] racc_goto_check = [ - 34, 2, 47, 4, 61, 51, 71, 17, 44, 27, - 53, 68, 3, 59, 29, 27, 57, 49, 32, 1, - 38, 38, 39, 26, 59, 43, 43, 34, 52, 34, - 24, 37, 54, 29, 55, 56, 35, 27, 31, 60, - 30, 62, 63, 23, 3, 33, 64, 65, 66, 67, - 3, 26, 20, 33, 69, 70, 5, 61, 24, 72, - nil, 17, nil, 27, 25, 32, 59, nil, nil, nil, - nil, 23, nil, nil, 57, nil, nil, 26, nil, 33, - 38, nil, 59, nil, 24, nil, nil, nil, nil, nil, - nil, nil, 25, nil, nil, nil, 3, 23, nil, nil, - nil, nil, 68, nil, nil, 68, 53, 71, 29, nil, - nil, nil, nil, nil, nil, nil, nil, nil, 25, 27, - 17, 27, 27, nil, 59, nil, 47, nil, nil, 51, - 27, 27, 44, 34, 2, 26, 26, nil, 61, nil, - 24, 49, 24, 24, nil, 49, 32, 59, nil, nil, - nil, 24, nil, 23, nil, 23, 23, 34, 2, 34, - 2, 27, 33, nil, 23, 33, 4, nil, nil, nil, - 22, nil, 27, nil, 25, 26, 25, 25, nil, nil, - 21, nil, 24, nil, nil, 25, nil, nil, 27, 4, - 34, 4, nil, 24, nil, 23, 34, 2, 22, nil, - nil, nil, nil, nil, 27, 27, 23, nil, 21, 24, - nil, nil, 4, 34, nil, 34, 25, nil, 26, 26, - nil, nil, 23, nil, 22, 24, 24, 25, nil, nil, - 27, nil, nil, nil, 21, nil, 34, nil, 23, 23, - nil, nil, nil, 25, nil, nil, 22, 22, 4, 4, - nil, 24, nil, 27, nil, nil, 21, 21, nil, 25, - 25, nil, 34, 2, 23, nil, nil, nil, nil, nil, - 34, 2, 34, 34, 24, 48, nil, 48, nil, 46, - 22, 46, 22, 22, 28, 25, 28, 23, 28, nil, - 21, 22, 21, 21, nil, nil, nil, nil, nil, nil, - nil, 21, nil, nil, nil, nil, 22, 48, 25, nil, - nil, 46, nil, nil, nil, nil, 21, nil, 28, nil, - nil, nil, 22, nil, nil, nil, nil, nil, nil, nil, - nil, 48, 21, 22, nil, 46, nil, nil, 28, nil, - nil, nil, 28, 21, nil, nil, nil, 48, nil, 22, - nil, 46, nil, 48, nil, nil, nil, 46, 28, 21, - nil, nil, 22, nil, 28, 22, 22, nil, nil, nil, - nil, nil, 21, nil, nil, 21, 21, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, 22, nil, nil, nil, nil, nil, 28, 28, nil, - nil, 21, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, 22, nil, nil, nil, nil, nil, - nil, 48, 48, nil, 21, 46, 46, nil, nil, nil, - nil, nil, 28, 28, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, 48, nil, nil, - nil, 46, nil, nil, nil, nil, nil, nil, 28, nil, + 35, 2, 27, 50, 60, 4, 35, 52, 26, 69, + 33, 62, 18, 60, 54, 3, 72, 48, 58, 1, + 28, 45, 39, 39, 34, 40, 28, 53, 30, 38, + 27, 55, 35, 56, 27, 57, 26, 44, 44, 34, + 26, 36, 32, 61, 18, 31, 63, 64, 28, 30, + 65, 3, 28, 66, 60, 67, 68, 21, 33, 3, + 25, 62, 70, 34, 71, 5, 73, nil, nil, nil, + nil, 29, 60, 29, 39, 29, nil, 58, nil, nil, + nil, nil, nil, nil, nil, nil, 29, 29, 25, nil, + nil, nil, 25, nil, nil, nil, nil, nil, nil, nil, + nil, 69, 69, 29, 3, nil, nil, 29, 54, nil, + 60, nil, nil, nil, nil, 27, 27, 72, 24, 26, + nil, 26, 26, 50, 30, 18, nil, 52, 26, 50, + 29, 28, nil, 28, 28, 60, 33, 35, 2, 48, + 28, 28, 62, 45, 34, 29, 24, 34, nil, nil, + 24, nil, 27, nil, nil, nil, nil, nil, 26, nil, + 35, 2, nil, 35, 2, nil, 4, nil, nil, 26, + 28, 25, nil, 25, 25, nil, nil, nil, nil, 23, + 25, 28, nil, nil, 29, nil, 29, 26, nil, 4, + nil, 4, nil, 35, nil, nil, 27, 27, nil, 28, + nil, nil, 26, 26, 35, 2, nil, 23, nil, nil, + 25, 23, nil, nil, 28, 28, 35, 4, 35, 29, + 29, 25, nil, nil, nil, 26, nil, nil, nil, 24, + nil, 24, 24, nil, nil, nil, nil, 28, 24, 25, + nil, nil, nil, 29, 35, nil, nil, nil, 4, 4, + 26, nil, nil, nil, 25, 25, 23, 23, nil, nil, + nil, nil, 28, nil, nil, 35, 2, nil, 24, 49, + nil, 49, 22, 35, 2, 35, 35, 25, nil, 24, + nil, 29, 49, 49, nil, nil, nil, nil, nil, nil, + 23, 29, 23, 23, nil, 29, nil, 24, nil, 23, + 22, nil, 25, 49, 22, nil, nil, nil, 23, 47, + nil, 47, 24, 24, nil, nil, nil, nil, nil, nil, + nil, nil, 47, 47, nil, nil, 49, nil, nil, 23, + nil, nil, nil, nil, nil, 24, nil, nil, nil, nil, + 23, 49, nil, 47, nil, nil, nil, nil, nil, 22, + 22, nil, nil, nil, nil, nil, nil, nil, 23, nil, + 24, nil, nil, nil, nil, nil, 47, nil, nil, nil, + nil, 23, nil, 23, 23, nil, nil, nil, nil, nil, + nil, 47, nil, 22, nil, 22, 22, nil, nil, nil, + nil, nil, 22, nil, nil, nil, 23, nil, nil, nil, + nil, 22, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, 49, 49, nil, nil, nil, + nil, 23, 22, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, 22, nil, nil, nil, nil, nil, 49, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, 22, nil, nil, nil, 47, 47, nil, nil, nil, + nil, nil, nil, nil, 22, nil, 22, 22, nil, nil, + nil, nil, nil, nil, nil, nil, nil, 49, nil, 47, + nil, nil, nil, nil, nil, nil, nil, 49, nil, 22, + nil, 49, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, 48, nil, nil, nil, 46, - nil, nil, nil, nil, nil, nil, 28, nil, nil, 48, - 48, nil, nil, 46, 46, nil, nil, nil, nil, nil, - 28, 28 ] + nil, nil, nil, nil, 22, nil, nil, 47, nil, nil, + nil, nil, nil, nil, nil, nil, nil, 47, nil, nil, + nil, 47 ] racc_goto_pointer = [ - nil, 19, 1, 12, -24, 27, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, -6, nil, nil, - -73, 167, 157, 30, 17, 51, 10, -4, 271, 12, - -1, -75, -23, -62, 0, -4, nil, 8, -69, -183, - nil, nil, nil, -183, -33, nil, 264, -39, 260, -23, - nil, -35, -43, -121, 17, -196, -99, -118, nil, -110, - -84, -119, -135, 23, -38, 15, 40, 11, 3, -83, - -82, -131, -37 ] + nil, 19, 1, 15, -27, 59, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, -6, nil, + nil, -75, 254, 161, 100, 42, -10, -16, 2, 53, + 23, -1, -78, -36, -83, 0, -4, nil, -1, -73, + -185, nil, nil, nil, -175, -25, nil, 289, -29, 249, + -42, nil, -38, -50, -122, 11, -200, -103, -120, nil, + -125, -86, -118, -133, 23, -39, 17, 43, 12, -3, + -78, -76, -124, -35 ] racc_goto_default = [ - nil, nil, nil, 150, 4, 7, 10, 12, 14, 16, - 18, 20, 24, 26, 30, 3, 6, nil, 52, 55, - 57, 74, 75, 76, 78, 79, 69, 70, 9, 11, - nil, nil, nil, nil, 61, nil, 29, nil, nil, 143, - 205, 145, 146, nil, nil, 119, 63, 80, nil, 109, - 104, nil, nil, nil, nil, nil, nil, nil, 194, 42, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil ] + nil, nil, nil, 148, 8, 11, 14, 16, 19, 21, + 23, 25, 28, 31, 2, 7, 10, 13, nil, 64, + 66, 67, 83, 84, 85, 74, 75, 79, 80, 15, + 17, nil, nil, nil, nil, 71, nil, 6, nil, nil, + 150, 210, 152, 153, nil, nil, 126, 48, 76, nil, + 117, 109, nil, nil, nil, nil, nil, nil, nil, 195, + 53, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil ] racc_token_table = { false => 0, @@ -568,47 +576,48 @@ racc_token_table = { :TRUE => 11, :FALSE => 12, :EQUALS => 13, - :LESSEQUAL => 14, - :NOTEQUAL => 15, - :DOT => 16, - :COLON => 17, - :LLCOLLECT => 18, - :RRCOLLECT => 19, - :QMARK => 20, - :LPAREN => 21, - :RPAREN => 22, - :ISEQUAL => 23, - :GREATEREQUAL => 24, - :GREATERTHAN => 25, - :LESSTHAN => 26, - :IF => 27, - :ELSE => 28, - :IMPORT => 29, - :DEFINE => 30, - :ELSIF => 31, - :VARIABLE => 32, - :CLASS => 33, - :INHERITS => 34, - :NODE => 35, - :BOOLEAN => 36, - :NAME => 37, - :SEMIC => 38, - :CASE => 39, - :DEFAULT => 40, - :AT => 41, - :LCOLLECT => 42, - :RCOLLECT => 43, - :CLASSNAME => 44, - :CLASSREF => 45, - :NOT => 46, - :OR => 47, - :AND => 48, - :UNDEF => 49, - :PARROW => 50 } + :APPENDS => 14, + :LESSEQUAL => 15, + :NOTEQUAL => 16, + :DOT => 17, + :COLON => 18, + :LLCOLLECT => 19, + :RRCOLLECT => 20, + :QMARK => 21, + :LPAREN => 22, + :RPAREN => 23, + :ISEQUAL => 24, + :GREATEREQUAL => 25, + :GREATERTHAN => 26, + :LESSTHAN => 27, + :IF => 28, + :ELSE => 29, + :IMPORT => 30, + :DEFINE => 31, + :ELSIF => 32, + :VARIABLE => 33, + :CLASS => 34, + :INHERITS => 35, + :NODE => 36, + :BOOLEAN => 37, + :NAME => 38, + :SEMIC => 39, + :CASE => 40, + :DEFAULT => 41, + :AT => 42, + :LCOLLECT => 43, + :RCOLLECT => 44, + :CLASSNAME => 45, + :CLASSREF => 46, + :NOT => 47, + :OR => 48, + :AND => 49, + :UNDEF => 50, + :PARROW => 51 } racc_use_result_var = true -racc_nt_base = 51 +racc_nt_base = 52 Racc_arg = [ racc_action_table, @@ -641,6 +650,7 @@ Racc_token_to_s_table = [ 'TRUE', 'FALSE', 'EQUALS', +'APPENDS', 'LESSEQUAL', 'NOTEQUAL', 'DOT', @@ -695,6 +705,7 @@ Racc_token_to_s_table = [ 'hostclass', 'nodedef', 'resourceoverride', +'append', 'funcvalues', 'namestrings', 'resourcerefs', @@ -823,8 +834,10 @@ module_eval <<'.,.,', 'grammar.ra', 46 # reduce 16 omitted -module_eval <<'.,.,', 'grammar.ra', 68 - def _reduce_17( val, _values, result ) + # reduce 17 omitted + +module_eval <<'.,.,', 'grammar.ra', 69 + def _reduce_18( val, _values, result ) args = aryfy(val[2]) result = ast AST::Function, :name => val[0], @@ -834,8 +847,8 @@ module_eval <<'.,.,', 'grammar.ra', 68 end .,., -module_eval <<'.,.,', 'grammar.ra', 74 - def _reduce_18( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 75 + def _reduce_19( val, _values, result ) result = ast AST::Function, :name => val[0], :arguments => AST::ASTArray.new({}), @@ -844,8 +857,8 @@ module_eval <<'.,.,', 'grammar.ra', 74 end .,., -module_eval <<'.,.,', 'grammar.ra', 81 - def _reduce_19( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 82 + def _reduce_20( val, _values, result ) args = aryfy(val[1]) result = ast AST::Function, :name => val[0], @@ -855,14 +868,14 @@ module_eval <<'.,.,', 'grammar.ra', 81 end .,., - # reduce 20 omitted - # reduce 21 omitted # reduce 22 omitted -module_eval <<'.,.,', 'grammar.ra', 91 - def _reduce_23( val, _values, result ) + # reduce 23 omitted + +module_eval <<'.,.,', 'grammar.ra', 92 + def _reduce_24( val, _values, result ) result = aryfy(val[0], val[2]) result.line = @lexer.line result.file = @lexer.file @@ -870,8 +883,6 @@ module_eval <<'.,.,', 'grammar.ra', 91 end .,., - # reduce 24 omitted - # reduce 25 omitted # reduce 26 omitted @@ -884,17 +895,19 @@ module_eval <<'.,.,', 'grammar.ra', 91 # reduce 30 omitted -module_eval <<'.,.,', 'grammar.ra', 104 - def _reduce_31( val, _values, result ) + # reduce 31 omitted + +module_eval <<'.,.,', 'grammar.ra', 105 + def _reduce_32( val, _values, result ) result = ast AST::Name, :value => val[0] result end .,., - # reduce 32 omitted + # reduce 33 omitted -module_eval <<'.,.,', 'grammar.ra', 115 - def _reduce_33( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 116 + def _reduce_34( val, _values, result ) unless val[0].is_a?(AST::ASTArray) val[0] = aryfy(val[0]) end @@ -906,8 +919,8 @@ module_eval <<'.,.,', 'grammar.ra', 115 end .,., -module_eval <<'.,.,', 'grammar.ra', 136 - def _reduce_34( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 137 + def _reduce_35( val, _values, result ) array = val[2] if array.instance_of?(AST::ResourceInstance) array = [array] @@ -930,31 +943,31 @@ module_eval <<'.,.,', 'grammar.ra', 136 end .,., -module_eval <<'.,.,', 'grammar.ra', 139 - def _reduce_35( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 140 + def _reduce_36( val, _values, result ) # This is a deprecated syntax. error "All resource specifications require names" result end .,., -module_eval <<'.,.,', 'grammar.ra', 142 - def _reduce_36( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 143 + def _reduce_37( val, _values, result ) # a defaults setting for a type result = ast(AST::ResourceDefaults, :type => val[0], :params => val[2]) result end .,., -module_eval <<'.,.,', 'grammar.ra', 147 - def _reduce_37( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 148 + def _reduce_38( val, _values, result ) result = ast AST::ResourceOverride, :object => val[0], :params => val[2] result end .,., -module_eval <<'.,.,', 'grammar.ra', 174 - def _reduce_38( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 175 + def _reduce_39( val, _values, result ) type = val[0] if (type == :exported and ! Puppet[:storeconfigs]) and ! Puppet[:parseonly] @@ -981,22 +994,22 @@ module_eval <<'.,.,', 'grammar.ra', 174 end .,., -module_eval <<'.,.,', 'grammar.ra', 175 - def _reduce_39( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 176 + def _reduce_40( val, _values, result ) result = :virtual result end .,., -module_eval <<'.,.,', 'grammar.ra', 176 - def _reduce_40( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 177 + def _reduce_41( val, _values, result ) result = :exported result end .,., -module_eval <<'.,.,', 'grammar.ra', 199 - def _reduce_41( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 200 + def _reduce_42( val, _values, result ) if val[0] =~ /^[a-z]/ Puppet.warning addcontext("Collection names must now be capitalized") end @@ -1018,8 +1031,8 @@ module_eval <<'.,.,', 'grammar.ra', 199 end .,., -module_eval <<'.,.,', 'grammar.ra', 209 - def _reduce_42( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 210 + def _reduce_43( val, _values, result ) if val[1] result = val[1] result.form = :virtual @@ -1030,8 +1043,8 @@ module_eval <<'.,.,', 'grammar.ra', 209 end .,., -module_eval <<'.,.,', 'grammar.ra', 217 - def _reduce_43( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 218 + def _reduce_44( val, _values, result ) if val[1] result = val[1] result.form = :exported @@ -1042,33 +1055,33 @@ module_eval <<'.,.,', 'grammar.ra', 217 end .,., - # reduce 44 omitted - # reduce 45 omitted -module_eval <<'.,.,', 'grammar.ra', 225 - def _reduce_46( val, _values, result ) + # reduce 46 omitted + +module_eval <<'.,.,', 'grammar.ra', 226 + def _reduce_47( val, _values, result ) result = ast AST::CollExpr, :test1 => val[0], :oper => val[1], :test2 => val[2] result end .,., - # reduce 47 omitted + # reduce 48 omitted -module_eval <<'.,.,', 'grammar.ra', 231 - def _reduce_48( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 232 + def _reduce_49( val, _values, result ) result = val[1] result.parens = true result end .,., - # reduce 49 omitted - # reduce 50 omitted -module_eval <<'.,.,', 'grammar.ra', 239 - def _reduce_51( val, _values, result ) + # reduce 51 omitted + +module_eval <<'.,.,', 'grammar.ra', 240 + def _reduce_52( val, _values, result ) result = ast AST::CollExpr, :test1 => val[0], :oper => val[1], :test2 => val[2] #result = ast AST::CollExpr #result.push *val @@ -1076,8 +1089,8 @@ module_eval <<'.,.,', 'grammar.ra', 239 end .,., -module_eval <<'.,.,', 'grammar.ra', 244 - def _reduce_52( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 245 + def _reduce_53( val, _values, result ) result = ast AST::CollExpr, :test1 => val[0], :oper => val[1], :test2 => val[2] #result = ast AST::CollExpr #result.push *val @@ -1085,21 +1098,21 @@ module_eval <<'.,.,', 'grammar.ra', 244 end .,., - # reduce 53 omitted - # reduce 54 omitted -module_eval <<'.,.,', 'grammar.ra', 251 - def _reduce_55( val, _values, result ) + # reduce 55 omitted + +module_eval <<'.,.,', 'grammar.ra', 252 + def _reduce_56( val, _values, result ) result = ast AST::ResourceInstance, :children => [val[0],val[2]] result end .,., - # reduce 56 omitted + # reduce 57 omitted -module_eval <<'.,.,', 'grammar.ra', 261 - def _reduce_57( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 262 + def _reduce_58( val, _values, result ) if val[0].instance_of?(AST::ResourceInstance) result = ast AST::ASTArray, :children => [val[0],val[2]] else @@ -1110,33 +1123,31 @@ module_eval <<'.,.,', 'grammar.ra', 261 end .,., - # reduce 58 omitted - # reduce 59 omitted -module_eval <<'.,.,', 'grammar.ra', 268 - def _reduce_60( val, _values, result ) + # reduce 60 omitted + +module_eval <<'.,.,', 'grammar.ra', 269 + def _reduce_61( val, _values, result ) result = ast AST::Undef, :value => :undef result end .,., -module_eval <<'.,.,', 'grammar.ra', 272 - def _reduce_61( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 273 + def _reduce_62( val, _values, result ) result = ast AST::Name, :value => val[0] result end .,., -module_eval <<'.,.,', 'grammar.ra', 276 - def _reduce_62( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 277 + def _reduce_63( val, _values, result ) result = ast AST::Type, :value => val[0] result end .,., - # reduce 63 omitted - # reduce 64 omitted # reduce 65 omitted @@ -1147,8 +1158,10 @@ module_eval <<'.,.,', 'grammar.ra', 276 # reduce 68 omitted -module_eval <<'.,.,', 'grammar.ra', 292 - def _reduce_69( val, _values, result ) + # reduce 69 omitted + +module_eval <<'.,.,', 'grammar.ra', 293 + def _reduce_70( val, _values, result ) if val[0] =~ /::/ raise Puppet::ParseError, "Cannot assign to variables in other namespaces" end @@ -1159,22 +1172,30 @@ module_eval <<'.,.,', 'grammar.ra', 292 end .,., -module_eval <<'.,.,', 'grammar.ra', 297 - def _reduce_70( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 298 + def _reduce_71( val, _values, result ) + variable = ast AST::Name, :value => val[0] + result = ast AST::VarDef, :name => variable, :value => val[2], :append => true + result + end +.,., + +module_eval <<'.,.,', 'grammar.ra', 303 + def _reduce_72( val, _values, result ) result = ast AST::ASTArray result end .,., -module_eval <<'.,.,', 'grammar.ra', 297 - def _reduce_71( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 303 + def _reduce_73( val, _values, result ) result = val[0] result end .,., -module_eval <<'.,.,', 'grammar.ra', 306 - def _reduce_72( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 312 + def _reduce_74( val, _values, result ) if val[0].instance_of?(AST::ASTArray) val[0].push(val[2]) result = val[0] @@ -1185,41 +1206,41 @@ module_eval <<'.,.,', 'grammar.ra', 306 end .,., -module_eval <<'.,.,', 'grammar.ra', 310 - def _reduce_73( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 316 + def _reduce_75( val, _values, result ) result = ast AST::ResourceParam, :param => val[0], :value => val[2] result end .,., -module_eval <<'.,.,', 'grammar.ra', 315 - def _reduce_74( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 321 + def _reduce_76( val, _values, result ) result = ast AST::ResourceParam, :param => val[0], :value => val[2], :add => true result end .,., - # reduce 75 omitted + # reduce 77 omitted - # reduce 76 omitted + # reduce 78 omitted -module_eval <<'.,.,', 'grammar.ra', 323 - def _reduce_77( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 329 + def _reduce_79( val, _values, result ) result = ast AST::ASTArray result end .,., -module_eval <<'.,.,', 'grammar.ra', 323 - def _reduce_78( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 329 + def _reduce_80( val, _values, result ) result = val[0] result end .,., -module_eval <<'.,.,', 'grammar.ra', 332 - def _reduce_79( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 338 + def _reduce_81( val, _values, result ) if val[0].instance_of?(AST::ASTArray) val[0].push(val[2]) result = val[0] @@ -1230,10 +1251,10 @@ module_eval <<'.,.,', 'grammar.ra', 332 end .,., - # reduce 80 omitted + # reduce 82 omitted -module_eval <<'.,.,', 'grammar.ra', 341 - def _reduce_81( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 347 + def _reduce_83( val, _values, result ) if val[0].instance_of?(AST::ASTArray) result = val[0].push(val[2]) else @@ -1243,10 +1264,6 @@ module_eval <<'.,.,', 'grammar.ra', 341 end .,., - # reduce 82 omitted - - # reduce 83 omitted - # reduce 84 omitted # reduce 85 omitted @@ -1275,8 +1292,12 @@ module_eval <<'.,.,', 'grammar.ra', 341 # reduce 97 omitted -module_eval <<'.,.,', 'grammar.ra', 368 - def _reduce_98( val, _values, result ) + # reduce 98 omitted + + # reduce 99 omitted + +module_eval <<'.,.,', 'grammar.ra', 374 + def _reduce_100( val, _values, result ) args = aryfy(val[2]) result = ast AST::Function, :name => val[0], @@ -1286,8 +1307,8 @@ module_eval <<'.,.,', 'grammar.ra', 368 end .,., -module_eval <<'.,.,', 'grammar.ra', 373 - def _reduce_99( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 379 + def _reduce_101( val, _values, result ) result = ast AST::Function, :name => val[0], :arguments => AST::ASTArray.new({}), @@ -1296,44 +1317,44 @@ module_eval <<'.,.,', 'grammar.ra', 373 end .,., -module_eval <<'.,.,', 'grammar.ra', 377 - def _reduce_100( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 383 + def _reduce_102( val, _values, result ) result = ast AST::String, :value => val[0] result end .,., -module_eval <<'.,.,', 'grammar.ra', 379 - def _reduce_101( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 385 + def _reduce_103( val, _values, result ) result = ast AST::FlatString, :value => val[0] result end .,., -module_eval <<'.,.,', 'grammar.ra', 383 - def _reduce_102( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 389 + def _reduce_104( val, _values, result ) result = ast AST::Boolean, :value => val[0] result end .,., -module_eval <<'.,.,', 'grammar.ra', 388 - def _reduce_103( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 394 + def _reduce_105( val, _values, result ) Puppet.warning addcontext("Deprecation notice: Resource references should now be capitalized") result = ast AST::ResourceReference, :type => val[0], :title => val[2] result end .,., -module_eval <<'.,.,', 'grammar.ra', 390 - def _reduce_104( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 396 + def _reduce_106( val, _values, result ) result = ast AST::ResourceReference, :type => val[0], :title => val[2] result end .,., -module_eval <<'.,.,', 'grammar.ra', 403 - def _reduce_105( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 409 + def _reduce_107( val, _values, result ) args = { :test => val[1], :statements => val[3] @@ -1348,19 +1369,19 @@ module_eval <<'.,.,', 'grammar.ra', 403 end .,., - # reduce 106 omitted + # reduce 108 omitted -module_eval <<'.,.,', 'grammar.ra', 408 - def _reduce_107( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 414 + def _reduce_109( val, _values, result ) result = ast AST::Else, :statements => val[2] result end .,., - # reduce 108 omitted + # reduce 110 omitted -module_eval <<'.,.,', 'grammar.ra', 420 - def _reduce_109( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 426 + def _reduce_111( val, _values, result ) options = val[3] unless options.instance_of?(AST::ASTArray) options = ast AST::ASTArray, :children => [val[3]] @@ -1370,10 +1391,10 @@ module_eval <<'.,.,', 'grammar.ra', 420 end .,., - # reduce 110 omitted + # reduce 112 omitted -module_eval <<'.,.,', 'grammar.ra', 430 - def _reduce_111( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 436 + def _reduce_113( val, _values, result ) if val[0].instance_of?(AST::ASTArray) val[0].push val[1] result = val[0] @@ -1384,15 +1405,15 @@ module_eval <<'.,.,', 'grammar.ra', 430 end .,., -module_eval <<'.,.,', 'grammar.ra', 434 - def _reduce_112( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 440 + def _reduce_114( val, _values, result ) result = ast AST::CaseOpt, :value => val[0], :statements => val[3] result end .,., -module_eval <<'.,.,', 'grammar.ra', 439 - def _reduce_113( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 445 + def _reduce_115( val, _values, result ) result = ast(AST::CaseOpt, :value => val[0], :statements => ast(AST::ASTArray) @@ -1401,10 +1422,10 @@ module_eval <<'.,.,', 'grammar.ra', 439 end .,., - # reduce 114 omitted + # reduce 116 omitted -module_eval <<'.,.,', 'grammar.ra', 449 - def _reduce_115( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 455 + def _reduce_117( val, _values, result ) if val[0].instance_of?(AST::ASTArray) val[0].push(val[2]) result = val[0] @@ -1415,26 +1436,26 @@ module_eval <<'.,.,', 'grammar.ra', 449 end .,., -module_eval <<'.,.,', 'grammar.ra', 453 - def _reduce_116( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 459 + def _reduce_118( val, _values, result ) result = ast AST::Selector, :param => val[0], :values => val[2] result end .,., - # reduce 117 omitted + # reduce 119 omitted -module_eval <<'.,.,', 'grammar.ra', 455 - def _reduce_118( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 461 + def _reduce_120( val, _values, result ) result = val[1] result end .,., - # reduce 119 omitted + # reduce 121 omitted -module_eval <<'.,.,', 'grammar.ra', 466 - def _reduce_120( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 472 + def _reduce_122( val, _values, result ) if val[0].instance_of?(AST::ASTArray) val[0].push(val[2]) result = val[0] @@ -1445,17 +1466,13 @@ module_eval <<'.,.,', 'grammar.ra', 466 end .,., -module_eval <<'.,.,', 'grammar.ra', 470 - def _reduce_121( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 476 + def _reduce_123( val, _values, result ) result = ast AST::ResourceParam, :param => val[0], :value => val[2] result end .,., - # reduce 122 omitted - - # reduce 123 omitted - # reduce 124 omitted # reduce 125 omitted @@ -1466,29 +1483,33 @@ module_eval <<'.,.,', 'grammar.ra', 470 # reduce 128 omitted -module_eval <<'.,.,', 'grammar.ra', 481 - def _reduce_129( val, _values, result ) + # reduce 129 omitted + + # reduce 130 omitted + +module_eval <<'.,.,', 'grammar.ra', 487 + def _reduce_131( val, _values, result ) result = ast AST::Default, :value => val[0] result end .,., -module_eval <<'.,.,', 'grammar.ra', 483 - def _reduce_130( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 489 + def _reduce_132( val, _values, result ) result = [val[0].value] result end .,., -module_eval <<'.,.,', 'grammar.ra', 487 - def _reduce_131( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 493 + def _reduce_133( val, _values, result ) results = val[0] << val[2].value result end .,., -module_eval <<'.,.,', 'grammar.ra', 495 - def _reduce_132( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 501 + def _reduce_134( val, _values, result ) val[1].each do |file| import(file) end @@ -1498,8 +1519,8 @@ module_eval <<'.,.,', 'grammar.ra', 495 end .,., -module_eval <<'.,.,', 'grammar.ra', 505 - def _reduce_133( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 511 + def _reduce_135( val, _values, result ) newdefine classname(val[1]), :arguments => val[2], :code => val[4] @lexer.indefine = false result = nil @@ -1509,8 +1530,8 @@ module_eval <<'.,.,', 'grammar.ra', 505 end .,., -module_eval <<'.,.,', 'grammar.ra', 509 - def _reduce_134( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 515 + def _reduce_136( val, _values, result ) newdefine classname(val[1]), :arguments => val[2] @lexer.indefine = false result = nil @@ -1518,8 +1539,8 @@ module_eval <<'.,.,', 'grammar.ra', 509 end .,., -module_eval <<'.,.,', 'grammar.ra', 517 - def _reduce_135( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 523 + def _reduce_137( val, _values, result ) # Our class gets defined in the parent namespace, not our own. @lexer.namepop newclass classname(val[1]), :code => val[4], :parent => val[2] @@ -1528,8 +1549,8 @@ module_eval <<'.,.,', 'grammar.ra', 517 end .,., -module_eval <<'.,.,', 'grammar.ra', 522 - def _reduce_136( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 528 + def _reduce_138( val, _values, result ) # Our class gets defined in the parent namespace, not our own. @lexer.namepop newclass classname(val[1]), :parent => val[2] @@ -1538,32 +1559,32 @@ module_eval <<'.,.,', 'grammar.ra', 522 end .,., -module_eval <<'.,.,', 'grammar.ra', 527 - def _reduce_137( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 533 + def _reduce_139( val, _values, result ) newnode val[1], :parent => val[2], :code => val[4] result = nil result end .,., -module_eval <<'.,.,', 'grammar.ra', 530 - def _reduce_138( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 536 + def _reduce_140( val, _values, result ) newnode val[1], :parent => val[2] result = nil result end .,., - # reduce 139 omitted - - # reduce 140 omitted - # reduce 141 omitted # reduce 142 omitted -module_eval <<'.,.,', 'grammar.ra', 544 - def _reduce_143( val, _values, result ) + # reduce 143 omitted + + # reduce 144 omitted + +module_eval <<'.,.,', 'grammar.ra', 550 + def _reduce_145( val, _values, result ) result = val[0] result = [result] unless result.is_a?(Array) result << val[2] @@ -1571,49 +1592,49 @@ module_eval <<'.,.,', 'grammar.ra', 544 end .,., - # reduce 144 omitted - - # reduce 145 omitted - # reduce 146 omitted # reduce 147 omitted -module_eval <<'.,.,', 'grammar.ra', 553 - def _reduce_148( val, _values, result ) + # reduce 148 omitted + + # reduce 149 omitted + +module_eval <<'.,.,', 'grammar.ra', 559 + def _reduce_150( val, _values, result ) result = nil result end .,., -module_eval <<'.,.,', 'grammar.ra', 557 - def _reduce_149( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 563 + def _reduce_151( val, _values, result ) result = ast AST::ASTArray, :children => [] result end .,., - # reduce 150 omitted + # reduce 152 omitted -module_eval <<'.,.,', 'grammar.ra', 562 - def _reduce_151( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 568 + def _reduce_153( val, _values, result ) result = nil result end .,., -module_eval <<'.,.,', 'grammar.ra', 566 - def _reduce_152( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 572 + def _reduce_154( val, _values, result ) result = val[1] result = [result] unless result[0].is_a?(Array) result end .,., - # reduce 153 omitted + # reduce 155 omitted -module_eval <<'.,.,', 'grammar.ra', 573 - def _reduce_154( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 579 + def _reduce_156( val, _values, result ) result = val[0] result = [result] unless result[0].is_a?(Array) result << val[2] @@ -1621,67 +1642,67 @@ module_eval <<'.,.,', 'grammar.ra', 573 end .,., -module_eval <<'.,.,', 'grammar.ra', 578 - def _reduce_155( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 584 + def _reduce_157( val, _values, result ) Puppet.warning addcontext("Deprecation notice: must now include '$' in prototype") result = [val[0], val[2]] result end .,., -module_eval <<'.,.,', 'grammar.ra', 582 - def _reduce_156( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 588 + def _reduce_158( val, _values, result ) Puppet.warning addcontext("Deprecation notice: must now include '$' in prototype") result = [val[0]] result end .,., -module_eval <<'.,.,', 'grammar.ra', 584 - def _reduce_157( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 590 + def _reduce_159( val, _values, result ) result = [val[0], val[2]] result end .,., -module_eval <<'.,.,', 'grammar.ra', 586 - def _reduce_158( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 592 + def _reduce_160( val, _values, result ) result = [val[0]] result end .,., - # reduce 159 omitted + # reduce 161 omitted -module_eval <<'.,.,', 'grammar.ra', 591 - def _reduce_160( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 597 + def _reduce_162( val, _values, result ) result = val[1] result end .,., - # reduce 161 omitted + # reduce 163 omitted -module_eval <<'.,.,', 'grammar.ra', 596 - def _reduce_162( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 602 + def _reduce_164( val, _values, result ) result = val[1] result end .,., - # reduce 163 omitted + # reduce 165 omitted - # reduce 164 omitted + # reduce 166 omitted -module_eval <<'.,.,', 'grammar.ra', 602 - def _reduce_165( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 608 + def _reduce_167( val, _values, result ) result = ast AST::Variable, :value => val[0] result end .,., -module_eval <<'.,.,', 'grammar.ra', 610 - def _reduce_166( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 616 + def _reduce_168( val, _values, result ) if val[1].instance_of?(AST::ASTArray) result = val[1] else @@ -1691,21 +1712,21 @@ module_eval <<'.,.,', 'grammar.ra', 610 end .,., -module_eval <<'.,.,', 'grammar.ra', 612 - def _reduce_167( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 618 + def _reduce_169( val, _values, result ) result = ast AST::ASTArray result end .,., - # reduce 168 omitted + # reduce 170 omitted - # reduce 169 omitted + # reduce 171 omitted - # reduce 170 omitted + # reduce 172 omitted -module_eval <<'.,.,', 'grammar.ra', 617 - def _reduce_171( val, _values, result ) +module_eval <<'.,.,', 'grammar.ra', 623 + def _reduce_173( val, _values, result ) result = nil result end diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb index 32b127a6b..1ff998d96 100644 --- a/lib/puppet/parser/scope.rb +++ b/lib/puppet/parser/scope.rb @@ -260,11 +260,15 @@ class Puppet::Parser::Scope # Set a variable in the current scope. This will override settings # in scopes above, but will not allow variables in the current scope # to be reassigned. - def setvar(name,value, file = nil, line = nil) - #Puppet.debug "Setting %s to '%s' at level %s" % - # [name.inspect,value,self.level] + def setvar(name,value, file = nil, line = nil, append = false) + #Puppet.debug "Setting %s to '%s' at level %s mode append %s" % + # [name.inspect,value,self.level, append] if @symtable.include?(name) - error = Puppet::ParseError.new("Cannot reassign variable %s" % name) + unless append + error = Puppet::ParseError.new("Cannot reassign variable %s" % name) + else + error = Puppet::ParseError.new("Cannot append, variable %s is defined in this scope" % name) + end if file error.file = file end @@ -273,7 +277,19 @@ class Puppet::Parser::Scope end raise error end - @symtable[name] = value + + unless append + @symtable[name] = value + else # append case + # lookup the value in the scope if it exists and insert the var + @symtable[name] = lookupvar(name) + # concatenate if string, append if array, nothing for other types + if value.is_a?(Array) + @symtable[name] += value + else + @symtable[name] << value + end + end end # Return an interpolated string. -- cgit From 7a3a38f58c099244c2a8b490f0b69c2fa63f3e16 Mon Sep 17 00:00:00 2001 From: Brice Figureau Date: Sat, 20 Sep 2008 14:14:44 +0200 Subject: Add rspec unit test for the append operator Signed-off-by: Brice Figureau --- CHANGELOG | 2 ++ spec/unit/parser/ast/vardef.rb | 47 ++++++++++++++++++++++++++++++++++++++++++ spec/unit/parser/lexer.rb | 3 ++- spec/unit/parser/parser.rb | 34 ++++++++++++++++++++++++++++++ spec/unit/parser/scope.rb | 37 +++++++++++++++++++++++++++++++++ test/data/snippets/append.pp | 11 ++++++++++ 6 files changed, 133 insertions(+), 1 deletion(-) create mode 100755 spec/unit/parser/ast/vardef.rb create mode 100755 spec/unit/parser/parser.rb create mode 100755 spec/unit/parser/scope.rb create mode 100644 test/data/snippets/append.pp diff --git a/CHANGELOG b/CHANGELOG index 74c8a93d7..cbbc2573f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,6 @@ 0.24.x + Fixed #1584 - Added support for appended variables + Fixed #1554 - Added support for multiple template directories Fixed #1500 - puppetrun not working diff --git a/spec/unit/parser/ast/vardef.rb b/spec/unit/parser/ast/vardef.rb new file mode 100755 index 000000000..6bd355c89 --- /dev/null +++ b/spec/unit/parser/ast/vardef.rb @@ -0,0 +1,47 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::VarDef do + before :each do + @scope = Puppet::Parser::Scope.new() + end + + describe "when evaluating" do + + it "should evaluate arguments" do + name = mock 'name' + value = mock 'value' + + name.expects(:safeevaluate).with(@scope) + value.expects(:safeevaluate).with(@scope) + + vardef = Puppet::Parser::AST::VarDef.new :name => name, :value => value, :file => nil, + :line => nil + vardef.evaluate(@scope) + end + + it "should be in append=false mode if called without append" do + name = stub 'name', :safeevaluate => "var" + value = stub 'value', :safeevaluate => "1" + + @scope.expects(:setvar).with { |name,value,file,line,append| append == nil } + + vardef = Puppet::Parser::AST::VarDef.new :name => name, :value => value, :file => nil, + :line => nil + vardef.evaluate(@scope) + end + + it "should call scope in append mode if append is true" do + name = stub 'name', :safeevaluate => "var" + value = stub 'value', :safeevaluate => "1" + + @scope.expects(:setvar).with { |name,value,file,line,append| append == true } + + vardef = Puppet::Parser::AST::VarDef.new :name => name, :value => value, :file => nil, + :line => nil, :append => true + vardef.evaluate(@scope) + end + + end +end diff --git a/spec/unit/parser/lexer.rb b/spec/unit/parser/lexer.rb index fb666054d..fed1ade7d 100755 --- a/spec/unit/parser/lexer.rb +++ b/spec/unit/parser/lexer.rb @@ -135,7 +135,8 @@ describe Puppet::Parser::Lexer::TOKENS do :QMARK => '?', :BACKSLASH => '\\', :FARROW => '=>', - :PARROW => '+>' + :PARROW => '+>', + :APPENDS => '+=' }.each do |name, string| it "should have a token named #{name.to_s}" do Puppet::Parser::Lexer::TOKENS[name].should_not be_nil diff --git a/spec/unit/parser/parser.rb b/spec/unit/parser/parser.rb new file mode 100755 index 000000000..94b19be40 --- /dev/null +++ b/spec/unit/parser/parser.rb @@ -0,0 +1,34 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +describe Puppet::Parser do + + AST = Puppet::Parser::AST + + before :each do + @parser = Puppet::Parser::Parser.new :environment => "development" + end + + describe "when parsing append operator" do + + it "should not raise syntax errors" do + lambda { @parser.parse("$var += something") }.should_not raise_error + end + + it "shouldraise syntax error on incomplete syntax " do + lambda { @parser.parse("$var += ") }.should raise_error + end + + it "should call AST::VarDef with append=true" do + AST::VarDef.expects(:new).with { |h| h[:append] == true } + @parser.parse("$var += 2") + end + + it "should work with arrays too" do + AST::VarDef.expects(:new).with { |h| h[:append] == true } + @parser.parse("$var += ['test']") + end + + end +end diff --git a/spec/unit/parser/scope.rb b/spec/unit/parser/scope.rb new file mode 100755 index 000000000..ec8ab6d7d --- /dev/null +++ b/spec/unit/parser/scope.rb @@ -0,0 +1,37 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +describe Puppet::Parser::Scope do + before :each do + @scope = Puppet::Parser::Scope.new() + @topscope = Puppet::Parser::Scope.new() + @scope.stubs(:parent).returns(@topscope) + end + + describe Puppet::Parser::Scope, "when setvar is called with append=true" do + + it "should raise error if the variable is already defined in this scope" do + @scope.setvar("var","1",nil,nil,false) + lambda { @scope.setvar("var","1",nil,nil,true) }.should raise_error(Puppet::ParseError) + end + + it "it should lookup current variable value" do + @scope.expects(:lookupvar).with("var").returns("2") + @scope.setvar("var","1",nil,nil,true) + end + + it "it should store the concatenated string '42'" do + @topscope.setvar("var","4",nil,nil,false) + @scope.setvar("var","2",nil,nil,true) + @scope.lookupvar("var").should == "42" + end + + it "it should store the concatenated array [4,2]" do + @topscope.setvar("var",[4],nil,nil,false) + @scope.setvar("var",[2],nil,nil,true) + @scope.lookupvar("var").should == [4,2] + end + + end +end diff --git a/test/data/snippets/append.pp b/test/data/snippets/append.pp new file mode 100644 index 000000000..28edeb177 --- /dev/null +++ b/test/data/snippets/append.pp @@ -0,0 +1,11 @@ +$var=['/tmp/file1','/tmp/file2'] + +class arraytest { + $var += ['/tmp/file3', '/tmp/file4'] + file { + $var: + content => "test" + } +} + +include arraytest -- cgit From 27f0c7d6e7bf3400ccecc6512d6a5d477cb9bea9 Mon Sep 17 00:00:00 2001 From: Andrew Shafer Date: Sat, 20 Sep 2008 22:00:19 -0600 Subject: fix failing hpux user specs feature 1508 Not sure these tests ever passed on any platform. Added some mocking to support the two tests. --- spec/unit/provider/user/hpux.rb | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/spec/unit/provider/user/hpux.rb b/spec/unit/provider/user/hpux.rb index 8b31658b8..4129a7ab6 100755 --- a/spec/unit/provider/user/hpux.rb +++ b/spec/unit/provider/user/hpux.rb @@ -7,23 +7,19 @@ provider_class = Puppet::Type.type(:user).provider(:hpuxuseradd) describe provider_class do # left from the useradd test... I have no clue what I'm doing. before do - @resource = stub("resource", :name => "myuser", :managehome? => nil) + @resource = stub("resource", :name => "myuser", :managehome? => nil, :should => "fakeval", :[] => "fakeval") @provider = provider_class.new(@resource) end it "should add -F when modifying a user" do - @resource.stubs(:should).returns "fakeval" - @resource.stubs(:[]).returns "fakeval" + @resource.expects(:allowdupe?).returns true @provider.expects(:execute).with { |args| args.include?("-F") } - - @provider.modify + @provider.uid = 1000 end it "should add -F when deleting a user" do - @resource.stubs(:should).returns "fakeval" - @resource.stubs(:[]).returns "fakeval" + @provider.stubs(:exists?).returns(true) @provider.expects(:execute).with { |args| args.include?("-F") } - @provider.delete end end -- cgit From c16a5aee245a9e34e6934debee8e66630aef0fda Mon Sep 17 00:00:00 2001 From: Andrew Shafer Date: Fri, 19 Sep 2008 17:00:03 -0600 Subject: Only apply splay the first run Issue 1491 --- lib/puppet/network/client/master.rb | 16 +++++++------- spec/unit/network/client/master.rb | 42 +++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/lib/puppet/network/client/master.rb b/lib/puppet/network/client/master.rb index 6f8e2770f..5e9422b7e 100644 --- a/lib/puppet/network/client/master.rb +++ b/lib/puppet/network/client/master.rb @@ -199,6 +199,7 @@ class Puppet::Network::Client::Master < Puppet::Network::Client self.class.instance = self @running = false + @splayed = false end # Mark that we should restart. The Puppet module checks whether we're running, @@ -478,20 +479,19 @@ class Puppet::Network::Client::Master < Puppet::Network::Client @lockfile end + def splayed? + @splayed + end + # Sleep when splay is enabled; else just return. def splay return unless Puppet[:splay] + return if splayed? - limit = Integer(Puppet[:splaylimit]) - - # Pick a splay time and then cache it. - unless time = Puppet::Util::Storage.cache(:configuration)[:splay_time] - time = rand(limit) - Puppet::Util::Storage.cache(:configuration)[:splay_time] = time - end - + time = rand(Integer(Puppet[:splaylimit])) Puppet.info "Sleeping for %s seconds (splay is enabled)" % time sleep(time) + @splayed = true end private diff --git a/spec/unit/network/client/master.rb b/spec/unit/network/client/master.rb index 754fd0583..f55ba316c 100755 --- a/spec/unit/network/client/master.rb +++ b/spec/unit/network/client/master.rb @@ -397,4 +397,46 @@ describe Puppet::Network::Client::Master, " when using the cached catalog" do @client.catalog.should equal(ral_config) end + + describe "when calling splay" do + it "should do nothing if splay is not enabled" do + Puppet.stubs(:[]).with(:splay).returns(false) + @client.expects(:rand).never + @client.send(:splay) + end + + describe "when splay is enabled" do + before do + Puppet.stubs(:[]).with(:splay).returns(true) + Puppet.stubs(:[]).with(:splaylimit).returns(42) + end + + it "should sleep for a random time" do + @client.expects(:rand).with(42).returns(42) + @client.expects(:sleep).with(42) + @client.send(:splay) + end + + it "should inform that it is splayed" do + @client.stubs(:rand).with(42).returns(42) + @client.stubs(:sleep).with(42) + Puppet.expects(:info) + @client.send(:splay) + end + + it "should set splay = true" do + @client.stubs(:rand).with(42).returns(42) + @client.stubs(:sleep).with(42) + @client.send(:splay) + @client.send(:splayed?).should == true + end + + it "should do nothing if already splayed" do + @client.stubs(:rand).with(42).returns(42).at_most_once + @client.stubs(:sleep).with(42).at_most_once + @client.send(:splay) + @client.send(:splay) + end + end + end end -- cgit From 5fbdc49dfdb39351c7f2d9e535577efc177cf838 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Tue, 23 Sep 2008 14:43:38 -0500 Subject: Fixed #1595 - Internally, Property#retrieve is no longer called when no 'should' value is available for a resource. --- CHANGELOG | 3 +++ lib/puppet/parameter.rb | 24 +----------------------- lib/puppet/property.rb | 5 +++++ spec/unit/parameter.rb | 24 ++++++++++++++++++++++++ spec/unit/property.rb | 24 ++++++++++++++++++++++++ 5 files changed, 57 insertions(+), 23 deletions(-) create mode 100755 spec/unit/parameter.rb create mode 100755 spec/unit/property.rb diff --git a/CHANGELOG b/CHANGELOG index cbbc2573f..2b61921d4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,7 @@ 0.24.x + Fixed #1595 - Internally, Property#retrieve is no longer called + when no 'should' value is available for a resource. + Fixed #1584 - Added support for appended variables Fixed #1554 - Added support for multiple template directories diff --git a/lib/puppet/parameter.rb b/lib/puppet/parameter.rb index 31e009af5..ef7f1c1ad 100644 --- a/lib/puppet/parameter.rb +++ b/lib/puppet/parameter.rb @@ -413,29 +413,7 @@ class Puppet::Parameter @shadow = nil end - # This should only be called for parameters, but go ahead and make - # it possible to call for properties, too. - def value - if self.is_a?(Puppet::Property) - # We should return the 'is' value if there's not 'should' - # value. This might be bad, though, because the 'should' - # method knows whether to return an array or not and that info - # is not exposed, and the 'is' value could be a symbol. I - # can't seem to create a test in which this is a problem, but - # that doesn't mean it's not one. - if self.should - return self.should - else - return self.retrieve - end - else - if defined? @value - return @value - else - return nil - end - end - end + attr_reader :value # Store the value provided. All of the checking should possibly be # late-binding (e.g., users might not exist when the value is assigned diff --git a/lib/puppet/property.rb b/lib/puppet/property.rb index 9e8bae7a4..913f43977 100644 --- a/lib/puppet/property.rb +++ b/lib/puppet/property.rb @@ -441,6 +441,11 @@ class Property < Puppet::Parameter return "%s(%s)" % [@resource.name,self.name] end + # Just return any should value we might have. + def value + self.should + end + # Provide a common hook for setting @should, just like params. def value=(value) self.should = value diff --git a/spec/unit/parameter.rb b/spec/unit/parameter.rb new file mode 100755 index 000000000..d6858c29d --- /dev/null +++ b/spec/unit/parameter.rb @@ -0,0 +1,24 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../spec_helper' + +require 'puppet/parameter' + +describe Puppet::Parameter do + describe "when returning the value" do + before do + @class = Class.new(Puppet::Parameter) + @class.initvars + @parameter = @class.new :resource => mock('resource') + end + + it "should return nil if no value is set" do + @parameter.value.should be_nil + end + + it "should return any set value" do + @parameter.value = "foo" + @parameter.value.should == "foo" + end + end +end diff --git a/spec/unit/property.rb b/spec/unit/property.rb new file mode 100755 index 000000000..a562f8bb4 --- /dev/null +++ b/spec/unit/property.rb @@ -0,0 +1,24 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../spec_helper' + +require 'puppet/property' + +describe Puppet::Property do + describe "when returning the value" do + before do + @class = Class.new(Puppet::Property) + @class.initvars + @property = @class.new :resource => mock('resource') + end + + it "should return nil if no value is set" do + @property.value.should be_nil + end + + it "should return any set 'should' value" do + @property.should = "foo" + @property.value.should == "foo" + end + end +end -- cgit From 8d5ded09b9c9c944695c015e6e95b10ccebd6fb5 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Tue, 23 Sep 2008 14:52:57 -0500 Subject: Removing some code in Parameter that is unnecessary. It's duplicated in Property, but was only ever called if the instance was Property -- in other words, the base class new about its subclass, but the subclass overrode that method any way. Signed-off-by: Luke Kanies --- lib/puppet/parameter.rb | 5 ----- lib/puppet/property.rb | 3 ++- spec/unit/property.rb | 11 +++++++++++ 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/puppet/parameter.rb b/lib/puppet/parameter.rb index ef7f1c1ad..f90193fc8 100644 --- a/lib/puppet/parameter.rb +++ b/lib/puppet/parameter.rb @@ -419,11 +419,6 @@ class Puppet::Parameter # late-binding (e.g., users might not exist when the value is assigned # but might when it is asked for). def value=(value) - # If we're a parameter, just hand the processing off to the should - # method. - if self.is_a?(Puppet::Property) - return self.should = value - end if respond_to?(:validate) validate(value) end diff --git a/lib/puppet/property.rb b/lib/puppet/property.rb index 913f43977..50a1b71de 100644 --- a/lib/puppet/property.rb +++ b/lib/puppet/property.rb @@ -446,7 +446,8 @@ class Property < Puppet::Parameter self.should end - # Provide a common hook for setting @should, just like params. + # Match the Parameter interface, but we really just use 'should' internally. + # Note that the should= method does all of the validation and such. def value=(value) self.should = value end diff --git a/spec/unit/property.rb b/spec/unit/property.rb index a562f8bb4..e5b1e0013 100755 --- a/spec/unit/property.rb +++ b/spec/unit/property.rb @@ -5,6 +5,17 @@ require File.dirname(__FILE__) + '/../spec_helper' require 'puppet/property' describe Puppet::Property do + describe "when setting the value" do + it "should just set the 'should' value" do + @class = Class.new(Puppet::Property) + @class.initvars + @property = @class.new :resource => mock('resource') + + @property.expects(:should=).with("foo") + @property.value = "foo" + end + end + describe "when returning the value" do before do @class = Class.new(Puppet::Property) -- cgit