cuted 1.0.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/bin/cute +55 -0
- data/bin/cuted +57 -0
- data/lib/cuted/command.rb +62 -0
- data/lib/cuted/queue.rb +96 -0
- metadata +97 -0
data/bin/cute
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# David Selassie
|
|
4
|
+
# April 28, 2011
|
|
5
|
+
|
|
6
|
+
# USAGE: cute CMD
|
|
7
|
+
# Enqueues a command to be executed by cuted.
|
|
8
|
+
|
|
9
|
+
require 'trollop'
|
|
10
|
+
require 'drb'
|
|
11
|
+
|
|
12
|
+
config = {'uri' => 'drbunix:///tmp/cuted.sock'}
|
|
13
|
+
if File.exists?(File.expand_path('~/.cuterc')) then
|
|
14
|
+
config.merge!(ParseConfig.new(File.expand_path('~/.cuterc')).params)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
options = Trollop::options do
|
|
18
|
+
opt :dir, 'run command in a dir other than the CWD', :default => Dir.pwd
|
|
19
|
+
opt :weight, 'weight of this command', :default => 1
|
|
20
|
+
opt :priority, 'priority of this command', :default => 0
|
|
21
|
+
opt :log, 'custom log file to use'
|
|
22
|
+
opt :prowl_key, 'custom Prowl key to notify'
|
|
23
|
+
opt :uri, 'server to submit commands to', :type => :string,
|
|
24
|
+
:default => ENV['cuted_uri'] || config['uri']
|
|
25
|
+
end
|
|
26
|
+
Trollop::die 'cute: You must specify a command' if ARGV.length < 1
|
|
27
|
+
|
|
28
|
+
# Join together the rest of the arguments as the command.
|
|
29
|
+
cmd = ARGV.join(' ')
|
|
30
|
+
|
|
31
|
+
begin
|
|
32
|
+
DRb.start_service
|
|
33
|
+
queue = DRbObject.new(nil, options[:uri])
|
|
34
|
+
|
|
35
|
+
# Get the real command stored in the queue.
|
|
36
|
+
cmd = queue.push_cmd(cmd)
|
|
37
|
+
|
|
38
|
+
# Set up attributes.
|
|
39
|
+
cmd.dir = options[:dir]
|
|
40
|
+
cmd.priority = options[:priority]
|
|
41
|
+
cmd.weight = options[:weight]
|
|
42
|
+
# Set a custom Prowl key if one is specified.
|
|
43
|
+
cmd.prowl_key = options[:prowl_key] if options[:prowl_key]
|
|
44
|
+
# Set a custom log if one is specified and it isn't empty.
|
|
45
|
+
cmd.log = options[:log] if options[:log] and options[:log].size > 0
|
|
46
|
+
|
|
47
|
+
# Try to run tasks.
|
|
48
|
+
queue.pop_run
|
|
49
|
+
|
|
50
|
+
puts "Command '#{cmd}' submitted"
|
|
51
|
+
rescue DRb::DRbConnError => err
|
|
52
|
+
puts "Could not connect to '#{options[:server]}'"
|
|
53
|
+
|
|
54
|
+
exit 1
|
|
55
|
+
end
|
data/bin/cuted
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# David Selassie
|
|
4
|
+
# April 28, 2011
|
|
5
|
+
|
|
6
|
+
# USAGE: cuted start | stop | run [-- ...]
|
|
7
|
+
# A simple process queue daemon. Add processes using the cute command.
|
|
8
|
+
|
|
9
|
+
# Requires a daemonizer!
|
|
10
|
+
require 'daemons'
|
|
11
|
+
# Also requires this simple config parser.
|
|
12
|
+
require 'parseconfig'
|
|
13
|
+
require 'logger'
|
|
14
|
+
require 'drb'
|
|
15
|
+
|
|
16
|
+
require 'cuted/queue'
|
|
17
|
+
|
|
18
|
+
Daemons.run_proc('cuted', :dir => '~', :log_output => true) do
|
|
19
|
+
logger = Logger.new(STDOUT)
|
|
20
|
+
|
|
21
|
+
# Set up defaults that might be overwritten.
|
|
22
|
+
config = {'max_concurrent' => 4, 'log' => 'cute.log',
|
|
23
|
+
'uri' => 'drbunix:///tmp/cuted.sock'}
|
|
24
|
+
# See if there's any user settings in ~/.cuterc and write them on top.
|
|
25
|
+
if File.exists?(File.expand_path('~/.cuterc'))
|
|
26
|
+
config.merge!(ParseConfig.new(File.expand_path('~/.cuterc')).params)
|
|
27
|
+
# If the log variable is there but blank, make it nil.
|
|
28
|
+
if config['log'] and config['log'].strip.length == 0 then
|
|
29
|
+
config['log'] = nil
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Use the environment variable PROWLKEY if it's there.
|
|
34
|
+
config['prowl_key'] = ENV['PROWLKEY'] if ENV['PROWLKEY']
|
|
35
|
+
|
|
36
|
+
logger.info "Will run #{config['max_concurrent']} processes concurrently"
|
|
37
|
+
logger.info 'Will log command outputs' if config['log']
|
|
38
|
+
if config['prowl_key'] then
|
|
39
|
+
logger.info "Will notify Prowl key #{config['prowl_key']} of completions"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
begin
|
|
43
|
+
queue = Cuted::Queue.new(:logger => logger,
|
|
44
|
+
:max_concurrent => config['max_concurrent'],
|
|
45
|
+
:defaults => {:prowl_key => config['prowl_key'],
|
|
46
|
+
:log => config['log']})
|
|
47
|
+
DRb.start_service(config['uri'], queue)
|
|
48
|
+
|
|
49
|
+
logger.info "Running on #{DRb.uri}"
|
|
50
|
+
ENV['cuted_uri'] = DRb.uri
|
|
51
|
+
|
|
52
|
+
# Wait. Let clients call methods on the queue.
|
|
53
|
+
DRb.thread.join
|
|
54
|
+
rescue Interrupt
|
|
55
|
+
logger.info 'Exiting'
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# David Selassie
|
|
4
|
+
# March 29, 2012
|
|
5
|
+
# cutejob.rb
|
|
6
|
+
|
|
7
|
+
# Structure for holding a queued shell command.
|
|
8
|
+
|
|
9
|
+
module Cuted
|
|
10
|
+
class Command
|
|
11
|
+
require 'drb'
|
|
12
|
+
require 'fileutils'
|
|
13
|
+
|
|
14
|
+
# Make sure the command is stored on the server.
|
|
15
|
+
include DRbUndumped
|
|
16
|
+
|
|
17
|
+
attr_accessor :dir, :cmd, :weight, :priority, :log, :prowl_key
|
|
18
|
+
attr_reader :pid
|
|
19
|
+
|
|
20
|
+
def initialize(cmd, opts = {})
|
|
21
|
+
@cmd = cmd.to_s
|
|
22
|
+
# If the options are set, use them; otherwise defaults.
|
|
23
|
+
@dir = opts[:dir] || Dir.pwd
|
|
24
|
+
# Since we can force opts[:log] to be nil, and we don't want the default.
|
|
25
|
+
@log = opts.has_key?(:log) ? opts[:log] : 'cute.log'
|
|
26
|
+
@priority = opts[:priority] || 0
|
|
27
|
+
@weight = opts[:weight] || 1
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Runs in the current thread.
|
|
31
|
+
def run
|
|
32
|
+
# Make sure the desired working directory exists.
|
|
33
|
+
FileUtils.makedirs(@dir) if not File.directory?(@dir)
|
|
34
|
+
# Actually run the command.
|
|
35
|
+
if @log then
|
|
36
|
+
@pid = spawn(@cmd, :chdir => @dir, [:out, :err] => [@log, 'a'])
|
|
37
|
+
else
|
|
38
|
+
@pid = spawn(@cmd, :chdir => @dir, [:out, :err] => '/dev/null')
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# In the forked process, wait for the command to be done.
|
|
42
|
+
Process.waitall
|
|
43
|
+
|
|
44
|
+
# Report that this command finished.
|
|
45
|
+
begin
|
|
46
|
+
require 'prowler'
|
|
47
|
+
Prowler.verify_certificate = false
|
|
48
|
+
Prowler.application = 'cuted'
|
|
49
|
+
Prowler.notify(@cmd, "Finished in #{@dir}", @prowl_key) if @prowl_key
|
|
50
|
+
rescue LoadError
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Return the CuteCommand.
|
|
54
|
+
self
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Print out dir> command string.
|
|
58
|
+
def to_s
|
|
59
|
+
"#{@dir}> #{@cmd}"
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
data/lib/cuted/queue.rb
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# David Selassie
|
|
4
|
+
# March 29, 2012
|
|
5
|
+
# cutequeue.rb
|
|
6
|
+
|
|
7
|
+
# Priority command queue.
|
|
8
|
+
|
|
9
|
+
module Cuted
|
|
10
|
+
class Queue
|
|
11
|
+
attr_accessor :running, :queue
|
|
12
|
+
|
|
13
|
+
def initialize(opts = {})
|
|
14
|
+
@queue = []
|
|
15
|
+
@running = []
|
|
16
|
+
|
|
17
|
+
# If the options are set, then use them; otherwise defaults.
|
|
18
|
+
@defaults = opts[:defaults] || {}
|
|
19
|
+
@max_concurrent = opts[:max_concurrent] || 4
|
|
20
|
+
@logger = opts[:logger]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Takes the text of a command or any structure that responds to :run.
|
|
24
|
+
def push_cmd(cmd)
|
|
25
|
+
@logger.info "Pushed command '#{cmd}'" if @logger
|
|
26
|
+
|
|
27
|
+
# Really we just need a command to be able to run.
|
|
28
|
+
if not cmd.respond_to?(:run)
|
|
29
|
+
require 'cuted/command'
|
|
30
|
+
cmd = Command.new(cmd)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Set up the command with the default values.
|
|
34
|
+
@defaults.each do |k, v|
|
|
35
|
+
cmd.send("#{k}=", v) if cmd.respond_to?("#{k}=")
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
@queue << cmd
|
|
39
|
+
sort
|
|
40
|
+
|
|
41
|
+
cmd
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Puts the highest priority commands first.
|
|
45
|
+
def sort
|
|
46
|
+
# If there is no priority, use 0.
|
|
47
|
+
@queue.sort_by! { |cmd| cmd.respond_to?(:priority) ? -cmd.priority : 0 }
|
|
48
|
+
|
|
49
|
+
self
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Return the highest priority command.
|
|
53
|
+
def peek
|
|
54
|
+
@queue[0]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Find the highest priority item and run it.
|
|
58
|
+
# If continue is true, then run other commands in the queue when done.
|
|
59
|
+
def pop_run(continue = true)
|
|
60
|
+
# If there are no commands, do nothing.
|
|
61
|
+
if @queue.length < 1 then
|
|
62
|
+
@logger.info 'No commands to run' if @logger
|
|
63
|
+
return
|
|
64
|
+
# If there isn't enough free weight to run, do nothing.
|
|
65
|
+
elsif peek.weight > @max_concurrent - @running.size and
|
|
66
|
+
@running.size > 0 then
|
|
67
|
+
@logger.info 'Not enough free weight to run' if @logger
|
|
68
|
+
return
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Pop off the highest priority command.
|
|
72
|
+
cmd = @queue.delete_at(0)
|
|
73
|
+
sort
|
|
74
|
+
|
|
75
|
+
# Run the command and return it.
|
|
76
|
+
@logger.info "Starting #{cmd}" if @logger
|
|
77
|
+
|
|
78
|
+
# Find out the weight of this command.
|
|
79
|
+
weight = cmd.respond_to?(:weight) ? cmd.weight : 1
|
|
80
|
+
# Add this command that many times to the running array.
|
|
81
|
+
@running.concat([cmd] * weight)
|
|
82
|
+
|
|
83
|
+
# In a separate thread...
|
|
84
|
+
thread = Thread.new do
|
|
85
|
+
# Run the command.
|
|
86
|
+
cmd.run
|
|
87
|
+
|
|
88
|
+
@logger.info "Finished #{cmd}" if @logger
|
|
89
|
+
# Make the command remove itself from the running list.
|
|
90
|
+
@running.delete_if { |o| o === cmd }
|
|
91
|
+
# Try to run queued commands once this one is done if we're continuing.
|
|
92
|
+
pop_run if continue
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: cuted
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- David Selassie
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2012-04-03 00:00:00.000000000 Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: riot
|
|
16
|
+
requirement: &70245431926940 !ruby/object:Gem::Requirement
|
|
17
|
+
none: false
|
|
18
|
+
requirements:
|
|
19
|
+
- - ~>
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '0.12'
|
|
22
|
+
type: :development
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: *70245431926940
|
|
25
|
+
- !ruby/object:Gem::Dependency
|
|
26
|
+
name: daemons
|
|
27
|
+
requirement: &70245431926160 !ruby/object:Gem::Requirement
|
|
28
|
+
none: false
|
|
29
|
+
requirements:
|
|
30
|
+
- - ~>
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '1.1'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: *70245431926160
|
|
36
|
+
- !ruby/object:Gem::Dependency
|
|
37
|
+
name: trollop
|
|
38
|
+
requirement: &70245431925580 !ruby/object:Gem::Requirement
|
|
39
|
+
none: false
|
|
40
|
+
requirements:
|
|
41
|
+
- - ~>
|
|
42
|
+
- !ruby/object:Gem::Version
|
|
43
|
+
version: '1.16'
|
|
44
|
+
type: :runtime
|
|
45
|
+
prerelease: false
|
|
46
|
+
version_requirements: *70245431925580
|
|
47
|
+
- !ruby/object:Gem::Dependency
|
|
48
|
+
name: parseconfig
|
|
49
|
+
requirement: &70245431924920 !ruby/object:Gem::Requirement
|
|
50
|
+
none: false
|
|
51
|
+
requirements:
|
|
52
|
+
- - ~>
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0.5'
|
|
55
|
+
type: :runtime
|
|
56
|
+
prerelease: false
|
|
57
|
+
version_requirements: *70245431924920
|
|
58
|
+
description:
|
|
59
|
+
email: selassid@gmail.com
|
|
60
|
+
executables:
|
|
61
|
+
- cuted
|
|
62
|
+
- cute
|
|
63
|
+
extensions: []
|
|
64
|
+
extra_rdoc_files: []
|
|
65
|
+
files:
|
|
66
|
+
- lib/cuted/queue.rb
|
|
67
|
+
- lib/cuted/command.rb
|
|
68
|
+
- !binary |-
|
|
69
|
+
YmluL2N1dGVk
|
|
70
|
+
- !binary |-
|
|
71
|
+
YmluL2N1dGU=
|
|
72
|
+
homepage: http://github.com/selassid/cuted
|
|
73
|
+
licenses: []
|
|
74
|
+
post_install_message: Install optional gem 'prowler' for Prowl notifications when
|
|
75
|
+
commands are complete.
|
|
76
|
+
rdoc_options: []
|
|
77
|
+
require_paths:
|
|
78
|
+
- lib
|
|
79
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
80
|
+
none: false
|
|
81
|
+
requirements:
|
|
82
|
+
- - ! '>='
|
|
83
|
+
- !ruby/object:Gem::Version
|
|
84
|
+
version: '0'
|
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
86
|
+
none: false
|
|
87
|
+
requirements:
|
|
88
|
+
- - ! '>='
|
|
89
|
+
- !ruby/object:Gem::Version
|
|
90
|
+
version: '0'
|
|
91
|
+
requirements: []
|
|
92
|
+
rubyforge_project:
|
|
93
|
+
rubygems_version: 1.8.17
|
|
94
|
+
signing_key:
|
|
95
|
+
specification_version: 3
|
|
96
|
+
summary: A simple single-machine batch queueing system.
|
|
97
|
+
test_files: []
|