summaryrefslogtreecommitdiff
path: root/spec/integration/parser
diff options
context:
space:
mode:
Diffstat (limited to 'spec/integration/parser')
-rw-r--r--spec/integration/parser/catalog_spec.rb20
-rw-r--r--spec/integration/parser/class_spec.rb37
-rwxr-xr-xspec/integration/parser/collector_spec.rb309
-rwxr-xr-xspec/integration/parser/compiler_spec.rb774
-rw-r--r--spec/integration/parser/conditionals_spec.rb117
-rw-r--r--spec/integration/parser/future_compiler_spec.rb396
-rw-r--r--spec/integration/parser/node_spec.rb185
-rw-r--r--spec/integration/parser/resource_expressions_spec.rb286
-rwxr-xr-xspec/integration/parser/ruby_manifest_spec.rb4
-rw-r--r--spec/integration/parser/scope_spec.rb245
10 files changed, 1744 insertions, 629 deletions
diff --git a/spec/integration/parser/catalog_spec.rb b/spec/integration/parser/catalog_spec.rb
index e37eb591a..39aeb394e 100644
--- a/spec/integration/parser/catalog_spec.rb
+++ b/spec/integration/parser/catalog_spec.rb
@@ -75,6 +75,14 @@ describe "A catalog" do
expect(resources_in(agent_catalog)).to_not include(*exported_resources)
end
end
+ end
+
+ describe 'using classic parser' do
+ before :each do
+ Puppet[:parser] = 'current'
+ end
+ it_behaves_like 'when compiled' do
+ end
it "compiles resource creation from appended array as two separate resources" do
# moved here from acceptance test "jeff_append_to_array.rb"
@@ -92,14 +100,6 @@ describe "A catalog" do
end
end
- describe 'using classic parser' do
- before :each do
- Puppet[:parser] = 'current'
- end
- it_behaves_like 'when compiled' do
- end
- end
-
describe 'using future parser' do
before :each do
Puppet[:parser] = 'future'
@@ -113,9 +113,9 @@ describe "A catalog" do
end
def master_and_agent_catalogs_for(manifest)
- master_catalog = Puppet::Resource::Catalog::Compiler.new.filter(compile_to_catalog(manifest))
+ compiler = Puppet::Resource::Catalog::Compiler.new
+ master_catalog = compiler.filter(compile_to_catalog(manifest))
agent_catalog = Puppet::Resource::Catalog.convert_from(:pson, master_catalog.render(:pson))
-
[master_catalog, agent_catalog]
end
diff --git a/spec/integration/parser/class_spec.rb b/spec/integration/parser/class_spec.rb
new file mode 100644
index 000000000..9f63eb083
--- /dev/null
+++ b/spec/integration/parser/class_spec.rb
@@ -0,0 +1,37 @@
+require 'spec_helper'
+require 'puppet_spec/language'
+
+describe "Class expressions" do
+ extend PuppetSpec::Language
+
+ before :each do
+ Puppet[:parser] = 'future'
+ end
+
+ produces(
+ "class hi { }" => '!defined(Class[Hi])',
+
+ "class hi { } include hi" => 'defined(Class[Hi])',
+ "include(hi) class hi { }" => 'defined(Class[Hi])',
+
+ "class hi { } class { hi: }" => 'defined(Class[Hi])',
+ "class { hi: } class hi { }" => 'defined(Class[Hi])',
+
+ "class bye { } class hi inherits bye { } include hi" => 'defined(Class[Hi]) and defined(Class[Bye])')
+
+ produces(<<-EXAMPLE => 'defined(Notify[foo]) and defined(Notify[bar]) and !defined(Notify[foo::bar])')
+ class bar { notify { 'bar': } }
+ class foo::bar { notify { 'foo::bar': } }
+ class foo inherits bar { notify { 'foo': } }
+
+ include foo
+ EXAMPLE
+
+ produces(<<-EXAMPLE => 'defined(Notify[foo]) and defined(Notify[bar]) and !defined(Notify[foo::bar])')
+ class bar { notify { 'bar': } }
+ class foo::bar { notify { 'foo::bar': } }
+ class foo inherits ::bar { notify { 'foo': } }
+
+ include foo
+ EXAMPLE
+end
diff --git a/spec/integration/parser/collector_spec.rb b/spec/integration/parser/collector_spec.rb
index 49ce74583..55ac66a5b 100755
--- a/spec/integration/parser/collector_spec.rb
+++ b/spec/integration/parser/collector_spec.rb
@@ -14,104 +14,263 @@ describe Puppet::Parser::Collector do
messages.should include(*expected_messages)
end
- it "matches on title" do
- expect_the_message_to_be(["the message"], <<-MANIFEST)
- @notify { "testing": message => "the message" }
+ 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 <| title == "testing" |>
- MANIFEST
- end
+ Notify <| |>
+ 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" }
+ 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 == "the message" |>
- MANIFEST
- end
+ Notify <| |> { message => "changed" }
+ 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" }
+ 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 <| title == "testing" and message == "the message" |>
- MANIFEST
- end
+ Notify <| tag == one |>
+ 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" }
+ it "matches on title" do
+ expect_the_message_to_be(["the message"], <<-MANIFEST)
+ @notify { "testing": message => "the message" }
- Notify <| title == "testing" or message == "other message" |>
- MANIFEST
- end
+ Notify <| title == "testing" |>
+ 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" }
+ 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 <| title == "testing" or message == "other message" |>
- MANIFEST
- end
+ Notify <| message == "the 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 }
+ 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 <| (title == "testing" or message == "the message") and withpath == true |>
- MANIFEST
- end
+ Notify <| message == "message" |>
+ MANIFEST
+ end
- it "does not do anything if nothing matches" do
- expect_the_message_to_be([], <<-MANIFEST)
- @notify { "testing": message => "different message" }
+ 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 == "does not exist" |>
- MANIFEST
- end
+ Notify <| title == "testing" and message == "the message" |>
+ 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" }
+ 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 != "the wrong one" |>
- MANIFEST
- end
+ Notify <| title == "testing" or message == "other message" |>
+ MANIFEST
+ end
- context "issue #10963" do
- it "collects with override when inside a class" do
- expect_the_message_to_be(["overridden message"], <<-MANIFEST)
- @notify { "testing": message => "original message" }
+ 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" }
- include collector_test
- class collector_test {
- Notify <| |> {
- message => "overridden 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 "collects with override when inside a define" do
- expect_the_message_to_be(["overridden message"], <<-MANIFEST)
- @notify { "testing": message => "original message" }
+ 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
+
+ context "overrides" do
+ it "modifies an existing array" do
+ expect_the_message_to_be([["original message", "extra message"]], <<-MANIFEST)
+ @notify { "testing": message => ["original message"] }
- collector_test { testing: }
- define collector_test() {
Notify <| |> {
- message => "overridden message"
+ message +> "extra message"
}
- }
- MANIFEST
+ 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"
+ end
end
diff --git a/spec/integration/parser/compiler_spec.rb b/spec/integration/parser/compiler_spec.rb
index f10ce1adc..e6069d834 100755
--- a/spec/integration/parser/compiler_spec.rb
+++ b/spec/integration/parser/compiler_spec.rb
@@ -15,499 +15,511 @@ describe "Puppet::Parser::Compiler" do
@scope = stub 'scope', :resource => @scope_resource, :source => mock("source")
end
- after do
- Puppet.settings.clear
- end
-
- # shared because tests are invoked both for classic and future parser
- #
- shared_examples_for "the compiler" do
- it "should be able to determine the configuration version from a local version control repository" do
- pending("Bug #14071 about semantics of Puppet::Util::Execute on Windows", :if => Puppet.features.microsoft_windows?) do
- # This should always work, because we should always be
- # in the puppet repo when we run this.
- version = %x{git rev-parse HEAD}.chomp
+ it "should be able to determine the configuration version from a local version control repository" do
+ pending("Bug #14071 about semantics of Puppet::Util::Execute on Windows", :if => Puppet.features.microsoft_windows?) do
+ # This should always work, because we should always be
+ # in the puppet repo when we run this.
+ version = %x{git rev-parse HEAD}.chomp
- Puppet.settings[:config_version] = 'git rev-parse HEAD'
+ Puppet.settings[:config_version] = 'git rev-parse HEAD'
- @parser = Puppet::Parser::ParserFactory.parser "development"
- @compiler = Puppet::Parser::Compiler.new(@node)
+ @parser = Puppet::Parser::ParserFactory.parser "development"
+ @compiler = Puppet::Parser::Compiler.new(@node)
- @compiler.catalog.version.should == version
- end
+ @compiler.catalog.version.should == version
end
+ end
+
+ it "should not create duplicate resources when a class is referenced both directly and indirectly by the node classifier (4792)" do
+ Puppet[:code] = <<-PP
+ class foo
+ {
+ notify { foo_notify: }
+ include bar
+ }
+ class bar
+ {
+ notify { bar_notify: }
+ }
+ PP
+
+ @node.stubs(:classes).returns(['foo', 'bar'])
+
+ catalog = Puppet::Parser::Compiler.compile(@node)
+
+ catalog.resource("Notify[foo_notify]").should_not be_nil
+ catalog.resource("Notify[bar_notify]").should_not be_nil
+ end
- it "should not create duplicate resources when a class is referenced both directly and indirectly by the node classifier (4792)" do
+ describe "when resolving class references" do
+ it "should favor local scope, even if there's an included class in topscope" do
Puppet[:code] = <<-PP
- class foo
- {
- notify { foo_notify: }
- include bar
+ class experiment {
+ class baz {
+ }
+ notify {"x" : require => Class[Baz] }
}
- class bar
- {
- notify { bar_notify: }
+ class baz {
}
+ include baz
+ include experiment
+ include experiment::baz
PP
- @node.stubs(:classes).returns(['foo', 'bar'])
+ catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode"))
- catalog = Puppet::Parser::Compiler.compile(@node)
+ notify_resource = catalog.resource( "Notify[x]" )
- catalog.resource("Notify[foo_notify]").should_not be_nil
- catalog.resource("Notify[bar_notify]").should_not be_nil
+ notify_resource[:require].title.should == "Experiment::Baz"
end
- describe "when resolving class references" do
- it "should favor local scope, even if there's an included class in topscope" do
- Puppet[:code] = <<-PP
- class experiment {
- class baz {
- }
- notify {"x" : require => Class[Baz] }
- }
+ it "should favor local scope, even if there's an unincluded class in topscope" do
+ Puppet[:code] = <<-PP
+ class experiment {
class baz {
}
- include baz
- include experiment
- include experiment::baz
- PP
-
- catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode"))
+ notify {"x" : require => Class[Baz] }
+ }
+ class baz {
+ }
+ include experiment
+ include experiment::baz
+ PP
- notify_resource = catalog.resource( "Notify[x]" )
+ catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode"))
- notify_resource[:require].title.should == "Experiment::Baz"
- end
+ notify_resource = catalog.resource( "Notify[x]" )
- it "should favor local scope, even if there's an unincluded class in topscope" do
- Puppet[:code] = <<-PP
- class experiment {
- class baz {
+ notify_resource[:require].title.should == "Experiment::Baz"
+ end
+ end
+ describe "(ticket #13349) when explicitly specifying top scope" do
+ ["class {'::bar::baz':}", "include ::bar::baz"].each do |include|
+ describe "with #{include}" do
+ it "should find the top level class" do
+ Puppet[:code] = <<-MANIFEST
+ class { 'foo::test': }
+ class foo::test {
+ #{include}
}
- notify {"x" : require => Class[Baz] }
- }
- class baz {
- }
- include experiment
- include experiment::baz
- PP
-
- catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode"))
+ class bar::baz {
+ notify { 'good!': }
+ }
+ class foo::bar::baz {
+ notify { 'bad!': }
+ }
+ MANIFEST
- notify_resource = catalog.resource( "Notify[x]" )
+ catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode"))
- notify_resource[:require].title.should == "Experiment::Baz"
- end
- end
- describe "(ticket #13349) when explicitly specifying top scope" do
- ["class {'::bar::baz':}", "include ::bar::baz"].each do |include|
- describe "with #{include}" do
- it "should find the top level class" do
- Puppet[:code] = <<-MANIFEST
- class { 'foo::test': }
- class foo::test {
- #{include}
- }
- class bar::baz {
- notify { 'good!': }
- }
- class foo::bar::baz {
- notify { 'bad!': }
- }
- MANIFEST
-
- catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode"))
-
- catalog.resource("Class[Bar::Baz]").should_not be_nil
- catalog.resource("Notify[good!]").should_not be_nil
- catalog.resource("Class[Foo::Bar::Baz]").should be_nil
- catalog.resource("Notify[bad!]").should be_nil
- end
+ catalog.resource("Class[Bar::Baz]").should_not be_nil
+ catalog.resource("Notify[good!]").should_not be_nil
+ catalog.resource("Class[Foo::Bar::Baz]").should be_nil
+ catalog.resource("Notify[bad!]").should be_nil
end
end
end
+ end
- it "should recompute the version after input files are re-parsed" do
- Puppet[:code] = 'class foo { }'
- Time.stubs(:now).returns(1)
- node = Puppet::Node.new('mynode')
- Puppet::Parser::Compiler.compile(node).version.should == 1
- Time.stubs(:now).returns(2)
- Puppet::Parser::Compiler.compile(node).version.should == 1 # no change because files didn't change
- Puppet::Resource::TypeCollection.any_instance.stubs(:stale?).returns(true).then.returns(false) # pretend change
- Puppet::Parser::Compiler.compile(node).version.should == 2
- end
-
- ['class', 'define', 'node'].each do |thing|
- it "should not allow '#{thing}' inside evaluated conditional constructs" do
- Puppet[:code] = <<-PP
- if true {
- #{thing} foo {
- }
- notify { decoy: }
- }
- PP
-
- begin
- Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode"))
- raise "compilation should have raised Puppet::Error"
- rescue Puppet::Error => e
- e.message.should =~ /at line 2/
- end
- end
- end
+ it "should recompute the version after input files are re-parsed" do
+ Puppet[:code] = 'class foo { }'
+ Time.stubs(:now).returns(1)
+ node = Puppet::Node.new('mynode')
+ Puppet::Parser::Compiler.compile(node).version.should == 1
+ Time.stubs(:now).returns(2)
+ Puppet::Parser::Compiler.compile(node).version.should == 1 # no change because files didn't change
+ Puppet::Resource::TypeCollection.any_instance.stubs(:stale?).returns(true).then.returns(false) # pretend change
+ Puppet::Parser::Compiler.compile(node).version.should == 2
+ end
- it "should not allow classes inside unevaluated conditional constructs" do
+ ['class', 'define', 'node'].each do |thing|
+ it "should not allow '#{thing}' inside evaluated conditional constructs" do
Puppet[:code] = <<-PP
- if false {
- class foo {
+ if true {
+ #{thing} foo {
}
+ notify { decoy: }
}
PP
- lambda { Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) }.should raise_error(Puppet::Error)
+ begin
+ Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode"))
+ raise "compilation should have raised Puppet::Error"
+ rescue Puppet::Error => e
+ e.message.should =~ /at line 2/
+ end
end
+ end
- describe "when defining relationships" do
- def extract_name(ref)
- ref.sub(/File\[(\w+)\]/, '\1')
- end
+ it "should not allow classes inside unevaluated conditional constructs" do
+ Puppet[:code] = <<-PP
+ if false {
+ class foo {
+ }
+ }
+ PP
- let(:node) { Puppet::Node.new('mynode') }
- let(:code) do
- <<-MANIFEST
- file { [a,b,c]:
- mode => 0644,
- }
- file { [d,e]:
- mode => 0755,
- }
- MANIFEST
- end
- let(:expected_relationships) { [] }
- let(:expected_subscriptions) { [] }
+ lambda { Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) }.should raise_error(Puppet::Error)
+ end
- before :each do
- Puppet[:code] = code
- end
+ describe "when defining relationships" do
+ def extract_name(ref)
+ ref.sub(/File\[(\w+)\]/, '\1')
+ end
+
+ let(:node) { Puppet::Node.new('mynode') }
+ let(:code) do
+ <<-MANIFEST
+ file { [a,b,c]:
+ mode => '0644',
+ }
+ file { [d,e]:
+ mode => '0755',
+ }
+ MANIFEST
+ end
+ let(:expected_relationships) { [] }
+ let(:expected_subscriptions) { [] }
- after :each do
- catalog = Puppet::Parser::Compiler.compile(node)
+ before :each do
+ Puppet[:code] = code
+ end
- resources = catalog.resources.select { |res| res.type == 'File' }
+ after :each do
+ catalog = Puppet::Parser::Compiler.compile(node)
- actual_relationships, actual_subscriptions = [:before, :notify].map do |relation|
- resources.map do |res|
- dependents = Array(res[relation])
- dependents.map { |ref| [res.title, extract_name(ref)] }
- end.inject(&:concat)
- end
+ resources = catalog.resources.select { |res| res.type == 'File' }
- actual_relationships.should =~ expected_relationships
- actual_subscriptions.should =~ expected_subscriptions
+ actual_relationships, actual_subscriptions = [:before, :notify].map do |relation|
+ resources.map do |res|
+ dependents = Array(res[relation])
+ dependents.map { |ref| [res.title, extract_name(ref)] }
+ end.inject(&:concat)
end
- it "should create a relationship" do
- code << "File[a] -> File[b]"
+ actual_relationships.should =~ expected_relationships
+ actual_subscriptions.should =~ expected_subscriptions
+ end
- expected_relationships << ['a','b']
- end
+ it "should create a relationship" do
+ code << "File[a] -> File[b]"
- it "should create a subscription" do
- code << "File[a] ~> File[b]"
+ expected_relationships << ['a','b']
+ end
- expected_subscriptions << ['a', 'b']
- end
+ it "should create a subscription" do
+ code << "File[a] ~> File[b]"
- it "should create relationships using title arrays" do
- code << "File[a,b] -> File[c,d]"
+ expected_subscriptions << ['a', 'b']
+ end
- expected_relationships.concat [
- ['a', 'c'],
- ['b', 'c'],
- ['a', 'd'],
- ['b', 'd'],
- ]
- end
+ it "should create relationships using title arrays" do
+ code << "File[a,b] -> File[c,d]"
- it "should create relationships using collection expressions" do
- code << "File <| mode == 0644 |> -> File <| mode == 0755 |>"
-
- expected_relationships.concat [
- ['a', 'd'],
- ['b', 'd'],
- ['c', 'd'],
- ['a', 'e'],
- ['b', 'e'],
- ['c', 'e'],
- ]
- end
+ expected_relationships.concat [
+ ['a', 'c'],
+ ['b', 'c'],
+ ['a', 'd'],
+ ['b', 'd'],
+ ]
+ end
- it "should create relationships using resource names" do
- code << "'File[a]' -> 'File[b]'"
+ it "should create relationships using collection expressions" do
+ code << "File <| mode == 0644 |> -> File <| mode == 0755 |>"
+
+ expected_relationships.concat [
+ ['a', 'd'],
+ ['b', 'd'],
+ ['c', 'd'],
+ ['a', 'e'],
+ ['b', 'e'],
+ ['c', 'e'],
+ ]
+ end
- expected_relationships << ['a', 'b']
- end
+ it "should create relationships using resource names" do
+ code << "'File[a]' -> 'File[b]'"
- it "should create relationships using variables" do
- code << <<-MANIFEST
- $var = File[a]
- $var -> File[b]
- MANIFEST
+ expected_relationships << ['a', 'b']
+ end
- expected_relationships << ['a', 'b']
- end
+ it "should create relationships using variables" do
+ code << <<-MANIFEST
+ $var = File[a]
+ $var -> File[b]
+ MANIFEST
- it "should create relationships using case statements" do
- code << <<-MANIFEST
- $var = 10
- case $var {
- 10: {
- file { s1: }
- }
- 12: {
- file { s2: }
- }
+ expected_relationships << ['a', 'b']
+ end
+
+ it "should create relationships using case statements" do
+ code << <<-MANIFEST
+ $var = 10
+ case $var {
+ 10: {
+ file { s1: }
}
- ->
- case $var + 2 {
- 10: {
- file { t1: }
- }
- 12: {
- file { t2: }
- }
+ 12: {
+ file { s2: }
}
- MANIFEST
+ }
+ ->
+ case $var + 2 {
+ 10: {
+ file { t1: }
+ }
+ 12: {
+ file { t2: }
+ }
+ }
+ MANIFEST
- expected_relationships << ['s1', 't2']
- end
+ expected_relationships << ['s1', 't2']
+ end
- it "should create relationships using array members" do
- code << <<-MANIFEST
- $var = [ [ [ File[a], File[b] ] ] ]
- $var[0][0][0] -> $var[0][0][1]
- MANIFEST
+ it "should create relationships using array members" do
+ code << <<-MANIFEST
+ $var = [ [ [ File[a], File[b] ] ] ]
+ $var[0][0][0] -> $var[0][0][1]
+ MANIFEST
- expected_relationships << ['a', 'b']
- end
+ expected_relationships << ['a', 'b']
+ end
- it "should create relationships using hash members" do
- code << <<-MANIFEST
- $var = {'foo' => {'bar' => {'source' => File[a], 'target' => File[b]}}}
- $var[foo][bar][source] -> $var[foo][bar][target]
- MANIFEST
+ it "should create relationships using hash members" do
+ code << <<-MANIFEST
+ $var = {'foo' => {'bar' => {'source' => File[a], 'target' => File[b]}}}
+ $var[foo][bar][source] -> $var[foo][bar][target]
+ MANIFEST
- expected_relationships << ['a', 'b']
- end
+ expected_relationships << ['a', 'b']
+ end
- it "should create relationships using resource declarations" do
- code << "file { l: } -> file { r: }"
+ it "should create relationships using resource declarations" do
+ code << "file { l: } -> file { r: }"
- expected_relationships << ['l', 'r']
- end
+ expected_relationships << ['l', 'r']
+ end
- it "should chain relationships" do
- code << "File[a] -> File[b] ~> File[c] <- File[d] <~ File[e]"
+ it "should chain relationships" do
+ code << "File[a] -> File[b] ~> File[c] <- File[d] <~ File[e]"
- expected_relationships << ['a', 'b'] << ['d', 'c']
- expected_subscriptions << ['b', 'c'] << ['e', 'd']
- end
+ expected_relationships << ['a', 'b'] << ['d', 'c']
+ expected_subscriptions << ['b', 'c'] << ['e', 'd']
end
+ end
- context 'when working with immutable node data' do
- context 'and have opted in to immutable_node_data' do
- before :each do
- Puppet[:immutable_node_data] = true
- end
+ context 'when working with immutable node data' do
+ context 'and have opted in to immutable_node_data' do
+ before :each do
+ Puppet[:immutable_node_data] = true
+ end
- def node_with_facts(facts)
- Puppet[:facts_terminus] = :memory
- Puppet::Node::Facts.indirection.save(Puppet::Node::Facts.new("testing", facts))
- node = Puppet::Node.new("testing")
- node.fact_merge
- node
- end
+ def node_with_facts(facts)
+ Puppet[:facts_terminus] = :memory
+ Puppet::Node::Facts.indirection.save(Puppet::Node::Facts.new("testing", facts))
+ node = Puppet::Node.new("testing")
+ node.fact_merge
+ node
+ end
- matcher :fail_compile_with do |node, message_regex|
- match do |manifest|
- @error = nil
- begin
- compile_to_catalog(manifest, node)
- false
- rescue Puppet::Error => e
- @error = e
- message_regex.match(e.message)
- end
+ matcher :fail_compile_with do |node, message_regex|
+ match do |manifest|
+ @error = nil
+ begin
+ PuppetSpec::Compiler.compile_to_catalog(manifest, node)
+ false
+ rescue Puppet::Error => e
+ @error = e
+ message_regex.match(e.message)
end
+ end
- failure_message_for_should do
- if @error
- "failed with #{@error}\n#{@error.backtrace}"
- else
- "did not fail"
- end
+ failure_message_for_should do
+ if @error
+ "failed with #{@error}\n#{@error.backtrace}"
+ else
+ "did not fail"
end
end
+ end
- it 'should make $facts available' do
- node = node_with_facts('the_facts' => 'straight')
+ it 'should make $facts available' do
+ node = node_with_facts('the_facts' => 'straight')
- catalog = compile_to_catalog(<<-MANIFEST, node)
- notify { 'test': message => $facts[the_facts] }
- MANIFEST
+ catalog = compile_to_catalog(<<-MANIFEST, node)
+ notify { 'test': message => $facts[the_facts] }
+ MANIFEST
- catalog.resource("Notify[test]")[:message].should == "straight"
- end
+ catalog.resource("Notify[test]")[:message].should == "straight"
+ end
- it 'should make $facts reserved' do
- node = node_with_facts('the_facts' => 'straight')
+ it 'should make $facts reserved' do
+ node = node_with_facts('the_facts' => 'straight')
- expect('$facts = {}').to fail_compile_with(node, /assign to a reserved variable name: 'facts'/)
- expect('class a { $facts = {} } include a').to fail_compile_with(node, /assign to a reserved variable name: 'facts'/)
- end
+ expect('$facts = {}').to fail_compile_with(node, /assign to a reserved variable name: 'facts'/)
+ expect('class a { $facts = {} } include a').to fail_compile_with(node, /assign to a reserved variable name: 'facts'/)
+ end
- it 'should make $facts immutable' do
- node = node_with_facts('string' => 'value', 'array' => ['string'], 'hash' => { 'a' => 'string' }, 'number' => 1, 'boolean' => true)
+ it 'should make $facts immutable' do
+ node = node_with_facts('string' => 'value', 'array' => ['string'], 'hash' => { 'a' => 'string' }, 'number' => 1, 'boolean' => true)
- expect('$i=inline_template("<% @facts[%q{new}] = 2 %>")').to fail_compile_with(node, /frozen Hash/i)
- expect('$i=inline_template("<% @facts[%q{string}].chop! %>")').to fail_compile_with(node, /frozen String/i)
+ expect('$i=inline_template("<% @facts[%q{new}] = 2 %>")').to fail_compile_with(node, /frozen Hash/i)
+ expect('$i=inline_template("<% @facts[%q{string}].chop! %>")').to fail_compile_with(node, /frozen String/i)
- expect('$i=inline_template("<% @facts[%q{array}][0].chop! %>")').to fail_compile_with(node, /frozen String/i)
- expect('$i=inline_template("<% @facts[%q{array}][1] = 2 %>")').to fail_compile_with(node, /frozen Array/i)
+ expect('$i=inline_template("<% @facts[%q{array}][0].chop! %>")').to fail_compile_with(node, /frozen String/i)
+ expect('$i=inline_template("<% @facts[%q{array}][1] = 2 %>")').to fail_compile_with(node, /frozen Array/i)
- expect('$i=inline_template("<% @facts[%q{hash}][%q{a}].chop! %>")').to fail_compile_with(node, /frozen String/i)
- expect('$i=inline_template("<% @facts[%q{hash}][%q{b}] = 2 %>")').to fail_compile_with(node, /frozen Hash/i)
- end
+ expect('$i=inline_template("<% @facts[%q{hash}][%q{a}].chop! %>")').to fail_compile_with(node, /frozen String/i)
+ expect('$i=inline_template("<% @facts[%q{hash}][%q{b}] = 2 %>")').to fail_compile_with(node, /frozen Hash/i)
+ end
- it 'should make $facts available even if there are no facts' do
- Puppet[:facts_terminus] = :memory
- node = Puppet::Node.new("testing2")
- node.fact_merge
+ it 'should make $facts available even if there are no facts' do
+ Puppet[:facts_terminus] = :memory
+ node = Puppet::Node.new("testing2")
+ node.fact_merge
- catalog = compile_to_catalog(<<-MANIFEST, node)
- notify { 'test': message => $facts }
- MANIFEST
+ catalog = compile_to_catalog(<<-MANIFEST, node)
+ notify { 'test': message => $facts }
+ MANIFEST
- expect(catalog).to have_resource("Notify[test]").with_parameter(:message, {})
- end
+ expect(catalog).to have_resource("Notify[test]").with_parameter(:message, {})
end
+ end
- context 'and have not opted in to immutable_node_data' do
- before :each do
- Puppet[:immutable_node_data] = false
- end
+ context 'and have not opted in to immutable_node_data' do
+ before :each do
+ Puppet[:immutable_node_data] = false
+ end
- it 'should not make $facts available' do
- Puppet[:facts_terminus] = :memory
- facts = Puppet::Node::Facts.new("testing", 'the_facts' => 'straight')
- Puppet::Node::Facts.indirection.save(facts)
- node = Puppet::Node.new("testing")
- node.fact_merge
+ it 'should not make $facts available' do
+ Puppet[:facts_terminus] = :memory
+ facts = Puppet::Node::Facts.new("testing", 'the_facts' => 'straight')
+ Puppet::Node::Facts.indirection.save(facts)
+ node = Puppet::Node.new("testing")
+ node.fact_merge
- catalog = compile_to_catalog(<<-MANIFEST, node)
- notify { 'test': message => "An $facts space" }
- MANIFEST
+ catalog = compile_to_catalog(<<-MANIFEST, node)
+ notify { 'test': message => "An $facts space" }
+ MANIFEST
- catalog.resource("Notify[test]")[:message].should == "An space"
- end
+ catalog.resource("Notify[test]")[:message].should == "An space"
end
end
+ end
- context 'when working with the trusted data hash' do
- context 'and have opted in to trusted_node_data' do
- before :each do
- Puppet[:trusted_node_data] = true
- end
-
- it 'should make $trusted available' do
- node = Puppet::Node.new("testing")
- node.trusted_data = { "data" => "value" }
+ context 'when working with the trusted data hash' do
+ context 'and have opted in to trusted_node_data' do
+ before :each do
+ Puppet[:trusted_node_data] = true
+ end
- catalog = compile_to_catalog(<<-MANIFEST, node)
- notify { 'test': message => $trusted[data] }
- MANIFEST
+ it 'should make $trusted available' do
+ node = Puppet::Node.new("testing")
+ node.trusted_data = { "data" => "value" }
- catalog.resource("Notify[test]")[:message].should == "value"
- end
+ catalog = compile_to_catalog(<<-MANIFEST, node)
+ notify { 'test': message => $trusted[data] }
+ MANIFEST
- it 'should not allow assignment to $trusted' do
- node = Puppet::Node.new("testing")
- node.trusted_data = { "data" => "value" }
-
- expect do
- catalog = compile_to_catalog(<<-MANIFEST, node)
- $trusted = 'changed'
- notify { 'test': message => $trusted == 'changed' }
- MANIFEST
- catalog.resource("Notify[test]")[:message].should == true
- end.to raise_error(Puppet::Error, /Attempt to assign to a reserved variable name: 'trusted'/)
- end
+ catalog.resource("Notify[test]")[:message].should == "value"
+ end
- it 'should not allow addition to $trusted hash' do
- node = Puppet::Node.new("testing")
- node.trusted_data = { "data" => "value" }
-
- expect do
- catalog = compile_to_catalog(<<-MANIFEST, node)
- $trusted['extra'] = 'added'
- notify { 'test': message => $trusted['extra'] == 'added' }
- MANIFEST
- catalog.resource("Notify[test]")[:message].should == true
- # different errors depending on regular or future parser
- end.to raise_error(Puppet::Error, /(can't modify frozen [hH]ash)|(Illegal attempt to assign)/)
- end
+ it 'should not allow assignment to $trusted' do
+ node = Puppet::Node.new("testing")
+ node.trusted_data = { "data" => "value" }
- it 'should not allow addition to $trusted hash via Ruby inline template' do
- node = Puppet::Node.new("testing")
- node.trusted_data = { "data" => "value" }
-
- expect do
- catalog = compile_to_catalog(<<-MANIFEST, node)
- $dummy = inline_template("<% @trusted['extra'] = 'added' %> lol")
- notify { 'test': message => $trusted['extra'] == 'added' }
- MANIFEST
- catalog.resource("Notify[test]")[:message].should == true
- end.to raise_error(Puppet::Error, /can't modify frozen [hH]ash/)
- end
+ expect do
+ catalog = compile_to_catalog(<<-MANIFEST, node)
+ $trusted = 'changed'
+ notify { 'test': message => $trusted == 'changed' }
+ MANIFEST
+ catalog.resource("Notify[test]")[:message].should == true
+ end.to raise_error(Puppet::Error, /Attempt to assign to a reserved variable name: 'trusted'/)
end
- context 'and have not opted in to trusted_node_data' do
- before :each do
- Puppet[:trusted_node_data] = false
- end
-
- it 'should not make $trusted available' do
- node = Puppet::Node.new("testing")
- node.trusted_data = { "data" => "value" }
+ it 'should not allow addition to $trusted hash' do
+ node = Puppet::Node.new("testing")
+ node.trusted_data = { "data" => "value" }
+ expect do
catalog = compile_to_catalog(<<-MANIFEST, node)
- notify { 'test': message => $trusted == undef }
+ $trusted['extra'] = 'added'
+ notify { 'test': message => $trusted['extra'] == 'added' }
MANIFEST
-
catalog.resource("Notify[test]")[:message].should == true
- end
+ # different errors depending on regular or future parser
+ end.to raise_error(Puppet::Error, /(can't modify frozen [hH]ash)|(Illegal attempt to assign)/)
+ end
- it 'should allow assignment to $trusted' do
- node = Puppet::Node.new("testing")
+ it 'should not allow addition to $trusted hash via Ruby inline template' do
+ node = Puppet::Node.new("testing")
+ node.trusted_data = { "data" => "value" }
+ expect do
catalog = compile_to_catalog(<<-MANIFEST, node)
- $trusted = 'changed'
- notify { 'test': message => $trusted == 'changed' }
+ $dummy = inline_template("<% @trusted['extra'] = 'added' %> lol")
+ notify { 'test': message => $trusted['extra'] == 'added' }
MANIFEST
-
catalog.resource("Notify[test]")[:message].should == true
- end
+ end.to raise_error(Puppet::Error, /can't modify frozen [hH]ash/)
end
end
- end
- describe 'using classic parser' do
- before :each do
- Puppet[:parser] = 'current'
+ context 'and have not opted in to trusted_node_data' do
+ before :each do
+ Puppet[:trusted_node_data] = false
+ end
+
+ it 'should not make $trusted available' do
+ node = Puppet::Node.new("testing")
+ node.trusted_data = { "data" => "value" }
+
+ catalog = compile_to_catalog(<<-MANIFEST, node)
+ notify { 'test': message => $trusted == undef }
+ MANIFEST
+
+ catalog.resource("Notify[test]")[:message].should == true
+ end
+
+ it 'should allow assignment to $trusted' do
+ node = Puppet::Node.new("testing")
+
+ catalog = compile_to_catalog(<<-MANIFEST, node)
+ $trusted = 'changed'
+ notify { 'test': message => $trusted == 'changed' }
+ MANIFEST
+
+ catalog.resource("Notify[test]")[:message].should == true
+ end
end
- it_behaves_like 'the compiler' do
+ end
+
+ context 'when evaluating collection' do
+ it 'matches on container inherited tags' do
+ Puppet[:code] = <<-MANIFEST
+ class xport_test {
+ tag 'foo_bar'
+ @notify { 'nbr1':
+ message => 'explicitly tagged',
+ tag => 'foo_bar'
+ }
+
+ @notify { 'nbr2':
+ message => 'implicitly tagged'
+ }
+
+ Notify <| tag == 'foo_bar' |> {
+ message => 'overridden'
+ }
+ }
+ include xport_test
+ MANIFEST
+
+ catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode"))
+
+ expect(catalog).to have_resource("Notify[nbr1]").with_parameter(:message, 'overridden')
+ expect(catalog).to have_resource("Notify[nbr2]").with_parameter(:message, 'overridden')
end
end
end
diff --git a/spec/integration/parser/conditionals_spec.rb b/spec/integration/parser/conditionals_spec.rb
new file mode 100644
index 000000000..82d950a06
--- /dev/null
+++ b/spec/integration/parser/conditionals_spec.rb
@@ -0,0 +1,117 @@
+require 'spec_helper'
+require 'puppet_spec/compiler'
+require 'matchers/resource'
+
+describe "Evaluation of Conditionals" do
+ include PuppetSpec::Compiler
+ include Matchers::Resource
+
+ shared_examples_for "a catalog built with conditionals" do
+ it "evaluates an if block correctly" do
+ catalog = compile_to_catalog(<<-CODE)
+ if( 1 == 1) {
+ notify { 'if': }
+ } elsif(2 == 2) {
+ notify { 'elsif': }
+ } else {
+ notify { 'else': }
+ }
+ CODE
+ expect(catalog).to have_resource("Notify[if]")
+ end
+
+ it "evaluates elsif block" do
+ catalog = compile_to_catalog(<<-CODE)
+ if( 1 == 3) {
+ notify { 'if': }
+ } elsif(2 == 2) {
+ notify { 'elsif': }
+ } else {
+ notify { 'else': }
+ }
+ CODE
+ expect(catalog).to have_resource("Notify[elsif]")
+ end
+
+ it "reaches the else clause if no expressions match" do
+ catalog = compile_to_catalog(<<-CODE)
+ if( 1 == 2) {
+ notify { 'if': }
+ } elsif(2 == 3) {
+ notify { 'elsif': }
+ } else {
+ notify { 'else': }
+ }
+ CODE
+ expect(catalog).to have_resource("Notify[else]")
+ end
+
+ it "evalutes false to false" do
+ catalog = compile_to_catalog(<<-CODE)
+ if false {
+ } else {
+ notify { 'false': }
+ }
+ CODE
+ expect(catalog).to have_resource("Notify[false]")
+ end
+
+ it "evaluates the string 'false' as true" do
+ catalog = compile_to_catalog(<<-CODE)
+ if 'false' {
+ notify { 'true': }
+ } else {
+ notify { 'false': }
+ }
+ CODE
+ expect(catalog).to have_resource("Notify[true]")
+ end
+
+ it "evaluates undefined variables as false" do
+ catalog = compile_to_catalog(<<-CODE)
+ if $undef_var {
+ } else {
+ notify { 'undef': }
+ }
+ CODE
+ expect(catalog).to have_resource("Notify[undef]")
+ end
+ end
+
+ context "current parser" do
+ before(:each) do
+ Puppet[:parser] = 'current'
+ end
+
+ it_behaves_like "a catalog built with conditionals"
+
+ it "evaluates empty string as false" do
+ catalog = compile_to_catalog(<<-CODE)
+ if '' {
+ notify { 'true': }
+ } else {
+ notify { 'empty': }
+ }
+ CODE
+ expect(catalog).to have_resource("Notify[empty]")
+ end
+ end
+
+ context "future parser" do
+ before(:each) do
+ Puppet[:parser] = 'future'
+ end
+ it_behaves_like "a catalog built with conditionals"
+
+ it "evaluates empty string as true" do
+ catalog = compile_to_catalog(<<-CODE)
+ if '' {
+ notify { 'true': }
+ } else {
+ notify { 'empty': }
+ }
+ CODE
+ expect(catalog).to have_resource("Notify[true]")
+ end
+ end
+end
diff --git a/spec/integration/parser/future_compiler_spec.rb b/spec/integration/parser/future_compiler_spec.rb
index 9d4d98776..d0fcfcdec 100644
--- a/spec/integration/parser/future_compiler_spec.rb
+++ b/spec/integration/parser/future_compiler_spec.rb
@@ -61,13 +61,73 @@ describe "Puppet::Parser::Compiler" do
expect(catalog).to have_resource("Notify[check_me]").with_parameter(:message, "evoe")
end
+ it 'Applies defaults from dynamic scopes (3x and future with reverted PUP-867)' do
+ catalog = compile_to_catalog(<<-CODE)
+ class a {
+ Notify { message => "defaulted" }
+ include b
+ notify { bye: }
+ }
+ class b { notify { hi: } }
+
+ include a
+ CODE
+ expect(catalog).to have_resource("Notify[hi]").with_parameter(:message, "defaulted")
+ expect(catalog).to have_resource("Notify[bye]").with_parameter(:message, "defaulted")
+ end
+
+ it 'gets default from inherited class (PUP-867)' do
+ catalog = compile_to_catalog(<<-CODE)
+ class a {
+ Notify { message => "defaulted" }
+ include c
+ notify { bye: }
+ }
+ class b { Notify { message => "inherited" } }
+ class c inherits b { notify { hi: } }
+
+ include a
+ CODE
+
+ expect(catalog).to have_resource("Notify[hi]").with_parameter(:message, "inherited")
+ expect(catalog).to have_resource("Notify[bye]").with_parameter(:message, "defaulted")
+ end
+
+ it 'looks up default parameter values from inherited class (PUP-2532)' do
+ catalog = compile_to_catalog(<<-CODE)
+ class a {
+ Notify { message => "defaulted" }
+ include c
+ notify { bye: }
+ }
+ class b { Notify { message => "inherited" } }
+ class c inherits b { notify { hi: } }
+
+ include a
+ notify {hi_test: message => Notify[hi][message] }
+ notify {bye_test: message => Notify[bye][message] }
+ CODE
+
+ expect(catalog).to have_resource("Notify[hi_test]").with_parameter(:message, "inherited")
+ expect(catalog).to have_resource("Notify[bye_test]").with_parameter(:message, "defaulted")
+ end
+
+ it 'does not allow override of class parameters using a resource override expression' do
+ expect do
+ compile_to_catalog(<<-CODE)
+ Class[a] { x => 2}
+ CODE
+ end.to raise_error(/Resource Override can only.*got: Class\[a\].*/)
+ end
+
describe "when resolving class references" do
- it "should favor local scope, even if there's an included class in topscope" do
+ it "should not favor local scope (with class included in topscope)" do
catalog = compile_to_catalog(<<-PP)
class experiment {
class baz {
}
notify {"x" : require => Class[Baz] }
+ notify {"y" : require => Class[Experiment::Baz] }
}
class baz {
}
@@ -76,15 +136,17 @@ describe "Puppet::Parser::Compiler" do
include experiment::baz
PP
- expect(catalog).to have_resource("Notify[x]").with_parameter(:require, be_resource("Class[Experiment::Baz]"))
+ expect(catalog).to have_resource("Notify[x]").with_parameter(:require, be_resource("Class[Baz]"))
+ expect(catalog).to have_resource("Notify[y]").with_parameter(:require, be_resource("Class[Experiment::Baz]"))
end
- it "should favor local scope, even if there's an unincluded class in topscope" do
+ it "should not favor local scope, (with class not included in topscope)" do
catalog = compile_to_catalog(<<-PP)
class experiment {
class baz {
}
notify {"x" : require => Class[Baz] }
+ notify {"y" : require => Class[Experiment::Baz] }
}
class baz {
}
@@ -92,7 +154,8 @@ describe "Puppet::Parser::Compiler" do
include experiment::baz
PP
- expect(catalog).to have_resource("Notify[x]").with_parameter(:require, be_resource("Class[Experiment::Baz]"))
+ expect(catalog).to have_resource("Notify[x]").with_parameter(:require, be_resource("Class[Baz]"))
+ expect(catalog).to have_resource("Notify[y]").with_parameter(:require, be_resource("Class[Experiment::Baz]"))
end
end
@@ -167,10 +230,10 @@ describe "Puppet::Parser::Compiler" do
def assert_creates_relationships(relationship_code, expectations)
base_manifest = <<-MANIFEST
file { [a,b,c]:
- mode => 0644,
+ mode => '0644',
}
file { [d,e]:
- mode => 0755,
+ mode => '0755',
}
MANIFEST
catalog = compile_to_catalog(base_manifest + relationship_code)
@@ -301,12 +364,13 @@ describe "Puppet::Parser::Compiler" do
end
it 'a missing variable as default value becomes undef' do
+ # strict variables not on
catalog = compile_to_catalog(<<-MANIFEST)
- class a ($b=$x) { notify {$b: message=>'meh'} }
+ class a ($b=$x) { notify {test: message=>"yes ${undef == $b}" } }
include a
MANIFEST
- expect(catalog).to have_resource("Notify[undef]").with_parameter(:message, "meh")
+ expect(catalog).to have_resource("Notify[test]").with_parameter(:message, "yes true")
end
end
@@ -366,5 +430,321 @@ describe "Puppet::Parser::Compiler" do
end
end
end
+
+ context 'when using typed parameters in definition' do
+ it 'accepts type compliant arguments' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ define foo(String $x) { }
+ foo { 'test': x =>'say friend' }
+ MANIFEST
+ expect(catalog).to have_resource("Foo[test]").with_parameter(:x, 'say friend')
+ end
+
+ it 'accepts anything when parameters are untyped' do
+ expect do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ define foo($a, $b, $c) { }
+ foo { 'test': a => String, b=>10, c=>undef }
+ MANIFEST
+ end.to_not raise_error()
+ end
+
+ it 'denies non type compliant arguments' do
+ expect do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ define foo(Integer $x) { }
+ foo { 'test': x =>'say friend' }
+ MANIFEST
+ end.to raise_error(/type Integer, got String/)
+ end
+
+ it 'denies non type compliant default argument' do
+ expect do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ define foo(Integer $x = 'pow') { }
+ foo { 'test': }
+ MANIFEST
+ end.to raise_error(/type Integer, got String/)
+ end
+
+ it 'accepts a Resource as a Type' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ define foo(Type[Bar] $x) {
+ notify { 'test': message => $x[text] }
+ }
+ define bar($text) { }
+ bar { 'joke': text => 'knock knock' }
+ foo { 'test': x => Bar[joke] }
+ MANIFEST
+ expect(catalog).to have_resource("Notify[test]").with_parameter(:message, 'knock knock')
+ end
+ end
+
+ context 'when using typed parameters in class' do
+ it 'accepts type compliant arguments' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ class foo(String $x) { }
+ class { 'foo': x =>'say friend' }
+ MANIFEST
+ expect(catalog).to have_resource("Class[Foo]").with_parameter(:x, 'say friend')
+ end
+
+ it 'accepts anything when parameters are untyped' do
+ expect do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ class foo($a, $b, $c) { }
+ class { 'foo': a => String, b=>10, c=>undef }
+ MANIFEST
+ end.to_not raise_error()
+ end
+
+ it 'denies non type compliant arguments' do
+ expect do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ class foo(Integer $x) { }
+ class { 'foo': x =>'say friend' }
+ MANIFEST
+ end.to raise_error(/type Integer, got String/)
+ end
+
+ it 'denies non type compliant default argument' do
+ expect do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ class foo(Integer $x = 'pow') { }
+ class { 'foo': }
+ MANIFEST
+ end.to raise_error(/type Integer, got String/)
+ end
+
+ it 'accepts a Resource as a Type' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ class foo(Type[Bar] $x) {
+ notify { 'test': message => $x[text] }
+ }
+ define bar($text) { }
+ bar { 'joke': text => 'knock knock' }
+ class { 'foo': x => Bar[joke] }
+ MANIFEST
+ expect(catalog).to have_resource("Notify[test]").with_parameter(:message, 'knock knock')
+ end
+ end
+
+ context 'when using typed parameters in lambdas' do
+ it 'accepts type compliant arguments' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ with('value') |String $x| { notify { "$x": } }
+ MANIFEST
+ expect(catalog).to have_resource("Notify[value]")
+ end
+
+ it 'handles an array as a single argument' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ with(['value', 'second']) |$x| { notify { "${x[0]} ${x[1]}": } }
+ MANIFEST
+ expect(catalog).to have_resource("Notify[value second]")
+ end
+
+ it 'denies when missing required arguments' do
+ expect do
+ compile_to_catalog(<<-MANIFEST)
+ with(1) |$x, $y| { }
+ MANIFEST
+ end.to raise_error(/Parameter \$y is required but no value was given/m)
+ end
+
+ it 'accepts anything when parameters are untyped' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ ['value', 1, true, undef].each |$x| { notify { "value: $x": } }
+ MANIFEST
+
+ expect(catalog).to have_resource("Notify[value: value]")
+ expect(catalog).to have_resource("Notify[value: 1]")
+ expect(catalog).to have_resource("Notify[value: true]")
+ expect(catalog).to have_resource("Notify[value: ]")
+ end
+
+ it 'accepts type-compliant, slurped arguments' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ with(1, 2) |Integer *$x| { notify { "${$x[0] + $x[1]}": } }
+ MANIFEST
+ expect(catalog).to have_resource("Notify[3]")
+ end
+
+ it 'denies non-type-compliant arguments' do
+ expect do
+ compile_to_catalog(<<-MANIFEST)
+ with(1) |String $x| { }
+ MANIFEST
+ end.to raise_error(/expected.*String.*actual.*Integer/m)
+ end
+
+ it 'denies non-type-compliant, slurped arguments' do
+ expect do
+ compile_to_catalog(<<-MANIFEST)
+ with(1, "hello") |Integer *$x| { }
+ MANIFEST
+ end.to raise_error(/called with mis-matched arguments.*expected.*Integer.*actual.*Integer, String/m)
+ end
+
+ it 'denies non-type-compliant default argument' do
+ expect do
+ compile_to_catalog(<<-MANIFEST)
+ with(1) |$x, String $defaulted = 1| { notify { "${$x + $defaulted}": }}
+ MANIFEST
+ end.to raise_error(/expected.*Any.*String.*actual.*Integer.*Integer/m)
+ end
+
+ it 'raises an error when a default argument value is an incorrect type and there are no arguments passed' do
+ expect do
+ compile_to_catalog(<<-MANIFEST)
+ with() |String $defaulted = 1| {}
+ MANIFEST
+ end.to raise_error(/expected.*String.*actual.*Integer/m)
+ end
+
+ it 'raises an error when the default argument for a slurped parameter is an incorrect type' do
+ expect do
+ compile_to_catalog(<<-MANIFEST)
+ with() |String *$defaulted = 1| {}
+ MANIFEST
+ end.to raise_error(/expected.*String.*actual.*Integer/m)
+ end
+
+ it 'allows using an array as the default slurped value' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ with() |String *$defaulted = [hi]| { notify { $defaulted[0]: } }
+ MANIFEST
+
+ expect(catalog).to have_resource('Notify[hi]')
+ end
+
+ it 'allows using a value of the type as the default slurped value' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ with() |String *$defaulted = hi| { notify { $defaulted[0]: } }
+ MANIFEST
+
+ expect(catalog).to have_resource('Notify[hi]')
+ end
+
+ it 'allows specifying the type of a slurped parameter as an array' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ with() |Array[String] *$defaulted = hi| { notify { $defaulted[0]: } }
+ MANIFEST
+
+ expect(catalog).to have_resource('Notify[hi]')
+ end
+
+ it 'raises an error when the number of default values does not match the parameter\'s size specification' do
+ expect do
+ compile_to_catalog(<<-MANIFEST)
+ with() |Array[String, 2] *$defaulted = hi| { }
+ MANIFEST
+ end.to raise_error(/expected.*arg count \{2,\}.*actual.*arg count \{1\}/m)
+ end
+
+ it 'raises an error when the number of passed values does not match the parameter\'s size specification' do
+ expect do
+ compile_to_catalog(<<-MANIFEST)
+ with(hi) |Array[String, 2] *$passed| { }
+ MANIFEST
+ end.to raise_error(/expected.*arg count \{2,\}.*actual.*arg count \{1\}/m)
+ end
+
+ it 'matches when the number of arguments passed for a slurp parameter match the size specification' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ with(hi, bye) |Array[String, 2] *$passed| {
+ $passed.each |$n| { notify { $n: } }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource('Notify[hi]')
+ expect(catalog).to have_resource('Notify[bye]')
+ end
+
+ it 'raises an error when the number of allowed slurp parameters exceeds the size constraint' do
+ expect do
+ compile_to_catalog(<<-MANIFEST)
+ with(hi, bye) |Array[String, 1, 1] *$passed| { }
+ MANIFEST
+ end.to raise_error(/expected.*arg count \{1\}.*actual.*arg count \{2\}/m)
+ end
+
+ it 'allows passing slurped arrays by specifying an array of arrays' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ with([hi], [bye]) |Array[Array[String, 1, 1]] *$passed| {
+ notify { $passed[0][0]: }
+ notify { $passed[1][0]: }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource('Notify[hi]')
+ expect(catalog).to have_resource('Notify[bye]')
+ end
+
+ it 'raises an error when a required argument follows an optional one' do
+ expect do
+ compile_to_catalog(<<-MANIFEST)
+ with() |$y = first, $x, Array[String, 1] *$passed = bye| {}
+ MANIFEST
+ end.to raise_error(/Parameter \$x is required/)
+ end
+
+ it 'raises an error when the minimum size of a slurped argument makes it required and it follows an optional argument' do
+ expect do
+ compile_to_catalog(<<-MANIFEST)
+ with() |$x = first, Array[String, 1] *$passed| {}
+ MANIFEST
+ end.to raise_error(/Parameter \$passed is required/)
+ end
+
+ it 'allows slurped arguments with a minimum size of 0 after an optional argument' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ with() |$x = first, Array[String, 0] *$passed| {
+ notify { $x: }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource('Notify[first]')
+ end
+
+ it 'accepts a Resource as a Type' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ define bar($text) { }
+ bar { 'joke': text => 'knock knock' }
+
+ with(Bar[joke]) |Type[Bar] $joke| { notify { "${joke[text]}": } }
+ MANIFEST
+ expect(catalog).to have_resource("Notify[knock knock]")
+ end
+ end
end
+
+ context 'when evaluating collection' do
+ it 'matches on container inherited tags' do
+ Puppet[:code] = <<-MANIFEST
+ class xport_test {
+ tag('foo_bar')
+ @notify { 'nbr1':
+ message => 'explicitly tagged',
+ tag => 'foo_bar'
+ }
+
+ @notify { 'nbr2':
+ message => 'implicitly tagged'
+ }
+
+ Notify <| tag == 'foo_bar' |> {
+ message => 'overridden'
+ }
+ }
+ include xport_test
+ MANIFEST
+
+ catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode"))
+
+ expect(catalog).to have_resource("Notify[nbr1]").with_parameter(:message, 'overridden')
+ expect(catalog).to have_resource("Notify[nbr2]").with_parameter(:message, 'overridden')
+ end
+ end
+
end
diff --git a/spec/integration/parser/node_spec.rb b/spec/integration/parser/node_spec.rb
new file mode 100644
index 000000000..7ce58f152
--- /dev/null
+++ b/spec/integration/parser/node_spec.rb
@@ -0,0 +1,185 @@
+require 'spec_helper'
+require 'puppet_spec/compiler'
+require 'matchers/resource'
+
+describe 'node statements' do
+ include PuppetSpec::Compiler
+ include Matchers::Resource
+
+ shared_examples_for 'nodes' do
+ it 'selects a node where the name is just a number' do
+ # Future parser doesn't allow a number in this position
+ catalog = compile_to_catalog(<<-MANIFEST, Puppet::Node.new("5"))
+ node 5 { notify { 'matched': } }
+ MANIFEST
+
+ expect(catalog).to have_resource('Notify[matched]')
+ end
+
+ it 'selects the node with a matching name' do
+ catalog = compile_to_catalog(<<-MANIFEST, Puppet::Node.new("nodename"))
+ node noden {}
+ node nodename { notify { matched: } }
+ node name {}
+ MANIFEST
+
+ expect(catalog).to have_resource('Notify[matched]')
+ end
+
+ it 'prefers a node with a literal name over one with a regex' do
+ catalog = compile_to_catalog(<<-MANIFEST, Puppet::Node.new("nodename"))
+ node /noden.me/ { notify { ignored: } }
+ node nodename { notify { matched: } }
+ MANIFEST
+
+ expect(catalog).to have_resource('Notify[matched]')
+ end
+
+ it 'selects a node where one of the names matches' do
+ catalog = compile_to_catalog(<<-MANIFEST, Puppet::Node.new("nodename"))
+ node different, nodename, other { notify { matched: } }
+ MANIFEST
+
+ expect(catalog).to have_resource('Notify[matched]')
+ end
+
+ it 'arbitrarily selects one of the matching nodes' do
+ catalog = compile_to_catalog(<<-MANIFEST, Puppet::Node.new("nodename"))
+ node /not/ { notify { 'is not matched': } }
+ node /name.*/ { notify { 'could be matched': } }
+ node /na.e/ { notify { 'could also be matched': } }
+ MANIFEST
+
+ expect([catalog.resource('Notify[could be matched]'), catalog.resource('Notify[could also be matched]')].compact).to_not be_empty
+ end
+
+ it 'selects a node where one of the names matches with a mixture of literals and regex' do
+ catalog = compile_to_catalog(<<-MANIFEST, Puppet::Node.new("nodename"))
+ node different, /name/, other { notify { matched: } }
+ MANIFEST
+
+ expect(catalog).to have_resource('Notify[matched]')
+ end
+
+ it 'errors when two nodes with regexes collide after some regex syntax is removed' do
+ expect do
+ compile_to_catalog(<<-MANIFEST)
+ node /a.*(c)?/ { }
+ node 'a.c' { }
+ MANIFEST
+ end.to raise_error(Puppet::Error, /Node 'a.c' is already defined/)
+ end
+
+ it 'provides captures from the regex in the node body' do
+ catalog = compile_to_catalog(<<-MANIFEST, Puppet::Node.new("nodename"))
+ node /(.*)/ { notify { "$1": } }
+ MANIFEST
+
+ expect(catalog).to have_resource('Notify[nodename]')
+ end
+
+ it 'selects the node with the matching regex' do
+ catalog = compile_to_catalog(<<-MANIFEST, Puppet::Node.new("nodename"))
+ node /node.*/ { notify { matched: } }
+ MANIFEST
+
+ expect(catalog).to have_resource('Notify[matched]')
+ end
+
+ it 'selects a node that is a literal string' do
+ catalog = compile_to_catalog(<<-MANIFEST, Puppet::Node.new("node.name"))
+ node 'node.name' { notify { matched: } }
+ MANIFEST
+
+ expect(catalog).to have_resource('Notify[matched]')
+ end
+
+ it 'selects a node that is a prefix of the agent name' do
+ Puppet[:strict_hostname_checking] = false
+ catalog = compile_to_catalog(<<-MANIFEST, Puppet::Node.new("node.name.com"))
+ node 'node.name' { notify { matched: } }
+ MANIFEST
+
+ expect(catalog).to have_resource('Notify[matched]')
+ end
+
+ it 'does not treat regex symbols as a regex inside a string literal' do
+ catalog = compile_to_catalog(<<-MANIFEST, Puppet::Node.new("nodexname"))
+ node 'node.name' { notify { 'not matched': } }
+ node 'nodexname' { notify { 'matched': } }
+ MANIFEST
+
+ expect(catalog).to have_resource('Notify[matched]')
+ end
+
+ it 'errors when two nodes have the same name' do
+ expect do
+ compile_to_catalog(<<-MANIFEST)
+ node name { }
+ node 'name' { }
+ MANIFEST
+ end.to raise_error(Puppet::Error, /Node 'name' is already defined/)
+ end
+ end
+
+ describe 'using classic parser' do
+ before :each do
+ Puppet[:parser] = 'current'
+ end
+
+ it_behaves_like 'nodes'
+
+ it 'includes the inherited nodes of the matching node' do
+ catalog = compile_to_catalog(<<-MANIFEST, Puppet::Node.new("nodename"))
+ node notmatched1 { notify { inherited: } }
+ node nodename inherits notmatched1 { notify { matched: } }
+ node notmatched2 { notify { ignored: } }
+ MANIFEST
+
+ expect(catalog).to have_resource('Notify[matched]')
+ expect(catalog).to have_resource('Notify[inherited]')
+ end
+
+ it 'raises deprecation warning for node inheritance for 3x parser' do
+ Puppet.expects(:warning).at_least_once
+ Puppet.expects(:warning).with(regexp_matches(/Deprecation notice\: Node inheritance is not supported in Puppet >= 4\.0\.0/))
+
+ catalog = compile_to_catalog(<<-MANIFEST, Puppet::Node.new("1.2.3.4"))
+ node default {}
+ node '1.2.3.4' inherits default { }
+ MANIFEST
+ end
+ end
+
+ describe 'using future parser' do
+ before :each do
+ Puppet[:parser] = 'future'
+ end
+
+ it_behaves_like 'nodes'
+
+ it 'is unable to parse a name that is an invalid number' do
+ expect do
+ compile_to_catalog('node 5name {} ')
+ end.to raise_error(Puppet::Error, /Illegal number/)
+ end
+
+ it 'parses a node name that is dotted numbers' do
+ catalog = compile_to_catalog(<<-MANIFEST, Puppet::Node.new("1.2.3.4"))
+ node 1.2.3.4 { notify { matched: } }
+ MANIFEST
+
+ expect(catalog).to have_resource('Notify[matched]')
+ end
+
+ it 'raises error for node inheritance' do
+ expect do
+ compile_to_catalog(<<-MANIFEST, Puppet::Node.new("nodename"))
+ node default {}
+ node nodename inherits default { }
+ MANIFEST
+ end.to raise_error(/Node inheritance is not supported in Puppet >= 4\.0\.0/)
+ end
+
+ end
+end
diff --git a/spec/integration/parser/resource_expressions_spec.rb b/spec/integration/parser/resource_expressions_spec.rb
new file mode 100644
index 000000000..c753f5a91
--- /dev/null
+++ b/spec/integration/parser/resource_expressions_spec.rb
@@ -0,0 +1,286 @@
+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'")
+
+ 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
+ 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
diff --git a/spec/integration/parser/ruby_manifest_spec.rb b/spec/integration/parser/ruby_manifest_spec.rb
index d0bd5f0e7..29e9c6379 100755
--- a/spec/integration/parser/ruby_manifest_spec.rb
+++ b/spec/integration/parser/ruby_manifest_spec.rb
@@ -11,10 +11,6 @@ describe "Pure ruby manifests" do
@test_dir = tmpdir('ruby_manifest_test')
end
- after do
- Puppet.settings.clear
- end
-
def write_file(name, contents)
path = File.join(@test_dir, name)
File.open(path, "w") { |f| f.write(contents) }
diff --git a/spec/integration/parser/scope_spec.rb b/spec/integration/parser/scope_spec.rb
index 1fd84f421..c10caa7f0 100644
--- a/spec/integration/parser/scope_spec.rb
+++ b/spec/integration/parser/scope_spec.rb
@@ -39,23 +39,27 @@ describe "Two step scoping for variables" do
Puppet[:parser] = 'future'
end
- describe "using plussignment to change in a new scope" do
- it "does not change a string in the parent scope" do
- # Expects to be able to concatenate string using +=
+ describe "using unsupported operators" do
+ it "issues an error for +=" do
expect do
- catalog = compile_to_catalog(<<-MANIFEST, Puppet::Node.new('the node'))
- $var = "top_msg"
- class override {
- $var += "override"
- include foo
- }
- class foo {
- notify { 'something': message => $var, }
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $var = ["top_msg"]
+ node default {
+ $var += ["override"]
}
+ MANIFEST
+ end.to raise_error(/The operator '\+=' is no longer supported/)
+ end
- include override
+ it "issues an error for -=" do
+ expect do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $var = ["top_msg"]
+ node default {
+ $var -= ["top_msg"]
+ }
MANIFEST
- end.to raise_error(/The value 'top_msg' cannot be converted to Numeric/)
+ end.to raise_error(/The operator '-=' is no longer supported/)
end
end
@@ -184,54 +188,6 @@ describe "Two step scoping for variables" do
end
describe "when using shadowing and inheritance" do
- it "finds value define in the inherited node" do
- expect_the_message_to_be('parent_msg') do <<-MANIFEST
- $var = "top_msg"
- node parent {
- $var = "parent_msg"
- }
- node default inherits parent {
- include foo
- }
- class foo {
- notify { 'something': message => $var, }
- }
- MANIFEST
- end
- end
-
- it "finds top scope when the class is included before the node defines the var" do
- expect_the_message_to_be('top_msg') do <<-MANIFEST
- $var = "top_msg"
- node parent {
- include foo
- }
- node default inherits parent {
- $var = "default_msg"
- }
- class foo {
- notify { 'something': message => $var, }
- }
- MANIFEST
- end
- end
-
- it "finds top scope when the class is included before the node defines the var" do
- expect_the_message_to_be('top_msg') do <<-MANIFEST
- $var = "top_msg"
- node parent {
- include foo
- }
- node default inherits parent {
- $var = "default_msg"
- }
- class foo {
- notify { 'something': message => $var, }
- }
- MANIFEST
- end
- end
-
it "finds values in its local scope" do
expect_the_message_to_be('local_msg') do <<-MANIFEST
node default {
@@ -363,7 +319,7 @@ describe "Two step scoping for variables" do
$var = "inner baz"
}
- class bar inherits baz {
+ class bar inherits foo::baz {
notify { 'something': message => $var, }
}
}
@@ -651,80 +607,6 @@ describe "Two step scoping for variables" do
end
end
- describe "using plussignment to change in a new scope" do
-
- it "does not change an array in the parent scope" do
- expect_the_message_to_be('top_msg') do <<-MANIFEST
- $var = ["top_msg"]
- class override {
- $var += ["override"]
- include foo
- }
- class foo {
- notify { 'something': message => $var, }
- }
-
- include override
- MANIFEST
- end
- end
-
- it "concatenates two arrays" do
- expect_the_message_to_be(['top_msg', 'override']) do <<-MANIFEST
- $var = ["top_msg"]
- class override {
- $var += ["override"]
- notify { 'something': message => $var, }
- }
-
- include override
- MANIFEST
- end
- end
-
- it "leaves an array of arrays unflattened" do
- expect_the_message_to_be([['top_msg'], ['override']]) do <<-MANIFEST
- $var = [["top_msg"]]
- class override {
- $var += [["override"]]
- notify { 'something': message => $var, }
- }
-
- include override
- MANIFEST
- end
- end
-
- it "does not change a hash in the parent scope" do
- expect_the_message_to_be({"key"=>"top_msg"}) do <<-MANIFEST
- $var = { "key" => "top_msg" }
- class override {
- $var += { "other" => "override" }
- include foo
- }
- class foo {
- notify { 'something': message => $var, }
- }
-
- include override
- MANIFEST
- end
- end
-
- it "replaces a value of a key in the hash instead of merging the values" do
- expect_the_message_to_be({"key"=>"override"}) do <<-MANIFEST
- $var = { "key" => "top_msg" }
- class override {
- $var += { "key" => "override" }
- notify { 'something': message => $var, }
- }
-
- include override
- MANIFEST
- end
- end
- end
-
describe "when using an enc" do
it "places enc parameters in top scope" do
enc_node = Puppet::Node.new("the node", { :parameters => { "var" => 'from_enc' } })
@@ -757,20 +639,16 @@ describe "Two step scoping for variables" do
end
end
- it "evaluates enc classes in the node scope when there is a matching node" do
- enc_node = Puppet::Node.new("the_node", { :classes => ['foo'] })
-
- expect_the_message_to_be('from matching node', enc_node) do <<-MANIFEST
- node inherited {
- $var = "from inherited"
- }
+ it "overrides enc variables from a node scope var" do
+ enc_node = Puppet::Node.new("the_node", { :classes => ['foo'], :parameters => { 'enc_var' => 'Set from ENC.' } })
- node the_node inherits inherited {
- $var = "from matching node"
+ expect_the_message_to_be('ENC overridden in node', enc_node) do <<-MANIFEST
+ node the_node {
+ $enc_var = "ENC overridden in node"
}
class foo {
- notify { 'something': message => $var, }
+ notify { 'something': message => $enc_var, }
}
MANIFEST
end
@@ -782,7 +660,74 @@ describe "Two step scoping for variables" do
before :each do
Puppet[:parser] = 'current'
end
- it_behaves_like 'the scope' do
+
+ it_behaves_like 'the scope'
+
+ it "finds value define in the inherited node" do
+ expect_the_message_to_be('parent_msg') do <<-MANIFEST
+ $var = "top_msg"
+ node parent {
+ $var = "parent_msg"
+ }
+ node default inherits parent {
+ include foo
+ }
+ class foo {
+ notify { 'something': message => $var, }
+ }
+ MANIFEST
+ end
+ end
+
+ it "finds top scope when the class is included before the node defines the var" do
+ expect_the_message_to_be('top_msg') do <<-MANIFEST
+ $var = "top_msg"
+ node parent {
+ include foo
+ }
+ node default inherits parent {
+ $var = "default_msg"
+ }
+ class foo {
+ notify { 'something': message => $var, }
+ }
+ MANIFEST
+ end
+ end
+
+ it "finds top scope when the class is included before the node defines the var" do
+ expect_the_message_to_be('top_msg') do <<-MANIFEST
+ $var = "top_msg"
+ node parent {
+ include foo
+ }
+ node default inherits parent {
+ $var = "default_msg"
+ }
+ class foo {
+ notify { 'something': message => $var, }
+ }
+ MANIFEST
+ end
+ end
+
+ it "evaluates enc classes in the node scope when there is a matching node" do
+ enc_node = Puppet::Node.new("the_node", { :classes => ['foo'] })
+
+ expect_the_message_to_be('from matching node', enc_node) do <<-MANIFEST
+ node inherited {
+ $var = "from inherited"
+ }
+
+ node the_node inherits inherited {
+ $var = "from matching node"
+ }
+
+ class foo {
+ notify { 'something': message => $var, }
+ }
+ MANIFEST
+ end
end
end
@@ -790,9 +735,7 @@ describe "Two step scoping for variables" do
before :each do
Puppet[:parser] = 'future'
end
- it_behaves_like 'the scope' do
- end
- end
+ it_behaves_like 'the scope'
+ end
end
-