outoftime-shell_elf 0.9.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.
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
+