// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
using EpicGames.Horde.Agents;
using EpicGames.Horde.Agents.Leases;
#pragma warning disable CA2227 // Collection properties should be read only
namespace EpicGames.Horde.Compute
{
///
/// Describes port used by a compute task
///
public class ConnectionMetadataPort
{
///
/// Built-in port used for agent and compute task communication
///
public const string ComputeId = "_horde_compute";
///
/// Externally visible port that is mapped to agent port
/// In direct connection mode, these two are identical.
///
public int Port { get; }
///
/// Port the local process on the agent is listening on
///
public int AgentPort { get; }
///
/// Constructor
///
///
///
public ConnectionMetadataPort(int port, int agentPort)
{
Port = port;
AgentPort = agentPort;
}
}
///
/// Request a machine to execute compute requests
///
public class AssignComputeRequest
{
///
/// Desired protocol version for the client
///
public int Protocol { get; set; }
///
/// Condition to identify machines that can execute the request
///
public Requirements? Requirements { get; set; }
///
/// Arbitrary ID to correlate the same request over multiple calls.
/// It's recommended to pick something globally unique, such as a UUID.
///
public string? RequestId { get; set; }
///
/// Details for making an agent connection
///
public ConnectionMetadataRequest? Connection { get; set; }
}
///
/// Request details for making an agent connection
///
public class ConnectionMetadataRequest
{
///
/// Public IP of client requesting a compute resource (initiator)
/// As communication between client and Horde server may be on an internal network,
/// the client is responsible for resolving and providing this information.
///
public string? ClientPublicIp { get; set; }
///
/// TCP/IP ports the compute resource will listen to.
/// Key = arbitrary name identifying the port
/// Value = actual port number
/// Relay connection mode uses this information to set up port forwarding.
///
public Dictionary Ports { get; set; } = new();
///
/// Type of connection mode that is preferred by the client. Server can still override.
///
public ConnectionMode? ModePreference { get; set; }
///
/// Prefer connecting to agent over a public IP even if a more optimal route is available. Server can still override.
/// This is useful to avoid sending traffic over VPN tunnels.
///
public bool? PreferPublicIp { get; set; }
///
/// Encryption mode to request. Server can still override.
///
public Encryption? Encryption { get; set; }
///
/// Maximum duration (in milliseconds) that communication can be inactive between
/// the compute client and task-running agent before the connection is terminated.
///
public int? InactivityTimeoutMs { get; set; }
}
///
/// Response to a cluster lookup request
///
public class GetClusterResponse
{
///
/// Compute cluster ID
///
public ClusterId ClusterId { get; set; }
}
///
/// Response to compute allocation request
///
public class AssignComputeResponse
{
///
/// IP address of the remote agent machine running the compute task
///
public string Ip { get; set; } = String.Empty;
///
/// How to establish a connection to the remote machine
///
public ConnectionMode ConnectionMode { get; set; }
///
/// An optional address (host:port) to use when connecting to agent via tunnel or relay mode
///
public string? ConnectionAddress { get; set; }
///
/// Port number on the remote machine
///
public int Port { get; set; }
///
/// Assigned ports (externally visible port -> local port on agent)
/// Key is an arbitrary name identifying the port (same as was given in )
/// When relay mode is used, ports can mapped to a different externally visible port.
/// If compute task uses and listens to port 7000, that port can be externally represented as something else.
/// For example, port 32743 can be pointed to port 7000.
/// This makes no difference for the compute task process, but the client/initiator making connections must
/// pay attention to this mapping.
///
public IReadOnlyDictionary Ports { get; set; } = new Dictionary();
///
/// Encryption used
///
public Encryption Encryption { get; set; } = Encryption.None;
///
/// Cryptographic nonce to identify the request, as a hex string
///
public string Nonce { get; set; } = String.Empty;
///
/// AES key for the channel, as a hex string
///
public string Key { get; set; } = String.Empty;
///
/// X.509 certificate used for SSL/TLS encryption
///
public string Certificate { get; set; } = String.Empty;
///
/// Which cluster this remote machine belongs to
///
public ClusterId ClusterId { get; set; }
///
/// Identifier for the remote machine
///
public AgentId AgentId { get; set; }
///
/// Agent version for the remote machine
///
public string? AgentVersion { get; set; }
///
/// Identifier for the new lease on the remote machine
///
public LeaseId LeaseId { get; set; }
///
/// Resources assigned to this machine
///
public Dictionary AssignedResources { get; set; } = new Dictionary();
///
/// Version number for the compute protocol
///
public int Protocol { get; set; }
///
/// Properties of the agent assigned to do the work
///
public IReadOnlyList Properties { get; set; } = new List();
}
///
/// Describe how to connect to the remote machine
///
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum ConnectionMode
{
///
/// Connection is established directly to remote machine, behaving like a normal TCP/UDP connection
///
Direct,
///
/// Connection is tunneled through Horde server.
/// When connecting, initiator must send a tunnel handshake request indicating which machine/IP to tunnel to.
/// Once handshake is complete, TCP connection behaves as normal (UDP not supported)
///
Tunnel,
///
/// Connection is established to remote machine via a relay.
/// Forwarding is transparent and behaves like a normal TCP/UDP connection.
///
Relay
}
///
/// Describe encryption for the compute resource connection
///
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum Encryption
{
///
/// No encryption enabled
///
None,
///
/// Use custom AES-based encryption transport
///
Aes,
///
/// Use SSL/TLS encryption with RSA 2048-bits
///
Ssl,
///
/// Use SSL/TLS encryption with ECDSA P-256
///
SslEcdsaP256
}
///
/// Resource needs declaration request
///
public class ResourceNeedsMessage
{
///
/// Unique session ID performing compute resource requests
///
public string SessionId { get; set; } = String.Empty;
///
/// Pool of agents requesting resources from
///
public string Pool { get; set; } = String.Empty;
///
/// Key/value of resources needed by session (such as CPU or memory, see KnownPropertyNames in Horde.Server)
///
public Dictionary ResourceNeeds { get; set; } = new();
}
///
/// Resource needs response
///
public class GetResourceNeedsResponse
{
///
/// List of resource needs
///
public List ResourceNeeds { get; set; } = new();
}
}