Files
UnrealEngineUWP/Engine/Source/Programs/Horde/HordeServer/Models/Globals.cs
Ben Marsh a62e966f19 Horde: Track conform count for each server separately.
[CL 16762809 by Ben Marsh in ue5-main branch]
2021-06-23 17:22:20 -04:00

391 lines
9.0 KiB
C#

// Copyright Epic Games, Inc. All Rights Reserved.
using Google.Protobuf.WellKnownTypes;
using HordeServer.Services;
using HordeServer.Utilities;
using Microsoft.IdentityModel.Tokens;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Linq;
using System.Security.Cryptography;
using System.Threading.Tasks;
namespace HordeServer.Models
{
/// <summary>
/// Base class for singleton documents
/// </summary>
public abstract class SingletonBase
{
/// <summary>
/// Unique id for this singleton
/// </summary>
[BsonId]
public ObjectId Id { get; set; }
/// <summary>
/// The revision index of this document
/// </summary>
public int Revision { get; set; }
/// <summary>
/// Callback to allow the singleton to fix up itself after being read
/// </summary>
public virtual void PostLoad()
{
}
}
/// <summary>
/// How frequently the maintence window repeats
/// </summary>
public enum ScheduledDowntimeFrequency
{
/// <summary>
/// Once
/// </summary>
Once,
/// <summary>
/// Every day
/// </summary>
Daily,
/// <summary>
/// Every week
/// </summary>
Weekly,
}
/// <summary>
/// Settings for the maintenance window
/// </summary>
public class ScheduledDowntime
{
/// <summary>
/// Start time
/// </summary>
public DateTimeOffset StartTime { get; set; }
/// <summary>
/// Finish time
/// </summary>
public DateTimeOffset FinishTime { get; set; }
/// <summary>
/// Frequency that the window repeats
/// </summary>
public ScheduledDowntimeFrequency Frequency { get; set; } = ScheduledDowntimeFrequency.Once;
/// <summary>
/// Gets the next scheduled downtime
/// </summary>
/// <param name="Now">The current time</param>
/// <returns>Start and finish time</returns>
public (DateTimeOffset StartTime, DateTimeOffset FinishTime) GetNext(DateTimeOffset Now)
{
TimeSpan Offset = TimeSpan.Zero;
if (Frequency == ScheduledDowntimeFrequency.Daily)
{
double Days = (Now - StartTime).TotalDays;
if (Days >= 1.0)
{
Days -= Days % 1.0;
}
Offset = TimeSpan.FromDays(Days);
}
else if (Frequency == ScheduledDowntimeFrequency.Weekly)
{
double Days = (Now - StartTime).TotalDays;
if (Days >= 7.0)
{
Days -= Days % 7.0;
}
Offset = TimeSpan.FromDays(Days);
}
return (StartTime + Offset, FinishTime + Offset);
}
/// <summary>
/// Determines if this schedule is active
/// </summary>
/// <param name="Now">The current time</param>
/// <returns>True if downtime is active</returns>
public bool IsActive(DateTimeOffset Now)
{
if (Frequency == ScheduledDowntimeFrequency.Once)
{
return Now >= StartTime && Now < FinishTime;
}
else if (Frequency == ScheduledDowntimeFrequency.Daily)
{
double Days = (Now - StartTime).TotalDays;
if (Days < 0.0)
{
return false;
}
else
{
return (Days % 1.0) < (FinishTime - StartTime).TotalDays;
}
}
else if (Frequency == ScheduledDowntimeFrequency.Weekly)
{
double Days = (Now - StartTime).TotalDays;
if(Days < 0.0)
{
return false;
}
else
{
return (Days % 7.0) < (FinishTime - StartTime).TotalDays;
}
}
else
{
return false;
}
}
}
/// <summary>
/// User notice
/// </summary>
public class Notice
{
/// <summary>
/// Unique id for this notice
/// </summary>
public string Id { get; set; } = String.Empty;
/// <summary>
/// Start time to display this message
/// </summary>
public DateTime? StartTime { get; set; }
/// <summary>
/// Finish time to display this message
/// </summary>
public DateTime? FinishTime { get; set; }
/// <summary>
/// Message to display
/// </summary>
public string Message { get; set; } = String.Empty;
}
/// <summary>
/// Path to a platform and stream to use for syncing AutoSDK
/// </summary>
public class AutoSdkWorkspace
{
/// <summary>
/// Name of this workspace
/// </summary>
public string? Name { get; set; }
/// <summary>
/// The agent properties to check (eg. "OSFamily=Windows")
/// </summary>
public List<string> Properties { get; set; } = new List<string>();
/// <summary>
/// Username for logging in to the server
/// </summary>
public string? UserName { get; set; }
/// <summary>
/// Stream to use
/// </summary>
[Required]
public string? Stream { get; set; }
}
/// <summary>
/// Information about an individual Perforce server
/// </summary>
public class PerforceServer
{
/// <summary>
/// The server and port. The server may be a DNS entry with multiple records, in which case it will be actively load balanced.
/// </summary>
public string ServerAndPort { get; set; } = "perforce:1666";
/// <summary>
/// Whether to query the healthcheck address under each server
/// </summary>
public bool HealthCheck { get; set; }
/// <summary>
/// Whether to resolve the DNS entries and load balance between different hosts
/// </summary>
public bool ResolveDns { get; set; }
/// <summary>
/// Maximum number of simultaneous conforms on this server
/// </summary>
public int MaxConformCount { get; set; }
/// <summary>
/// List of properties for an agent to be eligable to use this server
/// </summary>
public List<string>? Properties { get; set; }
}
/// <summary>
/// Credentials for a Perforce user
/// </summary>
public class PerforceCredentials
{
/// <summary>
/// The username
/// </summary>
public string UserName { get; set; } = String.Empty;
/// <summary>
/// Password for the user
/// </summary>
public string Password { get; set; } = String.Empty;
}
/// <summary>
/// Information about a cluster of Perforce servers.
/// </summary>
public class PerforceCluster
{
/// <summary>
/// The default cluster name
/// </summary>
public const string DefaultName = "Default";
/// <summary>
/// Name of the cluster
/// </summary>
[Required]
public string Name { get; set; } = null!;
/// <summary>
/// List of servers
/// </summary>
public List<PerforceServer> Servers { get; set; } = new List<PerforceServer>();
/// <summary>
/// List of server credentials
/// </summary>
public List<PerforceCredentials> Credentials { get; set; } = new List<PerforceCredentials>();
/// <summary>
/// List of autosdk streams
/// </summary>
public List<AutoSdkWorkspace> AutoSdk { get; set; } = new List<AutoSdkWorkspace>();
}
/// <summary>
/// Global server settings
/// </summary>
[SingletonDocument("5e3981cb28b8ec59cd07184a")]
public class Globals : SingletonBase
{
/// <summary>
/// The unique id for all globals objects
/// </summary>
public static ObjectId StaticId { get; } = new ObjectId("5e3981cb28b8ec59cd07184a");
/// <summary>
/// Unique instance id of this database
/// </summary>
public ObjectId InstanceId { get; set; }
/// <summary>
/// Set to true to drop the database on restart.
/// </summary>
public bool ForceReset { get; set; }
/// <summary>
/// The config revision
/// </summary>
public string? ConfigRevision { get; set; }
/// <summary>
/// Manually added status messages
/// </summary>
public List<Notice> Notices { get; set; } = new List<Notice>();
/// <summary>
/// List of Perforce clusters
/// </summary>
public List<PerforceCluster> PerforceClusters { get; set; } = new List<PerforceCluster>();
/// <summary>
/// Maximum number of simultaneous conforms
/// </summary>
public int MaxConformCount { get; set; }
/// <summary>
/// Randomly generated JWT signing key
/// </summary>
public byte[]? JwtSigningKey { get; set; }
/// <summary>
/// The current schema version
/// </summary>
public int? SchemaVersion { get; set; }
/// <summary>
/// The scheduled downtime
/// </summary>
public List<ScheduledDowntime> ScheduledDowntime { get; set; } = new List<ScheduledDowntime>();
/// <summary>
/// Private constructor for serialization
/// </summary>
public Globals()
{
InstanceId = ObjectId.GenerateNewId();
}
/// <summary>
/// Rotate the signing key
/// </summary>
public void RotateSigningKey()
{
using (RNGCryptoServiceProvider Generator = new RNGCryptoServiceProvider())
{
JwtSigningKey = new byte[128];
Generator.GetBytes(JwtSigningKey);
}
}
/// <summary>
/// Finds a perforce cluster with the given name
/// </summary>
/// <param name="Name">Name of the cluster</param>
/// <returns></returns>
public PerforceCluster? FindPerforceCluster(string? Name)
{
if (Name == null)
{
return PerforceClusters.FirstOrDefault();
}
else
{
return PerforceClusters.FirstOrDefault(x => String.Equals(x.Name, Name, StringComparison.OrdinalIgnoreCase));
}
}
/// <inheritdoc/>
public override void PostLoad()
{
base.PostLoad();
DateTimeOffset Now = DateTimeOffset.Now;
Notices.RemoveAll(x => x.FinishTime != null && x.FinishTime < Now);
ScheduledDowntime.RemoveAll(x => x.FinishTime < Now);
}
}
}