// Copyright Epic Games, Inc. All Rights Reserved. using EpicGames.Core; using HordeServer.Services; using HordeServer.Utilities; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Runtime.InteropServices; using System.Threading.Tasks; using TimeZoneConverter; namespace HordeServer { /// /// Types of storage to use for log data /// public enum StorageProviderType { /// /// AWS S3 /// S3, /// /// Local filesystem /// FileSystem, /// /// In-memory only (for testing) /// Transient, /// /// Relay to another server (useful for testing against prod) /// Relay, }; /// /// Specifies the service to use for controlling the size of the fleet /// public enum FleetManagerType { /// /// Default (empty) instance /// None, /// /// Use AWS EC2 instances /// Aws, } /// /// Settings for a remote execution instance /// /// Instances are a concept from Google's remote execution API that Horde implements. /// They are used for separating different type of executions into groups. /// public class RemoteExecInstanceSettings { /// /// gRPC URL for the content-addressable storage to be used for this instance /// [Required] public Uri? CasUrl { get; set; } /// /// gRPC URL for the action cache to be used for this instance /// [Required] public Uri? ActionCacheUrl { get; set; } /// /// Service account token for accessing the gRPC URLs /// [Required] public string? ServiceAccountToken { get; set; } /// /// List of pool IDs this instance can be scheduled in /// public List? PoolFilter { get; set; } /// /// Max time to wait for an agent to become available for serving a remote execution request (in milliseconds) /// public int AgentQueueTimeout { get; set; } = 5000; internal TimeSpan AgentQueueTimeoutAsTimeSpan() { return TimeSpan.FromMilliseconds(AgentQueueTimeout); } } /// /// Settings for remote execution /// public class RemoteExecSettings { /// /// Mapping instance names to instance settings /// public Dictionary Instances { get; set; } = new Dictionary(); /// /// Max number of concurrent leases per agent /// public int MaxConcurrentLeasesPerAgent { get; set; } = 2; } /// /// Global settings for the application /// public class ServerSettings { /// /// MongoDB connection string /// public string? DatabaseConnectionString { get; set; } /// /// MongoDB database name /// public string DatabaseName { get; set; } = "Horde"; /// /// The claim type for administrators /// public string AdminClaimType { get; set; } = HordeClaimTypes.InternalRole; /// /// Value of the claim type for administrators /// public string AdminClaimValue { get; set; } = "admin"; /// /// Optional certificate to trust in order to access the database (eg. AWS public cert for TLS) /// public string? DatabasePublicCert { get; set; } /// /// Access the database in read-only mode (avoids creating indices or updating content) /// Useful for debugging a local instance of HordeServer against a production database. /// public bool DatabaseReadOnlyMode { get; set; } = false; /// /// Optional PFX certificate to use for encryting agent SSL traffic. This can be a self-signed certificate, as long as it's trusted by agents. /// public string? ServerPrivateCert { get; set; } /// /// Issuer for tokens from the auth provider /// public string? OidcAuthority { get; set; } /// /// Client id for the OIDC authority /// public string? OidcClientId { get; set; } /// /// Optional redirect url provided to OIDC login /// public string? OidcSigninRedirect { get; set; } /// /// Name of the issuer in bearer tokens from the server /// public string? JwtIssuer { get; set; } = null!; /// /// Secret key used to sign JWTs. This setting is typically only used for development. In prod, a unique secret key will be generated and stored in the DB for each unique server instance. /// public string? JwtSecret { get; set; } = null!; /// /// Length of time before JWT tokens expire, in hourse /// public int JwtExpiryTimeHours { get; set; } = 4; /// /// Disable authentication for debugging purposes /// public bool DisableAuth { get; set; } /// /// Whether to enable Cors, generally for development purposes /// public bool CorsEnabled { get; set; } = false; /// /// Allowed Cors origin /// public string CorsOrigin { get; set; } = null!; /// /// Whether to enable a schedule in test data (false by default for development builds) /// public bool EnableScheduleInTestData { get; set; } /// /// Interval between rebuilding the schedule queue with a DB query. /// public TimeSpan SchedulePollingInterval { get; set; } = TimeSpan.FromSeconds(60.0); /// /// Interval between polling for new jobs /// public TimeSpan NoResourceBackOffTime { get; set; } = TimeSpan.FromSeconds(30.0); /// /// Interval between attempting to assign agents to take on jobs /// public TimeSpan InitiateJobBackOffTime { get; set; } = TimeSpan.FromSeconds(180.0); /// /// Interval between scheduling jobs when an unknown error occurs /// public TimeSpan UnknownErrorBackOffTime { get; set; } = TimeSpan.FromSeconds(120.0); /// /// Config for connecting to Redis server(s). /// Setting it to null will disable Redis use and connection /// See format at https://stackexchange.github.io/StackExchange.Redis/Configuration.html /// public string? RedisConnectionConfig { get; set; } /// /// Type of write cache to use in log service /// Currently Supported: "InMemory" or "Redis" /// public string LogServiceWriteCacheType { get; set; } = "InMemory"; /// /// Provider Type /// Currently Supported: "S3" or "FileSystem" /// public StorageProviderType ExternalStorageProviderType { get; set; } = StorageProviderType.FileSystem; /// /// Local log/artifact storage directory, if using type filesystem /// public string LocalLogsDir { get; set; } = "Logs"; /// /// Gets the full path referred to by LocalStorageDir /// public DirectoryReference LocalLogsDirRef => DirectoryReference.Combine(Program.DataDir, LocalLogsDir); /// /// Local blob storage directory, if using type filesystem /// public string LocalBlobsDir { get; set; } = "Blobs"; /// /// Gets the full path referred to by LocalStorageDir /// public DirectoryReference LocalBlobsDirRef => DirectoryReference.Combine(Program.DataDir, LocalBlobsDir); /// /// Local artifact storage directory, if using type filesystem /// public string LocalArtifactsDir { get; set; } = "Artifacts"; /// /// Gets the full path referred to by LocalStorageDir /// public DirectoryReference LocalArtifactsDirRef => DirectoryReference.Combine(Program.DataDir, LocalArtifactsDir); /// /// S3 bucket region for logfile storage /// public string S3BucketRegion { get; set; } = null!; /// /// Arn to assume for s3. "Basic", "AssumeRole", "AssumeRoleWebIdentity" only /// public string S3CredentialType { get; set; } = null!; /// /// S3 Client username (used in Basic auth type only) /// public string S3ClientKeyId { get; set; } = null!; /// /// S3 client password (used in Basic auth type only) /// public string S3ClientSecret { get; set; } = null!; /// /// Arn to assume for s3 /// public string S3AssumeArn { get; set; } = null!; /// /// S3 log bucket name /// public string S3LogBucketName { get; set; } = null!; /// /// S3 artifact bucket name /// public string S3ArtifactBucketName { get; set; } = null!; /// /// When using a relay storage provider, specifies the remote server to use /// public string? LogRelayServer { get; set; } /// /// Authentication token for using a relay server /// public string? LogRelayBearerToken { get; set; } /// /// Whether to log json to stdout /// public bool LogJsonToStdOut { get; set; } = false; /// /// Which fleet manager service to use /// public FleetManagerType FleetManager { get; set; } = FleetManagerType.None; /// /// Whether to run scheduled jobs. Not wanted for development. /// public bool DisableSchedules { get; set; } = true; /// /// Timezone for evaluating schedules /// public string? ScheduleTimeZone { get; set; } /// /// Address of the Perforce bridge /// public string? PerforceBridge { get; set; } /// /// Token for interacting with Slack /// public string? SlackToken { get; set; } /// /// Token for opening a socket to slack /// public string? SlackSocketToken { get; set; } /// /// Channel to send stream notification update failures to /// public string? UpdateStreamsNotificationChannel { get; set; } /// /// URI to the SmtpServer to use for sending email notifications /// public string? SmtpServer { get; set; } /// /// The email address to send email notifications from /// public string? EmailSenderAddress { get; set; } /// /// The name for the sender when sending email notifications /// public string? EmailSenderName { get; set; } /// /// The URl to use for generating links back to the dashboard. /// public Uri DashboardUrl { get; set; } = new Uri("https://localhost:3000"); /// /// The URL to Helix Swarm server /// public Uri? HelixSwarmServerUrl { get; set; } /// /// Username to use when communicating with Helix Swarm /// public string? HelixSwarmUsername { get; set; } /// /// Password to use when communicating with Helix Swarm /// public string? HelixSwarmPassword { get; set; } /// /// Projects in Helix Swarm to monitor, including the state of reviews in each project /// Multiple projects can be specified, separated by comma. /// public string? HelixSwarmProjects { get; set; } /// /// The p4 bridge server /// public string? P4BridgeServer { get; set; } /// /// The p4 bridge service username /// public string? P4BridgeServiceUsername { get; set; } /// /// The p4 bridge service password /// public string? P4BridgeServicePassword { get; set; } /// /// Whether the p4 bridge service account can impersonate other users /// public bool P4BridgeCanImpersonate { get; set; } = false; /// /// Whether to use the (temporary) P4 router while transitioning to p4 api /// public bool P4UseRouter { get; set; } = false; /// /// Set the minimum size of the global thread pool /// This value has been found in need of tweaking to avoid timeouts with the Redis client during bursts /// of traffic. Default is 16 for .NET Core CLR. The correct value is dependent on the traffic the Horde Server /// is receiving. For Epic's internal deployment, this is set to 40. /// public int? GlobalThreadPoolMinSize { get; set; } /// /// Path to the root config file /// public string? ConfigPath { get; set; } /// /// Settings for remote execution /// public RemoteExecSettings RemoteExecSettings { get; set; } = new RemoteExecSettings(); /// /// Lazily computed timezone value /// public TimeZoneInfo TimeZoneInfo { get { if (CachedTimeZoneInfo == null) { CachedTimeZoneInfo = (ScheduleTimeZone == null) ? TimeZoneInfo.Local : TZConvert.GetTimeZoneInfo(ScheduleTimeZone); } return CachedTimeZoneInfo; } } private TimeZoneInfo? CachedTimeZoneInfo; /// /// Whether to open a browser on startup /// public bool OpenBrowser { get; set; } = false; /// /// Check if Helix Swarm is enabled /// /// True if enabled public bool IsSwarmEnabled() { return HelixSwarmServerUrl != null; } /// /// Parse the comma-separated string of Swarm projects into an actual array /// /// Array of Swarm projects public string[] GetSwarmProjects() { return HelixSwarmProjects != null ? HelixSwarmProjects.Split(",") : Array.Empty(); } } }