#!/usr/bin/ruby # # This script will pull tests to complete from the Beanstalk Queue, # where they will be found in JSON form, and executes them. # # # TODO: Command line parsing: # # 1. set failure count 3 in a row, for example. # # 2. enable/disable logging to a file # # 3. Specify server name/port for the beanstalk queue. # # # Steve # -- # require 'beanstalk-client' require 'getoptlong' require 'json' # # Implementation of our protocol tests. # require 'tests/ftp' require 'tests/http' require 'tests/https' require 'tests/jabber' require 'tests/ldap' require 'tests/ping' require 'tests/rsync' require 'tests/smtp' require 'tests/ssh' # # This class encapsulates the raising and clearing of alerts # via Mauve. # class Alert attr_reader :details def initialize( test_details ) @details = test_details end def raise puts "RAISING ALERT: #{@details}" end def clear puts "CLEARING ALERT: #{@details}" end end # # This class contains the code for connecting to a Beanstalk queue, # fetching tests from it, and executing them # class Custodian # # The beanstalk queue. # attr_reader :queue # # Constructor: Connect to the queue # def initialize @queue = Beanstalk::Pool.new(['localhost:11300']) end # # Flush the queue. # def flush_queue! while( true ) begin job = @queue.reserve(1) id = job.id puts "\tDeleted job #{id}" if ( ENV['VERBOSE'] ) job.delete rescue Beanstalk::TimedOut => ex return end end end # # Process jobs from the queue - never return. # def run! while( true ) puts "\n\nWaiting for job.." if ( ENV['VERBOSE'] ) process_single_job() end end # # Fetch a single job from the queue, and process it. # def process_single_job begin job = @queue.reserve() puts "Job acquired: #{Time.new.inspect}" if ( ENV['VERBOSE'] ) # # Parse the JSON of the job body. # json = job.body hash = JSON.parse( json ) hash['verbose'] = 1 if ( ENV['VERBOSE'] ) # # Output the details. # if ( ENV['VERBOSE'] ) puts "JOB: #{job.id}" puts "Type of test is #{hash['test_type']}" hash.keys.each do |key| puts "\t#{key} => #{hash[key]}" end end # # Given the test-type of "YYY" we'll call the method "YYY_test", which # we assume comes from one of the files beneath ./tests/ # test = hash['test_type'] method = "#{test}_test".to_sym success = false count = 0 alert = Alert.new( hash ) # # We'll run no more than MAX times. # # We stop the execution on a single success. # while ( ( count < 5 ) && ( success == false ) ) if ( send( method, hash ) ) alert.clear() success= true end count += 1 end # # If we didn't succeed on any of the attempts raise the alert. # if ( ! success ) alert.raise() end rescue => ex puts "Exception raised processing job: #{ex}" ensure # # Delete the job - either we received an error, in which case # we should remove it to avoid picking it up again, or we handled # it successfully so it should be removed. # job.delete end end end # # Entry-point to our code. # if __FILE__ == $0 then begin opts = GetoptLong.new( [ "--verbose", "-v", GetoptLong::NO_ARGUMENT ], [ "--flush", "-f", GetoptLong::NO_ARGUMENT ], [ "--single", "-s", GetoptLong::NO_ARGUMENT ] ) opts.each do |opt, arg| case opt when "--verbose": ENV["VERBOSE"] = "1" when "--flush": ENV["FLUSH"] = "1" when "--single": ENV["SINGLE"] = "1" end end rescue StandardError => ex puts "Option parsing failed: #{ex.to_s}" exit end # # Create the object # worker = Custodian.new() # # Are we flushing the queue? # if ( ENV['FLUSH'] ) worker.flush_queue! exit(0) end # # Single step? # if ( ENV['SINGLE'] ) worker.process_single_job exit(0) end # # Otherwise loop indefinitely # worker.run! end