require 'json' require 'resque' require 'resque-lonely_job' require 'net/http' require 'digest/md5' module JamRuby # executes a mix of tracks, creating a final output mix class IcecastConfigWriter extend Resque::Plugins::JamLonelyJob @@log = Logging.logger[IcecastConfigWriter] attr_accessor :icecast_server_id def self.queue queue_name(::APP_CONFIG.icecast_server_id) end def self.queue_jobs_needing_retry # if we haven't seen updated_at be tickled in 5 minutes, but config_changed is still set to TRUE, this record has gotten stale IcecastServer.where("config_changed = 1 AND updated_at < (NOW() - interval '#{APP_CONFIG.icecast_max_missing_check} second')").find_each(:batch_size => 100) do |server| IcecastConfigWriter.enqueue(server.server_id) end end def self.queue_name(server_id) "icecast-#{server_id}" end def self.lock_timeout # this should be enough time to make sure the job has finished, but not so long that the system isn't recovering from a abandoned job 60 end def self.perform(icecast_server_id) icecast = IcecastConfigWriter.new() icecast.icecast_server_id = icecast_server_id icecast.run end def self.enqueue(server_id) begin Resque.enqueue_to(queue_name(server_id), IcecastConfigWriter, server_id) return true rescue @@log.error("unable to enqueue IceastConfigWriter(#{server_id}). #{$!}") # implies redis is down return false end end def initialize end def validate raise "icecast_server_id not spceified" unless icecast_server_id raise "queue routing mismatch error. requested icecast_server_id: #{icecast_server_id}, configured icecast_server_id: #{APP_CONFIG.icecast_server_id}" unless icecast_server_id == APP_CONFIG.icecast_server_id end def execute(cmd) system cmd $?.exitstatus end def reload cmd = APP_CONFIG.icecast_reload_cmd result = execute(cmd) raise "unable to execute icecast reload cmd=#{cmd}. result=#{$?}" unless result == 0 sleep APP_CONFIG.icecast_wait_after_reload end def run validate config_file = APP_CONFIG.icecast_config_file # check if the config file is there at all; if it's not, we need to generate it regardless if config has changed query = {server_id: icecast_server_id} icecast_server = IcecastServer.where(server_id: icecast_server_id).first raise "can not find icecast server with query #{query}" unless icecast_server if File.exist?(config_file) && !icecast_server.config_changed @@log.info("config not changed. skipping run for server: #{icecast_server.server_id}") else # don't try to write to the file if for some reason the model isn't valid # this could happen if an admin mucks around in the db directly raise "icecast_server.id=#{icecast_server.server_id} not valid. errors=#{icecast_server.errors.inspect}" unless icecast_server.valid? # write the new config to a temporary location tmp_config = Dir::Tmpname.make_tmpname(["#{Dir.tmpdir}/icecast", '.xml'], nil) buffer = nil # allow no write to the server while dumping XML icecast_server.with_lock do buffer = StringIO.new icecast_server.dumpXml(buffer) end buffer.rewind File.open(tmp_config, 'w') do |f| f.write buffer.read end # if written successfully, overwrite the current file FileUtils.mv tmp_config, config_file # reload server reload icecast_server.config_updated end @@log.info("successful update of config for server: #{icecast_server.server_id}") end end end