diff --git a/lib/puppet/parser/e_parser_adapter.rb b/lib/puppet/parser/e_parser_adapter.rb deleted file mode 100644 index fe0e28b55..000000000 --- a/lib/puppet/parser/e_parser_adapter.rb +++ /dev/null @@ -1,119 +0,0 @@ -require 'puppet/pops' - -module Puppet; module Parser; end; end; -# Adapts an egrammar/eparser to respond to the public API of the classic parser -# -class Puppet::Parser::EParserAdapter - - def initialize(classic_parser) - @classic_parser = classic_parser - @file = '' - @string = '' - @use = :undefined - end - - def file=(file) - @classic_parser.file = file - @file = file - @use = :file - end - - def parse(string = nil) - if @file =~ /\.rb$/ - return parse_ruby_file - else - self.string= string if string - parser = Puppet::Pops::Parser::Parser.new() - parse_result = if @use == :string - parser.parse_string(@string) - else - parser.parse_file(@file) - end - # Compute the source_file to set in created AST objects (it was either given, or it may be unknown - # if caller did not set a file and the present a string. - # - source_file = @file || "unknown-source-location" - - # Validate - validate(parse_result) - # Transform the result, but only if not nil - parse_result = Puppet::Pops::Model::AstTransformer.new(source_file, @classic_parser).transform(parse_result) if parse_result - if parse_result && !parse_result.is_a?(Puppet::Parser::AST::BlockExpression) - # Need to transform again, if result is not wrapped in something iterable when handed off to - # a new Hostclass as its code. - parse_result = Puppet::Parser::AST::BlockExpression.new(:children => [parse_result]) if parse_result - end - end - - Puppet::Parser::AST::Hostclass.new('', :code => parse_result) - end - - def validate(parse_result) - # TODO: This is too many hoops to jump through... ugly API - # could reference a ValidatorFactory.validator_3_1(acceptor) instead. - # and let the factory abstract the rest. - # - return unless parse_result - - acceptor = Puppet::Pops::Validation::Acceptor.new - validator = Puppet::Pops::Validation::ValidatorFactory_3_1.new().validator(acceptor) - validator.validate(parse_result) - - max_errors = Puppet[:max_errors] - max_warnings = Puppet[:max_warnings] + 1 - max_deprecations = Puppet[:max_deprecations] + 1 - - # If there are warnings output them - warnings = acceptor.warnings - if warnings.size > 0 - formatter = Puppet::Pops::Validation::DiagnosticFormatterPuppetStyle.new - emitted_w = 0 - emitted_dw = 0 - acceptor.warnings.each {|w| - if w.severity == :deprecation - # Do *not* call Puppet.deprecation_warning it is for internal deprecation, not - # deprecation of constructs in manifests! (It is not designed for that purpose even if - # used throughout the code base). - # - Puppet.warning(formatter.format(w)) if emitted_dw < max_deprecations - emitted_dw += 1 - else - Puppet.warning(formatter.format(w)) if emitted_w < max_warnings - emitted_w += 1 - end - break if emitted_w > max_warnings && emitted_dw > max_deprecations # but only then - } - end - - # If there were errors, report the first found. Use a puppet style formatter. - errors = acceptor.errors - if errors.size > 0 - formatter = Puppet::Pops::Validation::DiagnosticFormatterPuppetStyle.new - if errors.size == 1 || max_errors <= 1 - # raise immediately - raise Puppet::ParseError.new(formatter.format(errors[0])) - end - emitted = 0 - errors.each do |e| - Puppet.err(formatter.format(e)) - emitted += 1 - break if emitted >= max_errors - end - warnings_message = warnings.size > 0 ? ", and #{warnings.size} warnings" : "" - giving_up_message = "Found #{errors.size} errors#{warnings_message}. Giving up" - exception = Puppet::ParseError.new(giving_up_message) - exception.file = errors[0].file - raise exception - end - end - - def string=(string) - @classic_parser.string = string - @string = string - @use = :string - end - - def parse_ruby_file - @classic_parser.parse - end -end diff --git a/lib/puppet/parser/parser_factory.rb b/lib/puppet/parser/parser_factory.rb index 576bc8755..a9d93cf85 100644 --- a/lib/puppet/parser/parser_factory.rb +++ b/lib/puppet/parser/parser_factory.rb @@ -1,88 +1,77 @@ module Puppet; end module Puppet::Parser # The ParserFactory makes selection of parser possible. # Currently, it is possible to switch between two different parsers: # * classic_parser, the parser in 3.1 # * eparser, the Expression Based Parser # class ParserFactory # Produces a parser instance for the given environment def self.parser(environment) - case Puppet[:parser] - when 'future' - if Puppet[:evaluator] == 'future' - evaluating_parser(environment) - else - eparser(environment) - end + if Puppet[:parser] == 'future' + evaluating_parser(environment) else classic_parser(environment) end end # Creates an instance of the classic parser. # def self.classic_parser(environment) - require 'puppet/parser' - + # avoid expensive require if already loaded + require 'puppet/parser' unless defined? Puppet::Parser::Parser Puppet::Parser::Parser.new(environment) end - # Returns an instance of an EvaluatingParser + # Creates an instance of an E4ParserAdapter that adapts an + # EvaluatingParser to the 3x way of parsing. + # def self.evaluating_parser(file_watcher) # Since RGen is optional, test that it is installed @@asserted ||= false - assert_rgen_installed() unless @@asserted + unless @@asserted + # avoid this expensive check if already done + assert_rgen_installed() + # avoid expensive requires + require 'puppet/parser/e4_parser_adapter' + require 'puppet/pops/parser/code_merger' + end @@asserted = true - require 'puppet/parser/e4_parser_adapter' - require 'puppet/pops/parser/code_merger' + E4ParserAdapter.new(file_watcher) end - # Creates an instance of the expression based parser 'eparser' + # Asserts that RGen >= 0.6.1 is installed by checking that certain behavior is available. + # Note that this assert is expensive as it also requires puppet/pops (if not already loaded). # - def self.eparser(environment) - # Since RGen is optional, test that it is installed - @@asserted ||= false - assert_rgen_installed() unless @@asserted - @@asserted = true - require 'puppet/parser' - require 'puppet/parser/e_parser_adapter' - EParserAdapter.new(Puppet::Parser::Parser.new(environment)) - end - - private - def self.assert_rgen_installed begin require 'rgen/metamodel_builder' rescue LoadError raise Puppet::DevError.new("The gem 'rgen' version >= 0.6.1 is required when using the setting '--parser future'. Please install 'rgen'.") end # Since RGen is optional, there is nothing specifying its version. # It is not installed in any controlled way, so not possible to use gems to check (it may be installed some other way). # Instead check that "eContainer, and eContainingFeature" has been installed. require 'puppet/pops' begin litstring = Puppet::Pops::Model::LiteralString.new(); container = Puppet::Pops::Model::ArithmeticExpression.new(); container.left_expr = litstring raise "no eContainer" if litstring.eContainer() != container raise "no eContainingFeature" if litstring.eContainingFeature() != :left_expr rescue raise Puppet::DevError.new("The gem 'rgen' version >= 0.6.1 is required when using '--parser future'. An older version is installed, please update.") end end def self.code_merger - if Puppet[:parser] == 'future' && Puppet[:evaluator] == 'future' + if Puppet[:parser] == 'future' Puppet::Pops::Parser::CodeMerger.new else Puppet::Parser::CodeMerger.new end end - end - end diff --git a/spec/unit/parser/eparser_adapter_spec.rb b/spec/unit/parser/eparser_adapter_spec.rb deleted file mode 100644 index 173cfb783..000000000 --- a/spec/unit/parser/eparser_adapter_spec.rb +++ /dev/null @@ -1,407 +0,0 @@ -#! /usr/bin/env ruby -require 'spec_helper' -require 'puppet/parser/e_parser_adapter' - -describe Puppet::Parser do - - Puppet::Parser::AST - - before :each do - @known_resource_types = Puppet::Resource::TypeCollection.new("development") - @classic_parser = Puppet::Parser::Parser.new "development" - @parser = Puppet::Parser::EParserAdapter.new(@classic_parser) - @classic_parser.stubs(:known_resource_types).returns @known_resource_types - @true_ast = Puppet::Parser::AST::Boolean.new :value => true - end - - it "should require an environment at initialization" do - expect { - Puppet::Parser::EParserAdapter.new - }.to raise_error(ArgumentError, /wrong number of arguments/) - end - - describe "when parsing append operator" do - - it "should not raise syntax errors" do - expect { @parser.parse("$var += something") }.to_not raise_error - end - - it "should raise syntax error on incomplete syntax " do - expect { - @parser.parse("$var += ") - }.to raise_error(Puppet::ParseError, /Syntax error at end of file/) - end - - it "should create ast::VarDef with append=true" do - vardef = @parser.parse("$var += 2").code[0] - vardef.should be_a(Puppet::Parser::AST::VarDef) - vardef.append.should == true - end - - it "should work with arrays too" do - vardef = @parser.parse("$var += ['test']").code[0] - vardef.should be_a(Puppet::Parser::AST::VarDef) - vardef.append.should == true - end - - end - - describe "when parsing selector" do - it "should support hash access on the left hand side" do - expect { @parser.parse("$h = { 'a' => 'b' } $a = $h['a'] ? { 'b' => 'd', default => undef }") }.to_not raise_error - end - end - - describe "parsing 'unless'" do - it "should create the correct ast objects" do - Puppet::Parser::AST::Not.expects(:new).with { |h| h[:value].is_a?(Puppet::Parser::AST::Boolean) } - @parser.parse("unless false { $var = 1 }") - end - - it "should not raise an error with empty statements" do - expect { @parser.parse("unless false { }") }.to_not raise_error - end - - #test for bug #13296 - it "should not override 'unless' as a parameter inside resources" do - lambda { @parser.parse("exec {'/bin/echo foo': unless => '/usr/bin/false',}") }.should_not raise_error - end - end - - describe "when parsing parameter names" do - Puppet::Parser::Lexer::KEYWORDS.sort_tokens.each do |keyword| - it "should allow #{keyword} as a keyword" do - lambda { @parser.parse("exec {'/bin/echo foo': #{keyword} => '/usr/bin/false',}") }.should_not raise_error - end - end - end - - describe "when parsing 'if'" do - it "not, it should create the correct ast objects" do - Puppet::Parser::AST::Not.expects(:new).with { |h| h[:value].is_a?(Puppet::Parser::AST::Boolean) } - @parser.parse("if ! true { $var = 1 }") - end - - it "boolean operation, it should create the correct ast objects" do - Puppet::Parser::AST::BooleanOperator.expects(:new).with { - |h| h[:rval].is_a?(Puppet::Parser::AST::Boolean) and h[:lval].is_a?(Puppet::Parser::AST::Boolean) and h[:operator]=="or" - } - @parser.parse("if true or true { $var = 1 }") - - end - - it "comparison operation, it should create the correct ast objects" do - Puppet::Parser::AST::ComparisonOperator.expects(:new).with { - |h| h[:lval].is_a?(Puppet::Parser::AST::Name) and h[:rval].is_a?(Puppet::Parser::AST::Name) and h[:operator]=="<" - } - @parser.parse("if 1 < 2 { $var = 1 }") - - end - - end - - describe "when parsing if complex expressions" do - it "should create a correct ast tree" do - aststub = stub_everything 'ast' - Puppet::Parser::AST::ComparisonOperator.expects(:new).with { - |h| h[:rval].is_a?(Puppet::Parser::AST::Name) and h[:lval].is_a?(Puppet::Parser::AST::Name) and h[:operator]==">" - }.returns(aststub) - Puppet::Parser::AST::ComparisonOperator.expects(:new).with { - |h| h[:rval].is_a?(Puppet::Parser::AST::Name) and h[:lval].is_a?(Puppet::Parser::AST::Name) and h[:operator]=="==" - }.returns(aststub) - Puppet::Parser::AST::BooleanOperator.expects(:new).with { - |h| h[:rval]==aststub and h[:lval]==aststub and h[:operator]=="and" - } - @parser.parse("if (1 > 2) and (1 == 2) { $var = 1 }") - end - - it "should raise an error on incorrect expression" do - expect { - @parser.parse("if (1 > 2 > ) or (1 == 2) { $var = 1 }") - }.to raise_error(Puppet::ParseError, /Syntax error at '\)'/) - end - - end - - describe "when parsing resource references" do - - it "should not raise syntax errors" do - expect { @parser.parse('exec { test: param => File["a"] }') }.to_not raise_error - end - - it "should not raise syntax errors with multiple references" do - expect { @parser.parse('exec { test: param => File["a","b"] }') }.to_not raise_error - end - - it "should create an ast::ResourceReference" do - # NOTE: In egrammar, type and name are unified immediately to lower case whereas the regular grammar - # keeps the UC name in some contexts - it gets downcased later as the name of the type is in lower case. - # - Puppet::Parser::AST::ResourceReference.expects(:new).with { |arg| - arg[:line]==1 and arg[:pos] ==25 and arg[:type]=="file" and arg[:title].is_a?(Puppet::Parser::AST::ASTArray) - } - @parser.parse('exec { test: command => File["a","b"] }') - end - end - - describe "when parsing resource overrides" do - - it "should not raise syntax errors" do - expect { @parser.parse('Resource["title"] { param => value }') }.to_not raise_error - end - - it "should not raise syntax errors with multiple overrides" do - expect { @parser.parse('Resource["title1","title2"] { param => value }') }.to_not raise_error - end - - it "should create an ast::ResourceOverride" do - ro = @parser.parse('Resource["title1","title2"] { param => value }').code[0] - ro.should be_a(Puppet::Parser::AST::ResourceOverride) - ro.line.should == 1 - ro.object.should be_a(Puppet::Parser::AST::ResourceReference) - ro.parameters[0].should be_a(Puppet::Parser::AST::ResourceParam) - end - - end - - describe "when parsing if statements" do - - it "should not raise errors with empty if" do - expect { @parser.parse("if true { }") }.to_not raise_error - end - - it "should not raise errors with empty else" do - expect { @parser.parse("if false { notice('if') } else { }") }.to_not raise_error - end - - it "should not raise errors with empty if and else" do - expect { @parser.parse("if false { } else { }") }.to_not raise_error - end - - it "should create a nop node for empty branch" do - Puppet::Parser::AST::Nop.expects(:new).twice - @parser.parse("if true { }") - end - - it "should create a nop node for empty else branch" do - Puppet::Parser::AST::Nop.expects(:new) - @parser.parse("if true { notice('test') } else { }") - end - - it "should build a chain of 'ifs' if there's an 'elsif'" do - expect { @parser.parse(<<-PP) }.to_not raise_error - if true { notice('test') } elsif true {} else { } - PP - end - - end - - describe "when parsing function calls" do - it "should not raise errors with no arguments" do - expect { @parser.parse("tag()") }.to_not raise_error - end - - it "should not raise errors with rvalue function with no args" do - expect { @parser.parse("$a = template()") }.to_not raise_error - end - - it "should not raise errors with arguments" do - expect { @parser.parse("notice(1)") }.to_not raise_error - end - - it "should not raise errors with multiple arguments" do - expect { @parser.parse("notice(1,2)") }.to_not raise_error - end - - it "should not raise errors with multiple arguments and a trailing comma" do - expect { @parser.parse("notice(1,2,)") }.to_not raise_error - end - - end - - describe "when parsing arrays" do - it "should parse an array" do - expect { @parser.parse("$a = [1,2]") }.to_not raise_error - end - - it "should not raise errors with a trailing comma" do - expect { @parser.parse("$a = [1,2,]") }.to_not raise_error - end - - it "should accept an empty array" do - expect { @parser.parse("$var = []\n") }.to_not raise_error - end - end - - describe "when parsing classes" do - before :each do - @krt = Puppet::Resource::TypeCollection.new("development") - @classic_parser = Puppet::Parser::Parser.new "development" - @parser = Puppet::Parser::EParserAdapter.new(@classic_parser) - @classic_parser.stubs(:known_resource_types).returns @krt - end - - it "should not create new classes" do - @parser.parse("class foobar {}").code[0].should be_a(Puppet::Parser::AST::Hostclass) - @krt.hostclass("foobar").should be_nil - end - - it "should correctly set the parent class when one is provided" do - @parser.parse("class foobar inherits yayness {}").code[0].instantiate('')[0].parent.should == "yayness" - end - - it "should correctly set the parent class for multiple classes at a time" do - statements = @parser.parse("class foobar inherits yayness {}\nclass boo inherits bar {}").code - statements[0].instantiate('')[0].parent.should == "yayness" - statements[1].instantiate('')[0].parent.should == "bar" - end - - it "should define the code when some is provided" do - @parser.parse("class foobar { $var = val }").code[0].code.should_not be_nil - end - - it "should accept parameters with trailing comma" do - @parser.parse("file { '/example': ensure => file, }").should be - end - - it "should accept parametrized classes with trailing comma" do - @parser.parse("class foobar ($var1 = 0,) { $var = val }").code[0].code.should_not be_nil - end - - it "should define parameters when provided" do - foobar = @parser.parse("class foobar($biz,$baz) {}").code[0].instantiate('')[0] - foobar.arguments.should == {"biz" => nil, "baz" => nil} - end - end - - describe "when parsing resources" do - before :each do - @krt = Puppet::Resource::TypeCollection.new("development") - @classic_parser = Puppet::Parser::Parser.new "development" - @parser = Puppet::Parser::EParserAdapter.new(@classic_parser) - @classic_parser.stubs(:known_resource_types).returns @krt - end - - it "should be able to parse class resources" do - @krt.add(Puppet::Resource::Type.new(:hostclass, "foobar", :arguments => {"biz" => nil})) - expect { @parser.parse("class { foobar: biz => stuff }") }.to_not raise_error - end - - it "should correctly mark exported resources as exported" do - @parser.parse("@@file { '/file': }").code[0].exported.should be_true - end - - it "should correctly mark virtual resources as virtual" do - @parser.parse("@file { '/file': }").code[0].virtual.should be_true - end - end - - describe "when parsing nodes" do - it "should be able to parse a node with a single name" do - node = @parser.parse("node foo { }").code[0] - node.should be_a Puppet::Parser::AST::Node - node.names.length.should == 1 - node.names[0].value.should == "foo" - end - - it "should be able to parse a node with two names" do - node = @parser.parse("node foo, bar { }").code[0] - node.should be_a Puppet::Parser::AST::Node - node.names.length.should == 2 - node.names[0].value.should == "foo" - node.names[1].value.should == "bar" - end - - it "should be able to parse a node with three names" do - node = @parser.parse("node foo, bar, baz { }").code[0] - node.should be_a Puppet::Parser::AST::Node - node.names.length.should == 3 - node.names[0].value.should == "foo" - node.names[1].value.should == "bar" - node.names[2].value.should == "baz" - end - end - - it "should fail if trying to collect defaults" do - expect { - @parser.parse("@Port { protocols => tcp }") - }.to raise_error(Puppet::ParseError, /Defaults are not virtualizable/) - end - - context "when parsing collections" do - it "should parse basic collections" do - @parser.parse("Port <| |>").code. - should be_all {|x| x.is_a? Puppet::Parser::AST::Collection } - end - - it "should parse fully qualified collections" do - @parser.parse("Port::Range <| |>").code. - should be_all {|x| x.is_a? Puppet::Parser::AST::Collection } - end - end - - it "should not assign to a fully qualified variable" do - expect { - @parser.parse("$one::two = yay") - }.to raise_error(Puppet::ParseError, /Cannot assign to variables in other namespaces/) - end - - it "should parse assignment of undef" do - tree = @parser.parse("$var = undef") - tree.code.children[0].should be_an_instance_of Puppet::Parser::AST::VarDef - tree.code.children[0].value.should be_an_instance_of Puppet::Parser::AST::Undef - end - - it "should treat classes as case insensitive" do - @classic_parser.known_resource_types.import_ast(@parser.parse("class yayness {}"), '') - @classic_parser.known_resource_types.hostclass('yayness'). - should == @classic_parser.find_hostclass("", "YayNess") - end - - it "should treat defines as case insensitive" do - @classic_parser.known_resource_types.import_ast(@parser.parse("define funtest {}"), '') - @classic_parser.known_resource_types.hostclass('funtest'). - should == @classic_parser.find_hostclass("", "fUntEst") - end - context "when parsing method calls" do - it "should parse method call with one param lambda" do - expect { @parser.parse("$a.each |$a|{ debug $a }") }.to_not raise_error - end - it "should parse method call with two param lambda" do - expect { @parser.parse("$a.each |$a,$b|{ debug $a }") }.to_not raise_error - end - it "should parse method call with two param lambda and default value" do - expect { @parser.parse("$a.each |$a,$b=1|{ debug $a }") }.to_not raise_error - end - it "should parse method call without lambda (statement)" do - expect { @parser.parse("$a.each") }.to_not raise_error - end - it "should parse method call without lambda (expression)" do - expect { @parser.parse("$x = $a.each + 1") }.to_not raise_error - end - context "a receiver expression of type" do - it "variable should be allowed" do - expect { @parser.parse("$a.each") }.to_not raise_error - end - it "hasharrayaccess should be allowed" do - expect { @parser.parse("$a[0][1].each") }.to_not raise_error - end - it "quoted text should be allowed" do - expect { @parser.parse("\"monkey\".each") }.to_not raise_error - expect { @parser.parse("'monkey'.each") }.to_not raise_error - end - it "selector text should be allowed" do - expect { @parser.parse("$a ? { 'banana'=>[1,2,3]}.each") }.to_not raise_error - end - it "function call should be allowed" do - expect { @parser.parse("duh(1,2,3).each") }.to_not raise_error - end - it "method call should be allowed" do - expect { @parser.parse("$a.foo.bar") }.to_not raise_error - end - it "chained method calls with lambda should be allowed" do - expect { @parser.parse("$a.foo||{}.bar||{}") }.to_not raise_error - end - end - end -end diff --git a/spec/unit/parser/type_loader_spec.rb b/spec/unit/parser/type_loader_spec.rb index 659ffa942..5454528a7 100755 --- a/spec/unit/parser/type_loader_spec.rb +++ b/spec/unit/parser/type_loader_spec.rb @@ -1,225 +1,224 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/parser/type_loader' require 'puppet/parser/parser_factory' -require 'puppet/parser/e_parser_adapter' require 'puppet_spec/modules' require 'puppet_spec/files' describe Puppet::Parser::TypeLoader do include PuppetSpec::Modules include PuppetSpec::Files let(:empty_hostclass) { Puppet::Parser::AST::Hostclass.new('') } let(:loader) { Puppet::Parser::TypeLoader.new(:myenv) } it "should support an environment" do loader = Puppet::Parser::TypeLoader.new(:myenv) loader.environment.name.should == :myenv end it "should delegate its known resource types to its environment" do loader.known_resource_types.should be_instance_of(Puppet::Resource::TypeCollection) end describe "when loading names from namespaces" do it "should do nothing if the name to import is an empty string" do loader.try_load_fqname(:hostclass, "").should be_nil end it "should attempt to import each generated name" do loader.expects(:import_from_modules).with("foo/bar").returns([]) loader.expects(:import_from_modules).with("foo").returns([]) loader.try_load_fqname(:hostclass, "foo::bar") end it "should attempt to load each possible name going from most to least specific" do path_order = sequence('path') ['foo/bar/baz', 'foo/bar', 'foo'].each do |path| Puppet::Parser::Files.expects(:find_manifests_in_modules).with(path, anything).returns([nil, []]).in_sequence(path_order) end loader.try_load_fqname(:hostclass, 'foo::bar::baz') end end describe "when importing" do let(:stub_parser) { stub 'Parser', :file= => nil, :parse => empty_hostclass } before(:each) do Puppet::Parser::ParserFactory.stubs(:parser).with(anything).returns(stub_parser) end it "should return immediately when imports are being ignored" do Puppet::Parser::Files.expects(:find_manifests_in_modules).never Puppet[:ignoreimport] = true loader.import("foo", "/path").should be_nil end it "should find all manifests matching the file or pattern" do Puppet::Parser::Files.expects(:find_manifests_in_modules).with("myfile", anything).returns ["modname", %w{one}] loader.import("myfile", "/path") end it "should pass the environment when looking for files" do Puppet::Parser::Files.expects(:find_manifests_in_modules).with(anything, loader.environment).returns ["modname", %w{one}] loader.import("myfile", "/path") end it "should fail if no files are found" do Puppet::Parser::Files.expects(:find_manifests_in_modules).returns [nil, []] lambda { loader.import("myfile", "/path") }.should raise_error(Puppet::ImportError) end it "should parse each found file" do Puppet::Parser::Files.expects(:find_manifests_in_modules).returns ["modname", [make_absolute("/one")]] loader.expects(:parse_file).with(make_absolute("/one")).returns(Puppet::Parser::AST::Hostclass.new('')) loader.import("myfile", "/path") end it "should not attempt to import files that have already been imported" do loader = Puppet::Parser::TypeLoader.new(:myenv) Puppet::Parser::Files.expects(:find_manifests_in_modules).twice.returns ["modname", %w{/one}] loader.import("myfile", "/path").should_not be_empty loader.import("myfile", "/path").should be_empty end end describe "when importing all" do before do @base = tmpdir("base") # Create two module path directories @modulebase1 = File.join(@base, "first") FileUtils.mkdir_p(@modulebase1) @modulebase2 = File.join(@base, "second") FileUtils.mkdir_p(@modulebase2) Puppet[:modulepath] = "#{@modulebase1}#{File::PATH_SEPARATOR}#{@modulebase2}" end def mk_module(basedir, name) PuppetSpec::Modules.create(name, basedir) end # We have to pass the base path so that we can # write to modules that are in the second search path def mk_manifests(base, mod, type, files) exts = {"ruby" => ".rb", "puppet" => ".pp"} files.collect do |file| name = mod.name + "::" + file.gsub("/", "::") path = File.join(base, mod.name, "manifests", file + exts[type]) FileUtils.mkdir_p(File.split(path)[0]) # write out the class if type == "ruby" File.open(path, "w") { |f| f.print "hostclass '#{name}' do\nend" } else File.open(path, "w") { |f| f.print "class #{name} {}" } end name end end it "should load all puppet manifests from all modules in the specified environment" do @module1 = mk_module(@modulebase1, "one") @module2 = mk_module(@modulebase2, "two") mk_manifests(@modulebase1, @module1, "puppet", %w{a b}) mk_manifests(@modulebase2, @module2, "puppet", %w{c d}) loader.import_all loader.environment.known_resource_types.hostclass("one::a").should be_instance_of(Puppet::Resource::Type) loader.environment.known_resource_types.hostclass("one::b").should be_instance_of(Puppet::Resource::Type) loader.environment.known_resource_types.hostclass("two::c").should be_instance_of(Puppet::Resource::Type) loader.environment.known_resource_types.hostclass("two::d").should be_instance_of(Puppet::Resource::Type) end it "should load all ruby manifests from all modules in the specified environment" do Puppet.expects(:deprecation_warning).at_least(1) @module1 = mk_module(@modulebase1, "one") @module2 = mk_module(@modulebase2, "two") mk_manifests(@modulebase1, @module1, "ruby", %w{a b}) mk_manifests(@modulebase2, @module2, "ruby", %w{c d}) loader.import_all loader.environment.known_resource_types.hostclass("one::a").should be_instance_of(Puppet::Resource::Type) loader.environment.known_resource_types.hostclass("one::b").should be_instance_of(Puppet::Resource::Type) loader.environment.known_resource_types.hostclass("two::c").should be_instance_of(Puppet::Resource::Type) loader.environment.known_resource_types.hostclass("two::d").should be_instance_of(Puppet::Resource::Type) end it "should not load manifests from duplicate modules later in the module path" do @module1 = mk_module(@modulebase1, "one") # duplicate @module2 = mk_module(@modulebase2, "one") mk_manifests(@modulebase1, @module1, "puppet", %w{a}) mk_manifests(@modulebase2, @module2, "puppet", %w{c}) loader.import_all loader.environment.known_resource_types.hostclass("one::c").should be_nil end it "should load manifests from subdirectories" do @module1 = mk_module(@modulebase1, "one") mk_manifests(@modulebase1, @module1, "puppet", %w{a a/b a/b/c}) loader.import_all loader.environment.known_resource_types.hostclass("one::a::b").should be_instance_of(Puppet::Resource::Type) loader.environment.known_resource_types.hostclass("one::a::b::c").should be_instance_of(Puppet::Resource::Type) end it "should skip modules that don't have manifests" do @module1 = mk_module(@modulebase1, "one") @module2 = mk_module(@modulebase2, "two") mk_manifests(@modulebase2, @module2, "ruby", %w{c d}) loader.import_all loader.environment.known_resource_types.hostclass("one::a").should be_nil loader.environment.known_resource_types.hostclass("two::c").should be_instance_of(Puppet::Resource::Type) loader.environment.known_resource_types.hostclass("two::d").should be_instance_of(Puppet::Resource::Type) end end describe "when parsing a file" do it "should create a new parser instance for each file using the current environment" do parser = stub 'Parser', :file= => nil, :parse => empty_hostclass Puppet::Parser::ParserFactory.expects(:parser).twice.with(loader.environment).returns(parser) loader.parse_file("/my/file") loader.parse_file("/my/other_file") end it "should assign the parser its file and parse" do parser = mock 'parser' Puppet::Parser::ParserFactory.expects(:parser).with(loader.environment).returns(parser) parser.expects(:file=).with("/my/file") parser.expects(:parse).returns(empty_hostclass) loader.parse_file("/my/file") end end it "should be able to add classes to the current resource type collection" do file = tmpfile("simple_file.pp") File.open(file, "w") { |f| f.puts "class foo {}" } loader.import(File.basename(file), File.dirname(file)) loader.known_resource_types.hostclass("foo").should be_instance_of(Puppet::Resource::Type) end end