react 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.
- data/.document +5 -0
- data/.gitignore +22 -0
- data/LICENSE +20 -0
- data/README.md +74 -0
- data/Rakefile +54 -0
- data/bin/react +34 -0
- data/lib/react.rb +101 -0
- data/react.gemspec +59 -0
- data/test/react_test.rb +1 -0
- data/test/teststrap.rb +6 -0
- metadata +98 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Kriss Kowalik
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# React
|
2
|
+
|
3
|
+
React is a simple application that allows for remote execution of commands.
|
4
|
+
It uses Redis as a queue - it is blocking specified list and waits for new
|
5
|
+
entries - when an entry appears, then it is executing recognized command.
|
6
|
+
|
7
|
+
## Inspiration
|
8
|
+
|
9
|
+
It's inspired by Simon Willson's example of "Queue-activated shell scripts"
|
10
|
+
in his redis-tutorial:
|
11
|
+
|
12
|
+
while [ 1 ] do
|
13
|
+
redis-cli blpop restart-httpd 0
|
14
|
+
apache2ctl graceful
|
15
|
+
done
|
16
|
+
|
17
|
+
## Examples
|
18
|
+
|
19
|
+
Firs you have to prepare file with available commands. It can look like this:
|
20
|
+
|
21
|
+
# my_commands.yml
|
22
|
+
restart_httpd: |
|
23
|
+
apache2ctl graceful
|
24
|
+
restart_mysql: |
|
25
|
+
/etc/init.d/mysql restart
|
26
|
+
reboot: |
|
27
|
+
reboot
|
28
|
+
|
29
|
+
And now you can start consumer.
|
30
|
+
|
31
|
+
react my_commands.yml
|
32
|
+
|
33
|
+
## Commands
|
34
|
+
|
35
|
+
While your consumer is working, you can push any of specified command to it's
|
36
|
+
queue (default queue name is `queue`), eg:
|
37
|
+
|
38
|
+
redis-cli lpush queue restart_httpd
|
39
|
+
redis-cli lpush queue reboot
|
40
|
+
|
41
|
+
## Configuration
|
42
|
+
|
43
|
+
There are few more runtime options, which can be useful for you.
|
44
|
+
|
45
|
+
* you can specify queue wihch will be consumed:
|
46
|
+
|
47
|
+
react my_commands.yml --queue "my:queue:name"
|
48
|
+
|
49
|
+
* you can specify the database to which consumer should connect:
|
50
|
+
|
51
|
+
react my_commands.yml --host "yourhost.com" --port 6379 --db 2
|
52
|
+
|
53
|
+
* and finally, you can demonize the consumer:
|
54
|
+
|
55
|
+
react my_commands.yml --daemonize
|
56
|
+
|
57
|
+
## Links
|
58
|
+
|
59
|
+
* [My website](http://nu7hatch.com/)
|
60
|
+
* [Simon Wilson's Redis tutorial](http://simonwillison.net/static/2010/redis-tutorial/)
|
61
|
+
|
62
|
+
## Note on Patches/Pull Requests
|
63
|
+
|
64
|
+
* Fork the project.
|
65
|
+
* Make your feature addition or bug fix.
|
66
|
+
* Add tests for it. This is important so I don't break it in a
|
67
|
+
future version unintentionally.
|
68
|
+
* Commit, do not mess with rakefile, version, or history.
|
69
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
70
|
+
* Send me a pull request. Bonus points for topic branches.
|
71
|
+
|
72
|
+
## Copyright
|
73
|
+
|
74
|
+
Copyright (c) 2010 Kriss Kowalik. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "react"
|
8
|
+
gem.version = "0.0.1"
|
9
|
+
gem.default_executable = 'react'
|
10
|
+
gem.executables = ['react']
|
11
|
+
gem.summary = %Q{Redis based remote command executor.}
|
12
|
+
gem.description = %Q{A simple application that allows for remote execution of commands.}
|
13
|
+
gem.email = "kriss.kowalik@gmail.com"
|
14
|
+
gem.homepage = "http://github.com/nu7hatch/react"
|
15
|
+
gem.authors = ["Kriss 'nu7hatch' Kowalik"]
|
16
|
+
gem.add_development_dependency "riot", ">= 0.11.3"
|
17
|
+
gem.add_dependency "daemons", ">= 0"
|
18
|
+
end
|
19
|
+
Jeweler::GemcutterTasks.new
|
20
|
+
rescue LoadError
|
21
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
22
|
+
end
|
23
|
+
|
24
|
+
require 'rake/testtask'
|
25
|
+
Rake::TestTask.new(:test) do |test|
|
26
|
+
test.libs << 'lib' << 'test'
|
27
|
+
test.pattern = 'test/**/*_test.rb'
|
28
|
+
test.verbose = true
|
29
|
+
end
|
30
|
+
|
31
|
+
begin
|
32
|
+
require 'rcov/rcovtask'
|
33
|
+
Rcov::RcovTask.new do |test|
|
34
|
+
test.libs << 'test'
|
35
|
+
test.pattern = 'test/**/*_test.rb'
|
36
|
+
test.verbose = true
|
37
|
+
end
|
38
|
+
rescue LoadError
|
39
|
+
task :rcov do
|
40
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
task :test => :check_dependencies
|
45
|
+
|
46
|
+
task :default => :test
|
47
|
+
|
48
|
+
require 'rake/rdoctask'
|
49
|
+
Rake::RDocTask.new do |rdoc|
|
50
|
+
rdoc.rdoc_dir = 'rdoc'
|
51
|
+
rdoc.title = "React"
|
52
|
+
rdoc.rdoc_files.include('README*')
|
53
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
54
|
+
end
|
data/bin/react
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'react'
|
5
|
+
require 'optparse'
|
6
|
+
require 'daemons'
|
7
|
+
|
8
|
+
if ARGV.empty?
|
9
|
+
ARGV << '--help'
|
10
|
+
else
|
11
|
+
if File.exists?(ARGV[0])
|
12
|
+
options[:commands] = YAML.load_file(ARGV[0])
|
13
|
+
else
|
14
|
+
puts "ERROR: File not found: `#{ARGV[0]}`"
|
15
|
+
exit
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
options = {
|
20
|
+
:redis => {:host => '127.0.0.1', :port => 6379, :db => 0},
|
21
|
+
:commands => {},
|
22
|
+
:queue => 'queue'
|
23
|
+
}
|
24
|
+
|
25
|
+
opts = OptionParser.new do |opts|
|
26
|
+
opts.banner = "Usage: react commands.yml [options]"
|
27
|
+
opts.on('-q', '--queue [QUEUE]', 'Specify queue which will be consumed') {|val| val and options[:queue] = val }
|
28
|
+
opts.on('-h', '--host [HOST]', 'Select redis host') {|val| val and options[:redis][:host] = val }
|
29
|
+
opts.on('-p', '--port [PORT]', Integer, 'Select redis port') {|val| val and options[:redis][:port] = val }
|
30
|
+
opts.on('-D', '--db [DATABASE]', 'Select redis database number') {|val| val and options[:redis][:db] = val }
|
31
|
+
opts.on('-d', '--daemon', 'Run in background') { options[:daemon] = true }
|
32
|
+
end.parse(ARGV)
|
33
|
+
|
34
|
+
React.start(options).join
|
data/lib/react.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'redis'
|
2
|
+
require 'thread'
|
3
|
+
|
4
|
+
# React is a simple application that allows for remote execution of commands.
|
5
|
+
# It uses Redis as a queue - it is blocking specified list and waits for new
|
6
|
+
# entries - when an entry appears, then it is executing recognized command.
|
7
|
+
#
|
8
|
+
# == Inspiration
|
9
|
+
#
|
10
|
+
# It's inspired by Simon Willson's example of "Queue-activated shell scripts"
|
11
|
+
# in his redis-tutorial:
|
12
|
+
#
|
13
|
+
# while [ 1 ] do
|
14
|
+
# redis-cli blpop restart-httpd 0
|
15
|
+
# apache2ctl graceful
|
16
|
+
# done
|
17
|
+
#
|
18
|
+
# == Examples
|
19
|
+
#
|
20
|
+
# Firs you have to prepare file with available commands. It can look like this:
|
21
|
+
#
|
22
|
+
# # my_commands.yml
|
23
|
+
# restart_httpd: |
|
24
|
+
# apache2ctl graceful
|
25
|
+
# restart_mysql: |
|
26
|
+
# /etc/init.d/mysql restart
|
27
|
+
# reboot: |
|
28
|
+
# reboot
|
29
|
+
#
|
30
|
+
# Now you can start consumer.
|
31
|
+
#
|
32
|
+
# react my_commands.yml
|
33
|
+
#
|
34
|
+
# == Commands
|
35
|
+
#
|
36
|
+
# While your consumer is working, you can push any command to it's queue
|
37
|
+
# (default queue name is `queue`), eg:
|
38
|
+
#
|
39
|
+
# redis-cli lpush queue restart_httpd
|
40
|
+
# redis-cli lpush queue reboot
|
41
|
+
#
|
42
|
+
# == Configuration
|
43
|
+
#
|
44
|
+
# There are few more runtime options, which can be useful for you.
|
45
|
+
#
|
46
|
+
# # it will be consuming commands from specified queue
|
47
|
+
# react my_commands.yml --queue "my:queue:name"
|
48
|
+
#
|
49
|
+
# # you can specify the database to which React should connect
|
50
|
+
# react my_commands.yml --host "yourhost.com" --port 6379 --db 2
|
51
|
+
#
|
52
|
+
# # and finally, you can demonize your application
|
53
|
+
# react my_commands.yml --daemonize
|
54
|
+
module React
|
55
|
+
|
56
|
+
# It starts the consumer loop.
|
57
|
+
def self.start(conf)
|
58
|
+
@config = conf
|
59
|
+
|
60
|
+
puts "== Connected to #{redis.client.id}"
|
61
|
+
puts "== Waiting for commands from `#{@config[:queue]}`"
|
62
|
+
|
63
|
+
if @config[:daemon]
|
64
|
+
puts "== Daemonizing..."
|
65
|
+
Daemons.daemonize
|
66
|
+
end
|
67
|
+
|
68
|
+
loop do
|
69
|
+
begin
|
70
|
+
cid = redis.blpop(@config[:queue], 0)[1]
|
71
|
+
if cmd = @config[:commands][cid.to_s]
|
72
|
+
puts "\e[33m[#{Time.now}]\e[0m Reacting for `#{cid}` command"
|
73
|
+
threads.add(Thread.new { system(cmd) })
|
74
|
+
end
|
75
|
+
rescue Interrupt
|
76
|
+
puts "\nCleaning up..."
|
77
|
+
break
|
78
|
+
rescue Exception => ex
|
79
|
+
puts "ERROR: #{ex}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
self
|
84
|
+
end
|
85
|
+
|
86
|
+
# Returns group of executor threads.
|
87
|
+
def self.threads
|
88
|
+
@threads ||= ThreadGroup.new
|
89
|
+
end
|
90
|
+
|
91
|
+
# It joins all alive threads, and it's waiting till they will finish.
|
92
|
+
def self.join
|
93
|
+
threads.list.each {|t| t.join if t.alive? }
|
94
|
+
end
|
95
|
+
|
96
|
+
# Redis client instance.
|
97
|
+
def self.redis
|
98
|
+
@redis ||= Redis.new(@config[:redis])
|
99
|
+
end
|
100
|
+
|
101
|
+
end # React
|
data/react.gemspec
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{react}
|
8
|
+
s.version = "0.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Kriss 'nu7hatch' Kowalik"]
|
12
|
+
s.date = %q{2010-08-16}
|
13
|
+
s.default_executable = %q{react}
|
14
|
+
s.description = %q{A simple application that allows remote execution of commands.}
|
15
|
+
s.email = %q{kriss.kowalik@gmail.com}
|
16
|
+
s.executables = ["react"]
|
17
|
+
s.extra_rdoc_files = [
|
18
|
+
"LICENSE",
|
19
|
+
"README.md"
|
20
|
+
]
|
21
|
+
s.files = [
|
22
|
+
".document",
|
23
|
+
".gitignore",
|
24
|
+
"LICENSE",
|
25
|
+
"README.md",
|
26
|
+
"Rakefile",
|
27
|
+
"bin/react",
|
28
|
+
"lib/react.rb",
|
29
|
+
"react.gemspec",
|
30
|
+
"test/react_test.rb",
|
31
|
+
"test/teststrap.rb"
|
32
|
+
]
|
33
|
+
s.homepage = %q{http://github.com/nu7hatch/react}
|
34
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
35
|
+
s.require_paths = ["lib"]
|
36
|
+
s.rubygems_version = %q{1.3.6}
|
37
|
+
s.summary = %q{Redis based remote commands executor.}
|
38
|
+
s.test_files = [
|
39
|
+
"test/teststrap.rb",
|
40
|
+
"test/react_test.rb"
|
41
|
+
]
|
42
|
+
|
43
|
+
if s.respond_to? :specification_version then
|
44
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
45
|
+
s.specification_version = 3
|
46
|
+
|
47
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
48
|
+
s.add_development_dependency(%q<riot>, [">= 0.11.3"])
|
49
|
+
s.add_runtime_dependency(%q<daemons>, [">= 0"])
|
50
|
+
else
|
51
|
+
s.add_dependency(%q<riot>, [">= 0.11.3"])
|
52
|
+
s.add_dependency(%q<daemons>, [">= 0"])
|
53
|
+
end
|
54
|
+
else
|
55
|
+
s.add_dependency(%q<riot>, [">= 0.11.3"])
|
56
|
+
s.add_dependency(%q<daemons>, [">= 0"])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
data/test/react_test.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'teststrap'
|
data/test/teststrap.rb
ADDED
metadata
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: react
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Kriss 'nu7hatch' Kowalik
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-08-16 00:00:00 +02:00
|
18
|
+
default_executable: react
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: riot
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
- 11
|
30
|
+
- 3
|
31
|
+
version: 0.11.3
|
32
|
+
type: :development
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: daemons
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 0
|
43
|
+
version: "0"
|
44
|
+
type: :runtime
|
45
|
+
version_requirements: *id002
|
46
|
+
description: A simple application that allows remote execution of commands.
|
47
|
+
email: kriss.kowalik@gmail.com
|
48
|
+
executables:
|
49
|
+
- react
|
50
|
+
extensions: []
|
51
|
+
|
52
|
+
extra_rdoc_files:
|
53
|
+
- LICENSE
|
54
|
+
- README.md
|
55
|
+
files:
|
56
|
+
- .document
|
57
|
+
- .gitignore
|
58
|
+
- LICENSE
|
59
|
+
- README.md
|
60
|
+
- Rakefile
|
61
|
+
- bin/react
|
62
|
+
- lib/react.rb
|
63
|
+
- react.gemspec
|
64
|
+
- test/react_test.rb
|
65
|
+
- test/teststrap.rb
|
66
|
+
has_rdoc: true
|
67
|
+
homepage: http://github.com/nu7hatch/react
|
68
|
+
licenses: []
|
69
|
+
|
70
|
+
post_install_message:
|
71
|
+
rdoc_options:
|
72
|
+
- --charset=UTF-8
|
73
|
+
require_paths:
|
74
|
+
- lib
|
75
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
segments:
|
80
|
+
- 0
|
81
|
+
version: "0"
|
82
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
segments:
|
87
|
+
- 0
|
88
|
+
version: "0"
|
89
|
+
requirements: []
|
90
|
+
|
91
|
+
rubyforge_project:
|
92
|
+
rubygems_version: 1.3.6
|
93
|
+
signing_key:
|
94
|
+
specification_version: 3
|
95
|
+
summary: Redis based remote commands executor.
|
96
|
+
test_files:
|
97
|
+
- test/teststrap.rb
|
98
|
+
- test/react_test.rb
|