smith 0.5.8 → 0.5.10

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 (37) hide show
  1. data/bin/agency +49 -21
  2. data/bin/smithctl +51 -48
  3. data/lib/smith.rb +21 -17
  4. data/lib/smith/acl_compiler.rb +6 -3
  5. data/lib/smith/agent.rb +13 -24
  6. data/lib/smith/agent_monitoring.rb +5 -5
  7. data/lib/smith/agent_process.rb +4 -4
  8. data/lib/smith/application/agency.rb +11 -7
  9. data/lib/smith/bootstrap.rb +4 -4
  10. data/lib/smith/command.rb +26 -5
  11. data/lib/smith/command_base.rb +2 -2
  12. data/lib/smith/commands/agency/list.rb +11 -26
  13. data/lib/smith/commands/agency/logger.rb +1 -1
  14. data/lib/smith/commands/agency/restart.rb +1 -1
  15. data/lib/smith/commands/agency/start.rb +20 -7
  16. data/lib/smith/commands/agency/stop.rb +23 -10
  17. data/lib/smith/commands/agency/version.rb +31 -0
  18. data/lib/smith/commands/smithctl/commands.rb +1 -13
  19. data/lib/smith/commands/smithctl/pop.rb +34 -28
  20. data/lib/smith/commands/smithctl/{cat.rb → push.rb} +3 -3
  21. data/lib/smith/commands/smithctl/rm.rb +20 -0
  22. data/lib/smith/commands/smithctl/top.rb +1 -1
  23. data/lib/smith/config.rb +15 -0
  24. data/lib/smith/daemon.rb +68 -0
  25. data/lib/smith/{messaging/exceptions.rb → exceptions.rb} +6 -0
  26. data/lib/smith/logger.rb +2 -3
  27. data/lib/smith/messaging/acl/agent_keepalive.proto +2 -2
  28. data/lib/smith/messaging/acl/agent_lifecycle.proto +2 -2
  29. data/lib/smith/messaging/acl/default.rb +1 -1
  30. data/lib/smith/messaging/endpoint.rb +5 -5
  31. data/lib/smith/messaging/receiver.rb +19 -19
  32. data/lib/smith/messaging/sender.rb +3 -3
  33. data/lib/smith/version.rb +3 -0
  34. metadata +52 -41
  35. data/lib/smith/commands/agency/agency_version.rb +0 -24
  36. data/lib/smith/commands/agency/state.rb +0 -20
  37. data/lib/smith/commands/smithctl/smithctl_version.rb +0 -22
@@ -17,26 +17,26 @@ module Smith
17
17
  when 'running'
18
18
  if agent_process.last_keep_alive
19
19
  if agent_process.last_keep_alive > agent_process.started_at
20
- if (Time.now.utc.to_i - agent_process.last_keep_alive) > 10
20
+ if (Time.now.to_i - agent_process.last_keep_alive) > 10
21
21
  logger.fatal { "Agent not responding: #{agent_process.name}" }
22
22
  agent_process.no_process_running
23
23
  end
24
24
  else
25
- logger.warn { "Discarding keep_alives with timestamp before agent started: #{Time.at(agent_process.started_at)} > #{Time.at(agent_process.last_keep_alive)}" }
25
+ logger.warn { "Discarding keepalives with timestamp before agent started: #{Time.at(agent_process.started_at)} > #{Time.at(agent_process.last_keep_alive)}" }
26
26
  end
27
27
  end
28
28
  when 'starting'
29
- if (Time.now.utc.to_i - agent_process.started_at) > 10
29
+ if (Time.now.to_i - agent_process.started_at) > 10
30
30
  logger.error { "No response from agent for > 10 seconds. Agent probably didn't start" }
31
31
  agent_process.not_responding
32
32
  else
33
33
  logger.debug { "no keep alive from #{agent_process.name}" }
34
34
  end
35
- when 'stoping'
35
+ when 'stopping'
36
36
  logger.info { "Agent is shutting down: #{agent_process.name}" }
37
37
  when 'dead'
38
38
  logger.info { "Restarting dead agent: #{agent_process.name}" }
39
- Messaging::Sender.new('agency.control', :auto_delete => true, :durable => false, :strict => true).ready do |sender|
39
+ Messaging::Sender.new('agency.control', :auto_delete => false, :durable => false, :strict => true).ready do |sender|
40
40
  sender.publish_and_receive(ACL::Payload.new(:agency_command).content(:command => 'start', :args => [agent_process.name])) do |r|
41
41
  logger.debug { "Agent restart message acknowledged: #{agent_process.name}" }
42
42
  end
@@ -15,7 +15,7 @@ module Smith
15
15
  property :path, String, :required => true
16
16
  property :name, String, :required => true
17
17
  property :state, String, :required => true
18
- property :pid, String
18
+ property :pid, Integer
19
19
  property :started_at, Integer
20
20
  property :last_keep_alive, Integer
21
21
  property :metadata, String
@@ -95,7 +95,7 @@ module Smith
95
95
  # Start an agent. This forks and execs the bootstrapper class
96
96
  # which then becomes responsible for managing the agent process.
97
97
  def self.start(agent_process)
98
- agent_process.started_at = Time.now.utc
98
+ agent_process.started_at = Time.now
99
99
  agent_process.pid = fork do
100
100
 
101
101
  # Detach from the controlling terminal
@@ -115,9 +115,9 @@ module Smith
115
115
  STDIN.reopen("/dev/null")
116
116
  STDERR.reopen(STDOUT)
117
117
 
118
- bootstraper = File.expand_path(File.join(File.dirname(__FILE__), 'bootstrap.rb'))
118
+ bootstrapper = File.expand_path(File.join(File.dirname(__FILE__), 'bootstrap.rb'))
119
119
 
120
- exec('ruby', bootstraper, agent_process.path, agent_process.name, Smith.acl_cache_path.to_s)
120
+ exec('ruby', bootstrapper, agent_process.path, agent_process.name, Smith.acl_cache_path.to_s)
121
121
  end
122
122
 
123
123
  # We don't want any zombies.
@@ -15,7 +15,7 @@ module Smith
15
15
  end
16
16
 
17
17
  def setup_queues
18
- Messaging::Receiver.new('agency.control', :auto_delete => true, :durable => false, :strict => true).ready do |receiver|
18
+ Messaging::Receiver.new('agency.control', :auto_delete => false, :durable => false, :strict => true).ready do |receiver|
19
19
  receiver.subscribe do |r|
20
20
  r.reply do |responder|
21
21
  # Add a logger proc to the responder chain.
@@ -23,14 +23,14 @@ module Smith
23
23
 
24
24
  begin
25
25
  Command.run(r.payload.command, r.payload.args, :agency => self, :agents => @agent_processes, :responder => responder)
26
- rescue Command::UnkownCommandError => e
26
+ rescue Command::UnknownCommandError => e
27
27
  responder.value("Unknown command: #{r.payload.command}")
28
28
  end
29
29
  end
30
30
  end
31
31
  end
32
32
 
33
- Messaging::Receiver.new('agent.lifecycle', :auto_delete => true, :durable => false).ready do |receiver|
33
+ Messaging::Receiver.new('agent.lifecycle', :auto_delete => false, :durable => false).ready do |receiver|
34
34
  receiver.subscribe do |r|
35
35
  case r.payload.state
36
36
  when 'dead'
@@ -40,12 +40,12 @@ module Smith
40
40
  when 'acknowledge_stop'
41
41
  acknowledge_stop(r.payload)
42
42
  else
43
- logger.warn { "Unkown command received on agent.lifecycle queue: #{r.payload.state}" }
43
+ logger.warn { "Unknown command received on agent.lifecycle queue: #{r.payload.state}" }
44
44
  end
45
45
  end
46
46
  end
47
47
 
48
- Messaging::Receiver.new('agent.keepalive', :auto_delete => true, :durable => false).ready do |receiver|
48
+ Messaging::Receiver.new('agent.keepalive', :auto_delete => false, :durable => false).ready do |receiver|
49
49
  receiver.subscribe do |r|
50
50
  keep_alive(r.payload)
51
51
  end
@@ -59,8 +59,12 @@ module Smith
59
59
 
60
60
  # Stop the agency. This will wait for one second to ensure
61
61
  # that any messages are flushed.
62
- def stop
63
- Smith.stop(true)
62
+ def stop(&blk)
63
+ if blk
64
+ Smith.stop(true, &blk)
65
+ else
66
+ Smith.stop(true)
67
+ end
64
68
  end
65
69
 
66
70
  private
@@ -84,7 +84,7 @@ module Smith
84
84
 
85
85
  def send_dead_message
86
86
  logger.debug { "Sending dead message to agency: #{@agent_name}" }
87
- Messaging::Sender.new('agent.lifecycle', :auto_delete => true, :durable => false).ready do |sender|
87
+ Messaging::Sender.new('agent.lifecycle', :auto_delete => false, :durable => false).ready do |sender|
88
88
  sender.publish(ACL::Payload.new(:agent_lifecycle).content(:state => 'dead', :name => @agent_name))
89
89
  end
90
90
  end
@@ -137,10 +137,10 @@ Smith.load_acls
137
137
 
138
138
  bootstrapper = Smith::AgentBootstrap.new(path, agent_name)
139
139
 
140
- # I've tried putting the exception handling in the main reactor loog
141
- # but it doesn't do anything. I know theres a resaon for this but I
140
+ # I've tried putting the exception handling in the main reactor log
141
+ # but it doesn't do anything. I know there's a reason for this but I
142
142
  # don't what it is at the moment. Just beware that whenever there
143
- # is an exception the recator is not going going to be running.
143
+ # is an exception the reactor is not going going to be running.
144
144
  begin
145
145
  Smith.start do
146
146
  bootstrapper.load_agent
data/lib/smith/command.rb CHANGED
@@ -4,7 +4,7 @@ require 'trollop'
4
4
 
5
5
  module Smith
6
6
  class Command
7
- class UnkownCommandError < RuntimeError; end
7
+ class UnknownCommandError < RuntimeError; end
8
8
 
9
9
  include Logger
10
10
 
@@ -17,12 +17,12 @@ module Smith
17
17
  def self.run(command, args, vars)
18
18
  # Change _ to - underscores look so ugly as a command name.
19
19
  command = command.gsub(/-/, '_')
20
- logger.debug { "Agency command: #{command}#{(args.empty?) ? '' : " #{args.join(', ')}"}." }
21
-
22
20
  load_command(command)
23
21
 
24
22
  clazz = Commands.const_get(Extlib::Inflection.camelize(command)).new
25
23
 
24
+ logger.debug { "#{(Command.agency?) ? 'Agency' : 'Smithctl'} command: #{command}#{(args.empty?) ? '' : " #{args.join(', ')}"}." }
25
+
26
26
  begin
27
27
 
28
28
  clazz.parse_options(args)
@@ -62,17 +62,34 @@ module Smith
62
62
  when smithctl_command?(command)
63
63
  :smithctl
64
64
  else
65
- raise UnkownCommandError, "Unknown command: #{command}"
65
+ raise UnknownCommandError, "Unknown command: #{command}"
66
66
  end
67
67
  end
68
68
 
69
- private
69
+ def self.commands(type=:all)
70
+ types = case type
71
+ when :all
72
+ ['agency', 'smithctl']
73
+ when :agency
74
+ types = ['agency']
75
+ when :smithctl
76
+ types = ['smithctl']
77
+ else
78
+ raise ArgumentError, "Unknown command type"
79
+ end
80
+
81
+ types.map do |type|
82
+ Pathname.glob(base_path.join(type).join("**/*.rb"))
83
+ end.flatten.map {|p| to_command_name(p) }
84
+ end
70
85
 
71
86
  # Check to see if the command is an agency or smithctl command.
72
87
  def self.agency?
73
88
  Smith.constants.include?(:Agency)
74
89
  end
75
90
 
91
+ private
92
+
76
93
  # Return the full path of the ruby class.
77
94
  def self.command_path(command)
78
95
  send("#{command_type(command)}_path").join(command)
@@ -98,6 +115,10 @@ module Smith
98
115
  base_path.join('smithctl')
99
116
  end
100
117
 
118
+ def self.to_command_name(path)
119
+ path.basename(".rb").to_s
120
+ end
121
+
101
122
  # Return the command base path.
102
123
  def self.base_path
103
124
  @c64a6f4f ||= Smith.root_path.join('lib').join("smith").join('commands')
@@ -55,9 +55,9 @@ module Smith
55
55
  #{text}
56
56
 
57
57
  Usage:
58
- smithctl #{self.class.to_s.split('::').last.downcase} OPTIONS
58
+ smithctl #{self.class.to_s.split('::').last.downcase} [Options]
59
59
 
60
- OPTIONS are:
60
+ [Options] are:
61
61
  EOS
62
62
  end
63
63
  end
@@ -4,41 +4,26 @@ module Smith
4
4
  class List < CommandBase
5
5
  def execute
6
6
  responder.value do
7
- if options[:all]
8
- if agents.empty?
9
- "No agents running."
10
- else
11
- if options[:long]
12
- tabulate(long_format(agents), :header => "total #{agents.count}")
13
- else
14
- short_format(agents)
15
- end
16
- end
17
- else
18
- running_agents = agents.state(:running)
19
- if running_agents.empty?
20
- "No agents running."
21
- else
22
- if options[:long]
23
- tabulate(long_format(running_agents), :header => "total #{running_agents.count}")
24
- else
25
- short_format(running_agents)
26
- end
27
- end
28
- end
7
+ a = (options[:all]) ? agents : agents.state(:running)
8
+ (a.empty?) ? nil : format(a, options[:long])
29
9
  end
30
10
  end
31
11
 
32
12
  private
33
13
 
34
- def long_format(agents)
35
- agents.map do |a|
14
+ def format(a, long)
15
+ a = (target.empty?) ? a : a.select {|z| target.detect {|y| z.name == y } }.flatten
16
+ (long) ? tabulate(long_format(a), :header => "total #{a.count}") : short_format(a)
17
+ end
18
+
19
+ def long_format(a)
20
+ a.map do |a|
36
21
  [a.state, a.pid, (a.started_at) ? format_time(a.started_at) : '', (!(a.stopped? || a.null?) && !a.alive?) ? '(agent dead)' : "", a.name]
37
22
  end
38
23
  end
39
24
 
40
- def short_format(agents)
41
- agents.map(&:name).sort.join(" ")
25
+ def short_format(a)
26
+ a.map(&:name).sort.join(" ")
42
27
  end
43
28
 
44
29
  def format_time(t)
@@ -39,7 +39,7 @@ module Smith
39
39
  private
40
40
 
41
41
  def send_agent_control_message(agent, message)
42
- Messaging::Sender.new(agent.control_queue_name, :durable => false, :auto_delete => true).ready do |sender|
42
+ Messaging::Sender.new(agent.control_queue_name, :durable => false, :auto_delete => true, :persistent => true, :strict => true).ready do |sender|
43
43
  sender.publish(ACL::Payload.new(:agent_command).content(message))
44
44
  end
45
45
  end
@@ -12,7 +12,7 @@ module Smith
12
12
  include Common
13
13
 
14
14
  def execute
15
- Messaging::Sender.new('agency.control', :auto_delete => true, :durable => false, :strict => true).ready do |sender|
15
+ Messaging::Sender.new('agency.control', :auto_delete => false, :durable => false, :strict => true).ready do |sender|
16
16
  payload = ACL::Payload.new(:agency_command).content(:command => 'stop', :args => target)
17
17
 
18
18
  sender.publish_and_receive(payload) do |r|
@@ -9,15 +9,17 @@ module Smith
9
9
  include Common
10
10
 
11
11
  def execute
12
+
13
+ #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
14
+ #!!!!!!!!!!!! See not about target at end of this file !!!!!!!!!!!!!!
15
+ #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
16
+
12
17
  # Sort out any groups. If the group option is set it will override
13
18
  # any other specified agents.
14
19
  if options[:group]
15
20
  begin
16
- # I don't understand why I need to put self here. target= is a method
17
- # on Command so I would have thought that would be used but a local
18
- # variable is used instead of the method. TODO work out why and fix.
19
- self.target = agent_group(options[:group])
20
- if target.empty?
21
+ agents_to_start = agent_group(options[:group])
22
+ if agents_to_start.empty?
21
23
  responder.value("There are no agents in group: #{options[:group]}")
22
24
  return
23
25
  end
@@ -25,13 +27,15 @@ module Smith
25
27
  responder.value(e.message)
26
28
  return
27
29
  end
30
+ else
31
+ agents_to_start = target
28
32
  end
29
33
 
30
34
  responder.value do
31
- if target.empty?
35
+ if agents_to_start.empty?
32
36
  "Start what? No agent specified."
33
37
  else
34
- target.map do |agent|
38
+ agents_to_start.map do |agent|
35
39
  agents[agent].name = agent
36
40
  if agents[agent].path
37
41
  if options[:kill]
@@ -60,3 +64,12 @@ module Smith
60
64
  end
61
65
  end
62
66
  end
67
+
68
+
69
+ # A note about target.
70
+ #
71
+ # Target is a method and if you assign something to it strange things happen --
72
+ # even if the code doesn't get run! I'm not strictly sure what's going on but I
73
+ # think it's something to do with the a variable aliasing a method of the same
74
+ # name. So even though the code isn't being run it gets compiled and that
75
+ # somehow aliases the method. This looks like a bug in yarv to me.
@@ -9,6 +9,11 @@ module Smith
9
9
  include Common
10
10
 
11
11
  def execute
12
+
13
+ #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
14
+ #!!!!!!!!!!!! See not about target at end of this file !!!!!!!!!!!!!!
15
+ #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
16
+
12
17
  case target.first
13
18
  when 'agency'
14
19
  running_agents = agents.state(:running)
@@ -31,11 +36,8 @@ module Smith
31
36
  # any other specified agents.
32
37
  if options[:group]
33
38
  begin
34
- # I don't understand why I need to put self here. target= is a method
35
- # on Command so I would have thought that would be used but a local
36
- # variable is used instead of the method. TODO work out why and fix.
37
- self.target = agent_group(options[:group])
38
- if target.empty?
39
+ agents_to_stop = agent_group(options[:group])
40
+ if agents_to_stop.empty?
39
41
  responder.value("There are no agents in group: #{options[:group]}")
40
42
  return
41
43
  end
@@ -43,16 +45,18 @@ module Smith
43
45
  responder.value(e)
44
46
  return
45
47
  end
48
+ else
49
+ agents_to_stop = target
46
50
  end
47
51
 
48
- ret = target.inject([]) do |acc,agent_name|
52
+ ret = agents_to_stop.inject([]) do |acc,agent_name|
49
53
  acc << if agents[agent_name].running?
50
54
  agents[agent_name].stop
51
55
  nil
52
- else
53
- logger.warn { "Agent not running: #{agent_name}" }
54
- agent_name
55
- end
56
+ else
57
+ logger.warn { "Agent not running: #{agent_name}" }
58
+ agent_name
59
+ end
56
60
  end
57
61
  responder.value((ret.compact.empty?) ? nil : "Agent(s) not running: #{ret.compact.join(", ")}")
58
62
  end
@@ -68,3 +72,12 @@ module Smith
68
72
  end
69
73
  end
70
74
  end
75
+
76
+
77
+ # A note about target.
78
+ #
79
+ # Target is a method and if you assign something to it strange things happen --
80
+ # even if the code doesn't get run! I'm not strictly sure what's going on but I
81
+ # think it's something to do with the a variable aliasing a method of the same
82
+ # name. So even though the code isn't being run it gets compiled and that
83
+ # somehow aliases the method. This looks like a bug in yarv to me.
@@ -0,0 +1,31 @@
1
+ # -*- encoding: utf-8 -*-
2
+ module Smith
3
+ module Commands
4
+ class Version < CommandBase
5
+ def execute
6
+ # FIXME Puts some error handling for when the agency isn't running in a git repo.
7
+ if options[:git]
8
+ EM.system('git describe') do |output,status|
9
+ responder.value do
10
+ if status.exitstatus == 0
11
+ output.strip
12
+ else
13
+ 'The agency is not running in a git repo.'
14
+ end
15
+ end
16
+ end
17
+ else
18
+ responder.value(Smith::VERSION)
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def options_spec
25
+ banner "Display the agency version."
26
+
27
+ opt :git, "run git describe, assuming git is installed", :short => :g
28
+ end
29
+ end
30
+ end
31
+ end