From bc90c13322be5ef3bd30a2617f55f87bb3830dd9 Mon Sep 17 00:00:00 2001 From: Cyberboss Date: Mon, 28 Dec 2020 11:00:10 -0500 Subject: [PATCH] Adds swarm integration test --- .../Tgstation.Server.Tests/IntegrationTest.cs | 180 +++++++++++++++++- tests/Tgstation.Server.Tests/TestingServer.cs | 19 +- 2 files changed, 190 insertions(+), 9 deletions(-) diff --git a/tests/Tgstation.Server.Tests/IntegrationTest.cs b/tests/Tgstation.Server.Tests/IntegrationTest.cs index 77fd0425a3..f2c960e0f5 100644 --- a/tests/Tgstation.Server.Tests/IntegrationTest.cs +++ b/tests/Tgstation.Server.Tests/IntegrationTest.cs @@ -43,7 +43,7 @@ namespace Tgstation.Server.Tests [TestMethod] public async Task TestUpdateProtocolAndDisabledOAuth() { - using var server = new TestingServer(false); + using var server = new TestingServer(null, false); using var serverCts = new CancellationTokenSource(); var cancellationToken = serverCts.Token; var serverTask = server.Run(cancellationToken); @@ -113,6 +113,178 @@ namespace Tgstation.Server.Tests Assert.IsTrue(server.RestartRequested, "Server not requesting restart!"); } + [TestMethod] + public async Task TestSwarm() + { + const string PrivateKey = "adlfj73ywifhks7iwrgfegjs"; + + var controllerAddress = new Uri("http://localhost:5011"); + using var controller = new TestingServer(new SwarmConfiguration + { + Address = controllerAddress, + Identifier = "controller", + PrivateKey = PrivateKey + }, false, 5011); + using var node1 = new TestingServer(new SwarmConfiguration + { + Address = new Uri("http://localhost:5012"), + ControllerAddress = controllerAddress, + Identifier = "node1", + PrivateKey = PrivateKey + }, false, 5012); + using var node2 = new TestingServer(new SwarmConfiguration + { + Address = new Uri("http://localhost:5013"), + ControllerAddress = controllerAddress, + Identifier = "node2", + PrivateKey = PrivateKey + }, false, 5013); + using var serverCts = new CancellationTokenSource(); + var cancellationToken = serverCts.Token; + var serverTask = Task.WhenAll( + node1.Run(cancellationToken), + node2.Run(cancellationToken), + controller.Run(cancellationToken)); + + try + { + using var controllerClient = await CreateAdminClient(controller.Url, cancellationToken); + using var node1Client = await CreateAdminClient(node1.Url, cancellationToken); + using var node2Client = await CreateAdminClient(node2.Url, cancellationToken); + + var controllerInfo = await controllerClient.ServerInformation(cancellationToken); + + async Task WaitForSwarmServerUpdate() + { + ServerInformation serverInformation; + do + { + await Task.Delay(TimeSpan.FromSeconds(10)); + serverInformation = await node1Client.ServerInformation(cancellationToken); + } + while (serverInformation.SwarmServers.Count == 1); + } + + static void CheckInfo(ServerInformation serverInformation) + { + Assert.IsNotNull(serverInformation.SwarmServers); + Assert.AreEqual(3, serverInformation.SwarmServers.Count); + + var node1 = serverInformation.SwarmServers.SingleOrDefault(x => x.Identifier == "node1"); + Assert.IsNotNull(node1); + Assert.AreEqual(node1.Address, "http://localhost:5012"); + Assert.IsFalse(node1.Controller); + + var node2 = serverInformation.SwarmServers.SingleOrDefault(x => x.Identifier == "node2"); + Assert.IsNotNull(node2); + Assert.AreEqual(node2.Address, "http://localhost:5013"); + Assert.IsFalse(node2.Controller); + + var controller = serverInformation.SwarmServers.SingleOrDefault(x => x.Identifier == "controller"); + Assert.IsNotNull(controller); + Assert.AreEqual(controller.Address, "http://localhost:5011"); + Assert.IsTrue(controller.Controller); + } + + CheckInfo(controllerInfo); + + // wait a few minutes for the updated server list to dispatch + await Task.WhenAny( + WaitForSwarmServerUpdate(), + Task.Delay(TimeSpan.FromMinutes(4))); + + var node2Info = await node2Client.ServerInformation(cancellationToken); + var node1Info = await node1Client.ServerInformation(cancellationToken); + CheckInfo(node1Info); + CheckInfo(node2Info); + + // check user info is shared + var newUser = await node2Client.Users.Create(new UserUpdate + { + Name = "asdf", + Password = "asdfasdfasdfasdf", + Enabled = true, + PermissionSet = new PermissionSet + { + AdministrationRights = AdministrationRights.ChangeVersion + } + }, cancellationToken); + + var node1User = await node1Client.Users.GetId(newUser, cancellationToken); + Assert.AreEqual(newUser.Name, node1User.Name); + Assert.AreEqual(newUser.Enabled, node1User.Enabled); + + using var controllerUserClient = await clientFactory.CreateFromLogin( + controllerAddress, + newUser.Name, + "asdfasdfasdfasdf"); + + using var node1BadClient = clientFactory.CreateFromToken(node1.Url, controllerUserClient.Token); + await Assert.ThrowsExceptionAsync(() => node1BadClient.Administration.Read(cancellationToken)); + + // check instance info is not shared + var controllerInstance = await controllerClient.Instances.CreateOrAttach( + new Api.Models.Instance + { + Name = "ControllerInstance", + Path = Path.Combine(controller.Directory, "ControllerInstance") + }, + cancellationToken); + + var node2Instance = await node2Client.Instances.CreateOrAttach( + new Api.Models.Instance + { + Name = "Node2Instance", + Path = Path.Combine(node2.Directory, "Node2Instance") + }, + cancellationToken); + var node2InstanceList = await node2Client.Instances.List(null, cancellationToken); + Assert.AreEqual(1, node2InstanceList.Count); + Assert.AreEqual(node2Instance.Id, node2InstanceList.First().Id); + Assert.IsNotNull(await node2Client.Instances.GetId(node2Instance, cancellationToken)); + var controllerInstanceList = await controllerClient.Instances.List(null, cancellationToken); + Assert.AreEqual(1, controllerInstanceList.Count); + Assert.AreEqual(controllerInstance.Id, controllerInstanceList.First().Id); + Assert.IsNotNull(await controllerClient.Instances.GetId(controllerInstance, cancellationToken)); + + await Assert.ThrowsExceptionAsync(() => controllerClient.Instances.GetId(node2Instance, cancellationToken)); + await Assert.ThrowsExceptionAsync(() => node1Client.Instances.GetId(controllerInstance, cancellationToken)); + + // test update + var testUpdateVersion = new Version(4, 6, 2); + await node1Client.Administration.Update( + new Administration + { + NewVersion = testUpdateVersion + }, + cancellationToken); + await Task.WhenAny(Task.Delay(TimeSpan.FromMinutes(2)), serverTask); + Assert.IsTrue(serverTask.IsCompleted); + + void CheckServerUpdated(TestingServer server) + { + Assert.IsTrue(Directory.Exists(server.UpdatePath), "Update directory not present!"); + + var updatedAssemblyPath = Path.Combine(server.UpdatePath, "Tgstation.Server.Host.dll"); + Assert.IsTrue(File.Exists(updatedAssemblyPath), "Updated assembly missing!"); + + var updatedAssemblyVersion = FileVersionInfo.GetVersionInfo(updatedAssemblyPath); + Assert.AreEqual(testUpdateVersion, Version.Parse(updatedAssemblyVersion.FileVersion).Semver()); + } + + CheckServerUpdated(controller); + CheckServerUpdated(node1); + CheckServerUpdated(node2); + } + finally + { + serverCts.Cancel(); + await serverTask; + } + + Directory.Delete(Path.GetDirectoryName(controller.Directory), true); + } + static void TerminateAllDDs() { foreach (var proc in System.Diagnostics.Process.GetProcessesByName("DreamDaemon")) @@ -122,7 +294,7 @@ namespace Tgstation.Server.Tests async Task CreateAdminClient(Uri url, CancellationToken cancellationToken) { - var giveUpAt = DateTimeOffset.UtcNow.AddSeconds(60); + var giveUpAt = DateTimeOffset.UtcNow.AddMinutes(2); for(var I = 1; ; ++I) { try @@ -311,7 +483,7 @@ namespace Tgstation.Server.Tests Assert.Inconclusive("Cannot run server test because DreamDaemon will not start headless while the BYOND pager is running!"); } - using var server = new TestingServer(true); + using var server = new TestingServer(null, true); const int MaximumTestMinutes = 20; using var hardTimeoutCancellationTokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(MaximumTestMinutes)); @@ -554,7 +726,7 @@ namespace Tgstation.Server.Tests [TestMethod] public async Task TestRepoParentLookup() { - using var testingServer = new TestingServer(false); + using var testingServer = new TestingServer(null, false); LibGit2Sharp.Repository.Clone("https://github.com/Cyberboss/test", testingServer.Directory); var libGit2Repo = new LibGit2Sharp.Repository(testingServer.Directory); using var repo = new Host.Components.Repository.Repository( diff --git a/tests/Tgstation.Server.Tests/TestingServer.cs b/tests/Tgstation.Server.Tests/TestingServer.cs index 6a0788af86..f9c888e341 100644 --- a/tests/Tgstation.Server.Tests/TestingServer.cs +++ b/tests/Tgstation.Server.Tests/TestingServer.cs @@ -32,13 +32,13 @@ namespace Tgstation.Server.Tests IServer realServer; - public TestingServer(bool enableOAuth) + public TestingServer(SwarmConfiguration swarmConfiguration, bool enableOAuth, ushort port = 5010) { Directory = Environment.GetEnvironmentVariable("TGS4_TEST_TEMP_DIRECTORY"); if (String.IsNullOrWhiteSpace(Directory)) { Directory = Path.Combine(Path.GetTempPath(), "TGS4_INTEGRATION_TEST"); - if (System.IO.Directory.Exists(Directory)) + if (System.IO.Directory.Exists(Directory) && swarmConfiguration == null) try { System.IO.Directory.Delete(Directory, true); @@ -49,8 +49,8 @@ namespace Tgstation.Server.Tests Directory = Path.Combine(Directory, Guid.NewGuid().ToString()); System.IO.Directory.CreateDirectory(Directory); - const string UrlString = "http://localhost:5010"; - Url = new Uri(UrlString); + string urlString = $"http://localhost:{port}"; + Url = new Uri(urlString); //so we need a db //we have to rely on env vars @@ -73,7 +73,7 @@ namespace Tgstation.Server.Tests var args = new List() { String.Format(CultureInfo.InvariantCulture, "Database:DropDatabase={0}", true), - String.Format(CultureInfo.InvariantCulture, "General:ApiPort={0}", 5010), + String.Format(CultureInfo.InvariantCulture, "General:ApiPort={0}", port), String.Format(CultureInfo.InvariantCulture, "Database:DatabaseType={0}", DatabaseType), String.Format(CultureInfo.InvariantCulture, "Database:ConnectionString={0}", connectionString), String.Format(CultureInfo.InvariantCulture, "General:SetupWizardMode={0}", SetupWizardMode.Never), @@ -87,6 +87,15 @@ namespace Tgstation.Server.Tests "General:ByondTopicTimeout=3000" }; + if (swarmConfiguration != null) + { + args.Add($"Swarm:PrivateKey={swarmConfiguration.PrivateKey}"); + args.Add($"Swarm:Identifier={swarmConfiguration.Identifier}"); + args.Add($"Swarm:Address={swarmConfiguration.Address}"); + if (swarmConfiguration.ControllerAddress != null) + args.Add($"Swarm:ControllerAddress={swarmConfiguration.ControllerAddress}"); + } + // enable all oauth providers if (enableOAuth) foreach (var I in Enum.GetValues(typeof(OAuthProvider)))