sv 0.0.5
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.
- 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: []
|