Ejemplo n.º 1
0
 public override Task CollectLogs(string processId, string logsFolder, ISessionHostManager sessionHostManager)
 {
     // The game server is free to read the env variable for log folder and write all output to a file in that folder.
     // If required, we can add action handlers (see SystemOperations.RunProcess for example).
     // However, keeping the file handle around can be tricky.
     return(Task.CompletedTask);
 }
Ejemplo n.º 2
0
        public async Task CollectLogs(string id, string logsFolder, ISessionHostManager sessionHostManager)
        {
            try
            {
                _logger.LogVerbose($"Collecting logs for container {id}.");
                string destinationFileName = Path.Combine(logsFolder, ConsoleLogCaptureFileName);

                if (sessionHostManager.LinuxContainersOnWindows)
                {
                    // we do this for lcow since containers are running on a Hyper-V Linux machine
                    // which the host Windows machine does not have "copy" access to, to get the logs with a FileCopy
                    // this is only supposed to run on LocalMultiplayerAgent running on lcow
                    StringBuilder sb         = new StringBuilder();
                    Stream        logsStream = await _dockerClient.Containers.GetContainerLogsAsync(id,
                                                                                                    new ContainerLogsParameters()
                    {
                        ShowStdout = true, ShowStderr = true
                    });

                    using (StreamReader sr = new StreamReader(logsStream))
                    {
                        Stopwatch sw = new Stopwatch();
                        while (!sr.EndOfStream)
                        {
                            if (sw.Elapsed.Seconds > 3) // don't flood STDOUT with messages, output one every 3 seconds if logs are too many
                            {
                                _logger.LogVerbose($"Gathering logs for container {id}, please wait...");
                                sw.Restart();
                            }
                            _systemOperations.FileAppendAllText(destinationFileName, sr.ReadLine() + Environment.NewLine);
                        }
                    }
                    _logger.LogVerbose($"Written logs for container {id} to {destinationFileName}.");
                }
                else
                {
                    ContainerInspectResponse containerInspectResponse = await _dockerClient.Containers.InspectContainerAsync(id);

                    string dockerLogsPath = containerInspectResponse?.LogPath;
                    if (!string.IsNullOrEmpty(dockerLogsPath) && _systemOperations.FileExists(dockerLogsPath))
                    {
                        _logger.LogVerbose($"Copying log file {dockerLogsPath} for container {id} to {destinationFileName}.");
                        _systemOperations.FileCopy(dockerLogsPath, destinationFileName);
                    }
                }
            }
            catch (DockerContainerNotFoundException)
            {
                _logger.LogInformation($"Docker container {id} not found.");
            }
        }
Ejemplo n.º 3
0
        public override 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);

            // Create the dumps folder as a subfolder of the logs folder
            string dumpFolderPathOnVm = Path.Combine(logFolderPathOnVm, VmDirectories.GameDumpsFolderName);

            _systemOperations.CreateDirectory(dumpFolderPathOnVm);

            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, sessionHostManager.VmAgentSettings));

            _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.º 4
0
 abstract public Task <SessionHostInfo> CreateAndStart(int instanceNumber, GameResourceDetails gameResourceDetails, ISessionHostManager sessionHostManager);
Ejemplo n.º 5
0
 abstract public Task CollectLogs(string id, string logsFolder, ISessionHostManager sessionHostManager);
Ejemplo n.º 6
0
        /// <summary>
        /// Creates and starts a container, assigning <paramref name="instanceNumber"/> to it.
        /// </summary>
        /// <param name="instanceNumber">
        /// An instance number associated with a container. It is used to map assets folder to the container
        /// and then re-use for container recycling.
        /// </param>
        /// <returns>A <see cref="Task"/>.</returns>
        public async Task <SessionHostInfo> CreateAndStart(int instanceNumber, GameResourceDetails gameResourceDetails, ISessionHostManager sessionHostManager)
        {
            // The current Docker client doesn't yet allow specifying a local name for the image.
            // It is stored with as the remote path name. Thus, the parameter to CreateAndStartContainers
            // is the same as the remote image path.
            SessionHostsStartInfo sessionHostStartInfo = gameResourceDetails.SessionHostsStartInfo;
            ContainerImageDetails imageDetails         = sessionHostStartInfo.ImageDetails;
            string imageName = $"{imageDetails.Registry}/{imageDetails.ImageName}:{imageDetails.ImageTag ?? "latest"}";

            // The game containers need a unique folder to write their logs. Ideally,
            // we would specify the containerId itself as the subfolder. However, we have to
            // specify volume bindings before docker gives us the container id, so using
            // a random guid here instead
            string logFolderId = _systemOperations.NewGuid().ToString("D");
            ISessionHostConfiguration sessionHostConfiguration = new SessionHostContainerConfiguration(_vmConfiguration, _logger, _systemOperations, _dockerClient, sessionHostStartInfo);
            IList <PortMapping>       portMappings             = sessionHostConfiguration.GetPortMappings(instanceNumber);
            List <string>             environmentValues        = sessionHostConfiguration.GetEnvironmentVariablesForSessionHost(instanceNumber, logFolderId)
                                                                 .Select(x => $"{x.Key}={x.Value}").ToList();

            string dockerId = await CreateContainer(
                imageName,
                environmentValues,
                GetVolumeBindings(sessionHostStartInfo, instanceNumber, logFolderId),
                portMappings,
                GetStartGameCmd(sessionHostStartInfo),
                sessionHostStartInfo.HostConfigOverrides,
                GetGameWorkingDir(sessionHostStartInfo));

            SessionHostInfo sessionHost = sessionHostManager.AddNewSessionHost(dockerId, sessionHostStartInfo.AssignmentId, instanceNumber, logFolderId);

            // https://docs.docker.com/docker-for-windows/networking/
            string agentIPaddress = sessionHostManager.LinuxContainersOnWindows ? "host.docker.internal" : GetVmAgentIpAddress();

            sessionHostConfiguration.Create(instanceNumber, dockerId, agentIPaddress, _vmConfiguration, logFolderId);

            // on LinuxContainersForWindows, VMAgent will run in a Windows environment
            // but we want the Linux directory separator char
            if (sessionHostManager.LinuxContainersOnWindows)
            {
                string configFilePath = Path.Combine(_vmConfiguration.GetConfigRootFolderForSessionHost(instanceNumber),
                                                     VmDirectories.GsdkConfigFilename);
                File.WriteAllText(configFilePath, File.ReadAllText(configFilePath).
                                  Replace($"{_vmConfiguration.VmDirectories.GameLogsRootFolderContainer}\\\\",
                                          $"{_vmConfiguration.VmDirectories.GameLogsRootFolderContainer}/"));
            }
            try
            {
                await StartContainer(dockerId);

                _logger.LogInformation($"Started container {dockerId}, with assignmentId {sessionHostStartInfo.AssignmentId}, instance number {instanceNumber}, and logFolderId {logFolderId}");
            }
            catch (Exception exception)
            {
                _logger.LogException($"Failed to start container based host with instance number {instanceNumber}", exception);
                sessionHostManager.RemoveSessionHost(dockerId);
                sessionHost = null;
            }


            return(sessionHost);
        }