cognizant 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.travis.yml +17 -0
  4. data/Gemfile +4 -1
  5. data/{LICENSE → License.md} +4 -2
  6. data/Rakefile +5 -0
  7. data/Readme.md +95 -0
  8. data/bin/cognizant +76 -122
  9. data/bin/cognizantd +28 -61
  10. data/cognizant.gemspec +8 -4
  11. data/examples/apps/redis-server.cz +42 -0
  12. data/examples/apps/redis-server.yml +29 -0
  13. data/examples/apps/redis-server_dsl.cz +54 -0
  14. data/examples/apps/resque.cz +17 -0
  15. data/examples/apps/thin.cz +32 -0
  16. data/examples/apps/thin.yml +48 -0
  17. data/examples/cognizantd.yml +18 -47
  18. data/features/child_process.feature +62 -0
  19. data/features/commands.feature +65 -0
  20. data/features/cpu_usage.feature +45 -0
  21. data/features/daemon.feature +12 -0
  22. data/features/flapping.feature +39 -0
  23. data/features/memory_usage.feature +45 -0
  24. data/features/shell.feature +30 -0
  25. data/features/step_definitions/common_steps.rb +14 -0
  26. data/features/step_definitions/daemon_steps.rb +25 -0
  27. data/features/step_definitions/shell_steps.rb +96 -0
  28. data/features/support/env.rb +54 -0
  29. data/lib/cognizant.rb +1 -5
  30. data/lib/cognizant/application.rb +122 -0
  31. data/lib/cognizant/application/dsl_proxy.rb +23 -0
  32. data/lib/cognizant/client.rb +61 -0
  33. data/lib/cognizant/commands.rb +164 -0
  34. data/lib/cognizant/commands/actions.rb +30 -0
  35. data/lib/cognizant/commands/help.rb +10 -0
  36. data/lib/cognizant/commands/load.rb +10 -0
  37. data/lib/cognizant/commands/shutdown.rb +7 -0
  38. data/lib/cognizant/commands/status.rb +11 -0
  39. data/lib/cognizant/commands/use.rb +15 -0
  40. data/lib/cognizant/controller.rb +17 -0
  41. data/lib/cognizant/daemon.rb +279 -0
  42. data/lib/cognizant/interface.rb +17 -0
  43. data/lib/cognizant/log.rb +25 -0
  44. data/lib/cognizant/process.rb +138 -94
  45. data/lib/cognizant/process/actions.rb +30 -41
  46. data/lib/cognizant/process/actions/restart.rb +73 -17
  47. data/lib/cognizant/process/actions/start.rb +35 -12
  48. data/lib/cognizant/process/actions/stop.rb +38 -17
  49. data/lib/cognizant/process/attributes.rb +41 -10
  50. data/lib/cognizant/process/children.rb +36 -0
  51. data/lib/cognizant/process/{condition_check.rb → condition_delegate.rb} +11 -13
  52. data/lib/cognizant/process/conditions.rb +7 -4
  53. data/lib/cognizant/process/conditions/cpu_usage.rb +5 -6
  54. data/lib/cognizant/process/conditions/memory_usage.rb +2 -6
  55. data/lib/cognizant/process/dsl_proxy.rb +23 -0
  56. data/lib/cognizant/process/execution.rb +16 -9
  57. data/lib/cognizant/process/pid.rb +16 -6
  58. data/lib/cognizant/process/status.rb +14 -2
  59. data/lib/cognizant/process/trigger_delegate.rb +57 -0
  60. data/lib/cognizant/process/triggers.rb +19 -0
  61. data/lib/cognizant/process/triggers/flapping.rb +68 -0
  62. data/lib/cognizant/process/triggers/transition.rb +22 -0
  63. data/lib/cognizant/process/triggers/trigger.rb +15 -0
  64. data/lib/cognizant/shell.rb +142 -0
  65. data/lib/cognizant/system.rb +16 -0
  66. data/lib/cognizant/system/ps.rb +1 -1
  67. data/lib/cognizant/system/signal.rb +2 -2
  68. data/lib/cognizant/util/dsl_proxy_methods_handler.rb +25 -0
  69. data/lib/cognizant/util/fixnum_percent.rb +5 -0
  70. data/lib/cognizant/util/transform_hash_keys.rb +33 -0
  71. data/lib/cognizant/validations.rb +142 -142
  72. data/lib/cognizant/version.rb +1 -1
  73. metadata +131 -71
  74. data/README.md +0 -221
  75. data/examples/redis-server.rb +0 -28
  76. data/examples/resque.rb +0 -10
  77. data/images/logo-small.png +0 -0
  78. data/images/logo.png +0 -0
  79. data/images/logo.pxm +0 -0
  80. data/lib/cognizant/logging.rb +0 -33
  81. data/lib/cognizant/process/conditions/flapping.rb +0 -57
  82. data/lib/cognizant/process/conditions/trigger_condition.rb +0 -52
  83. data/lib/cognizant/server.rb +0 -14
  84. data/lib/cognizant/server/commands.rb +0 -80
  85. data/lib/cognizant/server/daemon.rb +0 -277
  86. data/lib/cognizant/server/interface.rb +0 -86
  87. data/lib/cognizant/util/symbolize_hash_keys.rb +0 -19
@@ -0,0 +1,45 @@
1
+ Feature: CPU Usage Condition
2
+
3
+ Here I want to test proper handling of cpu_usage condition.
4
+
5
+ Background:
6
+ Given a file named "consume_cpu.rb" with:
7
+ """ruby
8
+ require 'timeout'
9
+ $0 = File.basename(__FILE__) # Useful identification when debugging.
10
+ data = ''
11
+ Timeout::timeout(60) do
12
+ loop do
13
+ data += '*' * 100
14
+ end
15
+ end
16
+ data = nil
17
+ """
18
+ Given a file named "monitor.cz" with:
19
+ """ruby
20
+ Cognizant.application 'cpu_usage_app' do
21
+ pids_dir './cognizant/pids/'
22
+ logs_dir './cognizant/logs/'
23
+ monitor 'consume_cpu' do
24
+ autostart false
25
+ daemonize!
26
+ start_command 'ruby ./consume_cpu.rb'
27
+ check :cpu_usage, :every => 2.seconds, :above => 60.percent, :times => [2, 3], :do => :stop
28
+ end
29
+ end
30
+ """
31
+
32
+ @daemon
33
+ @shell
34
+ Scenario: Check CPU usage of a process that consumes a lot of CPU
35
+ Given the daemon is running
36
+ And the shell is running
37
+
38
+ When I run "load monitor.cz" successfully in the shell
39
+ And I run "use cpu_usage_app" successfully in the shell
40
+ Then the status of "consume_cpu" should be "stopped"
41
+
42
+ When I run "start consume_cpu" successfully in the shell
43
+ Then the status of "consume_cpu" should be "running"
44
+
45
+ And the status of "consume_cpu" should be "stopped"
@@ -0,0 +1,12 @@
1
+ Feature: Daemon
2
+
3
+ The monitoring daemon needs to run in the background and handle process
4
+ management and provide a command socket for interaction.
5
+
6
+ @daemon
7
+ Scenario: Start and stop the daemon
8
+ Given the daemon is running
9
+ Then a process named "cognizantd" should be running
10
+
11
+ When the daemon is stopped
12
+ Then a process named "cognizantd" should not be running
@@ -0,0 +1,39 @@
1
+ Feature: Flapping Check
2
+
3
+ Here I want to test proper handling of flapping check.
4
+
5
+ Background:
6
+ Given a file named "monitor.cz" with:
7
+ """ruby
8
+ Cognizant.application 'sleep_app' do
9
+ pids_dir './cognizant/pids/'
10
+ logs_dir './cognizant/logs/'
11
+ monitor 'sleep_process' do
12
+ autostart false
13
+ daemonize!
14
+ start_command 'sleep 8'
15
+ check :flapping, times: 3, within: 1.minute, retry_after: 15.seconds, retries: 0
16
+ end
17
+ end
18
+ """
19
+
20
+ @daemon
21
+ @shell
22
+ Scenario: Handle a process that restarts every 3 seconds
23
+ Given the daemon is running
24
+ And the shell is running
25
+
26
+ When I run "load monitor.cz" successfully in the shell
27
+ And I run "use sleep_app" successfully in the shell
28
+ Then the status of "sleep_process" should be "stopped"
29
+
30
+ When I run "start sleep_process" successfully in the shell
31
+ # Give 3 seconds grace time for process states to be realized by daemon.
32
+ Then the status of "sleep_process" should be "running" for 5 seconds
33
+ # TODO: The process goes through another stopped -> running cycle
34
+ # before being unmonitored. Add step to handle this.
35
+ Then the status of "sleep_process" should be "unmonitored" for 12 seconds
36
+ Then the status of "sleep_process" should be "running" for 5 seconds
37
+
38
+ When I run "stop sleep_process" successfully in the shell
39
+ Then the status of "sleep_process" should be "stopped"
@@ -0,0 +1,45 @@
1
+ Feature: Memory Usage Condition
2
+
3
+ Here I want to test proper handling of memory_usage condition.
4
+
5
+ Background:
6
+ Given a file named "consume_memory.rb" with:
7
+ """ruby
8
+ require 'timeout'
9
+ $0 = File.basename(__FILE__) # Useful identification when debugging.
10
+ data = ''
11
+ Timeout::timeout(60) do
12
+ loop do
13
+ data += '*' * 100
14
+ end
15
+ end
16
+ data = nil
17
+ """
18
+ Given a file named "monitor.cz" with:
19
+ """ruby
20
+ Cognizant.application 'memory_usage_app' do
21
+ pids_dir './cognizant/pids/'
22
+ logs_dir './cognizant/logs/'
23
+ monitor 'consume_memory' do
24
+ autostart false
25
+ daemonize!
26
+ start_command 'ruby ./consume_memory.rb'
27
+ check :memory_usage, :every => 2.seconds, :above => 10.megabytes, :times => [2, 3], :do => :stop
28
+ end
29
+ end
30
+ """
31
+
32
+ @daemon
33
+ @shell
34
+ Scenario: Check memory usage of a process that consumes a lot of memory
35
+ Given the daemon is running
36
+ And the shell is running
37
+
38
+ When I run "load monitor.cz" successfully in the shell
39
+ And I run "use memory_usage_app" successfully in the shell
40
+ Then the status of "consume_memory" should be "stopped"
41
+
42
+ When I run "start consume_memory" successfully in the shell
43
+ Then the status of "consume_memory" should be "running"
44
+
45
+ And the status of "consume_memory" should be "stopped"
@@ -0,0 +1,30 @@
1
+ Feature: Shell
2
+
3
+ Cognizant shell provides an interactive interface to pass commands to
4
+ the monitoring daemon.
5
+
6
+ @shell
7
+ Scenario: Shell should need a deamon to work
8
+ Then the shell is not running
9
+
10
+ @daemon
11
+ @shell
12
+ Scenario: Shell should realize that the daemon has gone away
13
+ Given the daemon is running
14
+ And the shell is running
15
+
16
+ When I run "help" in the shell
17
+ Then I should see "You can run the following commands" in the shell
18
+
19
+ When the daemon is stopped
20
+ And I run "help" in the shell
21
+ Then I should see "Error communicating with cognizantd" in the shell
22
+
23
+ @daemon
24
+ @shell
25
+ Scenario: Shell should prompt about an unrecognized command
26
+ Given the daemon is running
27
+ And the shell is running
28
+
29
+ When I run "test" in the shell
30
+ Then I should see "Unrecognized command" in the shell
@@ -0,0 +1,14 @@
1
+ require "aruba/api"
2
+
3
+ # When /^I run (cognizant) `([^`]*)`$/ do |_, cmd|
4
+ # cmd = "cognizant #{cmd} --sockfile ./example"
5
+ # step "I run `#{cmd}`"
6
+ # end
7
+
8
+ When /^a process named "([^"]*)" (?:should be|is) running$/ do |name|
9
+ `ps -eo command | grep ^#{name}`.should include(name)
10
+ end
11
+
12
+ When /^a process named "([^"]*)" (?:should not be|is not) running$/ do |name|
13
+ `ps -eo command | grep ^#{name}`.should_not include(name)
14
+ end
@@ -0,0 +1,25 @@
1
+ When /^the daemon is running$/ do
2
+ step %Q{I see "Cognizant daemon running successfully." on the daemon terminal}
3
+ end
4
+
5
+ When /^the daemon is stopped$/ do
6
+ Process.kill("TERM", @daemon_pipe.pid)
7
+ sleep 1
8
+ Process.kill("KILL", @daemon_pipe.pid)
9
+ end
10
+
11
+ When /^I (?:should )?see "([^"]*)" on the daemon terminal$/ do |string|
12
+ output = ""
13
+
14
+ begin
15
+ Timeout::timeout(30) do
16
+ while not output =~ /#{string}/
17
+ output += @daemon_pipe.readpartial(1)
18
+ end
19
+ end
20
+ rescue Timeout::Error, EOFError
21
+ nil
22
+ end
23
+
24
+ output.should include(string)
25
+ end
@@ -0,0 +1,96 @@
1
+ When /^the shell is running$/ do
2
+ step %Q{I see "You are speaking to the Cognizant Monitoring Daemon." in the shell}
3
+ end
4
+
5
+ When /^the shell is not running$/ do
6
+ step %Q{I see "Could not connect to Cognizant daemon process" in the shell}
7
+ end
8
+
9
+ When /^I run "([^"]*)" in the shell$/ do |string|
10
+ @shell_pipe.puts(string)
11
+ sleep 0.5
12
+ end
13
+
14
+ When /^I run "([^"]*)" successfully in the shell$/ do |cmd|
15
+ step %Q{I run "#{cmd}" in the shell}
16
+ step %Q{I should see "OK" in the shell}
17
+ end
18
+
19
+ # Once we have the required status, it should stay the same in the given next n seconds.
20
+ When /^the (status) of "([^"]*)" (?:should be|is) "([^"]*)" for (\d+) seconds$/ do |_, name, status, timeframe|
21
+ step %Q{the status of "#{name}" should be "#{status}"}
22
+
23
+ output = ""
24
+
25
+ begin
26
+ Timeout::timeout(timeframe.to_i) do
27
+ catch :stop do
28
+ loop do
29
+ buffer = ""
30
+ @shell_pipe.puts("status #{name}")
31
+ while not buffer =~ /\s#{name}\s.*since/
32
+ begin
33
+ buffer << @shell_pipe.readpartial(1)
34
+ rescue EOFError
35
+ retry
36
+ end
37
+ end
38
+ # We need just the line where it tells name and status.
39
+ output = buffer.split("\n").last
40
+ if buffer =~ /\s#{status}\s/
41
+ # Take rest before next check.
42
+ sleep 0.2
43
+ else
44
+ # Otherwise stop checking and report output in error.
45
+ throw :stop
46
+ end
47
+ end
48
+ end
49
+ end
50
+ rescue Timeout::Error
51
+ # We spent the time gracefully as required.
52
+ nil
53
+ end
54
+
55
+ # Successful if we reach here.
56
+ output.should include(status)
57
+ end
58
+
59
+ When /^the (status) of "([^"]*)" (?:should be|is) "([^"]*)"$/ do |_, name, status|
60
+ output = ""
61
+ time_step = 0.25
62
+ time_spent = 0
63
+ timeout = 30
64
+ begin
65
+ Timeout::timeout(time_step) do
66
+ time_spent += time_step
67
+ @shell_pipe.puts("status #{name}")
68
+ while not output =~ /\s#{status}\s/
69
+ buffer = @shell_pipe.readpartial(1)
70
+ output += buffer if buffer
71
+ end
72
+ end
73
+ rescue Timeout::Error, EOFError
74
+ retry unless time_spent > timeout
75
+ end
76
+
77
+ output.should include(status)
78
+ end
79
+
80
+ When /^I (?:should )?see "([^"]*)" in the shell$/ do |string|
81
+ sleep 0.5
82
+ output = ""
83
+
84
+ begin
85
+ Timeout::timeout(30) do
86
+ while not output =~ /#{string}/
87
+ buffer = @shell_pipe.readpartial(1)
88
+ output += buffer if buffer
89
+ end
90
+ end
91
+ rescue Timeout::Error, EOFError
92
+ nil
93
+ end
94
+
95
+ output.should include(string)
96
+ end
@@ -0,0 +1,54 @@
1
+ require 'coveralls'
2
+ Coveralls.wear!
3
+
4
+ require "aruba/cucumber"
5
+ require "aruba/api"
6
+ require "open3"
7
+ require "fileutils"
8
+
9
+ Before "@daemon" do
10
+ in_current_dir do
11
+ FileUtils.mkdir("cognizant")
12
+ File.open("cognizantd.yml", "w") do |f|
13
+ f.write <<-heredoc
14
+ daemonize: false
15
+ sockfile: ./cognizant/cognizantd.sock
16
+ pidfile: ./cognizant/cognizantd.pid
17
+ logfile: ./cognizant/cognizantd.log
18
+ heredoc
19
+ end
20
+
21
+ cmd = "cognizantd cognizantd.yml --trace"
22
+ Aruba.config.hooks.execute(:before_cmd, self, cmd)
23
+ announcer.dir(Dir.pwd)
24
+ announcer.cmd(cmd)
25
+
26
+ @daemon_pipe = IO.popen(cmd, "r")
27
+ sleep 2
28
+ end
29
+ end
30
+
31
+ After "@daemon" do
32
+ ::Process.kill("TERM", @daemon_pipe.pid)
33
+ sleep 0.5
34
+ ::Process.kill("KILL", @daemon_pipe.pid)
35
+ end
36
+
37
+ Before "@shell" do
38
+ in_current_dir do
39
+ cmd = "cognizant shell --sockfile ./cognizant/cognizantd.sock"
40
+
41
+ Aruba.config.hooks.execute(:before_cmd, self, cmd)
42
+ announcer.dir(Dir.pwd)
43
+ announcer.cmd(cmd)
44
+
45
+ @shell_pipe = IO.popen(cmd, "w+")
46
+ sleep 1
47
+ end
48
+ end
49
+
50
+ After "@shell" do
51
+ ::Process.kill("TERM", @shell_pipe.pid)
52
+ sleep 0.5
53
+ ::Process.kill("KILL", @shell_pipe.pid)
54
+ end
@@ -1,10 +1,6 @@
1
1
  require "active_support/core_ext/numeric"
2
2
  require "active_support/duration"
3
-
4
- require "cognizant/server"
3
+ require "cognizant/util/fixnum_percent"
5
4
 
6
5
  module Cognizant
7
- def self.monitor(process_name = nil, attributes = {}, &block)
8
- Cognizant::Server.daemon.monitor(process_name, attributes, &block)
9
- end
10
6
  end
@@ -0,0 +1,122 @@
1
+ require "eventmachine"
2
+
3
+ require "cognizant/controller"
4
+ require "cognizant/interface"
5
+ require "cognizant/system"
6
+ require "cognizant/application/dsl_proxy"
7
+
8
+ module Cognizant
9
+ def self.application(application_name = nil, attributes = {}, &block)
10
+ Cognizant::Controller.daemon.create_application(application_name, attributes, &block)
11
+ end
12
+
13
+ class Application
14
+ attr_accessor :name, :pids_dir, :logs_dir
15
+ attr_accessor :processes
16
+
17
+ def initialize(name = nil, options = {}, &block)
18
+ self.setup({ name: name }.merge(options), &block)
19
+ end
20
+
21
+ def setup(options = {}, &block)
22
+ self.reset!
23
+
24
+ set_attributes(options)
25
+
26
+ handle_initialize_block(&block) if block
27
+
28
+ raise "Application name is missing. Aborting." unless self.name
29
+ Log[self].info "Loading application #{self.name}..."
30
+
31
+ raise "Application processes are missing. Aborting." unless self.processes.keys.size > 0
32
+
33
+ self.setup_directories
34
+ end
35
+
36
+ def handle_initialize_block(&block)
37
+ if block.arity == 0
38
+ dsl_proxy = Cognizant::Application::DSLProxy.new(self, &block)
39
+ attributes = dsl_proxy.attributes
40
+ set_attributes(attributes)
41
+ else
42
+ instance_exec(self, &block)
43
+ end
44
+ end
45
+
46
+ def set_attributes(attributes)
47
+ procs = {}
48
+ procs = attributes.delete(:processes) if attributes.has_key?(:processes)
49
+
50
+ mons = {}
51
+ mons = attributes.delete(:monitor) if attributes.has_key?(:monitor)
52
+
53
+ # Attributes.
54
+ attributes.each do |key, value|
55
+ if self.respond_to?("#{key}=")
56
+ value = attributes.delete(key)
57
+ self.send("#{key}=", value)
58
+ end
59
+ end
60
+
61
+ # Processes.
62
+ procs.each do |key, value|
63
+ process(key, value)
64
+ end
65
+
66
+ # Automatically monitor these.
67
+ mons.each do |key, value|
68
+ monitor(key, value)
69
+ end
70
+ end
71
+
72
+ def monitor(process_name = nil, attributes = {}, &block)
73
+ proc = create_process(process_name, attributes, &block)
74
+ proc.monitor
75
+ proc
76
+ end
77
+
78
+ def process(process_name = nil, attributes = {}, &block)
79
+ create_process(process_name, attributes, &block)
80
+ end
81
+
82
+ def reset!
83
+ self.name = nil
84
+ self.pids_dir = nil
85
+ self.logs_dir = nil
86
+ self.processes.values.each(&:reset!) if self.processes.is_a?(Hash)
87
+ self.processes = {}
88
+ end
89
+ alias :shutdown! :reset!
90
+
91
+ def pids_dir
92
+ if @pids_dir and expanded_path = File.expand_path(@pids_dir)
93
+ expanded_path
94
+ else
95
+ "/var/run/cognizant/#{self.name}/pids/"
96
+ end
97
+ end
98
+
99
+ def logs_dir
100
+ if @logs_dir and expanded_path = File.expand_path(@logs_dir)
101
+ expanded_path
102
+ else
103
+ "/var/log/cognizant/#{self.name}/logs/"
104
+ end
105
+ end
106
+
107
+ def create_process(process_name, options = {}, &block)
108
+ p = Cognizant::Process.new(process_name, options, &block)
109
+ p.instance_variable_set(:@application, self)
110
+ self.processes[p.name.to_sym] = p
111
+ p
112
+ end
113
+
114
+ def tick
115
+ self.processes.values.each(&:tick)
116
+ end
117
+
118
+ def setup_directories
119
+ Cognizant::System.mkdir(self.pids_dir, self.logs_dir)
120
+ end
121
+ end
122
+ end