summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/blink.rb19
-rw-r--r--lib/blink/event.rb101
-rw-r--r--lib/blink/statechange.rb36
-rw-r--r--lib/blink/transaction.rb15
-rw-r--r--lib/blink/transportable.rb4
-rw-r--r--lib/blink/type.rb52
-rw-r--r--lib/blink/type/file.rb8
-rw-r--r--lib/blink/type/package.rb2
-rw-r--r--lib/blink/type/service.rb12
-rw-r--r--lib/blink/type/state.rb8
-rw-r--r--test/client/tc_client.rb2
-rw-r--r--test/types/tc_file.rb1
-rw-r--r--test/types/tc_service.rb7
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