smith 0.5.8 → 0.5.10

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