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.
- checksums.yaml +7 -0
- data/bin/agency +19 -7
- data/bin/pry-smith +11 -0
- data/bin/smithctl +19 -21
- data/lib/smith/acl_compiler.rb +101 -56
- data/lib/smith/acl_parser.rb +75 -0
- data/lib/smith/agent.rb +28 -43
- data/lib/smith/agent_cache.rb +43 -17
- data/lib/smith/agent_monitoring.rb +1 -1
- data/lib/smith/agent_process.rb +148 -53
- data/lib/smith/application/agency.rb +44 -54
- data/lib/smith/bootstrap.rb +31 -17
- data/lib/smith/cache.rb +4 -0
- data/lib/smith/command.rb +1 -3
- data/lib/smith/commands/agency/kill.rb +22 -5
- data/lib/smith/commands/agency/list.rb +9 -4
- data/lib/smith/commands/agency/logger.rb +25 -12
- data/lib/smith/commands/agency/object_count.rb +19 -8
- data/lib/smith/commands/agency/start.rb +7 -10
- data/lib/smith/commands/agency/stop.rb +30 -12
- data/lib/smith/commands/common.rb +1 -1
- data/lib/smith/commands/smithctl/acl.rb +6 -3
- data/lib/smith/commands/smithctl/dump.rb +79 -0
- data/lib/smith/commands/smithctl/firehose.rb +2 -1
- data/lib/smith/commands/smithctl/push.rb +27 -12
- data/lib/smith/commands/smithctl/status.rb +27 -0
- data/lib/smith/config.rb +140 -28
- data/lib/smith/daemon.rb +16 -3
- data/lib/smith/exceptions.rb +6 -3
- data/lib/smith/logger.rb +12 -24
- data/lib/smith/messaging/acl/agent_keepalive.proto +2 -2
- data/lib/smith/messaging/acl/agent_lifecycle.proto +15 -9
- data/lib/smith/messaging/acl/agent_stats.proto +6 -5
- data/lib/smith/messaging/acl/default.rb +2 -7
- data/lib/smith/messaging/acl_type_cache.rb +77 -0
- data/lib/smith/messaging/factory.rb +29 -0
- data/lib/smith/messaging/queue.rb +12 -10
- data/lib/smith/messaging/queue_definition.rb +21 -4
- data/lib/smith/messaging/receiver.rb +55 -62
- data/lib/smith/messaging/requeue.rb +1 -5
- data/lib/smith/messaging/sender.rb +48 -43
- data/lib/smith/messaging/util.rb +0 -10
- data/lib/smith/queue_definitions.rb +7 -4
- data/lib/smith/version.rb +1 -1
- data/lib/smith.rb +57 -56
- metadata +77 -128
- 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
|
-
|
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
|
-
|
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 |
|
29
|
-
|
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.
|
42
|
-
sender.publish(ACL::
|
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
|
-
|
11
|
-
|
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
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
-
|
48
|
-
if
|
49
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
68
|
-
|
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
|
74
|
-
|
75
|
-
|
76
|
-
|
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
|
-
|
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(
|
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
|
-
|
47
|
-
|
48
|
-
|
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
|
-
|
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
|
-
|
21
|
-
|
22
|
-
|
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
|
-
|
30
|
-
|
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
|
-
|
45
|
+
on_done = -> { blk.call("") }
|
37
46
|
|
38
|
-
|
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
|
-
|
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.
|
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
|
-
|
6
|
-
|
7
|
-
|
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
|
-
|
11
|
-
|
12
|
-
a.singleton = true
|
13
|
-
a.metadata = ''
|
21
|
+
def merge(h)
|
22
|
+
to_hash.merge(h)
|
14
23
|
end
|
15
24
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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
|
-
|
23
|
-
|
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
|
-
|
27
|
-
|
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
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
-
|
40
|
-
|
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
|
-
|
44
|
-
|
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
|