hamster-the-process-watcher 1.1.0

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.
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use ree@hampster --create
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ # A sample Gemfile
2
+ source "http://rubygems.org"
3
+ gem 'wrong'
4
+ gem 'jeweler'
5
+ gem 'daemons'
data/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+
2
+ Copyright (C) 2009,2010 Curtis Schofield
3
+
4
+ This program is free software: you can redistribute it and/or modify
5
+ it under the terms of the GNU General Public License as published by
6
+ the Free Software Foundation, either version 3 of the License, or
7
+ (at your option) any later version.
8
+
9
+ This program is distributed in the hope that it will be useful,
10
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ GNU General Public License for more details.
13
+
14
+ You should have received a copy of the GNU General Public License
15
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,21 @@
1
+ = Hampster
2
+
3
+ Simple process monitor -
4
+ Used to check if a process is running - if it insn't execute some ruby code
5
+
6
+ see ./bin/hampster --help for all the arguments that it takes
7
+
8
+ = Example
9
+
10
+ hampster --callback "%x{echo "Rails is Dead" | mail -s rails_is_dead me@ram9.cc}" --watch 'Rails:' --callback-message 'emailing you some fyi' --delay 1
11
+
12
+ samples.
13
+
14
+ Put this in a cronjob to run every minute and you have a very simple, robust way to detect missing jobs
15
+
16
+ This is not a replacement for more robust excellent froody things like Daemontools - but it is a simple way to
17
+ make sure a important background job is running in userland - without muking about with root
18
+
19
+ = Fin
20
+
21
+ Copyright (c) 2011 Curtis Schofield. See LICENSE for details.
@@ -0,0 +1,58 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "hamster-the-process-watcher"
8
+ gem.summary = %Q{Hamster : The process runner}
9
+ gem.description = %Q{Dedicated to hamsters running in wheels everywhere}
10
+ gem.email = "github.com@robotarmyma.de"
11
+ gem.homepage = "http://github.com/robotarmy/hamster"
12
+ gem.authors = ["Curtis Schofield"]
13
+ gem.add_development_dependency "bundler", ">= 0"
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
19
+ end
20
+
21
+ require 'rake/testtask'
22
+ Rake::TestTask.new(:test) do |test|
23
+ test.libs << 'lib' << 'test'
24
+ test.pattern = 'test/**/test_*.rb'
25
+ test.verbose = true
26
+ end
27
+
28
+ begin
29
+ require 'rcov/rcovtask'
30
+ Rcov::RcovTask.new do |test|
31
+ test.libs << 'test'
32
+ test.pattern = 'test/**/test_*.rb'
33
+ test.verbose = true
34
+ end
35
+ rescue LoadError
36
+ task :rcov do
37
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
38
+ end
39
+ end
40
+ task :check_dependencies do
41
+ puts '..bundler'
42
+ `bundle check || bundle install`
43
+ end
44
+
45
+ task :test => :check_dependencies
46
+
47
+
48
+ task :default => :test
49
+
50
+ require 'rake/rdoctask'
51
+ Rake::RDocTask.new do |rdoc|
52
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
53
+
54
+ rdoc.rdoc_dir = 'rdoc'
55
+ rdoc.title = "weasel #{version}"
56
+ rdoc.rdoc_files.include('README*')
57
+ rdoc.rdoc_files.include('lib/**/*.rb')
58
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.1.0
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env ruby -rubygems
2
+ $:.push File.join(File.dirname(__FILE__),'..','lib')
3
+ require 'optparse'
4
+ require 'hamster'
5
+ options = {}
6
+ OptionParser.new do |opts|
7
+ opts.banner = "Usage: #{__FILE__} [options]"
8
+ opts.on( '-h', '--help', 'Display this screen' ) do
9
+ puts opts
10
+ exit
11
+ end
12
+
13
+ opts.on("--command shellcode",String, "shell code to execute if process missing") do |v|
14
+ options[:command] = v
15
+ end
16
+
17
+ opts.on("--callback rubycode",String, "ruby code to execute if process missing") do |v|
18
+ options[:callback] = lambda { # may be evil
19
+ eval(v)
20
+ }
21
+ end
22
+ opts.on("--callback_message string",String, "message to write when callback fired") do |v|
23
+ options[:callback_message] = v
24
+ end
25
+
26
+ opts.on("--watch S",String, "process identifying string") do |v|
27
+ options[:watch] = v
28
+ end
29
+
30
+ opts.on("--num_cycles N", "total executions before exit") do |v|
31
+ options[:num_cycles] = v.to_i
32
+ end
33
+
34
+ opts.on("--delay F",Float, "delay between cycles") do |v|
35
+ options[:delay] = v
36
+ end
37
+
38
+ end.parse!
39
+ ProcessWatch.new(options).cycle
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ loop do
3
+ sleep 100
4
+ end
@@ -0,0 +1,62 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{hamster-the-process-watcher}
8
+ s.version = "1.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Curtis Schofield"]
12
+ s.date = %q{2011-07-07}
13
+ s.description = %q{Dedicated to hamsters running in wheels everywhere}
14
+ s.email = %q{github.com@robotarmyma.de}
15
+ s.executables = ["test_daemon", "hamster"]
16
+ s.extra_rdoc_files = [
17
+ "LICENSE",
18
+ "README.rdoc"
19
+ ]
20
+ s.files = [
21
+ ".document",
22
+ ".rvmrc",
23
+ "Gemfile",
24
+ "LICENSE",
25
+ "README.rdoc",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "bin/hamster",
29
+ "bin/test_daemon",
30
+ "hamster-the-process-watcher.gemspec",
31
+ "lib/hamster.rb",
32
+ "lib/process_watch.rb",
33
+ "test/helper.rb",
34
+ "test/test_process_watch.rb"
35
+ ]
36
+ s.homepage = %q{http://github.com/robotarmy/hamster}
37
+ s.require_paths = ["lib"]
38
+ s.rubygems_version = %q{1.6.1}
39
+ s.summary = %q{Hamster : The process runner}
40
+
41
+ if s.respond_to? :specification_version then
42
+ s.specification_version = 3
43
+
44
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
45
+ s.add_runtime_dependency(%q<wrong>, [">= 0"])
46
+ s.add_runtime_dependency(%q<jeweler>, [">= 0"])
47
+ s.add_runtime_dependency(%q<daemons>, [">= 0"])
48
+ s.add_development_dependency(%q<bundler>, [">= 0"])
49
+ else
50
+ s.add_dependency(%q<wrong>, [">= 0"])
51
+ s.add_dependency(%q<jeweler>, [">= 0"])
52
+ s.add_dependency(%q<daemons>, [">= 0"])
53
+ s.add_dependency(%q<bundler>, [">= 0"])
54
+ end
55
+ else
56
+ s.add_dependency(%q<wrong>, [">= 0"])
57
+ s.add_dependency(%q<jeweler>, [">= 0"])
58
+ s.add_dependency(%q<daemons>, [">= 0"])
59
+ s.add_dependency(%q<bundler>, [">= 0"])
60
+ end
61
+ end
62
+
@@ -0,0 +1,2 @@
1
+ require 'daemons'
2
+ require 'process_watch'
@@ -0,0 +1,56 @@
1
+ class ProcessWatch
2
+ attr_accessor :watch_string,
3
+ :delay,
4
+ :num_cycles,
5
+ :command,
6
+ :callback,
7
+ :callback_message
8
+
9
+ def initialize(options)
10
+ self.command = options[:command] || nil
11
+ self.callback = options[:callback] ||
12
+ lambda { p 'default callback does nothing'}
13
+ self.callback_message = (options[:callback_message] ||
14
+ "Callback Triggered").strip
15
+ self.watch_string = (options[:watch] ||
16
+ "this poem is a pomme").strip
17
+ self.delay = options[:delay] || 60
18
+ self.num_cycles = options[:num_cycles] || 3
19
+ end
20
+
21
+ class << self
22
+
23
+ def find(watch_string,ps_args = "")
24
+ cmd = %%ps -x#{ps_args} |grep '#{watch_string}' |grep -v grep | grep -v 'watch #{watch_string}' | awk '{print $1}'%
25
+ pids = %x{#{cmd}}.split()
26
+ end
27
+ end
28
+
29
+ public
30
+ def cycle
31
+ self.num_cycles.times do
32
+ _check
33
+ sleep self.delay
34
+ end
35
+ end
36
+ private
37
+ def _trigger_callback
38
+ _fork
39
+ ensure
40
+ self.callback.call
41
+ puts self.callback_message
42
+ end
43
+ def _check
44
+ if self.class.find(self.watch_string).empty?
45
+ _trigger_callback
46
+ end
47
+ end
48
+ def _fork
49
+ Daemonize.call_as_daemon(lambda {
50
+ p ENV['PWD']
51
+ Dir.chdir(ENV['PWD'])
52
+ ::Process.exec(self.command)
53
+ },"hamster.daemon.log",self.command)
54
+ end
55
+ end
56
+
@@ -0,0 +1,11 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require "bundler/setup"
4
+ require 'wrong/adapters/test_unit'
5
+
6
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
7
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
8
+ require 'hamster'
9
+
10
+ class Test::Unit::TestCase
11
+ end
@@ -0,0 +1,92 @@
1
+ require 'helper'
2
+ class TestProcessWatch < Test::Unit::TestCase
3
+ def test_find_running_daemon
4
+ cmd = %%./bin/test_daemon ID=1%
5
+ Process.detach(d_pid = fork {
6
+ Process.exec cmd
7
+ })
8
+ assert {
9
+ ProcessWatch.find('test_daemon ID=1').size == 1
10
+ }
11
+ %x{kill -9 #{d_pid}}
12
+ end
13
+ def test_pid_of_new_process_is_sibling
14
+ cmd = %%ruby ./bin/hamster --watch 'test_daemon ID=1' --command "./bin/test_daemon ID=1" --callback_message o_o__poot --delay 4 --num_cycles 45 %
15
+ Process.detach(h_pid = fork {
16
+ Process.exec cmd
17
+ })
18
+ sleep 3 # let it start
19
+ pid = ProcessWatch.find('test_daemon ID=1').first
20
+
21
+ assert {
22
+ lookup_parent_pid_via_ps('ruby ./bin/test_daemon ID=1') == lookup_parent_pid_via_ps('o_o__poot')
23
+ }
24
+ # clean up hamster and the test_daemon
25
+ %x{kill -9 #{pid}}
26
+ %x{kill -9 #{h_pid}}
27
+ end
28
+ def test_running_already_exit_without_action
29
+ cmd = %%./bin/test_daemon ID=1%
30
+ Process.detach(d_pid = fork {
31
+ Process.exec cmd
32
+ })
33
+ cmd = %%ruby ./bin/hamster --watch 'test_daemon ID=1' --command "./bin/test_daemon ID=1" --callback_message poot --delay 1 --num_cycles 2 %
34
+ Process.detach(h_pid = fork {
35
+ %x{#{cmd}}
36
+ })
37
+ pid, status = Process.waitpid2(h_pid)
38
+ assert {
39
+ status.exitstatus == 0
40
+ }
41
+ %x{kill -9 #{d_pid}}
42
+ end
43
+
44
+ def test_watches_self
45
+ callback_called = false
46
+ pw = ProcessWatch.new(:watch => "nothing", :delay => 1, :cycle => 1,
47
+ :callback => lambda {callback_called = true})
48
+ pw.cycle
49
+ assert {
50
+ callback_called
51
+ }
52
+ end
53
+ def test_watches_but_not_the_watch_string_exits
54
+ cmd = %%./bin/hamster --watch hamster --callback "puts 'toop'" --callback_message poot --delay 1 --num_cycles 2 %
55
+ out = %x{#{cmd}}
56
+ assert {
57
+ out =~ /poot/ && out =~ /toop/
58
+ }
59
+ end
60
+ def test_runs_test_daemon_command_if_not_present
61
+ cmd = %%ruby ./bin/hamster --watch 'test_daemon ID=1' --command "./bin/test_daemon ID=1" --callback_message poot --delay 1 --num_cycles 2 %
62
+ Process.detach(pid = fork {
63
+ Process.exec cmd
64
+ })
65
+ sleep 3 # let it start
66
+ assert {
67
+ ProcessWatch.find('test_daemon ID=1').size == 1
68
+ }
69
+ %x{kill -9 #{ProcessWatch.find('test_daemon ID=1').first}}
70
+ end
71
+
72
+ def test_daemon_keeps_running
73
+ cmd = %%ruby ./bin/hamster --watch 'test_daemon ID=1' --command "./bin/test_daemon ID=1" --callback_message poot --delay 5 --num_cycles 10 %
74
+ Process.detach(pid = fork {
75
+ Process.exec cmd
76
+ })
77
+ sleep 3 # let it start
78
+ %x{kill -9 #{pid}}
79
+ #child should live on if hampster is killed
80
+ sleep 3 # wait..for it
81
+ assert {
82
+ ProcessWatch.find('test_daemon ID=1').size == 1
83
+ }
84
+ %x{kill -9 #{ProcessWatch.find('test_daemon ID=1').first}}
85
+ end
86
+
87
+ def lookup_parent_pid_via_ps(match)
88
+ ProcessWatch.find(match,"f").first
89
+ end
90
+ end
91
+
92
+
metadata ADDED
@@ -0,0 +1,137 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hamster-the-process-watcher
3
+ version: !ruby/object:Gem::Version
4
+ hash: 19
5
+ prerelease:
6
+ segments:
7
+ - 1
8
+ - 1
9
+ - 0
10
+ version: 1.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Curtis Schofield
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-07-07 00:00:00 -07:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ hash: 3
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ version_requirements: *id001
32
+ name: wrong
33
+ prerelease: false
34
+ type: :runtime
35
+ - !ruby/object:Gem::Dependency
36
+ requirement: &id002 !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ hash: 3
42
+ segments:
43
+ - 0
44
+ version: "0"
45
+ version_requirements: *id002
46
+ name: jeweler
47
+ prerelease: false
48
+ type: :runtime
49
+ - !ruby/object:Gem::Dependency
50
+ requirement: &id003 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ hash: 3
56
+ segments:
57
+ - 0
58
+ version: "0"
59
+ version_requirements: *id003
60
+ name: daemons
61
+ prerelease: false
62
+ type: :runtime
63
+ - !ruby/object:Gem::Dependency
64
+ requirement: &id004 !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ hash: 3
70
+ segments:
71
+ - 0
72
+ version: "0"
73
+ version_requirements: *id004
74
+ name: bundler
75
+ prerelease: false
76
+ type: :development
77
+ description: Dedicated to hamsters running in wheels everywhere
78
+ email: github.com@robotarmyma.de
79
+ executables:
80
+ - test_daemon
81
+ - hamster
82
+ extensions: []
83
+
84
+ extra_rdoc_files:
85
+ - LICENSE
86
+ - README.rdoc
87
+ files:
88
+ - .document
89
+ - .rvmrc
90
+ - Gemfile
91
+ - LICENSE
92
+ - README.rdoc
93
+ - Rakefile
94
+ - VERSION
95
+ - bin/hamster
96
+ - bin/test_daemon
97
+ - hamster-the-process-watcher.gemspec
98
+ - lib/hamster.rb
99
+ - lib/process_watch.rb
100
+ - test/helper.rb
101
+ - test/test_process_watch.rb
102
+ has_rdoc: true
103
+ homepage: http://github.com/robotarmy/hamster
104
+ licenses: []
105
+
106
+ post_install_message:
107
+ rdoc_options: []
108
+
109
+ require_paths:
110
+ - lib
111
+ required_ruby_version: !ruby/object:Gem::Requirement
112
+ none: false
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ hash: 3
117
+ segments:
118
+ - 0
119
+ version: "0"
120
+ required_rubygems_version: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ hash: 3
126
+ segments:
127
+ - 0
128
+ version: "0"
129
+ requirements: []
130
+
131
+ rubyforge_project:
132
+ rubygems_version: 1.6.1
133
+ signing_key:
134
+ specification_version: 3
135
+ summary: "Hamster : The process runner"
136
+ test_files: []
137
+