summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/blink/client.rb53
-rw-r--r--lib/blink/transaction.rb50
-rw-r--r--lib/blink/transportable.rb163
-rw-r--r--lib/blink/type.rb37
-rw-r--r--lib/blink/type/component.rb5
-rw-r--r--lib/blink/type/state.rb10
-rw-r--r--lib/blink/type/typegen/filerecord.rb1
-rw-r--r--lib/blink/type/typegen/filetype.rb3
-rw-r--r--test/types/tc_basic.rb35
9 files changed, 232 insertions, 125 deletions
diff --git a/lib/blink/client.rb b/lib/blink/client.rb
index 014e2a33f..0c4c59bcf 100644
--- a/lib/blink/client.rb
+++ b/lib/blink/client.rb
@@ -8,6 +8,7 @@ require 'blink'
require 'blink/function'
require 'blink/type'
require 'blink/transaction'
+require 'blink/transportable'
module Blink
class ClientError < RuntimeError; end
@@ -27,45 +28,19 @@ module Blink
end
end
- def objects=(list)
- objects = []
- list.collect { |object|
- # create a Blink object from the list...
- #puts "yayness"
- if type = Blink::Type.type(object.type)
- namevar = type.namevar
- if namevar != :name
- object[namevar] = object[:name]
- object.delete(:name)
- end
- begin
- # this will fail if the type already exists
- # which may or may not be a good thing...
- typeobj = type.new(object)
- objects.push typeobj
- rescue => detail
- puts "Failed to create object: %s" % detail
- #puts object.class
- #puts object.inspect
- exit
- end
- else
- raise "Could not find object type %s" % object.type
- end
- }
-
- # okay, we have a list of all of the objects we're supposed
- # to execute
- # here's where we collect the rollbacks and record them
- # that means that we need, at the least:
- # - a standard mechanism for specifying that an object is no-op
- # - a standard object that is considered a rollback object
- #objects.each { |obj|
- # obj.evaluate
- #}
-
- transaction = Blink::Transaction.new(objects)
- transaction.run
+ # this method is how the client receives the tree of Transportable
+ # objects
+ # for now, just descend into the tree and perform and necessary
+ # manipulations
+ def objects=(tree)
+ container = tree.to_type
+
+ # for now we just evaluate the top-level container, but eventually
+ # there will be schedules and such associated with each object,
+ # and probably with the container itself
+ transaction = container.evaluate
+ #transaction = Blink::Transaction.new(objects)
+ transaction.evaluate
end
end
end
diff --git a/lib/blink/transaction.rb b/lib/blink/transaction.rb
index 7c0a86407..9535cd712 100644
--- a/lib/blink/transaction.rb
+++ b/lib/blink/transaction.rb
@@ -14,46 +14,50 @@ require 'blink/statechange'
#---------------------------------------------------------------
class Blink::Transaction
- attr_accessor :collect # do we collect the changes and perform them
- # all at once?
-
#---------------------------------------------------------------
# for now, just store the changes for executing linearly
# later, we might execute them as we receive them
def change(change)
- @changes.push change
+ @children.push change
end
#---------------------------------------------------------------
#---------------------------------------------------------------
def evaluate
- Blink.notice "evaluating %s changes" % @changes.length
- @changes.each { |change|
- msg = change.forward
+ Blink.notice "executing %s changes" % @children.length
+
+ @children.each { |change|
+ if change.is_a?(Blink::StateChange)
+ begin
+ change.forward
+ rescue => detail
+ Blink.error("%s failed: %s" % [change,detail])
+ # at this point, we would normally roll back the transaction
+ # but, um, i don't know how to do that yet
+ end
+ elsif change.is_a?(Blink::Transaction)
+ change.evaluate
+ else
+ raise "Transactions cannot handle objects of type %s" % child.class
+ end
}
end
#---------------------------------------------------------------
#---------------------------------------------------------------
+ # this should only be called by a Blink::Container object now
+ # and it should only receive an array
def initialize(tree)
@tree = tree
- @collect = true
- @changes = []
- end
- #---------------------------------------------------------------
- #---------------------------------------------------------------
- def run
- Blink.notice "running transaction"
- if @tree.is_a?(Array)
- @tree.each { |item|
- item.evaluate(self)
- }
- else
- @tree.evaluate(self)
- end
- Blink.notice "finished transaction"
- self.evaluate
+ # change collection is in-band, and message generation is out-of-band
+ # of course, exception raising is also out-of-band
+ @children = @tree.collect { |child|
+ # not all of the children will return a change
+ child.evaluate
+ }.flatten.reject { |child|
+ child.nil? # remove empties
+ }
end
#---------------------------------------------------------------
end
diff --git a/lib/blink/transportable.rb b/lib/blink/transportable.rb
index 101d8d29f..8a9cdf397 100644
--- a/lib/blink/transportable.rb
+++ b/lib/blink/transportable.rb
@@ -8,58 +8,145 @@
# information and out of which we'll receive our host-specific configuration
require 'blink'
-require 'blink/parser/parser'
module Blink
- module Parser
- #------------------------------------------------------------
- class TransObject < Hash
- attr_accessor :type
+ #------------------------------------------------------------
+ class TransObject < Hash
+ attr_accessor :type
- @@ohash = {}
- @@oarray = []
+ @@ohash = {}
+ @@oarray = []
- def TransObject.clear
- @@oarray.clear
- end
+ def TransObject.clear
+ @@oarray.clear
+ end
- def TransObject.list
- return @@oarray
- end
+ def TransObject.list
+ return @@oarray
+ end
- def initialize(name,type)
- self[:name] = name
- @type = type
- #if @@ohash.include?(name)
- # raise "%s already exists" % name
- #else
- # @@ohash[name] = self
- # @@oarray.push(self)
- #end
- @@oarray.push self
- end
+ def initialize(name,type)
+ self[:name] = name
+ @type = type
+ #if @@ohash.include?(name)
+ # raise "%s already exists" % name
+ #else
+ # @@ohash[name] = self
+ # @@oarray.push(self)
+ #end
+ @@oarray.push self
+ end
- def name
- return self[:name]
- end
+ def name
+ return self[:name]
+ end
+
+ def to_s
+ return "%s(%s) => %s" % [@type,self[:name],super]
+ end
- def to_s
- return "%s(%s) => %s" % [@type,self[:name],super]
+ def to_type
+ retobj = nil
+ if type = Blink::Type.type(self.type)
+ namevar = type.namevar
+ if namevar != :name
+ object[namevar] = object[:name]
+ object.delete(:name)
+ end
+ begin
+ # this will fail if the type already exists
+ # which may or may not be a good thing...
+ retobj = type.new(object)
+ rescue => detail
+ Blink.error "Failed to create object: %s" % detail
+ #puts object.class
+ #puts object.inspect
+ #exit
+ end
+ else
+ raise "Could not find object type %s" % object.type
end
+
+ return retobj
end
- #------------------------------------------------------------
+ end
+ #------------------------------------------------------------
- #------------------------------------------------------------
- class TransSetting
- attr_accessor :type, :name, :args
+ #------------------------------------------------------------
+ class TransSetting
+ attr_accessor :type, :name, :args, :evalcount
+
+ def initialize
+ @evalcount = 0
end
- #------------------------------------------------------------
- #------------------------------------------------------------
- # just a linear container for objects
- class TransBucket < Array
+ def evaluate(transaction)
+ @evalcount += 0
+ if type = Blink::Type.type(self.type)
+ # call the settings
+ type.send(self.name,self.args)
+ else
+ raise "Could not find object type %s" % setting.type
+ end
end
- #------------------------------------------------------------
end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ # just a linear container for objects
+ class TransBucket < Array
+ def to_type
+ # this container will contain the equivalent of all objects at
+ # this level
+ container = Blink::Container.new
+ nametable = {}
+
+ self.each { |child|
+ # the fact that we descend here means that we are
+ # always going to execute depth-first
+ # which is _probably_ a good thing, but one never knows...
+ if child.is_a?(Blink::TransBucket)
+ # just perform the same operation on any children
+ container.push(child.to_type)
+ elsif child.is_a?(Blink::Setting)
+ # XXX this is wrong, but for now just evaluate the settings
+ child.evaluate
+ elsif child.is_a?(Blink::TransObject)
+ # do a simple little naming hack to see if the object already
+ # exists in our scope
+ # this assumes that type/name combinations are globally
+ # unique
+ name = [child[:name],child[:type]].join("--")
+
+ if namecheck.include?(name)
+ object = namecheck[name]
+ child.each { |var,value|
+ # don't rename; this shouldn't be possible anyway
+ next if var == :name
+
+ # override any existing values
+ object[var] = value
+ }
+ else # the object does not exist yet in our scope
+ # now we have the object instantiated, in our scope
+ object = child.to_type
+ namecheck[name] = object
+
+ # this sets the order of the object
+ container.push object
+ end
+ else
+ raise "Client#mkobjects cannot handle objects of type %s" %
+ child.class
+ end
+ }
+
+ # at this point, no objects at are level are still Transportable
+ # objects
+ return container
+ end
+
+ end
+ #------------------------------------------------------------
end
diff --git a/lib/blink/type.rb b/lib/blink/type.rb
index 7caa6bd0a..1ea652182 100644
--- a/lib/blink/type.rb
+++ b/lib/blink/type.rb
@@ -126,6 +126,16 @@ class Blink::Type < Blink::Element
#---------------------------------------------------------------
#---------------------------------------------------------------
+ def Type.metaclass
+ if defined? @metaclass
+ return @metaclass
+ else
+ return false
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
# this is used for mapping object types (e.g., Blink::Type::File)
# to names (e.g., "file")
def Type.name
@@ -340,7 +350,7 @@ class Blink::Type < Blink::Element
#---------------------------------------------------------------
#---------------------------------------------------------------
- # instance methods related to instance intrinics
+ # instance methods related to instance intrinsics
# e.g., initialize() and name()
#---------------------------------------------------------------
#---------------------------------------------------------------
@@ -348,6 +358,7 @@ class Blink::Type < Blink::Element
#---------------------------------------------------------------
def initialize(hash)
@children = []
+ @evalcount = 0
@parent = nil
@noop = false
@@ -481,9 +492,18 @@ class Blink::Type < Blink::Element
# this method is responsible for collecting state changes
# we always descend into the children before we evaluate our current
# states
- def evaluate(transaction)
- self.each { |child|
- child.evaluate(transaction)
+ # this returns any changes resulting from testing, thus 'collect'
+ # rather than 'each'
+ def evaluate
+ # if we're a metaclass and we've already evaluated once...
+ if self.metaclass and @evalcount > 0
+ return
+ end
+ @evalcount += 1
+ # these might return messages, but the main action is through
+ # setting changes in the transactions
+ self.collect { |child|
+ child.evaluate
}
end
#---------------------------------------------------------------
@@ -506,6 +526,15 @@ class Blink::Type < Blink::Element
#---------------------------------------------------------------
#---------------------------------------------------------------
+ # do we actually do work, or do we modify the system instead?
+ # instances of a metaclass only get executed once per client process,
+ # while instances of normal classes get run every time
+ def metaclass
+ return self.class.metaclass
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
# this method is responsible for handling changes in dependencies
# for instance, restarting a service if a config file is changed
# in general, we just hand the method up to our parent, but for
diff --git a/lib/blink/type/component.rb b/lib/blink/type/component.rb
index 7d289e45f..43906f749 100644
--- a/lib/blink/type/component.rb
+++ b/lib/blink/type/component.rb
@@ -6,12 +6,11 @@
# this thing contains everything else, including itself
require 'blink'
-require 'blink/element'
-require 'blink/transaction'
require 'blink/type'
+require 'blink/transaction'
module Blink
- class Container < Blink::Element
+ class Component < Blink::Element
@name = :container
def initialize
diff --git a/lib/blink/type/state.rb b/lib/blink/type/state.rb
index 57fcf5683..4518fdd9a 100644
--- a/lib/blink/type/state.rb
+++ b/lib/blink/type/state.rb
@@ -31,16 +31,16 @@ class Blink::State < Blink::Element
#---------------------------------------------------------------
#---------------------------------------------------------------
- # this assumes the controlling process will actually execute the change
- # which will demonstrably not work with states that are part of a larger
- # whole, like FileRecordStates
- def evaluate(transaction)
+ # if we're not in sync, return a statechange capable of putting us
+ # in sync
+ def evaluate
Blink.verbose "evaluating %s" % self
self.retrieve
if self.insync?
Blink.verbose "%s is in sync" % self
+ return nil
else
- transaction.change(Blink::StateChange.new(self))
+ return Blink::StateChange.new(self)
end
end
#---------------------------------------------------------------
diff --git a/lib/blink/type/typegen/filerecord.rb b/lib/blink/type/typegen/filerecord.rb
index e7d145d81..8c7c042b5 100644
--- a/lib/blink/type/typegen/filerecord.rb
+++ b/lib/blink/type/typegen/filerecord.rb
@@ -14,6 +14,7 @@ class Blink::Type::FileRecord < Blink::Type::TypeGenerator
@options = [:name, :splitchar, :fields, :namevar, :filetype, :regex, :joinchar]
@abstract = true
+ @metaclass = true
@name = :filerecord
diff --git a/lib/blink/type/typegen/filetype.rb b/lib/blink/type/typegen/filetype.rb
index 23854ade9..44cb76826 100644
--- a/lib/blink/type/typegen/filetype.rb
+++ b/lib/blink/type/typegen/filetype.rb
@@ -12,9 +12,12 @@ class Blink::Type::FileType < Blink::Type::TypeGenerator
@options = [:name, :linesplit, :escapednewlines]
@abstract = true
+ @metaclass = true
@name = :filetype
+ @modsystem = true
+
#---------------------------------------------------------------
def FileType.newtype(hash)
unless hash.include?(:linesplit)
diff --git a/test/types/tc_basic.rb b/test/types/tc_basic.rb
index fae4168ce..f0030b950 100644
--- a/test/types/tc_basic.rb
+++ b/test/types/tc_basic.rb
@@ -21,12 +21,7 @@ class TestBasic < Test::Unit::TestCase
Blink[:debug] = 1
assert_nothing_raised() {
- unless Blink::Component.has_key?("sleeper")
- Blink::Component.new(
- :name => "sleeper"
- )
- end
- @component = Blink::Component["sleeper"]
+ @component = Blink::Component.new
}
assert_nothing_raised() {
@@ -62,7 +57,7 @@ class TestBasic < Test::Unit::TestCase
end
def test_name_calls
- [@component,@sleeper,@configfile].each { |obj|
+ [@sleeper,@configfile].each { |obj|
assert_nothing_raised(){
obj.name
}
@@ -72,11 +67,6 @@ class TestBasic < Test::Unit::TestCase
def test_name_equality
#puts "Component is %s, id %s" % [@component, @component.object_id]
assert_equal(
- "sleeper",
- @component.name
- )
-
- assert_equal(
File.join($blinkbase,"examples/root/etc/configfile"),
@configfile.name
)
@@ -88,11 +78,30 @@ class TestBasic < Test::Unit::TestCase
end
def test_object_retrieval
- [@component,@sleeper,@configfile].each { |obj|
+ [@sleeper,@configfile].each { |obj|
assert_equal(
obj.class[obj.name].object_id,
obj.object_id
)
}
end
+
+ def test_transaction
+ transaction = nil
+ assert_nothing_raised() {
+ transaction = @component.evaluate
+ }
+ assert_nothing_raised() {
+ transaction.evaluate
+ }
+ assert_nothing_raised() {
+ @sleeper[:running] = 0
+ }
+ assert_nothing_raised() {
+ transaction = @component.evaluate
+ }
+ assert_nothing_raised() {
+ transaction.evaluate
+ }
+ end
end