srv_manager 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []