// 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();
}
}
}