#!/usr/bin/env rspec require 'spec_helper' require 'tmpdir' require 'puppet/node/environment' require 'puppet/util/execution' describe Puppet::Node::Environment do include PuppetSpec::Files after do Puppet::Node::Environment.clear end it "should use the filetimeout for the ttl for the modulepath" do Puppet::Node::Environment.attr_ttl(:modulepath).should == Integer(Puppet[:filetimeout]) end it "should use the filetimeout for the ttl for the module list" do Puppet::Node::Environment.attr_ttl(:modules).should == Integer(Puppet[:filetimeout]) end it "should use the default environment if no name is provided while initializing an environment" do Puppet.settings.expects(:value).with(:environment).returns("one") Puppet::Node::Environment.new.name.should == :one end it "should treat environment instances as singletons" do Puppet::Node::Environment.new("one").should equal(Puppet::Node::Environment.new("one")) end it "should treat an environment specified as names or strings as equivalent" do Puppet::Node::Environment.new(:one).should equal(Puppet::Node::Environment.new("one")) end it "should return its name when converted to a string" do Puppet::Node::Environment.new(:one).to_s.should == "one" end it "should just return any provided environment if an environment is provided as the name" do one = Puppet::Node::Environment.new(:one) Puppet::Node::Environment.new(one).should equal(one) end describe "when managing known resource types" do before do @env = Puppet::Node::Environment.new("dev") @collection = Puppet::Resource::TypeCollection.new(@env) @env.stubs(:perform_initial_import).returns(Puppet::Parser::AST::Hostclass.new('')) Thread.current[:known_resource_types] = nil end it "should create a resource type collection if none exists" do Puppet::Resource::TypeCollection.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 @env = Puppet::Node::Environment.new("dev") @env.expects(:perform_initial_import).returns(Puppet::Parser::AST::Hostclass.new('')) @env.known_resource_types end it "should return the same collection even if stale if it's the same thread" do Puppet::Resource::TypeCollection.stubs(:new).returns @collection @env.known_resource_types.stubs(:stale?).returns true @env.known_resource_types.should equal(@collection) end it "should return the current thread associated collection if there is one" do Thread.current[:known_resource_types] = @collection @env.known_resource_types.should equal(@collection) end it "should give to all threads using the same environment the same collection if the collection isn't stale" do original_thread_type_collection = Puppet::Resource::TypeCollection.new(@env) Puppet::Resource::TypeCollection.expects(:new).with(@env).returns original_thread_type_collection @env.known_resource_types.should equal(original_thread_type_collection) original_thread_type_collection.expects(:require_reparse?).returns(false) Puppet::Resource::TypeCollection.stubs(:new).with(@env).returns @collection t = Thread.new { @env.known_resource_types.should equal(original_thread_type_collection) } t.join end it "should generate a new TypeCollection if the current one requires reparsing" do old_type_collection = @env.known_resource_types old_type_collection.stubs(:require_reparse?).returns true Thread.current[:known_resource_types] = nil new_type_collection = @env.known_resource_types new_type_collection.should be_a Puppet::Resource::TypeCollection new_type_collection.should_not equal(old_type_collection) end end it "should validate the modulepath directories" do real_file = Dir.mktmpdir path = %W[/one /two #{real_file}].join(File::PATH_SEPARATOR) Puppet[:modulepath] = path env = Puppet::Node::Environment.new("testing") env.modulepath.should == [real_file] end it "should prefix the value of the 'PUPPETLIB' environment variable to the module path if present" do Puppet::Util::Execution.withenv("PUPPETLIB" => %w{/l1 /l2}.join(File::PATH_SEPARATOR)) do env = Puppet::Node::Environment.new("testing") module_path = %w{/one /two}.join(File::PATH_SEPARATOR) env.expects(:validate_dirs).with(%w{/l1 /l2 /one /two}).returns %w{/l1 /l2 /one /two} env.expects(:[]).with(:modulepath).returns module_path env.modulepath.should == %w{/l1 /l2 /one /two} end end describe "when validating modulepath or manifestdir directories" do before :each do @path_one = make_absolute('/one') @path_two = make_absolute('/two') end it "should not return non-directories" do env = Puppet::Node::Environment.new("testing") FileTest.expects(:directory?).with(@path_one).returns true FileTest.expects(:directory?).with(@path_two).returns false env.validate_dirs([@path_one, @path_two]).should == [@path_one] end it "should use the current working directory to fully-qualify unqualified paths" do FileTest.stubs(:directory?).returns true env = Puppet::Node::Environment.new("testing") two = File.expand_path(File.join(Dir.getwd, "two")) env.validate_dirs([@path_one, 'two']).should == [@path_one, two] end end describe "when modeling a specific environment" do it "should have a method for returning the environment name" do Puppet::Node::Environment.new("testing").name.should == :testing end it "should provide an array-like accessor method for returning any environment-specific setting" do env = Puppet::Node::Environment.new("testing") env.should respond_to(:[]) end it "should ask the Puppet settings instance for the setting qualified with the environment name" do Puppet.settings.expects(:value).with("myvar", :testing).returns("myval") env = Puppet::Node::Environment.new("testing") env["myvar"].should == "myval" end it "should be able to return an individual module that exists in its module path" do env = Puppet::Node::Environment.new("testing") mod = mock 'module' Puppet::Module.expects(:new).with("one", env).returns mod mod.expects(:exist?).returns true env.module("one").should equal(mod) end it "should return nil if asked for a module that does not exist in its path" do env = Puppet::Node::Environment.new("testing") mod = mock 'module' Puppet::Module.expects(:new).with("one", env).returns mod mod.expects(:exist?).returns false env.module("one").should be_nil end it "should be able to return its modules" do Puppet::Node::Environment.new("testing").should respond_to(:modules) end describe ".modules" do it "should return a module named for every directory in each module path" do env = Puppet::Node::Environment.new("testing") env.expects(:modulepath).at_least_once.returns %w{/a /b} Dir.expects(:entries).with("/a").returns %w{foo bar} Dir.expects(:entries).with("/b").returns %w{bee baz} env.modules.collect{|mod| mod.name}.sort.should == %w{foo bar bee baz}.sort end it "should remove duplicates" do env = Puppet::Node::Environment.new("testing") env.expects(:modulepath).returns( %w{/a /b} ).at_least_once Dir.expects(:entries).with("/a").returns %w{foo} Dir.expects(:entries).with("/b").returns %w{foo} env.modules.collect{|mod| mod.name}.sort.should == %w{foo} end it "should ignore invalid modules" do env = Puppet::Node::Environment.new("testing") env.stubs(:modulepath).returns %w{/a} Dir.expects(:entries).with("/a").returns %w{foo bar} Puppet::Module.expects(:new).with { |name, env| name == "foo" }.returns mock("foomod", :name => "foo") Puppet::Module.expects(:new).with { |name, env| name == "bar" }.raises( Puppet::Module::InvalidName, "name is invalid" ) env.modules.collect{|mod| mod.name}.sort.should == %w{foo} end it "should create modules with the correct environment" do env = Puppet::Node::Environment.new("testing") env.expects(:modulepath).at_least_once.returns %w{/a} Dir.expects(:entries).with("/a").returns %w{foo} env.modules.each {|mod| mod.environment.should == env } end it "should cache the module list" do env = Puppet::Node::Environment.new("testing") env.expects(:modulepath).at_least_once.returns %w{/a} Dir.expects(:entries).once.with("/a").returns %w{foo} env.modules env.modules end end end describe Puppet::Node::Environment::Helper do before do @helper = Object.new @helper.extend(Puppet::Node::Environment::Helper) end it "should be able to set and retrieve the environment" do @helper.environment = :foo @helper.environment.name.should == :foo end it "should accept an environment directly" do env = Puppet::Node::Environment.new :foo @helper.environment = env @helper.environment.name.should == :foo end it "should accept an environment as a string" do env = Puppet::Node::Environment.new "foo" @helper.environment = env @helper.environment.name.should == :foo end end describe "when performing initial import" do before do @parser = Puppet::Parser::Parser.new("test") Puppet::Parser::Parser.stubs(:new).returns @parser @env = Puppet::Node::Environment.new("env") end it "should set the parser's string to the 'code' setting and parse if code is available" do Puppet.settings[:code] = "my code" @parser.expects(:string=).with "my code" @parser.expects(:parse) @env.instance_eval { perform_initial_import } end it "should set the parser's file to the 'manifest' setting and parse if no code is available and the manifest is available" do filename = tmpfile('myfile') File.open(filename, 'w'){|f| } Puppet.settings[:manifest] = filename @parser.expects(:file=).with filename @parser.expects(:parse) @env.instance_eval { perform_initial_import } end it "should pass the manifest file to the parser even if it does not exist on disk" do filename = tmpfile('myfile') Puppet.settings[:code] = "" Puppet.settings[:manifest] = filename @parser.expects(:file=).with(filename).once @parser.expects(:parse).once @env.instance_eval { perform_initial_import } end it "should fail helpfully if there is an error importing" do File.stubs(:exist?).returns true @env.stubs(:known_resource_types).returns Puppet::Resource::TypeCollection.new(@env) @parser.expects(:file=).once @parser.expects(:parse).raises ArgumentError lambda { @env.instance_eval { perform_initial_import } }.should raise_error(Puppet::Error) end it "should not do anything if the ignore_import settings is set" do Puppet.settings[:ignoreimport] = true @parser.expects(:string=).never @parser.expects(:file=).never @parser.expects(:parse).never @env.instance_eval { perform_initial_import } end it "should mark the type collection as needing a reparse when there is an error parsing" do @parser.expects(:parse).raises Puppet::ParseError.new("Syntax error at ...") @env.stubs(:known_resource_types).returns Puppet::Resource::TypeCollection.new(@env) lambda { @env.instance_eval { perform_initial_import } }.should raise_error(Puppet::Error, /Syntax error at .../) @env.known_resource_types.require_reparse?.should be_true end end end