srv_manager 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6257e9538f38e3ce480fcb2a4a46475e01af1489
4
+ data.tar.gz: 15d9025f67277caacecc04b3d6a3677fe2b13923
5
+ SHA512:
6
+ metadata.gz: b3124811a5c420f7e2683ee271ef8dc274cbdc67397531f44d8bb5e9ed1722c6e142874c72c025e27d3a928e2785f502a4b923a3acdaa50cc808a36db13ea6de
7
+ data.tar.gz: 55932a2d5f6883bb8c8adfb6a797c10cfcfd151b4056a2fd42f6dd054411c5011a2364bd97b513ba58a2b1421d1c26a0d934470ea01ecb16810a87d3a2560b7f
data/.gitignore ADDED
@@ -0,0 +1,19 @@
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
+ logs
19
+ data
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in srv_manager.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Gabriel Naiman
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
+ # SrvManager
2
+
3
+ Service admin and monitor
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'srv_manager'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install srv_manager
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
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 new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/srv ADDED
@@ -0,0 +1,176 @@
1
+ #!/usr/bin/env ruby
2
+ require 'thor'
3
+ require 'hirb'
4
+ require 'json'
5
+
6
+ require File.expand_path('../lib/srv_manager', File.dirname(__FILE__))
7
+
8
+
9
+ module SrvManager
10
+ class CLI < Thor
11
+
12
+ desc 'version', 'Show SrvManager version number'
13
+ def version
14
+ puts SrvManager::VERSION
15
+ end
16
+
17
+ option :dir, aliases: :d
18
+ option :processes, aliases: :p, type: :numeric
19
+ option :auto, type: :boolean
20
+ option :rvm, type: :boolean
21
+ option :env, type: :hash
22
+ desc 'add <NAME> <COMMAND>', 'Add new service'
23
+ def add(name, command)
24
+ Context.scoped do |ctx|
25
+ ctx.services << Service.new(name, command, options)
26
+ end
27
+ end
28
+
29
+ desc 'rm <NAME>', 'Remove service'
30
+ def rm(name)
31
+ Context.scoped do |ctx|
32
+ service = find_service name, ctx
33
+ if service
34
+ service.stop
35
+ ctx.services.delete service
36
+ end
37
+ end
38
+ end
39
+
40
+ desc 'start <NAME>', 'Start service'
41
+ def start(name)
42
+ Context.scoped(safe: true) do |ctx|
43
+ service = find_service name, ctx
44
+ service.start
45
+ end
46
+ end
47
+
48
+ desc 'stop <NAME>', 'Stop service'
49
+ def stop(name)
50
+ Context.scoped(safe: true) do |ctx|
51
+ service = find_service name, ctx
52
+ service.stop
53
+ end
54
+ end
55
+
56
+ desc 'restart <NAME>', 'Restart service'
57
+ def restart(name)
58
+ Context.scoped(safe: true) do |ctx|
59
+ service = find_service name, ctx
60
+ service.restart
61
+ end
62
+ end
63
+
64
+ desc 'ps <NAME>', 'Show service processes'
65
+ def ps(name)
66
+ Context.scoped do |ctx|
67
+ service = find_service name, ctx
68
+
69
+ return if service.processes.empty?
70
+
71
+ processes = service.processes.map do |process|
72
+ {
73
+ PID: process.id,
74
+ Status: process.alive? ? 'Running' : 'Killed'
75
+ }
76
+ end
77
+ puts Hirb::Helpers::AutoTable.render(processes, unicode: true, fields: [:PID, :Status])
78
+ end
79
+ end
80
+
81
+ option :start, type: :boolean
82
+ option :stop, type: :boolean
83
+ desc 'monitor', 'Start service monitor'
84
+ def monitor
85
+ Context.scoped do |ctx|
86
+ if options[:stop]
87
+ if ctx.monitor.alive?
88
+ ctx.monitor.stop
89
+ puts 'Monitor stoped'
90
+ end
91
+ elsif options[:start]
92
+ if ctx.monitor.alive?
93
+ puts "Monitor already started (PID: #{ctx.monitor.pid})"
94
+ else
95
+ ctx.monitor.start
96
+ puts "Monitor started (PID: #{ctx.monitor.pid})"
97
+ end
98
+ else
99
+ if ctx.monitor.alive?
100
+ puts "Monitor running (PID: #{ctx.monitor.pid})"
101
+ else
102
+ puts 'Monitor not running'
103
+ end
104
+ end
105
+ end
106
+ end
107
+
108
+ desc 'logs', 'Show logs'
109
+ def logs
110
+ puts `tail #{LOG_FILE}`
111
+ end
112
+
113
+ option :dir, type: :boolean, aliases: :d
114
+ option :env, type: :boolean, aliases: :e
115
+ desc 'ls', 'List configured services'
116
+ def ls
117
+ Context.scoped do |ctx|
118
+ return if ctx.services.empty?
119
+
120
+ services = ctx.services.sort_by(&:name).map do |service|
121
+ {
122
+ Service: service.name,
123
+ Status: service.started? ? 'Started' : 'Stoped',
124
+ Alive: service.processes.select(&:alive?).count,
125
+ Dead: service.processes.reject(&:alive?).count,
126
+ Command: service.command.text,
127
+ Env: service.command.env.map{|k,v| "#{k}=#{v}"},
128
+ Dir: service.command.dir
129
+ }
130
+ end
131
+
132
+ fields = [:Service, :Status, :Alive, :Dead, :Command]
133
+ fields << :Env if options[:env]
134
+ fields << :Dir if options[:dir]
135
+
136
+ puts Hirb::Helpers::AutoTable.render(services, unicode: true, fields: fields)
137
+ end
138
+ end
139
+
140
+ desc 'clean', 'Remove all services'
141
+ def clean
142
+ Context.scoped(safe: true) do |ctx|
143
+ ctx.clean
144
+ end
145
+ end
146
+
147
+ desc 'dump', 'Dump service configuration'
148
+ def dump
149
+ Context.scoped do |ctx|
150
+ puts JSON.pretty_generate(ctx.to_hash)
151
+ end
152
+ end
153
+
154
+ desc 'load', 'Load service configuration'
155
+ def load(filename)
156
+ json = JSON.parse(File.read(filename))
157
+ Context.scoped do |ctx|
158
+ ctx.load json
159
+ end
160
+ end
161
+
162
+ private
163
+
164
+ def find_service(name, context)
165
+ service = context.services.detect { |s| s.name == name }
166
+ unless service
167
+ puts "Service not found: #{name}"
168
+ exit 1
169
+ end
170
+ service
171
+ end
172
+
173
+ end
174
+ end
175
+
176
+ SrvManager::CLI.start
@@ -0,0 +1,21 @@
1
+ module SrvManager
2
+ class Command
3
+
4
+ attr_reader :text
5
+ attr_reader :dir
6
+ attr_reader :env
7
+ attr_reader :rvm
8
+
9
+ def initialize(text, options={})
10
+ @text = text
11
+ @dir = File.expand_path options[:dir].to_s
12
+ @env = options[:env] || {}
13
+ @rvm = options[:rvm] || false
14
+ end
15
+
16
+ def to_hash
17
+ {text: text, dir: dir, env: env, rvm: rvm}
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,60 @@
1
+ module SrvManager
2
+ class Context
3
+
4
+ def monitor
5
+ @monitor ||= Monitor.new
6
+ end
7
+
8
+ def services
9
+ @services ||= []
10
+ end
11
+
12
+ def clean
13
+ services.each(&:stop)
14
+ services.clear
15
+ end
16
+
17
+ def load(json)
18
+ clean
19
+ @services = json['services'].map { |js| Service.parse js }
20
+ end
21
+
22
+ def self.scoped(options={})
23
+ context = load
24
+ monitor_running = options[:safe] && context.monitor.alive?
25
+ context.monitor.stop if monitor_running
26
+ begin
27
+ yield context
28
+ ensure
29
+ save context
30
+ if monitor_running
31
+ context.monitor.start
32
+ save context
33
+ end
34
+ end
35
+ end
36
+
37
+ def to_hash
38
+ {services: services.map(&:to_hash)}
39
+ end
40
+
41
+ private
42
+
43
+ def self.load
44
+ LOGGER.info "Reading context from #{data_file}"
45
+ return new unless File.exists? data_file
46
+ Marshal.load(File.read(data_file))
47
+ end
48
+
49
+ def self.save(context)
50
+ LOGGER.info "Saving context to #{data_file}"
51
+ FileUtils.mkpath File.dirname(data_file) unless Dir.exists? File.dirname(data_file)
52
+ File.write data_file, Marshal.dump(context)
53
+ end
54
+
55
+ def self.data_file
56
+ File.expand_path('.srv_manager/context.bin', Dir.home)
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,43 @@
1
+ module SrvManager
2
+ class Monitor
3
+
4
+ attr_reader :pid
5
+
6
+ def start(sleep_time=60)
7
+ @pid = ::Process.fork do
8
+ loop do
9
+ Context.scoped do |ctx|
10
+ ctx.services.each do |service|
11
+ keep_alive service
12
+ end
13
+ end
14
+ sleep sleep_time
15
+ end
16
+ end
17
+ ::Process.detach @pid
18
+ end
19
+
20
+ def stop
21
+ ::Process.kill 'TERM', pid if pid
22
+ rescue Errno::ESRCH
23
+ end
24
+
25
+ def alive?
26
+ pid && ::Process.kill(0, pid) ? true : false
27
+ rescue Errno::ESRCH
28
+ false
29
+ end
30
+
31
+ def keep_alive(service)
32
+ service.processes.each do |process|
33
+ LOGGER.info "Monitoring service: #{service.name} (#{process.id || 'stoped'})"
34
+ if !process.started? && service.auto
35
+ process.start
36
+ elsif process.started? && !process.alive?
37
+ process.restart
38
+ end
39
+ end
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,76 @@
1
+ module SrvManager
2
+ class Process
3
+
4
+ attr_reader :command
5
+ attr_reader :id
6
+
7
+ def initialize(command)
8
+ @command = command
9
+ end
10
+
11
+ def start
12
+ return if alive?
13
+ command.rvm ? rvm_start : default_start
14
+ LOGGER.info "Started process #{@id}"
15
+ end
16
+
17
+ def stop
18
+ return unless started?
19
+ begin
20
+ ::Process.kill 'TERM', @id
21
+ rescue Errno::ESRCH
22
+ end
23
+ LOGGER.info "Stoped process #{@id}"
24
+ @id = nil
25
+ end
26
+
27
+ def restart
28
+ stop if alive?
29
+ start
30
+ end
31
+
32
+ def started?
33
+ !id.nil?
34
+ end
35
+
36
+ def alive?
37
+ started? && ::Process.kill(0, id) ? true : false
38
+ rescue Errno::ESRCH
39
+ false
40
+ end
41
+
42
+ private
43
+
44
+ def detault_start
45
+ @id = ::Process.spawn command.env, command.text, chdir: command.dir, out: '/dev/null', err: '/dev/null'
46
+ ::Process.detach @id
47
+ end
48
+
49
+ def rvm_start
50
+ pid_file = File.expand_path "#{self.object_id}_#{Time.now.to_i}.pid", TMP_PATH
51
+ rvm_pid = ::Process.spawn command.env.merge('SRV_COMMAND' => command.text, 'SRV_PIDFILE' => pid_file),
52
+ RVM_RUNNER,
53
+ chdir: command.dir,
54
+ out: '/dev/null',
55
+ err: '/dev/null'
56
+ ::Process.detach rvm_pid
57
+ @id = wait_for_pid pid_file
58
+ end
59
+
60
+ def wait_for_pid(filename)
61
+ Timeout.timeout(60) do
62
+ loop do
63
+ if File.exists? filename
64
+ pid = File.read(filename).to_i
65
+ File.delete filename
66
+ return pid
67
+ end
68
+ sleep 0.1
69
+ end
70
+ end
71
+ rescue Timeout::Error
72
+ nil
73
+ end
74
+
75
+ end
76
+ end
@@ -0,0 +1,54 @@
1
+ module SrvManager
2
+ class Service
3
+
4
+ attr_reader :name
5
+ attr_reader :command
6
+ attr_reader :processes
7
+ attr_reader :auto
8
+
9
+ def initialize(name, command, options={})
10
+ @name = name
11
+
12
+ @command = Command.new command, options
13
+
14
+ @processes = (options[:processes] || 1).times.map do
15
+ Process.new @command
16
+ end
17
+
18
+ @auto = options[:auto] || false
19
+ end
20
+
21
+ def start
22
+ processes.each(&:start)
23
+ LOGGER.info "Started service #{name}"
24
+ end
25
+
26
+ def stop
27
+ processes.each(&:stop)
28
+ LOGGER.info "Stoped service #{name}"
29
+ end
30
+
31
+ def restart
32
+ processes.each(&:restart)
33
+ end
34
+
35
+ def started?
36
+ processes.map(&:started?).reduce(:|)
37
+ end
38
+
39
+ def to_hash
40
+ {name: name, command: command.to_hash, processes: processes.count, auto: auto}
41
+ end
42
+
43
+ def self.parse(json)
44
+ new json['name'],
45
+ json['command']['text'],
46
+ dir: json['command']['dir'],
47
+ env: json['command']['env'],
48
+ rvm: json['command']['rvm'],
49
+ processes: json['processes'],
50
+ auto: json['auto']
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,3 @@
1
+ module SrvManager
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,21 @@
1
+ require 'fileutils'
2
+ require 'logger'
3
+ require 'timeout'
4
+
5
+ Dir.glob(File.expand_path('srv_manager/*.rb', File.dirname(__FILE__))).each { |f| require f }
6
+
7
+
8
+ module SrvManager
9
+
10
+ PATH = File.expand_path '.srv_manager', Dir.home
11
+ FileUtils.mkpath PATH unless Dir.exists? PATH
12
+
13
+ TMP_PATH = File.expand_path 'tmp', PATH
14
+ FileUtils.mkpath TMP_PATH unless Dir.exists? TMP_PATH
15
+
16
+ LOG_FILE = File.expand_path 'events.log', PATH
17
+ LOGGER = Logger.new LOG_FILE
18
+
19
+ RVM_RUNNER = File.expand_path '../rvm/runner', File.dirname(__FILE__)
20
+
21
+ end
data/rvm/runner ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # Load RVM into a shell session *as a function*
4
+ if [[ -s "$HOME/.rvm/scripts/rvm" ]] ; then
5
+ # First try to load from a user install
6
+ source "$HOME/.rvm/scripts/rvm"
7
+ elif [[ -s "/usr/local/rvm/scripts/rvm" ]] ; then
8
+ # Then try to load from a root install
9
+ source "/usr/local/rvm/scripts/rvm"
10
+ else
11
+ printf "ERROR: An RVM installation was not found.\n"
12
+ fi
13
+
14
+ $SRV_COMMAND & echo $! > $SRV_PIDFILE
@@ -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 'srv_manager/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "srv_manager"
8
+ spec.version = SrvManager::VERSION
9
+ spec.authors = ["Gabriel Naiman"]
10
+ spec.email = ["gabynaiman@gmail.com"]
11
+ spec.description = 'Service admin and monitor'
12
+ spec.summary = 'Service admin and monitor'
13
+ spec.homepage = 'http://github.com/gabynaiman/srv_manager'
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
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.3"
22
+ spec.add_development_dependency "rake"
23
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: srv_manager
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Gabriel Naiman
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-11-04 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.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
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: Service admin and monitor
42
+ email:
43
+ - gabynaiman@gmail.com
44
+ executables:
45
+ - srv
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - .gitignore
50
+ - Gemfile
51
+ - LICENSE.txt
52
+ - README.md
53
+ - Rakefile
54
+ - bin/srv
55
+ - lib/srv_manager.rb
56
+ - lib/srv_manager/command.rb
57
+ - lib/srv_manager/context.rb
58
+ - lib/srv_manager/monitor.rb
59
+ - lib/srv_manager/process.rb
60
+ - lib/srv_manager/service.rb
61
+ - lib/srv_manager/version.rb
62
+ - rvm/runner
63
+ - srv_manager.gemspec
64
+ homepage: http://github.com/gabynaiman/srv_manager
65
+ licenses:
66
+ - MIT
67
+ metadata: {}
68
+ post_install_message:
69
+ rdoc_options: []
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ requirements: []
83
+ rubyforge_project:
84
+ rubygems_version: 2.1.10
85
+ signing_key:
86
+ specification_version: 4
87
+ summary: Service admin and monitor
88
+ test_files: []