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
data/README.md DELETED
@@ -1,221 +0,0 @@
1
- # Cognizant: system utility to supervise your processes
2
-
3
- Let us say that you have a program that is critical for your application. Any
4
- downtime for the processes of this program would hurt your business. You want
5
- to make sure these processes are always up and working. If anything unexpected
6
- happens to their state, you want to be notified about it.
7
-
8
- Enter Cognizant. Cognizant is a system utility that supervises your processes,
9
- monitoring and ensuring their state based on a flexible criteria.
10
-
11
- In simpler terms, it keeps your processes up and running. It ensures
12
- that something productive (like restart) is done when the process being
13
- monitored matches a certain condition (like RAM, CPU usage or a custom
14
- condition).
15
-
16
- And if it matters, cognizant means "having knowledge or being aware of",
17
- according to Apple's Dictionary.
18
-
19
- Cognizant can be used to monitor any long running process, including the
20
- following:
21
-
22
- - Web servers (Nginx, Apache httpd, WebSocket, etc.)
23
- - Databases (Redis, MongoDB, MySQL, PostgreSQL, etc.)
24
- - Job workers (Resque, Sidekiq, Qless, etc.)
25
- - Message queue applications (RabbitMQ, Beanstalkd, etc.)
26
- - Logs collection daemons
27
- - Or any other program that needs to keep running
28
-
29
- ### Monitoring
30
-
31
- Cognizant can be used to monitor an application process' state so that it
32
- is running at all times. When cognizant is instructed to monitor a process,
33
- by default, the process will automatically be started. These processes are
34
- automatically monitored for their state. i.e. a process is automatically
35
- started again if it stops unexpectedly.
36
-
37
- ### Conditions
38
-
39
- Conditions provide a way to monitor and act on more than just the state of a
40
- process. For example, conditions can monitor the resource utilization (RAM,
41
- CPU, etc.) of the application process and restart it if it matches a
42
- condition.
43
-
44
- See examples for conditions usage.
45
-
46
- ### Notifications
47
-
48
- PS: Notifications currently need work.
49
-
50
- Notifications provide a way to alert system administrator of unexpected events
51
- happening with the process. Notifications can use multiple gateways, including
52
- email and twitter [direct message].
53
-
54
- ## Getting started
55
-
56
- Cognizant is written in the Ruby programming language, and hence depends on
57
- it. Ensure that you have Ruby 1.9+ installed and run:
58
-
59
- $ gem install cognizant
60
-
61
- ## Architecture overview
62
-
63
- _____
64
- ___________ |
65
- ____________ ____________ | | |
66
- | | | | -----> | Process A | |
67
- | Admin | | Monitoring | |___________| |
68
- | Utility | -----> | Daemon | ___________ | -- Managed Processes
69
- | >_ | | | | | |
70
- |____________| |____________| -----> | Process B | |
71
- |___________| |
72
- _____|
73
-
74
- Since cognizant administration and process monitoring are two separate concerns, they are serviced by separate applications, `cognizant` and `cognizantd`, respectively.
75
-
76
- ## Starting the monitoring daemon
77
-
78
- Cognizant runs the monitoring essentials through the `cognizantd` daemon application, which also maintains a server for accepting commands from the `cognizant` administration utility.
79
-
80
- The daemon, with it's default options, requires superuser access to certain system directories for storing logs and pid files. It can be started as follows:
81
-
82
- $ sudo cognizantd # ubuntu, debian, os x, etc.
83
- $ su -c 'cognizantd' # amazon linux, centos, rhel, etc.
84
-
85
- To start without superuser access, specify these file and directory config variables to where the user starting it has write access:
86
-
87
- $ cognizantd ./examples/cognizantd.yml
88
-
89
- # assuming that:
90
-
91
- $ cat ~/.examples/cognizantd.yml # find this file in source code for latest version
92
- ---
93
- socket: ~/.cognizant/cognizantd.sock
94
- pidfile: ~/.cognizant/cognizantd.pid
95
- logfile: ~/.cognizant/cognizantd.log
96
- pids_dir: ~/.cognizant/pids/
97
- logs_dir: ~/.cognizant/logs/
98
-
99
- monitor: {
100
- redis-server-1: {
101
- group: redis,
102
- start_command: /usr/local/bin/redis-server -,
103
- start_with_input: "daemonize no\nport 6666",
104
- ping_command: redis-cli -p 6666 PING,
105
- stop_signals: [TERM, INT]
106
- },
107
- redis-server-2: {
108
- group: redis,
109
- start_command: /usr/local/bin/redis-server -,
110
- start_with_input: "daemonize no\nport 7777",
111
- ping_command: redis-cli -p 7777 PING,
112
- stop_command: redis-cli -p 7777 SHUTDOWN
113
- },
114
- sleep: {
115
- start_command: sleep 3,
116
- checks: {
117
- flapping: {
118
- times: 4,
119
- within: 15, # Seconds.
120
- retry_after: 30 # Seconds.
121
- }
122
- }
123
- }
124
- }
125
-
126
- or
127
-
128
- # pass config directly into the daemon's STDIN
129
- $ echo <<EOF | cognizantd -
130
- ---
131
- socket: ~/.cognizant/cognizantd.sock
132
- ...
133
- monitor: {
134
- ...
135
- ...
136
- }
137
- EOF
138
-
139
- Process information can also be provided via Ruby code. See `load` command below.
140
-
141
- ## Using the administration utility
142
-
143
- Cognizant can be administered using the `cognizant` command line utility. This is an application for performing administration tasks like monitoring, starting, stopping processes or loading configuration and processes' information.
144
-
145
- Here are some basic operational commands:
146
-
147
- $ cognizant monitor redis # by group name
148
- $ cognizant restart redis-server-1 # by process name
149
-
150
- To get cognizant to ignore a particular process' state:
151
-
152
- $ cognizant unmonitor sleep-10
153
-
154
- Now check status of all managed processes:
155
-
156
- $ cognizant status
157
- +----------------+-------+--------------------------------------+
158
- | Process | Group | State |
159
- +----------------+-------+--------------------------------------+
160
- | redis-server-1 | redis | running since 1 minute |
161
- +----------------+-------+--------------------------------------+
162
- | redis-server-2 | redis | running since 2 minutes |
163
- +----------------+-------+--------------------------------------+
164
- | sleep-10 | | unmonitored since less than a minute |
165
- +----------------+-------+--------------------------------------+
166
- 2012-11-23 01:16:18 +0530
167
-
168
- Here's how you tell cognizant to start monitoring new processes:
169
-
170
- $ cognizant load ./examples/redis-server.rb # find this file in source code
171
-
172
- All of the available commands are:
173
-
174
- - `help` - Shows a list of commands or help for one command
175
- - `status [name]` - Display status of managed process(es) or group(s)
176
- - `load ./cog.rb` - Loads the process information from specified Ruby file
177
- - `monitor name` - Monitor the specified process or group
178
- - `unmonitor name` - Unmonitor the specified process or group
179
- - `start name` - Start the specified process or group
180
- - `stop name` - Stop the specified process or group
181
- - `restart name` - Restart the specified process or group
182
- - `shutdown` - Stop the monitoring daemon without affecting processes
183
-
184
- See `cognizant help` for options.
185
-
186
- ## Contributing
187
-
188
- Contributions are definitely welcome. To contribute, just follow the usual
189
- workflow:
190
-
191
- 1. Fork Cognizant
192
- 2. Create your feature branch (`git checkout -b my-new-feature`)
193
- 3. Commit your changes (`git commit -am 'Added some feature'`)
194
- 4. Push to the branch (`git push origin my-new-feature`)
195
- 5. Create new Github pull request
196
-
197
- ## Compatibility
198
-
199
- Cognizant was developed and tested under Ruby 1.9.2-p290.
200
-
201
- ## Similar programs
202
-
203
- - Monit
204
- - God (Ruby)
205
- - Bluepill (Ruby)
206
- - Supervisord (Python)
207
- - Upstart
208
- - SysV Init
209
- - Systemd
210
- - Launchd
211
-
212
- ### Links
213
-
214
- - Documentation: http://rubydoc.info/github/Gurpartap/cognizant/frames
215
- - Source: https://github.com/Gurpartap/cognizant
216
- - Rubygems: https://rubygems.org/gems/cognizant
217
-
218
- ## About
219
-
220
- Cognizant is a project of [Gurpartap Singh](http://gurpartap.com/). Feel free
221
- to get in touch.
@@ -1,28 +0,0 @@
1
- # 2 slave instances to master at port 6000.
2
- 3.times do |i|
3
- Cognizant.monitor("redis-server-600#{i}") do |process|
4
- process.group = "redis"
5
- # process.uid = "redis"
6
- # process.gid = "redis"
7
- process.start_command = "redis-server -"
8
- process.ping_command = "redis-cli -p 600#{i} PING"
9
-
10
- slaveof = i == 0 ? "" : "slaveof 127.0.0.1 6000"
11
- process.start_with_input = <<-heredoc
12
- daemonize no
13
- port 600#{i}
14
- #{slaveof}
15
- heredoc
16
-
17
- # process.on :always_true, :every => 2.seconds, :times => 3 do |p|
18
- # `say "Boom!"`
19
- # end
20
-
21
- process.check(:flapping, :times => 2, :within => 30.seconds, :retry_after => 7.seconds)
22
- process.check(:cpu_usage, :every => 3.seconds, :above => 60, :times => 3, :do => :restart)
23
-
24
- process.check(:memory_usage, :every => 5.seconds, :above => 100.megabytes, :times => [3, 5]) do |p|
25
- p.restart # Restart is the default anyways.
26
- end
27
- end
28
- end
@@ -1,10 +0,0 @@
1
- 5.times do |i|
2
- Cognizant.monitor "resque-worker-#{i}" do |process|
3
- process.group = "resque"
4
- process.uid = "deploy"
5
- process.gid = "deploy"
6
- process.chdir = "/apps/example/current/"
7
- process.env = { "QUEUE" => "*", "RACK_ENV" => "production" }
8
- process.start_command = "bundle exec rake resque:work"
9
- end
10
- end
Binary file
Binary file
Binary file
@@ -1,33 +0,0 @@
1
- require "logger"
2
-
3
- module Cognizant
4
- module Logging
5
- extend self
6
-
7
- def add_log_adapter(file)
8
- @files ||= Array.new
9
- @files << file unless @files.include?(file)
10
- @logger = nil
11
- end
12
-
13
- def log
14
- @files ||= Array.new($stdout)
15
- @logger ||= Logger.new(Files.new(*@files))
16
- end
17
- alias :logger :log
18
-
19
- class Files
20
- def initialize(*files)
21
- @files = files
22
- end
23
-
24
- def write(*args)
25
- @files.each { |t| t.write(*args) }
26
- end
27
-
28
- def close
29
- @files.each(&:close)
30
- end
31
- end
32
- end
33
- end
@@ -1,57 +0,0 @@
1
- require "cognizant/util/rotational_array"
2
-
3
- module Cognizant
4
- class Process
5
- module Conditions
6
- class Flapping < TriggerCondition
7
- TRIGGER_STATES = [:starting, :restarting]
8
-
9
- attr_accessor :times, :within, :retry_after
10
- attr_reader :timeline
11
-
12
- def initialize(process, options = {})
13
- options = { :times => 5, :within => 1, :retry_after => 5 }.merge(options)
14
-
15
- options.each_pair do |name, value|
16
- self.send("#{name}=", value) if self.respond_to?("#{name}=")
17
- end
18
-
19
- @timeline = Util::RotationalArray.new(@times)
20
- super
21
- end
22
-
23
- def notify(transition)
24
- if TRIGGER_STATES.include?(transition.to_name)
25
- self.timeline << Time.now.to_i
26
- self.check_flapping
27
- end
28
- end
29
-
30
- def reset!
31
- @timeline.clear
32
- super
33
- end
34
-
35
- def check_flapping
36
- # The process has not flapped if we haven't encountered enough incidents.
37
- return unless (@timeline.compact.length == self.times)
38
-
39
- # Check if the incident happend within the timeframe.
40
- duration = (@timeline.last - @timeline.first) <= self.within
41
-
42
- if duration
43
- puts "Flapping detected: retrying in #{self.retry_after} seconds"
44
-
45
- self.schedule_event(:start, self.retry_after) unless self.retry_after == 0 # retry_after zero means "do not retry, ever".
46
- self.schedule_event(:unmonitor, 0)
47
-
48
- @timeline.clear
49
-
50
- # This will prevent a transition from happening in the process state_machine.
51
- throw :halt
52
- end
53
- end
54
- end
55
- end
56
- end
57
- end
@@ -1,52 +0,0 @@
1
- module Cognizant
2
- class Process
3
- module Conditions
4
- class TriggerCondition
5
- attr_accessor :process, :mutex, :scheduled_events
6
-
7
- def initialize(process, options = {})
8
- self.process = process
9
- self.mutex = Mutex.new
10
- self.scheduled_events = []
11
- end
12
-
13
- def reset!
14
- self.cancel_all_events
15
- end
16
-
17
- def notify(transition)
18
- raise "Implement in subclass"
19
- end
20
-
21
- def dispatch!(event)
22
- self.process.dispatch!(event, self.class.name.split("::").last)
23
- end
24
-
25
- def schedule_event(event, delay)
26
- # TODO: Maybe wrap this in a ScheduledEvent class with methods like cancel.
27
- thread = Thread.new(self) do |trigger|
28
- begin
29
- sleep delay.to_f
30
- trigger.dispatch!(event)
31
- trigger.mutex.synchronize do
32
- trigger.scheduled_events.delete_if { |_, thread| thread == Thread.current }
33
- end
34
- rescue StandardError => e
35
- puts(e)
36
- puts(e.backtrace.join("\n"))
37
- end
38
- end
39
-
40
- self.scheduled_events.push([event, thread])
41
- end
42
-
43
- def cancel_all_events
44
- puts "Canceling all scheduled events"
45
- self.mutex.synchronize do
46
- self.scheduled_events.each {|_, thread| thread.kill}
47
- end
48
- end
49
- end
50
- end
51
- end
52
- end
@@ -1,14 +0,0 @@
1
- require "cognizant/server/daemon"
2
-
3
- module Cognizant
4
- module Server
5
- def self.start(options = {})
6
- @daemon = Cognizant::Server::Daemon.new(options)
7
- @daemon.bootup
8
- end
9
-
10
- def self.daemon
11
- @daemon
12
- end
13
- end
14
- end
@@ -1,80 +0,0 @@
1
- require "json"
2
- require "cognizant/system"
3
-
4
- module Cognizant
5
- module Server
6
- module Commands
7
- def self.load(config_file)
8
- Cognizant::Server.daemon.load(config_file)
9
- # yield("OK")
10
- end
11
-
12
- def self.status(*args)
13
- output_processes = []
14
- if args.size > 0
15
- Cognizant::Server.daemon.processes.values.each do |process|
16
- output_processes << process if args.include?(process.name) or args.include?(process.group)
17
- end
18
- if output_processes.size == 0
19
- raise("ERROR: No such process")
20
- # yield("OK")
21
- end
22
- else
23
- output_processes = Cognizant::Server.daemon.processes.values
24
- end
25
-
26
- output = []
27
- output_processes.each do |process|
28
- pid = process.read_pid
29
- output << {
30
- "Process" => process.name,
31
- "PID" => pid,
32
- "Group" => process.group,
33
- "State" => process.state,
34
- "Since" => process.last_transition_time,
35
- "% CPU" => System.cpu_usage(pid).to_f,
36
- "Memory" => System.memory_usage(pid).to_f # in KBs.
37
- }
38
- end
39
- yield(output.to_json)
40
- # yield("OK")
41
- end
42
-
43
- %w(monitor start stop restart unmonitor).each do |action|
44
- class_eval <<-END
45
- def self.#{action}(*args)
46
- unless args.size > 0
47
- raise("ERROR: Missing process name")
48
- return # yield("OK")
49
- end
50
- output_processes = []
51
- Cognizant::Server.daemon.processes.values.each do |process|
52
- if args.include?(process.name) or args.include?(process.group)
53
- output_processes << process
54
- end
55
- end
56
-
57
- if output_processes.size == 0
58
- raise("ERROR: No such process")
59
- # yield("OK")
60
- else
61
- output_processes.each do |process|
62
- process.handle_user_command(:#{action})
63
- end
64
- end
65
- end
66
- END
67
- end
68
-
69
- def self.shutdown
70
- Cognizant::Server.daemon.shutdown
71
- # yield("OK")
72
- end
73
-
74
- def self.method_missing(command, *args)
75
- raise("ERROR: Unknown command '#{command}'")
76
- # yield("OK")
77
- end
78
- end
79
- end
80
- end