puppet 2.6.7 → 2.6.8

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puppet might be problematic. Click here for more details.

Files changed (80) hide show
  1. data/CHANGELOG +49 -0
  2. data/install.rb +6 -2
  3. data/lib/puppet.rb +1 -1
  4. data/lib/puppet/application.rb +16 -8
  5. data/lib/puppet/application/agent.rb +2 -0
  6. data/lib/puppet/application/apply.rb +3 -0
  7. data/lib/puppet/application/master.rb +1 -1
  8. data/lib/puppet/configurer.rb +10 -1
  9. data/lib/puppet/defaults.rb +9 -0
  10. data/lib/puppet/file_serving/fileset.rb +1 -0
  11. data/lib/puppet/indirector/exec.rb +1 -2
  12. data/lib/puppet/indirector/report/yaml.rb +11 -0
  13. data/lib/puppet/node/environment.rb +1 -1
  14. data/lib/puppet/parameter.rb +2 -0
  15. data/lib/puppet/parameter/path.rb +42 -0
  16. data/lib/puppet/parser/compiler.rb +1 -1
  17. data/lib/puppet/parser/lexer.rb +3 -2
  18. data/lib/puppet/parser/parser_support.rb +0 -1
  19. data/lib/puppet/provider/exec/posix.rb +112 -0
  20. data/lib/puppet/provider/exec/shell.rb +17 -0
  21. data/lib/puppet/provider/group/groupadd.rb +3 -0
  22. data/lib/puppet/provider/nameservice/#directoryservice.rb# +519 -0
  23. data/lib/puppet/provider/package/gem.rb +2 -2
  24. data/lib/puppet/provider/package/macports.rb +106 -0
  25. data/lib/puppet/provider/service/debian.rb +6 -2
  26. data/lib/puppet/rails/inventory_node.rb +5 -0
  27. data/lib/puppet/reference/#providers.rb# +123 -0
  28. data/lib/puppet/resource/type_collection.rb +6 -1
  29. data/lib/puppet/simple_graph.rb +1 -1
  30. data/lib/puppet/transaction.rb +1 -1
  31. data/lib/puppet/transaction/report.rb +28 -10
  32. data/lib/puppet/type/cron.rb +3 -1
  33. data/lib/puppet/type/exec.rb +30 -167
  34. data/lib/puppet/type/file.rb +12 -1
  35. data/lib/puppet/type/file/source.rb +1 -0
  36. data/lib/puppet/type/group.rb +11 -1
  37. data/lib/puppet/type/service.rb +19 -11
  38. data/lib/puppet/util/command_line.rb +15 -12
  39. data/lib/puppet/util/command_line/puppetrun +0 -1
  40. data/lib/puppet/util/loadedfile.rb +1 -5
  41. data/lib/puppet/util/metric.rb +3 -5
  42. data/lib/puppet/util/plugins.rb +82 -0
  43. data/spec/integration/configurer_spec.rb +38 -5
  44. data/spec/integration/transaction_spec.rb +43 -42
  45. data/spec/lib/puppet_spec/verbose.rb +9 -0
  46. data/spec/shared_behaviours/path_parameters.rb +185 -0
  47. data/spec/spec_helper.rb +6 -0
  48. data/spec/unit/application/agent_spec.rb +7 -0
  49. data/spec/unit/application/apply_spec.rb +6 -0
  50. data/spec/unit/application/master_spec.rb +2 -2
  51. data/spec/unit/configurer_spec.rb +48 -0
  52. data/spec/unit/file_serving/fileset_spec.rb +8 -0
  53. data/spec/unit/indirector/certificate_status/#file_spec.rb# +188 -0
  54. data/spec/unit/indirector/exec_spec.rb +2 -3
  55. data/spec/unit/indirector/facts/inventory_active_record_spec.rb +5 -1
  56. data/spec/unit/indirector/report/yaml_spec.rb +38 -0
  57. data/spec/unit/node/environment_spec.rb +15 -14
  58. data/spec/unit/parameter/path_spec.rb +24 -0
  59. data/spec/unit/parser/compiler_spec.rb +1 -2
  60. data/spec/unit/parser/lexer_spec.rb +12 -0
  61. data/spec/unit/provider/exec/posix_spec.rb +120 -0
  62. data/spec/unit/provider/exec/shell_spec.rb +50 -0
  63. data/spec/unit/provider/group/groupadd_spec.rb +11 -1
  64. data/spec/unit/provider/package/gem_spec.rb +11 -1
  65. data/spec/unit/provider/package/macports_spec.rb +122 -0
  66. data/spec/unit/provider/service/debian_spec.rb +14 -2
  67. data/spec/unit/resource/#type_collection_spec.rb# +463 -0
  68. data/spec/unit/resource/type_collection_spec.rb +21 -17
  69. data/spec/unit/transaction/report_spec.rb +13 -2
  70. data/spec/unit/type/cron_spec.rb +466 -18
  71. data/spec/unit/type/exec_spec.rb +633 -106
  72. data/spec/unit/type/file/source_spec.rb +1 -0
  73. data/spec/unit/type/group_spec.rb +8 -1
  74. data/spec/unit/type_spec.rb +1 -1
  75. data/spec/unit/util/loadedfile_spec.rb +7 -0
  76. data/spec/unit/util/rdoc/parser_spec.rb +2 -1
  77. data/tasks/rake/git_workflow.rake +3 -1
  78. data/test/ral/type/exec.rb +87 -176
  79. metadata +21 -5
  80. data/lib/puppet/provider/package/darwinport.rb +0 -86
@@ -122,7 +122,18 @@ Puppet::Type.newtype(:file) do
122
122
 
123
123
  newparam(:recurse) do
124
124
  desc "Whether and how deeply to do recursive
125
- management."
125
+ management. Options are:
126
+
127
+ * `inf,true` --- Regular style recursion on both remote and local
128
+ directory structure.
129
+ * `remote` --- Descends recursively into the remote directory
130
+ but not the local directory. Allows copying of
131
+ a few files into a directory containing many
132
+ unmanaged files without scanning all the local files.
133
+ * `false` --- Default of no recursion.
134
+ * `[0-9]+` --- Same as true, but limit recursion. Warning: this syntax
135
+ has been deprecated in favor of the `recurselimit` attribute.
136
+ "
126
137
 
127
138
  newvalues(:true, :false, :inf, :remote, /^[0-9]+$/)
128
139
 
@@ -114,6 +114,7 @@ module Puppet
114
114
  param_name = (metadata_method == :checksum) ? :content : metadata_method
115
115
  next if metadata_method == :owner and !Puppet.features.root?
116
116
  next if metadata_method == :checksum and metadata.ftype == "directory"
117
+ next if metadata_method == :checksum and metadata.ftype == "link" and metadata.links == :manage
117
118
 
118
119
  if resource[param_name].nil? or resource[param_name] == :absent
119
120
  resource[param_name] = metadata.send(metadata_method)
@@ -1,4 +1,3 @@
1
-
2
1
  require 'etc'
3
2
  require 'facter'
4
3
 
@@ -15,6 +14,9 @@ module Puppet
15
14
  feature :manages_members,
16
15
  "For directories where membership is an attribute of groups not users."
17
16
 
17
+ feature :system_groups,
18
+ "The provider allows you to create system groups with lower GIDs."
19
+
18
20
  ensurable do
19
21
  desc "Create or remove the group."
20
22
 
@@ -95,5 +97,13 @@ module Puppet
95
97
 
96
98
  defaultto false
97
99
  end
100
+
101
+ newparam(:system, :boolean => true) do
102
+ desc "Whether the group is a system group with lower GID."
103
+
104
+ newvalues(:true, :false)
105
+
106
+ defaultto false
107
+ end
98
108
  end
99
109
  end
@@ -8,19 +8,24 @@ module Puppet
8
8
 
9
9
  newtype(:service) do
10
10
  @doc = "Manage running services. Service support unfortunately varies
11
- widely by platform -- some platforms have very little if any
11
+ widely by platform --- some platforms have very little if any
12
12
  concept of a running service, and some have a very codified and
13
13
  powerful concept. Puppet's service support will generally be able
14
- to make up for any inherent shortcomings (e.g., if there is no
14
+ to do the right thing regardless (e.g., if there is no
15
15
  'status' command, then Puppet will look in the process table for a
16
16
  command matching the service name), but the more information you
17
- can provide the better behaviour you will get. Or, you can just
18
- use a platform that has very good service support.
17
+ can provide, the better behaviour you will get. In particular, any
18
+ virtual services that don't have a predictable entry in the process table
19
+ (for example, `network` on Red Hat/CentOS systems) will manifest odd
20
+ behavior on restarts if you don't specify `hasstatus` or a `status`
21
+ command.
19
22
 
20
23
  Note that if a `service` receives an event from another resource,
21
24
  the service will get restarted. The actual command to restart the
22
- service depends on the platform. You can provide a special command
23
- for restarting with the `restart` attribute."
25
+ service depends on the platform. You can provide an explicit command
26
+ for restarting with the `restart` attribute, or use the init script's
27
+ restart command with the `hasrestart` attribute; if you do neither,
28
+ the service's stop and start commands will be used."
24
29
 
25
30
  feature :refreshable, "The provider can restart the service.",
26
31
  :methods => [:restart]
@@ -93,11 +98,14 @@ module Puppet
93
98
  that a large number of init scripts on different platforms do
94
99
  not support any kind of status command; thus, you must specify
95
100
  manually whether the service you are running has such a
96
- command (or you can specify a specific command using the
97
- `status` parameter).
98
-
99
- If you do not specify anything, then the service name will be
100
- looked for in the process table."
101
+ command. Alternately, you can provide a specific command using the
102
+ `status` attribute.
103
+
104
+ If you specify neither of these, then Puppet will look for the
105
+ service name in the process table. Be aware that 'virtual' init
106
+ scripts such as networking will respond poorly to refresh events
107
+ (via notify and subscribe relationships) if you don't override
108
+ this default behavior."
101
109
 
102
110
  newvalues(:true, :false)
103
111
  end
@@ -1,9 +1,10 @@
1
+ require "puppet/util/plugins"
2
+
1
3
  module Puppet
2
4
  module Util
3
5
  class CommandLine
4
6
 
5
- LegacyName = Hash.new{|h,k| k}.update(
6
- {
7
+ LegacyName = Hash.new{|h,k| k}.update(
7
8
  'agent' => 'puppetd',
8
9
  'cert' => 'puppetca',
9
10
  'doc' => 'puppetdoc',
@@ -13,9 +14,8 @@ module Puppet
13
14
  'queue' => 'puppetqd',
14
15
  'resource' => 'ralsh',
15
16
  'kick' => 'puppetrun',
16
- 'master' => 'puppetmasterd',
17
-
18
- })
17
+ 'master' => 'puppetmasterd'
18
+ )
19
19
 
20
20
  def initialize( zero = $0, argv = ARGV, stdin = STDIN )
21
21
  @zero = zero
@@ -23,6 +23,7 @@ module Puppet
23
23
  @stdin = stdin
24
24
 
25
25
  @subcommand_name, @args = subcommand_and_args( @zero, @argv, @stdin )
26
+ Puppet::Plugins.on_commandline_initialization(:command_line_object => self)
26
27
  end
27
28
 
28
29
  attr :subcommand_name
@@ -56,21 +57,23 @@ module Puppet
56
57
  puts usage_message
57
58
  elsif available_subcommands.include?(subcommand_name) #subcommand
58
59
  require_application subcommand_name
59
- Puppet::Application.find(subcommand_name).new(self).run
60
+ app = Puppet::Application.find(subcommand_name).new(self)
61
+ Puppet::Plugins.on_application_initialization(:appliation_object => self)
62
+ app.run
60
63
  else
61
64
  abort "Error: Unknown command #{subcommand_name}.\n#{usage_message}" unless execute_external_subcommand
62
65
  end
63
66
  end
64
67
 
65
68
  def execute_external_subcommand
66
- external_command = "puppet-#{subcommand_name}"
69
+ external_command = "puppet-#{subcommand_name}"
67
70
 
68
- require 'puppet/util'
69
- path_to_subcommand = Puppet::Util.which( external_command )
70
- return false unless path_to_subcommand
71
+ require 'puppet/util'
72
+ path_to_subcommand = Puppet::Util.which( external_command )
73
+ return false unless path_to_subcommand
71
74
 
72
- system( path_to_subcommand, *args )
73
- true
75
+ system( path_to_subcommand, *args )
76
+ true
74
77
  end
75
78
 
76
79
  def legacy_executable_name
@@ -107,7 +107,6 @@
107
107
  # option requires LDAP support at this point.
108
108
  #
109
109
  # ping::
110
- #
111
110
  # Do a ICMP echo against the target host. Skip hosts that don't respond to ping.
112
111
  #
113
112
  # = Example
@@ -34,10 +34,6 @@ module Puppet
34
34
  # Create the file. Must be passed the file path.
35
35
  def initialize(file)
36
36
  @file = file
37
- unless FileTest.exists?(@file)
38
- raise Puppet::NoSuchFile,
39
- "Can not use a non-existent file for parsing"
40
- end
41
37
  @statted = 0
42
38
  @stamp = nil
43
39
  @tstamp = stamp
@@ -50,7 +46,7 @@ module Puppet
50
46
  @statted = Time.now.to_i
51
47
  begin
52
48
  @stamp = File.stat(@file).ctime
53
- rescue Errno::ENOENT
49
+ rescue Errno::ENOENT, Errno::ENOTDIR
54
50
  @stamp = Time.now
55
51
  end
56
52
  end
@@ -122,7 +122,7 @@ class Puppet::Util::Metric
122
122
  def initialize(name,label = nil)
123
123
  @name = name.to_s
124
124
 
125
- @label = label || labelize(name)
125
+ @label = label || self.class.labelize(name)
126
126
 
127
127
  @values = []
128
128
  end
@@ -133,7 +133,7 @@ class Puppet::Util::Metric
133
133
 
134
134
  def newvalue(name,value,label = nil)
135
135
  raise ArgumentError.new("metric name #{name.inspect} is not a string") unless name.is_a? String
136
- label ||= labelize(name)
136
+ label ||= self.class.labelize(name)
137
137
  @values.push [name,label,value]
138
138
  end
139
139
 
@@ -174,10 +174,8 @@ class Puppet::Util::Metric
174
174
  @values.sort { |a, b| a[1] <=> b[1] }
175
175
  end
176
176
 
177
- private
178
-
179
177
  # Convert a name into a label.
180
- def labelize(name)
178
+ def self.labelize(name)
181
179
  name.to_s.capitalize.gsub("_", " ")
182
180
  end
183
181
  end
@@ -0,0 +1,82 @@
1
+ #
2
+ # This system manages an extensible set of metadata about plugins which it
3
+ # collects by searching for files named "plugin_init.rb" in a series of
4
+ # directories. Initially, these are simply the $LOAD_PATH.
5
+ #
6
+ # The contents of each file found is executed in the context of a Puppet::Plugins
7
+ # object (and thus scoped). An example file might contain:
8
+ #
9
+ # -------------------------------------------------------
10
+ # @name = "Greet the CA"
11
+ #
12
+ # @description = %q{
13
+ # This plugin causes a friendly greeting to print out on a master
14
+ # that is operating as the CA, after it has been set up but before
15
+ # it does anything.
16
+ # }
17
+ #
18
+ # def after_application_setup(options)
19
+ # if options[:application_object].is_a?(Puppet::Application::Master) && Puppet::SSL::CertificateAuthority.ca?
20
+ # puts "Hey, this is the CA!"
21
+ # end
22
+ # end
23
+ # -------------------------------------------------------
24
+ #
25
+ # Note that the instance variables are local to this Puppet::Plugin (and so may be used
26
+ # for maintaining state, etc.) but the plugin system does not provide any thread safety
27
+ # assurances, so they may not be adequate for some complex use cases.
28
+ #
29
+ #
30
+ module Puppet
31
+ class Plugins
32
+ Paths = [] # Where we might find plugin initialization code
33
+ Loaded = [] # Code we have found (one-to-one with paths once searched)
34
+ #
35
+ # Return all the Puppet::Plugins we know about, searching any new paths
36
+ #
37
+ def self.known
38
+ Paths[Loaded.length...Paths.length].each { |path|
39
+ file = File.join(path,'plugin_init.rb')
40
+ Loaded << (File.exist?(file) && new(file))
41
+ }
42
+ Loaded.compact
43
+ end
44
+ #
45
+ # Add more places to look for plugins without adding duplicates or changing the
46
+ # order of ones we've already found.
47
+ #
48
+ def self.look_in(*paths)
49
+ Paths.replace Paths | paths.flatten.collect { |path| File.expand_path(path) }
50
+ end
51
+ #
52
+ # Initially just look in $LOAD_PATH
53
+ #
54
+ look_in $LOAD_PATH
55
+ #
56
+ # Calling methods (hooks) on the class calls the method of the same name on
57
+ # all plugins that use that hook, passing in the same arguments to each
58
+ # and returning an array containing the results returned by each plugin as
59
+ # an array of [plugin_name,result] pairs.
60
+ #
61
+ def self.method_missing(hook,*args,&block)
62
+ known.
63
+ select { |p| p.respond_to? hook }.
64
+ collect { |p| [p.name,p.send(hook,*args,&block)] }
65
+ end
66
+ #
67
+ #
68
+ #
69
+ attr_reader :path,:name
70
+ def initialize(path)
71
+ @name = @path = path
72
+ class << self
73
+ private
74
+ def define_hooks
75
+ eval File.read(path),nil,path,1
76
+ end
77
+ end
78
+ define_hooks
79
+ end
80
+ end
81
+ end
82
+
@@ -5,6 +5,8 @@ require File.dirname(__FILE__) + '/../spec_helper'
5
5
  require 'puppet/configurer'
6
6
 
7
7
  describe Puppet::Configurer do
8
+ include PuppetSpec::Files
9
+
8
10
  describe "when downloading plugins" do
9
11
  it "should use the :pluginsignore setting, split on whitespace, for ignoring remote files" do
10
12
  resource = Puppet::Type.type(:notify).new :name => "yay"
@@ -17,19 +19,50 @@ describe Puppet::Configurer do
17
19
  end
18
20
 
19
21
  describe "when running" do
20
- it "should send a transaction report with valid data" do
21
- catalog = Puppet::Resource::Catalog.new
22
- catalog.add_resource(Puppet::Type.type(:notify).new(:title => "testing"))
22
+ before(:each) do
23
+ @catalog = Puppet::Resource::Catalog.new
24
+ @catalog.add_resource(Puppet::Type.type(:notify).new(:title => "testing"))
23
25
 
24
- configurer = Puppet::Configurer.new
26
+ # Make sure we don't try to persist the local state after the transaction ran,
27
+ # because it will fail during test (the state file is in an not existing directory)
28
+ # and we need the transaction to be successful to be able to produce a summary report
29
+ @catalog.host_config = false
30
+
31
+ @configurer = Puppet::Configurer.new
32
+ end
33
+
34
+ it "should send a transaction report with valid data" do
25
35
 
36
+ @configurer.stubs(:save_last_run_summary)
26
37
  Puppet::Transaction::Report.indirection.expects(:save).with do |x, report|
27
38
  report.time.class == Time and report.logs.length > 0
28
39
  end
29
40
 
30
41
  Puppet[:report] = true
31
42
 
32
- configurer.run :catalog => catalog
43
+ @configurer.run :catalog => @catalog
44
+ end
45
+
46
+ it "should save a correct last run summary" do
47
+ report = Puppet::Transaction::Report.new("apply")
48
+ report.stubs(:save)
49
+
50
+ Puppet[:lastrunfile] = tmpfile("lastrunfile")
51
+ Puppet[:report] = true
52
+
53
+ @configurer.run :catalog => @catalog, :report => report
54
+
55
+ summary = nil
56
+ File.open(Puppet[:lastrunfile], "r") do |fd|
57
+ summary = YAML.load(fd.read)
58
+ end
59
+
60
+ summary.should be_a(Hash)
61
+ %w{time changes events resources}.each do |key|
62
+ summary.should be_key(key)
63
+ end
64
+ summary["time"].should be_key("notify")
65
+ summary["time"]["last_run"].should >= Time.now.tv_sec
33
66
  end
34
67
  end
35
68
  end
@@ -107,29 +107,23 @@ describe Puppet::Transaction do
107
107
  file1 = tmpfile("file1")
108
108
  file2 = tmpfile("file2")
109
109
 
110
- file = Puppet::Type.type(:file).new(
111
-
112
- :path => path,
113
-
110
+ file = Puppet::Type.type(:file).new(
111
+ :path => path,
114
112
  :ensure => "file"
115
113
  )
116
114
 
117
- exec1 = Puppet::Type.type(:exec).new(
118
-
119
- :path => ENV["PATH"],
115
+ exec1 = Puppet::Type.type(:exec).new(
116
+ :path => ENV["PATH"],
120
117
  :command => "touch #{file1}",
121
118
  :refreshonly => true,
122
-
123
- :subscribe => Puppet::Resource.new(:file, path)
119
+ :subscribe => Puppet::Resource.new(:file, path)
124
120
  )
125
121
 
126
- exec2 = Puppet::Type.type(:exec).new(
127
-
128
- :path => ENV["PATH"],
129
- :command => "touch #{file2}",
122
+ exec2 = Puppet::Type.type(:exec).new(
123
+ :path => ENV["PATH"],
124
+ :command => "touch #{file2}",
130
125
  :refreshonly => true,
131
-
132
- :subscribe => Puppet::Resource.new(:file, path)
126
+ :subscribe => Puppet::Resource.new(:file, path)
133
127
  )
134
128
 
135
129
  catalog = mk_catalog(file, exec1, exec2)
@@ -141,33 +135,26 @@ describe Puppet::Transaction do
141
135
  it "should not let one failed refresh result in other refreshes failing" do
142
136
  path = tmpfile("path")
143
137
  newfile = tmpfile("file")
144
-
145
- file = Puppet::Type.type(:file).new(
146
-
138
+ file = Puppet::Type.type(:file).new(
147
139
  :path => path,
148
-
149
140
  :ensure => "file"
150
141
  )
151
142
 
152
- exec1 = Puppet::Type.type(:exec).new(
153
-
143
+ exec1 = Puppet::Type.type(:exec).new(
154
144
  :path => ENV["PATH"],
155
145
  :command => "touch /this/cannot/possibly/exist",
156
146
  :logoutput => true,
157
147
  :refreshonly => true,
158
148
  :subscribe => file,
159
-
160
149
  :title => "one"
161
150
  )
162
151
 
163
- exec2 = Puppet::Type.type(:exec).new(
164
-
152
+ exec2 = Puppet::Type.type(:exec).new(
165
153
  :path => ENV["PATH"],
166
154
  :command => "touch #{newfile}",
167
155
  :logoutput => true,
168
156
  :refreshonly => true,
169
157
  :subscribe => [file, exec1],
170
-
171
158
  :title => "two"
172
159
  )
173
160
 
@@ -184,22 +171,18 @@ describe Puppet::Transaction do
184
171
 
185
172
  Puppet[:ignoreschedules] = false
186
173
 
187
- file = Puppet::Type.type(:file).new(
188
-
174
+ file = Puppet::Type.type(:file).new(
189
175
  :name => tmpfile("file"),
190
-
191
176
  :ensure => "file",
192
177
  :backup => false
193
178
  )
194
179
 
195
180
  fname = tmpfile("exec")
196
181
 
197
- exec = Puppet::Type.type(:exec).new(
198
-
182
+ exec = Puppet::Type.type(:exec).new(
199
183
  :name => "touch #{fname}",
200
184
  :path => "/usr/bin:/bin",
201
185
  :schedule => "monthly",
202
-
203
186
  :subscribe => Puppet::Resource.new("file", file.name)
204
187
  )
205
188
 
@@ -236,29 +219,21 @@ describe Puppet::Transaction do
236
219
 
237
220
  it "should not attempt to evaluate resources with failed dependencies" do
238
221
 
239
- exec = Puppet::Type.type(:exec).new(
240
-
222
+ exec = Puppet::Type.type(:exec).new(
241
223
  :command => "/bin/mkdir /this/path/cannot/possibly/exit",
242
-
243
224
  :title => "mkdir"
244
225
  )
245
226
 
246
-
247
- file1 = Puppet::Type.type(:file).new(
248
-
227
+ file1 = Puppet::Type.type(:file).new(
249
228
  :title => "file1",
250
229
  :path => tmpfile("file1"),
251
-
252
230
  :require => exec,
253
231
  :ensure => :file
254
232
  )
255
233
 
256
-
257
- file2 = Puppet::Type.type(:file).new(
258
-
234
+ file2 = Puppet::Type.type(:file).new(
259
235
  :title => "file2",
260
236
  :path => tmpfile("file2"),
261
-
262
237
  :require => file1,
263
238
  :ensure => :file
264
239
  )
@@ -270,6 +245,32 @@ describe Puppet::Transaction do
270
245
  FileTest.should_not be_exists(file2[:path])
271
246
  end
272
247
 
248
+ it "should not trigger subscribing resources on failure" do
249
+ file1 = tmpfile("file1")
250
+ file2 = tmpfile("file2")
251
+
252
+ create_file1 = Puppet::Type.type(:exec).new(
253
+ :command => "/usr/bin/touch #{file1}"
254
+ )
255
+
256
+ exec = Puppet::Type.type(:exec).new(
257
+ :command => "/bin/mkdir /this/path/cannot/possibly/exit",
258
+ :title => "mkdir",
259
+ :notify => create_file1
260
+ )
261
+
262
+ create_file2 = Puppet::Type.type(:exec).new(
263
+ :command => "/usr/bin/touch #{file2}",
264
+ :subscribe => exec
265
+ )
266
+
267
+ catalog = mk_catalog(exec, create_file1, create_file2)
268
+ catalog.apply
269
+
270
+ FileTest.should_not be_exists(file1)
271
+ FileTest.should_not be_exists(file2)
272
+ end
273
+
273
274
  # #801 -- resources only checked in noop should be rescheduled immediately.
274
275
  it "should immediately reschedule noop resources" do
275
276
  Puppet::Type.type(:schedule).mkdefaultschedules