module L4dServer class Launcher def self.spawn(server) new(server).spawn end def self.cleanup(server) new(server).cleanup end def initialize(server) @server = server @template = server.server_template end def spawn setup_directories generate_config mount_overlayfs generate_systemd_unit enable_and_start_service rescue StandardError => e Rails.logger.error("Failed to spawn server: #{e.message}") Rails.logger.error(e.backtrace.join("\n")) @server.update(status: :failed) raise end def cleanup unmount_overlayfs cleanup_directories end private def setup_directories server_dir = "/opt/l4d2/servers/#{@server.id}" FileUtils.mkdir_p("#{server_dir}/upper") FileUtils.mkdir_p("#{server_dir}/work") FileUtils.mkdir_p("#{server_dir}/merged") end def generate_config ConfigGenerator.generate(@server) end def mount_overlayfs server_dir = "/opt/l4d2/servers/#{@server.id}" overlays = @template.template_overlays.ordered.map { |to| "/opt/l4d2/overlays/#{to.overlay.name}" }.join(":") lower_dirs = "#{overlays}:/opt/l4d2/installation" mount_cmd = "fuse-overlayfs -o lowerdir=#{lower_dirs},upperdir=#{server_dir}/upper,workdir=#{server_dir}/work #{server_dir}/merged" success = system(mount_cmd) raise "Failed to mount overlayfs" unless success end def generate_systemd_unit SystemdManager.new(@server).create_unit_file end def enable_and_start_service SystemdManager.new(@server).enable_and_start @server.update(status: :starting) end def unmount_overlayfs server_dir = "/opt/l4d2/servers/#{@server.id}" merged = "#{server_dir}/merged" if Dir.exist?(merged) system("mountpoint -q #{merged} && umount #{merged}") end rescue StandardError => e Rails.logger.warn("Failed to unmount overlayfs: #{e.message}") end def cleanup_directories server_dir = "/opt/l4d2/servers/#{@server.id}" FileUtils.rm_rf(server_dir) if Dir.exist?(server_dir) rescue StandardError => e Rails.logger.warn("Failed to cleanup directories: #{e.message}") end end end