smith 0.5.13.1 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/bin/agency +19 -7
  3. data/bin/pry-smith +11 -0
  4. data/bin/smithctl +19 -21
  5. data/lib/smith/acl_compiler.rb +101 -56
  6. data/lib/smith/acl_parser.rb +75 -0
  7. data/lib/smith/agent.rb +28 -43
  8. data/lib/smith/agent_cache.rb +43 -17
  9. data/lib/smith/agent_monitoring.rb +1 -1
  10. data/lib/smith/agent_process.rb +148 -53
  11. data/lib/smith/application/agency.rb +44 -54
  12. data/lib/smith/bootstrap.rb +31 -17
  13. data/lib/smith/cache.rb +4 -0
  14. data/lib/smith/command.rb +1 -3
  15. data/lib/smith/commands/agency/kill.rb +22 -5
  16. data/lib/smith/commands/agency/list.rb +9 -4
  17. data/lib/smith/commands/agency/logger.rb +25 -12
  18. data/lib/smith/commands/agency/object_count.rb +19 -8
  19. data/lib/smith/commands/agency/start.rb +7 -10
  20. data/lib/smith/commands/agency/stop.rb +30 -12
  21. data/lib/smith/commands/common.rb +1 -1
  22. data/lib/smith/commands/smithctl/acl.rb +6 -3
  23. data/lib/smith/commands/smithctl/dump.rb +79 -0
  24. data/lib/smith/commands/smithctl/firehose.rb +2 -1
  25. data/lib/smith/commands/smithctl/push.rb +27 -12
  26. data/lib/smith/commands/smithctl/status.rb +27 -0
  27. data/lib/smith/config.rb +140 -28
  28. data/lib/smith/daemon.rb +16 -3
  29. data/lib/smith/exceptions.rb +6 -3
  30. data/lib/smith/logger.rb +12 -24
  31. data/lib/smith/messaging/acl/agent_keepalive.proto +2 -2
  32. data/lib/smith/messaging/acl/agent_lifecycle.proto +15 -9
  33. data/lib/smith/messaging/acl/agent_stats.proto +6 -5
  34. data/lib/smith/messaging/acl/default.rb +2 -7
  35. data/lib/smith/messaging/acl_type_cache.rb +77 -0
  36. data/lib/smith/messaging/factory.rb +29 -0
  37. data/lib/smith/messaging/queue.rb +12 -10
  38. data/lib/smith/messaging/queue_definition.rb +21 -4
  39. data/lib/smith/messaging/receiver.rb +55 -62
  40. data/lib/smith/messaging/requeue.rb +1 -5
  41. data/lib/smith/messaging/sender.rb +48 -43
  42. data/lib/smith/messaging/util.rb +0 -10
  43. data/lib/smith/queue_definitions.rb +7 -4
  44. data/lib/smith/version.rb +1 -1
  45. data/lib/smith.rb +57 -56
  46. metadata +77 -128
  47. data/lib/smith/messaging/payload.rb +0 -100
@@ -3,43 +3,56 @@ module Smith
3
3
  module Commands
4
4
  class Logger < CommandBase
5
5
  def execute
6
- responder.succeed(_logger)
6
+ responder.succeed((_logger))
7
7
  end
8
8
 
9
9
  def _logger(&blk)
10
+ error_messages = []
10
11
  if options[:level].nil?
11
- "No log level. You must specify a log level and a target"
12
+ error_messages << "No log level. You must specify a log level and a target"
12
13
  else
13
14
  case target.first
14
15
  when 'all'
15
16
  agents.state(:running).each do |agent|
16
- send_agent_control_message(agent, :command => 'log_level', :options => options[:level])
17
+ error_messages << set_log_level(agent.uuid)
17
18
  end
19
+ ''
18
20
  when 'agency'
19
21
  begin
20
22
  logger.info { "Setting agency log level to: #{options[:level]}" }
21
23
  log_level(options[:level])
22
24
  rescue ArgumentError
23
- logger.error { "Incorrect log level: #{options[:level]}" }
25
+ m = "Incorrect log level: #{options[:level]}"
26
+ logger.error { m }
27
+ error_messages << m
24
28
  end
29
+ ''
25
30
  when nil
26
- "No target. You must specify one of the following: 'agency', 'all' or a list of agents"
31
+ error_messages << "No target. You must specify one of the following: 'agency', 'all' or a list of agents"
27
32
  else
28
- target.each do |agent|
29
- if agents[agent].running?
30
- send_agent_control_message(agents[agent], :command => 'log_level', :options => [options[:level]])
31
- end
33
+ target.each do |uuid|
34
+ error_messages << set_log_level(uuid)
32
35
  end
33
36
  end
34
37
  end
35
- ''
38
+ error_messages.compact.join(", ")
36
39
  end
37
40
 
38
41
  private
39
42
 
43
+ def set_log_level(uuid)
44
+ agent = agents[uuid]
45
+ if agent && agent.running?
46
+ send_agent_control_message(agent, :command => 'log_level', :options => [options[:level]])
47
+ nil
48
+ else
49
+ "Agent does not exist: #{uuid}"
50
+ end
51
+ end
52
+
40
53
  def send_agent_control_message(agent, message)
41
- Messaging::Sender.new(agent.control_queue_name, :durable => false, :auto_delete => true, :persistent => true, :strict => true) do |sender|
42
- sender.publish(ACL::Factory.create(:agent_command, message))
54
+ Messaging::Sender.new(agent.control_queue_def) do |sender|
55
+ sender.publish(ACL::AgentCommand.new(message))
43
56
  end
44
57
  end
45
58
 
@@ -1,5 +1,9 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
+ # FIXME. This needs to be either fixed up or removed. It dumps the output into
4
+ # the log at the moment which isn't the place for it. It should also be a
5
+ # smithctl command rather than an agency command.
6
+
3
7
  module Smith
4
8
  module Commands
5
9
  class ObjectCount < CommandBase
@@ -7,18 +11,25 @@ module Smith
7
11
  if target.size > 1
8
12
  responder.succeed("You can only specify one agent at at time.")
9
13
  else
10
- object_count(target.first) do |objects|
11
- responder.succeed(objects)
14
+ agent = agents[target.first]
15
+ if agent.running?
16
+
17
+ # object_count(agent) do |objects|
18
+ # responder.succeed(objects)
19
+ # end
20
+
21
+ object_count(agent)
22
+ responder.succeed('') #(objects)
23
+ else
24
+ responder.succeed("Agent not running: #{target.first}")
12
25
  end
13
26
  end
14
27
  end
15
28
 
16
- def object_count(agent, &blk)
17
- if agents[agent].running?
18
- Messaging::Sender.new(agent.control_queue_name, :durable => false, :auto_delete => true, :persistent => true, :strict => true) do |sender|
19
- sender.on_reply(blk)
20
- sender.publish(ACL::Payload.new(:agent_command, :command => 'object_count', :options => [options[:threshold].to_s]))
21
- end
29
+ def object_count(agent) #, &blk)
30
+ Messaging::Sender.new(agent.control_queue_def) do |sender|
31
+ # sender.on_reply(blk)
32
+ sender.publish(ACL::AgentCommand.new(:command => 'object_count', :options => [options[:threshold].to_s]))
22
33
  end
23
34
  end
24
35
 
@@ -16,7 +16,7 @@ module Smith
16
16
 
17
17
  def start(&blk)
18
18
  #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
19
- #!!!!!!!!!!!! See note about target at end of this file !!!!!!!!!!!!!!
19
+ #!!!!!!!!!!!! See note about target at end of this file !!!!!!!!!!!!!
20
20
  #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
21
21
 
22
22
  # Sort out any groups. If the group option is set it will override
@@ -44,15 +44,13 @@ module Smith
44
44
  blk.call("Start what? No agent specified.")
45
45
  else
46
46
  worker = ->(agent_name, iter) do
47
- agents[agent_name].name = agent_name
48
- if agents[agent_name].path
49
- if options[:kill]
50
- agents[agent_name].kill
51
- end
52
- agents[agent_name].start
53
- iter.return(agent_name)
47
+ running_agents = agents.find_by_name(agent_name)
48
+ if !running_agents.empty? && running_agents.first.singleton
49
+ iter.return("Agent already running: #{agent_name}")
54
50
  else
55
- iter.return("Unknown agent: #{agents[agent_name].name}")
51
+ agent = agents.create(agent_name)
52
+ agent.start
53
+ iter.return((agent.state == 'starting') ? "#{agent_name}: #{agent.uuid}" : '')
56
54
  end
57
55
  end
58
56
 
@@ -67,7 +65,6 @@ module Smith
67
65
  def options_spec
68
66
  banner "Start an agent/agents or group of agents."
69
67
 
70
- opt :kill, "Reset the state of the agent before starting", :short => :k
71
68
  opt :group, "Start everything in the specified group", :type => :string, :short => :g
72
69
  end
73
70
  end
@@ -30,7 +30,7 @@ module Smith
30
30
  Smith.stop
31
31
  else
32
32
  logger.warn { "Agents are still running: #{running_agents.map(&:name).join(", ")}." }
33
- logger.info { "Agency not shutting down. Use force_stop if you really want to shut it down." }
33
+ logger.info { "Agency not shutting down. Use --force if you really want to shut it down." }
34
34
  blk.call("Not shutting down, agents are still running: #{running_agents.map(&:name).join(", ")}.")
35
35
  end
36
36
  end
@@ -53,7 +53,7 @@ module Smith
53
53
  # any other specified agents.
54
54
  if options[:group]
55
55
  begin
56
- agents_to_stop = agent_group(options[:group])
56
+ agents_to_stop = agents.find_by_name(agent_group(options[:group])).map(&:uuid)
57
57
  if agents_to_stop.empty?
58
58
  blk.call("There are no agents in group: #{options[:group]}")
59
59
  end
@@ -61,22 +61,39 @@ module Smith
61
61
  return blk.call(e.message)
62
62
  end
63
63
  else
64
- agents_to_stop = target
64
+ if options[:name]
65
+ agents_to_stop = agents.find_by_name(options[:name]).map(&:uuid)
66
+ else
67
+ agents_to_stop = target
68
+ end
65
69
  end
66
70
 
67
- ret = agents_to_stop.inject([]) do |acc,agent_name|
68
- acc << stop_if_running(agents[agent_name])
71
+ errors = agents_to_stop.inject([]) { |acc,uuid| acc << stop_if_running(uuid) }
72
+ blk.call(format_error_message(errors))
73
+ end
74
+
75
+ def stop_if_running(uuid)
76
+ agent = agents[uuid]
77
+ if agent
78
+ if agent.running?
79
+ agent.stop
80
+ nil
81
+ end
82
+ else
83
+ logger.warn { "Agent does not exist: #{uuid}" }
84
+ uuid
69
85
  end
70
- blk.call((ret.compact.empty?) ? '' : "Agent(s) not running: #{ret.compact.join(", ")}")
71
86
  end
72
87
 
73
- def stop_if_running(agent)
74
- if agent.running?
75
- agent.stop
76
- nil
88
+ def format_error_message(errors)
89
+ errors = errors.compact
90
+ case errors.size
91
+ when 0
92
+ ''
93
+ when 1
94
+ "Agent does not exist: #{errors.first}"
77
95
  else
78
- logger.warn { "Agent not running: #{agent.name}" }
79
- agent.name
96
+ "Agents do not exist: #{errors.join(", ")}"
80
97
  end
81
98
  end
82
99
 
@@ -84,6 +101,7 @@ module Smith
84
101
  banner "Stop an agent/agents."
85
102
 
86
103
  opt :group, "Stop everything in the specified group", :type => :string, :short => :g
104
+ opt :name, "Stop an agent(s) by name", :type => :string, :short => :n
87
105
  opt :force, "If stopping the agency and there are agents running stop anyway"
88
106
  end
89
107
  end
@@ -6,7 +6,7 @@ module Smith
6
6
  agents = Smith.agent_paths.map do |path|
7
7
  group_dir = path.join(group)
8
8
  if group_dir.exist? && group_dir.directory?
9
- agents = Pathname.glob("#{path.join(group)}/*_agent.rb")
9
+ agents = Pathname.glob(group_dir.join("*_agent.rb"))
10
10
  agents.map {|a| Extlib::Inflection.camelize(a.basename(".rb").to_s)}
11
11
  else
12
12
  nil
@@ -10,6 +10,7 @@ module Smith
10
10
  end
11
11
 
12
12
  def _acl
13
+ acl_type_cache = AclTypeCache.instance
13
14
  if options[:show]
14
15
  if target.empty?
15
16
  "You must supply an ACL file name."
@@ -43,9 +44,11 @@ module Smith
43
44
  ""
44
45
  else
45
46
  join_string = (options[:long]) ? "\n" : " "
46
- Pathname.glob(Smith.acl_path.map {|p| "#{p}#{File::SEPARATOR}*"}).map do |p|
47
- p.basename(".proto")
48
- end.sort.join(join_string)
47
+ acl_type_cache.dump_types.keys.map(&:to_s).sort.join(join_string)
48
+
49
+ # Pathname.glob(Smith.acl_path.map {|p| "#{p}#{File::SEPARATOR}*"}).map do |p|
50
+ # p.basename(".proto")
51
+ # end.sort.join(join_string)
49
52
  end
50
53
  end
51
54
 
@@ -0,0 +1,79 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'multi_json'
3
+ require 'smith/messaging/queue'
4
+
5
+ module Smith
6
+ module Commands
7
+ class Dump < CommandBase
8
+ def execute
9
+ MultiJson.use(:oj)
10
+ dump
11
+ end
12
+
13
+ def dump
14
+ case target.size
15
+ when 0
16
+ "No queue specified. Please specify a queue."
17
+ when 1
18
+ queue = target.first
19
+ Messaging::Queue.number_of_messages(queue) do |queue_length|
20
+ Messaging::Receiver.new(queue, :auto_ack => false, :prefetch => 1000, :passive => true) do |receiver|
21
+
22
+ count = 0
23
+ t_start = Time.now.to_f
24
+
25
+ receiver.on_error do |ch,channel_close|
26
+ raise
27
+ case channel_close.reply_code
28
+ when 404
29
+ responder.succeed("Queue does not exist: #{queue}")
30
+ else
31
+ responder.succeed("Unknown error: #{channel_close.reply_text}")
32
+ end
33
+ Smith.stop
34
+ end
35
+
36
+ if queue_length > 0
37
+ EM.add_periodic_timer(1) do
38
+ Messaging::Queue.number_of_messages(queue) do |queue_length|
39
+ if queue_length == 0
40
+ t_end = Time.now.to_f
41
+ if options[:verbose]
42
+ responder.succeed("dumped #{count} messages in #{t_end - t_start} seconds.")
43
+ else
44
+ responder.succeed("")
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ receiver.subscribe do |payload, r|
51
+ if payload
52
+ EM.next_tick do
53
+ STDOUT.puts MultiJson.dump(payload)
54
+ end
55
+ end
56
+ count += 1
57
+ r.ack(true)
58
+ end
59
+ else
60
+ responder.succeed("No messages on queue: #{queue}")
61
+ end
62
+ end
63
+ end
64
+ else
65
+ "You can only specify one queue at a time"
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ def options_spec
72
+ banner "Dump a queue to STDOUT.\n\n This is a very DANGEROUS command in that it removes all messages from a queue."
73
+
74
+ opt :'yes-i-want-to-remove-all-messaeges-from-the-queue', "Remove all messages from the queue and print to stdout", :type => :boolean, :short => :none
75
+ opt :verbose, "print the number of messages backed up.", :type => :boolean, :short => :v
76
+ end
77
+ end
78
+ end
79
+ end
@@ -9,7 +9,8 @@ module Smith
9
9
  channel.topic('amq.rabbitmq.trace', :durable => true) do |exchange|
10
10
  channel.queue('smith.firehose', :durable => true) do |queue|
11
11
  queue.bind(exchange, :routing_key => "publish.#{Smith.config.smith.namespace}.#{queue_name}").subscribe do |m,p|
12
- message = ACL::Payload.decode(p, m.headers['properties']['type'])
12
+ clazz = @acl_type_cache.get_by_hash(m.headers['properties']['type'])
13
+ message = clazz.new.parse_from_string(data)
13
14
  puts (options[:json_given]) ? message.to_json : message.inspect
14
15
  end
15
16
  end
@@ -17,29 +17,35 @@ module Smith
17
17
  if target.size == 0
18
18
  blk.call("No queue specified. Please specify a queue.")
19
19
  else
20
- begin
21
- Messaging::Sender.new(target.first, :auto_delete => options[:dynamic], :persistent => true, :nowait => false, :strict => true) do |sender|
22
- if options[:reply]
20
+ Messaging::Sender.new(target.first, :auto_delete => options[:dynamic], :persistent => true, :nowait => false, :strict => true) do |sender|
21
+ if options[:reply]
22
+ begin
23
23
  timeout = Smith::Messaging::Timeout.new(options[:timeout]) do |message_id|
24
24
  blk.call("Timed out after: #{options[:timeout]} seconds for message: #{options[:message]}: message_id: #{message_id}")
25
25
  end
26
26
 
27
27
  sender.on_reply(:timeout => timeout) { |payload| blk.call(payload.to_hash) }
28
28
  sender.publish(json_to_payload(options[:message], options[:type]))
29
- else
30
- on_work = ->(message, iter) do
29
+ rescue ACL::Error, MultiJson::DecodeError => e
30
+ blk.call(e.message)
31
+ end
32
+ else
33
+ on_work = ->(message, iter) do
34
+ begin
31
35
  sender.publish(json_to_payload(message, options[:type])) do
32
36
  iter.next
33
37
  end
38
+ rescue MultiJson::DecodeError => e
39
+ blk.call("Json error: #{e.message}")
40
+ rescue ACL::Error => e
41
+ blk.call(e.message)
34
42
  end
43
+ end
35
44
 
36
- on_done = -> { blk.call("") }
45
+ on_done = -> { blk.call("") }
37
46
 
38
- iterator.each(on_work, on_done)
39
- end
47
+ iterator.each(on_work, on_done)
40
48
  end
41
- rescue MultiJson::DecodeError => e
42
- blk.call(e)
43
49
  end
44
50
  end
45
51
  end
@@ -62,7 +68,16 @@ module Smith
62
68
  end
63
69
 
64
70
  def json_to_payload(data, type)
65
- ACL::Factory.create(type, MultiJson.load(data, :symbolize_keys => true))
71
+ begin
72
+ ACL::Factory.create(type, MultiJson.load(data, :symbolize_keys => true))
73
+ rescue NoMethodError => e
74
+ m = /undefined method `(.*?)=' for.*/.match(e.message)
75
+ if m
76
+ raise ACL::Error, "Error, invalid field name: #{m[1]}"
77
+ else
78
+ raise ACL::Error
79
+ end
80
+ end
66
81
  end
67
82
 
68
83
  def options_spec
@@ -73,7 +88,7 @@ module Smith
73
88
  opt :file, "read messages from the named file", :type => :string, :short => :f
74
89
  opt :number, "the number of times to send the message", :type => :integer, :default => 1, :short => :n
75
90
  opt :reply, "set a reply listener.", :short => :r
76
- opt :timeout, "timeout when waiting for a reply", :type => :integer, :depends => :reply, :default => Smith.config.agency.timeout
91
+ opt :timeout, "timeout when waiting for a reply", :type => :integer, :depends => :reply, :default => Smith.config.smith.timeout
77
92
  opt :dynamic, "send message to a dynamic queue", :type => :boolean, :default => false, :short => :d
78
93
 
79
94
  conflicts :reply, :number, :file
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'smith/messaging/queue'
4
+
5
+ module Smith
6
+ module Commands
7
+ class Status < CommandBase
8
+ def execute
9
+ status do |s|
10
+ responder.succeed((s) ? "Agency running" : "Agency not running")
11
+ end
12
+ end
13
+
14
+ def status(&blk)
15
+ Messaging::Queue.number_of_consumers(QueueDefinitions::Agency_control) do |consumers_count|
16
+ blk.call(consumers_count > 0)
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def options_spec
23
+ banner "Shows the status of the agency."
24
+ end
25
+ end
26
+ end
27
+ end
data/lib/smith/config.rb CHANGED
@@ -1,47 +1,159 @@
1
+ #!/usr/bin/env ruby
1
2
  # -*- encoding: utf-8 -*-
3
+
4
+ require 'pathname'
5
+
2
6
  module Smith
7
+
8
+ class ConfigNotFoundError < IOError; end
9
+
3
10
  class Config
4
11
 
5
- @@config = Optimism.new.tap do |o|
6
- o.agency do |a|
7
- a.timeout = 30
12
+ CONFIG_FILENAME = '.smithrc'
13
+
14
+ attr_accessor :agent, :agency, :amqp, :logging, :smith, :eventmachine, :smith, :ruby
15
+
16
+ to_hash = proc do
17
+ def to_hash
18
+ Hash[*members.zip(values).flatten]
8
19
  end
9
20
 
10
- o.agent do |a|
11
- a.monitor = false
12
- a.singleton = true
13
- a.metadata = ''
21
+ def merge(h)
22
+ to_hash.merge(h)
14
23
  end
15
24
 
16
- o.amqp do |a|
17
- a.publish do |p|
18
- p.ack = true
19
- p.headers = {}
20
- end
25
+ def has_key?(k)
26
+ to_hash.has_key?(k)
27
+ end
28
+ end
29
+
30
+ Struct.new("Agent", :monitor, :singleton, :metadata, :prefetch, &to_hash)
31
+ Struct.new("Agency", :cache_path, :agent_path, :acl_path, :acl_cache_path, :pid_dir, &to_hash)
32
+ Struct.new("AmqpOpts", :durable, :auto_delete, &to_hash)
33
+ Struct.new("Broker", :host, :port, :user, :password, :vhost, &to_hash)
34
+ Struct.new("Subscribe", :ack, &to_hash)
35
+ Struct.new("Pop", :ack, &to_hash)
36
+ Struct.new("Publish", :headers, &to_hash)
37
+ Struct.new("Amqp", :broker, :exchange, :queue, :publish, :subscribe, :pop, &to_hash)
38
+ Struct.new("Appender", :type, :filename, &to_hash)
39
+ Struct.new("Logging", :trace, :level, :default_pattern, :default_date_pattern, :appender, :filetype, :vhost, &to_hash)
40
+ Struct.new("Smith", :namespace, :timeout, &to_hash)
41
+ Struct.new("Eventmachine", :file_descriptors, :epoll, :kqueue, &to_hash)
42
+
43
+ def initialize
44
+ load_config
45
+ end
46
+
47
+ def reload
48
+ @config = Config.new
49
+ end
50
+
51
+ def to_hash
52
+ {:agent => @agent, :agency => @agency, :amqp => @amqp, :eventmachine => @eventmachine, :logging => @logging, :smith => @smith, :ruby => @ruby}
53
+ end
54
+
55
+ def path
56
+ @config_file
57
+ end
58
+
59
+ def self.get
60
+ @config ||= Config.new
61
+ end
62
+
63
+ private
21
64
 
22
- a.pop.ack = true
23
- a.subscribe.ack = true
65
+ def set_as_boolean(config, k, default=nil)
66
+ v = config[k]
67
+ if v.nil? && default
68
+ default
69
+ else
70
+ v = v.downcase
71
+ if v == 'true'
72
+ true
73
+ elsif v == 'false'
74
+ false
75
+ else
76
+ raise ArgumentError, "#{k} must be true or false, #{v} given."
77
+ end
24
78
  end
79
+ end
80
+
81
+ def set_as_string(config, k, default=nil)
82
+ config[k]
83
+ end
25
84
 
26
- o.agency do |a|
27
- a.timeout = 30
85
+ def set_as_integer(config, k, default=nil)
86
+ v = config[k]
87
+ if v.nil? && default
88
+ default
89
+ else
90
+ begin
91
+ Integer(v)
92
+ rescue
93
+ raise ArgumentError, "#{k} must be an integer. #{v} given."
94
+ end
28
95
  end
96
+ end
97
+
98
+ def load_config
99
+ config = read_config_file(find_config_file)
100
+
101
+ amqp_opts = Struct::AmqpOpts.new(true, false)
102
+ cache_path = Pathname.new(config[:agency_cache_path]).expand_path
103
+ local_acl_path = Pathname.new(__FILE__).dirname.join('messaging').join('acl').expand_path
104
+ acl_path = "#{local_acl_path}#{File::PATH_SEPARATOR}#{config[:acl_path]}"
105
+ broker = Struct::Broker.new(config[:broker_host], set_as_integer(config, :broker_port), config[:broker_user], config[:broker_password], config[:broker_vhost] || '/')
106
+ appender = Struct::Appender.new(config[:logging_appender_type], config[:logging_appender_filename])
29
107
 
30
- o.logging do |l|
31
- l.trace = false
32
- l.level = :debug
33
- l.appender = 'Stdout'
34
- l.default_pattern = '%d [%5p] %7l - %34c:%-3L - %m\\n'
35
- l.default_date_pattern = '%Y/%m/%d %H:%M:%S.%N'
108
+ @agent = Struct::Agent.new(set_as_boolean(config, :agent_monitor), set_as_boolean(config, :agent_singleton), '', set_as_integer(config, :agent_prefetch))
109
+ @agency = Struct::Agency.new(cache_path, config[:agent_path], acl_path, cache_path.join('acl'), config[:agency_pid_dir])
110
+ @amqp = Struct::Amqp.new(broker, amqp_opts, amqp_opts, Struct::Publish.new({}), Struct::Subscribe.new(true), Struct::Pop.new(true))
111
+ @eventmachine = Struct::Eventmachine.new(set_as_integer(config, :file_descriptors, 1024), set_as_boolean(config, :epoll, true), set_as_boolean(config, :kqueue, true))
112
+ @logging = Struct::Logging.new(config[:logging_trace], config[:logging_level], config[:logging_pattern], config[:logging_date_pattern], appender)
113
+ @smith = Struct::Smith.new(config[:smith_namespace], set_as_integer(config, :smith_timeout))
114
+
115
+ # Set the default ruby runtime. This will use the ruby that is in the path.
116
+ @ruby = Hash.new(config[:default_vm] || 'ruby')
117
+
118
+ config[:agent_vm] && config[:agent_vm].split(/\s+/).each do |vm_spec|
119
+ agent, vm = vm_spec.split(/:/)
120
+ @ruby[agent] = vm
36
121
  end
37
- end._merge!(Optimism.require('/etc/smith/smithrc', "#{ENV['HOME']}/.smithrc"))
38
122
 
39
- # I'm sure there's a better way of doing this ...
40
- @@config.agency['acl_cache_path'] = Pathname.new(@@config.agency.cache_path).join('acl')
41
- @@config.agency['acl_path'] = "#{Pathname.new(__FILE__).dirname.join('messaging').join('acl')}#{File::PATH_SEPARATOR}#{@@config.agency['acl_path']}"
123
+ find_config_file
124
+ end
42
125
 
43
- def self.get
44
- @@config
126
+ # Read the config file
127
+ def read_config_file(config_file)
128
+ @config_file = config_file
129
+ config_file.readlines.inject({}) do |a, line|
130
+ a.tap do |acc|
131
+ parameters = line.gsub(/#.*$/, '').strip
132
+ unless parameters.empty?
133
+ key, value = parameters.split(/\s+/, 2)
134
+ a[key.to_sym] = value
135
+ end
136
+ end
137
+ end
138
+ end
139
+
140
+ # Find the config file. If it isn't in the CWD recurse up the file path
141
+ # until it reaches the user home directory. If it gets to the home
142
+ # directory without finding a config file rais a ConfigNotFoundError
143
+ # exception.
144
+ #
145
+ # path: the pathname to find the config file. Defaults to CWD.
146
+ # recursive: rucures up the path. Defaults to true.
147
+ def find_config_file(path=Pathname.new(".").expand_path, recursive=true)
148
+ conf = path.join(CONFIG_FILENAME)
149
+ if conf.exist?
150
+ return conf
151
+ else
152
+ if path == Pathname.new(ENV['HOME'])
153
+ raise ConfigNotFoundError, "Cannot find a usable config file."
154
+ end
155
+ find_config_file(path.dirname)
156
+ end
45
157
  end
46
158
  end
47
159
  end