outoftime-shell_elf 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,116 @@
1
+ = Shell Elf
2
+
3
+ http://github.com/outoftime/shell_elf
4
+
5
+ Shell Elf is a small, standalone daemon whose sole purpose is to read shell
6
+ commands out of a Starling queue, execute them, and optionally POST back to an
7
+ HTTP service on success or failure. Shell Elf is distributed as a RubyGem, but
8
+ is not meant to be included as a library; rather, it contains the standalone
9
+ `shell-elf` executable, which starts and stops the daemon.
10
+
11
+ Features
12
+
13
+ * Send a single shell command or a batch of commands to be executed serially
14
+ * Specify postback URLs for success and failure, so that your application can
15
+ be notified when batch has completed.
16
+ * Gracefully catch TERM and INT signals, either finishing the current batch or
17
+ requeueing it for later processing before exiting.
18
+
19
+ == Usage
20
+
21
+ === Installation
22
+
23
+ sudo gem install outoftime-shell_elf --source=gems.github.com
24
+
25
+ === Starting the daemon
26
+
27
+ shell-elf start -- [options]
28
+
29
+ The following options are available; none are required:
30
+
31
+ [-q/--queue] name of the Starling queue to read from (default: shell_elf)
32
+ [-h/--host] hostname at which the Starling daemon is running (default: localhost)
33
+ [-p/--port] port on which the Starling daemon is listening (default: 22122)
34
+ [--log-file] full path to a log file (default: no logging)
35
+ [--log-level] level at which to log (default: WARN)
36
+
37
+ === Stopping the daemon
38
+
39
+ shell-elf stop
40
+
41
+ === Sending commands from your application
42
+
43
+ require 'rubygems'
44
+ gem 'starling-starling'
45
+ require 'starling'
46
+
47
+ starling = Starling.new('localhost:22122')
48
+
49
+ # send the single command `touch /tmp/hey`
50
+ starling.set('shell_elf', :command => ['touch', '/tmp/hey'])
51
+
52
+ # send the two commands `touch /tmp/one` followed by `touch /tmp/2`
53
+ starling.set('shell_elf', :commands => [['touch', '/tmp/one'], ['touch', '/tmp/two']])
54
+
55
+ # send a command with success/failure postbacks
56
+ starling.set('shell_elf', :command => ['touch', '/tmp/hey'],
57
+ :options => { :success => { 'http://localhost:3000/command/success' },
58
+ { :failure => { 'http://localhost:3000/command/failure' }})
59
+
60
+ # send a command with instructions to requeue if the daemon is interrupted
61
+ starling.set('shell_elf', :command => ['sleep', '120'],
62
+ :options => { :on_interrupt => :requeue })
63
+
64
+ === A note about batches and postbacks
65
+
66
+ If the postbacks are specified (you can specify neither, either, or both),
67
+ ShellElf will send an HTTP POST request to the specified URL. The batch is
68
+ considered successful if all of the commands exit with a status code of 0. If
69
+ one of the commands in the batch exits with a non-zero status code, *the
70
+ rest of the commands in the batch are not run*, and the failure postback will
71
+ be sent. ShellElf waits until the postback request completes before retrieving
72
+ the next batch from the queue.
73
+
74
+ == The ShellElf Client
75
+
76
+ ...doesn't exist. I'm a big fan of libraries presenting intuitive APIs to the
77
+ application layer, but in this case a general-purpose client would be so
78
+ trivial that it doesn't seem worth writing one. Further, I assume that in the
79
+ general use case, applications will want to build task-specific APIs that wrap
80
+ the task of shelling out via Starling. Having a further layer of abstraction
81
+ doesn't seem to add much value.
82
+
83
+ == Dependencies
84
+
85
+ * escape
86
+ * starling-starling
87
+ * daemons
88
+ * choice
89
+
90
+ == Contact and Futher Reading
91
+
92
+ Contact: Mat Brown <mat@patch.com>
93
+ Further Reading: http://outofti.me/tagged/shell_elf
94
+
95
+ == The MIT License
96
+
97
+ Copyright (c) 2009 Mat Brown
98
+
99
+ Permission is hereby granted, free of charge, to any person obtaining a copy
100
+ of this software and associated documentation files (the "Software"), to deal
101
+ in the Software without restriction, including without limitation the rights
102
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
103
+ copies of the Software, and to permit persons to whom the Software is
104
+ furnished to do so, subject to the following conditions:
105
+
106
+ The above copyright notice and this permission notice shall be included in
107
+ all copies or substantial portions of the Software.
108
+
109
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
110
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
111
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
112
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
113
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
114
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
115
+ THE SOFTWARE.
116
+
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+
3
+ ENV['RUBYOPT'] = '-W1'
4
+
5
+ Dir['tasks/**/*.rake'].each { |t| load t }
6
+
7
+ task :default => :spec
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :minor: 9
3
+ :patch: 0
4
+ :major: 0
data/bin/shell-elf ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ gem 'daemons'
5
+ require 'tmpdir'
6
+ require 'daemons'
7
+
8
+ Daemons.run(File.join(File.dirname(File.dirname(__FILE__)), 'script', 'shell-elf-listener'),
9
+ :dir_mode => :normal,
10
+ :dir => Dir.tmpdir)
@@ -0,0 +1,41 @@
1
+ module ShellElf
2
+ class Batch
3
+ class <<self
4
+ def execute(params)
5
+ new(params).execute
6
+ end
7
+ end
8
+
9
+ def initialize(params)
10
+ options = params[:options] || {}
11
+ @commands =
12
+ if params[:command]
13
+ [params[:command]]
14
+ elsif params[:commands]
15
+ params[:commands]
16
+ else
17
+ []
18
+ end
19
+ @success_postback = options.delete(:success)
20
+ @failure_postback = options.delete(:failure)
21
+ end
22
+
23
+ def execute
24
+ return self if @commands.empty?
25
+ if @commands.all? { |command| Job.execute(command).success? }
26
+ if @success_postback
27
+ ShellElf.logger.debug("Posting back to #{@success_postback}")
28
+ Net::HTTP.post_form(URI.parse(@success_postback), {})
29
+ else
30
+ ShellElf.logger.debug("No success postback given")
31
+ end
32
+ elsif @failure_postback
33
+ ShellElf.logger.debug("Posting back to #{@failure_postback}")
34
+ Net::HTTP.post_form(URI.parse(@failure_postback), {})
35
+ else
36
+ ShellElf.logger.debug("No failure postback given")
37
+ end
38
+ self
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,32 @@
1
+ module ShellElf
2
+ class Job
3
+ class <<self
4
+ def execute(command)
5
+ new(command).execute
6
+ end
7
+ end
8
+
9
+ def initialize(command)
10
+ @command = command
11
+ end
12
+
13
+ def execute
14
+ escaped_command = Escape.shell_command(@command)
15
+ ShellElf.logger.info(escaped_command)
16
+ fork do
17
+ # this way we can detach from the shell session
18
+ # so the child process doesn't receive SIGINTs
19
+ Process.setsid
20
+ exec(escaped_command)
21
+ end
22
+ Process.wait(-1)
23
+ @success = $? == 0
24
+ ShellElf.logger.debug(@success ? 'success' : 'failed')
25
+ self
26
+ end
27
+
28
+ def success?
29
+ @success
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,61 @@
1
+ module ShellElf
2
+ class Runner
3
+ class <<self
4
+ private :new
5
+
6
+ def run(options)
7
+ runner = new(options)
8
+ signal_handler = proc do
9
+ ShellElf.logger.info("Received signal - gracefully exiting...")
10
+ exit if runner.interrupted!
11
+ end
12
+ trap('INT', &signal_handler)
13
+ trap('TERM', &signal_handler)
14
+ runner.run
15
+ end
16
+ end
17
+
18
+ def initialize(options)
19
+ @options = options
20
+ end
21
+
22
+ def run
23
+ until @interrupted
24
+ if @current = starling.fetch(@options[:queue])
25
+ ShellElf.logger.debug("RUNNER: #{@current.inspect}")
26
+ ShellElf::Batch.execute(@current)
27
+ else
28
+ sleep Starling::WAIT_TIME
29
+ end
30
+ end
31
+ end
32
+
33
+ def interrupted!
34
+ # return true unless @current
35
+ if @current && @current[:options] && @current[:options][:on_interrupt] == :requeue
36
+ starling.set(@options[:queue], @current)
37
+ true
38
+ else
39
+ @interrupted = true
40
+ false
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def starling(delay = nil)
47
+ begin
48
+ sleep(delay) if delay
49
+ @queue ||= Starling.new("#{@options[:host]}:#{@options[:port]}")
50
+ @queue.stats
51
+ @queue
52
+ rescue MemCache::MemCacheError => e
53
+ ShellElf.logger.error("Unable to connect to Starling: #{e.message}")
54
+ @queue = nil
55
+ delay ||= 0.125
56
+ delay = delay *= 2 if delay < 1
57
+ retry
58
+ end
59
+ end
60
+ end
61
+ end
data/lib/shell_elf.rb ADDED
@@ -0,0 +1,18 @@
1
+ gem 'escape'
2
+ require 'net/http'
3
+ require 'escape'
4
+ require 'logger'
5
+
6
+ module ShellElf
7
+ autoload :Job, File.expand_path(File.join(File.dirname(__FILE__), 'shell_elf', 'job.rb'))
8
+ autoload :Batch, File.expand_path(File.join(File.dirname(__FILE__), 'shell_elf', 'batch.rb'))
9
+ autoload :Runner, File.expand_path(File.join(File.dirname(__FILE__), 'shell_elf', 'runner.rb'))
10
+
11
+ class <<self
12
+ attr_writer :logger
13
+
14
+ def logger
15
+ @logger ||= Logger.new(nil) # stub logger
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ gem 'starling-starling'
5
+ gem 'choice'
6
+ require 'starling'
7
+ require 'choice'
8
+ require File.join(File.dirname(File.dirname(__FILE__)), 'lib', 'shell_elf')
9
+
10
+ Choice.options do
11
+ option :queue do
12
+ short '-q'
13
+ long '--queue=QUEUE'
14
+ desc 'Name of starling queue to process'
15
+ default 'shell_elf'
16
+ end
17
+
18
+ option :host do
19
+ short '-h'
20
+ long '--host=HOST'
21
+ desc 'Host at which to connect to Starling'
22
+ default '127.0.0.1'
23
+ end
24
+
25
+ option :port do
26
+ short '-p'
27
+ long '--port=PORT'
28
+ desc 'Port on which to connect to Starling'
29
+ default '22122'
30
+ end
31
+
32
+ option :log_file do
33
+ long '--log-file=LOG_FILE'
34
+ desc 'File for logging output'
35
+ end
36
+
37
+ option :log_level do
38
+ long '--log-level=LOG_LEVEL'
39
+ desc 'Log level'
40
+ valid Logger::SEV_LABEL
41
+ default 'WARN'
42
+ end
43
+ end
44
+
45
+ ShellElf.logger = Logger.new(Choice.choices[:log_file])
46
+ ShellElf.logger.progname = "shell_elf[#{Choice.choices[:queue]}]"
47
+ ShellElf.logger.level = Logger.const_get(Choice.choices[:log_level])
48
+
49
+ ShellElf.logger.info("Starting ShellElf...")
50
+ ShellElf::Runner.run(Choice.choices)
51
+ ShellElf.logger.info("Exiting...")
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+ gem 'sinatra'
3
+ require 'fileutils'
4
+ require 'tmpdir'
5
+ require 'sinatra'
6
+
7
+ post '/touch/:filename' do
8
+ FileUtils.touch(File.join(Dir.tmpdir, 'shell_elf', 'sandbox', params[:filename]))
9
+ end
@@ -0,0 +1,139 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe 'ShellElf' do
4
+ include SpecHelper
5
+
6
+ shared_examples_for 'successful batch' do
7
+ it 'should postback to success URL' do
8
+ File.exist?(sandbox('success')).should be_true
9
+ end
10
+
11
+ it 'should not postback to failure URL' do
12
+ File.exist?(sandbox('failure')).should be_false
13
+ end
14
+ end
15
+
16
+ shared_examples_for 'failed batch' do
17
+ it 'should postback to failure URL' do
18
+ File.exist?(sandbox('failure')).should be_true
19
+ end
20
+
21
+ it 'should not postback to success URL' do
22
+ File.exist?(sandbox('success')).should be_false
23
+ end
24
+ end
25
+
26
+ it 'should run a single command' do
27
+ starling_send_and_wait(:command => ['touch', sandbox('test')])
28
+ File.exist?(sandbox('test')).should be_true
29
+ end
30
+
31
+ it 'should run multiple commands' do
32
+ starling_send_and_wait(:commands => [['touch', sandbox('first')], ['touch', sandbox('second')]])
33
+ %w(first second).all? { |f| File.exist?(sandbox(f)) }.should be_true
34
+ end
35
+
36
+ describe 'on success with single command' do
37
+ before :each do
38
+ starling_send_and_wait(:command => ['true'], :options => { :success => http_touch_url('success'), :failure => http_touch_url('failure') })
39
+ end
40
+
41
+ it_should_behave_like 'successful batch'
42
+ end
43
+
44
+ describe 'on failure with single command' do
45
+ before :each do
46
+ starling_send_and_wait(:command => ['false'], :options => { :success => http_touch_url('success'), :failure => http_touch_url('failure') })
47
+ end
48
+
49
+ it_should_behave_like 'failed batch'
50
+ end
51
+
52
+ describe 'with failed command followed by successful command' do
53
+ before :each do
54
+ starling_send_and_wait(:commands => [['false'], ['touch', sandbox('never_get_here')]], :options => { :success => http_touch_url('success'), :failure => http_touch_url('failure') })
55
+ end
56
+
57
+ it_should_behave_like 'failed batch'
58
+
59
+ it 'should not run successful command' do
60
+ File.exist?(sandbox('never_get_here')).should be_false
61
+ end
62
+ end
63
+
64
+ describe 'with successful command followed by failed command' do
65
+ before :each do
66
+ starling_send_and_wait(:commands => [['touch', sandbox('first')], ['false']], :options => { :success => http_touch_url('success'), :failure => http_touch_url('failure') })
67
+ end
68
+
69
+ it_should_behave_like 'failed batch'
70
+
71
+ it 'should run successful command' do
72
+ File.exist?(sandbox('first')).should be_true
73
+ end
74
+ end
75
+
76
+ describe 'with bogus input' do
77
+ before :each do
78
+ starling_send('bogus')
79
+ starling_send_and_wait(:command => ['touch', sandbox('success')])
80
+ end
81
+
82
+ it 'should continue running' do
83
+ File.exist?(sandbox('success')).should be_true
84
+ end
85
+ end
86
+
87
+ describe 'with empty commands' do
88
+ before :each do
89
+ starling_send(:commands => [], :success => http_touch_url('success'), :failure => http_touch_url('failure'))
90
+ starling_send_and_wait(:command => ['touch', sandbox('after')])
91
+ end
92
+
93
+ %w(success failure).each do |postback|
94
+ it "should not send #{postback} postback" do
95
+ File.exist?(sandbox(postback)).should be_false
96
+ end
97
+ end
98
+
99
+ it 'should continue running' do
100
+ File.exist?(sandbox('after')).should be_true
101
+ end
102
+ end
103
+
104
+ %w(TERM INT).each do |signal|
105
+ describe "when SIG#{signal} sent" do
106
+ before :each do
107
+ starling_wait
108
+ starling_send(:commands => [['kill', '-s', signal, shell_elf_pid.to_s], ['touch', sandbox('done')]])
109
+ starling_send(:command => ['touch', sandbox('never_get_here')])
110
+ wait_for_exit
111
+ end
112
+
113
+ it 'should trap the signal and complete current command before exiting' do
114
+ File.exist?(sandbox('done')).should be_true
115
+ end
116
+
117
+ it 'should not run any further commands' do
118
+ File.exist?(sandbox('never_get_here')).should be_false
119
+ end
120
+ end
121
+
122
+ describe "when SIG#{signal} sent with requeue option" do
123
+ before :each do
124
+ @params = { :commands => [['kill', '-s', signal, shell_elf_pid.to_s], ['touch', sandbox('requeued')]], :options => { :on_interrupt => :requeue }}
125
+ starling_wait
126
+ starling_send(@params)
127
+ wait_for_exit
128
+ end
129
+
130
+ it 'should trap the signal and requeue the job' do
131
+ @starling.fetch('shell_elf_test').should == @params
132
+ end
133
+
134
+ it 'should not finish the running command' do
135
+ File.exist?(sandbox('requeued')).should be_false
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,77 @@
1
+ require 'rubygems'
2
+ gem 'rspec'
3
+ gem 'starling-starling'
4
+ require 'fileutils'
5
+ require 'tmpdir'
6
+ require 'spec'
7
+ require 'starling'
8
+ require 'ruby-debug'
9
+
10
+ Spec::Runner.configure do |config|
11
+ config.before(:all) do
12
+ @starling = Starling.new('localhost:73787')
13
+ begin
14
+ @starling.stats
15
+ rescue
16
+ abort('Starling is not responding! Run rake spec:environment to start background services.')
17
+ end
18
+ end
19
+
20
+ config.before(:each) do
21
+ @starling.delete('shell_elf_test')
22
+ for entry in Dir.glob(File.join(Dir.tmpdir, 'shell_elf', 'sandbox', '*'))
23
+ if File.file?(entry)
24
+ FileUtils.rm_r(File.expand_path(entry))
25
+ end
26
+ end
27
+ end
28
+
29
+ config.after(:each) do
30
+ @starling.delete('shell_elf_test')
31
+ end
32
+ end
33
+
34
+ module SpecHelper
35
+ def sandbox(*path)
36
+ File.expand_path(File.join(Dir.tmpdir, 'shell_elf', 'sandbox', path))
37
+ end
38
+
39
+ def starling_send_and_wait(params)
40
+ starling_send(params)
41
+ starling_wait
42
+ end
43
+
44
+ def starling_send(params)
45
+ @starling.set('shell_elf_test', params)
46
+ end
47
+
48
+ def starling_wait
49
+ starling_send(:command => ['true'])
50
+ 100.times do |i|
51
+ return if @starling.sizeof('shell_elf_test') == 0
52
+ sleep 0.01
53
+ end
54
+ raise 'Timed out waiting for Starling queue to clear.'
55
+ end
56
+
57
+ def http_touch_url(filename)
58
+ "http://localhost:7397/touch/#{filename}"
59
+ end
60
+
61
+ def wait_for_exit(timeout = 1)
62
+ pid = shell_elf_pid
63
+ (timeout * 100).times do
64
+ begin
65
+ Process.kill(0, pid)
66
+ sleep 0.01
67
+ rescue Errno::ESRCH
68
+ return
69
+ end
70
+ end
71
+ raise "Timed out waiting for pid #{pid} to end!"
72
+ end
73
+
74
+ def shell_elf_pid
75
+ File.open(File.join(Dir.tmpdir, 'shell_elf', 'log', 'shell-elf.pid')) { |f| f.read.to_i }
76
+ end
77
+ end
@@ -0,0 +1,22 @@
1
+ begin
2
+ gem 'technicalpickles-jeweler', '~> 0.8.1'
3
+ require 'jeweler'
4
+ Jeweler::Tasks.new do |s|
5
+ s.name = 'shell_elf'
6
+ s.executables = 'shell-elf'
7
+ s.summary = 'Daemon which executes shell commands read out of a Starling queue and optionally posts back to an HTTP server on success/failure'
8
+ s.email = 'mat@patch.com'
9
+ s.homepage = 'http://github.com/outoftime/sunspot'
10
+ s.description = 'Daemon which executes shell commands read out of a Starling queue and optionally posts back to an HTTP server on success/failure'
11
+ s.authors = ['Mat Brown']
12
+ s.files = FileList['[A-Z]*', '{bin,lib,spec,tasks,script}/**/*']
13
+ s.has_rdoc = false
14
+ s.add_dependency 'escape', '>= 0.0.4'
15
+ s.add_dependency 'starling-starling', '~> 0.9'
16
+ s.add_dependency 'daemons', '~> 1.0'
17
+ s.add_dependency 'choice', '>= 0.1'
18
+ s.add_development_dependency 'rspec', '~> 1.1'
19
+ s.add_development_dependency 'ruby-debug', '~> 0.10'
20
+ s.add_development_dependency 'sinatra', '>= 0.9'
21
+ end
22
+ end
data/tasks/spec.rake ADDED
@@ -0,0 +1,36 @@
1
+ require 'fileutils'
2
+ require 'tmpdir'
3
+ require 'spec/rake/spectask'
4
+
5
+ desc 'Run spec suite'
6
+ Spec::Rake::SpecTask.new('spec') do |t|
7
+ t.spec_files = FileList['spec/**/*_spec.rb']
8
+ end
9
+
10
+ namespace :spec do
11
+ desc "Start spec environment processes"
12
+ task :environment do
13
+ base_dir = File.expand_path(File.dirname(File.dirname(__FILE__)))
14
+ spec_dir = File.join(base_dir, 'spec')
15
+ tmp_dir = File.join(Dir.tmpdir, 'shell_elf')
16
+ FileUtils.mkdir_p(File.join(tmp_dir, 'log'))
17
+ FileUtils.mkdir_p(File.join(tmp_dir, 'sandbox'))
18
+ running = true
19
+ trap('INT') { running = false }
20
+ trap('TERM') { running = false }
21
+ fork do
22
+ exec("starling -P #{File.join(tmp_dir, 'log', 'starling.pid')} -p 73787")
23
+ end
24
+ fork do
25
+ exec("ruby #{spec_dir}/services/http_service.rb -p 7397")
26
+ end
27
+ while running
28
+ shell_elf_pid = fork do
29
+ exec("#{File.join(base_dir, 'bin', 'shell-elf')} run -- -p 73787 -q shell_elf_test --log-file=#{File.join(tmp_dir, 'log', 'shell_elf.out')} --log-level=DEBUG")
30
+ end
31
+ File.open(File.join(tmp_dir, 'log', 'shell-elf.pid'), 'w') { |f| f << shell_elf_pid }
32
+ Process.waitpid(shell_elf_pid)
33
+ end
34
+ Process.waitall
35
+ end
36
+ end
metadata ADDED
@@ -0,0 +1,138 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: outoftime-shell_elf
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.0
5
+ platform: ruby
6
+ authors:
7
+ - Mat Brown
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-04-01 00:00:00 -07:00
13
+ default_executable: shell-elf
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: escape
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.4
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: starling-starling
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: "0.9"
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: daemons
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: "1.0"
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: choice
47
+ type: :runtime
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0.1"
54
+ version:
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ type: :development
58
+ version_requirement:
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ~>
62
+ - !ruby/object:Gem::Version
63
+ version: "1.1"
64
+ version:
65
+ - !ruby/object:Gem::Dependency
66
+ name: ruby-debug
67
+ type: :development
68
+ version_requirement:
69
+ version_requirements: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ~>
72
+ - !ruby/object:Gem::Version
73
+ version: "0.10"
74
+ version:
75
+ - !ruby/object:Gem::Dependency
76
+ name: sinatra
77
+ type: :development
78
+ version_requirement:
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: "0.9"
84
+ version:
85
+ description: Daemon which executes shell commands read out of a Starling queue and optionally posts back to an HTTP server on success/failure
86
+ email: mat@patch.com
87
+ executables:
88
+ - shell-elf
89
+ extensions: []
90
+
91
+ extra_rdoc_files: []
92
+
93
+ files:
94
+ - Rakefile
95
+ - README.rdoc
96
+ - VERSION.yml
97
+ - bin/shell-elf
98
+ - lib/shell_elf
99
+ - lib/shell_elf/runner.rb
100
+ - lib/shell_elf/batch.rb
101
+ - lib/shell_elf/job.rb
102
+ - lib/shell_elf.rb
103
+ - spec/spec_helper.rb
104
+ - spec/services
105
+ - spec/services/http_service.rb
106
+ - spec/shell_elf_spec.rb
107
+ - tasks/gemspec.rake
108
+ - tasks/spec.rake
109
+ - script/shell-elf-listener
110
+ has_rdoc: true
111
+ homepage: http://github.com/outoftime/sunspot
112
+ post_install_message:
113
+ rdoc_options:
114
+ - --inline-source
115
+ - --charset=UTF-8
116
+ require_paths:
117
+ - lib
118
+ required_ruby_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: "0"
123
+ version:
124
+ required_rubygems_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: "0"
129
+ version:
130
+ requirements: []
131
+
132
+ rubyforge_project:
133
+ rubygems_version: 1.2.0
134
+ signing_key:
135
+ specification_version: 2
136
+ summary: Daemon which executes shell commands read out of a Starling queue and optionally posts back to an HTTP server on success/failure
137
+ test_files: []
138
+