diff --git a/spec/integration/parser/collector_spec.rb b/spec/integration/parser/collector_spec.rb index aa227f613..02fdcbb61 100755 --- a/spec/integration/parser/collector_spec.rb +++ b/spec/integration/parser/collector_spec.rb @@ -1,317 +1,324 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet_spec/compiler' require 'puppet/parser/collector' describe Puppet::Parser::Collector do include PuppetSpec::Compiler def expect_the_message_to_be(expected_messages, code, node = Puppet::Node.new('the node')) catalog = compile_to_catalog(code, node) messages = catalog.resources.find_all { |resource| resource.type == 'Notify' }. collect { |notify| notify[:message] } messages.should include(*expected_messages) end shared_examples_for "virtual resource collection" do it "matches everything when no query given" do expect_the_message_to_be(["the other message", "the message"], <<-MANIFEST) @notify { "testing": message => "the message" } @notify { "other": message => "the other message" } Notify <| |> MANIFEST end it "matches regular resources " do expect_the_message_to_be(["changed", "changed"], <<-MANIFEST) notify { "testing": message => "the message" } notify { "other": message => "the other message" } Notify <| |> { message => "changed" } MANIFEST end it "matches on tags" do expect_the_message_to_be(["wanted"], <<-MANIFEST) @notify { "testing": tag => ["one"], message => "wanted" } @notify { "other": tag => ["two"], message => "unwanted" } Notify <| tag == one |> MANIFEST end it "matches on title" do expect_the_message_to_be(["the message"], <<-MANIFEST) @notify { "testing": message => "the message" } Notify <| title == "testing" |> MANIFEST end it "matches on other parameters" do expect_the_message_to_be(["the message"], <<-MANIFEST) @notify { "testing": message => "the message" } @notify { "other testing": message => "the wrong message" } Notify <| message == "the message" |> MANIFEST end it "matches against elements of an array valued parameter" do expect_the_message_to_be([["the", "message"]], <<-MANIFEST) @notify { "testing": message => ["the", "message"] } @notify { "other testing": message => ["not", "here"] } Notify <| message == "message" |> MANIFEST end it "matches with bare word" do expect_the_message_to_be(["wanted"], <<-MANIFEST) @notify { "testing": tag => ["one"], message => "wanted" } Notify <| tag == one |> MANIFEST end it "matches with single quoted string" do expect_the_message_to_be(["wanted"], <<-MANIFEST) @notify { "testing": tag => ["one"], message => "wanted" } Notify <| tag == 'one' |> MANIFEST end it "matches with double quoted string" do expect_the_message_to_be(["wanted"], <<-MANIFEST) @notify { "testing": tag => ["one"], message => "wanted" } Notify <| tag == "one" |> MANIFEST end it "matches with double quoted string with interpolated expression" do expect_the_message_to_be(["wanted"], <<-MANIFEST) @notify { "testing": tag => ["one"], message => "wanted" } $x = 'one' Notify <| tag == "$x" |> MANIFEST end it "allows criteria to be combined with 'and'" do expect_the_message_to_be(["the message"], <<-MANIFEST) @notify { "testing": message => "the message" } @notify { "other": message => "the message" } Notify <| title == "testing" and message == "the message" |> MANIFEST end it "allows criteria to be combined with 'or'" do expect_the_message_to_be(["the message", "other message"], <<-MANIFEST) @notify { "testing": message => "the message" } @notify { "other": message => "other message" } @notify { "yet another": message => "different message" } Notify <| title == "testing" or message == "other message" |> MANIFEST end it "allows criteria to be combined with 'or'" do expect_the_message_to_be(["the message", "other message"], <<-MANIFEST) @notify { "testing": message => "the message" } @notify { "other": message => "other message" } @notify { "yet another": message => "different message" } Notify <| title == "testing" or message == "other message" |> MANIFEST end it "allows criteria to be grouped with parens" do expect_the_message_to_be(["the message", "different message"], <<-MANIFEST) @notify { "testing": message => "different message", withpath => true } @notify { "other": message => "the message" } @notify { "yet another": message => "the message", withpath => true } Notify <| (title == "testing" or message == "the message") and withpath == true |> MANIFEST end it "does not do anything if nothing matches" do expect_the_message_to_be([], <<-MANIFEST) @notify { "testing": message => "different message" } Notify <| title == "does not exist" |> MANIFEST end it "excludes items with inequalities" do expect_the_message_to_be(["good message"], <<-MANIFEST) @notify { "testing": message => "good message" } @notify { "the wrong one": message => "bad message" } Notify <| title != "the wrong one" |> MANIFEST end it "does not exclude resources with unequal arrays" do expect_the_message_to_be(["message", ["not this message", "or this one"]], <<-MANIFEST) @notify { "testing": message => "message" } @notify { "the wrong one": message => ["not this message", "or this one"] } Notify <| message != "not this message" |> MANIFEST end it "does not exclude tags with inequalities" do expect_the_message_to_be(["wanted message", "the way it works"], <<-MANIFEST) @notify { "testing": tag => ["wanted"], message => "wanted message" } @notify { "other": tag => ["why"], message => "the way it works" } Notify <| tag != "why" |> MANIFEST end it "does not collect classes" do node = Puppet::Node.new('the node') expect do catalog = compile_to_catalog(<<-MANIFEST, node) class theclass { @notify { "testing": message => "good message" } } Class <| |> MANIFEST end.to raise_error(/Classes cannot be collected/) end it "does not collect resources that don't exist" do node = Puppet::Node.new('the node') expect do catalog = compile_to_catalog(<<-MANIFEST, node) class theclass { @notify { "testing": message => "good message" } } SomeResource <| |> MANIFEST end.to raise_error(/Resource type someresource doesn't exist/) end context "overrides" do it "modifies an existing array" do expect_the_message_to_be([["original message", "extra message"]], <<-MANIFEST) @notify { "testing": message => ["original message"] } Notify <| |> { message +> "extra message" } MANIFEST end it "converts a scalar to an array" do expect_the_message_to_be([["original message", "extra message"]], <<-MANIFEST) @notify { "testing": message => "original message" } Notify <| |> { message +> "extra message" } MANIFEST end it "collects with override when inside a class (#10963)" do expect_the_message_to_be(["overridden message"], <<-MANIFEST) @notify { "testing": message => "original message" } include collector_test class collector_test { Notify <| |> { message => "overridden message" } } MANIFEST end it "collects with override when inside a define (#10963)" do expect_the_message_to_be(["overridden message"], <<-MANIFEST) @notify { "testing": message => "original message" } collector_test { testing: } define collector_test() { Notify <| |> { message => "overridden message" } } MANIFEST end # Catches regression in implemented behavior, this is not to be taken as this is the wanted behavior # but it has been this way for a long time. it "collects and overrides user defined resources immediately (before queue is evaluated)" do expect_the_message_to_be(["overridden"], <<-MANIFEST) define foo($message) { notify { "testing": message => $message } } foo { test: message => 'given' } Foo <| |> { message => 'overridden' } MANIFEST end # Catches regression in implemented behavior, this is not to be taken as this is the wanted behavior # but it has been this way for a long time. it "collects and overrides user defined resources immediately (virtual resources not queued)" do expect_the_message_to_be(["overridden"], <<-MANIFEST) define foo($message) { @notify { "testing": message => $message } } foo { test: message => 'given' } Notify <| |> # must be collected or the assertion does not find it Foo <| |> { message => 'overridden' } MANIFEST end # Catches regression in implemented behavior, this is not to be taken as this is the wanted behavior # but it has been this way for a long time. # Note difference from none +> case where the override takes effect it "collects and overrides user defined resources with +>" do expect_the_message_to_be([["given", "overridden"]], <<-MANIFEST) define foo($message) { notify { "$name": message => $message } } foo { test: message => ['given'] } Notify <| |> { message +> ['overridden'] } MANIFEST end it "collects and overrides virtual resources multiple times using multiple collects" do expect_the_message_to_be(["overridden2"], <<-MANIFEST) @notify { "testing": message => "original" } Notify <| |> { message => 'overridden1' } Notify <| |> { message => 'overridden2' } MANIFEST end it "collects and overrides non virtual resources multiple times using multiple collects" do expect_the_message_to_be(["overridden2"], <<-MANIFEST) notify { "testing": message => "original" } Notify <| |> { message => 'overridden1' } Notify <| |> { message => 'overridden2' } MANIFEST end end end describe "in the current parser" do before :each do Puppet[:parser] = 'current' end it_behaves_like "virtual resource collection" end describe "in the future parser" do before :each do Puppet[:parser] = 'future' end it_behaves_like "virtual resource collection" + + it "collects and overrides attributes specified with a * => hash" do + expect_the_message_to_be(["overridden1"], <<-MANIFEST) + notify { "testing": message => "original" } + Notify <| |> { * => { message => 'overridden1' }} + MANIFEST + end end end diff --git a/spec/integration/parser/resource_expressions_spec.rb b/spec/integration/parser/resource_expressions_spec.rb index bac1f77ad..ca6b93c7d 100644 --- a/spec/integration/parser/resource_expressions_spec.rb +++ b/spec/integration/parser/resource_expressions_spec.rb @@ -1,299 +1,301 @@ require 'spec_helper' require 'puppet_spec/language' describe "Puppet resource expressions" do extend PuppetSpec::Language describe "future parser" do before :each do Puppet[:parser] = 'future' end produces( "$a = notify $b = example $c = { message => hello } @@Resource[$a] { $b: * => $c } realize(Resource[$a, $b]) " => "Notify[example][message] == 'hello'") context "resource titles" do produces( "notify { thing: }" => "defined(Notify[thing])", "$x = thing notify { $x: }" => "defined(Notify[thing])", "notify { [thing]: }" => "defined(Notify[thing])", "$x = [thing] notify { $x: }" => "defined(Notify[thing])", "notify { [[nested, array]]: }" => "defined(Notify[nested]) and defined(Notify[array])", "$x = [[nested, array]] notify { $x: }" => "defined(Notify[nested]) and defined(Notify[array])", "notify { []: }" => [], # this asserts nothing added "$x = [] notify { $x: }" => [], # this asserts nothing added "notify { default: }" => "!defined(Notify['default'])", # nothing created because this is just a local default "$x = default notify { $x: }" => "!defined(Notify['default'])") fails( "notify { '': }" => /Empty string title/, "$x = '' notify { $x: }" => /Empty string title/, "notify { 1: }" => /Illegal title type.*Expected String, got Integer/, "$x = 1 notify { $x: }" => /Illegal title type.*Expected String, got Integer/, "notify { [1]: }" => /Illegal title type.*Expected String, got Integer/, "$x = [1] notify { $x: }" => /Illegal title type.*Expected String, got Integer/, "notify { 3.0: }" => /Illegal title type.*Expected String, got Float/, "$x = 3.0 notify { $x: }" => /Illegal title type.*Expected String, got Float/, "notify { [3.0]: }" => /Illegal title type.*Expected String, got Float/, "$x = [3.0] notify { $x: }" => /Illegal title type.*Expected String, got Float/, "notify { true: }" => /Illegal title type.*Expected String, got Boolean/, "$x = true notify { $x: }" => /Illegal title type.*Expected String, got Boolean/, "notify { [true]: }" => /Illegal title type.*Expected String, got Boolean/, "$x = [true] notify { $x: }" => /Illegal title type.*Expected String, got Boolean/, "notify { [false]: }" => /Illegal title type.*Expected String, got Boolean/, "$x = [false] notify { $x: }" => /Illegal title type.*Expected String, got Boolean/, "notify { undef: }" => /Missing title.*undef/, "$x = undef notify { $x: }" => /Missing title.*undef/, "notify { [undef]: }" => /Missing title.*undef/, "$x = [undef] notify { $x: }" => /Missing title.*undef/, "notify { {nested => hash}: }" => /Illegal title type.*Expected String, got Hash/, "$x = {nested => hash} notify { $x: }" => /Illegal title type.*Expected String, got Hash/, "notify { [{nested => hash}]: }" => /Illegal title type.*Expected String, got Hash/, "$x = [{nested => hash}] notify { $x: }" => /Illegal title type.*Expected String, got Hash/, "notify { /regexp/: }" => /Illegal title type.*Expected String, got Regexp/, "$x = /regexp/ notify { $x: }" => /Illegal title type.*Expected String, got Regexp/, "notify { [/regexp/]: }" => /Illegal title type.*Expected String, got Regexp/, "$x = [/regexp/] notify { $x: }" => /Illegal title type.*Expected String, got Regexp/, "notify { [dupe, dupe]: }" => /The title 'dupe' has already been used/, "notify { dupe:; dupe: }" => /The title 'dupe' has already been used/, "notify { [dupe]:; dupe: }" => /The title 'dupe' has already been used/, "notify { [default, default]:}" => /The title 'default' has already been used/, "notify { default:; default:}" => /The title 'default' has already been used/, "notify { [default]:; default:}" => /The title 'default' has already been used/) end context "type names" do produces( "notify { testing: }" => "defined(Notify[testing])") produces( "$a = notify; Resource[$a] { testing: }" => "defined(Notify[testing])") produces( "Resource['notify'] { testing: }" => "defined(Notify[testing])") produces( "Resource[sprintf('%s', 'notify')] { testing: }" => "defined(Notify[testing])") produces( "$a = ify; Resource[\"not$a\"] { testing: }" => "defined(Notify[testing])") produces( "Notify { testing: }" => "defined(Notify[testing])") produces( "Resource[Notify] { testing: }" => "defined(Notify[testing])") produces( "Resource['Notify'] { testing: }" => "defined(Notify[testing])") produces( "class a { notify { testing: } } class { a: }" => "defined(Notify[testing])") produces( "class a { notify { testing: } } Class { a: }" => "defined(Notify[testing])") produces( "class a { notify { testing: } } Resource['class'] { a: }" => "defined(Notify[testing])") produces( "define a::b { notify { testing: } } a::b { title: }" => "defined(Notify[testing])") produces( "define a::b { notify { testing: } } A::B { title: }" => "defined(Notify[testing])") produces( "define a::b { notify { testing: } } Resource['a::b'] { title: }" => "defined(Notify[testing])") fails( "'class' { a: }" => /Illegal Resource Type expression.*got String/) fails( "'' { testing: }" => /Illegal Resource Type expression.*got String/) fails( "1 { testing: }" => /Illegal Resource Type expression.*got Integer/) fails( "3.0 { testing: }" => /Illegal Resource Type expression.*got Float/) fails( "true { testing: }" => /Illegal Resource Type expression.*got Boolean/) fails( "'not correct' { testing: }" => /Illegal Resource Type expression.*got String/) fails( "Notify[hi] { testing: }" => /Illegal Resource Type expression.*got Notify\['hi'\]/) fails( "[Notify, File] { testing: }" => /Illegal Resource Type expression.*got Array\[Type\[Resource\]\]/) fails( "define a::b { notify { testing: } } 'a::b' { title: }" => /Illegal Resource Type expression.*got String/) fails( "Does::Not::Exist { title: }" => /Invalid resource type does::not::exist/) end context "local defaults" do produces( "notify { example:; default: message => defaulted }" => "Notify[example][message] == 'defaulted'", "notify { example: message => specific; default: message => defaulted }" => "Notify[example][message] == 'specific'", "notify { example: message => undef; default: message => defaulted }" => "Notify[example][message] == undef", "notify { [example, other]: ; default: message => defaulted }" => "Notify[example][message] == 'defaulted' and Notify[other][message] == 'defaulted'", "notify { [example, default]: message => set; other: }" => "Notify[example][message] == 'set' and Notify[other][message] == 'set'") end context "order of evaluation" do fails("notify { hi: message => value; bye: message => Notify[hi][message] }" => /Resource not found: Notify\['hi'\]/) produces("notify { hi: message => (notify { param: message => set }); bye: message => Notify[param][message] }" => "defined(Notify[hi]) and Notify[bye][message] == 'set'") fails("notify { bye: message => Notify[param][message]; hi: message => (notify { param: message => set }) }" => /Resource not found: Notify\['param'\]/) end context "parameters" do produces( "notify { title: message => set }" => "Notify[title][message] == 'set'", "$x = set notify { title: message => $x }" => "Notify[title][message] == 'set'", "notify { title: *=> { message => set } }" => "Notify[title][message] == 'set'", "$x = { message => set } notify { title: * => $x }" => "Notify[title][message] == 'set'", # picks up defaults "$x = { owner => the_x } $y = { mode => '0666' } $t = '/tmp/x' file { default: * => $x; $t: path => '/somewhere', * => $y }" => "File[$t][mode] == '0666' and File[$t][owner] == 'the_x' and File[$t][path] == '/somewhere'", # explicit wins over default - no error "$x = { owner => the_x, mode => '0777' } $y = { mode => '0666' } $t = '/tmp/x' file { default: * => $x; $t: path => '/somewhere', * => $y }" => "File[$t][mode] == '0666' and File[$t][owner] == 'the_x' and File[$t][path] == '/somewhere'") + produces("notify{title:}; Notify[title] { * => { message => set}}" => "Notify[title][message] == 'set'") + fails("notify { title: unknown => value }" => /Invalid parameter unknown/) # this really needs to be a better error message. fails("notify { title: * => { hash => value }, message => oops }" => /Invalid parameter hash/) # should this be a better error message? fails("notify { title: message => oops, * => { hash => value } }" => /Invalid parameter hash/) fails("notify { title: * => { unknown => value } }" => /Invalid parameter unknown/) fails(" $x = { mode => '0666' } $y = { owner => the_y } $t = '/tmp/x' file { $t: * => $x, * => $y }" => /Unfolding of attributes from Hash can only be used once per resource body/) end context "virtual" do produces( "@notify { example: }" => "!defined(Notify[example])", "@notify { example: } realize(Notify[example])" => "defined(Notify[example])", "@notify { virtual: message => set } notify { real: message => Notify[virtual][message] }" => "Notify[real][message] == 'set'") end context "exported" do produces( "@@notify { example: }" => "!defined(Notify[example])", "@@notify { example: } realize(Notify[example])" => "defined(Notify[example])", "@@notify { exported: message => set } notify { real: message => Notify[exported][message] }" => "Notify[real][message] == 'set'") end context "explicit undefs" do # PUP-3505 produces(" $x = 10 define foo($x = undef) { notify { example: message => \"'$x'\" } } foo {'blah': x => undef } " => "Notify[example][message] == \"''\"") end end describe "current parser" do before :each do Puppet[:parser] = 'current' end produces( "notify { thing: }" => ["Notify[thing]"], "$x = thing notify { $x: }" => ["Notify[thing]"], "notify { [thing]: }" => ["Notify[thing]"], "$x = [thing] notify { $x: }" => ["Notify[thing]"], "notify { [[nested, array]]: }" => ["Notify[nested]", "Notify[array]"], "$x = [[nested, array]] notify { $x: }" => ["Notify[nested]", "Notify[array]"], # deprecate? "notify { 1: }" => ["Notify[1]"], "$x = 1 notify { $x: }" => ["Notify[1]"], # deprecate? "notify { [1]: }" => ["Notify[1]"], "$x = [1] notify { $x: }" => ["Notify[1]"], # deprecate? "notify { 3.0: }" => ["Notify[3.0]"], "$x = 3.0 notify { $x: }" => ["Notify[3.0]"], # deprecate? "notify { [3.0]: }" => ["Notify[3.0]"], "$x = [3.0] notify { $x: }" => ["Notify[3.0]"]) # :( fails( "notify { true: }" => /Syntax error/) produces("$x = true notify { $x: }" => ["Notify[true]"]) # this makes no sense given the [false] case produces( "notify { [true]: }" => ["Notify[true]"], "$x = [true] notify { $x: }" => ["Notify[true]"]) # *sigh* fails( "notify { false: }" => /Syntax error/, "$x = false notify { $x: }" => /No title provided and :notify is not a valid resource reference/, "notify { [false]: }" => /No title provided and :notify is not a valid resource reference/, "$x = [false] notify { $x: }" => /No title provided and :notify is not a valid resource reference/) # works for variable value, not for literal. deprecate? fails("notify { undef: }" => /Syntax error/) produces( "$x = undef notify { $x: }" => ["Notify[undef]"], # deprecate? "notify { [undef]: }" => ["Notify[undef]"], "$x = [undef] notify { $x: }" => ["Notify[undef]"]) fails("notify { {nested => hash}: }" => /Syntax error/) #produces("$x = {nested => hash} notify { $x: }" => ["Notify[{nested => hash}]"]) #it is created, but isn't possible to reference the resource. deprecate? #produces("notify { [{nested => hash}]: }" => ["Notify[{nested => hash}]"]) #it is created, but isn't possible to reference the resource. deprecate? #produces("$x = [{nested => hash}] notify { $x: }" => ["Notify[{nested => hash}]"]) #it is created, but isn't possible to reference the resource. deprecate? fails( "notify { /regexp/: }" => /Syntax error/, "$x = /regexp/ notify { $x: }" => /Syntax error/, "notify { [/regexp/]: }" => /Syntax error/, "$x = [/regexp/] notify { $x: }" => /Syntax error/, "notify { default: }" => /Syntax error/, "$x = default notify { $x: }" => /Syntax error/, "notify { [default]: }" => /Syntax error/, "$x = [default] notify { $x: }" => /Syntax error/) end end