cognizant 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .DS_Store
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --asset images:images
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in cognizant.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Gurpartap Singh
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,160 @@
1
+ # Cognizant
2
+
3
+ Cognizant is a system utility that supervises your processes, ensuring their
4
+ state based on a flexible criteria.
5
+
6
+ In simpler terms, cognizant keeps your processes up and running. It ensures
7
+ that something productive (like restart) is done when the process being
8
+ monitored matches a certain condition (like RAM, CPU usage or a custom
9
+ condition).
10
+
11
+ PS: Although the core works efficiently, yet the command interface to the
12
+ utility, documentation and some other features need a lot of work.
13
+ Contributions will be overwhelmingly welcomed!
14
+
15
+ PS2: This README is written as a roadmap for the features cognizant would
16
+ ideally provide. Some of them might not yet be implemented (e.g. conditions).
17
+
18
+ Cognizant can be used to monitor any long running process, including the
19
+ following examples:
20
+
21
+ - Web servers (Nginx, Apache httpd, WebSocket, etc.)
22
+ - Databases (Redis, MongoDB, MySQL, PostgreSQL, etc.)
23
+ - Job workers (Resque, Sidekiq, etc.)
24
+ - Message queue applications (RabbitMQ, Beanstalkd, etc.)
25
+ - Logs collection daemon
26
+ - Or any other program that needs to keep running
27
+
28
+ ## Monitoring
29
+
30
+ Cognizant can be used to monitor an application process' state so that it
31
+ is running at all times. When cognizant is instructed to monitor a process,
32
+ by default, the process will automatically be started. These processes are
33
+ automatically monitored for their state. i.e. a process is automatically
34
+ started again if it stops unexpectedly.
35
+
36
+ ## Conditions
37
+
38
+ Conditions provide a way to monitor and act on more than just the state of a
39
+ process. For example, conditions can monitor the resource utilization (RAM,
40
+ CPU, etc.) of the application process and restart it if it matches a
41
+ condition.
42
+
43
+ ## Getting started
44
+
45
+ Cognizant is written in the Ruby programming language, and hence depends on
46
+ it. Ensure that you have Ruby 1.9+ installed and run:
47
+
48
+ $ gem install cognizant
49
+
50
+ ## Architecture overview
51
+
52
+ _____
53
+ ___________ |
54
+ ____________ ____________ | | |
55
+ | | | | -----> | Process A | |
56
+ | Admin | | Monitoring | |___________| |
57
+ | Utility | -----> | Daemon | ___________ | -- Managed Processes
58
+ | >_ | | | | | |
59
+ |____________| |____________| -----> | Process B | |
60
+ |___________| |
61
+ _____|
62
+
63
+ Since cognizant administration and process monitoring are two separate concerns, they are serviced by separate applications, `cognizant` and `cognizantd`, respectively.
64
+
65
+ ## Starting the monitoring daemon
66
+
67
+ Cognizant runs the monitoring essentials through the `cognizantd` daemon application, which also maintains a server for accepting commands from the `cognizant` administration utility.
68
+
69
+ 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:
70
+
71
+ $ sudo cognizantd # ubuntu, debian, os x, etc.
72
+ $ su -c 'cognizantd' # amazon linux, centos, rhel, etc.
73
+
74
+ To start without superuser access, specify these file and directory config variables to where the user starting it has write access:
75
+
76
+ $ cognizantd ~/.cognizant/cognizantd.yml
77
+
78
+ # assuming that:
79
+
80
+ $ cat ~/.cognizant/cognizantd.yml
81
+ ---
82
+ socket: ~/.cognizant/cognizantd.sock
83
+ pidfile: ~/.cognizant/cognizantd.pid
84
+ logfile: ~/.cognizant/cognizantd.log
85
+ pids_dir: ~/.cognizant/pids/
86
+ logs_dir: ~/.cognizant/logs/
87
+
88
+ or
89
+
90
+ # pass config directly into the daemon's STDIN
91
+ $ echo <<EOF | cognizantd -
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
+ EOF
99
+
100
+ ## Using the administration utility
101
+
102
+ 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.
103
+
104
+ Here's how you tell cognizant to start monitoring a new process:
105
+
106
+ $ cognizant monitor --name resque-worker-1 \
107
+ --group resque \
108
+ --chdir /apps/example/current/ \
109
+ --start_command "bundle exec rake resque:work"
110
+
111
+ Now check it's status:
112
+
113
+ $ cognizant status resque-worker-1
114
+ ---
115
+ resque-worker-1: {
116
+ state: running,
117
+ uptime: 3600 # 1 hour
118
+ }
119
+
120
+ Or check status of all processes:
121
+
122
+ $ cognizant status resque-worker-1
123
+ ---
124
+ redis-server: {
125
+ state: running,
126
+ uptime: 5400 # 1 hour 30 minutes
127
+ },
128
+ resque-worker-1: {
129
+ group: resque,
130
+ state: running,
131
+ uptime: 3600 # 1 hour
132
+ },
133
+ resque-worker-2: {
134
+ group: resque,
135
+ state: stoppped,
136
+ uptime: 0
137
+ }
138
+
139
+ ### Works anywhere
140
+
141
+ Cognizant can be used on any operating system where Ruby 1.9+ works.
142
+
143
+ ### What are the other programs similar to cognizant?
144
+
145
+ - Monit
146
+ - God (Ruby)
147
+ - Bluepill (Ruby)
148
+ - Supervisord (Python)
149
+ - Upstart
150
+ - SysV Init
151
+ - Systemd
152
+ - Launchd
153
+
154
+ ### Which one of these should I be using?
155
+
156
+ The one that gets your job done efficiently.
157
+
158
+ ### What does the term "cognizant" mean?
159
+
160
+ If it matters, cognizant means "having knowledge or being aware of", according to Apple's Dictionary.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
data/bin/cognizant ADDED
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Set the process name.
4
+ $0 = File.basename(__FILE__)
5
+
6
+ # Flush standard output/error immediately.
7
+ $stdout.sync = true
8
+ $stderr.sync = true
9
+
10
+ begin
11
+ require 'optparse'
12
+
13
+ options = {
14
+ socket: "/var/run/cognizant/cognizantd.sock"
15
+ }
16
+
17
+ OptionParser.new do |opts|
18
+ opts.banner = <<-EOF
19
+ Usage:
20
+ cognizant [OPTIONS] command
21
+
22
+ Options:
23
+ EOF
24
+
25
+ opts.on("-s FILE", "--socket FILE", String, "The socket lock file for the server") do |value|
26
+ options[:socket] = value
27
+ end
28
+
29
+ opts.on("-b ADDRESS", "--bind-address ADDRESS", String, "The interface to bind the TCP server to.") do |value|
30
+ options[:bind_address] = value
31
+ end
32
+
33
+ opts.on("-p PORT", "--port PORT", Integer, "The TCP port to start the server with.") do |value|
34
+ options[:port] = value
35
+ end
36
+
37
+ opts.on("--username USERNAME", String, "Username for securing server access.") do |value|
38
+ options[:username] = value
39
+ end
40
+
41
+ opts.on("--password PASSWORD", String, "Password to accompany the username.") do |value|
42
+ options[:password] = value
43
+ end
44
+
45
+ opts.on("-t", "--trace", "Turn on tracing, enable full backtrace.") do
46
+ options[:trace] = true
47
+ end
48
+
49
+ opts.on("-v", "--version", "Print the version number and exit.") do
50
+ require 'cognizant/version'
51
+ $stdout.puts Cognizant::VERSION
52
+ exit(0)
53
+ end
54
+ end.parse!
55
+
56
+ require 'cognizant'
57
+ Cognizant.add_process(options)
58
+ end
data/bin/cognizantd ADDED
@@ -0,0 +1,124 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Set the process name.
4
+ $0 = File.basename(__FILE__)
5
+
6
+ # Flush standard output/error immediately.
7
+ $stdout.sync = true
8
+ $stderr.sync = true
9
+
10
+ begin
11
+ require 'optparse'
12
+
13
+ options = {}
14
+
15
+ OptionParser.new do |opts|
16
+ opts.banner = <<-EOF
17
+ Usage:
18
+ cognizantd [OPTIONS] [CONFIG | -]
19
+
20
+ Options:
21
+ EOF
22
+
23
+ opts.on("--[no-]daemonize", "Whether or not to daemonize cognizant into background.") do |value|
24
+ options[:daemonize] = value
25
+ end
26
+
27
+ opts.on("--pidfile FILE", String, "The pid lock file for the daemon.") do |value|
28
+ options[:pidfile] = value
29
+ end
30
+
31
+ opts.on("--logfile FILE", String, "The file to log the daemon's operations information into.") do |value|
32
+ options[:logfile] = value
33
+ end
34
+
35
+ opts.on("--loglevel LEVEL", String, "The level of information to log.") do |value|
36
+ options[:loglevel] = value
37
+ end
38
+
39
+ opts.on("--env ENV", String, "Environment variables for managed processes to inherit.") do |value|
40
+ options[:env] = value
41
+ end
42
+
43
+ opts.on("--chdir DIRECTORY", String, "The current working directory for the managed processes to start with.") do |value|
44
+ options[:chdir] = value
45
+ end
46
+
47
+ opts.on("--umask UMASK", String, "Permission mode limitations for file and directory creation.") do |value|
48
+ options[:umask] = value
49
+ end
50
+
51
+ opts.on("--user USER", String, "Run the daemon and managed processes as the given user.") do |value|
52
+ options[:user] = value
53
+ end
54
+
55
+ opts.on("--group GROUP", String, "Run the daemon and managed processes as the given user group.") do |value|
56
+ options[:group] = value
57
+ end
58
+
59
+ opts.on("--pids-dir DIRECTORY", String, "Directory to store the pid files of managed processes, when required.") do |value|
60
+ options[:pids_dir] = value
61
+ end
62
+
63
+ opts.on("--logs-dir DIRECTORY", String, "Directory to store the log files of managed processes, when required.") do |value|
64
+ options[:logs_dir] = value
65
+ end
66
+
67
+ opts.on("-s FILE", "--socket FILE", String, "The socket lock file for the server") do |value|
68
+ options[:socket] = value
69
+ end
70
+
71
+ opts.on("-b ADDRESS", "--bind-address ADDRESS", String, "The interface to bind the TCP server to.") do |value|
72
+ options[:bind_address] = value
73
+ end
74
+
75
+ opts.on("-p PORT", "--port PORT", Integer, "The TCP port to start the server with.") do |value|
76
+ options[:port] = value
77
+ end
78
+
79
+ opts.on("--username USERNAME", String, "Username for securing server access.") do |value|
80
+ options[:username] = value
81
+ end
82
+
83
+ opts.on("--password PASSWORD", String, "Password to accompany the username.") do |value|
84
+ options[:password] = value
85
+ end
86
+
87
+ opts.on("-t", "--trace", "Turn on tracing, enable full backtrace.") do
88
+ options[:trace] = true
89
+ end
90
+
91
+ opts.on("-v", "--version", "Print the version number and exit.") do
92
+ require 'cognizant/version'
93
+ $stdout.puts Cognizant::VERSION
94
+ exit(0)
95
+ end
96
+ end.parse!
97
+
98
+ if config_file = ARGV.shift
99
+ if config_file.eql?("-")
100
+ config = YAML.load(ARGF.read)
101
+ else
102
+ config = YAML.load_file(config_file)
103
+ end
104
+ config = config.inject({}) { |c,(k,v)| c[k.gsub("-", "_").to_sym] = v; c }
105
+ options = config.merge(options)
106
+ end
107
+
108
+ require 'cognizant/server'
109
+ Cognizant::Server.start(options)
110
+ rescue => exception
111
+ if exception.instance_of?(SystemExit)
112
+ raise
113
+ else
114
+ $stderr.puts "ERROR: While executing #{$0} ... (#{exception.class})"
115
+ $stderr.puts " #{exception.message}\n\n"
116
+ if options[:trace]
117
+ $stderr.puts exception.backtrace.join("\n")
118
+ $stderr.puts "\n(See usage by running #{$0} with --help)"
119
+ else
120
+ $stderr.puts "(See full trace by running #{$0} with --trace)"
121
+ end
122
+ exit(1)
123
+ end
124
+ end
data/cognizant.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/cognizant/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Gurpartap Singh"]
6
+ gem.email = ["contact@gurpartap.com"]
7
+ gem.description = "Process monitoring and management framework"
8
+ gem.summary = "Cognizant is an advanced and efficient process monitoring and management framework."
9
+ gem.homepage = "http://gurpartap.github.com/cognizant/"
10
+
11
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ gem.files = `git ls-files`.split("\n")
13
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ gem.name = "cognizant"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Cognizant::VERSION
17
+
18
+ gem.add_development_dependency "rake"
19
+ gem.add_development_dependency "redcarpet"
20
+ gem.add_development_dependency "yard"
21
+
22
+ gem.add_dependency "eventmachine"
23
+ gem.add_dependency "state_machine"
24
+ end
@@ -0,0 +1,23 @@
1
+ ---
2
+ daemonize: false
3
+ pidfile: ~/.cognizant/cognizantd.pid
4
+ logfile: ~/.cognizant/cognizantd.log
5
+ socket: ~/.cognizant/cognizantd.sock
6
+ pids_dir: ~/.cognizant/pids/
7
+ logs_dir: ~/.cognizant/logs/
8
+
9
+ monitor: {
10
+ redis-server: {
11
+ start_command: /usr/local/bin/redis-server -,
12
+ start_with_input: daemonize no,
13
+ ping_command: redis-cli PING,
14
+ autostart: true,
15
+ stop_timeout: 5,
16
+ start_timeout: 2,
17
+ restart_timeout: 2
18
+ },
19
+ sleep-10: {
20
+ start_command: sleep 10,
21
+ autostart: true
22
+ }
23
+ }
@@ -0,0 +1,17 @@
1
+ # 4 slave instances to master at port 6000.
2
+ 5.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
+ end
17
+ end
@@ -0,0 +1,10 @@
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
data/images/logo.png ADDED
Binary file
data/images/logo.pxm ADDED
Binary file
@@ -0,0 +1,9 @@
1
+ module Cognizant
2
+ module Client
3
+ class CLI
4
+ def initialize(command)
5
+ puts "got #{command}"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,33 @@
1
+ require "eventmachine"
2
+
3
+ module Cognizant
4
+ module Client
5
+ class Interface
6
+ def initialize(server = nil)
7
+ @sock = EventMachine.connect(server, Client::Interface::Handler)
8
+ end
9
+
10
+ def method_missing(*meth, &blk)
11
+ @sock.queue << blk
12
+ @sock.send_object(meth)
13
+ end
14
+
15
+ class Handler < EventMachine::Connection
16
+ # include EventMachine::Protocols::SASLauthclient
17
+ include EventMachine::Protocols::ObjectProtocol
18
+
19
+ attr_reader :queue
20
+
21
+ def post_init
22
+ @queue = []
23
+ end
24
+
25
+ def receive_object(obj)
26
+ if callback = @queue.shift
27
+ callback.call(obj)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,33 @@
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
@@ -0,0 +1,60 @@
1
+ module Cognizant
2
+ class Process
3
+ module Actions
4
+ module Restart
5
+ # Environment variables for process during restart.
6
+ # @return [Hash] Defaults to {}
7
+ attr_accessor :restart_env
8
+
9
+ # The command to run before the process is restarted. The exit status
10
+ # of this command determines whether or not to proceed.
11
+ # @return [String] Defaults to nil
12
+ attr_accessor :restart_before_command
13
+
14
+ # The command to restart the process with. This command can optionally
15
+ # be similar in behavior to the stop command, since the process will
16
+ # anyways be automatically started again, if autostart is set to true.
17
+ # @return [String] Defaults to nil
18
+ attr_accessor :restart_command
19
+
20
+ # The signals to pass to the process one by one attempting to restart
21
+ # it. Each signal is passed within the timeout period over equally
22
+ # distributed intervals (min. 2 seconds). Override with signals without
23
+ # "KILL" to never force kill the process.
24
+ # e.g. ["TERM", "INT"]
25
+ # @return [Array] Defaults to ["TERM", "INT", "KILL"]
26
+ attr_accessor :restart_signals
27
+
28
+ # The grace time period in seconds for the process to stop within
29
+ # (for restart). Covers the time period for the restart command or
30
+ # signals. After the timeout is over, the process is checked for
31
+ # running status and if not stopped, it re-enters the auto start
32
+ # lifecycle based on conditions.
33
+ # @return [String] Defaults to 10
34
+ attr_accessor :restart_timeout
35
+
36
+ # The command to run after the process is restarted.
37
+ # @return [String] Defaults to nil
38
+ attr_accessor :restart_after_command
39
+
40
+ def restart_process
41
+ result_handler = Proc.new do |result|
42
+ # If it is a boolean and value is true OR if it's an execution result and it succeeded.
43
+ if (!!result == result and result) or (result.respond_to?(:succeeded?) and result.succeeded?)
44
+ unlink_pid unless pid_running?
45
+ end
46
+ end
47
+ execute_action(
48
+ result_handler,
49
+ env: (self.env || {}).merge(self.restart_env || {}),
50
+ before: self.restart_before_command,
51
+ command: self.restart_command,
52
+ signals: self.restart_signals,
53
+ after: self.restart_after_command,
54
+ timeout: self.restart_timeout
55
+ )
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end