diff --git a/lib/puppet/type/exec.rb b/lib/puppet/type/exec.rb index 57c99416f9..387432b6c3 100644 --- a/lib/puppet/type/exec.rb +++ b/lib/puppet/type/exec.rb @@ -21,6 +21,10 @@ module Puppet command only when some other resource is changed. (See the notes on refreshing below.) + The `enable_idempotency_check` attribute is used to check if one of the above mentioned + attributes has been set. The parameter defaults to `false` and raises an error if + enabled and no idempotency attributes has been set. + The state managed by an `exec` resource represents whether the specified command _needs to be_ executed during the catalog run. The target state is always that the command does not need to be executed. If the initial state is that the @@ -582,6 +586,39 @@ def check(value) end end + newcheck(:enable_idempotency_check) do + desc <<-'EOT' + Warning: disabling idempotency validation might result in changes + in EVERY Puppet run! + + Idempotency is ensured by setting either one of the following + attributes: + + - `creates` + - `onlyif` + - `unless` + - `refreshonly` + EOT + + defaultto :false + newvalues(:true, :false) + + def check(value) + if value == :true + result = true + param = [:onlyif, :unless, :refreshonly, :creates] + result = false unless (resource.to_hash.keys & param).any? + unless result + raise ArgumentError, _("Missing idempotency. Set at least one of 'onlyif', 'unless', 'refreshonly', 'creates' or disable check by adding 'enable_idempotency_check => false' ") + end + + result + else + true + end + end + end + # Exec names are not isomorphic with the objects. @isomorphic = false diff --git a/spec/unit/type/exec_spec.rb b/spec/unit/type/exec_spec.rb index ebc6f48038..9ef38ef34c 100644 --- a/spec/unit/type/exec_spec.rb +++ b/spec/unit/type/exec_spec.rb @@ -648,6 +648,39 @@ def instance(path) @test = Puppet::Type.type(:exec).new(:name => @executable) end + describe "enable_idempotency_check" do + + [:true, :false].each do |value| + it "should accept '#{value}'" do + @test[:enable_idempotency_check] = value + expect(@test[:enable_idempotency_check]).to eq(value) + end + end + + [1, 0, "1", "0", "yes", "y", "no", "n"].each do |value| + it "should reject '#{value}'" do + expect { @test[:enable_idempotency_check] = value }. + to raise_error(Puppet::Error, + /Invalid value #{value.inspect}\. Valid values are true, false/ + ) + end + end + + context "without an idempotency check" do + it "should raise error" do + type = Puppet::Type.type(:exec).new(:name => @command, :enable_idempotency_check => true) + expect(type[:enable_idempotency_check]).to eq(:true) + end + end + + context "with an idempotency check" do + it "should not fail" do + type = Puppet::Type.type(:exec).new(:name => @command, :enable_idempotency_check => true, :refreshonly => true) + expect(type).to be + end + end + end + describe ":refreshonly" do { :true => false, :false => true }.each do |input, result| it "should return '#{result}' when given '#{input}'" do