Merge branch 'refactor/0.24.x/transaction_changes' of git://github.com/lak/puppet into 0.24.x

This commit is contained in:
James Turnbull 2008-07-05 10:21:36 +10:00
Родитель 083f4ca786 9d69b3fd12
Коммит 81be1c5c3f
16 изменённых файлов: 340 добавлений и 339 удалений

Просмотреть файл

@ -1,28 +0,0 @@
require 'puppet'
require 'puppet/util/methodhelper'
require 'puppet/util/errors'
module Puppet
# events are transient packets of information; they result in one or more (or none)
# subscriptions getting triggered, and then they get cleared
# eventually, these will be passed on to some central event system
class Event
include Puppet
include Puppet::Util::MethodHelper
include Puppet::Util::Errors
attr_accessor :event, :source, :transaction
@@events = []
def initialize(args)
set_options symbolize_options(args)
requiredopts(:event, :source)
end
def to_s
@source.to_s + " -> " + self.event.to_s
end
end
end

Просмотреть файл

@ -139,7 +139,7 @@ class Puppet::Type
end
if ensureparam and ! ensureparam.insync?(currentvalues[ensureparam])
changes << Puppet::PropertyChange.new(ensureparam, currentvalues[ensureparam])
changes << Puppet::Transaction::Change.new(ensureparam, currentvalues[ensureparam])
# Else, if the 'ensure' property is correctly absent, then do
# nothing
elsif ensureparam and currentvalues[ensureparam] == :absent
@ -149,7 +149,7 @@ class Puppet::Type
currentvalues[property] ||= :absent
! property.insync?(currentvalues[property])
}.collect { |property|
Puppet::PropertyChange.new(property, currentvalues[property])
Puppet::Transaction::Change.new(property, currentvalues[property])
}
end

Просмотреть файл

@ -58,7 +58,7 @@ class Puppet::PGraph < Puppet::SimpleGraph
# to, which is the same thing as saying all edges directly below
# This vertex in the graph.
adjacent(source, :direction => :out, :type => :edges).find_all do |edge|
edge.match?(event.event)
edge.match?(event.name)
end
end.compact.flatten
end

Просмотреть файл

@ -2,7 +2,6 @@
# blocks for actually doing work on the system.
require 'puppet'
require 'puppet/propertychange'
require 'puppet/parameter'
module Puppet

Просмотреть файл

@ -1,141 +0,0 @@
# the class responsible for actually doing any work
# enables no-op and logging/rollback
module Puppet
# Handle all of the work around performing an actual change,
# including calling 'sync' on the properties and producing events.
class PropertyChange
attr_accessor :is, :should, :type, :path, :property, :transaction, :changed, :proxy
# The log file generated when this object was changed.
attr_reader :report
# Switch the goals of the property, thus running the change in reverse.
def backward
@property.should = @is
@is = @property.retrieve
unless defined? @transaction
raise Puppet::Error,
"PropertyChange '%s' tried to be executed outside of transaction" %
self
end
unless @property.insync?(@is)
@property.info "Backing %s" % self
return self.go
else
@property.debug "rollback is already in sync: %s vs. %s" %
[@is, @property.should.inspect]
return nil
end
end
def changed?
self.changed
end
# Create our event object.
def event(name)
# default to a simple event type
unless name.is_a?(Symbol)
@property.warning("Property '%s' returned invalid event '%s'; resetting to default" %
[@property.class, name])
event = @property.resource.class.name.id2name + "_changed"
end
Puppet::Event.new(
:event => name,
:transaction => @transaction,
:source => self.source
)
end
def initialize(property, currentvalue)
unless property.is_a?(Puppet::Property)
raise Puppet::DevError, "Got a %s instead of a property" %
property.class
end
@property = property
@path = [property.path,"change"].flatten
@is = currentvalue
@should = property.should
@changed = false
end
# Perform the actual change. This method can go either forward or
# backward, and produces an event.
def go
if skip?
if self.noop
return [event(:noop)]
else
return nil
end
end
# The transaction catches any exceptions here.
events = @property.sync
if events.nil?
return nil
end
if events.is_a?(Array)
if events.empty?
return nil
end
else
events = [events]
end
return events.collect { |name|
@report = @property.log(@property.change_to_s(@is, @should))
event(name)
}
end
def forward
#@property.debug "moving change forward"
unless defined? @transaction
raise Puppet::Error,
"PropertyChange '%s' tried to be executed outside of transaction" %
self
end
return self.go
end
def noop
return @property.noop
end
def skip?
if @property.insync?(@is)
@property.info "Already in sync"
return true
end
if @property.noop
@property.log "is %s, should be %s (noop)" %
[property.is_to_s(@is), property.should_to_s(@should)]
#@property.debug "%s is noop" % @property
return true
end
return false
end
def source
self.proxy || @property.resource
end
def to_s
return "change %s.%s(%s)" %
[@transaction.object_id, self.object_id, @property.change_to_s(@is, @should)]
#return "change %s.%s" % [@transaction.object_id, self.object_id]
end
end
end

Просмотреть файл

@ -2,10 +2,12 @@
# and performs them
require 'puppet'
require 'puppet/propertychange'
module Puppet
class Transaction
require 'puppet/transaction/change'
require 'puppet/transaction/event'
attr_accessor :component, :catalog, :ignoreschedules
attr_accessor :sorted_resources, :configurator
@ -96,7 +98,7 @@ class Transaction
# Create an edge with this resource as both the source and
# target. The triggering method treats these specially for
# logging.
events = resourceevents.collect { |e| e.event }
events = resourceevents.collect { |e| e.name }
set_trigger(Puppet::Relationship.new(resource, resource, :callback => :refresh, :event => events))
end
end
@ -109,7 +111,6 @@ class Transaction
changes.collect { |change|
@changes << change
@count += 1
change.transaction = self
events = nil
begin
# use an array, so that changes can return more than one
@ -278,7 +279,7 @@ class Transaction
# of course, bad.
edge = orig_edge.class.new(orig_edge.source, orig_edge.target)
label = orig_edge.label.dup
label[:event] = events.collect { |e| e.event }
label[:event] = events.collect { |e| e.name }
edge.label = label
set_trigger(edge)
end
@ -680,11 +681,7 @@ class Transaction
[callback, subs.length]
# And then add an event for it.
return [Puppet::Event.new(
:event => :noop,
:transaction => self,
:source => resource
)]
return [Puppet::Transaction::Event.new(:noop, resource)]
end
if subs.length == 1 and subs[0].source == resource
@ -712,11 +709,7 @@ class Transaction
end
# And then add an event for it.
trigged << Puppet::Event.new(
:event => :triggered,
:transaction => self,
:source => resource
)
trigged << Puppet::Transaction::Event.new(:triggered, resource)
triggered(resource, callback)
end

Просмотреть файл

@ -0,0 +1,94 @@
require 'puppet/transaction'
require 'puppet/transaction/event'
# Handle all of the work around performing an actual change,
# including calling 'sync' on the properties and producing events.
class Puppet::Transaction::Change
attr_accessor :is, :should, :path, :property, :changed, :proxy
# Switch the goals of the property, thus running the change in reverse.
def backward
@is, @should = @should, @is
@property.should = @should
@property.info "Reversing %s" % self
return self.go
end
def changed?
self.changed
end
# Create our event object.
def event(name)
# default to a simple event type
unless name.is_a?(Symbol)
@property.warning("Property '%s' returned invalid event '%s'; resetting to default" %
[@property.class, name])
name = @property.event(should)
end
Puppet::Transaction::Event.new(name, self.resource)
end
def initialize(property, currentvalue)
@property = property
@path = [property.path,"change"].flatten
@is = currentvalue
@should = property.should
@changed = false
end
# Perform the actual change. This method can go either forward or
# backward, and produces an event.
def go
if self.noop?
@property.log "is %s, should be %s (noop)" % [property.is_to_s(@is), property.should_to_s(@should)]
return [event(:noop)]
end
# The transaction catches any exceptions here.
events = @property.sync
if events.nil?
return nil
end
if events.is_a?(Array)
if events.empty?
return nil
end
else
events = [events]
end
return events.collect { |name|
@report = @property.log(@property.change_to_s(@is, @should))
event(name)
}
end
def forward
return self.go
end
# Is our property noop? This is used for generating special events.
def noop?
return @property.noop
end
# The resource that generated this change. This is used for handling events,
# and the proxy resource is used for generated resources, since we can't
# send an event to a resource we don't have a direct relationship. If we
# have a proxy resource, then the events will be considered to be from
# that resource, rather than us, so the graph resolution will still work.
def resource
self.proxy || @property.resource
end
def to_s
return "change %s" % @property.change_to_s(@is, @should)
end
end

Просмотреть файл

@ -0,0 +1,21 @@
require 'puppet'
require 'puppet/util/methodhelper'
require 'puppet/util/errors'
# events are transient packets of information; they result in one or more (or none)
# subscriptions getting triggered, and then they get cleared
# eventually, these will be passed on to some central event system
class Puppet::Transaction::Event
include Puppet::Util::MethodHelper
include Puppet::Util::Errors
attr_reader :name, :source
def initialize(name, source)
@name, @source = name, source
end
def to_s
source.to_s + " -> " + name.to_s
end
end

Просмотреть файл

@ -1,6 +1,5 @@
require 'puppet'
require 'puppet/util/log'
require 'puppet/event'
require 'puppet/util/metric'
require 'puppet/property'
require 'puppet/parameter'
@ -415,7 +414,6 @@ class Type
end # Puppet::Type
end
require 'puppet/propertychange'
require 'puppet/provider'
# Always load these types.

Просмотреть файл

@ -1,6 +1,5 @@
# Description of yum repositories
require 'puppet/propertychange'
require 'puppet/util/inifile'
module Puppet

Просмотреть файл

@ -47,8 +47,8 @@ end
describe Puppet::PGraph, " when matching edges" do
before do
@graph = Puppet::PGraph.new
@event = Puppet::Event.new(:source => "a", :event => :yay)
@none = Puppet::Event.new(:source => "a", :event => :NONE)
@event = Puppet::Transaction::Event.new(:yay, "a")
@none = Puppet::Transaction::Event.new(:NONE, "a")
@edges = {}
@edges["a/b"] = Puppet::Relationship.new("a", "b", {:event => :yay, :callback => :refresh})

182
spec/unit/transaction/change.rb Executable file
Просмотреть файл

@ -0,0 +1,182 @@
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../../spec_helper'
require 'puppet/transaction/change'
describe Puppet::Transaction::Change do
Change = Puppet::Transaction::Change
describe "when initializing" do
before do
@property = stub 'property', :path => "/property/path", :should => "shouldval"
end
it "should require the property and current value" do
lambda { Change.new() }.should raise_error
end
it "should set its property to the provided property" do
Change.new(@property, "value").property.should == :property
end
it "should set its 'is' value to the provided value" do
Change.new(@property, "value").is.should == "value"
end
it "should retrieve the 'should' value from the property" do
# Yay rspec :)
Change.new(@property, "value").should.should == @property.should
end
it "should set its path to the path of the property plus 'change'" do
Change.new(@property, "value").path.should == [@property.path, "change"]
end
end
describe "when an instance" do
before do
@property = stub 'property', :path => "/property/path", :should => "shouldval"
@change = Change.new(@property, "value")
end
it "should be noop if the property is noop" do
@property.expects(:noop).returns true
@change.noop?.should be_true
end
it "should set its resource to the proxy if it has one" do
@change.proxy = :myresource
@change.resource.should == :myresource
end
it "should set its resource to the property's resource if no proxy is set" do
@property.expects(:resource).returns :myresource
@change.resource.should == :myresource
end
it "should have a method for marking that it's been execution" do
@change.changed = true
@change.changed?.should be_true
end
describe "and creating an event" do
before do
@property.stubs(:resource).returns "myresource"
end
it "should produce a warning if the event name is not a symbol" do
@property.expects(:warning)
@property.stubs(:event).returns :myevent
@change.event("a string")
end
it "should use the property to generate the event name if the provided name is not a symbol" do
@property.stubs(:warning)
@property.expects(:event).with(@change.should).returns :myevent
Puppet::Transaction::Event.expects(:new).with { |name, source| name == :myevent }
@change.event("a string")
end
end
describe "and executing" do
describe "in noop mode" do
before { @change.stubs(:noop?).returns true }
it "should log that it is in noop" do
@property.expects(:is_to_s)
@property.expects(:should_to_s)
@property.expects(:log)
@change.stubs :event
@change.forward
end
it "should produce a :noop event and return" do
@property.stub_everything
@change.expects(:event).with(:noop).returns :noop_event
@change.forward.should == [:noop_event]
end
end
describe "without noop" do
before do
@change.stubs(:noop?).returns false
@property.stub_everything
@property.stubs(:resource).returns "myresource"
end
it "should sync the property" do
@property.expects(:sync)
@change.forward
end
it "should return nil if syncing the property returns nil" do
@property.stubs(:sync).returns nil
@change.forward.should be_nil
end
it "should return nil if syncing the property returns an empty array" do
@property.stubs(:sync).returns []
@change.forward.should be_nil
end
it "should log the change" do
@property.expects(:sync).returns [:one]
@property.expects(:log)
@property.expects(:change_to_s)
@change.forward
end
it "should return an array of events" do
@property.expects(:sync).returns [:one, :two]
@change.expects(:event).with(:one).returns :uno
@change.expects(:event).with(:two).returns :dos
@change.forward.should == [:uno, :dos]
end
end
describe "backward" do
before do
@property = stub 'property'
@property.stub_everything
@property.stubs(:should).returns "shouldval"
@change = Change.new(@property, "value")
@change.stubs :go
end
it "should swap the 'is' and 'should' values" do
@change.backward
@change.is.should == "shouldval"
@change.should.should == "value"
end
it "should set the 'should' value on the property to the previous 'is' value" do
@property.expects(:should=).with "value"
@change.backward
end
it "should log that it's reversing the change" do
@property.expects(:info)
@change.backward
end
it "should execute" do
@change.expects(:go)
@change.backward
end
end
end
end
end

25
spec/unit/transaction/event.rb Executable file
Просмотреть файл

@ -0,0 +1,25 @@
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../../spec_helper'
require 'puppet/transaction/event'
describe Puppet::Transaction::Event do
Event = Puppet::Transaction::Event
it "should require a name and a source" do
lambda { Event.new }.should raise_error(ArgumentError)
end
it "should have a name getter" do
Event.new(:foo, "bar").name.should == :foo
end
it "should have a source accessor" do
Event.new(:foo, "bar").source.should == "bar"
end
it "should be able to produce a string containing the event name and the source" do
Event.new(:event, :source).to_s.should == "source -> event"
end
end

Просмотреть файл

@ -88,7 +88,7 @@ module PuppetTest::Support::Utils
newevents = nil
assert_nothing_raised("Transaction %s %s failed" % [type, msg]) {
newevents = trans.send(method).reject { |e| e.nil? }.collect { |e|
e.event
e.name
}
}

Просмотреть файл

@ -1,140 +0,0 @@
#!/usr/bin/env ruby
#
# Created by Luke A. Kanies on 2006-12-21.
# Copyright (c) 2006. All rights reserved.
require File.dirname(__FILE__) + '/../lib/puppettest'
require 'puppettest'
class TestPropertyChange < Test::Unit::TestCase
include PuppetTest
class FakeProperty < Puppet::Property
attr_accessor :is, :should, :resource
attr_reader :noop
def change_to_s(currentvalue, newvalue)
"fake change"
end
def insync?(is)
is == @should
end
def log(msg)
Puppet::Util::Log.create(
:level => :info,
:source => self,
:message => msg
)
end
def noop
if defined? @noop
@noop
else
false
end
end
def path
"fakechange"
end
def should_to_s(newvalue)
newvalue.to_s
end
def sync
if insync?(@is)
return nil
else
@is = @should
return :fake_change
end
end
def to_s
path
end
end
def mkchange
property = FakeProperty.new :resource => "fakeparent"
property.is = :start
property.should = :finish
property.resource = :parent
change = nil
assert_nothing_raised do
change = Puppet::PropertyChange.new(property, :start)
end
change.transaction = :trans
assert_equal(:start, change.is, "@is did not get copied")
assert_equal(:finish, change.should, "@should did not get copied")
assert_equal(%w{fakechange change}, change.path, "path did not get set correctly")
assert(! change.changed?, "change defaulted to already changed")
return change
end
def test_go
change = mkchange
coll = logcollector()
events = nil
# First make sure we get an immediate return
assert_nothing_raised do
events = change.go
end
assert_instance_of(Array, events, "events were not returned in an array")
assert_instance_of(Puppet::Event, events[0], "event array did not contain events")
event = events.shift
{:event => :fake_change, :transaction => :trans, :source => :parent}.each do |method, val|
assert_equal(val, event.send(method), "Event did not set %s correctly" % method)
end
# Disabled, because it fails when running the whole suite at once.
#assert(coll.detect { |l| l.message == "fake change" }, "Did not log change")
assert_equal(change.property.is, change.property.should, "did not call sync method")
# Now make sure that proxy sources can be set.
assert_nothing_raised do
change.proxy = :other
end
# Reset, so we change again
change.property.is = :start
change.is = :start
assert_nothing_raised do
events = change.go
end
assert_instance_of(Array, events, "events were not returned in an array")
assert_instance_of(Puppet::Event, events[0], "event array did not contain events")
event = events.shift
{:event => :fake_change, :transaction => :trans, :source => :other}.each do |method, val|
assert_equal(val, event.send(method), "Event did not set %s correctly" % method)
end
#assert(coll.detect { |l| l.message == "fake change" }, "Did not log change")
assert_equal(change.property.is, change.property.should, "did not call sync method")
end
# Related to #542. Make sure changes in noop mode produce the :noop event.
def test_noop_event
change = mkchange
assert(! change.skip?, "Change is already being skipped")
Puppet[:noop] = true
change.property.noop = true
assert(change.noop, "did not set noop")
assert(change.skip?, "setting noop did not mark change for skipping")
event = nil
assert_nothing_raised("Could not generate noop event") do
event = change.forward
end
assert_equal(1, event.length, "got wrong number of events")
assert_equal(:noop, event[0].event, "did not generate noop mode when in noop")
end
end

Просмотреть файл

@ -796,7 +796,7 @@ class TestTransactions < Test::Unit::TestCase
def test_proxy_resources
type = mkreducer do
def evaluate
return Puppet::PropertyChange.new(Fakeprop.new(
return Puppet::Transaction::Change.new(Fakeprop.new(
:path => :path, :is => :is, :should => :should, :name => self.name, :resource => "a parent"), :is)
end
end
@ -815,7 +815,7 @@ class TestTransactions < Test::Unit::TestCase
assert(changes.length > 0, "did not get any changes")
changes.each do |change|
assert_equal(resource, change.source, "change did not get proxy set correctly")
assert_equal(resource, change.resource, "change did not get proxy set correctly")
end
end
@ -905,10 +905,9 @@ class TestTransactions < Test::Unit::TestCase
assert(result, "c did not trigger anything")
assert_instance_of(Array, result)
event = result.shift
assert_instance_of(Puppet::Event, event)
assert_equal(:triggered, event.event, "event was not set correctly")
assert_instance_of(Puppet::Transaction::Event, event)
assert_equal(:triggered, event.name, "event was not set correctly")
assert_equal(c, event.source, "source was not set correctly")
assert_equal(trans, event.transaction, "transaction was not set correctly")
assert(trans.triggered?(c, :refresh),
"Transaction did not store the trigger")
@ -995,7 +994,7 @@ class TestTransactions < Test::Unit::TestCase
newparam(:name) {}
newproperty(:testing) do
def sync
self.is = self.should
# noop
:ran_testing
end
end