summaryrefslogtreecommitdiff
path: root/spec/integration
diff options
context:
space:
mode:
Diffstat (limited to 'spec/integration')
-rwxr-xr-xspec/integration/agent/logging_spec.rb4
-rwxr-xr-xspec/integration/application/doc_spec.rb7
-rwxr-xr-xspec/integration/configurer_spec.rb14
-rwxr-xr-xspec/integration/defaults_spec.rb26
-rw-r--r--spec/integration/environments/default_manifest_spec.rb274
-rwxr-xr-xspec/integration/faces/documentation_spec.rb4
-rw-r--r--spec/integration/file_bucket/file_spec.rb21
-rwxr-xr-xspec/integration/indirector/catalog/compiler_spec.rb2
-rwxr-xr-xspec/integration/indirector/catalog/queue_spec.rb2
-rw-r--r--spec/integration/indirector/facts/facter_spec.rb2
-rwxr-xr-xspec/integration/indirector/file_content/file_server_spec.rb4
-rwxr-xr-xspec/integration/node/environment_spec.rb32
-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
-rw-r--r--spec/integration/provider/cron/crontab_spec.rb285
-rwxr-xr-xspec/integration/ssl/certificate_authority_spec.rb26
-rwxr-xr-xspec/integration/ssl/certificate_request_spec.rb6
-rwxr-xr-xspec/integration/ssl/certificate_revocation_list_spec.rb2
-rwxr-xr-xspec/integration/ssl/host_spec.rb2
-rwxr-xr-xspec/integration/transaction_spec.rb16
-rwxr-xr-xspec/integration/type/file_spec.rb27
-rw-r--r--spec/integration/type/nagios_spec.rb21
-rw-r--r--spec/integration/type/sshkey_spec.rb22
-rwxr-xr-xspec/integration/type/tidy_spec.rb3
-rw-r--r--spec/integration/type/user_spec.rb36
-rwxr-xr-xspec/integration/util/autoload_spec.rb12
-rwxr-xr-xspec/integration/util/rdoc/parser_spec.rb7
-rw-r--r--spec/integration/util/windows/process_spec.rb12
-rwxr-xr-xspec/integration/util/windows/security_spec.rb71
-rwxr-xr-xspec/integration/util/windows/user_spec.rb84
-rwxr-xr-xspec/integration/util_spec.rb4
39 files changed, 2498 insertions, 903 deletions
diff --git a/spec/integration/agent/logging_spec.rb b/spec/integration/agent/logging_spec.rb
index c686397c8..1c4394a6e 100755
--- a/spec/integration/agent/logging_spec.rb
+++ b/spec/integration/agent/logging_spec.rb
@@ -88,6 +88,10 @@ describe 'agent logging' do
else
it "when evoked with #{argv}, logs to #{expected[:loggers].inspect} at level #{expected[:level]}" do
+ if Facter.value(:kernelmajversion).to_f < 6.0
+ pending("requires win32-eventlog gem upgrade to 0.6.2 on Windows 2003")
+ end
+
# This logger is created by the Puppet::Settings object which creates and
# applies a catalog to ensure that configuration files and users are in
# place.
diff --git a/spec/integration/application/doc_spec.rb b/spec/integration/application/doc_spec.rb
index 77fc38625..040dde72d 100755
--- a/spec/integration/application/doc_spec.rb
+++ b/spec/integration/application/doc_spec.rb
@@ -35,11 +35,12 @@ describe Puppet::Application::Doc do
end
puppet = Puppet::Application[:doc]
- Puppet[:modulepath] = modules_dir
- Puppet[:manifest] = site_file
puppet.options[:mode] = :rdoc
- expect { puppet.run_command }.to exit_with 0
+ env = Puppet::Node::Environment.create(:rdoc, [modules_dir], site_file)
+ Puppet.override(:current_environment => env) do
+ expect { puppet.run_command }.to exit_with 0
+ end
Puppet::FileSystem.exist?('doc').should be_true
ensure
diff --git a/spec/integration/configurer_spec.rb b/spec/integration/configurer_spec.rb
index 2e0c7370e..be1f34ca3 100755
--- a/spec/integration/configurer_spec.rb
+++ b/spec/integration/configurer_spec.rb
@@ -6,20 +6,6 @@ require 'puppet/configurer'
describe Puppet::Configurer do
include PuppetSpec::Files
- describe "when downloading plugins" do
- it "should use the :pluginsignore setting, split on whitespace, for ignoring remote files" do
- Puppet.settings.stubs(:use)
- resource = Puppet::Type.type(:notify).new :name => "yay"
- Puppet::Type.type(:file).expects(:new).at_most(2).with do |args|
- args[:ignore] == Puppet[:pluginsignore].split(/\s+/)
- end.returns resource
-
- configurer = Puppet::Configurer.new
- configurer.stubs(:download_plugins?).returns true
- configurer.download_plugins(Puppet::Node::Environment.remote(:testing))
- end
- end
-
describe "when running" do
before(:each) do
@catalog = Puppet::Resource::Catalog.new("testing", Puppet.lookup(:environments).get(Puppet[:environment]))
diff --git a/spec/integration/defaults_spec.rb b/spec/integration/defaults_spec.rb
index 8c8432b6f..734785230 100755
--- a/spec/integration/defaults_spec.rb
+++ b/spec/integration/defaults_spec.rb
@@ -5,6 +5,32 @@ require 'puppet/defaults'
require 'puppet/rails'
describe "Puppet defaults" do
+
+ describe "when default_manifest is set" do
+ it "returns ./manifests by default" do
+ expect(Puppet[:default_manifest]).to eq('./manifests')
+ end
+
+ it "errors when $environment is part of the value" do
+ expect {
+ Puppet[:default_manifest] = '/$environment/manifest.pp'
+ }.to raise_error Puppet::Settings::ValidationError, /cannot interpolate.*\$environment/
+ end
+ end
+
+ describe "when disable_per_environment_manifest is set" do
+ it "returns false by default" do
+ expect(Puppet[:disable_per_environment_manifest]).to eq(false)
+ end
+
+ it "errors when set to true and default_manifest is not an absolute path" do
+ expect {
+ Puppet[:default_manifest] = './some/relative/manifest.pp'
+ Puppet[:disable_per_environment_manifest] = true
+ }.to raise_error Puppet::Settings::ValidationError, /'default_manifest' setting must be.*absolute/
+ end
+ end
+
describe "when setting the :factpath" do
it "should add the :factpath to Facter's search paths" do
Facter.expects(:search).with("/my/fact/path")
diff --git a/spec/integration/environments/default_manifest_spec.rb b/spec/integration/environments/default_manifest_spec.rb
new file mode 100644
index 000000000..6d4564037
--- /dev/null
+++ b/spec/integration/environments/default_manifest_spec.rb
@@ -0,0 +1,274 @@
+require 'spec_helper'
+
+module EnvironmentsDefaultManifestsSpec
+describe "default manifests" do
+ FS = Puppet::FileSystem
+
+ shared_examples_for "puppet with default_manifest settings" do
+ let(:confdir) { Puppet[:confdir] }
+ let(:environmentpath) { File.expand_path("envdir", confdir) }
+
+ context "relative default" do
+ let(:testingdir) { File.join(environmentpath, "testing") }
+
+ before(:each) do
+ FileUtils.mkdir_p(testingdir)
+ end
+
+ it "reads manifest from ./manifest of a basic directory environment" do
+ manifestsdir = File.join(testingdir, "manifests")
+ FileUtils.mkdir_p(manifestsdir)
+
+ File.open(File.join(manifestsdir, "site.pp"), "w") do |f|
+ f.puts("notify { 'ManifestFromRelativeDefault': }")
+ end
+
+ File.open(File.join(confdir, "puppet.conf"), "w") do |f|
+ f.puts("environmentpath=#{environmentpath}")
+ end
+
+ expect(a_catalog_compiled_for_environment('testing')).to(
+ include_resource('Notify[ManifestFromRelativeDefault]')
+ )
+ end
+ end
+
+ context "set absolute" do
+ let(:testingdir) { File.join(environmentpath, "testing") }
+
+ before(:each) do
+ FileUtils.mkdir_p(testingdir)
+ end
+
+ it "reads manifest from an absolute default_manifest" do
+ manifestsdir = File.expand_path("manifests", confdir)
+ FileUtils.mkdir_p(manifestsdir)
+
+ File.open(File.join(confdir, "puppet.conf"), "w") do |f|
+ f.puts(<<-EOF)
+ environmentpath=#{environmentpath}
+ default_manifest=#{manifestsdir}
+ EOF
+ end
+
+ File.open(File.join(manifestsdir, "site.pp"), "w") do |f|
+ f.puts("notify { 'ManifestFromAbsoluteDefaultManifest': }")
+ end
+
+ expect(a_catalog_compiled_for_environment('testing')).to(
+ include_resource('Notify[ManifestFromAbsoluteDefaultManifest]')
+ )
+ end
+
+ it "reads manifest from directory environment manifest when environment.conf manifest set" do
+ default_manifestsdir = File.expand_path("manifests", confdir)
+ File.open(File.join(confdir, "puppet.conf"), "w") do |f|
+ f.puts(<<-EOF)
+ environmentpath=#{environmentpath}
+ default_manifest=#{default_manifestsdir}
+ EOF
+ end
+
+ manifestsdir = File.join(testingdir, "special_manifests")
+ FileUtils.mkdir_p(manifestsdir)
+
+ File.open(File.join(manifestsdir, "site.pp"), "w") do |f|
+ f.puts("notify { 'ManifestFromEnvironmentConfManifest': }")
+ end
+
+ File.open(File.join(testingdir, "environment.conf"), "w") do |f|
+ f.puts("manifest=./special_manifests")
+ end
+
+ expect(a_catalog_compiled_for_environment('testing')).to(
+ include_resource('Notify[ManifestFromEnvironmentConfManifest]')
+ )
+ expect(Puppet[:default_manifest]).to eq(default_manifestsdir)
+ end
+
+ it "ignores manifests in the local ./manifests if default_manifest specifies another directory" do
+ default_manifestsdir = File.expand_path("manifests", confdir)
+ FileUtils.mkdir_p(default_manifestsdir)
+
+ File.open(File.join(confdir, "puppet.conf"), "w") do |f|
+ f.puts(<<-EOF)
+ environmentpath=#{environmentpath}
+ default_manifest=#{default_manifestsdir}
+ EOF
+ end
+
+ File.open(File.join(default_manifestsdir, "site.pp"), "w") do |f|
+ f.puts("notify { 'ManifestFromAbsoluteDefaultManifest': }")
+ end
+
+ implicit_manifestsdir = File.join(testingdir, "manifests")
+ FileUtils.mkdir_p(implicit_manifestsdir)
+
+ File.open(File.join(implicit_manifestsdir, "site.pp"), "w") do |f|
+ f.puts("notify { 'ManifestFromImplicitRelativeEnvironmentManifestDirectory': }")
+ end
+
+ expect(a_catalog_compiled_for_environment('testing')).to(
+ include_resource('Notify[ManifestFromAbsoluteDefaultManifest]')
+ )
+ end
+
+ it "raises an exception if default_manifest has $environment in it" do
+ File.open(File.join(confdir, "puppet.conf"), "w") do |f|
+ f.puts(<<-EOF)
+ environmentpath=#{environmentpath}
+ default_manifest=/foo/$environment
+ EOF
+ end
+
+ expect { Puppet.initialize_settings }.to raise_error(Puppet::Settings::ValidationError, /cannot interpolate.*\$environment.*in.*default_manifest/)
+ end
+ end
+
+ context "with disable_per_environment_manifest true" do
+ let(:manifestsdir) { File.expand_path("manifests", confdir) }
+ let(:testingdir) { File.join(environmentpath, "testing") }
+
+ before(:each) do
+ FileUtils.mkdir_p(testingdir)
+ end
+
+ before(:each) do
+ FileUtils.mkdir_p(manifestsdir)
+
+ File.open(File.join(confdir, "puppet.conf"), "w") do |f|
+ f.puts(<<-EOF)
+ environmentpath=#{environmentpath}
+ default_manifest=#{manifestsdir}
+ disable_per_environment_manifest=true
+ EOF
+ end
+
+ File.open(File.join(manifestsdir, "site.pp"), "w") do |f|
+ f.puts("notify { 'ManifestFromAbsoluteDefaultManifest': }")
+ end
+ end
+
+ it "reads manifest from the default manifest setting" do
+ expect(a_catalog_compiled_for_environment('testing')).to(
+ include_resource('Notify[ManifestFromAbsoluteDefaultManifest]')
+ )
+ end
+
+ it "refuses to compile if environment.conf specifies a different manifest" do
+ File.open(File.join(testingdir, "environment.conf"), "w") do |f|
+ f.puts("manifest=./special_manifests")
+ end
+
+ expect { a_catalog_compiled_for_environment('testing') }.to(
+ raise_error(Puppet::Error, /disable_per_environment_manifest.*environment.conf.*manifest.*conflict/)
+ )
+ end
+
+ it "reads manifest from default_manifest setting when environment.conf has manifest set if setting equals default_manifest setting" do
+ File.open(File.join(testingdir, "environment.conf"), "w") do |f|
+ f.puts("manifest=#{manifestsdir}")
+ end
+
+ expect(a_catalog_compiled_for_environment('testing')).to(
+ include_resource('Notify[ManifestFromAbsoluteDefaultManifest]')
+ )
+ end
+
+ it "logs errors if environment.conf specifies a different manifest" do
+ File.open(File.join(testingdir, "environment.conf"), "w") do |f|
+ f.puts("manifest=./special_manifests")
+ end
+
+ Puppet.initialize_settings
+ expect(Puppet[:environmentpath]).to eq(environmentpath)
+ environment = Puppet.lookup(:environments).get('testing')
+ expect(environment.manifest).to eq(manifestsdir)
+ expect(@logs.first.to_s).to match(%r{disable_per_environment_manifest.*is true, but.*environment.*at #{testingdir}.*has.*environment.conf.*manifest.*#{testingdir}/special_manifests})
+ end
+
+ it "raises an error if default_manifest is not absolute" do
+ File.open(File.join(confdir, "puppet.conf"), "w") do |f|
+ f.puts(<<-EOF)
+ environmentpath=#{environmentpath}
+ default_manifest=./relative
+ disable_per_environment_manifest=true
+ EOF
+ end
+
+ expect { Puppet.initialize_settings }.to raise_error(Puppet::Settings::ValidationError, /default_manifest.*must be.*absolute.*when.*disable_per_environment_manifest.*true/)
+ end
+ end
+
+ context "in legacy environments" do
+ let(:environmentpath) { '' }
+ let(:manifestsdir) { File.expand_path("default_manifests", confdir) }
+ let(:legacy_manifestsdir) { File.expand_path('manifests', confdir) }
+
+ before(:each) do
+ FileUtils.mkdir_p(manifestsdir)
+
+ File.open(File.join(confdir, "puppet.conf"), "w") do |f|
+ f.puts(<<-EOF)
+ default_manifest=#{manifestsdir}
+ disable_per_environment_manifest=true
+ manifest=#{legacy_manifestsdir}
+ EOF
+ end
+
+ File.open(File.join(manifestsdir, "site.pp"), "w") do |f|
+ f.puts("notify { 'ManifestFromAbsoluteDefaultManifest': }")
+ end
+ end
+
+ it "has no effect on compilation" do
+ FileUtils.mkdir_p(legacy_manifestsdir)
+
+ File.open(File.join(legacy_manifestsdir, "site.pp"), "w") do |f|
+ f.puts("notify { 'ManifestFromLegacy': }")
+ end
+
+ expect(a_catalog_compiled_for_environment('testing')).to(
+ include_resource('Notify[ManifestFromLegacy]')
+ )
+ end
+ end
+ end
+
+ describe 'using future parser' do
+ before :each do
+ Puppet[:parser] = 'future'
+ end
+ it_behaves_like 'puppet with default_manifest settings'
+ end
+
+ describe 'using current parser' do
+ before :each do
+ Puppet[:parser] = 'current'
+ end
+ it_behaves_like 'puppet with default_manifest settings'
+ end
+
+ RSpec::Matchers.define :include_resource do |expected|
+ match do |actual|
+ actual.resources.map(&:ref).include?(expected)
+ end
+
+ def failure_message_for_should
+ "expected #{@actual.resources.map(&:ref)} to include #{expected}"
+ end
+
+ def failure_message_for_should_not
+ "expected #{@actual.resources.map(&:ref)} not to include #{expected}"
+ end
+ end
+
+ def a_catalog_compiled_for_environment(envname)
+ Puppet.initialize_settings
+ expect(Puppet[:environmentpath]).to eq(environmentpath)
+ node = Puppet::Node.new('testnode', :environment => 'testing')
+ expect(node.environment).to eq(Puppet.lookup(:environments).get('testing'))
+ Puppet::Parser::Compiler.compile(node)
+ end
+end
+end
diff --git a/spec/integration/faces/documentation_spec.rb b/spec/integration/faces/documentation_spec.rb
index bd1f8008a..803d61599 100755
--- a/spec/integration/faces/documentation_spec.rb
+++ b/spec/integration/faces/documentation_spec.rb
@@ -16,10 +16,6 @@ describe "documentation of faces" do
# bug in it, triggered in something the user might do.
context "face help messages" do
- # we need to set a bunk module path here, because without doing so,
- # the autoloader will try to use it before it is initialized.
- Puppet[:modulepath] = "/dev/null"
-
Puppet::Face.faces.sort.each do |face_name|
# REVISIT: We should walk all versions of the face here...
let :help do Puppet::Face[:help, :current] end
diff --git a/spec/integration/file_bucket/file_spec.rb b/spec/integration/file_bucket/file_spec.rb
index 2c411fdf7..f0dbecaa3 100644
--- a/spec/integration/file_bucket/file_spec.rb
+++ b/spec/integration/file_bucket/file_spec.rb
@@ -41,4 +41,25 @@ describe Puppet::FileBucket::File do
end
end
end
+
+ describe "saving binary files" do
+ describe "on Ruby 1.8.7", :if => RUBY_VERSION.match(/^1\.8/) do
+ let(:binary) { "\xD1\xF2\r\n\x81NuSc\x00" }
+
+ it "does not error when the same contents are saved twice" do
+ bucket_file = Puppet::FileBucket::File.new(binary)
+ Puppet::FileBucket::File.indirection.save(bucket_file, bucket_file.name)
+ Puppet::FileBucket::File.indirection.save(bucket_file, bucket_file.name)
+ end
+ end
+ describe "on Ruby 1.9+", :if => RUBY_VERSION.match(/^1\.9|^2/) do
+ let(:binary) { "\xD1\xF2\r\n\x81NuSc\x00".force_encoding(Encoding::ASCII_8BIT) }
+
+ it "does not error when the same contents are saved twice" do
+ bucket_file = Puppet::FileBucket::File.new(binary)
+ Puppet::FileBucket::File.indirection.save(bucket_file, bucket_file.name)
+ Puppet::FileBucket::File.indirection.save(bucket_file, bucket_file.name)
+ end
+ end
+ end
end
diff --git a/spec/integration/indirector/catalog/compiler_spec.rb b/spec/integration/indirector/catalog/compiler_spec.rb
index 1e7f17298..11c448575 100755
--- a/spec/integration/indirector/catalog/compiler_spec.rb
+++ b/spec/integration/indirector/catalog/compiler_spec.rb
@@ -13,8 +13,6 @@ describe Puppet::Resource::Catalog::Compiler do
@catalog.add_resource(@two = Puppet::Resource.new(:file, "/two"))
end
- after { Puppet.settings.clear }
-
it "should remove virtual resources when filtering" do
@one.virtual = true
Puppet::Resource::Catalog.indirection.terminus.filter(@catalog).resource_refs.should == [ @two.ref ]
diff --git a/spec/integration/indirector/catalog/queue_spec.rb b/spec/integration/indirector/catalog/queue_spec.rb
index fe359236f..00f3f2e53 100755
--- a/spec/integration/indirector/catalog/queue_spec.rb
+++ b/spec/integration/indirector/catalog/queue_spec.rb
@@ -17,8 +17,6 @@ describe "Puppet::Resource::Catalog::Queue" do
Puppet[:trace] = true
end
- after { Puppet.settings.clear }
-
it "should render catalogs to pson and publish them via the queue client when catalogs are saved" do
terminus = Puppet::Resource::Catalog.indirection.terminus(:queue)
diff --git a/spec/integration/indirector/facts/facter_spec.rb b/spec/integration/indirector/facts/facter_spec.rb
index c71ff0937..b552431f9 100644
--- a/spec/integration/indirector/facts/facter_spec.rb
+++ b/spec/integration/indirector/facts/facter_spec.rb
@@ -13,7 +13,7 @@ describe Puppet::Node::Facts::Facter do
end
end
- Facter.stubs(:clear)
+ Facter.stubs(:reset)
cat = compile_to_catalog('notify { $downcase_test: }',
Puppet::Node.indirection.find('foo'))
diff --git a/spec/integration/indirector/file_content/file_server_spec.rb b/spec/integration/indirector/file_content/file_server_spec.rb
index 103a028a2..ee0db17a9 100755
--- a/spec/integration/indirector/file_content/file_server_spec.rb
+++ b/spec/integration/indirector/file_content/file_server_spec.rb
@@ -48,9 +48,9 @@ describe Puppet::Indirector::FileContent::FileServer, " when finding files" do
file = File.join(modpath, "files", "myfile")
File.open(file, "wb") { |f| f.write "1\r\n" }
- Puppet.settings[:modulepath] = path
+ env = Puppet::Node::Environment.create(:foo, [path])
- result = Puppet::FileServing::Content.indirection.find("modules/mymod/myfile")
+ result = Puppet::FileServing::Content.indirection.find("modules/mymod/myfile", :environment => env)
result.should_not be_nil
result.should be_instance_of(Puppet::FileServing::Content)
diff --git a/spec/integration/node/environment_spec.rb b/spec/integration/node/environment_spec.rb
index f8a7ace7d..797e4105c 100755
--- a/spec/integration/node/environment_spec.rb
+++ b/spec/integration/node/environment_spec.rb
@@ -80,6 +80,30 @@ describe Puppet::Node::Environment do
end
end
+ shared_examples_for "the environment's initial import in the future" do |settings|
+ it "a manifest referring to a directory invokes recursive parsing of all its files in sorted order" do
+ settings.each do |name, value|
+ Puppet[name] = value
+ end
+
+ # fixture has three files 00_a.pp, 01_b.pp, and 02_c.pp. The 'b' file
+ # depends on 'a' being evaluated first. The 'c' file is empty (to ensure
+ # empty things do not break the directory import).
+ #
+ dirname = my_fixture('sitedir2')
+
+ # Set the manifest to the directory to make it parse and combine them when compiling
+ node = Puppet::Node.new('testnode',
+ :environment => Puppet::Node::Environment.create(:testing, [], dirname))
+
+ catalog = Puppet::Parser::Compiler.compile(node)
+
+ expect(catalog).to have_resource('Class[A]')
+ expect(catalog).to have_resource('Class[B]')
+ expect(catalog).to have_resource('Notify[variables]').with_parameter(:message, "a: 10, b: 10 c: 20")
+ end
+ end
+
describe 'using classic parser' do
it_behaves_like "the environment's initial import",
:parser => 'current',
@@ -92,18 +116,10 @@ describe Puppet::Node::Environment do
describe 'using future parser' do
it_behaves_like "the environment's initial import",
:parser => 'future',
- :evaluator => 'future',
# Turned off because currently future parser turns on the binder which
# causes lookup of facts that are uninitialized and it will fail with
# errors for 'osfamily' etc. This can be turned back on when the binder
# is taken out of the equation.
:strict_variables => false
-
- context 'and evaluator current' do
- it_behaves_like "the environment's initial import",
- :parser => 'future',
- :evaluator => 'current',
- :strict_variables => false
- end
end
end
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
-
diff --git a/spec/integration/provider/cron/crontab_spec.rb b/spec/integration/provider/cron/crontab_spec.rb
index a88eb1c76..e75523cac 100644
--- a/spec/integration/provider/cron/crontab_spec.rb
+++ b/spec/integration/provider/cron/crontab_spec.rb
@@ -2,9 +2,11 @@
require 'spec_helper'
require 'puppet/file_bucket/dipper'
+require 'puppet_spec/compiler'
describe Puppet::Type.type(:cron).provider(:crontab), '(integration)', :unless => Puppet.features.microsoft_windows? do
include PuppetSpec::Files
+ include PuppetSpec::Compiler
before :each do
Puppet::Type.type(:cron).stubs(:defaultprovider).returns described_class
@@ -33,20 +35,6 @@ describe Puppet::Type.type(:cron).provider(:crontab), '(integration)', :unless =
tmpfile('cron_integration_specs')
end
- def run_in_catalog(*resources)
- catalog = Puppet::Resource::Catalog.new
- catalog.host_config = false
- resources.each do |resource|
- resource.expects(:err).never
- catalog.add_resource(resource)
- end
-
- # the resources are not properly contained and generated resources
- # will end up with dangling edges without this stubbing:
- catalog.stubs(:container_of).returns resources[0]
- catalog.apply
- end
-
def expect_output(fixture_name)
File.read(crontab_user1).should == File.read(my_fixture(fixture_name))
end
@@ -54,56 +42,53 @@ describe Puppet::Type.type(:cron).provider(:crontab), '(integration)', :unless =
describe "when managing a cron entry" do
it "should be able to purge unmanaged entries" do
- resource = Puppet::Type.type(:cron).new(
- :name => 'only managed entry',
- :ensure => :present,
- :command => '/bin/true',
- :target => crontab_user1,
- :user => crontab_user1
- )
- resources = Puppet::Type.type(:resources).new(
- :name => 'cron',
- :purge => 'true'
- )
- run_in_catalog(resource, resources)
+ apply_with_error_check(<<-MANIFEST)
+ cron {
+ 'only managed entry':
+ ensure => 'present',
+ command => '/bin/true',
+ target => '#{crontab_user1}',
+ }
+ resources { 'cron': purge => 'true' }
+ MANIFEST
expect_output('purged')
end
describe "with ensure absent" do
it "should do nothing if entry already absent" do
- resource = Puppet::Type.type(:cron).new(
- :name => 'no_such_entry',
- :ensure => :absent,
- :target => crontab_user1,
- :user => crontab_user1
- )
- run_in_catalog(resource)
+ apply_with_error_check(<<-MANIFEST)
+ cron {
+ 'no_such_entry':
+ ensure => 'absent',
+ target => '#{crontab_user1}',
+ }
+ MANIFEST
expect_output('crontab_user1')
end
it "should remove the resource from crontab if present" do
- resource = Puppet::Type.type(:cron).new(
- :name => 'My daily failure',
- :ensure => :absent,
- :target => crontab_user1,
- :user => crontab_user1
- )
- run_in_catalog(resource)
+ apply_with_error_check(<<-MANIFEST)
+ cron {
+ 'My daily failure':
+ ensure => 'absent',
+ target => '#{crontab_user1}',
+ }
+ MANIFEST
expect_output('remove_named_resource')
end
it "should remove a matching cronentry if present" do
- resource = Puppet::Type.type(:cron).new(
- :name => 'no_such_named_resource_in_crontab',
- :ensure => :absent,
- :minute => [ '17-19', '22' ],
- :hour => [ '0-23/2' ],
- :weekday => 'Tue',
- :command => '/bin/unnamed_regular_command',
- :target => crontab_user1,
- :user => crontab_user1
- )
- run_in_catalog(resource)
+ apply_with_error_check(<<-MANIFEST)
+ cron {
+ 'no_such_named_resource_in_crontab':
+ ensure => absent,
+ minute => [ '17-19', '22' ],
+ hour => [ '0-23/2' ],
+ weekday => 'Tue',
+ command => '/bin/unnamed_regular_command',
+ target => '#{crontab_user1}',
+ }
+ MANIFEST
expect_output('remove_unnamed_resource')
end
end
@@ -112,137 +97,141 @@ describe Puppet::Type.type(:cron).provider(:crontab), '(integration)', :unless =
context "and no command specified" do
it "should work if the resource is already present" do
- resource = Puppet::Type.type(:cron).new(
- :name => 'My daily failure',
- :special => 'daily',
- :target => crontab_user1,
- :user => crontab_user1
- )
- run_in_catalog(resource)
+ apply_with_error_check(<<-MANIFEST)
+ cron {
+ 'My daily failure':
+ special => 'daily',
+ target => '#{crontab_user1}',
+ }
+ MANIFEST
expect_output('crontab_user1')
end
it "should fail if the resource needs creating" do
- resource = Puppet::Type.type(:cron).new(
- :name => 'Entirely new resource',
- :special => 'daily',
- :target => crontab_user1,
- :user => crontab_user1
- )
- resource.expects(:err).with(regexp_matches(/no command/))
- run_in_catalog(resource)
+ manifest = <<-MANIFEST
+ cron {
+ 'Entirely new resource':
+ special => 'daily',
+ target => '#{crontab_user1}',
+ }
+ MANIFEST
+ apply_compiled_manifest(manifest) do |res|
+ if res.ref == 'Cron[Entirely new resource]'
+ res.expects(:err).with(regexp_matches(/no command/))
+ else
+ res.expects(:err).never
+ end
+ end
end
end
it "should do nothing if entry already present" do
- resource = Puppet::Type.type(:cron).new(
- :name => 'My daily failure',
- :special => 'daily',
- :command => '/bin/false',
- :target => crontab_user1,
- :user => crontab_user1
- )
- run_in_catalog(resource)
+ apply_with_error_check(<<-MANIFEST)
+ cron {
+ 'My daily failure':
+ special => 'daily',
+ command => '/bin/false',
+ target => '#{crontab_user1}',
+ }
+ MANIFEST
expect_output('crontab_user1')
end
it "should work correctly when managing 'target' but not 'user'" do
- resource = Puppet::Type.type(:cron).new(
- :name => 'My daily failure',
- :special => 'daily',
- :command => '/bin/false',
- :target => crontab_user1
- )
- run_in_catalog(resource)
+ apply_with_error_check(<<-MANIFEST)
+ cron {
+ 'My daily failure':
+ special => 'daily',
+ command => '/bin/false',
+ target => '#{crontab_user1}',
+ }
+ MANIFEST
expect_output('crontab_user1')
end
it "should do nothing if a matching entry already present" do
- resource = Puppet::Type.type(:cron).new(
- :name => 'no_such_named_resource_in_crontab',
- :ensure => :present,
- :minute => [ '17-19', '22' ],
- :hour => [ '0-23/2' ],
- :command => '/bin/unnamed_regular_command',
- :target => crontab_user1,
- :user => crontab_user1
- )
- run_in_catalog(resource)
+ apply_with_error_check(<<-MANIFEST)
+ cron {
+ 'no_such_named_resource_in_crontab':
+ ensure => present,
+ minute => [ '17-19', '22' ],
+ hour => [ '0-23/2' ],
+ command => '/bin/unnamed_regular_command',
+ target => '#{crontab_user1}',
+ }
+ MANIFEST
expect_output('crontab_user1')
end
it "should add a new normal entry if currently absent" do
- resource = Puppet::Type.type(:cron).new(
- :name => 'new entry',
- :ensure => :present,
- :minute => '12',
- :weekday => 'Tue',
- :command => '/bin/new',
- :environment => [
- 'MAILTO=""',
- 'SHELL=/bin/bash'
- ],
- :target => crontab_user1,
- :user => crontab_user1
- )
- run_in_catalog(resource)
+ apply_with_error_check(<<-MANIFEST)
+ cron {
+ 'new entry':
+ ensure => present,
+ minute => '12',
+ weekday => 'Tue',
+ command => '/bin/new',
+ environment => [
+ 'MAILTO=""',
+ 'SHELL=/bin/bash'
+ ],
+ target => '#{crontab_user1}',
+ }
+ MANIFEST
expect_output('create_normal_entry')
end
it "should add a new special entry if currently absent" do
- resource = Puppet::Type.type(:cron).new(
- :name => 'new special entry',
- :ensure => :present,
- :special => 'reboot',
- :command => 'echo "Booted" 1>&2',
- :environment => 'MAILTO=bob@company.com',
- :target => crontab_user1,
- :user => crontab_user1
- )
- run_in_catalog(resource)
+ apply_with_error_check(<<-MANIFEST)
+ cron {
+ 'new special entry':
+ ensure => present,
+ special => 'reboot',
+ command => 'echo "Booted" 1>&2',
+ environment => 'MAILTO=bob@company.com',
+ target => '#{crontab_user1}',
+ }
+ MANIFEST
expect_output('create_special_entry')
end
it "should change existing entry if out of sync" do
- resource = Puppet::Type.type(:cron).new(
- :name => 'Monthly job',
- :ensure => :present,
- :special => 'monthly',
-# :minute => ['22'],
- :command => '/usr/bin/monthly',
- :environment => [],
- :target => crontab_user1,
- :user => crontab_user1
- )
- run_in_catalog(resource)
+ apply_with_error_check(<<-MANIFEST)
+ cron {
+ 'Monthly job':
+ ensure => present,
+ special => 'monthly',
+ #minute => ['22'],
+ command => '/usr/bin/monthly',
+ environment => [],
+ target => '#{crontab_user1}',
+ }
+ MANIFEST
expect_output('modify_entry')
end
it "should change a special schedule to numeric if requested" do
- resource = Puppet::Type.type(:cron).new(
- :name => 'My daily failure',
- :special => 'absent',
- :command => '/bin/false',
- :target => crontab_user1,
- :user => crontab_user1
- )
- run_in_catalog(resource)
+ apply_with_error_check(<<-MANIFEST)
+ cron {
+ 'My daily failure':
+ special => 'absent',
+ command => '/bin/false',
+ target => '#{crontab_user1}',
+ }
+ MANIFEST
expect_output('unspecialized')
end
it "should not try to move an entry from one file to another" do
# force the parsedfile provider to also parse user1's crontab
- random_resource = Puppet::Type.type(:cron).new(
- :name => 'foo',
- :ensure => :absent,
- :target => crontab_user1,
- :user => crontab_user1
- )
- resource = Puppet::Type.type(:cron).new(
- :name => 'My daily failure',
- :special => 'daily',
- :command => "/bin/false",
- :target => crontab_user2,
- :user => crontab_user2
- )
- run_in_catalog(resource)
+ apply_with_error_check(<<-MANIFEST)
+ cron {
+ 'foo':
+ ensure => absent,
+ target => '#{crontab_user1}';
+ 'My daily failure':
+ special => 'daily',
+ command => "/bin/false",
+ target => '#{crontab_user2}',
+ }
+ MANIFEST
File.read(crontab_user1).should == File.read(my_fixture('moved_cronjob_input1'))
File.read(crontab_user2).should == File.read(my_fixture('moved_cronjob_input2'))
end
diff --git a/spec/integration/ssl/certificate_authority_spec.rb b/spec/integration/ssl/certificate_authority_spec.rb
index acbc38cbc..3cf494afa 100755
--- a/spec/integration/ssl/certificate_authority_spec.rb
+++ b/spec/integration/ssl/certificate_authority_spec.rb
@@ -99,6 +99,32 @@ describe Puppet::SSL::CertificateAuthority, :unless => Puppet.features.microsoft
end
end
+ describe "when revoking certificate" do
+ it "should work for one certificate" do
+ certificate_request_for("luke.madstop.com")
+
+ ca.sign("luke.madstop.com")
+ ca.revoke("luke.madstop.com")
+
+ expect { ca.verify("luke.madstop.com") }.to raise_error(
+ Puppet::SSL::CertificateAuthority::CertificateVerificationError,
+ "certificate revoked"
+ )
+ end
+
+ it "should work for several certificates" do
+ 3.times.each do |c|
+ certificate_request_for("luke.madstop.com")
+ ca.sign("luke.madstop.com")
+ ca.destroy("luke.madstop.com")
+ end
+ ca.revoke("luke.madstop.com")
+
+ ca.crl.content.revoked.map { |r| r.serial }.should == [2,3,4] # ca has serial 1
+ end
+
+ end
+
it "allows autosigning certificates concurrently", :unless => Puppet::Util::Platform.windows? do
Puppet[:autosign] = true
hosts = (0..4).collect { |i| certificate_request_for("host#{i}") }
diff --git a/spec/integration/ssl/certificate_request_spec.rb b/spec/integration/ssl/certificate_request_spec.rb
index 4a035d532..eeb29da79 100755
--- a/spec/integration/ssl/certificate_request_spec.rb
+++ b/spec/integration/ssl/certificate_request_spec.rb
@@ -10,8 +10,6 @@ describe Puppet::SSL::CertificateRequest do
# Get a safe temporary file
dir = tmpdir("csr_integration_testing")
- Puppet.settings.clear
-
Puppet.settings[:confdir] = dir
Puppet.settings[:vardir] = dir
Puppet.settings[:group] = Process.gid
@@ -26,10 +24,6 @@ describe Puppet::SSL::CertificateRequest do
Puppet::SSL::CertificateRequest.indirection.termini.clear
end
- after do
- Puppet.settings.clear
- end
-
it "should be able to generate CSRs" do
@csr.generate(@key)
end
diff --git a/spec/integration/ssl/certificate_revocation_list_spec.rb b/spec/integration/ssl/certificate_revocation_list_spec.rb
index 06a69a741..ec344926b 100755
--- a/spec/integration/ssl/certificate_revocation_list_spec.rb
+++ b/spec/integration/ssl/certificate_revocation_list_spec.rb
@@ -20,8 +20,6 @@ describe Puppet::SSL::CertificateRevocationList do
after {
Puppet::SSL::Host.ca_location = :none
- Puppet.settings.clear
-
# This is necessary so the terminus instances don't lie around.
Puppet::SSL::Host.indirection.termini.clear
}
diff --git a/spec/integration/ssl/host_spec.rb b/spec/integration/ssl/host_spec.rb
index fbb108db7..18f0d17fc 100755
--- a/spec/integration/ssl/host_spec.rb
+++ b/spec/integration/ssl/host_spec.rb
@@ -22,8 +22,6 @@ describe Puppet::SSL::Host do
after {
Puppet::SSL::Host.ca_location = :none
-
- Puppet.settings.clear
}
it "should be considered a CA host if its name is equal to 'ca'" do
diff --git a/spec/integration/transaction_spec.rb b/spec/integration/transaction_spec.rb
index fc1fff228..35557b8f2 100755
--- a/spec/integration/transaction_spec.rb
+++ b/spec/integration/transaction_spec.rb
@@ -193,6 +193,22 @@ describe Puppet::Transaction do
Puppet::FileSystem.exist?(file2).should be_true
end
+ it "should apply no resources whatsoever if a pre_run_check fails" do
+ path = tmpfile("path")
+ file = Puppet::Type.type(:file).new(
+ :path => path,
+ :ensure => "file"
+ )
+ notify = Puppet::Type.type(:notify).new(
+ :title => "foo"
+ )
+ notify.expects(:pre_run_check).raises(Puppet::Error, "fail for testing")
+
+ catalog = mk_catalog(file, notify)
+ catalog.apply
+ Puppet::FileSystem.exist?(path).should_not be_true
+ end
+
it "should not let one failed refresh result in other refreshes failing" do
path = tmpfile("path")
newfile = tmpfile("file")
diff --git a/spec/integration/type/file_spec.rb b/spec/integration/type/file_spec.rb
index ed8a9768f..d1862e241 100755
--- a/spec/integration/type/file_spec.rb
+++ b/spec/integration/type/file_spec.rb
@@ -78,7 +78,7 @@ describe Puppet::Type.type(:file), :uses_checksums => true do
it "should not attempt to manage files that do not exist if no means of creating the file is specified" do
source = tmpfile('source')
- catalog.add_resource described_class.new :path => source, :mode => 0755
+ catalog.add_resource described_class.new :path => source, :mode => '0755'
status = catalog.apply.report.resource_statuses["File[#{source}]"]
status.should_not be_failed
@@ -155,7 +155,7 @@ describe Puppet::Type.type(:file), :uses_checksums => true do
it "should set executable bits for existing readable directories" do
set_mode(0600, target)
- catalog.add_resource described_class.new(:path => target, :ensure => :directory, :mode => 0644)
+ catalog.add_resource described_class.new(:path => target, :ensure => :directory, :mode => '0644')
catalog.apply
(get_mode(target) & 07777).should == 0755
@@ -992,13 +992,13 @@ describe Puppet::Type.type(:file), :uses_checksums => true do
describe "on Windows systems", :if => Puppet.features.microsoft_windows? do
def expects_sid_granted_full_access_explicitly(path, sid)
- inherited_ace = Windows::Security::INHERITED_ACE
+ inherited_ace = Puppet::Util::Windows::AccessControlEntry::INHERITED_ACE
aces = get_aces_for_path_by_sid(path, sid)
aces.should_not be_empty
aces.each do |ace|
- ace.mask.should == Windows::File::FILE_ALL_ACCESS
+ ace.mask.should == Puppet::Util::Windows::File::FILE_ALL_ACCESS
(ace.flags & inherited_ace).should_not == inherited_ace
end
end
@@ -1008,13 +1008,13 @@ describe Puppet::Type.type(:file), :uses_checksums => true do
end
def expects_at_least_one_inherited_ace_grants_full_access(path, sid)
- inherited_ace = Windows::Security::INHERITED_ACE
+ inherited_ace = Puppet::Util::Windows::AccessControlEntry::INHERITED_ACE
aces = get_aces_for_path_by_sid(path, sid)
aces.should_not be_empty
aces.any? do |ace|
- ace.mask == Windows::File::FILE_ALL_ACCESS &&
+ ace.mask == Puppet::Util::Windows::File::FILE_ALL_ACCESS &&
(ace.flags & inherited_ace) == inherited_ace
end.should be_true
end
@@ -1045,10 +1045,10 @@ describe Puppet::Type.type(:file), :uses_checksums => true do
describe "when processing SYSTEM ACEs" do
before do
@sids = {
- :current_user => Puppet::Util::Windows::Security.name_to_sid(Sys::Admin.get_login),
+ :current_user => Puppet::Util::Windows::SID.name_to_sid(Puppet::Util::Windows::ADSI::User.current_user_name),
:system => Win32::Security::SID::LocalSystem,
- :admin => Puppet::Util::Windows::Security.name_to_sid("Administrator"),
- :guest => Puppet::Util::Windows::Security.name_to_sid("Guest"),
+ :admin => Puppet::Util::Windows::SID.name_to_sid("Administrator"),
+ :guest => Puppet::Util::Windows::SID.name_to_sid("Guest"),
:users => Win32::Security::SID::BuiltinUsers,
:power_users => Win32::Security::SID::PowerUsers,
:none => Win32::Security::SID::Nobody
@@ -1132,7 +1132,7 @@ describe Puppet::Type.type(:file), :uses_checksums => true do
system_aces.should_not be_empty
system_aces.each do |ace|
- ace.mask.should == Windows::File::FILE_GENERIC_READ
+ ace.mask.should == Puppet::Util::Windows::File::FILE_GENERIC_READ
end
end
@@ -1173,8 +1173,9 @@ describe Puppet::Type.type(:file), :uses_checksums => true do
sd = Puppet::Util::Windows::Security.get_security_descriptor(path)
sd.dacl.allow(
'S-1-1-0', #everyone
- Windows::File::FILE_ALL_ACCESS,
- Windows::File::OBJECT_INHERIT_ACE | Windows::File::CONTAINER_INHERIT_ACE)
+ Puppet::Util::Windows::File::FILE_ALL_ACCESS,
+ Puppet::Util::Windows::AccessControlEntry::OBJECT_INHERIT_ACE |
+ Puppet::Util::Windows::AccessControlEntry::CONTAINER_INHERIT_ACE)
Puppet::Util::Windows::Security.set_security_descriptor(path, sd)
end
@@ -1252,7 +1253,7 @@ describe Puppet::Type.type(:file), :uses_checksums => true do
system_aces.each do |ace|
# unlike files, Puppet sets execute bit on directories that are readable
- ace.mask.should == Windows::File::FILE_GENERIC_READ | Windows::File::FILE_GENERIC_EXECUTE
+ ace.mask.should == Puppet::Util::Windows::File::FILE_GENERIC_READ | Puppet::Util::Windows::File::FILE_GENERIC_EXECUTE
end
end
diff --git a/spec/integration/type/nagios_spec.rb b/spec/integration/type/nagios_spec.rb
index 818b61649..9610daefa 100644
--- a/spec/integration/type/nagios_spec.rb
+++ b/spec/integration/type/nagios_spec.rb
@@ -6,9 +6,11 @@ require 'puppet/file_bucket/dipper'
describe "Nagios file creation" do
include PuppetSpec::Files
+ let(:initial_mode) { 0600 }
+
before :each do
FileUtils.touch(target_file)
- File.chmod(0600, target_file)
+ Puppet::FileSystem.chmod(initial_mode, target_file)
Puppet::FileBucket::Dipper.any_instance.stubs(:backup) # Don't backup to filebucket
end
@@ -33,13 +35,6 @@ describe "Nagios file creation" do
catalog.apply
end
- # These three helpers are from file_spec.rb
- #
- # @todo Define those centrally as well?
- def get_mode(file)
- Puppet::FileSystem.stat(file).mode
- end
-
context "when creating a nagios config file" do
context "which is not managed" do
it "should choose the file mode if requested" do
@@ -51,14 +46,12 @@ describe "Nagios file creation" do
:mode => '0640'
)
run_in_catalog(resource)
- # sticky bit only applies to directories in Windows
- mode = Puppet.features.microsoft_windows? ? "640" : "100640"
- ( "%o" % get_mode(target_file) ).should == mode
+ expect_file_mode(target_file, "640")
end
end
context "which is managed" do
- it "should not the mode" do
+ it "should not override the mode" do
file_res = Puppet::Type.type(:file).new(
:name => target_file,
:ensure => :present
@@ -71,10 +64,8 @@ describe "Nagios file creation" do
:mode => '0640'
)
run_in_catalog(file_res, nag_res)
- ( "%o" % get_mode(target_file) ).should_not == "100640"
+ expect_file_mode(target_file, initial_mode.to_s(8))
end
end
-
end
-
end
diff --git a/spec/integration/type/sshkey_spec.rb b/spec/integration/type/sshkey_spec.rb
new file mode 100644
index 000000000..d1b1e01c7
--- /dev/null
+++ b/spec/integration/type/sshkey_spec.rb
@@ -0,0 +1,22 @@
+#! /usr/bin/env ruby
+require 'spec_helper'
+require 'puppet_spec/files'
+require 'puppet_spec/compiler'
+
+describe Puppet::Type.type(:sshkey), '(integration)', :unless => Puppet.features.microsoft_windows? do
+ include PuppetSpec::Files
+ include PuppetSpec::Compiler
+
+ let(:target) { tmpfile('ssh_known_hosts') }
+ let(:manifest) { "sshkey { 'test':
+ ensure => 'present',
+ type => 'rsa',
+ key => 'TESTKEY',
+ target => '#{target}' }"
+ }
+
+ it "should create a new known_hosts file with mode 0644" do
+ apply_compiled_manifest(manifest)
+ expect_file_mode(target, "644")
+ end
+end
diff --git a/spec/integration/type/tidy_spec.rb b/spec/integration/type/tidy_spec.rb
index 9c044d703..7dbefb6ca 100755
--- a/spec/integration/type/tidy_spec.rb
+++ b/spec/integration/type/tidy_spec.rb
@@ -23,6 +23,9 @@ describe Puppet::Type.type(:tidy) do
catalog = Puppet::Resource::Catalog.new
catalog.add_resource(tidy)
+ # avoid crude failures because of nil resources that result
+ # from implicit containment and lacking containers
+ catalog.stubs(:container_of).returns tidy
catalog.apply
diff --git a/spec/integration/type/user_spec.rb b/spec/integration/type/user_spec.rb
new file mode 100644
index 000000000..4724fe9d5
--- /dev/null
+++ b/spec/integration/type/user_spec.rb
@@ -0,0 +1,36 @@
+#! /usr/bin/env ruby
+require 'spec_helper'
+require 'puppet_spec/files'
+require 'puppet_spec/compiler'
+
+describe Puppet::Type.type(:user), '(integration)', :unless => Puppet.features.microsoft_windows? do
+ include PuppetSpec::Files
+ include PuppetSpec::Compiler
+
+ context "when set to purge ssh keys from a file" do
+ let(:tempfile) { file_containing('user_spec', "# comment\nssh-rsa KEY-DATA key-name\nssh-rsa KEY-DATA key name\n") }
+ # must use an existing user, or the generated key resource
+ # will fail on account of an invalid user for the key
+ # - root should be a safe default
+ let(:manifest) { "user { 'root': purge_ssh_keys => '#{tempfile}' }" }
+
+ it "should purge authorized ssh keys" do
+ apply_compiled_manifest(manifest)
+ File.read(tempfile).should_not =~ /key-name/
+ end
+
+ it "should purge keys with spaces in the comment string" do
+ apply_compiled_manifest(manifest)
+ File.read(tempfile).should_not =~ /key name/
+ end
+
+ context "with other prefetching resources evaluated first" do
+ let(:manifest) { "host { 'test': before => User[root] } user { 'root': purge_ssh_keys => '#{tempfile}' }" }
+
+ it "should purge authorized ssh keys" do
+ apply_compiled_manifest(manifest)
+ File.read(tempfile).should_not =~ /key-name/
+ end
+ end
+ end
+end
diff --git a/spec/integration/util/autoload_spec.rb b/spec/integration/util/autoload_spec.rb
index bfe8b67d2..c352fea9e 100755
--- a/spec/integration/util/autoload_spec.rb
+++ b/spec/integration/util/autoload_spec.rb
@@ -95,12 +95,12 @@ describe Puppet::Util::Autoload do
file = File.join(libdir, "plugin.rb")
- Puppet[:modulepath] = modulepath
-
- with_loader("foo", "foo") do |dir, loader|
- with_file(:plugin, file.split("/")) do
- loader.load(:plugin)
- loader.class.should be_loaded("foo/plugin.rb")
+ Puppet.override(:environments => Puppet::Environments::Static.new(Puppet::Node::Environment.create(:production, [modulepath]))) do
+ with_loader("foo", "foo") do |dir, loader|
+ with_file(:plugin, file.split("/")) do
+ loader.load(:plugin)
+ loader.class.should be_loaded("foo/plugin.rb")
+ end
end
end
end
diff --git a/spec/integration/util/rdoc/parser_spec.rb b/spec/integration/util/rdoc/parser_spec.rb
index d3bbef45c..2fdaf8019 100755
--- a/spec/integration/util/rdoc/parser_spec.rb
+++ b/spec/integration/util/rdoc/parser_spec.rb
@@ -127,6 +127,13 @@ end
end.should_not(be_empty, "Could not match #{content_patterns} in any of the files found in #{glob}")
end
+ around(:each) do |example|
+ env = Puppet::Node::Environment.create(:doc_test_env, [modules_dir], manifests_dir)
+ Puppet.override({:environments => Puppet::Environments::Static.new(env), :current_environment => env}) do
+ example.run
+ end
+ end
+
before :each do
prepare_manifests_and_modules
Puppet.settings[:document_all] = document_all
diff --git a/spec/integration/util/windows/process_spec.rb b/spec/integration/util/windows/process_spec.rb
index 6dc54d228..60eae3443 100644
--- a/spec/integration/util/windows/process_spec.rb
+++ b/spec/integration/util/windows/process_spec.rb
@@ -18,5 +18,17 @@ describe "Puppet::Util::Windows::Process", :if => Puppet.features.microsoft_wind
Puppet::Util::Windows::User.should be_admin
Puppet::Util::Windows::Process.process_privilege_symlink?.should be_false
end
+
+ it "should be able to lookup a standard Windows process privilege" do
+ Puppet::Util::Windows::Process.lookup_privilege_value('SeShutdownPrivilege') do |luid|
+ luid.should_not be_nil
+ luid.should be_instance_of(Puppet::Util::Windows::Process::LUID)
+ end
+ end
+
+ it "should raise an error for an unknown privilege name" do
+ fail_msg = /LookupPrivilegeValue\(, foo, .*\): A specified privilege does not exist/
+ expect { Puppet::Util::Windows::Process.lookup_privilege_value('foo') }.to raise_error(Puppet::Util::Windows::Error, fail_msg)
+ end
end
end
diff --git a/spec/integration/util/windows/security_spec.rb b/spec/integration/util/windows/security_spec.rb
index fa0eadc0d..7f7aa7cb6 100755
--- a/spec/integration/util/windows/security_spec.rb
+++ b/spec/integration/util/windows/security_spec.rb
@@ -1,8 +1,6 @@
#!/usr/bin/env ruby
require 'spec_helper'
-require 'puppet/util/adsi'
-
if Puppet.features.microsoft_windows?
class WindowsSecurityTester
require 'puppet/util/windows/security'
@@ -15,11 +13,11 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win
before :all do
@sids = {
- :current_user => Puppet::Util::Windows::Security.name_to_sid(Sys::Admin.get_login),
+ :current_user => Puppet::Util::Windows::SID.name_to_sid(Puppet::Util::Windows::ADSI::User.current_user_name),
:system => Win32::Security::SID::LocalSystem,
- :admin => Puppet::Util::Windows::Security.name_to_sid("Administrator"),
+ :admin => Puppet::Util::Windows::SID.name_to_sid("Administrator"),
:administrators => Win32::Security::SID::BuiltinAdministrators,
- :guest => Puppet::Util::Windows::Security.name_to_sid("Guest"),
+ :guest => Puppet::Util::Windows::SID.name_to_sid("Guest"),
:users => Win32::Security::SID::BuiltinUsers,
:power_users => Win32::Security::SID::PowerUsers,
:none => Win32::Security::SID::Nobody,
@@ -31,11 +29,12 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win
# (like \\localhost) to fail with unhelpful error messages.
# Put a check for this upfront to aid debug should this strike again.
service = Puppet::Type.type(:service).new(:name => 'lmhosts')
- service.provider.status.should == :running
+ expect(service.provider.status).to eq(:running), 'lmhosts service is not running'
end
let (:sids) { @sids }
let (:winsec) { WindowsSecurityTester.new }
+ let (:klass) { Puppet::Util::Windows::File }
def set_group_depending_on_current_user(path)
if sids[:current_user] == sids[:system]
@@ -53,8 +52,8 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win
def grant_everyone_full_access(path)
sd = winsec.get_security_descriptor(path)
everyone = 'S-1-1-0'
- inherit = WindowsSecurityTester::OBJECT_INHERIT_ACE | WindowsSecurityTester::CONTAINER_INHERIT_ACE
- sd.dacl.allow(everyone, Windows::File::FILE_ALL_ACCESS, inherit)
+ inherit = Puppet::Util::Windows::AccessControlEntry::OBJECT_INHERIT_ACE | Puppet::Util::Windows::AccessControlEntry::CONTAINER_INHERIT_ACE
+ sd.dacl.allow(everyone, klass::FILE_ALL_ACCESS, inherit)
winsec.set_security_descriptor(path, sd)
end
@@ -178,7 +177,7 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win
# when running under SYSTEM account, multiple ACEs come back
# so we only care that we have at least one of these
system_aces.any? do |ace|
- ace.mask == Windows::File::FILE_ALL_ACCESS
+ ace.mask == klass::FILE_ALL_ACCESS
end.should be_true
# changing the owner/group will no longer make the SD protected
@@ -186,7 +185,7 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win
winsec.set_owner(sids[:administrators], path)
system_aces.find do |ace|
- ace.mask == Windows::File::FILE_ALL_ACCESS && ace.inherited?
+ ace.mask == klass::FILE_ALL_ACCESS && ace.inherited?
end.should_not be_nil
end
@@ -227,7 +226,7 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win
# when running under SYSTEM account, multiple ACEs come back
# so we only care that we have at least one of these
system_aces.any? do |ace|
- ace.mask == WindowsSecurityTester::FILE_ALL_ACCESS
+ ace.mask == klass::FILE_ALL_ACCESS
end.should be_true
# changing the mode will make the SD protected
@@ -237,7 +236,7 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win
# and should have a non-inherited SYSTEM ACE(s)
system_aces = winsec.get_aces_for_path_by_sid(path, sids[:system])
system_aces.each do |ace|
- ace.mask.should == Windows::File::FILE_ALL_ACCESS && ! ace.inherited?
+ ace.mask.should == klass::FILE_ALL_ACCESS && ! ace.inherited?
end
end
@@ -259,25 +258,25 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win
before :each do
winsec.set_group(sids[:none], path)
winsec.set_mode(0600, path)
- winsec.add_attributes(path, WindowsSecurityTester::FILE_ATTRIBUTE_READONLY)
- (winsec.get_attributes(path) & WindowsSecurityTester::FILE_ATTRIBUTE_READONLY).should be_nonzero
+ Puppet::Util::Windows::File.add_attributes(path, klass::FILE_ATTRIBUTE_READONLY)
+ (Puppet::Util::Windows::File.get_attributes(path) & klass::FILE_ATTRIBUTE_READONLY).should be_nonzero
end
it "should make them writable if any sid has write permission" do
winsec.set_mode(WindowsSecurityTester::S_IWUSR, path)
- (winsec.get_attributes(path) & WindowsSecurityTester::FILE_ATTRIBUTE_READONLY).should == 0
+ (Puppet::Util::Windows::File.get_attributes(path) & klass::FILE_ATTRIBUTE_READONLY).should == 0
end
it "should leave them read-only if no sid has write permission and should allow full access for SYSTEM" do
winsec.set_mode(WindowsSecurityTester::S_IRUSR | WindowsSecurityTester::S_IXGRP, path)
- (winsec.get_attributes(path) & WindowsSecurityTester::FILE_ATTRIBUTE_READONLY).should be_nonzero
+ (Puppet::Util::Windows::File.get_attributes(path) & klass::FILE_ATTRIBUTE_READONLY).should be_nonzero
system_aces = winsec.get_aces_for_path_by_sid(path, sids[:system])
# when running under SYSTEM account, and set_group / set_owner hasn't been called
# SYSTEM full access will be restored
system_aces.any? do |ace|
- ace.mask == Windows::File::FILE_ALL_ACCESS
+ ace.mask == klass::FILE_ALL_ACCESS
end.should be_true
end
end
@@ -291,7 +290,7 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win
it "should report when extra aces are encounted" do
sd = winsec.get_security_descriptor(path)
(544..547).each do |rid|
- sd.dacl.allow("S-1-5-32-#{rid}", WindowsSecurityTester::STANDARD_RIGHTS_ALL)
+ sd.dacl.allow("S-1-5-32-#{rid}", klass::STANDARD_RIGHTS_ALL)
end
winsec.set_security_descriptor(path, sd)
@@ -301,12 +300,12 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win
it "should return deny aces" do
sd = winsec.get_security_descriptor(path)
- sd.dacl.deny(sids[:guest], WindowsSecurityTester::FILE_GENERIC_WRITE)
+ sd.dacl.deny(sids[:guest], klass::FILE_GENERIC_WRITE)
winsec.set_security_descriptor(path, sd)
guest_aces = winsec.get_aces_for_path_by_sid(path, sids[:guest])
guest_aces.find do |ace|
- ace.type == WindowsSecurityTester::ACCESS_DENIED_ACE_TYPE
+ ace.type == Puppet::Util::Windows::AccessControlEntry::ACCESS_DENIED_ACE_TYPE
end.should_not be_nil
end
@@ -314,12 +313,12 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win
sd = winsec.get_security_descriptor(path)
dacl = Puppet::Util::Windows::AccessControlList.new
dacl.allow(
- sids[:current_user], WindowsSecurityTester::STANDARD_RIGHTS_ALL | WindowsSecurityTester::SPECIFIC_RIGHTS_ALL
+ sids[:current_user], klass::STANDARD_RIGHTS_ALL | klass::SPECIFIC_RIGHTS_ALL
)
dacl.allow(
sids[:everyone],
- WindowsSecurityTester::FILE_GENERIC_READ,
- WindowsSecurityTester::INHERIT_ONLY_ACE | WindowsSecurityTester::OBJECT_INHERIT_ACE
+ klass::FILE_GENERIC_READ,
+ Puppet::Util::Windows::AccessControlEntry::INHERIT_ONLY_ACE | Puppet::Util::Windows::AccessControlEntry::OBJECT_INHERIT_ACE
)
winsec.set_security_descriptor(path, sd)
@@ -344,8 +343,8 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win
it "should be present when the access control list is unprotected" do
# add a bunch of aces to the parent with permission to add children
- allow = WindowsSecurityTester::STANDARD_RIGHTS_ALL | WindowsSecurityTester::SPECIFIC_RIGHTS_ALL
- inherit = WindowsSecurityTester::OBJECT_INHERIT_ACE | WindowsSecurityTester::CONTAINER_INHERIT_ACE
+ allow = klass::STANDARD_RIGHTS_ALL | klass::SPECIFIC_RIGHTS_ALL
+ inherit = Puppet::Util::Windows::AccessControlEntry::OBJECT_INHERIT_ACE | Puppet::Util::Windows::AccessControlEntry::CONTAINER_INHERIT_ACE
sd = winsec.get_security_descriptor(parent)
sd.dacl.allow(
@@ -356,7 +355,7 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win
(544..547).each do |rid|
sd.dacl.allow(
"S-1-5-32-#{rid}",
- WindowsSecurityTester::STANDARD_RIGHTS_ALL,
+ klass::STANDARD_RIGHTS_ALL,
inherit
)
end
@@ -371,10 +370,12 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win
describe "for an administrator", :if => Puppet.features.root? do
before :each do
+ is_dir = Puppet::FileSystem.directory?(path)
winsec.set_mode(WindowsSecurityTester::S_IRWXU | WindowsSecurityTester::S_IRWXG, path)
set_group_depending_on_current_user(path)
winsec.set_owner(sids[:guest], path)
- lambda { File.open(path, 'r') }.should raise_error(Errno::EACCES)
+ expected_error = RUBY_VERSION =~ /^2\./ && is_dir ? Errno::EISDIR : Errno::EACCES
+ lambda { File.open(path, 'r') }.should raise_error(expected_error)
end
after :each do
@@ -446,14 +447,14 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win
describe "when the sid refers to a deleted trustee" do
it "should retrieve the user sid" do
sid = nil
- user = Puppet::Util::ADSI::User.create("delete_me_user")
+ user = Puppet::Util::Windows::ADSI::User.create("delete_me_user")
user.commit
begin
- sid = Sys::Admin::get_user(user.name).sid
+ sid = Puppet::Util::Windows::ADSI::User.new(user.name).sid.to_s
winsec.set_owner(sid, path)
winsec.set_mode(WindowsSecurityTester::S_IRWXU, path)
ensure
- Puppet::Util::ADSI::User.delete(user.name)
+ Puppet::Util::Windows::ADSI::User.delete(user.name)
end
winsec.get_owner(path).should == sid
@@ -462,14 +463,14 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win
it "should retrieve the group sid" do
sid = nil
- group = Puppet::Util::ADSI::Group.create("delete_me_group")
+ group = Puppet::Util::Windows::ADSI::Group.create("delete_me_group")
group.commit
begin
- sid = Sys::Admin::get_group(group.name).sid
+ sid = Puppet::Util::Windows::ADSI::Group.new(group.name).sid.to_s
winsec.set_group(sid, path)
winsec.set_mode(WindowsSecurityTester::S_IRWXG, path)
ensure
- Puppet::Util::ADSI::Group.delete(group.name)
+ Puppet::Util::Windows::ADSI::Group.delete(group.name)
end
winsec.get_group(path).should == sid
winsec.get_mode(path).should == WindowsSecurityTester::S_IRWXG
@@ -813,7 +814,7 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win
Puppet::Util::Windows::AccessControlEntry::CONTAINER_INHERIT_ACE
sd = winsec.get_security_descriptor(dir)
- sd.dacl.allow(sd.owner, Windows::File::FILE_ALL_ACCESS, inherit_flags)
+ sd.dacl.allow(sd.owner, klass::FILE_ALL_ACCESS, inherit_flags)
winsec.set_security_descriptor(dir, sd)
sd = winsec.get_security_descriptor(dir)
@@ -834,7 +835,7 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win
Puppet::Util::Windows::AccessControlEntry::CONTAINER_INHERIT_ACE
sd = winsec.get_security_descriptor(dir)
- sd.dacl.deny(sids[:guest], Windows::File::FILE_ALL_ACCESS, inherit_flags)
+ sd.dacl.deny(sids[:guest], klass::FILE_ALL_ACCESS, inherit_flags)
winsec.set_security_descriptor(dir, sd)
sd = winsec.get_security_descriptor(dir)
diff --git a/spec/integration/util/windows/user_spec.rb b/spec/integration/util/windows/user_spec.rb
index 0435b2cdc..4e873b34c 100755
--- a/spec/integration/util/windows/user_spec.rb
+++ b/spec/integration/util/windows/user_spec.rb
@@ -10,23 +10,23 @@ describe "Puppet::Util::Windows::User", :if => Puppet.features.microsoft_windows
it "should be an admin if user's token contains the Administrators SID" do
Puppet::Util::Windows::User.expects(:check_token_membership).returns(true)
- Win32::Security.expects(:elevated_security?).never
+ Puppet::Util::Windows::Process.expects(:elevated_security?).never
Puppet::Util::Windows::User.should be_admin
end
it "should not be an admin if user's token doesn't contain the Administrators SID" do
Puppet::Util::Windows::User.expects(:check_token_membership).returns(false)
- Win32::Security.expects(:elevated_security?).never
+ Puppet::Util::Windows::Process.expects(:elevated_security?).never
Puppet::Util::Windows::User.should_not be_admin
end
it "should raise an exception if we can't check token membership" do
- Puppet::Util::Windows::User.expects(:check_token_membership).raises(Win32::Security::Error, "Access denied.")
- Win32::Security.expects(:elevated_security?).never
+ Puppet::Util::Windows::User.expects(:check_token_membership).raises(Puppet::Util::Windows::Error, "Access denied.")
+ Puppet::Util::Windows::Process.expects(:elevated_security?).never
- lambda { Puppet::Util::Windows::User.admin? }.should raise_error(Win32::Security::Error, /Access denied./)
+ lambda { Puppet::Util::Windows::User.admin? }.should raise_error(Puppet::Util::Windows::Error, /Access denied./)
end
end
@@ -36,24 +36,90 @@ describe "Puppet::Util::Windows::User", :if => Puppet.features.microsoft_windows
end
it "should be an admin if user is running with elevated privileges" do
- Win32::Security.stubs(:elevated_security?).returns(true)
+ Puppet::Util::Windows::Process.stubs(:elevated_security?).returns(true)
Puppet::Util::Windows::User.expects(:check_token_membership).never
Puppet::Util::Windows::User.should be_admin
end
it "should not be an admin if user is not running with elevated privileges" do
- Win32::Security.stubs(:elevated_security?).returns(false)
+ Puppet::Util::Windows::Process.stubs(:elevated_security?).returns(false)
Puppet::Util::Windows::User.expects(:check_token_membership).never
Puppet::Util::Windows::User.should_not be_admin
end
it "should raise an exception if the process fails to open the process token" do
- Win32::Security.stubs(:elevated_security?).raises(Win32::Security::Error, "Access denied.")
+ Puppet::Util::Windows::Process.stubs(:elevated_security?).raises(Puppet::Util::Windows::Error, "Access denied.")
Puppet::Util::Windows::User.expects(:check_token_membership).never
- lambda { Puppet::Util::Windows::User.admin? }.should raise_error(Win32::Security::Error, /Access denied./)
+ lambda { Puppet::Util::Windows::User.admin? }.should raise_error(Puppet::Util::Windows::Error, /Access denied./)
+ end
+ end
+
+ describe "module function" do
+ let(:username) { 'fabio' }
+ let(:bad_password) { 'goldilocks' }
+ let(:logon_fail_msg) { /Failed to logon user "fabio": Logon failure: unknown user name or bad password./ }
+
+ def expect_logon_failure_error(&block)
+ expect {
+ yield
+ }.to raise_error { |error|
+ expect(error).to be_a(Puppet::Util::Windows::Error)
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/ms681385(v=vs.85).aspx
+ # ERROR_LOGON_FAILURE 1326
+ expect(error.code).to eq(1326)
+ }
+ end
+
+ describe "load_profile" do
+ it "should raise an error when provided with an incorrect username and password" do
+ expect_logon_failure_error {
+ Puppet::Util::Windows::User.load_profile(username, bad_password)
+ }
+ end
+
+ it "should raise an error when provided with an incorrect username and nil password" do
+ expect_logon_failure_error {
+ Puppet::Util::Windows::User.load_profile(username, nil)
+ }
+ end
+ end
+
+ describe "logon_user" do
+ it "should raise an error when provided with an incorrect username and password" do
+ expect_logon_failure_error {
+ Puppet::Util::Windows::User.logon_user(username, bad_password)
+ }
+ end
+
+ it "should raise an error when provided with an incorrect username and nil password" do
+ expect_logon_failure_error {
+ Puppet::Util::Windows::User.logon_user(username, nil)
+ }
+ end
+ end
+
+ describe "password_is?" do
+ it "should return false given an incorrect username and password" do
+ Puppet::Util::Windows::User.password_is?(username, bad_password).should be_false
+ end
+
+ it "should return false given an incorrect username and nil password" do
+ Puppet::Util::Windows::User.password_is?(username, nil).should be_false
+ end
+
+ it "should return false given a nil username and an incorrect password" do
+ Puppet::Util::Windows::User.password_is?(nil, bad_password).should be_false
+ end
+ end
+
+ describe "check_token_membership" do
+ it "should not raise an error" do
+ # added just to call an FFI code path on all platforms
+ lambda { Puppet::Util::Windows::User.check_token_membership }.should_not raise_error
+ end
end
end
end
diff --git a/spec/integration/util_spec.rb b/spec/integration/util_spec.rb
index d8d96aad8..84f155123 100755
--- a/spec/integration/util_spec.rb
+++ b/spec/integration/util_spec.rb
@@ -36,7 +36,7 @@ describe Puppet::Util do
admins = 'S-1-5-32-544'
dacl = Puppet::Util::Windows::AccessControlList.new
- dacl.allow(admins, Windows::File::FILE_ALL_ACCESS)
+ dacl.allow(admins, Puppet::Util::Windows::File::FILE_ALL_ACCESS)
protect = true
expected_sd = Puppet::Util::Windows::SecurityDescriptor.new(admins, admins, dacl, protect)
Puppet::Util::Windows::Security.set_security_descriptor(file, expected_sd)
@@ -45,7 +45,7 @@ describe Puppet::Util do
Puppet::Util.replace_file(file, ignored_mode) do |temp_file|
ignored_sd = Puppet::Util::Windows::Security.get_security_descriptor(temp_file.path)
users = 'S-1-5-11'
- ignored_sd.dacl.allow(users, Windows::File::FILE_GENERIC_READ)
+ ignored_sd.dacl.allow(users, Puppet::Util::Windows::File::FILE_GENERIC_READ)
Puppet::Util::Windows::Security.set_security_descriptor(temp_file.path, ignored_sd)
end