From 2e4c933fbedf1d0b51303dd8a6773d934af5a230 Mon Sep 17 00:00:00 2001 From: ChiaraBm Date: Sat, 2 Aug 2025 21:12:38 +0200 Subject: [PATCH] Started implementing server deletion --- .../ServerSys/Abstractions/Server.cs | 5 ++ .../Services/NewServerService.cs | 65 ++++++++++++++++++- 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/MoonlightServers.Daemon/ServerSys/Abstractions/Server.cs b/MoonlightServers.Daemon/ServerSys/Abstractions/Server.cs index 257195a..8393a8d 100644 --- a/MoonlightServers.Daemon/ServerSys/Abstractions/Server.cs +++ b/MoonlightServers.Daemon/ServerSys/Abstractions/Server.cs @@ -190,6 +190,11 @@ public class Server : IAsyncDisposable }); } + public async Task Delete() + { + await FileSystem.Delete(); + } + #region State machine handlers private async Task HandleStart() diff --git a/MoonlightServers.Daemon/Services/NewServerService.cs b/MoonlightServers.Daemon/Services/NewServerService.cs index 9001d59..dbf3c19 100644 --- a/MoonlightServers.Daemon/Services/NewServerService.cs +++ b/MoonlightServers.Daemon/Services/NewServerService.cs @@ -1,11 +1,13 @@ using System.Collections.Concurrent; +using MoonCore.Exceptions; using MoonCore.Helpers; using MoonCore.Models; using MoonlightServers.Daemon.Mappers; using MoonlightServers.Daemon.Models.Cache; using MoonlightServers.Daemon.ServerSys; -using MoonlightServers.Daemon.ServerSys.Abstractions; +using MoonlightServers.Daemon.ServerSystem; using MoonlightServers.DaemonShared.PanelSide.Http.Responses; +using Server = MoonlightServers.Daemon.ServerSys.Abstractions.Server; namespace MoonlightServers.Daemon.Services; @@ -88,9 +90,68 @@ public class NewServerService : IHostedLifecycleService if (server == null) throw new ArgumentException("No server with this id found", nameof(serverId)); + if (server.StateMachine.State == ServerState.Installing) + throw new HttpApiException("Unable to delete a server while it is installing", 400); + + if (server.StateMachine.State != ServerState.Offline) + { + // If the server is not offline we need to wait until it goes offline, we + // do that by creating the serverOfflineWaiter task completion source which will get triggered + // when the event handler for state changes gets informed that the server state is now offline + + var serverOfflineWaiter = new TaskCompletionSource(); + var timeoutCancellation = new CancellationTokenSource(); + + // Set timeout to 10 seconds, this gives the server 10 seconds to go offline, before the request fails + timeoutCancellation.CancelAfter(TimeSpan.FromSeconds(10)); + + // Subscribe to state updates in order to get notified when the server is offline + server.StateMachine.OnTransitioned(transition => + { + // Only listen for changes to offline + if (transition.Destination != ServerState.Offline) + return; + + // If the timeout has already been reached, ignore all changes + if (timeoutCancellation.IsCancellationRequested) + return; + + // Server is finally offline, notify the request that we now can delete the server + serverOfflineWaiter.SetResult(); + }); + + // Now we trigger the kill and waiting for the server to be deleted + await server.StateMachine.FireAsync(ServerTrigger.Kill); + + try + { + await serverOfflineWaiter.Task.WaitAsync(timeoutCancellation.Token); + + await DeleteServer_Unhandled(server); + } + catch (TaskCanceledException) + { + Logger.LogWarning( + "Deletion of server {id} failed because it didnt stop in time despite being killed", + server.Context.Configuration.Id + ); + + throw new HttpApiException( + "Could not kill the server in time for the deletion. Please try again later", + 500 + ); + } + } + else + await DeleteServer_Unhandled(server); + } + + private async Task DeleteServer_Unhandled(Server server) + { + await server.Delete(); await server.DisposeAsync(); - Servers.Remove(serverId, out _); + Servers.Remove(server.Context.Configuration.Id, out _); } #region Lifetime