2023-01-18 17:01:50 -05:00
|
|
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
|
|
2023-03-03 15:28:01 -05:00
|
|
|
using System.Collections.Generic;
|
2023-09-25 10:38:16 -04:00
|
|
|
using System.Linq;
|
2023-01-20 12:03:59 -05:00
|
|
|
using System.Net;
|
2023-08-18 08:00:44 -04:00
|
|
|
using System.Threading;
|
2023-03-27 16:37:56 -04:00
|
|
|
using System.Threading.Tasks;
|
2023-01-20 12:03:59 -05:00
|
|
|
using EpicGames.Core;
|
2023-01-18 17:01:50 -05:00
|
|
|
using EpicGames.Horde.Compute;
|
2023-03-17 09:50:40 -04:00
|
|
|
using Horde.Server.Acls;
|
2023-09-25 10:38:16 -04:00
|
|
|
using Horde.Server.Agents;
|
2023-09-26 11:52:56 -04:00
|
|
|
using Horde.Server.Agents.Pools;
|
2023-03-17 09:50:40 -04:00
|
|
|
using Horde.Server.Server;
|
|
|
|
|
using Horde.Server.Utilities;
|
2023-01-18 17:01:50 -05:00
|
|
|
using Microsoft.AspNetCore.Authorization;
|
|
|
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
|
using Microsoft.Extensions.Options;
|
|
|
|
|
|
2023-03-17 09:50:40 -04:00
|
|
|
namespace Horde.Server.Compute
|
2023-01-18 17:01:50 -05:00
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Controller for the /api/v2/compute endpoint
|
|
|
|
|
/// </summary>
|
|
|
|
|
[ApiController]
|
|
|
|
|
[Authorize]
|
|
|
|
|
[Route("[controller]")]
|
|
|
|
|
public class ComputeControllerV2 : HordeControllerBase
|
|
|
|
|
{
|
2023-03-27 16:37:56 -04:00
|
|
|
readonly ComputeService _computeService;
|
2023-01-18 17:01:50 -05:00
|
|
|
readonly IOptionsSnapshot<GlobalConfig> _globalConfig;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Constructor
|
|
|
|
|
/// </summary>
|
2023-03-27 16:37:56 -04:00
|
|
|
public ComputeControllerV2(ComputeService computeService, IOptionsSnapshot<GlobalConfig> globalConfig)
|
2023-01-18 17:01:50 -05:00
|
|
|
{
|
2023-03-27 16:37:56 -04:00
|
|
|
_computeService = computeService;
|
2023-01-18 17:01:50 -05:00
|
|
|
_globalConfig = globalConfig;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Add tasks to be executed remotely
|
|
|
|
|
/// </summary>
|
2023-01-20 14:55:40 -05:00
|
|
|
/// <param name="clusterId">Id of the compute cluster</param>
|
2023-01-18 17:01:50 -05:00
|
|
|
/// <param name="request">The request parameters</param>
|
2023-08-18 08:00:44 -04:00
|
|
|
/// <param name="cancellationToken">Cancellation token for the operation</param>
|
2023-01-18 17:01:50 -05:00
|
|
|
/// <returns></returns>
|
|
|
|
|
[HttpPost]
|
|
|
|
|
[Authorize]
|
2023-01-20 14:55:40 -05:00
|
|
|
[Route("/api/v2/compute/{clusterId}")]
|
2023-08-18 08:00:44 -04:00
|
|
|
public async Task<ActionResult<AssignComputeResponse>> AssignComputeResourceAsync(ClusterId clusterId, [FromBody] AssignComputeRequest request, CancellationToken cancellationToken)
|
2023-01-18 17:01:50 -05:00
|
|
|
{
|
2023-11-14 08:05:02 -05:00
|
|
|
if (!_globalConfig.Value.TryGetComputeCluster(clusterId, out ComputeClusterConfig? clusterConfig))
|
2023-01-18 17:01:50 -05:00
|
|
|
{
|
2023-01-20 14:55:40 -05:00
|
|
|
return NotFound(clusterId);
|
|
|
|
|
}
|
2023-11-14 08:05:02 -05:00
|
|
|
if (!clusterConfig.Authorize(ComputeAclAction.AddComputeTasks, User))
|
2023-01-20 14:55:40 -05:00
|
|
|
{
|
2023-04-18 10:53:15 -04:00
|
|
|
return Forbid(ComputeAclAction.AddComputeTasks, clusterId);
|
2023-01-18 17:01:50 -05:00
|
|
|
}
|
2023-01-20 12:03:59 -05:00
|
|
|
|
2023-11-14 08:05:02 -05:00
|
|
|
AllocateResourceParams arp = new(clusterId, request.Requirements)
|
2023-10-26 16:26:51 -04:00
|
|
|
{
|
|
|
|
|
RequestId = request.RequestId,
|
|
|
|
|
RequesterIp = HttpContext.Connection.RemoteIpAddress,
|
2023-11-14 08:05:02 -05:00
|
|
|
ParentLeaseId = User.GetLeaseClaim(),
|
|
|
|
|
Ports = request.Connection?.Ports ?? new Dictionary<string, int>(),
|
|
|
|
|
ConnectionMode = request.Connection?.ModePreference,
|
|
|
|
|
RequesterPublicIp = request.Connection?.ClientPublicIp,
|
|
|
|
|
UsePublicIp = request.Connection?.PreferPublicIp
|
2023-10-26 16:26:51 -04:00
|
|
|
};
|
|
|
|
|
ComputeResource? computeResource = await _computeService.TryAllocateResourceAsync(arp, cancellationToken);
|
2023-02-28 16:43:25 -05:00
|
|
|
if (computeResource == null)
|
2023-01-20 12:03:59 -05:00
|
|
|
{
|
2023-02-28 16:43:25 -05:00
|
|
|
return StatusCode((int)HttpStatusCode.ServiceUnavailable);
|
2023-01-20 12:03:59 -05:00
|
|
|
}
|
|
|
|
|
|
2023-11-15 08:16:04 -05:00
|
|
|
Dictionary<string, ConnectionMetadataPort> responsePorts = new ();
|
|
|
|
|
foreach ((string name, ComputeResourcePort crp) in computeResource.Ports)
|
|
|
|
|
{
|
|
|
|
|
responsePorts[name] = new ConnectionMetadataPort(crp.Port, crp.AgentPort);
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-28 16:43:25 -05:00
|
|
|
AssignComputeResponse response = new AssignComputeResponse();
|
|
|
|
|
response.Ip = computeResource.Ip.ToString();
|
2023-11-14 08:05:02 -05:00
|
|
|
response.Port = computeResource.Ports[ConnectionMetadataPort.ComputeId].Port;
|
2023-10-26 16:26:51 -04:00
|
|
|
response.ConnectionMode = computeResource.ConnectionMode;
|
2023-11-15 08:16:04 -05:00
|
|
|
response.ConnectionAddress = computeResource.ConnectionAddress;
|
|
|
|
|
response.Ports = responsePorts;
|
2023-02-28 16:43:25 -05:00
|
|
|
response.Nonce = StringUtils.FormatHexString(computeResource.Task.Nonce.Span);
|
2023-03-06 09:55:49 -05:00
|
|
|
response.Key = StringUtils.FormatHexString(computeResource.Task.Key.Span);
|
2023-08-18 11:57:46 -04:00
|
|
|
response.AgentId = computeResource.AgentId;
|
2023-08-18 08:42:04 -04:00
|
|
|
response.LeaseId = computeResource.LeaseId;
|
2023-03-21 21:13:49 -04:00
|
|
|
response.Properties = computeResource.Properties;
|
2023-03-06 09:55:49 -05:00
|
|
|
|
|
|
|
|
foreach (KeyValuePair<string, int> pair in computeResource.Task.Resources)
|
|
|
|
|
{
|
|
|
|
|
response.AssignedResources.Add(pair.Key, pair.Value);
|
|
|
|
|
}
|
2023-01-20 14:55:40 -05:00
|
|
|
|
2023-02-27 10:37:26 -05:00
|
|
|
return response;
|
|
|
|
|
}
|
2023-09-25 10:38:16 -04:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Get current resource needs for active sessions
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="clusterId">ID of the compute cluster</param>
|
|
|
|
|
/// <returns>List of resource needs</returns>
|
|
|
|
|
[HttpGet]
|
|
|
|
|
[Authorize]
|
|
|
|
|
[Route("/api/v2/compute/{clusterId}/resource-needs")]
|
|
|
|
|
public async Task<ActionResult<GetResourceNeedsResponse>> GetResourceNeedsAsync(ClusterId clusterId)
|
|
|
|
|
{
|
|
|
|
|
if (!_globalConfig.Value.TryGetComputeCluster(clusterId, out ComputeClusterConfig? clusterConfig))
|
|
|
|
|
{
|
|
|
|
|
return NotFound(clusterId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!clusterConfig.Authorize(ComputeAclAction.GetComputeTasks, User))
|
|
|
|
|
{
|
|
|
|
|
return Forbid(ComputeAclAction.GetComputeTasks, clusterId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
List<ResourceNeedsMessage> resourceNeeds =
|
|
|
|
|
(await _computeService.GetResourceNeedsAsync())
|
|
|
|
|
.Where(x => x.ClusterId == clusterId.ToString())
|
|
|
|
|
.OrderBy(x => x.Timestamp)
|
|
|
|
|
.Select(x => new ResourceNeedsMessage { SessionId = x.SessionId, Pool = x.Pool, ResourceNeeds = x.ResourceNeeds })
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
return new GetResourceNeedsResponse { ResourceNeeds = resourceNeeds };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Declare resource needs for a session to help server calculate current demand
|
|
|
|
|
/// <see cref="KnownPropertyNames"/> for resource name property names
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="clusterId">Id of the compute cluster</param>
|
|
|
|
|
/// <param name="request">Resource needs request</param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
[HttpPost]
|
|
|
|
|
[Authorize]
|
|
|
|
|
[Route("/api/v2/compute/{clusterId}/resource-needs")]
|
|
|
|
|
public async Task<ActionResult<AssignComputeResponse>> SetResourceNeedsAsync(ClusterId clusterId, [FromBody] ResourceNeedsMessage request)
|
|
|
|
|
{
|
|
|
|
|
if (!_globalConfig.Value.TryGetComputeCluster(clusterId, out ComputeClusterConfig? clusterConfig))
|
|
|
|
|
{
|
|
|
|
|
return NotFound(clusterId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!clusterConfig.Authorize(ComputeAclAction.AddComputeTasks, User))
|
|
|
|
|
{
|
|
|
|
|
return Forbid(ComputeAclAction.AddComputeTasks, clusterId);
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-26 11:52:56 -04:00
|
|
|
await _computeService.SetResourceNeedsAsync(clusterId, request.SessionId, new PoolId(request.Pool).ToString(), request.ResourceNeeds);
|
2023-09-25 10:38:16 -04:00
|
|
|
return Ok(new { message = "Resource needs set" });
|
|
|
|
|
}
|
2023-01-18 17:01:50 -05:00
|
|
|
}
|
|
|
|
|
}
|