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.
Files changed (5) hide show
  1. data/bin/cute +55 -0
  2. data/bin/cuted +57 -0
  3. data/lib/cuted/command.rb +62 -0
  4. data/lib/cuted/queue.rb +96 -0
  5. 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
@@ -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: []