sv 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +34 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +2 -0
- data/bin/sv +74 -0
- data/lib/sv/api.rb +111 -0
- data/lib/sv/base.rb +15 -0
- data/lib/sv/cli/server.rb +69 -0
- data/lib/sv/config.rb +102 -0
- data/lib/sv/custom_logger.rb +25 -0
- data/lib/sv/error.rb +4 -0
- data/lib/sv/ext/net_http.rb +19 -0
- data/lib/sv/ext/string.rb +29 -0
- data/lib/sv/job.rb +139 -0
- data/lib/sv/logger.rb +22 -0
- data/lib/sv/server.rb +97 -0
- data/lib/sv/status.rb +32 -0
- data/lib/sv/supervisor/config.rb +62 -0
- data/lib/sv/templates/job.erb +19 -0
- data/lib/sv/templates/supervisor.conf.erb +15 -0
- data/lib/sv/version.rb +3 -0
- data/lib/sv.rb +5 -0
- data/sv.gemspec +23 -0
- metadata +97 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: fc419ac67937b89b2a0519205e4b38b6346d73b5
|
4
|
+
data.tar.gz: 29b88973f788938d4410b970778806da8b9fe7a7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7622979c1a4afe44cded6315fda8f98c9368e98779edb7776b2c6e13c4bc9d50697fd07fff902b0bb623d9beee65917db38914416f01a1b22f2431ae7f3dcfdc
|
7
|
+
data.tar.gz: b661919407a5a9f97a22bf1ce23cc79cc2073008206f187e9b4b694ef9ab7efb4c941462ffd71e5a8de529d22b41f4ea195251967f9623e293fae1bb095876de
|
data/.gitignore
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
/.config
|
4
|
+
/coverage/
|
5
|
+
/InstalledFiles
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/test/tmp/
|
9
|
+
/test/version_tmp/
|
10
|
+
/tmp/
|
11
|
+
|
12
|
+
## Specific to RubyMotion:
|
13
|
+
.dat*
|
14
|
+
.repl_history
|
15
|
+
build/
|
16
|
+
|
17
|
+
## Documentation cache and generated files:
|
18
|
+
/.yardoc/
|
19
|
+
/_yardoc/
|
20
|
+
/doc/
|
21
|
+
/rdoc/
|
22
|
+
|
23
|
+
## Environment normalisation:
|
24
|
+
/.bundle/
|
25
|
+
/lib/bundler/man/
|
26
|
+
|
27
|
+
# for a library or gem, you might want to ignore these files since the code is
|
28
|
+
# intended to run in multiple environments; otherwise, check them in:
|
29
|
+
# Gemfile.lock
|
30
|
+
# .ruby-version
|
31
|
+
# .ruby-gemset
|
32
|
+
|
33
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
34
|
+
.rvmrc
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Neeraj
|
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,29 @@
|
|
1
|
+
# Sv
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'sv'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install sv
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it ( https://github.com/[my-github-username]/sv/fork )
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/bin/sv
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
|
3
|
+
$LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
Signal.trap("INT") { exit 1 }
|
6
|
+
|
7
|
+
require 'optparse'
|
8
|
+
require 'sv/error'
|
9
|
+
require 'sv/version'
|
10
|
+
require 'sv/logger'
|
11
|
+
require 'pathname'
|
12
|
+
require 'sv/cli/server'
|
13
|
+
|
14
|
+
logger = ::Sv::Logger.logger
|
15
|
+
options = {}
|
16
|
+
|
17
|
+
opts_parser = OptionParser.new do |opts|
|
18
|
+
|
19
|
+
banner = []
|
20
|
+
banner << "Usage: sv [global options] command [options] args"
|
21
|
+
banner << "Commands: start stop start restart status print-config"
|
22
|
+
|
23
|
+
opts.banner = banner.join("\n")
|
24
|
+
|
25
|
+
opts.on("-d", "--app-dir [APP DIR]" , "Set app dir") do |d|
|
26
|
+
path = Pathname.new(d)
|
27
|
+
raise ::Sv::Error, "app_dir path must be absolute" if path.relative?
|
28
|
+
options[:app_dir] = path
|
29
|
+
end
|
30
|
+
|
31
|
+
opts.on("-v", "--version", "Show version") do |v|
|
32
|
+
puts ::Sv::VERSION
|
33
|
+
exit
|
34
|
+
end
|
35
|
+
|
36
|
+
opts.on("--debug", "Show debug messages") do
|
37
|
+
options[:debug] = true
|
38
|
+
logger.level = ::Logger::DEBUG
|
39
|
+
end
|
40
|
+
|
41
|
+
opts.on("--trace", "Show debug messages and exception stack trace") do
|
42
|
+
options[:debug] = true
|
43
|
+
options[:trace] = true
|
44
|
+
logger.level = ::Logger::DEBUG
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
48
|
+
puts opts
|
49
|
+
exit
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
begin
|
54
|
+
opts_parser.order!(ARGV)
|
55
|
+
app_dir = options[:app_dir] || Dir.pwd
|
56
|
+
|
57
|
+
if ARGV.size == 0
|
58
|
+
puts opts_parser
|
59
|
+
exit
|
60
|
+
end
|
61
|
+
cli = ::Sv::Cli::Server.new(app_dir, argv: ARGV.dup)
|
62
|
+
cli.run
|
63
|
+
|
64
|
+
rescue OptionParser::InvalidOption, OptionParser::MissingArgument, ::Sv::Error => e
|
65
|
+
cause = e.cause
|
66
|
+
if options[:trace]
|
67
|
+
puts cause
|
68
|
+
cause ? (raise cause) : (raise e)
|
69
|
+
else
|
70
|
+
logger.debug "#{cause.message}" if cause
|
71
|
+
logger.error "#{e.message}"
|
72
|
+
abort
|
73
|
+
end
|
74
|
+
end
|
data/lib/sv/api.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'sv/base'
|
2
|
+
require 'sv/error'
|
3
|
+
require 'sv/ext/net_http'
|
4
|
+
require 'xmlrpc/client'
|
5
|
+
require 'ostruct'
|
6
|
+
|
7
|
+
module Sv
|
8
|
+
class Api < ::Sv::Base
|
9
|
+
|
10
|
+
attr_reader :socket_path
|
11
|
+
|
12
|
+
def initialize(socket_path)
|
13
|
+
@socket_path = socket_path
|
14
|
+
end
|
15
|
+
|
16
|
+
def start_jobs(wait: true)
|
17
|
+
wait ? start_jobs_in_foreground : start_jobs_in_background
|
18
|
+
end
|
19
|
+
|
20
|
+
def shutdown
|
21
|
+
call "supervisor.shutdown"
|
22
|
+
close_connection
|
23
|
+
end
|
24
|
+
|
25
|
+
def start_job(group, name)
|
26
|
+
call "supervisor.startProcess", "#{group}:#{name}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def stop_job(group, name)
|
30
|
+
call "supervisor.stopProcess" if not job_stopped?(group, name)
|
31
|
+
rescue XMLRPC::FaultException => e
|
32
|
+
return true if e.faultString == "NOT_RUNNING"
|
33
|
+
raise e
|
34
|
+
end
|
35
|
+
|
36
|
+
def job_stopped?(group, name)
|
37
|
+
job = call "supervisor.getProcessInfo", "#{group}:#{name}"
|
38
|
+
job["state"] == 0 ? true : false
|
39
|
+
end
|
40
|
+
|
41
|
+
def pid
|
42
|
+
call "supervisor.getPID"
|
43
|
+
end
|
44
|
+
|
45
|
+
def print_status
|
46
|
+
puts "pid #{pid}"
|
47
|
+
jobs = self.jobs
|
48
|
+
name_width = jobs.map { |j| j.name.size }.max
|
49
|
+
template = "%-#{name_width}s %-10s %-7s %-20s\n"
|
50
|
+
printf template, "name", "state", "pid", "uptime"
|
51
|
+
puts "-"*(name_width + 10 + 7 + 20 + 2)
|
52
|
+
jobs.each do |job|
|
53
|
+
logger.debug { require 'pp'; PP.pp job.to_h, out="" ; out }
|
54
|
+
printf template, job.name, job.statename, job.pid, uptime(job.start)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def start_jobs_in_background
|
59
|
+
call "supervisor.startAllProcesses", "wait=false"
|
60
|
+
end
|
61
|
+
|
62
|
+
def start_jobs_in_foreground
|
63
|
+
jobs.each do |j|
|
64
|
+
printf "#{j.name}: starting"
|
65
|
+
start_job j.group, j.name
|
66
|
+
puts "\r#{j.name}: started "
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def jobs
|
71
|
+
jobs_array = call "supervisor.getAllProcessInfo"
|
72
|
+
jobs = jobs_array.map { |j| OpenStruct.new(j) }
|
73
|
+
end
|
74
|
+
|
75
|
+
def close_connection
|
76
|
+
@rpc = nil
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def rpc
|
83
|
+
@rpc ||= ::XMLRPC::Client.new(socket_path, "/RPC2")
|
84
|
+
end
|
85
|
+
|
86
|
+
def call(*args)
|
87
|
+
output = rpc.call(*args)
|
88
|
+
return output
|
89
|
+
rescue XMLRPC::FaultException => e
|
90
|
+
puts
|
91
|
+
puts e.message
|
92
|
+
raise ::Sv::Error, "error running command #{args[0]}"
|
93
|
+
end
|
94
|
+
|
95
|
+
def uptime(started_at)
|
96
|
+
return "-" if started_at.to_i == 0
|
97
|
+
uptime = (Time.now.to_i - started_at).to_i
|
98
|
+
mm, ss = uptime.divmod(60)
|
99
|
+
hh, mm = mm.divmod(60)
|
100
|
+
dd, hh = hh.divmod(24)
|
101
|
+
if dd == 1
|
102
|
+
"%d day, %02d::%02d::%02d" % [dd, hh, mm, ss]
|
103
|
+
elsif dd > 1
|
104
|
+
"%d days, %02d::%02d::%02d" % [dd, hh, mm, ss]
|
105
|
+
else
|
106
|
+
"%02d::%02d::%02d" % [hh, mm, ss]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
end
|
data/lib/sv/base.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'sv/server'
|
2
|
+
|
3
|
+
module Sv::Cli
|
4
|
+
|
5
|
+
class Server
|
6
|
+
|
7
|
+
attr_reader :app_dir, :argv
|
8
|
+
|
9
|
+
def initialize(app_dir, argv: ARGV)
|
10
|
+
@app_dir = app_dir
|
11
|
+
@argv = argv
|
12
|
+
end
|
13
|
+
|
14
|
+
def run
|
15
|
+
opts.parse!(argv)
|
16
|
+
command = argv.shift.to_sym
|
17
|
+
case command
|
18
|
+
when :start, :restart
|
19
|
+
server.send command, auto_start: options[:auto_start], wait: options[:wait]
|
20
|
+
when :'print-config'
|
21
|
+
server.send :print_config
|
22
|
+
when :stop, :status
|
23
|
+
server.send command
|
24
|
+
when :help
|
25
|
+
help argv.shift
|
26
|
+
else
|
27
|
+
raise ::Sv::Error, "no such command #{command}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def help(command)
|
34
|
+
command = command.to_sym if command
|
35
|
+
case command
|
36
|
+
when :start, :restart
|
37
|
+
banner = []
|
38
|
+
banner << "sv [global options] #{command} [options]"
|
39
|
+
banner = banner.join("\n")
|
40
|
+
opts.banner = banner
|
41
|
+
puts opts
|
42
|
+
else
|
43
|
+
puts "no help available for command: #{command}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def server
|
48
|
+
@server ||= ::Sv::Server.new(app_dir)
|
49
|
+
end
|
50
|
+
|
51
|
+
def opts
|
52
|
+
@opts ||= OptionParser.new do |opts|
|
53
|
+
opts.on("-a", "--auto-start" , "auto start jobs") do
|
54
|
+
options[:auto_start] = true
|
55
|
+
end
|
56
|
+
|
57
|
+
opts.on("-w", "--wait" , "wait for jobs to start successfully") do
|
58
|
+
options[:wait] = true
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def options
|
64
|
+
@options ||= {}
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
data/lib/sv/config.rb
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'sv/logger'
|
2
|
+
require 'sv/error'
|
3
|
+
require 'sv/job'
|
4
|
+
|
5
|
+
module Sv
|
6
|
+
class Config
|
7
|
+
include Logger
|
8
|
+
|
9
|
+
attr_reader :app_dir
|
10
|
+
|
11
|
+
def initialize(app_dir)
|
12
|
+
@app_dir = app_dir
|
13
|
+
@instances = {}
|
14
|
+
@working_dir = app_dir
|
15
|
+
end
|
16
|
+
|
17
|
+
def socket_path
|
18
|
+
@socket_path ||= "#{app_dir}/tmp/sockets/supervisor.sock"
|
19
|
+
end
|
20
|
+
|
21
|
+
def pidfile
|
22
|
+
@pidfile ||= "#{app_dir}/tmp/pids/supervisor.pid"
|
23
|
+
end
|
24
|
+
|
25
|
+
def logfile
|
26
|
+
@logfile ||= "#{app_dir}/log/supervisord.log"
|
27
|
+
end
|
28
|
+
|
29
|
+
def jobs
|
30
|
+
@jobs ||= begin
|
31
|
+
load_from_file
|
32
|
+
jobs_map.values
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def jobs_map
|
39
|
+
@jobs_map ||= {}
|
40
|
+
end
|
41
|
+
|
42
|
+
def instances(instances_map)
|
43
|
+
@instances = instances_map
|
44
|
+
end
|
45
|
+
|
46
|
+
def job(name, &block)
|
47
|
+
name = name.to_sym
|
48
|
+
j = jobs_map[name] || Job.new(name)
|
49
|
+
j.instance_eval &block
|
50
|
+
jobs_map[name] = j
|
51
|
+
end
|
52
|
+
|
53
|
+
def working_dir(working_dir)
|
54
|
+
@working_dir = working_dir
|
55
|
+
end
|
56
|
+
|
57
|
+
def load_from_file
|
58
|
+
load_jobs("#{app_dir}/config/jobs.yml", optional: true)
|
59
|
+
read_config("#{app_dir}/config/jobs.rb", optional: true)
|
60
|
+
read_config("#{app_dir}/config/sv.rb")
|
61
|
+
set_instances
|
62
|
+
set_working_dir
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
def set_instances
|
67
|
+
jobs_map.each do |name, job|
|
68
|
+
job.numprocs @instances[name] if @instances.key? name
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def set_working_dir
|
73
|
+
jobs_map.values.each do |job|
|
74
|
+
job.working_dir || job.working_dir(@working_dir)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def read_config(path, optional: false)
|
79
|
+
if not File.readable? path
|
80
|
+
raise ::Sv::Error, "config file #{path} missing" if not optional
|
81
|
+
return
|
82
|
+
end
|
83
|
+
instance_eval File.read(path), path
|
84
|
+
end
|
85
|
+
|
86
|
+
def load_jobs(path, optional: false)
|
87
|
+
if not File.readable? path
|
88
|
+
raise ::Sv::Error, "config file #{path} missing" if not optional
|
89
|
+
return
|
90
|
+
end
|
91
|
+
require 'yaml'
|
92
|
+
job_definitions = YAML.load_file(path)
|
93
|
+
job_definitions.each do |j|
|
94
|
+
name = j['name'].to_sym
|
95
|
+
job = Job.new(name)
|
96
|
+
job.update(j)
|
97
|
+
jobs_map[name] = job
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'sv/ext/string'
|
3
|
+
module Sv
|
4
|
+
class CustomLogger < ::Logger
|
5
|
+
|
6
|
+
def initialize(file)
|
7
|
+
super(file)
|
8
|
+
@level = ::Logger::INFO
|
9
|
+
end
|
10
|
+
|
11
|
+
def format_message(severity, timestamp, progname, msg)
|
12
|
+
case severity
|
13
|
+
when "INFO"
|
14
|
+
"#{msg}\n"
|
15
|
+
when "ERROR"
|
16
|
+
"#{severity.bold.red} #{msg}\n"
|
17
|
+
when "WARN"
|
18
|
+
"#{severity.downcase.bold.yellow} #{msg}\n"
|
19
|
+
else
|
20
|
+
"#{severity.downcase.bold.blue} #{msg}\n"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
data/lib/sv/error.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
module Net
|
5
|
+
class HTTP
|
6
|
+
alias_method :orig_connect, :connect
|
7
|
+
|
8
|
+
def connect
|
9
|
+
path = Pathname.new(address)
|
10
|
+
if path.exist?
|
11
|
+
@socket = Net::BufferedIO.new UNIXSocket.new address
|
12
|
+
on_connect
|
13
|
+
else
|
14
|
+
orig_connect
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class String
|
2
|
+
def colorize(color_code)
|
3
|
+
"\e[#{color_code}m#{self}\e[0m"
|
4
|
+
end
|
5
|
+
|
6
|
+
def bold
|
7
|
+
"\e[1m#{self}\e[0m"
|
8
|
+
end
|
9
|
+
|
10
|
+
def white
|
11
|
+
colorize(37)
|
12
|
+
end
|
13
|
+
|
14
|
+
def green
|
15
|
+
colorize(32)
|
16
|
+
end
|
17
|
+
|
18
|
+
def yellow
|
19
|
+
colorize(33)
|
20
|
+
end
|
21
|
+
|
22
|
+
def red
|
23
|
+
colorize(31)
|
24
|
+
end
|
25
|
+
|
26
|
+
def blue
|
27
|
+
colorize(34)
|
28
|
+
end
|
29
|
+
end
|
data/lib/sv/job.rb
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
module Sv
|
2
|
+
class Job
|
3
|
+
|
4
|
+
attr_reader :name
|
5
|
+
attr_writer :working_dir
|
6
|
+
|
7
|
+
def initialize(name)
|
8
|
+
set :name, name
|
9
|
+
end
|
10
|
+
|
11
|
+
def name
|
12
|
+
attributes.fetch :name
|
13
|
+
end
|
14
|
+
|
15
|
+
def command(*args)
|
16
|
+
set_or_get :command, args
|
17
|
+
end
|
18
|
+
|
19
|
+
def working_dir(*args)
|
20
|
+
set_or_get :working_dir, args
|
21
|
+
end
|
22
|
+
|
23
|
+
def env(*args)
|
24
|
+
set_or_get :env, args
|
25
|
+
end
|
26
|
+
|
27
|
+
def numprocs(*args)
|
28
|
+
set_or_get :numprocs, args do |v|
|
29
|
+
v.to_i
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def autorestart(*args)
|
34
|
+
set_or_get :autorestart, args
|
35
|
+
end
|
36
|
+
|
37
|
+
def startsecs(*args)
|
38
|
+
set_or_get :startsecs, args
|
39
|
+
end
|
40
|
+
|
41
|
+
def startretries(*args)
|
42
|
+
set_or_get :startretries, args
|
43
|
+
end
|
44
|
+
|
45
|
+
def stopsignal(*args)
|
46
|
+
set_or_get :stopsignal, args
|
47
|
+
end
|
48
|
+
|
49
|
+
def stopwaitsecs(*args)
|
50
|
+
set_or_get :stopwaitsecs, args
|
51
|
+
end
|
52
|
+
|
53
|
+
def killasgroup(*args)
|
54
|
+
set_or_get :killasgroup, args
|
55
|
+
end
|
56
|
+
|
57
|
+
def redirect_stderr(*args)
|
58
|
+
set_or_get :redirect_stderr, args
|
59
|
+
end
|
60
|
+
|
61
|
+
def stdout_logfile(*args)
|
62
|
+
set_or_get :stdout_logfile, args
|
63
|
+
end
|
64
|
+
|
65
|
+
def stderr_logfile(*args)
|
66
|
+
set_or_get :stderr_logfile, args
|
67
|
+
end
|
68
|
+
|
69
|
+
def update(attrs)
|
70
|
+
attrs.each do |key ,value|
|
71
|
+
set key, value
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def render
|
76
|
+
return if not attributes.values.all?
|
77
|
+
return if attributes[:numprocs] < 1
|
78
|
+
File.open(template) do |f|
|
79
|
+
erb = ERB.new(f.read, nil, '-')
|
80
|
+
erb.result(binding)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def attributes
|
87
|
+
@attributes ||= {
|
88
|
+
name: nil,
|
89
|
+
command: nil,
|
90
|
+
working_dir: nil,
|
91
|
+
env: "",
|
92
|
+
numprocs: 0,
|
93
|
+
autorestart: true,
|
94
|
+
startsecs: 1,
|
95
|
+
startretries: 3,
|
96
|
+
stopsignal: :TERM,
|
97
|
+
stopwaitsecs: 10,
|
98
|
+
killasgroup: true,
|
99
|
+
redirect_stderr: true,
|
100
|
+
stdout_logfile: "/dev/null",
|
101
|
+
stderr_logfile: "/dev/null"
|
102
|
+
}
|
103
|
+
end
|
104
|
+
|
105
|
+
def set_or_get(key, args)
|
106
|
+
key = key.to_sym
|
107
|
+
if args.length == 0
|
108
|
+
return attributes.fetch key
|
109
|
+
else
|
110
|
+
value = args.first
|
111
|
+
if block_given?
|
112
|
+
value = yield value
|
113
|
+
end
|
114
|
+
set key, value
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def set(key, value)
|
119
|
+
key = key.to_sym
|
120
|
+
if attributes.key? key
|
121
|
+
attributes.store key, value
|
122
|
+
else
|
123
|
+
raise "no such key #{key}"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
def template
|
129
|
+
@template ||= Pathname.new("#{__dir__}/templates/job.erb")
|
130
|
+
end
|
131
|
+
|
132
|
+
def binding
|
133
|
+
attrs = OpenStruct.new(attributes)
|
134
|
+
attrs.instance_eval { binding }
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
data/lib/sv/logger.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'sv/custom_logger'
|
3
|
+
module Sv
|
4
|
+
module Logger
|
5
|
+
|
6
|
+
def self.logger
|
7
|
+
@logger ||= ::Sv::CustomLogger.new(STDOUT)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.stderr
|
11
|
+
@stderr ||= ::Logger.new(STDERR)
|
12
|
+
end
|
13
|
+
|
14
|
+
def logger
|
15
|
+
::Sv::Logger.logger
|
16
|
+
end
|
17
|
+
|
18
|
+
def stderr
|
19
|
+
::Sv::Logger.stderr
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/sv/server.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'sv/config'
|
2
|
+
require 'sv/api'
|
3
|
+
require 'sv/supervisor/config'
|
4
|
+
require 'sv/status'
|
5
|
+
|
6
|
+
module Sv
|
7
|
+
class Server
|
8
|
+
|
9
|
+
attr_reader :app_dir
|
10
|
+
|
11
|
+
def initialize(app_dir)
|
12
|
+
@app_dir = app_dir
|
13
|
+
end
|
14
|
+
|
15
|
+
def start(auto_start: false, wait: false)
|
16
|
+
init_once
|
17
|
+
if server_status.running?
|
18
|
+
puts "supervisor already running with pid #{api.pid}"
|
19
|
+
return
|
20
|
+
end
|
21
|
+
system "supervisord -c #{supervisor_config.generated_path}"
|
22
|
+
puts "Started"
|
23
|
+
api.start_jobs(wait: wait) if auto_start
|
24
|
+
end
|
25
|
+
|
26
|
+
def stop
|
27
|
+
init_once
|
28
|
+
if server_status.stopped?
|
29
|
+
puts "Stopped"
|
30
|
+
return
|
31
|
+
end
|
32
|
+
api.shutdown
|
33
|
+
server_status.wait_until_stopped
|
34
|
+
puts "Stopped"
|
35
|
+
end
|
36
|
+
|
37
|
+
def restart(auto_start: false, wait: false)
|
38
|
+
stop if server_status.running?
|
39
|
+
start auto_start: auto_start, wait: wait
|
40
|
+
end
|
41
|
+
|
42
|
+
def start_jobs
|
43
|
+
api.start_jobs
|
44
|
+
puts api.errors
|
45
|
+
end
|
46
|
+
|
47
|
+
def status
|
48
|
+
if server_status.running?
|
49
|
+
api.print_status
|
50
|
+
else
|
51
|
+
puts "Stopped"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def print_config
|
56
|
+
puts File.read(supervisor_config.generated_path)
|
57
|
+
end
|
58
|
+
|
59
|
+
def required_paths
|
60
|
+
paths = [
|
61
|
+
"#{app_dir}/tmp/sockets/",
|
62
|
+
"#{app_dir}/tmp/pids/",
|
63
|
+
"#{app_dir}/log/"
|
64
|
+
]
|
65
|
+
paths.each do |path|
|
66
|
+
path = Pathname.new(path)
|
67
|
+
raise ::Sv::Error, "required path missing #{path}" if not path.exist?
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def config
|
74
|
+
@config ||= ::Sv::Config.new(app_dir)
|
75
|
+
end
|
76
|
+
|
77
|
+
def api
|
78
|
+
@api ||= ::Sv::Api.new(config.socket_path)
|
79
|
+
end
|
80
|
+
|
81
|
+
def server_status
|
82
|
+
@server_status ||= ::Sv::Status.new(config.socket_path)
|
83
|
+
end
|
84
|
+
|
85
|
+
def supervisor_config
|
86
|
+
@supervisor_config ||= ::Sv::Supervisor::Config.new(config)
|
87
|
+
end
|
88
|
+
|
89
|
+
def init_once
|
90
|
+
@init_once ||= begin
|
91
|
+
required_paths
|
92
|
+
true
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
data/lib/sv/status.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
module Sv
|
4
|
+
class Status
|
5
|
+
|
6
|
+
attr_reader :socket_path
|
7
|
+
|
8
|
+
def initialize(socket_path)
|
9
|
+
@socket_path = socket_path
|
10
|
+
end
|
11
|
+
|
12
|
+
def running?
|
13
|
+
s = UNIXSocket.new(socket_path)
|
14
|
+
s.close
|
15
|
+
return true
|
16
|
+
rescue Errno::ECONNREFUSED, Errno::ENOENT
|
17
|
+
return false
|
18
|
+
end
|
19
|
+
|
20
|
+
def stopped?
|
21
|
+
not running?
|
22
|
+
end
|
23
|
+
|
24
|
+
def wait_until_stopped
|
25
|
+
loop do
|
26
|
+
break if not running?
|
27
|
+
sleep 0.1
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'ostruct'
|
3
|
+
require 'sv/logger'
|
4
|
+
|
5
|
+
module Sv::Supervisor
|
6
|
+
class Config
|
7
|
+
|
8
|
+
include ::Sv::Logger
|
9
|
+
attr_reader :config
|
10
|
+
|
11
|
+
def initialize(config)
|
12
|
+
@config = config
|
13
|
+
end
|
14
|
+
|
15
|
+
def path
|
16
|
+
"#{config.app_dir}/tmp/supervisor.conf"
|
17
|
+
end
|
18
|
+
|
19
|
+
def generated_path
|
20
|
+
generate_once
|
21
|
+
path
|
22
|
+
end
|
23
|
+
|
24
|
+
def generate_config_file
|
25
|
+
erb = ERB.new(File.read(template), nil, '-')
|
26
|
+
File.open(path, 'w') do |f|
|
27
|
+
f.write erb.result(binding)
|
28
|
+
end
|
29
|
+
rescue => e
|
30
|
+
raise ::Sv::Error, "unable to generate supervisor config"
|
31
|
+
end
|
32
|
+
|
33
|
+
def generate_once
|
34
|
+
return if @config_generated
|
35
|
+
generate_config_file
|
36
|
+
@config_generated = true
|
37
|
+
end
|
38
|
+
|
39
|
+
def template
|
40
|
+
"#{__dir__}/../templates/supervisor.conf.erb"
|
41
|
+
end
|
42
|
+
|
43
|
+
def rendered_jobs
|
44
|
+
jobs = config.jobs
|
45
|
+
jobs.inject("") do |str, job|
|
46
|
+
render = job.render
|
47
|
+
str << render if render
|
48
|
+
str
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def binding
|
53
|
+
opts = OpenStruct.new
|
54
|
+
opts.socket_path = config.socket_path
|
55
|
+
opts.pidfile = config.pidfile
|
56
|
+
opts.logfile = config.logfile
|
57
|
+
opts.rendered_jobs = rendered_jobs
|
58
|
+
opts.instance_eval { binding }
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
[program:<%= name %>]
|
2
|
+
process_name=%(program_name)s_%(process_num)02d
|
3
|
+
command=<%= command %>
|
4
|
+
directory=<%= working_dir %>
|
5
|
+
numprocs=<%= numprocs %>
|
6
|
+
autostart=false
|
7
|
+
autorestart=<%= autorestart %>
|
8
|
+
startsecs=<%= startsecs %>
|
9
|
+
startretries=<%= startretries %>
|
10
|
+
stopsignal=<%= stopsignal %>
|
11
|
+
stopwaitsecs=<%= stopwaitsecs %>
|
12
|
+
killasgroup=<%= killasgroup %>
|
13
|
+
<% if redirect_stderr -%>
|
14
|
+
redirect_stderr=<%= redirect_stderr %>
|
15
|
+
<% end -%>
|
16
|
+
stdout_logfile=<%= stdout_logfile %>
|
17
|
+
stderr_logfile=<%= stderr_logfile %>
|
18
|
+
environment=<%= env %>
|
19
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
[unix_http_server]
|
2
|
+
file=<%= socket_path %>
|
3
|
+
|
4
|
+
[supervisord]
|
5
|
+
logfile=<%= logfile %>
|
6
|
+
loglevel=info
|
7
|
+
pidfile=<%= pidfile %>
|
8
|
+
|
9
|
+
[rpcinterface:supervisor]
|
10
|
+
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
|
11
|
+
|
12
|
+
[supervisorctl]
|
13
|
+
serverurl=unix://<%= socket_path %>
|
14
|
+
|
15
|
+
<%= rendered_jobs %>
|
data/lib/sv/version.rb
ADDED
data/lib/sv.rb
ADDED
data/sv.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'sv/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "sv"
|
8
|
+
spec.version = Sv::VERSION
|
9
|
+
spec.authors = ["Neeraj"]
|
10
|
+
spec.email = ["neeraj.bhunwal@gmail.com"]
|
11
|
+
spec.summary = %q{A wrapper for supervisord}
|
12
|
+
spec.description = %q{A wrapper for supervisord}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sv
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.5
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Neeraj
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-11-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: A wrapper for supervisord
|
42
|
+
email:
|
43
|
+
- neeraj.bhunwal@gmail.com
|
44
|
+
executables:
|
45
|
+
- sv
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- ".gitignore"
|
50
|
+
- Gemfile
|
51
|
+
- LICENSE.txt
|
52
|
+
- README.md
|
53
|
+
- Rakefile
|
54
|
+
- bin/sv
|
55
|
+
- lib/sv.rb
|
56
|
+
- lib/sv/api.rb
|
57
|
+
- lib/sv/base.rb
|
58
|
+
- lib/sv/cli/server.rb
|
59
|
+
- lib/sv/config.rb
|
60
|
+
- lib/sv/custom_logger.rb
|
61
|
+
- lib/sv/error.rb
|
62
|
+
- lib/sv/ext/net_http.rb
|
63
|
+
- lib/sv/ext/string.rb
|
64
|
+
- lib/sv/job.rb
|
65
|
+
- lib/sv/logger.rb
|
66
|
+
- lib/sv/server.rb
|
67
|
+
- lib/sv/status.rb
|
68
|
+
- lib/sv/supervisor/config.rb
|
69
|
+
- lib/sv/templates/job.erb
|
70
|
+
- lib/sv/templates/supervisor.conf.erb
|
71
|
+
- lib/sv/version.rb
|
72
|
+
- sv.gemspec
|
73
|
+
homepage: ''
|
74
|
+
licenses:
|
75
|
+
- MIT
|
76
|
+
metadata: {}
|
77
|
+
post_install_message:
|
78
|
+
rdoc_options: []
|
79
|
+
require_paths:
|
80
|
+
- lib
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
requirements: []
|
92
|
+
rubyforge_project:
|
93
|
+
rubygems_version: 2.2.2
|
94
|
+
signing_key:
|
95
|
+
specification_version: 4
|
96
|
+
summary: A wrapper for supervisord
|
97
|
+
test_files: []
|