* VRFS-801 - almost working on my dev machine
This commit is contained in:
parent
230a4d3f64
commit
d89ce4c140
|
|
@ -50,6 +50,8 @@ gem 'postgres-copy', '0.6.0'
|
|||
gem 'aws-sdk', '1.29.1'
|
||||
gem 'bugsnag'
|
||||
gem 'resque'
|
||||
gem 'resque-retry'
|
||||
gem 'resque-failed-job-mailer'
|
||||
|
||||
gem 'eventmachine', '1.0.3'
|
||||
gem 'amqp', '0.9.8'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
require 'resque/server'
|
||||
require 'resque-retry'
|
||||
require 'resque-retry/server'
|
||||
|
||||
JamAdmin::Application.routes.draw do
|
||||
|
||||
|
|
|
|||
|
|
@ -86,3 +86,4 @@ music_sessions_have_claimed_recording.sql
|
|||
discardable_recorded_tracks2.sql
|
||||
icecast.sql
|
||||
home_page_promos.sql
|
||||
mix_job_watch.sql
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
-- add some columns to help understand mix job completion
|
||||
ALTER TABLE mixes ADD COLUMN completed BOOLEAN NOT NULL DEFAULT FALSE;
|
||||
ALTER TABLE mixes ADD COLUMN error_count INTEGER NOT NULL DEFAULT 0;
|
||||
ALTER TABLE mixes ADD COLUMN error_reason TEXT;
|
||||
ALTER TABLE mixes ADD COLUMN error_detail TEXT;
|
||||
|
|
@ -24,9 +24,12 @@ gem 'carrierwave'
|
|||
gem 'aasm', '3.0.16'
|
||||
gem 'devise', '>= 1.1.2'
|
||||
gem 'postgres-copy'
|
||||
gem 'resque'
|
||||
gem 'geokit-rails'
|
||||
gem 'postgres_ext'
|
||||
gem 'resque'
|
||||
gem 'resque-retry'
|
||||
gem 'resque-failed-job-mailer' #, :path => "/Users/seth/workspace/resque_failed_job_mailer"
|
||||
gem 'oj'
|
||||
|
||||
if devenv
|
||||
gem 'jam_db', :path=> "../db/target/ruby_package"
|
||||
|
|
@ -43,6 +46,7 @@ group :test do
|
|||
gem 'database_cleaner', '0.7.0'
|
||||
gem 'rest-client'
|
||||
gem 'faker'
|
||||
gem 'resque_spec'
|
||||
end
|
||||
|
||||
# Specify your gem's dependencies in jam_ruby.gemspec
|
||||
|
|
|
|||
|
|
@ -1,16 +1,15 @@
|
|||
module JamRuby
|
||||
|
||||
class Profanity
|
||||
@@dictionary_file = File.join('config/profanity.yml')
|
||||
@@dictionary_file = File.join(File.dirname(__FILE__), '../../..', 'config/profanity.yml')
|
||||
@@dictionary = nil
|
||||
|
||||
def self.dictionary
|
||||
if File.exists? @@dictionary_file
|
||||
@@dictionary ||= YAML.load_file(@@dictionary_file)
|
||||
else
|
||||
@@dictionary = []
|
||||
end
|
||||
@@dictionary
|
||||
@@dictionary ||= load_dictionary
|
||||
end
|
||||
|
||||
def self.load_dictionary
|
||||
YAML.load_file(@@dictionary_file)
|
||||
end
|
||||
|
||||
def self.check_word(word)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ module JamRuby
|
|||
|
||||
@@def_opts = { :expires => 3600 * 24, :secure => true } # 24 hours from now
|
||||
|
||||
S3_PREFIX = 's3://'
|
||||
|
||||
def initialize(aws_bucket, aws_key, aws_secret)
|
||||
@aws_bucket = aws_bucket
|
||||
@s3 = AWS::S3.new(:access_key_id => aws_key, :secret_access_key => aws_secret)
|
||||
|
|
@ -16,7 +18,11 @@ module JamRuby
|
|||
end
|
||||
|
||||
def s3_url(filename)
|
||||
"s3://#{@aws_bucket}/#{filename}"
|
||||
"#{S3_PREFIX}#{@aws_bucket}/#{filename}"
|
||||
end
|
||||
|
||||
def s3_url?(filename)
|
||||
filename.start_with? S3_PREFIX
|
||||
end
|
||||
|
||||
def url(filename, options = @@def_opts)
|
||||
|
|
@ -34,8 +40,12 @@ module JamRuby
|
|||
}
|
||||
end
|
||||
|
||||
def sign_url(path, options = @@def_opts)
|
||||
s3_bucket.objects[path].url_for(:read, options).to_s
|
||||
def sign_url(key, options = @@def_opts, operation = :read)
|
||||
s3_bucket.objects[key].url_for(operation, options).to_s
|
||||
end
|
||||
|
||||
def presigned_post(key, options = @@def_opts)
|
||||
s3_bucket.objects[key].presigned_post(options)
|
||||
end
|
||||
|
||||
def multipart_upload_start(upload_filename)
|
||||
|
|
@ -58,6 +68,10 @@ module JamRuby
|
|||
s3_bucket.objects[filename].delete
|
||||
end
|
||||
|
||||
def upload(key, filename)
|
||||
s3_bucket.objects[key].write(:file => filename)
|
||||
end
|
||||
|
||||
def delete_folder(folder)
|
||||
s3_bucket.objects.with_prefix(folder).delete_all
|
||||
end
|
||||
|
|
|
|||
|
|
@ -11,39 +11,32 @@ module JamRuby
|
|||
|
||||
def self.schedule(recording, manifest)
|
||||
raise if recording.nil?
|
||||
raise if manifest.nil?
|
||||
|
||||
mix = Mix.new
|
||||
mix = Mix.new
|
||||
mix.recording = recording
|
||||
mix.manifest = manifest
|
||||
mix.manifest = manifest.to_json
|
||||
mix.save
|
||||
mix.url = construct_filename(recording.id, mix.id)
|
||||
mix.save
|
||||
mix
|
||||
end
|
||||
|
||||
def self.next(mix_server)
|
||||
# First check if there are any mixes started so long ago that we want to re-run them
|
||||
Mix.where("completed_at IS NULL AND started_at < ?", Time.now - MAX_MIX_TIME).each do |mix|
|
||||
# FIXME: This should probably throw some kind of log, since it means something went wrong
|
||||
mix.started_at = nil
|
||||
mix.mix_server = nil
|
||||
mix.save
|
||||
if mix.save
|
||||
Resque.enqueue(AudioMixer, mix.id, mix.sign_put)
|
||||
end
|
||||
|
||||
mix = Mix.where(:started_at => nil).limit(1).first
|
||||
return nil if mix.nil?
|
||||
|
||||
mix.started_at = Time.now
|
||||
mix.mix_server = mix_server
|
||||
mix.save
|
||||
|
||||
mix
|
||||
end
|
||||
|
||||
def errored(reason, detail)
|
||||
self.error_reason = reason
|
||||
self.error_detail = detail
|
||||
self.error_count = self.error_count + 1
|
||||
save
|
||||
end
|
||||
|
||||
def finish(length, md5)
|
||||
self.completed_at = Time.now
|
||||
self.length = length
|
||||
self.md5 = md5
|
||||
self.completed = true
|
||||
save
|
||||
end
|
||||
|
||||
|
|
@ -52,12 +45,16 @@ module JamRuby
|
|||
end
|
||||
|
||||
def is_completed
|
||||
!completed_at.nil?
|
||||
completed
|
||||
end
|
||||
|
||||
def sign_url
|
||||
def sign_url(expiration_time = 120)
|
||||
# expire link in 1 minute--the expectation is that a client is immediately following this link
|
||||
s3_manager.sign_url(filename, {:expires => 120 , :response_content_type => 'audio/ogg'})
|
||||
s3_manager.sign_url(filename, {:expires => expiration_time, :response_content_type => 'audio/ogg', :secure => false})
|
||||
end
|
||||
|
||||
def sign_put(expiration_time = 3600 * 24)
|
||||
s3_manager.sign_url(filename, {:expires => expiration_time, :content_type => 'audio/ogg', :secure => false}, :put)
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
|||
|
|
@ -91,9 +91,8 @@ module JamRuby
|
|||
recorded_track
|
||||
end
|
||||
|
||||
def sign_url
|
||||
# expire link in 1 minute--the expectation is that a client is immediately following this link
|
||||
s3_manager.sign_url(url, {:expires => 120, :response_content_type => 'audio/ogg'})
|
||||
def sign_url(expiration_time = 120)
|
||||
s3_manager.sign_url(url, {:expires => expiration_time, :response_content_type => 'audio/ogg', :secure => false})
|
||||
end
|
||||
|
||||
def upload_start(length, md5)
|
||||
|
|
|
|||
|
|
@ -268,7 +268,7 @@ module JamRuby
|
|||
return unless recorded_track.fully_uploaded
|
||||
end
|
||||
|
||||
self.mixes << Mix.schedule(self, base_mix_manifest.to_json)
|
||||
self.mixes << Mix.schedule(self, base_mix_manifest)
|
||||
|
||||
save
|
||||
end
|
||||
|
|
@ -298,12 +298,13 @@ module JamRuby
|
|||
mix_params = []
|
||||
recorded_tracks.each do |recorded_track|
|
||||
return nil unless recorded_track.fully_uploaded
|
||||
manifest["files"] << { "url" => recorded_track.url, "codec" => "vorbis", "offset" => 0 }
|
||||
manifest["files"] << { "filename" => recorded_track.sign_url(60 * 60 * 24), "codec" => "vorbis", "offset" => 0 }
|
||||
mix_params << { "level" => 100, "balance" => 0 }
|
||||
end
|
||||
|
||||
manifest["timeline"] << { "timestamp" => 0, "mix" => mix_params }
|
||||
manifest["timeline"] << { "timestamp" => duration, "end" => true }
|
||||
manifest["output"] = { "codec" => "vorbis" }
|
||||
manifest["recording_id"] = self.id
|
||||
manifest
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,25 +1,36 @@
|
|||
require 'json'
|
||||
require 'resque'
|
||||
require 'resque-retry'
|
||||
require 'net/http'
|
||||
require 'digest/md5'
|
||||
|
||||
module JamRuby
|
||||
|
||||
@queue = :audiomixer
|
||||
|
||||
class AudioMixer
|
||||
|
||||
@queue = :audiomixer
|
||||
|
||||
#extend Resque::Plugins::Retry
|
||||
|
||||
@@log = Logging.logger[AudioMixer]
|
||||
|
||||
def self.perform(manifest)
|
||||
audiomixer = AudioMixer.new
|
||||
audiomixer.run(manifest)
|
||||
attr_accessor :mix_id, :manifest, :manifest_file, :output_filename, :error_out_filename, :postback_output_url,
|
||||
:error_reason, :error_detail
|
||||
|
||||
def self.perform(mix_id, postback_output_url)
|
||||
audiomixer = AudioMixer.new()
|
||||
audiomixer.postback_output_url = postback_output_url
|
||||
audiomixer.mix_id = mix_id
|
||||
audiomixer.run
|
||||
end
|
||||
|
||||
def initialize
|
||||
|
||||
#@s3_manager = S3Manager.new(APP_CONFIG.aws_bucket, APP_CONFIG.aws_access_key_id, APP_CONFIG.aws_secret_access_key)
|
||||
end
|
||||
|
||||
def validate
|
||||
raise "no manifest specified" unless @manifest
|
||||
|
||||
raise "no files specified" if !@manifest[:files] || @manifest[:files].length == 0
|
||||
|
||||
@manifest[:files].each do |file|
|
||||
|
|
@ -35,41 +46,209 @@ module JamRuby
|
|||
|
||||
raise "no output specified" unless @manifest[:output]
|
||||
raise "no output codec specified" unless @manifest[:output][:codec]
|
||||
raise "no output filename specified" unless @manifest[:output][:filename]
|
||||
raise "no timeline specified" unless @manifest[:timeline]
|
||||
raise "no recording_id specified" unless @manifest[:recording_id]
|
||||
raise "no mix_id specified" unless @manifest[:mix_id]
|
||||
end
|
||||
|
||||
raise "no timeline specified" if !@manifest[:timeline] || @manifest[:timeline].length == 0
|
||||
|
||||
@manifest[:timeline].each do |entry|
|
||||
def fetch_audio_files
|
||||
@manifest[:files].each do |file|
|
||||
filename = file[:filename]
|
||||
if filename.start_with? "http"
|
||||
# fetch it from wherever, put it somewhere on disk, and replace filename in the file parameter with the local disk one
|
||||
download_filename = Dir::Tmpname.make_tmpname(["#{Dir.tmpdir}audiomixer-file", '.ogg'], nil)
|
||||
|
||||
uri = URI(filename)
|
||||
open download_filename, 'w' do |io|
|
||||
begin
|
||||
Net::HTTP.start(uri.host, uri.port) do |http|
|
||||
request = Net::HTTP::Get.new uri
|
||||
http.request request do |response|
|
||||
response_code = response.code.to_i
|
||||
unless response_code >= 200 && response_code <= 299
|
||||
raise "bad status code: #{response_code}. body: #{response.body}"
|
||||
end
|
||||
response.read_body do |chunk|
|
||||
io.write chunk
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue Exception => e
|
||||
@error_reason = "unable to download"
|
||||
@error_detail = "url #{filename}, error=#{e}"
|
||||
raise e
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
filename = download_filename
|
||||
file[:filename] = download_filename
|
||||
end
|
||||
|
||||
raise "no file located at: #{filename}" unless File.exist? filename
|
||||
end
|
||||
end
|
||||
|
||||
def fetch_audio_files
|
||||
def prepare
|
||||
# make sure there is a place to write the .ogg mix
|
||||
prepare_output
|
||||
|
||||
# make sure there is a place to write the error_out json file (if audiomixer fails this is needed)
|
||||
prepare_error_out
|
||||
|
||||
prepare_manifest
|
||||
end
|
||||
|
||||
def run(manifest)
|
||||
# write the manifest object to file, to pass into audiomixer
|
||||
def prepare_manifest
|
||||
|
||||
@manifest = symbolize_keys(manifest)
|
||||
|
||||
validate
|
||||
|
||||
fetch_audio_files
|
||||
|
||||
manifest_file = Dir::Tmpname.make_tmpname "/var/tmp/audiomixer/manifest-#{@manifest['recordingId']}", nil
|
||||
File.open(manifest_file,"w") do |f|
|
||||
@manifest_file = Dir::Tmpname.make_tmpname( ["#{Dir.tmpdir}audiomixer-manifest-#{@manifest['recording_id']}", '.json'], nil)
|
||||
File.open(@manifest_file,"w") do |f|
|
||||
f.write(@manifest.to_json)
|
||||
end
|
||||
|
||||
#{"files": [{"codec": "vorbis", "offset": 0, "filename": "TPD - bass.flac-stereo.ogg"},
|
||||
# {"codec": "vorbis", "offset": 0, "filename": "TPD - bg vox.flac-stereo.ogg"},
|
||||
# {"codec": "vorbis", "offset": 0, "filename": "TPD - drums.flac-stereo.ogg"},
|
||||
# {"codec": "vorbis", "offset": 0, "filename": "TPD - guitars.flac-stereo.ogg"},
|
||||
# {"codec": "vorbis", "offset": 0, "filename": "TPD - lead vox.flac-stereo.ogg"}],
|
||||
# "output": {"codec": "vorbis", "filename": "mix.ogg"},
|
||||
# "timeline":
|
||||
# [{"timestamp": 0, "mix": [{"balance": 0, "level": 100}, {"balance": 0, "level": 100}, {"balance": 0, "level": 100}, {"balance": 0, "level": 100}, {"balance": 0, "level": 100}]}]}
|
||||
@@log.debug("manifest: #{@manifest}")
|
||||
end
|
||||
|
||||
# make a suitable location to store the output mix, and pass the chosen filepath into the manifest
|
||||
def prepare_output
|
||||
@output_filename = Dir::Tmpname.make_tmpname( ["#{Dir.tmpdir}audiomixer-output-#{@manifest['recording_id']}", '.ogg'], nil)
|
||||
|
||||
# update manifest so that audiomixer writes here
|
||||
@manifest[:output][:filename] = @output_filename
|
||||
|
||||
@@log.debug("output ogg file: #{@output_filename}")
|
||||
end
|
||||
|
||||
# make a suitable location to store an output error file, which will be populated on failure to help diagnose problems.
|
||||
def prepare_error_out
|
||||
@error_out_filename = Dir::Tmpname.make_tmpname( ["#{Dir.tmpdir}audiomixer-error-out-#{@manifest['recording_id']}", '.ogg'], nil)
|
||||
|
||||
# update manifest so that audiomixer writes here
|
||||
@manifest[:error_out] = @error_out_filename
|
||||
|
||||
@@log.debug("error_out: #{@error_out_filename}")
|
||||
end
|
||||
|
||||
# read in and parse the error file that audiomixer pops out
|
||||
def parse_error_out
|
||||
error_out_data = File.read(@error_out_filename)
|
||||
begin
|
||||
@error_out = JSON.parse(error_out_data)
|
||||
rescue
|
||||
@error_reason = "unable-parse-error-out"
|
||||
@@log.error("unable to parse error_out_data: #{error_out_data} from error_out: #{@error_out_filename}")
|
||||
end
|
||||
|
||||
@error_reason = @error_out[:reason]
|
||||
@error_reason = "unspecified-reason" unless @error_reason
|
||||
@error_detail = @error_detail[:detail]
|
||||
end
|
||||
|
||||
def postback
|
||||
raise "no output file after mix" unless File.exist? @output_filename
|
||||
|
||||
@@log.debug("posting mix to #{@postback_output_url}")
|
||||
|
||||
uri = URI.parse(@postback_output_url)
|
||||
http = Net::HTTP.new(uri.host, uri.port)
|
||||
request = Net::HTTP::Put.new(uri.request_uri)
|
||||
|
||||
response = nil
|
||||
File.open(@output_filename,"r") do |f|
|
||||
request.body_stream=f
|
||||
request["Content-Type"] = "audio/ogg"
|
||||
request.add_field('Content-Length', File.size(@output_filename))
|
||||
response = http.request(request)
|
||||
end
|
||||
|
||||
response_code = response.code.to_i
|
||||
unless response_code >= 200 && response_code <= 299
|
||||
@error_reason = "postback-mix-to-s3"
|
||||
raise "unable to put to url: #{@postback_output_url}, status: #{response.code}, body: #{response.body}"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def post_success(mix)
|
||||
raise "no output file after mix" unless File.exist? @output_filename
|
||||
|
||||
length = File.size(@output_filename)
|
||||
|
||||
md5 = Digest::MD5.new
|
||||
File.open(@output_filename, 'rb').each {|line| md5.update(line)}
|
||||
|
||||
mix.finish(length, md5.to_s)
|
||||
end
|
||||
|
||||
def post_error(mix, e)
|
||||
begin
|
||||
|
||||
# if error_reason is null, assume this is an unhandled error
|
||||
unless @error_reason
|
||||
@error_reason = "unhandled-job-exception"
|
||||
@error_detail = e.to_s
|
||||
end
|
||||
mix.errored(error_reason, error_detail)
|
||||
|
||||
rescue
|
||||
@@log.error "unable to post back to the database the error"
|
||||
end
|
||||
end
|
||||
|
||||
def run
|
||||
@@log.info("audiomixer job starting. mix_id #{mix_id}")
|
||||
|
||||
mix = Mix.find(mix_id)
|
||||
|
||||
begin
|
||||
@manifest = symbolize_keys(JSON.parse(mix.manifest))
|
||||
@manifest[:mix_id] = mix_id # slip in the mix_id so that the job can add it to the ogg comments
|
||||
|
||||
# sanity check the manifest
|
||||
validate
|
||||
|
||||
# if http files are specified, bring them local
|
||||
fetch_audio_files
|
||||
|
||||
# write the manifest to file, so that it can be passed to audiomixer as an filepath argument
|
||||
prepare
|
||||
|
||||
result = execute(@manifest_file)
|
||||
|
||||
if result
|
||||
if $? == 0
|
||||
postback
|
||||
post_success(mix)
|
||||
@@log.info("audiomixer job successful. mix_id #{mix_id}")
|
||||
else
|
||||
parse_error_out
|
||||
error_msg = "audiomixer job failed status=#{$?} error_reason=#{error_reason} error_detail=#{error_detail}"
|
||||
@@log.info(error_msg)
|
||||
raise error_msg
|
||||
end
|
||||
else
|
||||
@@log.error("unable to find audiomixer")
|
||||
error_msg = "audiomixer job failed status=#{$?} error_reason=#{error_reason} error_detail=#{error_detail}"
|
||||
@@log.info(error_msg)
|
||||
@error_reason = "unable-find-appmixer"
|
||||
raise error_msg
|
||||
end
|
||||
rescue Exception => e
|
||||
post_error(mix, e)
|
||||
raise
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def manifest=(value)
|
||||
@manifest = symbolize_keys(value)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def execute(manifest_file)
|
||||
audiomixer_cmd = "#{APP_CONFIG.audiomixer_path} #{manifest_file}"
|
||||
|
||||
@@log.debug("executing #{audiomixer_cmd}")
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ require 'spec_helper'
|
|||
|
||||
describe Mix do
|
||||
before do
|
||||
stub_const("APP_CONFIG", app_config)
|
||||
@user = FactoryGirl.create(:user)
|
||||
@connection = FactoryGirl.create(:connection, :user => @user)
|
||||
@instrument = FactoryGirl.create(:instrument, :description => 'a great instrument')
|
||||
|
|
@ -13,12 +14,13 @@ describe Mix do
|
|||
@recording.stop
|
||||
@recording.claim(@user, "name", "description", Genre.first, true, true)
|
||||
@recording.errors.any?.should be_false
|
||||
@mix = Mix.schedule(@recording, "{}")
|
||||
@mix = Mix.schedule(@recording, {})
|
||||
@mix.reload
|
||||
end
|
||||
|
||||
it "should create a mix for a user's recording properly" do
|
||||
@mix.recording_id.should == @recording.id
|
||||
@mix.manifest.should == "{}"
|
||||
@mix.manifest.should == {}.to_json
|
||||
@mix.mix_server.should be_nil
|
||||
@mix.started_at.should be_nil
|
||||
@mix.completed_at.should be_nil
|
||||
|
|
@ -33,15 +35,6 @@ describe Mix do
|
|||
expect { @mix2 = Mix.schedule(Recording.find('lskdjflsd')) }.to raise_error
|
||||
end
|
||||
|
||||
it "should return a mix when the cron asks for it" do
|
||||
this_mix = Mix.next("server")
|
||||
this_mix.id.should == @mix.id
|
||||
@mix.reload
|
||||
@mix.started_at.should_not be_nil
|
||||
@mix.mix_server.should == "server"
|
||||
@mix.completed_at.should be_nil
|
||||
end
|
||||
|
||||
it "should record when a mix has finished" do
|
||||
Mix.find(@mix.id).finish(10000, "md5hash")
|
||||
@mix.reload
|
||||
|
|
@ -50,15 +43,6 @@ describe Mix do
|
|||
@mix.md5.should == "md5hash"
|
||||
end
|
||||
|
||||
it "should re-run a mix if it was started a long time ago" do
|
||||
this_mix = Mix.next("server")
|
||||
@mix.reload
|
||||
@mix.started_at -= 1000000
|
||||
@mix.save
|
||||
this_mix = Mix.next("server")
|
||||
this_mix.id.should == @mix.id
|
||||
end
|
||||
|
||||
it "signs url" do
|
||||
stub_const("APP_CONFIG", app_config)
|
||||
@mix.sign_url.should_not be_nil
|
||||
|
|
|
|||
|
|
@ -1,30 +1,285 @@
|
|||
require 'spec_helper'
|
||||
require 'fileutils'
|
||||
|
||||
# these tests avoid the use of ActiveRecord and FactoryGirl to do blackbox, non test-instrumented tests
|
||||
describe AudioMixer do
|
||||
|
||||
include UsesTempFiles
|
||||
|
||||
let(:audiomixer) { AudioMixer.new }
|
||||
let(:manifest) { {} }
|
||||
let(:valid_manifest) { make_manifest(manifest) }
|
||||
|
||||
def valid_files(manifest)
|
||||
manifest["files"] = [ {"codec" => "vorbis", "offset" => 0, "filename" => "/some/path"} ]
|
||||
end
|
||||
def valid_output(manifest)
|
||||
manifest["output"] = { "codec" => "vorbis" }
|
||||
end
|
||||
def valid_timeline(manifest)
|
||||
manifest["timeline"] = []
|
||||
end
|
||||
def valid_recording(manifest)
|
||||
manifest["recording_id"] = "record1"
|
||||
end
|
||||
def valid_mix(manifest)
|
||||
manifest["mix_id"] = "mix1"
|
||||
end
|
||||
|
||||
def make_manifest(manifest)
|
||||
valid_files(manifest)
|
||||
valid_output(manifest)
|
||||
valid_timeline(manifest)
|
||||
valid_recording(manifest)
|
||||
valid_mix(manifest)
|
||||
return manifest
|
||||
end
|
||||
|
||||
|
||||
before(:each) do
|
||||
stub_const("APP_CONFIG", app_config)
|
||||
end
|
||||
|
||||
|
||||
describe "validate" do
|
||||
|
||||
it "no manifest" do
|
||||
expect { audiomixer.validate }.to raise_error("no manifest specified")
|
||||
end
|
||||
|
||||
it "no files specified" do
|
||||
expect{ audiomixer.run({}) }.to raise_error("no files specified")
|
||||
audiomixer.manifest = manifest
|
||||
expect { audiomixer.validate }.to raise_error("no files specified")
|
||||
end
|
||||
|
||||
it "no codec specified" do
|
||||
expect{ audiomixer.run({ "files" => [ {"offset" => 0, "filename" => "/some/path"} ] }) }.to raise_error("no codec specified")
|
||||
audiomixer.manifest = { "files" => [ {"offset" => 0, "filename" => "/some/path"} ] }
|
||||
expect { audiomixer.validate }.to raise_error("no codec specified")
|
||||
end
|
||||
|
||||
it "no offset specified" do
|
||||
expect{ audiomixer.run({ "files" => [ {"codec" => "vorbis", "filename" => "/some/path"} ] }) }.to raise_error("no offset specified")
|
||||
audiomixer.manifest = { "files" => [ {"codec" => "vorbis", "filename" => "/some/path"} ] }
|
||||
expect { audiomixer.validate }.to raise_error("no offset specified")
|
||||
end
|
||||
|
||||
it "no output specified" do
|
||||
expect{ audiomixer.run({ "files" => [ {"codec" => "vorbis", "offset" => 0, "filename" => "/some/path"} ] }) }.to raise_error("no output specified")
|
||||
valid_files(manifest)
|
||||
audiomixer.manifest = manifest
|
||||
audiomixer.manifest = { "files" => [ {"codec" => "vorbis", "offset" => 0, "filename" => "/some/path"} ] }
|
||||
expect { audiomixer.validate }.to raise_error("no output specified")
|
||||
end
|
||||
|
||||
it "no recording_id specified" do
|
||||
valid_files(manifest)
|
||||
valid_output(manifest)
|
||||
valid_timeline(manifest)
|
||||
valid_mix(manifest)
|
||||
audiomixer.manifest = manifest
|
||||
expect { audiomixer.validate }.to raise_error("no recording_id specified")
|
||||
end
|
||||
|
||||
it "no mix_id specified" do
|
||||
valid_files(manifest)
|
||||
valid_output(manifest)
|
||||
valid_timeline(manifest)
|
||||
valid_recording(manifest)
|
||||
audiomixer.manifest = manifest
|
||||
expect { audiomixer.validate }.to raise_error("no mix_id specified")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
describe "fetch_audio_files" do
|
||||
|
||||
it "get upset if file doesn't exist" do
|
||||
audiomixer.manifest = { "files" => [ {"codec" => "vorbis", "offset" => 0, "filename" => "/some/bogus/path"} ] }
|
||||
expect { audiomixer.fetch_audio_files }.to raise_error("no file located at: /some/bogus/path")
|
||||
end
|
||||
end
|
||||
|
||||
describe "prepare_manifest" do
|
||||
it "writes manifest as json to file" do
|
||||
audiomixer.manifest = valid_manifest
|
||||
audiomixer.prepare_manifest
|
||||
|
||||
File.read(audiomixer.manifest_file).should == valid_manifest.to_json
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
describe "prepare_output" do
|
||||
it "can be written to" do
|
||||
audiomixer.manifest = valid_manifest
|
||||
audiomixer.prepare_output
|
||||
|
||||
File.open(audiomixer.manifest[:output][:filename] ,"w") do |f|
|
||||
f.write("tickle")
|
||||
end
|
||||
|
||||
File.read(audiomixer.manifest[:output][:filename]).should == "tickle"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
describe "prepare_error_out" do
|
||||
it "can be written to" do
|
||||
audiomixer.manifest = valid_manifest
|
||||
audiomixer.prepare_error_out
|
||||
|
||||
File.open(audiomixer.manifest[:error_out] ,"w") do |f|
|
||||
f.write("some_error")
|
||||
end
|
||||
|
||||
File.read(audiomixer.manifest[:error_out]).should == "some_error"
|
||||
end
|
||||
end
|
||||
|
||||
describe "integration" do
|
||||
|
||||
sample_ogg='sample.ogg'
|
||||
in_directory_with_file(sample_ogg)
|
||||
|
||||
|
||||
before(:each) do
|
||||
content_for_file("ogg goodness")
|
||||
@user = FactoryGirl.create(:user)
|
||||
@connection = FactoryGirl.create(:connection, :user => @user)
|
||||
@instrument = FactoryGirl.create(:instrument, :description => 'a great instrument')
|
||||
@track = FactoryGirl.create(:track, :connection => @connection, :instrument => @instrument)
|
||||
@music_session = FactoryGirl.create(:music_session, :creator => @user, :musician_access => true)
|
||||
@music_session.connections << @connection
|
||||
@music_session.save
|
||||
@recording = Recording.start(@music_session, @user)
|
||||
@recording.stop
|
||||
@recording.claim(@user, "name", "description", Genre.first, true, true)
|
||||
@recording.errors.any?.should be_false
|
||||
end
|
||||
|
||||
describe "simulated" do
|
||||
|
||||
let (:local_files_manifest) {
|
||||
{
|
||||
:files => [ { :codec => :vorbis, :offset => 0, :filename => sample_ogg} ],
|
||||
:output => { :codec => :vorbis },
|
||||
:timeline => [ {:timestamp => 0, :mix => [{:balance => 0, :level => 100}]} ],
|
||||
:recording_id => "recording1"
|
||||
}
|
||||
}
|
||||
|
||||
# stub out methods that are environmentally sensitive (so as to skip s3, and not run an actual audiomixer)
|
||||
before(:each) do
|
||||
AudioMixer.any_instance.stub(:execute) do |manifest_file|
|
||||
output_filename = JSON.parse(File.read(manifest_file))['output']['filename']
|
||||
FileUtils.touch output_filename
|
||||
end
|
||||
|
||||
AudioMixer.any_instance.stub(:postback) # don't actually post resulting off file up
|
||||
end
|
||||
|
||||
|
||||
describe "perform" do
|
||||
|
||||
# this case does not talk to redis, does not run the real audiomixer, and does not actually talk with s3
|
||||
# but it does talk to the database and verifies all the other logic
|
||||
it "success" do
|
||||
@mix = Mix.schedule(@recording, local_files_manifest)
|
||||
AudioMixer.perform(@mix.id, @mix.sign_url(60 * 60 * 24))
|
||||
@mix.reload
|
||||
@mix.completed.should be_true
|
||||
@mix.length.should == 0
|
||||
@mix.md5.should == 'd41d8cd98f00b204e9800998ecf8427e' # that's the md5 of a touched file
|
||||
end
|
||||
|
||||
it "errored" do
|
||||
local_files_manifest[:files][0][:filename] = '/some/path/to/nowhere'
|
||||
@mix = Mix.schedule(@recording, local_files_manifest)
|
||||
expect{ AudioMixer.perform(@mix.id, @mix.sign_url(60 * 60 * 24)) }.to raise_error
|
||||
@mix.reload
|
||||
@mix.completed.should be_false
|
||||
@mix.error_count.should == 1
|
||||
@mix.error_reason.should == "unhandled-job-exception"
|
||||
@mix.error_detail.should == "no file located at: /some/path/to/nowhere"
|
||||
end
|
||||
end
|
||||
|
||||
describe "with resque-spec" do
|
||||
|
||||
before(:each) do
|
||||
ResqueSpec.reset!
|
||||
end
|
||||
|
||||
it "should have been enqueued because mix got scheduled" do
|
||||
@mix = Mix.schedule(@recording, local_files_manifest)
|
||||
AudioMixer.should have_queue_size_of(1)
|
||||
end
|
||||
|
||||
it "should actually run the job" do
|
||||
with_resque do
|
||||
@mix = Mix.schedule(@recording, local_files_manifest)
|
||||
end
|
||||
|
||||
@mix.reload
|
||||
@mix.completed.should be_true
|
||||
@mix.length.should == 0
|
||||
@mix.md5.should == 'd41d8cd98f00b204e9800998ecf8427e' # that's the md5 of a touched file, which is what the stubbed :execute does
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# these tests try to run the job with minimal faking. Here's what we still fake:
|
||||
# we don't run audiomixer. audiomixer is tested already
|
||||
# we don't run redis and actual resque, because that's tested by resque/resque-spec
|
||||
describe "full", :aws => true do
|
||||
|
||||
let (:s3_manifest) {
|
||||
{
|
||||
:files => [ { :codec => :vorbis, :offset => 0, :filename => @s3_sample } ],
|
||||
:output => { :codec => :vorbis },
|
||||
:timeline => [ {:timestamp => 0, :mix => [{:balance => 0, :level => 100}]} ],
|
||||
:recording_id => @recording.id
|
||||
}
|
||||
}
|
||||
|
||||
before(:each) do
|
||||
test_config = app_config
|
||||
key = "audiomixer/#{sample_ogg}"
|
||||
@s3_manager = S3Manager.new(test_config.aws_bucket, test_config.aws_access_key_id, test_config.aws_secret_access_key)
|
||||
|
||||
# put a file in s3
|
||||
@s3_manager.upload(key, sample_ogg)
|
||||
|
||||
# create a signed url that the job will use to fetch it back down
|
||||
@s3_sample = @s3_manager.sign_url(key, :secure => false)
|
||||
|
||||
AudioMixer.any_instance.stub(:execute) do |manifest_file|
|
||||
output_filename = JSON.parse(File.read(manifest_file))['output']['filename']
|
||||
FileUtils.touch output_filename
|
||||
end
|
||||
end
|
||||
|
||||
it "completes" do
|
||||
with_resque do
|
||||
@mix = Mix.schedule(@recording, s3_manifest)
|
||||
end
|
||||
|
||||
@mix.reload
|
||||
@mix.completed.should be_true
|
||||
@mix.length.should == 0
|
||||
@mix.md5.should == 'd41d8cd98f00b204e9800998ecf8427e' # that's the md5 of a touched file, which is what the stubbed :execute does
|
||||
end
|
||||
|
||||
it "fails" do
|
||||
with_resque do
|
||||
s3_manifest[:files][0][:filename] = @s3_manager.url('audiomixer/bogus.ogg', :secure => false) # take off some of the trailing chars of the url
|
||||
expect{ Mix.schedule(@recording, s3_manifest) }.to raise_error
|
||||
end
|
||||
|
||||
@mix = Mix.order('id desc').limit(1).first()
|
||||
@mix.reload
|
||||
@mix.completed.should be_false
|
||||
@mix.error_reason.should == "unable to download"
|
||||
#ResqueFailedJobMailer::Mailer.deliveries.count.should == 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ require 'active_record'
|
|||
require 'jam_db'
|
||||
require 'spec_db'
|
||||
require 'uses_temp_files'
|
||||
require 'resque_spec'
|
||||
require 'resque_failed_job_mailer'
|
||||
|
||||
# recreate test database and migrate it
|
||||
SpecDb::recreate_database
|
||||
|
|
@ -38,6 +40,17 @@ CarrierWave.configure do |config|
|
|||
config.enable_processing = false
|
||||
end
|
||||
|
||||
|
||||
#Resque::Failure::Notifier.configure do |config|
|
||||
# config.from = 'dummy@jamkazam.com' # from address
|
||||
# config.to = 'dummy@jamkazam.com' # to address
|
||||
# config.include_payload = true # enabled by default
|
||||
#end
|
||||
#Resque::Failure::Multiple.classes = [Resque::Failure::Redis, Resque::Failure::Notifier]
|
||||
#Resque::Failure.backend = Resque::Failure::Multiple
|
||||
|
||||
|
||||
|
||||
#uncomment the following line to use spork with the debugger
|
||||
#require 'spork/ext/ruby-debug'
|
||||
|
||||
|
|
@ -62,8 +75,8 @@ Spork.prefork do
|
|||
config.filter_run :focus
|
||||
|
||||
# you can mark a test as slow so that developers won't commonly hit it, but build server will http://blog.davidchelimsky.net/2010/06/14/filtering-examples-in-rspec-2/
|
||||
config.filter_run_excluding slow: true unless ENV['RUN_SLOW_TESTS'] == "1" || ENV['SLOW'] == "1" || ENV['ALL_TESTS'] == "1"
|
||||
config.filter_run_excluding aws: true unless ENV['RUN_AWS_TESTS'] == "1" || ENV['AWS'] == "1" || ENV['ALL_TESTS'] == "1"
|
||||
config.filter_run_excluding slow: true unless run_tests? :slow
|
||||
config.filter_run_excluding aws: true unless run_tests? :aws
|
||||
|
||||
config.before(:suite) do
|
||||
DatabaseCleaner.strategy = :truncation, {:except => %w[instruments genres] }
|
||||
|
|
|
|||
|
|
@ -39,12 +39,21 @@ def app_config
|
|||
return klass.new
|
||||
end
|
||||
|
||||
def run_tests? type
|
||||
type = type.to_s.capitalize
|
||||
|
||||
ENV["RUN_#{type}_TESTS"] == "1" || ENV[type] == "1" || ENV['ALL_TESTS'] == "1"
|
||||
end
|
||||
|
||||
def wipe_s3_test_bucket
|
||||
test_config = app_config
|
||||
s3 = AWS::S3.new(:access_key_id => test_config.aws_access_key_id,
|
||||
:secret_access_key => test_config.aws_secret_access_key)
|
||||
test_bucket = s3.buckets[JAMKAZAM_TESTING_BUCKET]
|
||||
if test_bucket.name == JAMKAZAM_TESTING_BUCKET
|
||||
#test_bucket.clear!
|
||||
# don't bother if the user isn't doing AWS tests
|
||||
if run_tests? :aws
|
||||
test_config = app_config
|
||||
s3 = AWS::S3.new(:access_key_id => test_config.aws_access_key_id,
|
||||
:secret_access_key => test_config.aws_secret_access_key)
|
||||
test_bucket = s3.buckets[JAMKAZAM_TESTING_BUCKET]
|
||||
if test_bucket.name == JAMKAZAM_TESTING_BUCKET
|
||||
#test_bucket.clear!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -27,7 +27,7 @@ gem 'bcrypt-ruby', '3.0.1'
|
|||
gem 'faker', '1.0.1'
|
||||
gem 'will_paginate', '3.0.3'
|
||||
gem 'bootstrap-will_paginate', '0.0.6'
|
||||
gem 'em-websocket', '>=0.4.0'
|
||||
gem 'em-websocket', '>=0.4.0' #, :path => '/Users/seth/workspace/em-websocket'
|
||||
gem 'uuidtools', '2.1.2'
|
||||
gem 'ruby-protocol-buffers', '1.2.2'
|
||||
gem 'pg', '0.15.1'
|
||||
|
|
@ -59,6 +59,8 @@ gem 'geokit-rails'
|
|||
gem 'postgres_ext'
|
||||
gem 'haml-rails'
|
||||
gem 'resque'
|
||||
gem 'resque-retry'
|
||||
gem 'resque-failed-job-mailer'
|
||||
|
||||
gem 'quiet_assets', :group => :development
|
||||
|
||||
|
|
@ -109,6 +111,7 @@ end
|
|||
group :production do
|
||||
gem 'unicorn'
|
||||
gem 'newrelic_rpm'
|
||||
gem 'god'
|
||||
end
|
||||
|
||||
group :package do
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
||||
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
||||
|
||||
require 'resque/tasks'
|
||||
require File.expand_path('../config/application', __FILE__)
|
||||
|
||||
SampleApp::Application.load_tasks
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ class ApiMixesController < ApiController
|
|||
end
|
||||
|
||||
def download
|
||||
# XXX: needs to permission check
|
||||
redirect_to @mix.sign_url
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -164,5 +164,6 @@ include JamRuby
|
|||
config.ga_ua = 'UA-44184562-2' # google analytics
|
||||
config.ga_suppress_admin = true
|
||||
|
||||
config.redis_host = "localhost:6379"
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ development:
|
|||
host: localhost
|
||||
pool: 5
|
||||
timeout: 5000
|
||||
prepared_statements: false
|
||||
|
||||
# Warning: The database defined as "test" will be erased and
|
||||
# re-generated from your development database when you run "rake".
|
||||
|
|
@ -23,6 +24,7 @@ test: &test
|
|||
host: localhost
|
||||
pool: 5
|
||||
timeout: 5000
|
||||
prepared_statements: false
|
||||
|
||||
production:
|
||||
adapter: postgresql
|
||||
|
|
@ -32,6 +34,7 @@ production:
|
|||
host: localhost
|
||||
pool: 5
|
||||
timeout: 5000
|
||||
prepared_statements: false
|
||||
|
||||
cucumber:
|
||||
<<: *test
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
rails_env = ENV['RAILS_ENV'] || "production"
|
||||
rails_root = ENV['RAILS_ROOT'] || "/data/github/current"
|
||||
num_workers = rails_env == 'production' ? 5 : 2
|
||||
|
||||
num_workers.times do |num|
|
||||
God.watch do |w|
|
||||
w.dir = "#{rails_root}"
|
||||
w.name = "resque-#{num}"
|
||||
w.group = 'resque'
|
||||
w.interval = 30.seconds
|
||||
w.env = {"QUEUE"=>"critical,high,low", "RAILS_ENV"=>rails_env}
|
||||
w.start = "/usr/bin/rake -f #{rails_root}/Rakefile environment resque:work"
|
||||
|
||||
w.uid = 'git'
|
||||
w.gid = 'git'
|
||||
|
||||
# restart if memory gets too high
|
||||
w.transition(:up, :restart) do |on|
|
||||
on.condition(:memory_usage) do |c|
|
||||
c.above = 350.megabytes
|
||||
c.times = 2
|
||||
end
|
||||
end
|
||||
|
||||
# determine the state on startup
|
||||
w.transition(:init, { true => :up, false => :start }) do |on|
|
||||
on.condition(:process_running) do |c|
|
||||
c.running = true
|
||||
end
|
||||
end
|
||||
|
||||
# determine when process has finished starting
|
||||
w.transition([:start, :restart], :up) do |on|
|
||||
on.condition(:process_running) do |c|
|
||||
c.running = true
|
||||
c.interval = 5.seconds
|
||||
end
|
||||
|
||||
# failsafe
|
||||
on.condition(:tries) do |c|
|
||||
c.times = 5
|
||||
c.transition = :start
|
||||
c.interval = 5.seconds
|
||||
end
|
||||
end
|
||||
|
||||
# start if process is not running
|
||||
w.transition(:up, :start) do |on|
|
||||
on.condition(:process_running) do |c|
|
||||
c.running = false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,17 +1,22 @@
|
|||
if Rails.env == "development" && Rails.application.config.bootstrap_dev_users
|
||||
|
||||
# create one user per employee, +1 for peter2 because he asked for it
|
||||
User.create_dev_user("Seth", "Call", "seth@jamkazam.com", "jam123", "Austin", "TX", "US", nil, 'http://www.jamkazam.com/assets/avatars/avatar_seth.jpg')
|
||||
User.create_dev_user("Brian", "Smith", "briansmith@jamkazam.com", "jam123", "Apex", "NC", "US", nil, 'http://www.jamkazam.com/assets/avatars/avatar_brian.jpg')
|
||||
User.create_dev_user("Peter", "Walker", "peter@jamkazam.com", "jam123", "Austin", "TX", "US", nil, 'http://www.jamkazam.com/assets/avatars/avatar_peter.jpg')
|
||||
User.create_dev_user("Peter", "Walker", "peter2@jamkazam.com", "jam123", "Austin", "TX", "US", nil, 'http://www.jamkazam.com/assets/avatars/avatar_peter.jpg')
|
||||
User.create_dev_user("David", "Wilson", "david@jamkazam.com", "jam123", "Austin", "TX", "US", nil, 'http://www.jamkazam.com/assets/avatars/avatar_david.jpg')
|
||||
User.create_dev_user("Jonathon", "Wilson", "jonathon@jamkazam.com", "jam123", "Bozeman", "MT", "US", [{:instrument_id => "keyboard", :proficiency_level => 4, :priority => 1}], 'http://www.jamkazam.com/assets/avatars/avatar_jonathon.jpg')
|
||||
User.create_dev_user("Jonathan", "Kolyer", "jonathan@jamkazam.com", "jam123", "Austin", "TX", "US", nil, nil)
|
||||
User.create_dev_user("Oswald", "Becca", "os@jamkazam.com", "jam123", "Austin", "TX", "US", nil, nil)
|
||||
User.create_dev_user("Anthony", "Davis", "anthony@jamkazam.com", "jam123", "Austin", "TX", "US", nil, nil)
|
||||
User.create_dev_user("George", "Currie", "george@jamkazam.com", "jam123", "Austin", "TX", "US", nil, nil)
|
||||
User.create_dev_user("Chris", "Doughty", "chris@jamkazam.com", "jam123", "Austin", "TX", "US", nil, nil)
|
||||
# if we've run once before, don't run again
|
||||
unless User.find_by_email("seth@jamkazam.com")
|
||||
|
||||
# create one user per employee, +1 for peter2 because he asked for it
|
||||
User.create_dev_user("Seth", "Call", "seth@jamkazam.com", "jam123", "Austin", "TX", "US", nil, 'http://www.jamkazam.com/assets/avatars/avatar_seth.jpg')
|
||||
User.create_dev_user("Brian", "Smith", "briansmith@jamkazam.com", "jam123", "Apex", "NC", "US", nil, 'http://www.jamkazam.com/assets/avatars/avatar_brian.jpg')
|
||||
User.create_dev_user("Peter", "Walker", "peter@jamkazam.com", "jam123", "Austin", "TX", "US", nil, 'http://www.jamkazam.com/assets/avatars/avatar_peter.jpg')
|
||||
User.create_dev_user("Peter", "Walker", "peter2@jamkazam.com", "jam123", "Austin", "TX", "US", nil, 'http://www.jamkazam.com/assets/avatars/avatar_peter.jpg')
|
||||
User.create_dev_user("David", "Wilson", "david@jamkazam.com", "jam123", "Austin", "TX", "US", nil, 'http://www.jamkazam.com/assets/avatars/avatar_david.jpg')
|
||||
User.create_dev_user("Jonathon", "Wilson", "jonathon@jamkazam.com", "jam123", "Bozeman", "MT", "US", [{:instrument_id => "keyboard", :proficiency_level => 4, :priority => 1}], 'http://www.jamkazam.com/assets/avatars/avatar_jonathon.jpg')
|
||||
User.create_dev_user("Jonathan", "Kolyer", "jonathan@jamkazam.com", "jam123", "Austin", "TX", "US", nil, nil)
|
||||
User.create_dev_user("Oswald", "Becca", "os@jamkazam.com", "jam123", "Austin", "TX", "US", nil, nil)
|
||||
User.create_dev_user("Anthony", "Davis", "anthony@jamkazam.com", "jam123", "Austin", "TX", "US", nil, nil)
|
||||
User.create_dev_user("George", "Currie", "george@jamkazam.com", "jam123", "Austin", "TX", "US", nil, nil)
|
||||
User.create_dev_user("Chris", "Doughty", "chris@jamkazam.com", "jam123", "Austin", "TX", "US", nil, nil)
|
||||
User.create_dev_user("Daniel", "Weigh", "daniel@jamkazam.com", "jam123", "Austin", "TX", "US", nil, nil)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,13 +2,14 @@ require 'amqp'
|
|||
require 'jam_ruby'
|
||||
require 'bugsnag'
|
||||
|
||||
# Creates a connection to RabbitMQ.
|
||||
# Creates a connection to RabbitMQ
|
||||
|
||||
# On that single connection, a channel is created (which is a way to multiplex multiple queues/topics over the same TCP connection with rabbitmq)
|
||||
# Then connections to the client_exchange and user_exchange are made, and put into the MQRouter static variables
|
||||
# If this code completes (which implies that Rails can start to begin with, because this is in an initializer),
|
||||
# then the Rails app itself is free to send messages over these exchanges
|
||||
# TODO: reconnect logic if rabbitmq goes down...
|
||||
|
||||
# Also starts websocket-gateway
|
||||
module JamWebEventMachine
|
||||
|
||||
def self.run_em
|
||||
|
|
@ -33,6 +34,15 @@ module JamWebEventMachine
|
|||
Rails.logger.debug "MQRouter.user_exchange = #{MQRouter.user_exchange}"
|
||||
end
|
||||
end
|
||||
|
||||
if Rails.application.config.websocket_gateway_enable
|
||||
|
||||
Thread.new { JamWebsockets::Server.new.run :port => Rails.application.config.websocket_gateway_port,
|
||||
:emwebsocket_debug => Rails.application.config.websocket_gateway_internal_debug,
|
||||
:connect_time_stale => Rails.application.config.websocket_gateway_connect_time_stale,
|
||||
:connect_time_expire => Rails.application.config.websocket_gateway_connect_time_expire }
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -72,4 +82,4 @@ module JamWebEventMachine
|
|||
end
|
||||
end
|
||||
|
||||
JamWebEventMachine.start
|
||||
JamWebEventMachine.start unless $rails_rake_task
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
Resque.redis = Rails.application.config.redis_host
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
require 'resque_failed_job_mailer'
|
||||
|
||||
Resque::Failure::Notifier.configure do |config|
|
||||
config.from = 'seth@jamkazam.com'
|
||||
config.to = 'seth@jamkazam.com'
|
||||
end
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
if Rails.application.config.websocket_gateway_enable
|
||||
|
||||
JamWebsockets::Server.new.run :port => Rails.application.config.websocket_gateway_port,
|
||||
:emwebsocket_debug => Rails.application.config.websocket_gateway_internal_debug,
|
||||
:connect_time_stale => Rails.application.config.websocket_gateway_connect_time_stale,
|
||||
:connect_time_expire => Rails.application.config.websocket_gateway_connect_time_expire
|
||||
|
||||
# Thread.new { JamWebsockets::Server.new.run :port => Rails.application.config.websocket_gateway_port,
|
||||
# :emwebsocket_debug => Rails.application.config.websocket_gateway_internal_debug,
|
||||
# :connect_time_stale => Rails.application.config.websocket_gateway_connect_time_stale,
|
||||
# :connect_time_expire => Rails.application.config.websocket_gateway_connect_time_expire }
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
development: localhost:6379
|
||||
test: localhost:6379
|
||||
production: localhost:6379
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# this rake file is meant to hold shortcuts/helpers for starting onerous command line executions
|
||||
|
||||
# bundle exec rake audiomixer
|
||||
task :audiomixer do
|
||||
|
||||
|
||||
Rails.application.config.websocket_gateway_enable = false # prevent websocket gateway from starting
|
||||
|
||||
Rake::Task['environment'].invoke
|
||||
|
||||
ENV['QUEUE'] = 'audiomixer'
|
||||
Rake::Task['resque:work'].invoke
|
||||
end
|
||||
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
#!/bin/bash -l
|
||||
|
||||
# default config values
|
||||
PORT=3000
|
||||
BUILD_NUMBER=`cat /var/lib/jam-web/BUILD_NUMBER`
|
||||
|
||||
|
||||
CONFIG_FILE="/etc/jam-web/audiomixer-worker-upstart.conf"
|
||||
if [ -e "$CONFIG_FILE" ]; then
|
||||
. "$CONFIG_FILE"
|
||||
fi
|
||||
|
||||
# I don't like doing this, but the next command (bundle exec) retouches/generates
|
||||
# the gemfile. This unfortunately means the next debian update doesn't update this file.
|
||||
# Ultimately this means an old Gemfile.lock is left behind for a new package,
|
||||
# and bundle won't run because it thinks it has the wrong versions of gems
|
||||
rm -f Gemfile.lock
|
||||
|
||||
RAILS_ENV=production BUILD_NUMBER=$BUILD_NUMBER QUEUE=audiomixer exec bundle exec rake environment resque:work
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
description "audiomixer-worker"
|
||||
|
||||
start on startup
|
||||
start on runlevel [2345]
|
||||
stop on runlevel [016]
|
||||
|
||||
exec start-stop-daemon --start --chdir /var/lib/jam-web --exec /var/lib/jam-web/script/package/audiomixer-worker-upstart-run.sh
|
||||
|
|
@ -18,3 +18,41 @@ mkdir -p /var/log/$NAME
|
|||
chown -R $USER:$GROUP /var/lib/$NAME
|
||||
chown -R $USER:$GROUP /etc/$NAME
|
||||
chown -R $USER:$GROUP /var/log/$NAME
|
||||
|
||||
|
||||
# do the same for audiomixer-worker
|
||||
NAME="audiomxer-worker"
|
||||
|
||||
USER="$NAME"
|
||||
GROUP="$NAME"
|
||||
|
||||
# copy upstart file
|
||||
cp /var/lib/$NAME/script/package/$NAME.conf /etc/init/$NAME.conf
|
||||
|
||||
mkdir -p /var/lib/$NAME/log
|
||||
mkdir -p /var/lib/$NAME/tmp
|
||||
mkdir -p /etc/$NAME
|
||||
mkdir -p /var/log/$NAME
|
||||
|
||||
chown -R $USER:$GROUP /var/lib/$NAME
|
||||
chown -R $USER:$GROUP /etc/$NAME
|
||||
chown -R $USER:$GROUP /var/log/$NAME
|
||||
|
||||
|
||||
# do the same for icecast-worker
|
||||
NAME="icecast-worker"
|
||||
|
||||
USER="$NAME"
|
||||
GROUP="$NAME"
|
||||
|
||||
# copy upstart file
|
||||
cp /var/lib/$NAME/script/package/$NAME.conf /etc/init/$NAME.conf
|
||||
|
||||
mkdir -p /var/lib/$NAME/log
|
||||
mkdir -p /var/lib/$NAME/tmp
|
||||
mkdir -p /etc/$NAME
|
||||
mkdir -p /var/log/$NAME
|
||||
|
||||
chown -R $USER:$GROUP /var/lib/$NAME
|
||||
chown -R $USER:$GROUP /etc/$NAME
|
||||
chown -R $USER:$GROUP /var/log/$NAME
|
||||
|
|
|
|||
|
|
@ -25,3 +25,54 @@ then
|
|||
|
||||
userdel $NAME
|
||||
fi
|
||||
|
||||
|
||||
|
||||
NAME="audiomixer-worker"
|
||||
|
||||
set -e
|
||||
if [ "$1" = "remove" ]
|
||||
then
|
||||
set +e
|
||||
# stop the process, if any is found. we don't want this failing to cause an error, though.
|
||||
sudo stop $NAME
|
||||
set -e
|
||||
|
||||
if [ -f /etc/init/$NAME.conf ]; then
|
||||
rm /etc/init/$NAME.conf
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$1" = "purge" ]
|
||||
then
|
||||
if [ -d /var/lib/$NAME ]; then
|
||||
rm -rf /var/lib/$NAME
|
||||
fi
|
||||
|
||||
userdel $NAME
|
||||
fi
|
||||
|
||||
|
||||
NAME="icecast-worker"
|
||||
|
||||
set -e
|
||||
if [ "$1" = "remove" ]
|
||||
then
|
||||
set +e
|
||||
# stop the process, if any is found. we don't want this failing to cause an error, though.
|
||||
sudo stop $NAME
|
||||
set -e
|
||||
|
||||
if [ -f /etc/init/$NAME.conf ]; then
|
||||
rm /etc/init/$NAME.conf
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$1" = "purge" ]
|
||||
then
|
||||
if [ -d /var/lib/$NAME ]; then
|
||||
rm -rf /var/lib/$NAME
|
||||
fi
|
||||
|
||||
userdel $NAME
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
|
||||
NAME="jam-web"
|
||||
|
||||
set -eu
|
||||
|
||||
HOME="/var/lib/$NAME"
|
||||
USER="$NAME"
|
||||
GROUP="$NAME"
|
||||
|
|
@ -32,5 +33,81 @@ then
|
|||
"$USER" >/dev/null
|
||||
fi
|
||||
|
||||
# NISno longer a possible problem; stop ignoring errors
|
||||
# NIS no longer a possible problem; stop ignoring errors
|
||||
set -e
|
||||
|
||||
|
||||
|
||||
# do the same for audiomixer-worker
|
||||
NAME="audiomixer-worker"
|
||||
|
||||
set -eu
|
||||
|
||||
HOME="/var/lib/$NAME"
|
||||
USER="$NAME"
|
||||
GROUP="$NAME"
|
||||
|
||||
# if NIS is used, then errors can occur but be non-fatal
|
||||
if which ypwhich >/dev/null 2>&1 && ypwhich >/dev/null 2>&1
|
||||
then
|
||||
set +e
|
||||
fi
|
||||
|
||||
if ! getent group "$GROUP" >/dev/null
|
||||
then
|
||||
addgroup --system "$GROUP" >/dev/null
|
||||
fi
|
||||
|
||||
# creating user if it isn't already there
|
||||
if ! getent passwd "$USER" >/dev/null
|
||||
then
|
||||
adduser \
|
||||
--system \
|
||||
--home $HOME \
|
||||
--shell /bin/false \
|
||||
--disabled-login \
|
||||
--ingroup "$GROUP" \
|
||||
--gecos "$USER" \
|
||||
"$USER" >/dev/null
|
||||
fi
|
||||
|
||||
# NIS no longer a possible problem; stop ignoring errors
|
||||
set -e
|
||||
|
||||
|
||||
|
||||
# do the same for icecast-worker
|
||||
NAME="icecast-worker"
|
||||
|
||||
set -eu
|
||||
|
||||
HOME="/var/lib/$NAME"
|
||||
USER="$NAME"
|
||||
GROUP="$NAME"
|
||||
|
||||
# if NIS is used, then errors can occur but be non-fatal
|
||||
if which ypwhich >/dev/null 2>&1 && ypwhich >/dev/null 2>&1
|
||||
then
|
||||
set +e
|
||||
fi
|
||||
|
||||
if ! getent group "$GROUP" >/dev/null
|
||||
then
|
||||
addgroup --system "$GROUP" >/dev/null
|
||||
fi
|
||||
|
||||
# creating user if it isn't already there
|
||||
if ! getent passwd "$USER" >/dev/null
|
||||
then
|
||||
adduser \
|
||||
--system \
|
||||
--home $HOME \
|
||||
--shell /bin/false \
|
||||
--disabled-login \
|
||||
--ingroup "$GROUP" \
|
||||
--gecos "$USER" \
|
||||
"$USER" >/dev/null
|
||||
fi
|
||||
|
||||
# NIS no longer a possible problem; stop ignoring errors
|
||||
set -e
|
||||
|
|
@ -38,6 +38,8 @@ gem 'bugsnag'
|
|||
gem 'geokit-rails'
|
||||
gem 'postgres_ext'
|
||||
gem 'resque'
|
||||
gem 'resque-retry'
|
||||
gem 'resque-failed-job-mailer'
|
||||
|
||||
group :development do
|
||||
gem 'pry'
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ module JamWebsockets
|
|||
@log.info "cleaning up server"
|
||||
@router.cleanup
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -49,7 +48,7 @@ module JamWebsockets
|
|||
end
|
||||
|
||||
def start_websocket_listener(listen_ip, port, emwebsocket_debug)
|
||||
EventMachine::WebSocket.start(:host => listen_ip, :port => port, :debug => emwebsocket_debug) do |ws|
|
||||
EventMachine::WebSocket.run(:host => listen_ip, :port => port, :debug => emwebsocket_debug) do |ws|
|
||||
@log.info "new client #{ws}"
|
||||
@router.new_client(ws)
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue