Merge pull request #1034 from tgstation/DocsAndStuff

Docs and stuff
This commit is contained in:
Jordan Brown
2020-06-01 16:14:08 -04:00
committed by GitHub
19 changed files with 129 additions and 10 deletions
@@ -8,6 +8,7 @@ using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Api;
using Tgstation.Server.Api.Models;
using Tgstation.Server.Host.Components.Events;
using Tgstation.Server.Host.Core;
using Tgstation.Server.Host.IO;
using Tgstation.Server.Host.Jobs;
@@ -12,6 +12,7 @@ using Tgstation.Server.Api.Models;
using Tgstation.Server.Api.Models.Internal;
using Tgstation.Server.Host.Components.Byond;
using Tgstation.Server.Host.Components.Chat;
using Tgstation.Server.Host.Components.Events;
using Tgstation.Server.Host.Components.Repository;
using Tgstation.Server.Host.Components.Session;
using Tgstation.Server.Host.Core;
@@ -5,7 +5,7 @@ using System.Threading.Tasks;
using Tgstation.Server.Host.Components.StaticFiles;
using Tgstation.Server.Host.Components.Watchdog;
namespace Tgstation.Server.Host.Components
namespace Tgstation.Server.Host.Components.Events
{
/// <inheritdoc />
sealed class EventConsumer : IEventConsumer
@@ -0,0 +1,25 @@
using System;
namespace Tgstation.Server.Host.Components.Events
{
/// <summary>
/// Attribute for indicating the script that a given <see cref="EventType"/> runs.
/// </summary>
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
sealed class EventScriptAttribute : Attribute
{
/// <summary>
/// The name of the script the event script the <see cref="EventType"/> runs.
/// </summary>
public string ScriptName { get; }
/// <summary>
/// Initializes a new instance of the <see cref="EventScriptAttribute"/> <see langword="class"/>.
/// </summary>
/// <param name="scriptName">The value of <see cref="ScriptName"/>.</param>
public EventScriptAttribute(string scriptName)
{
ScriptName = scriptName ?? throw new ArgumentNullException(nameof(scriptName));
}
}
}
@@ -1,4 +1,4 @@
namespace Tgstation.Server.Host.Components
namespace Tgstation.Server.Host.Components.Events
{
/// <summary>
/// Types of events. Mirror in tgs.dm
@@ -8,86 +8,103 @@
/// <summary>
/// Parameters: Reference name, commit sha
/// </summary>
[EventScript("RepoResetOrigin")]
RepoResetOrigin,
/// <summary>
/// Parameters: Checkout target
/// </summary>
[EventScript("RepoCheckout")]
RepoCheckout,
/// <summary>
/// No parameters
/// </summary>
[EventScript("RepoFetch")]
RepoFetch,
/// <summary>
/// Parameters: Pull request number, pull request sha, merger message
/// </summary>
[EventScript("RepoMergePullRequest")]
RepoMergePullRequest,
/// <summary>
/// Parameters: Absolute path to repository root
/// </summary>
[EventScript("PreSynchronize")]
RepoPreSynchronize,
/// <summary>
/// Parameters: Version being installed
/// </summary>
[EventScript("ByondInstallStart")]
ByondInstallStart,
/// <summary>
/// Parameters: Error string
/// </summary>
[EventScript("ByondInstallFail")]
ByondInstallFail,
/// <summary>
/// Parameters: Old active version, new active version
/// </summary>
[EventScript("ByondActiveVersionChange")]
ByondActiveVersionChange,
/// <summary>
/// Parameters: Game directory path, origin commit sha
/// </summary>
[EventScript("PreCompile")]
CompileStart,
/// <summary>
/// No parameters
/// </summary>
[EventScript("CompileCancelled")]
CompileCancelled,
/// <summary>
/// Parameters: Game directory path, "1" if compile succeeded and api validation failed, "0" otherwise
/// </summary>
[EventScript("CompileFailure")]
CompileFailure,
/// <summary>
/// Parameters: Game directory path
/// </summary>
[EventScript("PostCompile")]
CompileComplete,
/// <summary>
/// No parameters
/// </summary>
[EventScript("InstanceAutoUpdateStart")]
InstanceAutoUpdateStart,
/// <summary>
/// Parameters: Base sha, target sha, base reference, target reference
/// </summary>
[EventScript("RepoMergeConflict")]
RepoMergeConflict,
/// <summary>
/// No parameters
/// </summary>
[EventScript("DeploymentComplete")]
DeploymentComplete,
/// <summary>
/// Before the watchdog shutsdown. Not sent for graceful shutdowns. No parameters.
/// </summary>
[EventScript("WatchdogShutdown")]
WatchdogShutdown,
/// <summary>
/// Before the watchdog detaches. No parameters.
/// </summary>
[EventScript("WatchdogDetach")]
WatchdogDetach,
}
}
@@ -2,7 +2,7 @@
using System.Threading;
using System.Threading.Tasks;
namespace Tgstation.Server.Host.Components
namespace Tgstation.Server.Host.Components.Events
{
/// <summary>
/// Consumes <see cref="EventType"/>s and takes the appropriate actions
@@ -0,0 +1,8 @@
# Event System
Many actions in TGS generate events. They are dispatched via the [IEventConsumer](./IEventConsumer.cs) ([main implementation](./EventConsumer.cs)). From there, it is further sent to the two actual consumers.
1. The static files system will attempt to run a script with the given [EventScriptAttribute](./EventScriptAttribute)'s `ScriptName`.
1. The watchdog will send a mirror of the event via the DMAPI to be handled by game code.
With these two endpoints, the behaviour of TGS is highly customizable as server operators can customize the entire process.
@@ -9,6 +9,7 @@ using Tgstation.Server.Api.Rights;
using Tgstation.Server.Host.Components.Byond;
using Tgstation.Server.Host.Components.Chat;
using Tgstation.Server.Host.Components.Deployment;
using Tgstation.Server.Host.Components.Events;
using Tgstation.Server.Host.Components.Repository;
using Tgstation.Server.Host.Components.Watchdog;
using Tgstation.Server.Host.Database;
@@ -7,6 +7,7 @@ using Tgstation.Server.Host.Components.Byond;
using Tgstation.Server.Host.Components.Chat;
using Tgstation.Server.Host.Components.Chat.Commands;
using Tgstation.Server.Host.Components.Deployment;
using Tgstation.Server.Host.Components.Events;
using Tgstation.Server.Host.Components.Interop.Bridge;
using Tgstation.Server.Host.Components.Repository;
using Tgstation.Server.Host.Components.Session;
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using Tgstation.Server.Host.Components.Events;
namespace Tgstation.Server.Host.Components.Interop.Topic
{
@@ -10,6 +10,7 @@ Component code is where the magic and tears of TGS are made. There are six main
There exist two more namespaces in here that don't directly fit in these 6 components.
- [Events](./Events) deals with the TGS event system.
- [Interop](./Interop) deals with the bulk of DMAPI communication (Though it's not all contained here).
- [Session](./Session) contains the classes used for actually executing DreamDaemon, sending topic requests, receiving bridge requests, among other things.
@@ -19,4 +20,6 @@ While the database represents stored instance data, in component code an instanc
`IInstance`s are created via the [IInstanceFactory](./IInstanceFactory.cs) ([implementation](./InstanceFactory.cs)) and are generally controlled via the [IInstanceManager](./IInstanceManager.cs) ([implementation](./InstanceManager.cs)).
Many classes in here implement [IHostedService](), `InstanceManager` being the only one that is called by the ASP.NET runtime. In the case of instances `StartAsync()` is called when an `Instance` is being brought online (from server startup or user request). The `Instance` handles calling `StartAsync()` on its various subcomponents that need it. When an `Instance` is being brought offline (from server shutdown/restart/update or user request) the same pattern is followed calling `StopAsync()`.
Many classes in here implement [IHostedService](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-3.1&tabs=visual-studio), `InstanceManager` being the only one that is called by the ASP.NET runtime. In the case of instances `StartAsync()` is called when an `Instance` is being brought online (from server startup or user request). The `Instance` handles calling `StartAsync()` on its various subcomponents that need it. When an `Instance` is being brought offline (from server shutdown/restart/update or user request) the same pattern is followed calling `StopAsync()`.
`IInstanceManager` is the sole point where the controllers talk to component code. It also dispatches bridge requests to their relevant instances.
@@ -8,6 +8,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Api.Models;
using Tgstation.Server.Host.Components.Events;
using Tgstation.Server.Host.IO;
using Tgstation.Server.Host.Jobs;
@@ -4,6 +4,7 @@ using System;
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Api.Models;
using Tgstation.Server.Host.Components.Events;
using Tgstation.Server.Host.Core;
using Tgstation.Server.Host.IO;
using Tgstation.Server.Host.Jobs;
@@ -9,6 +9,7 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Api.Models;
using Tgstation.Server.Host.Components.Events;
using Tgstation.Server.Host.Core;
using Tgstation.Server.Host.IO;
using Tgstation.Server.Host.Jobs;
@@ -32,12 +33,18 @@ namespace Tgstation.Server.Host.Components.StaticFiles
const string CodeModificationsHeadFile = "HeadInclude.dm";
const string CodeModificationsTailFile = "TailInclude.dm";
static readonly IReadOnlyDictionary<EventType, string> EventTypeScriptFileNameMap = new Dictionary<EventType, string>
{
{ EventType.CompileStart, "PreCompile" },
{ EventType.CompileComplete, "PostCompile" },
{ EventType.RepoPreSynchronize, "PreSynchronize" }
};
static readonly IReadOnlyDictionary<EventType, string> EventTypeScriptFileNameMap = new Dictionary<EventType, string>(
Enum.GetValues(typeof(EventType))
.OfType<EventType>()
.Select(
eventType => new KeyValuePair<EventType, string>(
eventType,
typeof(EventType)
.GetField(eventType.ToString())
.GetCustomAttributes(false)
.OfType<EventScriptAttribute>()
.First()
.ScriptName)));
/// <summary>
/// The <see cref="IIOManager"/> for <see cref="Configuration"/>
@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Api.Models;
using Tgstation.Server.Host.Components.Events;
using Tgstation.Server.Host.Security;
namespace Tgstation.Server.Host.Components.StaticFiles
@@ -3,6 +3,7 @@ using System;
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Api.Models.Internal;
using Tgstation.Server.Host.Components.Events;
using Tgstation.Server.Host.Components.Session;
namespace Tgstation.Server.Host.Components.Watchdog
@@ -11,6 +11,7 @@ using Tgstation.Server.Api.Models.Internal;
using Tgstation.Server.Api.Rights;
using Tgstation.Server.Host.Components.Chat;
using Tgstation.Server.Host.Components.Deployment;
using Tgstation.Server.Host.Components.Events;
using Tgstation.Server.Host.Components.Interop.Topic;
using Tgstation.Server.Host.Components.Session;
using Tgstation.Server.Host.Core;
@@ -0,0 +1,21 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
namespace Tgstation.Server.Host.Components.Events.Tests
{
/// <summary>
/// Tests for the <see cref="EventScriptAttribute"/> <see langword="class"/>.
/// </summary>
[TestClass]
public sealed class TestEventScriptAttribute
{
[TestMethod]
public void TestConstruction()
{
Assert.ThrowsException<ArgumentNullException>(() => new EventScriptAttribute(null));
var test = new EventScriptAttribute("test");
Assert.AreEqual("test", test.ScriptName);
}
}
}
@@ -0,0 +1,28 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Tgstation.Server.Host.Components.Events.Tests
{
[TestClass]
public sealed class TestEventType
{
[TestMethod]
public void TestAllEventTypesHaveUniqueEventScriptAttributes()
{
var allScripts = new HashSet<string>();
foreach (var eventType in Enum.GetValues(typeof(EventType))) {
var list = typeof(EventType)
.GetField(eventType.ToString())
.GetCustomAttributes(false)
.OfType<EventScriptAttribute>()
.ToList();
Assert.AreEqual(1, list.Count, $"EventType: {eventType}");
var attribute = list.First();
Assert.IsTrue(allScripts.Add(attribute.ScriptName), $"Non-unique script Name: {attribute.ScriptName}");
}
}
}
}