cognizant 0.0.2 → 0.0.3

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