Ejemplo n.º 1
0
        public async Task CreateAndStartContainerWaitForExit(SessionHostsStartInfo startParameters)
        {
            IList <PortMapping> portMappings = GetPortMappings(startParameters, 0);

            if (startParameters.SessionHostType == SessionHostType.Container)
            {
                foreach (PortMapping mapping in portMappings)
                {
                    _logger.LogInformation(
                        $"{mapping.GamePort.Name} ({mapping.GamePort.Protocol}): Local port {mapping.NodePort} mapped to container port {mapping.GamePort.Number} ");
                }
            }

            DownloadAndExtractAllAssets(startParameters);
            DownloadGameCertificates(startParameters);

            ISessionHostRunner sessionHostRunner =
                _sessionHostRunnerFactory.CreateSessionHostRunner(startParameters.SessionHostType, _vmConfiguration, _logger);

            // RetrieveResources does a docker pull
            // If we're running Linux containers on Windows, we might want to pull image from ACR first to save the image on local.
            // In this case, you need to simply change the value to true for forcePullFromAcrOnLinuxContainersOnWindows in MultiplayerSettings.json
            // so if this image is not locally built, docker create will do a docker pull first
            // In another case, we might have built the image locally but tagged with a fake registry name (e.g. myacr.io/mygame:0.1),
            // Then make sure to change the value to false if you want to use the image from local.
            if (Globals.GameServerEnvironment == GameServerEnvironment.Windows || Globals.Settings.ForcePullFromAcrOnLinuxContainersOnWindows)
            {
                await sessionHostRunner.RetrieveResources(startParameters);
            }

            NoOpSessionHostManager sessionHostManager = new NoOpSessionHostManager();
            SessionHostInfo        sessionHostInfo    =
                await sessionHostRunner.CreateAndStart(0, new GameResourceDetails { SessionHostsStartInfo = startParameters }, sessionHostManager);

            if (sessionHostInfo == null)
            {
                return;
            }

            string typeSpecificId = sessionHostInfo.TypeSpecificId;

            _logger.LogInformation("Waiting for heartbeats from the game server.....");

            await sessionHostRunner.WaitOnServerExit(typeSpecificId).ConfigureAwait(false);

            string logFolder = Path.Combine(Globals.VmConfiguration.VmDirectories.GameLogsRootFolderVm, sessionHostInfo.LogFolderId);
            await sessionHostRunner.CollectLogs(typeSpecificId, logFolder, sessionHostManager);

            await sessionHostRunner.TryDelete(typeSpecificId);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Runs the specified action periodically (including amidst exceptions) until explicitly cancelled.
        /// </summary>
        /// <param name="action">The operation to perform periodically.</param>
        /// <param name="systemOperations"></param>
        /// <param name="intervalBetweenRuns">Delay between performing the operation (if the operation completes or if there is an exception).</param>
        /// <param name="cancellationToken">Mostly used for unit testing.</param>
        /// <returns></returns>
        public static async Task RunUntilCancelled(
            Func <Task> action,
            ISystemOperations systemOperations,
            TimeSpan intervalBetweenRuns,
            CancellationToken cancellationToken,
            MultiLogger logger)
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                try
                {
                    await action();
                }
                catch (Exception e)
                {
                    logger.LogException(e);
                }

                try
                {
                    await systemOperations.Delay((int)intervalBetweenRuns.TotalMilliseconds, cancellationToken);
                }
                catch (TaskCanceledException ex)
                {
                    logger.LogInformation($"{nameof(RunUntilCancelled)} stopped with {nameof(TaskCanceledException)}: {ex}");
                }
            }
        }
Ejemplo n.º 3
0
        public void ExtractAssets(string assetFileName, string targetFolder)
        {
            string fileExtension = Path.GetExtension(assetFileName).ToLowerInvariant();

            // If the OS is windows use the native .NET zip extraction (we only support .zip for Windows).
            if (_systemOperations.IsOSPlatform(OSPlatform.Windows))
            {
                if (fileExtension != Constants.ZipExtension)
                {
                    throw new AssetExtractionFailedException($"Only Zip format is supported in Windows. Unable to extract {assetFileName}.");
                }

                if (Directory.Exists(targetFolder))
                {
                    Directory.Delete(targetFolder, true);
                }

                _systemOperations.ExtractToDirectory(assetFileName, targetFolder);
            }
            else
            {
                // If the OS is Linux, we need to extract files using Linux Command Line.
                if (!LinuxSupportedFileExtensions.Any(extension => assetFileName.EndsWith(extension, StringComparison.InvariantCultureIgnoreCase)))
                {
                    throw new AssetExtractionFailedException($"Only Tar, Tar.gz and Zip formats are supported in Linux. Unable to extract {assetFileName}.");
                }

                _systemOperations.CreateDirectory(targetFolder);

                ProcessStartInfo processStartInfo = Path.GetExtension(assetFileName).ToLowerInvariant() == Constants.ZipExtension
                   ? GetProcessStartInfoForZip(assetFileName, targetFolder)
                   : GetProcessStartInfoForTarOrGZip(assetFileName, targetFolder);

                _logger.LogInformation($"Starting asset extraction with command arguments: {processStartInfo.Arguments}");

                using (Process process = new Process())
                {
                    process.StartInfo = processStartInfo;
                    (int exitCode, string stdOut, string stdErr) = _systemOperations.RunProcessWithStdCapture(process);

                    if (!string.IsNullOrEmpty(stdOut))
                    {
                        _logger.LogVerbose(stdOut);
                    }

                    if (!string.IsNullOrEmpty(stdErr))
                    {
                        _logger.LogError(stdErr);
                    }

                    if (exitCode != 0)
                    {
                        throw new AssetExtractionFailedException($"Asset extraction for file {assetFileName} failed. Errors: {stdErr ?? string.Empty}");
                    }

                    _systemOperations.SetUnixOwnerIfNeeded(targetFolder, true);
                }
            }
        }
Ejemplo n.º 4
0
        public async Task CreateAndStartContainerWaitForExit(SessionHostsStartInfo startParameters)
        {
            IList <PortMapping> portMappings = GetPortMappings(startParameters, 0);

            if (startParameters.SessionHostType == SessionHostType.Container)
            {
                foreach (PortMapping mapping in portMappings)
                {
                    _logger.LogInformation(
                        $"{mapping.GamePort.Name} ({mapping.GamePort.Protocol}): Local port {mapping.NodePort} mapped to container port {mapping.GamePort.Number} ");
                }
            }

            DownloadAndExtractAllAssets(startParameters);
            DownloadGameCertificates(startParameters);

            ISessionHostRunner sessionHostRunner =
                _sessionHostRunnerFactory.CreateSessionHostRunner(startParameters.SessionHostType, _vmConfiguration, _logger);

            // RetrieveResources does a docker pull
            // if we're running Linux containers on Windows we do not want that, since we might
            // have built the image locally but tagged with a fake registry name (e.g. myacr.io/mygame:0.1)
            // docker pull will try to connect to myacr.io and fail
            // if this image is not locally built, docker create (executed next) will do a docker pull first
            if (Globals.GameServerEnvironment == GameServerEnvironment.Windows)
            {
                await sessionHostRunner.RetrieveResources(startParameters);
            }

            NoOpSessionHostManager sessionHostManager = new NoOpSessionHostManager();
            SessionHostInfo        sessionHostInfo    =
                await sessionHostRunner.CreateAndStart(0, new GameResourceDetails { SessionHostsStartInfo = startParameters }, sessionHostManager);

            string containerId = sessionHostInfo.TypeSpecificId;

            _logger.LogInformation("Waiting for heartbeats from the game server.....");

            await sessionHostRunner.WaitOnServerExit(containerId).ConfigureAwait(false);

            string logFolder = Path.Combine(Globals.VmConfiguration.VmDirectories.GameLogsRootFolderVm, sessionHostInfo.LogFolderId);
            await sessionHostRunner.CollectLogs(containerId, logFolder, sessionHostManager);

            await sessionHostRunner.TryDelete(containerId);
        }
Ejemplo n.º 5
0
        public Task <SessionHostInfo> CreateAndStart(int instanceNumber, GameResourceDetails gameResourceDetails, ISessionHostManager sessionHostManager)
        {
            SessionHostsStartInfo sessionHostStartInfo = gameResourceDetails.SessionHostsStartInfo;
            string sessionHostUniqueId = Guid.NewGuid().ToString("D");
            string logFolderPathOnVm   = Path.Combine(_vmConfiguration.VmDirectories.GameLogsRootFolderVm, sessionHostUniqueId);

            _systemOperations.CreateDirectory(logFolderPathOnVm);

            ISessionHostConfiguration sessionHostConfiguration = new SessionHostProcessConfiguration(_vmConfiguration, _logger, _systemOperations, sessionHostStartInfo);
            string configFolderPathOnVm = _vmConfiguration.GetConfigRootFolderForSessionHost(instanceNumber);

            _systemOperations.CreateDirectory(configFolderPathOnVm);

            ProcessStartInfo processStartInfo = new ProcessStartInfo();

            (string executableFileName, string arguments) = GetExecutableAndArguments(sessionHostStartInfo, instanceNumber);
            processStartInfo.FileName         = executableFileName;
            processStartInfo.Arguments        = arguments;
            processStartInfo.WorkingDirectory = sessionHostStartInfo.GameWorkingDirectory ?? Path.GetDirectoryName(executableFileName);
            processStartInfo.Environment.AddRange(sessionHostConfiguration.GetEnvironmentVariablesForSessionHost(instanceNumber, sessionHostUniqueId));

            _logger.LogInformation($"Starting process for session host with instance number {instanceNumber} and process info: FileName - {executableFileName}, Args - {arguments}.");

            SessionHostInfo sessionHost = sessionHostManager.AddNewSessionHost(sessionHostUniqueId, sessionHostStartInfo.AssignmentId, instanceNumber, sessionHostUniqueId, SessionHostType.Process);

            sessionHostConfiguration.Create(instanceNumber, sessionHostUniqueId, GetVmAgentIpAddress(), _vmConfiguration, sessionHostUniqueId);

            try
            {
                string processId = _processWrapper.Start(processStartInfo).ToString();
                sessionHostManager.UpdateSessionHostTypeSpecificId(sessionHostUniqueId, processId);
                _logger.LogInformation($"Started process for session host. Instance Number: {instanceNumber}, UniqueId: {sessionHostUniqueId}, ProcessId: {processId}");
            }
            catch (Exception exception)
            {
                _logger.LogException($"Failed to start process based host with instance number {instanceNumber}", exception);
                sessionHostManager.RemoveSessionHost(sessionHostUniqueId);
                sessionHost = null;
            }

            return(Task.FromResult(sessionHost));
        }
Ejemplo n.º 6
0
        public async Task WaitOnServerExit(string containerId)
        {
            ContainerWaitResponse containerWaitResponse = await _dockerClient.Containers.WaitContainerAsync(containerId).ConfigureAwait(false);

            _logger.LogInformation($"Container {containerId} exited with exit code {containerWaitResponse.StatusCode}.");
        }