summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/puppet/parser/ast/collection.rb17
-rw-r--r--lib/puppet/parser/ast/component.rb8
-rw-r--r--lib/puppet/parser/ast/hostclass.rb3
-rw-r--r--lib/puppet/parser/ast/objectdef.rb8
-rw-r--r--lib/puppet/parser/interpreter.rb41
-rw-r--r--lib/puppet/parser/scope.rb87
-rw-r--r--lib/puppet/rails.rb25
-rw-r--r--lib/puppet/rails/rails_object.rb2
-rwxr-xr-xtest/language/interpreter.rb12
-rw-r--r--test/language/parser.rb1
-rwxr-xr-xtest/language/rails.rb8
-rwxr-xr-xtest/language/scope.rb88
-rw-r--r--test/puppettest.rb1
13 files changed, 240 insertions, 61 deletions
diff --git a/lib/puppet/parser/ast/collection.rb b/lib/puppet/parser/ast/collection.rb
index e3afbbd8f..030f1c239 100644
--- a/lib/puppet/parser/ast/collection.rb
+++ b/lib/puppet/parser/ast/collection.rb
@@ -13,7 +13,20 @@ class Puppet::Parser::AST
count = 0
# Now perform the actual collection, yo.
+
+ # First get everything from the export table.
+
+ # FIXME This will only find objects that are before us in the tree,
+ # which is a problem.
+ objects = scope.exported(type)
+
+ array = objects.values
+
Puppet::Rails::RailsObject.find_all_by_collectable(true).each do |obj|
+ if objects.include?(obj.name)
+ debug("%s[%s] is already exported" % [type, obj.name])
+ next
+ end
count += 1
trans = obj.to_trans
@@ -30,13 +43,15 @@ class Puppet::Parser::AST
args[:arguments] = {}
trans.each do |p,v| args[:arguments][p] = v end
-
+
# XXX Because the scopes don't expect objects to return values,
# we have to manually add our objects to the scope. This is
# uber-lame.
scope.setobject(args)
end
+
+
scope.debug("Collected %s objects of type %s" %
[count, type])
diff --git a/lib/puppet/parser/ast/component.rb b/lib/puppet/parser/ast/component.rb
index 5cf3f5c57..317c8ced5 100644
--- a/lib/puppet/parser/ast/component.rb
+++ b/lib/puppet/parser/ast/component.rb
@@ -10,7 +10,7 @@ class Puppet::Parser::AST
# The class name
@name = :component
- attr_accessor :type, :args, :code, :scope, :keyword
+ attr_accessor :type, :args, :code, :scope, :keyword, :collectable
#def evaluate(scope,hash,objtype,objname)
def evaluate(hash)
@@ -19,6 +19,8 @@ class Puppet::Parser::AST
objname = hash[:name]
arguments = hash[:arguments] || {}
+ @collectable = hash[:collectable]
+
pscope = origscope
#pscope = if ! Puppet[:lexical] or hash[:asparent] == false
# origscope
@@ -32,6 +34,10 @@ class Puppet::Parser::AST
)
newcontext = hash[:newcontext]
+ if @collectable or origscope.collectable
+ scope.collectable = true
+ end
+
unless self.is_a? AST::HostClass and ! newcontext
#scope.warning "Setting context to %s" % self.object_id
scope.context = self.object_id
diff --git a/lib/puppet/parser/ast/hostclass.rb b/lib/puppet/parser/ast/hostclass.rb
index 7f381db2a..f66078d7a 100644
--- a/lib/puppet/parser/ast/hostclass.rb
+++ b/lib/puppet/parser/ast/hostclass.rb
@@ -148,7 +148,8 @@ class Puppet::Parser::AST
:scope => scope,
:arguments => args,
:name => name,
- :asparent => true
+ :asparent => true,
+ :collectable => self.collectable
)
else
return false
diff --git a/lib/puppet/parser/ast/objectdef.rb b/lib/puppet/parser/ast/objectdef.rb
index f15a082e2..7ccc9851a 100644
--- a/lib/puppet/parser/ast/objectdef.rb
+++ b/lib/puppet/parser/ast/objectdef.rb
@@ -91,13 +91,9 @@ class Puppet::Parser::AST
:name => objname,
:arguments => hash,
:file => @file,
- :line => @line
+ :line => @line,
+ :collectable => self.collectable
)
-
- # Retain our collectable marking
- if self.collectable
- obj.collectable = true
- end
rescue Puppet::ParseError => except
except.line = self.line
except.file = self.file
diff --git a/lib/puppet/parser/interpreter.rb b/lib/puppet/parser/interpreter.rb
index ee447076f..9bc1cca43 100644
--- a/lib/puppet/parser/interpreter.rb
+++ b/lib/puppet/parser/interpreter.rb
@@ -85,6 +85,18 @@ module Puppet
@local = hash[:Local] || false
+ if hash.include?(:ForkSave)
+ @forksave = hash[:ForkSave]
+ else
+ # This is just too dangerous right now. Sorry, it's going
+ # to have to be slow.
+ @forksave = false
+ end
+
+ if Puppet[:storeconfigs]
+ Puppet::Rails.init
+ end
+
# Create our parser object
parsefiles
end
@@ -250,13 +262,28 @@ module Puppet
Puppet::Rails.init
- # We store all of the objects, even the collectable ones
- benchmark(:info, "Stored configuration for #{client}") do
- Puppet::Rails::Host.store(
- :objects => objects,
- :host => client,
- :facts => facts
- )
+ # Fork the storage, since we don't need the client waiting
+ # on that. How do I avoid this duplication?
+ if @forksave
+ fork {
+ # We store all of the objects, even the collectable ones
+ benchmark(:info, "Stored configuration for #{client}") do
+ Puppet::Rails::Host.store(
+ :objects => objects,
+ :host => client,
+ :facts => facts
+ )
+ end
+ }
+ else
+ # We store all of the objects, even the collectable ones
+ benchmark(:info, "Stored configuration for #{client}") do
+ Puppet::Rails::Host.store(
+ :objects => objects,
+ :host => client,
+ :facts => facts
+ )
+ end
end
# Now that we've stored everything, we need to strip out
diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb
index 54f0f42ad..baef76bfa 100644
--- a/lib/puppet/parser/scope.rb
+++ b/lib/puppet/parser/scope.rb
@@ -20,11 +20,11 @@ module Puppet::Parser
attr_accessor :parent, :level, :interp
attr_accessor :name, :type, :topscope, :base, :keyword
- attr_accessor :top, :context, :translated
+ attr_accessor :top, :context, :translated, :collectable
# This is probably not all that good of an idea, but...
# This way a parent can share its tables with all of its children.
- attr_writer :nodetable, :classtable, :definedtable
+ attr_writer :nodetable, :classtable, :definedtable, :exportable
# Whether we behave declaratively. Note that it's a class variable,
# so all scopes behave the same.
@@ -150,6 +150,12 @@ module Puppet::Parser
raise Puppet::DevError, "No classtable has been defined"
end
+ if defined? @exportable
+ scope.exportable = @exportable
+ else
+ raise Puppet::DevError, "No exportable has been defined"
+ end
+
if defined? @definedtable
scope.definedtable = @definedtable
else
@@ -304,6 +310,31 @@ module Puppet::Parser
return bucket
end
+ # Return the hash of objects that we specifically exported. We return
+ # a hash to make it easy for the caller to deduplicate based on name.
+ def exported(type)
+ if @exportable.include?(type)
+ return @exportable[type].dup
+ else
+ return {}
+ end
+ end
+
+ # Store our object in the central export table.
+ def exportobject(obj)
+ if @exportable.include?(obj.type) and
+ @exportable[obj.type].include?(obj.name)
+ raise Puppet::ParseError, "Object %s[%s] is already exported" %
+ [obj.type, obj.name]
+ end
+
+ debug "Exporting %s[%s]" % [obj.type, obj.name]
+
+ @exportable[obj.type][obj.name] = obj
+
+ return obj
+ end
+
# Pull in all of the appropriate classes and evaluate them. It'd
# be nice if this didn't know quite so much about how AST::Node
# operated internally. This is used when a list of classes is passed in,
@@ -433,6 +464,11 @@ module Puppet::Parser
# A table for storing nodes.
@nodetable = Hash.new(nil)
+ # The list of objects that will available for export.
+ @exportable = Hash.new { |types, type|
+ types[type] = {}
+ }
+
# Eventually, if we support sites, this will allow definitions
# of nodes with the same name in different sites. For now
# the top-level scope is always the only site scope.
@@ -685,6 +721,8 @@ module Puppet::Parser
file = hash[:file]
line = hash[:line]
+ collectable = hash[:collectable] || self.collectable
+
# Verify that we're not overriding any already-set parameters.
if localobj = @localobjectable[type][name]
params.each { |var, value|
@@ -703,6 +741,24 @@ module Puppet::Parser
# First look for it in a parent scope
obj = lookupobject(:name => name, :type => type)
+ if obj
+ unless collectable == obj.collectable
+ msg = nil
+ if collectable
+ msg = "Exported %s[%s] cannot override local objects"
+ [type, name]
+ else
+ msg = "Local %s[%s] cannot override exported objects"
+ [type, name]
+ end
+
+ error = Puppet::ParseError.new(msg)
+ error.line = line
+ error.file = file
+ raise error
+ end
+ end
+
unless obj and obj != :undefined
unless obj = @objectable[type][name]
obj = self.newobject(
@@ -712,6 +768,8 @@ module Puppet::Parser
:file => file
)
+ obj.collectable = collectable
+
# only set these if we've created the object,
# which is the most common case
# FIXME we eventually need to store the file
@@ -872,7 +930,9 @@ module Puppet::Parser
# it is.
if objecttype = lookuptype(child.type)
# It's a defined type, so evaluate it. Retain whether
- # the object is collectable.
+ # the object is collectable. If the object is collectable,
+ # then it will store all of its contents into the
+ # @exportable table, rather than returning them.
result = objecttype.safeevaluate(
:name => child.name,
:type => child.type,
@@ -880,22 +940,15 @@ module Puppet::Parser
:scope => self,
:collectable => child.collectable
)
-
- # If the child is collectable, then mark all of the
- # results as collectable. This is how we retain
- # collectability through components and such.
+ else
+ # If it's collectable, then store it.
if child.collectable
- result.delve do |object|
- if object.is_a? Puppet::TransObject
- debug "Collecting %s[%s]" %
- [object.type, object.name]
- object.collectable = true
- end
- end
+ exportobject(child)
+ result = nil
+ else
+ # It's a builtin type, so just return it directly
+ result = child
end
- else
- # It's a builtin type, so just return it directly
- result = child
end
else
raise Puppet::DevError,
diff --git a/lib/puppet/rails.rb b/lib/puppet/rails.rb
index 2a5b31408..0d01fbe07 100644
--- a/lib/puppet/rails.rb
+++ b/lib/puppet/rails.rb
@@ -26,6 +26,8 @@ module Puppet::Rails
Puppet.config.setdefaults(:puppetmaster,
:dblocation => { :default => "$statedir/clientconfigs.sqlite3",
:mode => 0600,
+ :owner => "$user",
+ :group => "$group",
:desc => "The database cache for client configurations. Used for
querying within the language."
},
@@ -39,16 +41,24 @@ module Puppet::Rails
used when networked databases are used."],
:railslog => {:default => "$logdir/puppetrails.log",
:mode => 0600,
+ :owner => "$user",
+ :group => "$group",
:desc => "Where Rails-specific logs are sent"
}
)
+ def self.clear
+ @inited = false
+ end
+
# Set up our database connection. It'd be nice to have a "use" system
# that could make callbacks.
def self.init
- unless defined? @inited and @inited
+ # This global init does not work for testing, because we remove
+ # the state dir on every test.
+ #unless (defined? @inited and @inited) or defined? Test::Unit::TestCase
+ unless (defined? @inited and @inited)
Puppet.config.use(:puppet)
- Puppet.config.use(:puppetmaster)
ActiveRecord::Base.logger = Logger.new(Puppet[:railslog])
args = {:adapter => Puppet[:dbadapter]}
@@ -65,13 +75,14 @@ module Puppet::Rails
ActiveRecord::Base.establish_connection(args)
- unless FileTest.exists?(args[:database])
- require 'puppet/rails/database'
- Puppet::Rails::Database.up
- end
-
@inited = true
end
+
+ if Puppet[:dbadapter] == "sqlite3" and ! FileTest.exists?(Puppet[:dblocation])
+ require 'puppet/rails/database'
+ Puppet::Rails::Database.up
+ end
+ Puppet.config.use(:puppetmaster)
end
end
diff --git a/lib/puppet/rails/rails_object.rb b/lib/puppet/rails/rails_object.rb
index 1b725c984..d1e58e453 100644
--- a/lib/puppet/rails/rails_object.rb
+++ b/lib/puppet/rails/rails_object.rb
@@ -24,7 +24,7 @@ class Puppet::Rails::RailsObject < ActiveRecord::Base
# is collectable, though, since that would cause it to get stripped
# from the configuration.
def to_trans
- obj = Puppet::TransObject.new(ptype(), name())
+ obj = Puppet::TransObject.new(name(), ptype())
[:file, :line, :tags].each do |method|
if val = send(method)
diff --git a/test/language/interpreter.rb b/test/language/interpreter.rb
index 509f004a4..8603e7f1c 100755
--- a/test/language/interpreter.rb
+++ b/test/language/interpreter.rb
@@ -86,8 +86,9 @@ class TestInterpreter < Test::Unit::TestCase
assert_nothing_raised {
interp = Puppet::Parser::Interpreter.new(
:Manifest => file,
- :UseNodes => false
- )
+ :UseNodes => false,
+ :ForkSave => false
+ )
}
facts = {}
@@ -102,13 +103,6 @@ class TestInterpreter < Test::Unit::TestCase
assert(obj, "Could not find host object")
end
- if defined? ActiveRecord
- def test_collectstorage
- end
- else
- $stderr.puts "Install Rails for configuration storage testing"
- end
-
if Facter["domain"].value == "madstop.com"
begin
require 'ldap'
diff --git a/test/language/parser.rb b/test/language/parser.rb
index 848f0128e..d82916eea 100644
--- a/test/language/parser.rb
+++ b/test/language/parser.rb
@@ -304,6 +304,7 @@ class TestParser < Test::Unit::TestCase
# Verify that collectable objects are marked that way.
def test_collectable
+ Puppet[:storeconfigs] = true
["@port { ssh: protocols => tcp, number => 22 }",
"@port { ssh: protocols => tcp, number => 22;
smtp: protocols => tcp, number => 25 }"].each do |text|
diff --git a/test/language/rails.rb b/test/language/rails.rb
index 13bee4c5b..df76b4f20 100755
--- a/test/language/rails.rb
+++ b/test/language/rails.rb
@@ -97,14 +97,6 @@ class TestRails < Test::Unit::TestCase
assert_equal(collectable.length, list.length,
"Did not get the right number of objects")
end
-
- def test_railsinit
- assert_nothing_raised {
- Puppet::Rails.init
- }
-
- assert(FileTest.exists?(Puppet[:dblocation]), "Database does not exist")
- end
else
$stderr.puts "Install Rails for Rails and Caching tests"
end
diff --git a/test/language/scope.rb b/test/language/scope.rb
index aeac1f20b..c980e5055 100755
--- a/test/language/scope.rb
+++ b/test/language/scope.rb
@@ -714,9 +714,91 @@ class TestScope < Test::Unit::TestCase
trans = scope.evaluate(:ast => top)
}
- trans.flatten.each do |obj|
- assert(obj.collectable, "Object %s[%s] is not collectable" %
- [obj.type, obj.name])
+ %w{file}.each do |type|
+ objects = scope.exported(type)
+
+ assert(!objects.empty?, "Did not get an exported %s" % type)
end
end
+
+ # Verify that we can both store and collect an object in the same
+ # run.
+ def test_storeandcollect
+ Puppet[:storeconfigs] = true
+ Puppet::Rails.clear
+ Puppet::Rails.init
+ sleep 1
+ children = []
+ file = tempfile()
+ File.open(file, "w") { |f|
+ #f.puts "@file { \"#{file}\": mode => 644 }
+#file <||>"
+ f.puts "
+@host { puppet: ip => \"192.168.0.3\" }
+
+host <||>"
+ }
+
+ interp = nil
+ assert_nothing_raised {
+ interp = Puppet::Parser::Interpreter.new(
+ :Manifest => file,
+ :UseNodes => false,
+ :ForkSave => false
+ )
+ }
+
+ 2.times { |i|
+ objects = nil
+ assert_nothing_raised {
+ objects = interp.run("localhost", {})
+ }
+ }
+ end
+
+ # Verify that we cannot override differently exported objects
+ def test_exportedoverrides
+ filename = tempfile()
+ children = []
+
+ obj = fileobj(filename, "owner" => "root")
+ obj.collectable = true
+ # create the parent class
+ children << classobj("parent", :code => AST::ASTArray.new(
+ :children => [
+ obj
+ ]
+ ))
+
+ # now create a child class with differ values
+ children << classobj("child", :parentclass => nameobj("parent"),
+ :code => AST::ASTArray.new(
+ :children => [
+ fileobj(filename, "owner" => "bin")
+ ]
+ ))
+
+ # Now call the child class
+ assert_nothing_raised("Could not add AST nodes for calling") {
+ children << AST::ObjectDef.new(
+ :type => nameobj("child"),
+ :name => nameobj("yayness"),
+ :params => astarray()
+ )
+ }
+
+ top = nil
+ assert_nothing_raised("Could not create top object") {
+ top = AST::ASTArray.new(
+ :children => children
+ )
+ }
+
+ objects = nil
+ scope = nil
+ assert_raise(Puppet::ParseError, "Incorrectly allowed override") {
+ scope = Puppet::Parser::Scope.new()
+ objects = scope.evaluate(:ast => top)
+ }
+ end
end
diff --git a/test/puppettest.rb b/test/puppettest.rb
index ed0383fd3..c509055d1 100644
--- a/test/puppettest.rb
+++ b/test/puppettest.rb
@@ -132,6 +132,7 @@ module TestPuppet
@@tmppids.clear
Puppet::Type.allclear
Puppet::Storage.clear
+ Puppet::Rails.clear
Puppet.clear
@memoryatend = Puppet::Util.memory