diff --git a/src/Tgstation.Server.Client.GraphQL/GQL/Mutations/RepositoryBasedServerUpdate.graphql b/src/Tgstation.Server.Client.GraphQL/GQL/Mutations/RepositoryBasedServerUpdate.graphql
new file mode 100644
index 0000000000..4f8a3bdbcc
--- /dev/null
+++ b/src/Tgstation.Server.Client.GraphQL/GQL/Mutations/RepositoryBasedServerUpdate.graphql
@@ -0,0 +1,11 @@
+mutation RepositoryBasedServerUpdate($targetVersion: Semver!) {
+ changeServerVersionViaTrackedRepository(input: { targetVersion: $targetVersion }) {
+ errors {
+ ... on ErrorMessageError {
+ additionalData
+ errorCode
+ message
+ }
+ }
+ }
+}
diff --git a/src/Tgstation.Server.Client.GraphQL/GQL/Mutations/RestartServer.graphql b/src/Tgstation.Server.Client.GraphQL/GQL/Mutations/RestartServer.graphql
new file mode 100644
index 0000000000..c9abfea9ef
--- /dev/null
+++ b/src/Tgstation.Server.Client.GraphQL/GQL/Mutations/RestartServer.graphql
@@ -0,0 +1,11 @@
+mutation RestartServer() {
+ restartServer() {
+ errors {
+ ... on ErrorMessageError {
+ additionalData
+ errorCode
+ message
+ }
+ }
+ }
+}
diff --git a/src/Tgstation.Server.Client.GraphQL/GQL/Queries/GetUpdateInformation.graphql b/src/Tgstation.Server.Client.GraphQL/GQL/Queries/GetUpdateInformation.graphql
new file mode 100644
index 0000000000..dfa3498216
--- /dev/null
+++ b/src/Tgstation.Server.Client.GraphQL/GQL/Queries/GetUpdateInformation.graphql
@@ -0,0 +1,10 @@
+query GetUpdateInformation($forceFresh: Boolean!) {
+ swarm {
+ updateInformation {
+ generatedAt
+ latestVersion(forceFresh: $forceFresh)
+ updateInProgress
+ trackedRepositoryUrl(forceFresh: $forceFresh)
+ }
+ }
+}
diff --git a/src/Tgstation.Server.Host/GraphQL/Mutations/AdministrationMutations.cs b/src/Tgstation.Server.Host/GraphQL/Mutations/AdministrationMutations.cs
new file mode 100644
index 0000000000..6214227d80
--- /dev/null
+++ b/src/Tgstation.Server.Host/GraphQL/Mutations/AdministrationMutations.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+using HotChocolate;
+using HotChocolate.Types;
+
+using Tgstation.Server.Api.Models.Response;
+using Tgstation.Server.Api.Rights;
+using Tgstation.Server.Host.Authority;
+using Tgstation.Server.Host.GraphQL.Scalars;
+using Tgstation.Server.Host.Security;
+
+namespace Tgstation.Server.Host.GraphQL.Mutations
+{
+ ///
+ /// related s.
+ ///
+ [ExtendObjectType(typeof(Mutation))]
+ [GraphQLDescription(Mutation.GraphQLDescription)]
+ public sealed class AdministrationMutations
+ {
+ ///
+ /// Restarts the server node without terminating running game instances.
+ ///
+ /// The for the .
+ /// A representing the running operation.
+ [TgsGraphQLAuthorize(nameof(IAdministrationAuthority.TriggerServerRestart))]
+ [Error(typeof(ErrorMessageException))]
+ public async ValueTask RestartServer(
+ [Service] IGraphQLAuthorityInvoker administrationAuthority)
+ {
+ ArgumentNullException.ThrowIfNull(administrationAuthority);
+ await administrationAuthority.Invoke(
+ authority => authority.TriggerServerRestart());
+
+ return new Query();
+ }
+
+ ///
+ /// Restarts the server node without terminating running game instances and changes its .
+ ///
+ /// The semver of the server available in the tracked repository to switch to.
+ /// The for the .
+ /// The for the operation.
+ /// A representing the running operation.
+ [TgsGraphQLAuthorize(AdministrationRights.ChangeVersion)]
+ [Error(typeof(ErrorMessageException))]
+ public async ValueTask ChangeServerVersionViaTrackedRepository(
+ Version targetVersion,
+ [Service] IGraphQLAuthorityInvoker administrationAuthority,
+ CancellationToken cancellationToken)
+ {
+ ArgumentNullException.ThrowIfNull(targetVersion);
+ ArgumentNullException.ThrowIfNull(administrationAuthority);
+ await administrationAuthority.Invoke(
+ authority => authority.TriggerServerVersionChange(targetVersion, false, cancellationToken));
+ return new Query();
+ }
+
+ ///
+ /// Restarts the server node without terminating running game instances and changes its .
+ ///
+ /// The semver of the server available in the tracked repository to switch to.
+ /// The for the .
+ /// The for the operation.
+ /// A FileTicket that should be used to upload a zip containing the update data to the file transfer service.
+ [TgsGraphQLAuthorize(AdministrationRights.UploadVersion)]
+ [Error(typeof(ErrorMessageException))]
+ [GraphQLType]
+ public async ValueTask ChangeServerVersionViaUpload(
+ Version targetVersion,
+ [Service] IGraphQLAuthorityInvoker administrationAuthority,
+ CancellationToken cancellationToken)
+ {
+ ArgumentNullException.ThrowIfNull(targetVersion);
+ ArgumentNullException.ThrowIfNull(administrationAuthority);
+ var response = await administrationAuthority.Invoke(
+ authority => authority.TriggerServerVersionChange(targetVersion, true, cancellationToken));
+
+ return response.FileTicket ?? throw new InvalidOperationException("Administration authority did not generate a FileUploadTicket!");
+ }
+ }
+}
diff --git a/src/Tgstation.Server.Host/GraphQL/Types/ServerSwarm.cs b/src/Tgstation.Server.Host/GraphQL/Types/ServerSwarm.cs
index 286aad2adf..35d82985dd 100644
--- a/src/Tgstation.Server.Host/GraphQL/Types/ServerSwarm.cs
+++ b/src/Tgstation.Server.Host/GraphQL/Types/ServerSwarm.cs
@@ -7,7 +7,6 @@ using HotChocolate;
using Microsoft.Extensions.Options;
using Tgstation.Server.Host.Configuration;
-using Tgstation.Server.Host.Core;
using Tgstation.Server.Host.GraphQL.Interfaces;
using Tgstation.Server.Host.Properties;
using Tgstation.Server.Host.Security;
@@ -20,19 +19,6 @@ namespace Tgstation.Server.Host.GraphQL.Types
///
public sealed class ServerSwarm
{
- ///
- /// If there is a swarm update in progress.
- ///
- /// The to use.
- /// if there is an update in progress, otherwise.
- [TgsGraphQLAuthorize]
- public bool UpdateInProgress(
- [Service] IServerControl serverControl)
- {
- ArgumentNullException.ThrowIfNull(serverControl);
- return serverControl.UpdateInProgress;
- }
-
///
/// Gets the swarm protocol major version in use.
///
@@ -78,5 +64,12 @@ namespace Tgstation.Server.Host.GraphQL.Types
ArgumentNullException.ThrowIfNull(swarmService);
return swarmService.GetSwarmServers()?.Select(x => new SwarmNode(x)).ToList();
}
+
+ ///
+ /// Gets the for the swarm.
+ ///
+ /// A new .
+ [TgsGraphQLAuthorize]
+ public UpdateInformation UpdateInformation() => new();
}
}
diff --git a/src/Tgstation.Server.Host/GraphQL/Types/UpdateInformation.cs b/src/Tgstation.Server.Host/GraphQL/Types/UpdateInformation.cs
new file mode 100644
index 0000000000..27e758bd79
--- /dev/null
+++ b/src/Tgstation.Server.Host/GraphQL/Types/UpdateInformation.cs
@@ -0,0 +1,117 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+using HotChocolate;
+
+using Tgstation.Server.Api.Models.Response;
+using Tgstation.Server.Host.Authority;
+using Tgstation.Server.Host.Core;
+using Tgstation.Server.Host.Utils;
+
+namespace Tgstation.Server.Host.GraphQL.Types
+{
+ ///
+ /// Gets information about updates for the .
+ ///
+ public sealed class UpdateInformation : IDisposable
+ {
+ ///
+ /// to prevent duplicate cache generations in one query.
+ ///
+ readonly SemaphoreSlim cacheReadSemaphore;
+
+ ///
+ /// If the cache was already force generated this query.
+ ///
+ bool cacheForceGenerated;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public UpdateInformation()
+ {
+ cacheReadSemaphore = new SemaphoreSlim(1, 1);
+ }
+
+ ///
+ public void Dispose()
+ => cacheReadSemaphore.Dispose();
+
+ ///
+ /// If there is a swarm update in progress. This is not affected by .
+ ///
+ /// The to use.
+ /// if there is an update in progress, otherwise.
+ public bool UpdateInProgress(
+ [Service] IServerControl serverControl)
+ {
+ ArgumentNullException.ThrowIfNull(serverControl);
+ return serverControl.UpdateInProgress;
+ }
+
+ ///
+ /// Gets the of the GitHub repository updates are sourced from.
+ ///
+ /// If the local cache TGS keeps of this data will be bypassed.
+ /// The for the to use.
+ /// The for the operation.
+ /// The of the GitHub repository updates are sourced from on success. if a GitHub API error occurred.
+ public async ValueTask TrackedRepositoryUrl(
+ bool forceFresh,
+ [Service] IGraphQLAuthorityInvoker administrationAuthority,
+ CancellationToken cancellationToken)
+ => (await GetAdministrationResponseSafe(forceFresh, administrationAuthority, cancellationToken)).TrackedRepositoryUrl;
+
+ ///
+ /// Gets the time the was generated.
+ ///
+ /// The for the to use.
+ /// The for the operation.
+ /// The time the was generated on success. if a GitHub API error occurred.
+ public async ValueTask GeneratedAt(
+ [Service] IGraphQLAuthorityInvoker administrationAuthority,
+ CancellationToken cancellationToken)
+ => (await GetAdministrationResponseSafe(false, administrationAuthority, cancellationToken)).GeneratedAt;
+
+ ///
+ /// Gets the latest of tgstation-server available on the GitHub repository updates are sourced from.
+ ///
+ /// If the local cache TGS keeps of this data will be bypassed.
+ /// The for the to use.
+ /// The for the operation.
+ /// The of the latest TGS version on success. if a GitHub API error occurred.
+ public async ValueTask LatestVersion(
+ bool forceFresh,
+ [Service] IGraphQLAuthorityInvoker administrationAuthority,
+ CancellationToken cancellationToken)
+ => (await GetAdministrationResponseSafe(forceFresh, administrationAuthority, cancellationToken)).LatestVersion;
+
+ ///
+ /// Safely retrieve the from a given without generating the cache multiple times in one query.
+ ///
+ /// If the local cache TGS keeps of this data will be bypassed.
+ /// The for the to use.
+ /// The for the operation.
+ /// A resulting in the from the .
+ async ValueTask GetAdministrationResponseSafe(
+ bool forceFresh,
+ IGraphQLAuthorityInvoker administrationAuthority,
+ CancellationToken cancellationToken)
+ {
+ using (await SemaphoreSlimContext.Lock(cacheReadSemaphore, cancellationToken))
+ {
+ if (cacheForceGenerated)
+ forceFresh = false;
+ else
+ cacheForceGenerated |= forceFresh;
+
+ ArgumentNullException.ThrowIfNull(administrationAuthority);
+ var response = await administrationAuthority.Invoke(
+ authority => authority.GetUpdateInformation(forceFresh, cancellationToken));
+
+ return response;
+ }
+ }
+ }
+}
diff --git a/tests/Tgstation.Server.Tests/Live/AdministrationTest.cs b/tests/Tgstation.Server.Tests/Live/AdministrationTest.cs
index afeabef6f5..d50205da90 100644
--- a/tests/Tgstation.Server.Tests/Live/AdministrationTest.cs
+++ b/tests/Tgstation.Server.Tests/Live/AdministrationTest.cs
@@ -13,11 +13,13 @@ namespace Tgstation.Server.Tests.Live
{
sealed class AdministrationTest
{
- readonly IAdministrationClient client;
+ readonly IMultiServerClient client;
+ readonly IAdministrationClient restClient;
- public AdministrationTest(IAdministrationClient client)
+ public AdministrationTest(MultiServerClient client)
{
this.client = client ?? throw new ArgumentNullException(nameof(client));
+ this.restClient = client.RestClient.Administration;
}
public async Task Run(CancellationToken cancellationToken)
@@ -29,24 +31,24 @@ namespace Tgstation.Server.Tests.Live
async Task TestLogs(CancellationToken cancellationToken)
{
- var logs = await client.ListLogs(null, cancellationToken);
+ var logs = await restClient.ListLogs(null, cancellationToken);
Assert.AreNotEqual(0, logs.Count);
var logFile = logs[0];
Assert.IsNotNull(logFile);
Assert.IsFalse(string.IsNullOrWhiteSpace(logFile.Name));
Assert.IsNull(logFile.FileTicket);
- var downloadedTuple = await client.GetLog(logFile, cancellationToken);
+ var downloadedTuple = await restClient.GetLog(logFile, cancellationToken);
Assert.AreEqual(logFile.Name, downloadedTuple.Item1.Name);
Assert.IsTrue(logFile.LastModified <= downloadedTuple.Item1.LastModified);
Assert.IsNull(logFile.FileTicket);
- await ApiAssert.ThrowsException>(() => client.GetLog(new LogFileResponse
+ await ApiAssert.ThrowsException>(() => restClient.GetLog(new LogFileResponse
{
Name = "very_fake_path.log"
}, cancellationToken), ErrorCode.IOError);
- await ApiAssert.ThrowsException>(() => client.GetLog(new LogFileResponse
+ await ApiAssert.ThrowsException>(() => restClient.GetLog(new LogFileResponse
{
Name = "../out_of_bounds.file"
}, cancellationToken));
@@ -54,24 +56,59 @@ namespace Tgstation.Server.Tests.Live
async Task TestRead(CancellationToken cancellationToken)
{
- var model = await client.Read(false, cancellationToken);
+ await client.Execute(
+ async restServerClient =>
+ {
+ var restClient = restServerClient.Administration;
- //we've released a few 5.x versions now, check the release checker is at least somewhat functional
- Assert.IsTrue(4 < model.LatestVersion.Major);
- Assert.IsNotNull(model.TrackedRepositoryUrl);
- Assert.IsTrue(model.GeneratedAt.HasValue);
- Assert.IsTrue(model.GeneratedAt.Value <= DateTimeOffset.UtcNow);
+ var model = await restClient.Read(false, cancellationToken);
- // test the cache
- var newerModel = await client.Read(false, cancellationToken);
- Assert.AreEqual(model.GeneratedAt, newerModel.GeneratedAt);
+ //we've released a few 5.x versions now, check the release checker is at least somewhat functional
+ Assert.IsNotNull(model.LatestVersion);
+ Assert.IsTrue(4 < model.LatestVersion.Major);
+ Assert.IsNotNull(model.TrackedRepositoryUrl);
+ Assert.IsTrue(model.GeneratedAt.HasValue);
+ Assert.IsTrue(model.GeneratedAt.Value <= DateTimeOffset.UtcNow);
- await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
+ // test the cache
+ var newerModel = await restClient.Read(false, cancellationToken);
+ Assert.AreEqual(model.GeneratedAt, newerModel.GeneratedAt);
- var newestModel = await client.Read(true, cancellationToken);
- Assert.AreNotEqual(model.GeneratedAt, newestModel.GeneratedAt);
- Assert.IsNotNull(newestModel.GeneratedAt);
- Assert.IsTrue(model.GeneratedAt < newestModel.GeneratedAt);
+ await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
+
+ var newestModel = await restClient.Read(true, cancellationToken);
+ Assert.AreNotEqual(model.GeneratedAt, newestModel.GeneratedAt);
+ Assert.IsNotNull(newestModel.GeneratedAt);
+ Assert.IsTrue(model.GeneratedAt < newestModel.GeneratedAt);
+ },
+ async gqlClient =>
+ {
+ var queryResult = await gqlClient.RunQueryEnsureNoErrors(
+ gql => gql.GetUpdateInformation.ExecuteAsync(false, cancellationToken),
+ cancellationToken);
+
+ // we've released a few 5.x versions now, check the release checker is at least somewhat functional
+ Assert.IsNotNull(queryResult.Swarm.UpdateInformation.LatestVersion);
+ Assert.IsTrue(4 < queryResult.Swarm.UpdateInformation.LatestVersion.Major);
+ Assert.IsNotNull(queryResult.Swarm.UpdateInformation.TrackedRepositoryUrl);
+ Assert.IsTrue(queryResult.Swarm.UpdateInformation.GeneratedAt.HasValue);
+ Assert.IsTrue(queryResult.Swarm.UpdateInformation.GeneratedAt.Value <= DateTimeOffset.UtcNow);
+
+ // test the cache
+ var queryResult2 = await gqlClient.RunQueryEnsureNoErrors(
+ gql => gql.GetUpdateInformation.ExecuteAsync(false, cancellationToken),
+ cancellationToken);
+ Assert.AreEqual(queryResult.Swarm.UpdateInformation.GeneratedAt, queryResult2.Swarm.UpdateInformation.GeneratedAt);
+
+ await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
+ var queryResult3 = await gqlClient.RunQueryEnsureNoErrors(
+ gql => gql.GetUpdateInformation.ExecuteAsync(true, cancellationToken),
+ cancellationToken);
+
+ Assert.AreNotEqual(queryResult.Swarm.UpdateInformation.GeneratedAt, queryResult3.Swarm.UpdateInformation.GeneratedAt);
+ Assert.IsNotNull(queryResult3.Swarm.UpdateInformation.GeneratedAt);
+ Assert.IsTrue(queryResult.Swarm.UpdateInformation.GeneratedAt.Value < queryResult3.Swarm.UpdateInformation.GeneratedAt.Value);
+ });
}
}
}
diff --git a/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs b/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs
index 9140a29df3..5df923a85c 100644
--- a/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs
+++ b/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs
@@ -340,21 +340,29 @@ namespace Tgstation.Server.Tests.Live
}
//attempt to update to stable
- var responseModel = await TestWithoutAndWithPermission(
- () => adminClient.RestClient.Administration.Update(
- new ServerUpdateRequest
- {
- NewVersion = TestUpdateVersion,
- UploadZip = false,
- },
- null,
- cancellationToken),
- adminClient.RestClient,
- AdministrationRights.ChangeVersion);
+ await adminClient.Execute(
+ async restClient =>
+ {
+ var responseModel = await TestWithoutAndWithPermission(
+ () => restClient.Administration.Update(
+ new ServerUpdateRequest
+ {
+ NewVersion = TestUpdateVersion,
+ UploadZip = false,
+ },
+ null,
+ cancellationToken),
+ adminClient.RestClient,
+ AdministrationRights.ChangeVersion);
- Assert.IsNotNull(responseModel);
- Assert.IsNull(responseModel.FileTicket);
- Assert.AreEqual(TestUpdateVersion, responseModel.NewVersion);
+ Assert.IsNotNull(responseModel);
+ Assert.IsNull(responseModel.FileTicket);
+ Assert.AreEqual(TestUpdateVersion, responseModel.NewVersion);
+ },
+ async gqlClient => await gqlClient.RunMutationEnsureNoErrors(
+ gql => gql.RepositoryBasedServerUpdate.ExecuteAsync(TestUpdateVersion, cancellationToken),
+ result => result,
+ cancellationToken));
try
{
@@ -524,17 +532,25 @@ namespace Tgstation.Server.Tests.Live
CheckInfo(controllerInfo);
// test update
- var responseModel = await controllerClient.RestClient.Administration.Update(
- new ServerUpdateRequest
+ await controllerClient.Execute(
+ async restClient =>
{
- NewVersion = TestUpdateVersion
- },
- null,
- cancellationToken);
+ var responseModel = await restClient.Administration.Update(
+ new ServerUpdateRequest
+ {
+ NewVersion = TestUpdateVersion
+ },
+ null,
+ cancellationToken);
- Assert.IsNotNull(responseModel);
- Assert.IsNull(responseModel.FileTicket);
- Assert.AreEqual(TestUpdateVersion, responseModel.NewVersion);
+ Assert.IsNotNull(responseModel);
+ Assert.IsNull(responseModel.FileTicket);
+ Assert.AreEqual(TestUpdateVersion, responseModel.NewVersion);
+ },
+ async gqlClient => await gqlClient.RunMutationEnsureNoErrors(
+ gql => gql.RepositoryBasedServerUpdate.ExecuteAsync(TestUpdateVersion, cancellationToken),
+ result => result,
+ cancellationToken));
await Task.WhenAny(Task.Delay(TimeSpan.FromMinutes(2)), serverTask);
Assert.IsTrue(serverTask.IsCompleted);
@@ -711,13 +727,18 @@ namespace Tgstation.Server.Tests.Live
await ApiAssert.ThrowsException(() => node1Client.RestClient.Instances.GetId(controllerInstance, cancellationToken), Api.Models.ErrorCode.ResourceNotPresent);
// test update
- await node1Client.RestClient.Administration.Update(
- new ServerUpdateRequest
- {
- NewVersion = TestUpdateVersion
- },
- null,
- cancellationToken);
+ await node1Client.Execute(
+ async restClient => await restClient.Administration.Update(
+ new ServerUpdateRequest
+ {
+ NewVersion = TestUpdateVersion
+ },
+ null,
+ cancellationToken),
+ async gqlClient => await gqlClient.RunMutationEnsureNoErrors(
+ gql => gql.RepositoryBasedServerUpdate.ExecuteAsync(TestUpdateVersion, cancellationToken),
+ result => result,
+ cancellationToken));
await Task.WhenAny(Task.Delay(TimeSpan.FromMinutes(2)), serverTask);
Assert.IsTrue(serverTask.IsCompleted);
@@ -752,13 +773,22 @@ namespace Tgstation.Server.Tests.Live
await using var controllerClient2 = await CreateAdminClient(controller.ApiUrl, cancellationToken);
await using var node1Client2 = await CreateAdminClient(node1.ApiUrl, cancellationToken);
- await ApiAssert.ThrowsException(() => controllerClient2.RestClient.Administration.Update(
- new ServerUpdateRequest
- {
- NewVersion = TestUpdateVersion
- },
- null,
- cancellationToken), Api.Models.ErrorCode.SwarmIntegrityCheckFailed);
+ await controllerClient2.Execute(
+ async restClient => await ApiAssert.ThrowsException(
+ () => restClient.Administration.Update(
+ new ServerUpdateRequest
+ {
+ NewVersion = TestUpdateVersion
+ },
+ null,
+ cancellationToken),
+ Api.Models.ErrorCode.SwarmIntegrityCheckFailed),
+ async gqlClient => await ApiAssert.OperationFails(
+ gqlClient,
+ gql => gql.RepositoryBasedServerUpdate.ExecuteAsync(TestUpdateVersion, cancellationToken),
+ result => result,
+ Client.GraphQL.ErrorCode.SwarmIntegrityCheckFailed,
+ cancellationToken));
// regression: test updating also works from the controller
serverTask = Task.WhenAll(
@@ -954,7 +984,13 @@ namespace Tgstation.Server.Tests.Live
Assert.IsFalse(node2Info.SwarmServers.Any(x => x.Identifier == "node1"));
// restart the controller
- await controllerClient.RestClient.Administration.Restart(cancellationToken);
+ await controllerClient.Execute(
+ restClient => restClient.Administration.Restart(cancellationToken),
+ async gqlClient => await gqlClient.RunMutationEnsureNoErrors(
+ gql => gql.RestartServer.ExecuteAsync(cancellationToken),
+ result => result,
+ cancellationToken));
+
await Task.WhenAny(
controllerTask,
Task.Delay(TimeSpan.FromMinutes(1), cancellationToken));
@@ -976,7 +1012,12 @@ namespace Tgstation.Server.Tests.Live
await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken);
// restart node2
- await node2Client.RestClient.Administration.Restart(cancellationToken);
+ await node2Client.Execute(
+ restClient => restClient.Administration.Restart(cancellationToken),
+ async gqlClient => await gqlClient.RunMutationEnsureNoErrors(
+ gql => gql.RestartServer.ExecuteAsync(cancellationToken),
+ result => result,
+ cancellationToken));
await Task.WhenAny(
node2Task,
Task.Delay(TimeSpan.FromMinutes(1)));
@@ -988,14 +1029,22 @@ namespace Tgstation.Server.Tests.Live
Assert.IsNull(controllerInfo.SwarmServers.SingleOrDefault(x => x.Identifier == "node2"));
// update should fail
- await ApiAssert.ThrowsException(
- () => controllerClient2.RestClient.Administration.Update(new ServerUpdateRequest
- {
- NewVersion = TestUpdateVersion
- },
- null,
- cancellationToken),
- Api.Models.ErrorCode.SwarmIntegrityCheckFailed);
+ await controllerClient2.Execute(
+ async restClient => await ApiAssert.ThrowsException(
+ () => restClient.Administration.Update(
+ new ServerUpdateRequest
+ {
+ NewVersion = TestUpdateVersion
+ },
+ null,
+ cancellationToken),
+ Api.Models.ErrorCode.SwarmIntegrityCheckFailed),
+ async gqlClient => await ApiAssert.OperationFails(
+ gqlClient,
+ gql => gql.RepositoryBasedServerUpdate.ExecuteAsync(TestUpdateVersion, cancellationToken),
+ result => result,
+ Client.GraphQL.ErrorCode.SwarmIntegrityCheckFailed,
+ cancellationToken));
node2Task = node2.Run(cancellationToken).AsTask();
await using var node2Client2 = await CreateAdminClient(node2.ApiUrl, cancellationToken);
@@ -1486,7 +1535,7 @@ namespace Tgstation.Server.Tests.Live
jobsHubTestTask = FailFast(await jobsHubTest.Run(cancellationToken)); // returns Task
var rootTest = FailFast(RawRequestTests.Run(restClientFactory, firstAdminRestClient, cancellationToken));
- var adminTest = FailFast(new AdministrationTest(firstAdminRestClient.Administration).Run(cancellationToken));
+ var adminTest = FailFast(new AdministrationTest(firstAdminMultiClient).Run(cancellationToken));
var usersTest = FailFast(new UsersTest(firstAdminMultiClient).Run(cancellationToken).AsTask());
var instanceManagerTest = new InstanceManagerTest(firstAdminRestClient, server.Directory);
@@ -1632,7 +1681,12 @@ namespace Tgstation.Server.Tests.Live
await Task.Delay(1000, cancellationToken);
jobsHubTest.ExpectShutdown();
- await firstAdminRestClient.Administration.Restart(cancellationToken);
+ await firstAdminMultiClient.Execute(
+ restClient => restClient.Administration.Restart(cancellationToken),
+ async gqlClient => await gqlClient.RunMutationEnsureNoErrors(
+ gql => gql.RestartServer.ExecuteAsync(cancellationToken),
+ result => result,
+ cancellationToken));
}
catch
{
@@ -1777,7 +1831,12 @@ namespace Tgstation.Server.Tests.Live
Assert.AreEqual(WatchdogStatus.Offline, dd.Status);
jobsHubTest.ExpectShutdown();
- await adminClient.Administration.Restart(cancellationToken);
+ await multiClient.Execute(
+ restClient => restClient.Administration.Restart(cancellationToken),
+ async gqlClient => await gqlClient.RunMutationEnsureNoErrors(
+ gql => gql.RestartServer.ExecuteAsync(cancellationToken),
+ result => result,
+ cancellationToken));
}
await Task.WhenAny(serverTask, Task.Delay(TimeSpan.FromMinutes(1), cancellationToken));
@@ -1844,7 +1903,12 @@ namespace Tgstation.Server.Tests.Live
expectedStaged = compileJob.Id.Value;
jobsHubTest.ExpectShutdown();
- await restAdminClient.Administration.Restart(cancellationToken);
+ await adminClient.Execute(
+ restClient => restClient.Administration.Restart(cancellationToken),
+ async gqlClient => await gqlClient.RunMutationEnsureNoErrors(
+ gql => gql.RestartServer.ExecuteAsync(cancellationToken),
+ result => result,
+ cancellationToken));
}
await Task.WhenAny(serverTask, Task.Delay(TimeSpan.FromMinutes(1), cancellationToken));