diff --git a/admin/app/admin/dashboard.rb b/admin/app/admin/dashboard.rb index 45d0e5054..3a70134aa 100644 --- a/admin/app/admin/dashboard.rb +++ b/admin/app/admin/dashboard.rb @@ -8,7 +8,7 @@ ActiveAdmin.register_page "Dashboard" do span "JamKazam Data Administration Portal" small ul do li "Admin users are users with the admin boolean set to true" - li "Create/Edit JamKazam users using the 'Jam User' menu in header" + li "Invite JamKazam users using the 'Users > Invite' menu in header" li "Admin users are created/deleted when toggling the 'admin' flag for JamKazam users" end end diff --git a/admin/app/admin/icecast_admin_authentication.rb b/admin/app/admin/icecast_admin_authentication.rb new file mode 100644 index 000000000..1298409cb --- /dev/null +++ b/admin/app/admin/icecast_admin_authentication.rb @@ -0,0 +1,3 @@ +ActiveAdmin.register JamRuby::IcecastAdminAuthentication, :as => 'Admin Authentication' do + menu :parent => 'Icecast' +end diff --git a/admin/app/admin/icecast_bootstrap.rb b/admin/app/admin/icecast_bootstrap.rb new file mode 100644 index 000000000..201575fb4 --- /dev/null +++ b/admin/app/admin/icecast_bootstrap.rb @@ -0,0 +1,101 @@ +ActiveAdmin.register_page "Bootstrap" do + menu :parent => 'Icecast' + + page_action :create_server, :method => :post do + + template = IcecastTemplate.find(params[:jam_ruby_icecast_server][:template_id]) + hostname = params[:jam_ruby_icecast_server][:hostname] + + server = IcecastServer.new + server.template = template + server.hostname = hostname + server.location = hostname + server.server_id = hostname + server.save! + + redirect_to admin_bootstrap_path, :notice => "Server created. If you start job worker (bundle exec rake all_jobs), it should update your icecast config." + end + + page_action :brew_template, :method => :post do + # to make this template, I just did 'brew install icecast', and then based the rest of this code on what I saw in /usr/local/etc/icecast.xml + + limit = IcecastLimit.new + limit.clients = 100 + limit.sources = 2 + limit.queue_size = 524288 + limit.client_timeout = 30 + limit.header_timeout = 15 + limit.source_timeout = 10 + limit.burst_size = 65535 + limit.save! + + admin_auth = IcecastAdminAuthentication.new + admin_auth.source_password = 'blueberryjam' + admin_auth.relay_user = 'jamjam' + admin_auth.relay_password = 'blueberryjam' + admin_auth.admin_user = 'jamjam' + admin_auth.admin_password = 'blueberryjam' + admin_auth.save! + + path = IcecastPath.new + path.base_dir = '/usr/local/Cellar/icecast/2.3.3/share/icecast' + path.log_dir = '/usr/local/Cellar/icecast/2.3.3/var/log/icecast' + path.web_root = '/usr/local/Cellar/icecast/2.3.3/share/icecast/web' + path.admin_root = '/usr/local/Cellar/icecast/2.3.3/share/icecast/admin' + path.pid_file = nil + path.save! + + security = IcecastSecurity.new + security.chroot = false + security.save! + + logging = IcecastLogging.new + logging.access_log = 'access.log' + logging.error_log = 'error.log' + logging.log_level = 3 # you might want to change this after creating the template + logging.log_size = 10000 + logging.save! + + listen_socket1 = IcecastListenSocket.new + listen_socket1.port = 8000 + listen_socket1.save! + + listen_socket2 = IcecastListenSocket.new + listen_socket2.port = 8001 + listen_socket2.save! + + template = IcecastTemplate.new + template.name = "Brew-#{IcecastTemplate.count + 1}" + template.admin_email = 'nobody@jamkazam.com' + template.fileserve = true + template.limit = limit + template.admin_auth = admin_auth + template.path = path + template.security = security + template.logging = logging + template.listen_sockets = [listen_socket1, listen_socket2] + template.save! + + redirect_to admin_bootstrap_path, :notice => "Brew template created. Create a server now with that template specified." + end + + action_item do + link_to "Create Brew Template", admin_bootstrap_brew_template_path, :method => :post + end + + content do + + if IcecastTemplate.count == 0 + para "You need to create at least one template for your environment" + else + semantic_form_for IcecastServer.new, :url => admin_bootstrap_create_server_path, :builder => ActiveAdmin::FormBuilder do |f| + f.inputs "New Server" do + f.input :hostname + f.input :template + end + f.actions + end + end + + end +end diff --git a/admin/app/admin/icecast_directory.rb b/admin/app/admin/icecast_directory.rb new file mode 100644 index 000000000..cb0f959ee --- /dev/null +++ b/admin/app/admin/icecast_directory.rb @@ -0,0 +1,3 @@ +ActiveAdmin.register JamRuby::IcecastDirectory, :as => 'Directory' do + menu :parent => 'Icecast' +end diff --git a/admin/app/admin/icecast_limit.rb b/admin/app/admin/icecast_limit.rb new file mode 100644 index 000000000..bbd0f9283 --- /dev/null +++ b/admin/app/admin/icecast_limit.rb @@ -0,0 +1,3 @@ +ActiveAdmin.register JamRuby::IcecastLimit, :as => 'Limit' do + menu :parent => 'Icecast' +end diff --git a/admin/app/admin/icecast_listen_socket.rb b/admin/app/admin/icecast_listen_socket.rb new file mode 100644 index 000000000..57fb80d6e --- /dev/null +++ b/admin/app/admin/icecast_listen_socket.rb @@ -0,0 +1,3 @@ +ActiveAdmin.register JamRuby::IcecastListenSocket, :as => 'Listener' do + menu :parent => 'Icecast' +end diff --git a/admin/app/admin/icecast_logging.rb b/admin/app/admin/icecast_logging.rb new file mode 100644 index 000000000..2465aa0ec --- /dev/null +++ b/admin/app/admin/icecast_logging.rb @@ -0,0 +1,3 @@ +ActiveAdmin.register JamRuby::IcecastLogging, :as => 'Logging' do + menu :parent => 'Icecast' +end diff --git a/admin/app/admin/icecast_master_server_relay.rb b/admin/app/admin/icecast_master_server_relay.rb new file mode 100644 index 000000000..c458d2dbd --- /dev/null +++ b/admin/app/admin/icecast_master_server_relay.rb @@ -0,0 +1,3 @@ +ActiveAdmin.register JamRuby::IcecastMasterServerRelay, :as => 'Master Server Relay' do + menu :parent => 'Icecast' +end diff --git a/admin/app/admin/icecast_mount.rb b/admin/app/admin/icecast_mount.rb new file mode 100644 index 000000000..f8de558d2 --- /dev/null +++ b/admin/app/admin/icecast_mount.rb @@ -0,0 +1,3 @@ +ActiveAdmin.register JamRuby::IcecastMount, :as => 'Mount' do + menu :parent => 'Icecast' +end diff --git a/admin/app/admin/icecast_path.rb b/admin/app/admin/icecast_path.rb new file mode 100644 index 000000000..c44eaadb5 --- /dev/null +++ b/admin/app/admin/icecast_path.rb @@ -0,0 +1,3 @@ +ActiveAdmin.register JamRuby::IcecastPath, :as => 'Path' do + menu :parent => 'Icecast' +end diff --git a/admin/app/admin/icecast_relay.rb b/admin/app/admin/icecast_relay.rb new file mode 100644 index 000000000..96b1946a2 --- /dev/null +++ b/admin/app/admin/icecast_relay.rb @@ -0,0 +1,3 @@ +ActiveAdmin.register JamRuby::IcecastRelay, :as => 'Relay' do + menu :parent => 'Icecast' +end diff --git a/admin/app/admin/icecast_security.rb b/admin/app/admin/icecast_security.rb new file mode 100644 index 000000000..a074cae6d --- /dev/null +++ b/admin/app/admin/icecast_security.rb @@ -0,0 +1,3 @@ +ActiveAdmin.register JamRuby::IcecastSecurity, :as => 'Security' do + menu :parent => 'Icecast' +end diff --git a/admin/app/admin/icecast_server.rb b/admin/app/admin/icecast_server.rb new file mode 100644 index 000000000..08b8e4f7e --- /dev/null +++ b/admin/app/admin/icecast_server.rb @@ -0,0 +1,3 @@ +ActiveAdmin.register JamRuby::IcecastServer, :as => 'Server' do + menu :parent => 'Icecast' +end diff --git a/admin/app/admin/icecast_template.rb b/admin/app/admin/icecast_template.rb new file mode 100644 index 000000000..30ea716d0 --- /dev/null +++ b/admin/app/admin/icecast_template.rb @@ -0,0 +1,3 @@ +ActiveAdmin.register JamRuby::IcecastTemplate, :as => 'Template' do + menu :parent => 'Icecast' +end diff --git a/admin/app/admin/icecast_user_authentication.rb b/admin/app/admin/icecast_user_authentication.rb new file mode 100644 index 000000000..945748700 --- /dev/null +++ b/admin/app/admin/icecast_user_authentication.rb @@ -0,0 +1,3 @@ +ActiveAdmin.register JamRuby::IcecastUserAuthentication, :as => 'User Authentication' do + menu :parent => 'Icecast' +end diff --git a/db/build b/db/build index 259aea93b..e0ece07a9 100755 --- a/db/build +++ b/db/build @@ -19,7 +19,7 @@ rm -rf $TARGET mkdir -p $PG_BUILD_OUT mkdir -p $PG_RUBY_PACKAGE_OUT -bundle update +#bundle update echo "building migrations" bundle exec pg_migrate build --source . --out $PG_BUILD_OUT --test --verbose diff --git a/db/up/icecast.sql b/db/up/icecast.sql index fc0c2a2d9..413995310 100644 --- a/db/up/icecast.sql +++ b/db/up/icecast.sql @@ -207,9 +207,9 @@ CREATE UNLOGGED TABLE icecast_mounts ( CREATE TABLE icecast_paths ( id VARCHAR(64) PRIMARY KEY NOT NULL DEFAULT uuid_generate_v4(), - base_dir VARCHAR(1024) NOT NULL DEFAULT './', + base_dir VARCHAR(1024) NOT NULL DEFAULT './', log_dir VARCHAR(1024) NOT NULL DEFAULT './logs', - pid_file VARCHAR(1024) NOT NULL DEFAULT './icecast.pid', + pid_file VARCHAR(1024) DEFAULT './icecast.pid', web_root VARCHAR(1024) NOT NULL DEFAULT './web', admin_root VARCHAR(1024) NOT NULL DEFAULT './admin', allow_ip VARCHAR(1024), @@ -271,6 +271,28 @@ CREATE TABLE icecast_master_server_relays( updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); + +CREATE TABLE icecast_templates ( + id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(), + + limit_id VARCHAR(64) REFERENCES icecast_limits(id), + admin_auth_id VARCHAR(64) REFERENCES icecast_admin_authentications(id), + directory_id VARCHAR(64) REFERENCES icecast_directories(id), + master_relay_id VARCHAR(64) REFERENCES icecast_master_server_relays(id), + path_id VARCHAR(64) REFERENCES icecast_paths(id), + logging_id VARCHAR(64) REFERENCES icecast_loggings(id), + security_id VARCHAR(64) REFERENCES icecast_securities(id), + + location VARCHAR(1024) NOT NULL, + name VARCHAR(256) NOT NULL, + admin_email VARCHAR(1024) NOT NULL DEFAULT 'admin@jamkazam.com', + fileserve BOOLEAN NOT NULL DEFAULT TRUE, + + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + + CREATE TABLE icecast_servers ( id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(), --use this to mark the server configuration as needing to be regenerated @@ -282,24 +304,25 @@ CREATE TABLE icecast_servers ( path_id VARCHAR(64) REFERENCES icecast_paths(id), logging_id VARCHAR(64) REFERENCES icecast_loggings(id), security_id VARCHAR(64) REFERENCES icecast_securities(id), + template_id VARCHAR(64) NOT NULL REFERENCES icecast_templates(id), -- This is the DNS name or IP address that will be used for the stream directory lookups or possibily -- the playlist generation if a Host header is not provided. While localhost is shown as an example, -- in fact you will want something that your listeners can use. hostname VARCHAR(1024) NOT NULL, + server_id VARCHAR(1024) UNIQUE NOT NULL, --This sets the location string for this icecast instance. It will be shown e.g in the web interface. - location VARCHAR(1024) NOT NULL, + location VARCHAR(1024), --This should contain contact details for getting in touch with the server administrator. - admin_email VARCHAR(1024) NOT NULL DEFAULT 'admin@jamkazam.com', + admin_email VARCHAR(1024), -- This flag turns on the icecast2 fileserver from which static files can be served. -- All files are served relative to the path specified in the configuration -- setting. By DEFAULT the setting is enabled so that requests for the images -- on the status page are retrievable. - fileserve BOOLEAN NOT NULL DEFAULT TRUE, + fileserve BOOLEAN, -- This optional setting allows for the administrator of the server to override the -- DEFAULT server identification. The DEFAULT is icecast followed by a version number -- and most will not care to change it however this setting will change that. - server_id VARCHAR(1024), created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP @@ -337,6 +360,17 @@ CREATE TABLE icecast_server_sockets ( ALTER TABLE icecast_server_sockets ADD CONSTRAINT server_socket_uniqkey UNIQUE (icecast_listen_socket_id, icecast_server_id); +CREATE TABLE icecast_template_sockets ( + id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(), + icecast_listen_socket_id VARCHAR(64) REFERENCES icecast_listen_sockets(id) ON DELETE CASCADE, + icecast_template_id VARCHAR(64) REFERENCES icecast_templates(id) ON DELETE CASCADE, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +ALTER TABLE icecast_template_sockets ADD CONSTRAINT template_socket_uniqkey UNIQUE (icecast_listen_socket_id, icecast_template_id); + + diff --git a/ruby/lib/jam_ruby.rb b/ruby/lib/jam_ruby.rb index b9dbfbdbf..e683d8088 100755 --- a/ruby/lib/jam_ruby.rb +++ b/ruby/lib/jam_ruby.rb @@ -30,6 +30,7 @@ require "jam_ruby/lib/s3_manager" require "jam_ruby/lib/profanity" require "jam_ruby/lib/em_helper.rb" require "jam_ruby/resque/audiomixer" +require "jam_ruby/resque/icecast_config_writer" require "jam_ruby/resque/scheduled/audiomixer_retry" require "jam_ruby/mq_router" require "jam_ruby/base_manager" @@ -105,7 +106,7 @@ require "jam_ruby/models/icecast_path" require "jam_ruby/models/icecast_relay" require "jam_ruby/models/icecast_security" require "jam_ruby/models/icecast_server" -#require "jam_ruby/models/icecast_servermisc" +require "jam_ruby/models/icecast_template" require "jam_ruby/models/icecast_user_authentication" include Jampb diff --git a/ruby/lib/jam_ruby/models/icecast_admin_authentication.rb b/ruby/lib/jam_ruby/models/icecast_admin_authentication.rb index dc84bb3b4..e09f0bf6a 100644 --- a/ruby/lib/jam_ruby/models/icecast_admin_authentication.rb +++ b/ruby/lib/jam_ruby/models/icecast_admin_authentication.rb @@ -2,6 +2,7 @@ module JamRuby class IcecastAdminAuthentication < ActiveRecord::Base has_many :servers, :class_name => "JamRuby::IcecastServer", :inverse_of => :admin_auth, :foreign_key => "admin_auth_id" + has_many :templates, :class_name => "JamRuby::IcecastTemplate", :inverse_of => :admin_auth, :foreign_key => "admin_auth_id" validates :source_password, presence: true, length: {minimum: 5} validates :admin_password, presence: true, length: {minimum: 5} diff --git a/ruby/lib/jam_ruby/models/icecast_directory.rb b/ruby/lib/jam_ruby/models/icecast_directory.rb index d2672fa00..f308f147a 100644 --- a/ruby/lib/jam_ruby/models/icecast_directory.rb +++ b/ruby/lib/jam_ruby/models/icecast_directory.rb @@ -2,6 +2,7 @@ module JamRuby class IcecastDirectory < ActiveRecord::Base has_many :servers, :class_name => "JamRuby::IcecastServer", :inverse_of => :directory, :foreign_key => "directory_id" + has_many :templates, :class_name => "JamRuby::IcecastDirectory", :inverse_of => :directory, :foreign_key => "directory_id" validates :yp_url_timeout, presence: true, numericality: {only_integer: true}, length: {in: 1..30} validates :yp_url, presence: true @@ -13,8 +14,5 @@ module JamRuby dir.tag! 'yp-url', yp_url end end - - - end end \ No newline at end of file diff --git a/ruby/lib/jam_ruby/models/icecast_limit.rb b/ruby/lib/jam_ruby/models/icecast_limit.rb index da0d4b9d2..26375768f 100644 --- a/ruby/lib/jam_ruby/models/icecast_limit.rb +++ b/ruby/lib/jam_ruby/models/icecast_limit.rb @@ -2,6 +2,7 @@ module JamRuby class IcecastLimit < ActiveRecord::Base has_many :servers, class_name: 'JamRuby::IcecastServer', inverse_of: :limit, foreign_key: 'limit_id' + has_many :templates, class_name: 'JamRuby::IcecastTemplate', inverse_of: :limit, foreign_key: 'limit_id' validates :clients, presence: true, numericality: {only_integer: true}, length: {in: 1..15000} validates :sources, presence: true, numericality: {only_integer: true}, length: {in:1..10000} diff --git a/ruby/lib/jam_ruby/models/icecast_logging.rb b/ruby/lib/jam_ruby/models/icecast_logging.rb index cf10c6256..3afcdcd9a 100644 --- a/ruby/lib/jam_ruby/models/icecast_logging.rb +++ b/ruby/lib/jam_ruby/models/icecast_logging.rb @@ -2,6 +2,7 @@ module JamRuby class IcecastLogging < ActiveRecord::Base has_many :servers, :class_name => "JamRuby::IcecastServer", :inverse_of => :logging, :foreign_key => "logging_id" + has_many :templates, :class_name => "JamRuby::IcecastTemplate", :inverse_of => :logging, :foreign_key => "logging_id" validates :access_log, presence: true validates :error_log, presence: true diff --git a/ruby/lib/jam_ruby/models/icecast_master_server_relay.rb b/ruby/lib/jam_ruby/models/icecast_master_server_relay.rb index 3e99a5a19..6c678a9fa 100644 --- a/ruby/lib/jam_ruby/models/icecast_master_server_relay.rb +++ b/ruby/lib/jam_ruby/models/icecast_master_server_relay.rb @@ -2,6 +2,7 @@ module JamRuby class IcecastMasterServerRelay < ActiveRecord::Base has_many :servers, :class_name => "JamRuby::IcecastServer", :inverse_of => :master_relay, :foreign_key => "master_relay_id" + has_many :templates, :class_name => "JamRuby::IcecastTemplate", :inverse_of => :master_relay, :foreign_key => "master_relay_id" validates :master_server, presence: true, length: {minimum: 1} validates :master_server_port, presence: true, numericality: {only_integer: true}, length: {in: 1..65535} diff --git a/ruby/lib/jam_ruby/models/icecast_path.rb b/ruby/lib/jam_ruby/models/icecast_path.rb index 682f55165..b84022535 100644 --- a/ruby/lib/jam_ruby/models/icecast_path.rb +++ b/ruby/lib/jam_ruby/models/icecast_path.rb @@ -2,10 +2,10 @@ module JamRuby class IcecastPath < ActiveRecord::Base has_many :servers, :class_name => "JamRuby::IcecastServer", :inverse_of => :path, :foreign_key => "path_id" + has_many :templates, :class_name => "JamRuby::IcecastTemplate", :inverse_of => :path, :foreign_key => "path_id" validates :base_dir, presence: true validates :log_dir, presence: true - validates :pid_file, presence: true validates :web_root, presence: true validates :admin_root, presence: true @@ -14,7 +14,7 @@ module JamRuby builder.tag! 'paths' do |paths| paths.tag! 'basedir', base_dir paths.tag! 'logdir', log_dir - paths.tag! 'pidfile', pid_file + paths.tag! 'pidfile', pid_file if pid_file paths.tag! 'webroot', web_root paths.tag! 'adminroot', admin_root paths.tag! 'allow-ip', allow_ip if allow_ip diff --git a/ruby/lib/jam_ruby/models/icecast_security.rb b/ruby/lib/jam_ruby/models/icecast_security.rb index 6a9c1ca56..d6bfc45a7 100644 --- a/ruby/lib/jam_ruby/models/icecast_security.rb +++ b/ruby/lib/jam_ruby/models/icecast_security.rb @@ -2,6 +2,7 @@ module JamRuby class IcecastSecurity < ActiveRecord::Base has_many :servers, :class_name => "JamRuby::IcecastServer", :inverse_of => :security, :foreign_key => "security_id" + has_many :templates, :class_name => "JamRuby::IcecastTemplate", :inverse_of => :security, :foreign_key => "security_id" validates :chroot, :inclusion => {:in => [true, false]} diff --git a/ruby/lib/jam_ruby/models/icecast_server.rb b/ruby/lib/jam_ruby/models/icecast_server.rb index f872a6481..a4961fa51 100644 --- a/ruby/lib/jam_ruby/models/icecast_server.rb +++ b/ruby/lib/jam_ruby/models/icecast_server.rb @@ -1,6 +1,9 @@ module JamRuby class IcecastServer < ActiveRecord::Base + belongs_to :template, :class_name => "JamRuby::IcecastTemplate", foreign_key: 'template_id', :inverse_of => :servers + + # all are overrides, because the template defines all of these as well. When building the XML, we will prefer these if set belongs_to :limit, :class_name => "JamRuby::IcecastLimit", foreign_key: 'limit_id', :inverse_of => :servers belongs_to :admin_auth, :class_name => "JamRuby::IcecastAdminAuthentication", foreign_key: 'admin_auth_id', :inverse_of => :servers belongs_to :directory, :class_name => "JamRuby::IcecastDirectory", foreign_key: 'directory_id', :inverse_of => :servers @@ -8,24 +11,18 @@ module JamRuby belongs_to :path, :class_name => "JamRuby::IcecastPath", foreign_key: 'path_id', :inverse_of => :servers belongs_to :logging, :class_name => "JamRuby::IcecastLogging", foreign_key: 'logging_id', :inverse_of => :servers belongs_to :security, :class_name => "JamRuby::IcecastSecurity", foreign_key: 'security_id', :inverse_of => :servers - - has_and_belongs_to_many :mounts, :class_name => "JamRuby::IcecastMount", :join_table => "icecast_server_mounts" has_and_belongs_to_many :listen_sockets, :class_name => "JamRuby::IcecastListenSocket", :join_table => "icecast_server_sockets" + + # mounts and relays are naturally server-specific, though + has_and_belongs_to_many :mounts, :class_name => "JamRuby::IcecastMount", :join_table => "icecast_server_mounts" has_and_belongs_to_many :relays, :class_name => "JamRuby::IcecastRelay", :join_table => "icecast_server_relays" validates :config_changed, :inclusion => {:in => [true, false]} validates :hostname, presence: true - validates :location, presence: true - validates :admin_email, presence: true - validates :fileserve, :inclusion => {:in => [true, false]} + validates :fileserve, :inclusion => {:in => [true, false]}, :if => lambda {|s| s.fileserve.present? } validates :server_id, presence: true - validates :limit, presence: true - validates :admin_auth, presence: true - validates :path, presence: true - validates :logging, presence: true - validates :security, presence: true - validates :listen_sockets, length: {minimum: 1} + validates :template, presence: true def dumpXml (output=$stdout, indent=1) @@ -33,27 +30,36 @@ module JamRuby builder.tag! 'icecast' do |icecast| icecast.tag! 'hostname', hostname - icecast.tag! 'location', location - icecast.tag! 'admin', admin_email - icecast.tag! 'fileserve', fileserve ? 1 : 0 + icecast.tag! 'location', location.nil? ? template.location : location icecast.tag! 'server-id', server_id + icecast.tag! 'admin', admin_email ? admin_email : template.admin_email + icecast.tag! 'fileserve', fileserve.nil? ? (template.fileserve ? 1 : 0) : (fileserve ? 1 : 0) - limit.dumpXml(builder) unless limit.nil? - admin_auth.dumpXml(builder) unless admin_auth.nil? - directory.dumpXml(builder) unless directory.nil? - master_relay.dumpXml(builder) unless master_relay.nil? - path.dumpXml(builder) unless path.nil? - logging.dumpXml(builder) unless logging.nil? - security.dumpXml(builder) unless security.nil? + # do we have an override specified? or do we go with the template + current_limit = limit ? limit : template.limit + current_admin_auth = admin_auth ? admin_auth : template.admin_auth + current_directory = directory ? directory : template.directory + current_master_relay = master_relay ? master_relay : template.master_relay + current_path = path ? path : template.path + current_logging = logging ? logging : template.logging + current_security = security ? security : template.security + current_listen_sockets = listen_sockets.length > 0 ? listen_sockets : template.listen_sockets + + current_limit.dumpXml(builder) unless current_limit.nil? + current_admin_auth.dumpXml(builder) unless current_admin_auth.nil? + current_directory.dumpXml(builder) unless current_directory.nil? + current_master_relay.dumpXml(builder) unless current_master_relay.nil? + current_path.dumpXml(builder) unless current_path.nil? + current_logging.dumpXml(builder) unless current_logging.nil? + current_security.dumpXml(builder) unless current_security.nil? + current_listen_sockets.each do |listen_socket| + listen_socket.dumpXml(builder) + end relays.each do |relay| relay.dumpXml(builder) end - listen_sockets.each do |listen_socket| - listen_socket.dumpXml(builder) - end - mounts.each do |mount| mount.dumpXml(builder) end diff --git a/ruby/lib/jam_ruby/models/icecast_servermisc.rb b/ruby/lib/jam_ruby/models/icecast_servermisc.rb deleted file mode 100644 index c88c34a7d..000000000 --- a/ruby/lib/jam_ruby/models/icecast_servermisc.rb +++ /dev/null @@ -1,6 +0,0 @@ -module JamRuby - class IcecastServerMisc < ActiveRecord::Base - self.primary_key = 'id' - - end -end \ No newline at end of file diff --git a/ruby/lib/jam_ruby/models/icecast_template.rb b/ruby/lib/jam_ruby/models/icecast_template.rb new file mode 100644 index 000000000..96226f268 --- /dev/null +++ b/ruby/lib/jam_ruby/models/icecast_template.rb @@ -0,0 +1,28 @@ +module JamRuby + class IcecastTemplate < ActiveRecord::Base + + + belongs_to :limit, :class_name => "JamRuby::IcecastLimit", foreign_key: 'limit_id', :inverse_of => :templates + belongs_to :admin_auth, :class_name => "JamRuby::IcecastAdminAuthentication", foreign_key: 'admin_auth_id', :inverse_of => :templates + belongs_to :directory, :class_name => "JamRuby::IcecastDirectory", foreign_key: 'directory_id', :inverse_of => :templates + belongs_to :master_relay, :class_name => "JamRuby::IcecastMasterServerRelay", foreign_key: 'master_relay_id', :inverse_of => :templates + belongs_to :path, :class_name => "JamRuby::IcecastPath", foreign_key: 'path_id', :inverse_of => :templates + belongs_to :logging, :class_name => "JamRuby::IcecastLogging", foreign_key: 'logging_id', :inverse_of => :templates + belongs_to :security, :class_name => "JamRuby::IcecastSecurity", foreign_key: 'security_id', :inverse_of => :templates + + has_many :servers, :class_name => "JamRuby::IcecastServer", :inverse_of => :template, :foreign_key => "template_id" + has_and_belongs_to_many :listen_sockets, :class_name => "JamRuby::IcecastListenSocket", :join_table => "icecast_template_sockets" + + validates :name, presence: true + validates :location, presence: true + validates :admin_email, presence: true + validates :fileserve, :inclusion => {:in => [true, false]} + + validates :limit, presence: true + validates :admin_auth, presence: true + validates :path, presence: true + validates :logging, presence: true + validates :security, presence: true + validates :listen_sockets, length: {minimum: 1} + end +end \ No newline at end of file diff --git a/ruby/lib/jam_ruby/resque/icecast_config_writer.rb b/ruby/lib/jam_ruby/resque/icecast_config_writer.rb new file mode 100644 index 000000000..6c9964e76 --- /dev/null +++ b/ruby/lib/jam_ruby/resque/icecast_config_writer.rb @@ -0,0 +1,75 @@ +require 'json' +require 'resque' +require 'resque-retry' +require 'net/http' +require 'digest/md5' + +module JamRuby + + # executes a mix of tracks, creating a final output mix + class IcecastConfigWriter + + @queue = :icecast_config_writer + + @@log = Logging.logger[IcecastConfigWriter] + + attr_accessor :icecast_server_id + + def self.perform(icecast_server_id) + icecast = IcecastConfigWriter.new() + icecast.icecast_server_id = icecast_server_id + icecast.run + end + + def initialize + + end + + def validate + raise "icecast_server_id not spceified" unless icecast_server_id + end + + def reload + cmd = APP_CONFIG.icecast_reload_cmd + + system(cmd) + + raise "unable to execute icecast reload cmd=#{cmd}. result=#{$?}" unless $? == 0 + 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 = {id: icecast_server_id} + query[:config_changed] = true if File.exist? config_file + + icecast_server = IcecastServer.where(query).first + + icecast_server.with_lock do + + icecast_server.validate + # 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) + File.open(tmp_config, 'w') { + icecast_server.dumpXml(output=tmp_config) + } + + # if written successfully, overwrite the current file + FileUtils.mv tmp_config, config_file + + # reload server + reload + + icecast_server.config_changed = false + icecast_server.save! + end + end + end +end \ No newline at end of file diff --git a/ruby/spec/factories.rb b/ruby/spec/factories.rb index 5a98cd836..1cdaede1b 100644 --- a/ruby/spec/factories.rb +++ b/ruby/spec/factories.rb @@ -234,10 +234,19 @@ FactoryGirl.define do factory :icecast_server, :class => JamRuby::IcecastServer do hostname Faker::Lorem.characters(10) - location Faker::Lorem.characters(10) server_id Faker::Lorem.characters(10) factory :icecast_server_minimal do + association :template, :factory => :icecast_template_minimal + end + end + + factory :icecast_template, :class => JamRuby::IcecastTemplate do + + name Faker::Lorem.characters(10) + location Faker::Lorem.characters(10) + + factory :icecast_template_minimal do association :limit, :factory => :icecast_limit association :admin_auth, :factory => :icecast_admin_authentication association :path, :factory => :icecast_path diff --git a/ruby/spec/jam_ruby/models/icecast_server_spec.rb b/ruby/spec/jam_ruby/models/icecast_server_spec.rb index 24b0a3051..f667c6994 100644 --- a/ruby/spec/jam_ruby/models/icecast_server_spec.rb +++ b/ruby/spec/jam_ruby/models/icecast_server_spec.rb @@ -16,10 +16,10 @@ describe IcecastServer do output.rewind xml = Nokogiri::XML(output) xml.css('icecast hostname').text.should == server.hostname - xml.css('icecast location').text.should == server.location - xml.css('icecast admin').text.should == server.admin_email - xml.css('icecast fileserve').text.should == (server.fileserve ? '1' : '0') xml.css('icecast server-id').text.should == server.server_id + xml.css('icecast location').text.should == server.template.location + xml.css('icecast admin').text.should == server.template.admin_email + xml.css('icecast fileserve').text.should == (server.template.fileserve ? '1' : '0') xml.css('icecast limits').length.should == 1 xml.css('icecast authentication').length.should == 1 xml.css('icecast directory').length.should == 0 diff --git a/ruby/spec/jam_ruby/models/icecast_template_spec.rb b/ruby/spec/jam_ruby/models/icecast_template_spec.rb new file mode 100644 index 000000000..267acdd0d --- /dev/null +++ b/ruby/spec/jam_ruby/models/icecast_template_spec.rb @@ -0,0 +1,10 @@ +require 'spec_helper' + +describe IcecastListenSocket do + + let(:template) { template = FactoryGirl.create(:icecast_template_minimal) } + + it "save" do + template.errors.any?.should be_false + end +end diff --git a/web/config/application.rb b/web/config/application.rb index caaee5db9..d8cdbd50a 100644 --- a/web/config/application.rb +++ b/web/config/application.rb @@ -168,6 +168,11 @@ include JamRuby config.audiomixer_path = "/var/lib/audiomixer/audiomixer/audiomixerapp" + # if it looks like linux, use init.d script; otherwise use kill + config.icecast_reload_cmd = File.exist? '/usr/bin/icecast2' ? '/etc/init.d/icecast2 reload' : 'bash -l -c "kill -1 `ps -f -c | grep icecast | awk \'{print $2}\'`"' + # if it looks like linux, use that path; otherwise use the brew default path + config.icecast_config_file = File.exist? '/etc/icecast2/icecast.xml' ? '/etc/icecast2/icecast.xml' : '/usr/local/etc/icecast.xml' + config.email_alerts_alias = 'alerts@jamkazam.com' # should be used for 'oh no' server down/service down sorts of emails config.email_generic_from = 'nobody@jamkazam.com' config.email_smtp_address = 'smtp.sendgrid.net'