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.
- 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
|