smith 0.5.13.1 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
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