cuted 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []