isono 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2 @@
1
+ *~
2
+ #*#
@@ -0,0 +1,38 @@
1
+ # -*- coding: utf-8 -*-
2
+ $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
3
+ require 'isono/version'
4
+
5
+
6
+ task :gem do
7
+ require 'rubygems'
8
+ require 'rake/gempackagetask'
9
+
10
+ spec = Gem::Specification.new do |s|
11
+ s.platform = Gem::Platform::RUBY
12
+ s.version = Isono::VERSION
13
+ s.authors = ['axsh Ltd.', 'Masahiro Fujiwara']
14
+ s.email = ['dev@axsh.net', 'm-fujiwara@axsh.net']
15
+ s.homepage = 'http://github.com/axsh/isono'
16
+ s.summary = 'Messaging and agent fabric'
17
+ s.name = 'isono'
18
+ s.require_path = 'lib'
19
+ s.required_ruby_version = '>= 1.8.7'
20
+ s.rubyforge_project = 'isono'
21
+
22
+ s.files = `git ls-files -c`.split("\n")
23
+
24
+ s.bindir='bin'
25
+ s.executables = %w(cli)
26
+
27
+ s.add_dependency "amqp", "0.7.0"
28
+ s.add_dependency "eventmachine", "1.0.0.beta.3"
29
+ s.add_dependency "statemachine", ">= 1.0.0"
30
+ s.add_dependency "log4r"
31
+
32
+ s.add_development_dependency 'bacon'
33
+ s.add_development_dependency 'rake'
34
+ end
35
+
36
+ File.open('isono.gemspec', 'w'){|f| f.write(spec.to_ruby) }
37
+ sh "gem build isono.gemspec"
38
+ end
@@ -2,43 +2,44 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{isono}
5
- s.version = "0.1.0"
5
+ s.version = "0.2.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["axsh Ltd.", "Masahiro Fujiwara"]
9
- s.date = %q{2010-11-18}
9
+ s.date = %q{2011-05-26}
10
10
  s.default_executable = %q{cli}
11
11
  s.email = ["dev@axsh.net", "m-fujiwara@axsh.net"]
12
12
  s.executables = ["cli"]
13
- s.files = ["bin/cli", "lib/ext/shellwords.rb", "lib/isono.rb", "lib/isono/rack/object_method.rb", "lib/isono/rack/proc.rb", "lib/isono/rack/data_store.rb", "lib/isono/rack/map.rb", "lib/isono/rack/job.rb", "lib/isono/rack/builder.rb", "lib/isono/rack/thread_pass.rb", "lib/isono/logger.rb", "lib/isono/rack.rb", "lib/isono/messaging_client.rb", "lib/isono/resource_manifest.rb", "lib/isono/node.rb", "lib/isono/manifest.rb", "lib/isono/event_delegate_context.rb", "lib/isono/amqp_client.rb", "lib/isono/node_modules/job_channel.rb", "lib/isono/node_modules/base.rb", "lib/isono/node_modules/event_logger.rb", "lib/isono/node_modules/event_channel.rb", "lib/isono/node_modules/node_heartbeat.rb", "lib/isono/node_modules/node_collector.rb", "lib/isono/node_modules/data_store.rb", "lib/isono/node_modules/job_collector.rb", "lib/isono/node_modules/rpc_channel.rb", "lib/isono/node_modules/job_worker.rb", "lib/isono/serializer.rb", "lib/isono/models/node_state.rb", "lib/isono/models/resource_instance.rb", "lib/isono/models/event_log.rb", "lib/isono/models/job_state.rb", "lib/isono/daemonize.rb", "lib/isono/util.rb", "lib/isono/thread_pool.rb", "lib/isono/runner/rpc_server.rb", "lib/isono/runner/agent.rb", "lib/isono/event_observable.rb", "isono.gemspec", "LICENSE", "NOTICE"]
13
+ s.files = [".gitignore", "LICENSE", "NOTICE", "Rakefile", "bin/cli", "isono.gemspec", "lib/ext/shellwords.rb", "lib/isono.rb", "lib/isono/amqp_client.rb", "lib/isono/daemonize.rb", "lib/isono/event_delegate_context.rb", "lib/isono/event_observable.rb", "lib/isono/logger.rb", "lib/isono/manifest.rb", "lib/isono/messaging_client.rb", "lib/isono/models/event_log.rb", "lib/isono/models/job_state.rb", "lib/isono/models/node_state.rb", "lib/isono/models/resource_instance.rb", "lib/isono/node.rb", "lib/isono/node_modules/base.rb", "lib/isono/node_modules/data_store.rb", "lib/isono/node_modules/event_channel.rb", "lib/isono/node_modules/event_logger.rb", "lib/isono/node_modules/job_channel.rb", "lib/isono/node_modules/job_collector.rb", "lib/isono/node_modules/job_worker.rb", "lib/isono/node_modules/node_collector.rb", "lib/isono/node_modules/node_heartbeat.rb", "lib/isono/node_modules/rpc_channel.rb", "lib/isono/rack.rb", "lib/isono/rack/builder.rb", "lib/isono/rack/data_store.rb", "lib/isono/rack/job.rb", "lib/isono/rack/map.rb", "lib/isono/rack/object_method.rb", "lib/isono/rack/proc.rb", "lib/isono/rack/thread_pass.rb", "lib/isono/resource_manifest.rb", "lib/isono/runner/base.rb", "lib/isono/runner/cli.rb", "lib/isono/runner/rpc_server.rb", "lib/isono/serializer.rb", "lib/isono/thread_pool.rb", "lib/isono/util.rb", "lib/isono/version.rb", "spec/amqp_client_spec.rb", "spec/event_observable_spec.rb", "spec/file_channel_spec.rb", "spec/job_channel_spec.rb", "spec/logger_spec.rb", "spec/manifest_spec.rb", "spec/node_spec.rb", "spec/resource_loader_spec.rb", "spec/rpc_channel_spec.rb", "spec/spec_helper.rb", "spec/thread_pool_spec.rb", "spec/util_spec.rb", "tasks/load_resource_manifest.rake"]
14
14
  s.homepage = %q{http://github.com/axsh/isono}
15
15
  s.require_paths = ["lib"]
16
16
  s.required_ruby_version = Gem::Requirement.new(">= 1.8.7")
17
- s.rubygems_version = %q{1.3.6}
18
- s.summary = %q{Messageing and agent fabric}
17
+ s.rubyforge_project = %q{isono}
18
+ s.rubygems_version = %q{1.3.7}
19
+ s.summary = %q{Messaging and agent fabric}
19
20
 
20
21
  if s.respond_to? :specification_version then
21
22
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
22
23
  s.specification_version = 3
23
24
 
24
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
25
- s.add_runtime_dependency(%q<amqp>, [">= 0.6.7"])
26
- s.add_runtime_dependency(%q<eventmachine>, [">= 0.12.10"])
25
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
26
+ s.add_runtime_dependency(%q<amqp>, ["= 0.7.0"])
27
+ s.add_runtime_dependency(%q<eventmachine>, ["= 1.0.0.beta.3"])
27
28
  s.add_runtime_dependency(%q<statemachine>, [">= 1.0.0"])
28
29
  s.add_runtime_dependency(%q<log4r>, [">= 0"])
29
30
  s.add_development_dependency(%q<bacon>, [">= 0"])
30
31
  s.add_development_dependency(%q<rake>, [">= 0"])
31
32
  else
32
- s.add_dependency(%q<amqp>, [">= 0.6.7"])
33
- s.add_dependency(%q<eventmachine>, [">= 0.12.10"])
33
+ s.add_dependency(%q<amqp>, ["= 0.7.0"])
34
+ s.add_dependency(%q<eventmachine>, ["= 1.0.0.beta.3"])
34
35
  s.add_dependency(%q<statemachine>, [">= 1.0.0"])
35
36
  s.add_dependency(%q<log4r>, [">= 0"])
36
37
  s.add_dependency(%q<bacon>, [">= 0"])
37
38
  s.add_dependency(%q<rake>, [">= 0"])
38
39
  end
39
40
  else
40
- s.add_dependency(%q<amqp>, [">= 0.6.7"])
41
- s.add_dependency(%q<eventmachine>, [">= 0.12.10"])
41
+ s.add_dependency(%q<amqp>, ["= 0.7.0"])
42
+ s.add_dependency(%q<eventmachine>, ["= 1.0.0.beta.3"])
42
43
  s.add_dependency(%q<statemachine>, [">= 1.0.0"])
43
44
  s.add_dependency(%q<log4r>, [">= 0"])
44
45
  s.add_dependency(%q<bacon>, [">= 0"])
@@ -1,14 +1,14 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
3
  module Isono
4
- VERSION='0.1.0'
5
-
4
+ require 'isono/version'
5
+ require 'isono/logger'
6
+
6
7
  autoload :Node, 'isono/node'
7
8
  autoload :AmqpClient, 'isono/amqp_client'
8
9
  autoload :Daemonize, 'isono/daemonize'
9
10
  autoload :Util, 'isono/util'
10
11
  autoload :ThreadPool, 'isono/thread_pool'
11
- autoload :Logger, 'isono/logger'
12
12
  autoload :Manifest, 'isono/manifest'
13
13
  autoload :Serializer, 'isono/serializer'
14
14
  autoload :EventObservable, 'isono/event_observable'
@@ -28,7 +28,8 @@ module Isono
28
28
  autoload :JobCollector, 'isono/node_modules/job_collector'
29
29
  end
30
30
  module Runner
31
- autoload :Agent, 'isono/runner/agent'
31
+ autoload :Base, 'isono/runner/base'
32
+ autoload :CLI, 'isono/runner/cli'
32
33
  autoload :RpcServer, 'isono/runner/rpc_server'
33
34
  end
34
35
  module Rack
@@ -71,26 +71,35 @@ module Isono
71
71
  :pass=>broker_uri.password ||default[:pass]
72
72
  }
73
73
  opts.merge!(args) if args.is_a?(Hash)
74
-
75
- @amqp_client = ::AMQP.connect(opts)
76
- @amqp_client.instance_eval {
77
- def settings
78
- @settings
79
- end
80
- }
81
- @amqp_client.callback {
82
- on_connect
83
- if blk
84
- blk.arity == 1 ? blk.call(:success) : blk.call
85
- end
86
- }
87
- @amqp_client.errback {
88
- logger.error("Failed to connect to the broker: #{amqp_server_uri}")
89
- blk.call(:error) if blk && blk.arity == 1
90
- }
91
- # Note: Thread.current[:mq] is utilized in amqp gem.
92
- Thread.current[:mq] = ::MQ.new(@amqp_client)
93
74
 
75
+ prepare_connect {
76
+ @amqp_client = ::AMQP.connect(opts)
77
+ @amqp_client.instance_eval {
78
+ def settings
79
+ @settings
80
+ end
81
+ }
82
+ @amqp_client.connection_status { |t|
83
+ case t
84
+ when :connected
85
+ # here is tried also when reconnected
86
+ on_connect
87
+ when :disconnected
88
+ on_disconnected
89
+ end
90
+ }
91
+ # the block argument is called once at the initial connection.
92
+ @amqp_client.callback {
93
+ after_connect
94
+ if blk
95
+ blk.arity == 1 ? blk.call(self) : blk.call
96
+ end
97
+ }
98
+ @amqp_client.errback {
99
+ logger.error("Failed to connect to the broker: #{amqp_server_uri}")
100
+ blk.call(self) if blk && blk.arity == 1
101
+ }
102
+ }
94
103
  self
95
104
  end
96
105
 
@@ -105,24 +114,50 @@ module Isono
105
114
 
106
115
  def on_connect
107
116
  end
108
-
117
+
118
+ def on_disconnected
119
+ end
120
+
109
121
  def on_close
110
122
  end
111
123
 
124
+ def before_connect
125
+ end
126
+
127
+ def after_connect
128
+ end
129
+
130
+ def before_close
131
+ end
132
+
133
+ def after_close
134
+ end
135
+
112
136
  def close(&blk)
113
137
  return unless connected?
114
138
 
115
- @amqp_client.close {
116
- begin
117
- on_close
118
- blk.call if blk
119
- ensure
120
- @amqp_client = nil
121
- Thread.current[:mq] = nil
122
- end
139
+ prepare_close {
140
+ @amqp_client.close {
141
+ begin
142
+ on_close
143
+ after_close
144
+ blk.call if blk
145
+ ensure
146
+ @amqp_client = nil
147
+ Thread.current[:mq] = nil
148
+ end
149
+ }
123
150
  }
124
151
  end
125
152
 
153
+ # Create new AMQP channel object
154
+ #
155
+ # @note Do not have to close by user. Channel close is performed
156
+ # as part of connection close.
157
+ def create_channel
158
+ MQ.new(@amqp_client)
159
+ end
160
+
126
161
  # Publish a message to the designated exchange.
127
162
  #
128
163
  # @param [String] exname The exchange name
@@ -148,22 +183,16 @@ module Isono
148
183
  }
149
184
  end
150
185
 
151
- def define_queue(queue_name, exchange_name, opts={}, &blk)
152
- q = amq.queue(queue_name, opts)
153
- amq.exchanges.has_key? exchange_name
154
- q.bind( exchange_name, opts ).subscribe &blk
186
+ private
187
+ def prepare_connect(&blk)
188
+ before_connect
189
+ blk.call
155
190
  end
156
191
 
157
-
158
- def identity_queue(unique_id)
159
- amq.direct('identity')
160
- begin
161
- define_queue("ident.#{unique_id}", "identity", {:exclusive=>true, :nowait=>false})
162
- rescue MQ::Error => e
163
- logger.error("The node having same ID already exists: #{unique_id}")
164
- raise e
165
- end
192
+ def prepare_close(&blk)
193
+ before_close
194
+ blk.call
166
195
  end
167
-
196
+
168
197
  end
169
198
  end
@@ -6,16 +6,18 @@ module Isono
6
6
  # Injects +logger+ method to the included class.
7
7
  # The output message from the logger methods starts the module name trailing message body.
8
8
  module Logger
9
+ @rootlogger = Log4r::Logger.new('Isono')
10
+
11
+ def self.initialize(l4r_output=Log4r::StdoutOutputter.new('stdout'))
12
+ # Isono top level logger
13
+ formatter = Log4r::PatternFormatter.new(:depth => 9999, # stack trace depth
14
+ :pattern => "%d %c [%l]: %M",
15
+ :date_format => "%Y/%m/%d %H:%M:%S"
16
+ )
17
+ l4r_output.formatter = formatter
18
+ @rootlogger.outputters = l4r_output
19
+ end
9
20
 
10
- # Isono top level logger
11
- rootlog = Log4r::Logger.new('Isono')
12
- formatter = Log4r::PatternFormatter.new(:depth => 9999, # stack trace depth
13
- :pattern => "%d %c [%l]: %M",
14
- :date_format => "%Y/%m/%d %H:%M:%S"
15
- )
16
- rootlog.add(Log4r::StdoutOutputter.new('stdout', :formatter => formatter))
17
-
18
-
19
21
  def self.included(klass)
20
22
  klass.class_eval {
21
23
 
@@ -45,4 +47,12 @@ module Isono
45
47
  end
46
48
 
47
49
  end
50
+
51
+ # Set STDOUT as the default log output.
52
+ # To replace another log device, put the line below at the top of
53
+ # your code:
54
+ # Isono::Logger.initialize(Log4r::SyslogOutputter.new('mysyslog'))
55
+ # To disable any of log output:
56
+ # Isono::Logger.initialize(Log4r::Outputter.new('null'))
57
+ Logger.initialize
48
58
  end
@@ -25,14 +25,15 @@ module Isono
25
25
 
26
26
  # @param [String] app_root Application root folder
27
27
  # @param [block]
28
- def initialize(app_root, &blk)
28
+ def initialize(app_root='.', &blk)
29
29
  @node_modules = []
30
30
  resolve_abs_app_root(app_root)
31
31
  @config = ConfigStruct.new
32
32
  @config.app_root = app_root
33
33
 
34
34
  instance_eval(&blk) if blk
35
- load_config
35
+
36
+ load_config(@config_path) if @config_path
36
37
  end
37
38
 
38
39
  # Regist a node module class to be initialized/terminated.
@@ -65,7 +66,7 @@ module Isono
65
66
  end
66
67
 
67
68
  def node_id
68
- "#{@node_name}-#{@node_instance_id}"
69
+ "#{@node_name}.#{@node_instance_id}"
69
70
  end
70
71
 
71
72
  def config_path(path=nil)
@@ -79,18 +80,16 @@ module Isono
79
80
  end
80
81
  @config
81
82
  end
82
-
83
83
 
84
- private
85
84
  # load config file and merge up with the config tree.
86
85
  # it will not work when the config_path is nil or the file is missed
87
- def load_config
88
- if @config_path && File.exist?(@config_path)
89
- buf = File.read(@config_path)
90
- eval("#{buf}", binding, @config_path)
91
- end
86
+ def load_config(path)
87
+ return unless File.exist?(path)
88
+ buf = File.read(path)
89
+ eval("#{buf}", binding, path)
92
90
  end
93
91
 
92
+ private
94
93
  def resolve_abs_app_root(app_root_path)
95
94
  pt = Pathname.new(app_root_path)
96
95
  if pt.absolute?
@@ -12,6 +12,7 @@ module Isono
12
12
  include EventObservable
13
13
 
14
14
  def self.inherited(klass)
15
+ super
15
16
  klass.class_eval {
16
17
  include Logger
17
18
  }
@@ -66,46 +67,61 @@ module Isono
66
67
  manifest.node_id
67
68
  end
68
69
 
69
- def on_connect
70
+ def before_connect
70
71
  raise "node_id is not set" if node_id.nil?
71
72
 
72
- #amq.prefetch(1)
73
- identity_queue(node_id)
74
- init_modules
75
-
76
- fire_event(:node_ready, {:node_id=> self.node_id})
77
- logger.info("Started : AMQP Server=#{amqp_server_uri.to_s}, ID=#{node_id}, token=#{boot_token}")
78
- end
79
-
80
- def on_close
81
- term_modules
82
- end
73
+ @value_objects = {}
83
74
 
84
- private
85
-
86
- def init_modules
87
75
  manifest.node_modules.each { |modclass, *args|
88
76
  if !@value_objects.has_key?(modclass)
89
77
  @value_objects[modclass] = vo = ValueObject.new(self, modclass)
90
78
 
91
- if modclass.initialize_hook.is_a?(Proc)
92
- vo.instance_eval(&modclass.initialize_hook)
93
- end
94
-
95
- logger.debug("Initialized #{modclass.to_s}")
79
+ node_hook_proc(:before_connect).call(modclass, *args)
96
80
  end
97
81
  }
98
82
  end
99
83
 
100
- def term_modules
101
- manifest.node_modules.reverse.each { |modclass, *args|
84
+ def after_connect
85
+ setup_identity_queue
86
+
87
+ manifest.node_modules.each &node_hook_proc(:after_connect)
88
+ # TODO: remove initialize_hook
89
+ manifest.node_modules.each &node_hook_proc(:initialize)
90
+
91
+ logger.info("Started : AMQP Server=#{amqp_server_uri.to_s}, ID=#{node_id}, token=#{boot_token}")
92
+ end
93
+
94
+ def before_close
95
+ manifest.node_modules.reverse.each &node_hook_proc(:before_close)
96
+ # TODO: remove terminate_hook
97
+ manifest.node_modules.reverse.each &node_hook_proc(:terminate)
98
+ end
99
+
100
+ def after_close
101
+ manifest.node_modules.reverse.each &node_hook_proc(:after_close)
102
+ end
103
+
104
+ private
105
+
106
+ def node_hook_proc(hook)
107
+ proc { |modclass, *args|
108
+ node_hook = modclass.node_hooks[hook]
109
+ next unless node_hook.is_a?(Proc)
102
110
  vo = @value_objects[modclass]
103
- if vo && modclass.terminate_hook.is_a?(Proc)
104
- vo.instance_eval(&modclass.terminate_hook)
111
+ if vo
112
+ vo.instance_eval(&node_hook)
105
113
  end
106
- logger.info("Terminated #{modclass.to_s}")
107
114
  }
108
115
  end
116
+
117
+ def setup_identity_queue
118
+ amq = create_channel
119
+ amq.errback {
120
+ logger.error("The node has same node ID is running already")
121
+ exit(1)
122
+ }
123
+ amq.queue("ident.#{node_id}", {:exclusive=>true})
124
+ end
109
125
 
110
126
  class ValueObject
111
127
  module DelegateMethods