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)); }
abstract public Task <SessionHostInfo> CreateAndStart(int instanceNumber, GameResourceDetails gameResourceDetails, ISessionHostManager sessionHostManager);
/// <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); }