cognizant 0.0.1

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