diff options
-rw-r--r-- | lib/puppet/node/environment.rb | 8 | ||||
-rw-r--r-- | lib/puppet/parser/resource_type_collection.rb | 60 | ||||
-rwxr-xr-x | spec/unit/node/environment.rb | 32 | ||||
-rw-r--r-- | spec/unit/parser/resource_type_collection.rb | 120 |
4 files changed, 202 insertions, 18 deletions
diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb index 395d50657..a6e33703b 100644 --- a/lib/puppet/node/environment.rb +++ b/lib/puppet/node/environment.rb @@ -42,6 +42,14 @@ class Puppet::Node::Environment @name = name end + def known_resource_types + if @known_resource_types.nil? or @known_resource_types.stale? + @known_resource_types = Puppet::Parser::ResourceTypeCollection.new(self) + @known_resource_types.perform_initial_import + end + @known_resource_types + end + def module(name) mod = Puppet::Module.new(name, self) return nil unless mod.exist? diff --git a/lib/puppet/parser/resource_type_collection.rb b/lib/puppet/parser/resource_type_collection.rb index c6a91887a..cb90d9bb9 100644 --- a/lib/puppet/parser/resource_type_collection.rb +++ b/lib/puppet/parser/resource_type_collection.rb @@ -1,18 +1,8 @@ class Puppet::Parser::ResourceTypeCollection attr_reader :environment - @code = {} - - def self.[]=(environment, code) - @code[environment] = code - end - - def self.[](environment) - @code[environment] - end - - def initialize(environment) - @environment = environment + def initialize(env) + @environment = env.is_a?(String) ? Puppet::Node::Environment.new(env) : env @hostclasses = {} @definitions = {} @nodes = {} @@ -20,8 +10,7 @@ class Puppet::Parser::ResourceTypeCollection # So we can keep a list and match the first-defined regex @node_list = [] - # Store the most recently created code collection globally per environment. - self.class[self.environment] = self + @watched_files = {} end def <<(thing) @@ -124,6 +113,49 @@ class Puppet::Parser::ResourceTypeCollection end end + def perform_initial_import + parser = Puppet::Parser::Parser.new(environment) + if code = Puppet.settings.uninterpolated_value(:code, environment.to_s) and code != "" + parser.string = code + else + file = Puppet.settings.value(:manifest, environment.to_s) + return unless File.exist?(file) + parser.file = file + end + parser.parse + rescue => detail + msg = "Could not parse for environment #{environment}: #{detail}" + error = Puppet::Error.new(msg) + error.set_backtrace(detail.backtrace) + raise error + end + + def stale? + @watched_files.values.detect { |file| file.changed? } + end + + def version + return @version if defined?(@version) + + if environment[:config_version] == "" + @version = Time.now.to_i + return @version + end + + @version = Puppet::Util.execute([environment[:config_version]]).strip + + rescue Puppet::ExecutionFailure => e + raise Puppet::ParseError, "Unable to set config_version: #{e.message}" + end + + def watch_file(file) + @watched_files[file] = Puppet::Util::LoadedFile.new(file) + end + + def watching_file?(file) + @watched_files.include?(file) + end + private def find_fully_qualified(name, type) diff --git a/spec/unit/node/environment.rb b/spec/unit/node/environment.rb index b9bfcafc8..26d9aabe1 100755 --- a/spec/unit/node/environment.rb +++ b/spec/unit/node/environment.rb @@ -43,6 +43,38 @@ describe Puppet::Node::Environment do Puppet::Node::Environment.new(:one).to_s.should == "one" end + describe "when managing known resource types" do + before do + @env = Puppet::Node::Environment.new("dev") + @collection = Puppet::Parser::ResourceTypeCollection.new(@env) + @collection.stubs(:perform_initial_import) + end + + it "should create a resource type collection if none exists" do + Puppet::Parser::ResourceTypeCollection.expects(:new).with(@env).returns @collection + @env.known_resource_types.should equal(@collection) + end + + it "should reuse any existing resource type collection" do + @env.known_resource_types.should equal(@env.known_resource_types) + end + + it "should perform the initial import when creating a new collection" do + @collection.expects(:perform_initial_import) + Puppet::Parser::ResourceTypeCollection.expects(:new).returns @collection + + @env.known_resource_types + end + + it "should create and return a new collection rather than returning a stale collection" do + @env.known_resource_types.expects(:stale?).returns true + + Puppet::Parser::ResourceTypeCollection.expects(:new).returns @collection + + @env.known_resource_types.should equal(@collection) + end + end + [:modulepath, :manifestdir].each do |setting| it "should validate the #{setting} directories" do path = %w{/one /two}.join(File::PATH_SEPARATOR) diff --git a/spec/unit/parser/resource_type_collection.rb b/spec/unit/parser/resource_type_collection.rb index 0d7795c3a..b2f1588c9 100644 --- a/spec/unit/parser/resource_type_collection.rb +++ b/spec/unit/parser/resource_type_collection.rb @@ -12,12 +12,13 @@ describe Puppet::Parser::ResourceTypeCollection do end it "should require an environment at initialization" do - Puppet::Parser::ResourceTypeCollection.new("foo").environment.should == "foo" + env = Puppet::Node::Environment.new("testing") + Puppet::Parser::ResourceTypeCollection.new(env).environment.should equal(env) end - it "should store itself as the environment-specific code collection in its class" do - code = Puppet::Parser::ResourceTypeCollection.new("foo") - Puppet::Parser::ResourceTypeCollection["foo"].should equal(code) + it "should convert the environment into an environment instance if a string is provided" do + env = Puppet::Node::Environment.new("testing") + Puppet::Parser::ResourceTypeCollection.new("testing").environment.should equal(env) end it "should be able to add a resource type" do @@ -225,4 +226,115 @@ describe Puppet::Parser::ResourceTypeCollection do @loader.node("foo").should equal(node2) end end + + describe "when managing files" do + before do + @loader = Puppet::Parser::ResourceTypeCollection.new("env") + Puppet::Util::LoadedFile.stubs(:new).returns stub("watched_file") + end + + it "should have a method for specifying a file should be watched" do + @loader.should respond_to(:watch_file) + end + + it "should have a method for determining if a file is being watched" do + @loader.watch_file("/foo/bar") + @loader.should be_watching_file("/foo/bar") + end + + it "should use LoadedFile to watch files" do + Puppet::Util::LoadedFile.expects(:new).with("/foo/bar").returns stub("watched_file") + @loader.watch_file("/foo/bar") + end + + it "should be considered stale if any files have changed" do + file1 = stub 'file1', :changed? => false + file2 = stub 'file2', :changed? => true + Puppet::Util::LoadedFile.expects(:new).times(2).returns(file1).then.returns(file2) + @loader.watch_file("/foo/bar") + @loader.watch_file("/other/bar") + + @loader.should be_stale + end + + it "should not be considered stable if no files have changed" do + file1 = stub 'file1', :changed? => false + file2 = stub 'file2', :changed? => false + Puppet::Util::LoadedFile.expects(:new).times(2).returns(file1).then.returns(file2) + @loader.watch_file("/foo/bar") + @loader.watch_file("/other/bar") + + @loader.should_not be_stale + end + end + + describe "when performing initial import" do + before do + @parser = stub 'parser', :file= => nil, :string => nil, :parse => nil + Puppet::Parser::Parser.stubs(:new).returns @parser + @code = Puppet::Parser::ResourceTypeCollection.new("env") + end + + it "should create a new parser instance" do + Puppet::Parser::Parser.expects(:new).returns @parser + @code.perform_initial_import + end + + it "should set the parser's string to the 'code' setting and parse if code is available" do + Puppet.settings[:code] = "my code" + @parser.expects(:string=).with "my code" + @parser.expects(:parse) + @code.perform_initial_import + end + + it "should set the parser's file to the 'manifest' setting and parse if no code is available and the manifest is available" do + File.expects(:exist?).with("/my/file").returns true + Puppet.settings[:manifest] = "/my/file" + @parser.expects(:file=).with "/my/file" + @parser.expects(:parse) + @code.perform_initial_import + end + + it "should not attempt to load a manifest if none is present" do + File.expects(:exist?).with("/my/file").returns false + Puppet.settings[:manifest] = "/my/file" + @parser.expects(:file=).never + @parser.expects(:parse).never + @code.perform_initial_import + end + + it "should fail helpfully if there is an error importing" do + File.stubs(:exist?).returns true + @parser.expects(:parse).raises ArgumentError + lambda { @code.perform_initial_import }.should raise_error(Puppet::Error) + end + end + + describe "when determining the configuration version" do + before do + @code = Puppet::Parser::ResourceTypeCollection.new("env") + end + + it "should default to the current time" do + time = Time.now + + Time.stubs(:now).returns time + @code.version.should == time.to_i + end + + it "should use the output of the environment's config_version setting if one is provided" do + @code.environment.stubs(:[]).with(:config_version).returns("/my/foo") + + Puppet::Util.expects(:execute).with(["/my/foo"]).returns "output\n" + @code.version.should == "output" + end + + it "should raise a puppet parser error if executing config_version fails" do + @code.environment.stubs(:[]).with(:config_version).returns("test") + Puppet::Util.expects(:execute).raises(Puppet::ExecutionFailure.new("msg")) + + lambda { @code.version }.should raise_error(Puppet::ParseError) + end + + end end |