diff options
-rw-r--r-- | lib/blink.rb | 19 | ||||
-rw-r--r-- | lib/blink/event.rb | 101 | ||||
-rw-r--r-- | lib/blink/statechange.rb | 36 | ||||
-rw-r--r-- | lib/blink/transaction.rb | 15 | ||||
-rw-r--r-- | lib/blink/transportable.rb | 4 | ||||
-rw-r--r-- | lib/blink/type.rb | 52 | ||||
-rw-r--r-- | lib/blink/type/file.rb | 8 | ||||
-rw-r--r-- | lib/blink/type/package.rb | 2 | ||||
-rw-r--r-- | lib/blink/type/service.rb | 12 | ||||
-rw-r--r-- | lib/blink/type/state.rb | 8 | ||||
-rw-r--r-- | test/client/tc_client.rb | 2 | ||||
-rw-r--r-- | test/types/tc_file.rb | 1 | ||||
-rw-r--r-- | test/types/tc_service.rb | 7 |
13 files changed, 206 insertions, 61 deletions
diff --git a/lib/blink.rb b/lib/blink.rb index 74c03f2de..274e37550 100644 --- a/lib/blink.rb +++ b/lib/blink.rb @@ -120,25 +120,6 @@ module Blink @@config[param] = value end - # a simple class for creating callbacks - class Event - attr_reader :event, :object - attr_writer :event, :object - - def initialize(args) - @event = args[:event] - @object = args[:object] - - if @event.nil? or @object.nil? - raise "Event.new called incorrectly" - end - end - - def trigger - @object.trigger(@event) - end - end - # a class for storing state # not currently used class Storage diff --git a/lib/blink/event.rb b/lib/blink/event.rb index 1dbc1b21e..1f3f21f72 100644 --- a/lib/blink/event.rb +++ b/lib/blink/event.rb @@ -7,10 +7,93 @@ require 'blink' require 'blink/type' module Blink - # a simple class for creating callbacks + # 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 - attr_reader :event, :object - attr_writer :event, :object + # subscriptions are permanent associations determining how different + # objects react to an event + class Subscription + attr_accessor :source, :event, :target, :method + + def initialize(hash) + @triggered = false + + hash.each { |method,value| + # assign each value appropriately + # this is probably wicked-slow + self.send(method.to_s + "=",value) + } + Blink.warning "New Subscription: '%s' => '%s'" % + [@source,@event] + end + + # the transaction is passed in so that we can notify it if + # something fails + def trigger(transaction) + # we need some mechanism for only triggering a subscription + # once per transaction, but, um, we don't want it to only + # be once per process lifetime + # so, for now, just trigger as many times as we can, rather than + # as few... + unless @triggered + Blink.verbose "'%s' generated '%s'; triggering '%s' on '%s'" % + [@source,@event,@method,@target] + begin + if @target.respond_to?(@method) + @target.send(@method) + else + Blink.verbose "'%s' of type '%s' does not respond to '%s'" % + [@target,@target.class,@method.inspect] + end + rescue => detail + # um, what the heck do i do when an object fails to refresh? + # shouldn't that result in the transaction rolling back? + # XXX yeah, it should + Blink.error "'%s' failed to refresh: '%s'" % + [@target,detail] + raise + #raise "We need to roll '%s' transaction back" % + #transaction + end + #@triggered = true + end + end + end + + attr_accessor :event, :object, :transaction + + @@events = [] + + @@subscriptions = [] + + def Event.process + Blink.warning "Processing events" + @@events.each { |event| + @@subscriptions.find_all { |sub| + #Blink.warning "Sub source: '%s'; event object: '%s'" % + # [sub.source.inspect,event.object.inspect] + sub.source == event.object and + (sub.event == event.event or + sub.event == :ALL_EVENTS) + }.each { |sub| + Blink.notice "Found sub" + sub.trigger(event.transaction) + } + } + + @@events.clear + end + + def Event.subscribe(hash) + if hash[:event] == '*' + hash[:event] = :ALL_EVENTS + end + sub = Subscription.new(hash) + + # add to the correct area + @@subscriptions.push sub + end def initialize(args) unless args.include?(:event) and args.include?(:object) @@ -19,10 +102,14 @@ module Blink @event = args[:event] @object = args[:object] - end + @transaction = args[:transaction] + + Blink.warning "New Event: '%s' => '%s'" % + [@object,@event] - def trigger - @object.trigger(@event) + # initially, just stuff all instances into a central bucket + # to be handled as a batch + @@events.push self end end end @@ -32,7 +119,7 @@ end # here i'm separating out the methods dealing with handling events # currently not in use, so... -class Blink::Type +class Blink::NotUsed #--------------------------------------------------------------- # return action array # these are actions to use for responding to events diff --git a/lib/blink/statechange.rb b/lib/blink/statechange.rb index 0f2987fb9..e50f67c5a 100644 --- a/lib/blink/statechange.rb +++ b/lib/blink/statechange.rb @@ -8,7 +8,7 @@ module Blink class StateChange - attr_accessor :is, :should, :type, :path, :state + attr_accessor :is, :should, :type, :path, :state, :transaction #--------------------------------------------------------------- def initialize(state) @@ -23,15 +23,47 @@ module Blink #--------------------------------------------------------------- def forward Blink.notice "moving change forward" + + unless defined? @transaction + raise "StateChange '%s' tried to be executed outside of transaction" % + self + end if @state.noop Blink.notice "%s is noop" % @state Blink.notice "change noop is %s" % @state.noop else Blink.notice "Calling sync on %s" % @state begin - @state.sync + event = @state.sync + + # default to a simple event type + if event.nil? + event = @state.parent.name.id2name + "_changed" + elsif ! event.is_a?(Symbol) + Blink.notice "State '%s' retuned invalid event '%s'; resetting to default" % + [@state.class,event] + + event = @state.parent.name.id2name + "_changed" + end + + # i should maybe include object type, but the event type + # should basically point to that, right? + return Blink::Event.new( + :event => event, + :object => @state.parent, + :transaction => @transaction, + :message => self.to_s + ) rescue => detail Blink.error "%s failed: %s" % [self.to_s,detail] + # there should be a way to ask the state what type of event + # it would have generated, but... + return Blink::Event.new( + :event => @state.parent.name.id2name + "_failed", + :object => @state.parent, + :transaction => @transaction, + :message => "Failed: " + self.to_s + ) end end end diff --git a/lib/blink/transaction.rb b/lib/blink/transaction.rb index 0da2ae766..fcdca0128 100644 --- a/lib/blink/transaction.rb +++ b/lib/blink/transaction.rb @@ -45,29 +45,32 @@ class Transaction @changes.each { |change| if change.is_a?(Blink::StateChange) + change.transaction = self begin change.forward - @@changed.push change.state.parent + #@@changed.push change.state.parent rescue => detail Blink.error("%s failed: %s" % [change,detail]) # at this point, we would normally do error handling # but i haven't decided what to do for that yet # so just record that a sync failed for a given object - @@failures[change.state.parent] += 1 + #@@failures[change.state.parent] += 1 # this still could get hairy; what if file contents changed, # but a chmod failed? how would i handle that error? dern end elsif change.is_a?(Blink::Transaction) - # nothing...? + # yay, recursion + change.evaluate else raise "Transactions cannot handle objects of type %s" % child.class end } if @toplevel # if we're the top transaction, perform the refreshes - notifies = @@changed.uniq.collect { |object| - object.notify - }.flatten.uniq + Blink::Event.process + #notifies = @@changed.uniq.collect { |object| + # object.notify + #}.flatten.uniq # now we have the entire list of objects to notify else diff --git a/lib/blink/transportable.rb b/lib/blink/transportable.rb index 3df5229cc..0834d6c9b 100644 --- a/lib/blink/transportable.rb +++ b/lib/blink/transportable.rb @@ -37,7 +37,7 @@ module Blink end def longname - return [object.type,object[:name]].join('--') + return [self.type,self[:name]].join('--') end def name @@ -101,6 +101,8 @@ module Blink #------------------------------------------------------------ # just a linear container for objects class TransBucket < Array + attr_accessor :name + def to_type # this container will contain the equivalent of all objects at # this level diff --git a/lib/blink/type.rb b/lib/blink/type.rb index 15f54aa7b..b25a4e6c0 100644 --- a/lib/blink/type.rb +++ b/lib/blink/type.rb @@ -5,6 +5,7 @@ # included so we can test object types require 'blink' require 'blink/element' +require 'blink/event' require 'blink/type/state' @@ -517,14 +518,21 @@ class Blink::Type < Blink::Element # we have to set the name of our object before anything else, # because it might be used in creating the other states - if hash.has_key?(self.class.namevar) - self[self.class.namevar] = hash[self.class.namevar] - #Blink.notice("%s: namevar [%s], hash name [%s], name [%s], name2 [%s]" % - # [self.class,self.class.namevar,hash[self.class.namevar],self.name,self[self.class.namevar]]) - hash.delete(self.class.namevar) + namevar = self.class.namevar + + # if they're not using :name for the namevar but we got :name (probably + # from the parser) + if namevar != :name and hash.include?(:name) + self[namevar] = hash[:name] + hash.delete(:name) + # else if we got the namevar + elsif hash.has_key?(namevar) + self[namevar] = hash[namevar] + hash.delete(namevar) + # else something's screwy else p hash - p self.class.namevar + p namevar raise TypeError.new("A name must be provided to %s at initialization time" % self.class) end @@ -681,17 +689,35 @@ class Blink::Type < Blink::Element #--------------------------------------------------------------- #--------------------------------------------------------------- - # this is a tad weird, because we specify the requirements on the - # "parent" object, but it's the child object that needs to know - # which objects to notify; i.e., object A requires object B, so if - # object B gets modified it needs to notify object A. + # for each object we require, subscribe to all events that it + # generates + # we might reduce the level of subscription eventually, but for now... def metarequire(requires) unless requires.is_a?(Array) requires = [requires] end - requires.each { |object| - Blink.debug("%s requires %s" % [self.name,object.name]) - object.addnotify(self) + requires.each { |rname| + # we just have a name and a type, and we need to convert it + # to an object... + type = nil + object = nil + tname = rname[0] + unless type = Blink::Type.type(tname) + raise "Could not find type %s" % tname + end + name = rname[1] + unless object = type[name] + raise "Could not retrieve object '%s' of type '%s'" % + [name,type] + end + Blink.debug("%s requires %s" % [self.name,object]) + Blink::Event.subscribe( + :source => object, + :event => '*', + :target => self, + :method => :refresh + ) + #object.addnotify(self) } end #--------------------------------------------------------------- diff --git a/lib/blink/type/file.rb b/lib/blink/type/file.rb index 176b2e672..4784fe054 100644 --- a/lib/blink/type/file.rb +++ b/lib/blink/type/file.rb @@ -32,7 +32,7 @@ module Blink rescue => detail raise detail end - #self.parent.newevent(:event => :inode_changed) + return :file_created end end @@ -87,7 +87,7 @@ module Blink [self.parent[:path],self.should,detail] end - #self.parent.newevent(:event => :inode_changed) + return :inode_changed end end @@ -143,7 +143,7 @@ module Blink rescue raise "failed to chmod #{self.parent[:path]}: #{$!}" end - #self.parent.newevent(:event => :inode_changed) + return :inode_changed end end @@ -236,7 +236,7 @@ module Blink raise "failed to chgrp %s to %s: %s" % [self.parent[:path], self.should, $!] end - #self.parent.newevent(:event => :inode_changed) + return :inode_changed end end end diff --git a/lib/blink/type/package.rb b/lib/blink/type/package.rb index c4df3ddd2..23a8abb95 100644 --- a/lib/blink/type/package.rb +++ b/lib/blink/type/package.rb @@ -19,7 +19,7 @@ module Blink # raise "failed to sync #{@params[:file]}: #{$!}" #end - #self.parent.newevent(:event => :inode_changed) + #return :package_installed end end end diff --git a/lib/blink/type/service.rb b/lib/blink/type/service.rb index 0fe71f61a..24228cdab 100644 --- a/lib/blink/type/service.rb +++ b/lib/blink/type/service.rb @@ -67,7 +67,9 @@ module Blink if self.should > 0 if status < 1 Blink.debug "Starting '%s'" % self - unless self.parent.initcmd("start") + if self.parent.initcmd("start") + return :service_started + else raise "Failed to start '%s'" % self.parent.name end else @@ -79,7 +81,9 @@ module Blink end elsif status > 0 Blink.debug "Stopping '%s'" % self - unless self.parent.initcmd("stop") + if self.parent.initcmd("stop") + return :service_stopped + else raise "Failed to stop %s" % self.name end else @@ -172,6 +176,10 @@ module Blink @initscript = Service.search(self.name) end end + + def refresh + self.initcmd("restart") + end end # Blink::Type::Service end # Blink::Type end diff --git a/lib/blink/type/state.rb b/lib/blink/type/state.rb index 209095065..042e3784b 100644 --- a/lib/blink/type/state.rb +++ b/lib/blink/type/state.rb @@ -90,14 +90,14 @@ class Blink::State < Blink::Element #--------------------------------------------------------------- #--------------------------------------------------------------- - def refresh(transaction) - self.retrieve + #def refresh(transaction) + # self.retrieve # we definitely need some way to batch these refreshes, so a # given object doesn't get refreshed multiple times in a single # run - @parent.refresh - end + # @parent.refresh + #end #--------------------------------------------------------------- #--------------------------------------------------------------- diff --git a/test/client/tc_client.rb b/test/client/tc_client.rb index 9a78147fa..4586ea111 100644 --- a/test/client/tc_client.rb +++ b/test/client/tc_client.rb @@ -16,7 +16,7 @@ class TestClient < Test::Unit::TestCase def test_local client = nil assert_nothing_raised() { - client = Blink::Client.new(:Local => true) + client = Blink::Client.new(:Listen => false) } facts = %w{operatingsystem operatingsystemrelease} diff --git a/test/types/tc_file.rb b/test/types/tc_file.rb index e3079a560..9be428eb9 100644 --- a/test/types/tc_file.rb +++ b/test/types/tc_file.rb @@ -58,7 +58,6 @@ class TestFile < Test::Unit::TestCase def test_group [%x{groups}.chomp.split(/ /), Process.groups].flatten.each { |group| - puts "Testing %s" % group assert_nothing_raised() { @file[:group] = group } diff --git a/test/types/tc_service.rb b/test/types/tc_service.rb index d0e18cb25..287f23178 100644 --- a/test/types/tc_service.rb +++ b/test/types/tc_service.rb @@ -50,6 +50,13 @@ class TestService < Test::Unit::TestCase } assert(@sleeper.insync?) + # test refreshing it + assert_nothing_raised() { + @sleeper.refresh + } + + assert(@sleeper.respond_to?(:refresh)) + # now stop it assert_nothing_raised() { @sleeper[:running] = 0 |