Merge pull request #706 from tgstation/598-TestChatTagging

Chat and watchdog fixes
This commit is contained in:
Jordan Brown
2018-09-22 20:09:19 -04:00
committed by GitHub
11 changed files with 133 additions and 32 deletions
+2 -3
View File
@@ -222,14 +222,13 @@
export_lock = FALSE
/datum/tgs_api/v4/OnReboot()
var/json = Export(TGS4_COMM_WORLD_REBOOT)
var/list/result = json_decode(json)
var/list/result = Export(TGS4_COMM_WORLD_REBOOT)
if(!result)
return
//okay so the standard TGS4 proceedure is: right before rebooting change the port to whatever was sent to us in the above json's data parameter
var/port = json[TGS4_PARAMETER_DATA]
var/port = result[TGS4_PARAMETER_DATA]
if(!isnum(port))
return //this is valid, server may just want use to reboot
@@ -70,7 +70,7 @@ namespace Tgstation.Server.Host.Watchdog
logger.LogInformation("Detected dotnet executable at {0}", dotnetPath);
var rootLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var assemblyStoragePath = Path.Combine(rootLocation, "lib"); //always always next to watchdog
var defaultAssemblyPath = Path.GetFullPath(Path.Combine(assemblyStoragePath, "Default"));
#if DEBUG
@@ -114,7 +114,7 @@ namespace Tgstation.Server.Host.Watchdog
using (var process = new Process())
{
process.StartInfo.FileName = dotnetPath;
process.StartInfo.WorkingDirectory = Environment.CurrentDirectory; //for appsettings
process.StartInfo.WorkingDirectory = rootLocation; //for appsettings
var arguments = new List<string>
{
@@ -138,7 +138,7 @@ namespace Tgstation.Server.Host.Watchdog
logger.LogInformation("Launching host...");
var iShotTheSheriff = false;
var killedHostProcess = false;
try
{
process.Start();
@@ -172,7 +172,7 @@ namespace Tgstation.Server.Host.Watchdog
{
if (!process.HasExited)
{
iShotTheSheriff = true;
killedHostProcess = true;
process.Kill();
process.WaitForExit();
}
@@ -206,7 +206,7 @@ namespace Tgstation.Server.Host.Watchdog
}
throw new Exception(String.Format(CultureInfo.InvariantCulture, "Host propagated exception: {0}", data));
default:
if (iShotTheSheriff)
if (killedHostProcess)
{
logger.LogWarning("Watchdog forced to kill host process!");
cancellationToken.ThrowIfCancellationRequested();
@@ -122,7 +122,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
var channelsAvailable = new TaskCompletionSource<object>();
client.Ready += () =>
{
channelsAvailable.SetResult(null);
channelsAvailable.TrySetResult(null);
return Task.CompletedTask;
};
using (cancellationToken.Register(() => channelsAvailable.SetCanceled()))
@@ -291,16 +291,17 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
listenTask = Task.Factory.StartNew(() =>
{
while (!disconnecting && client.IsConnected)
while (!disconnecting && client.IsConnected && client.Nickname != nickname)
{
client.ListenOnce(true);
if (disconnecting || !client.IsConnected)
break;
client.Listen(false);
//ensure we have the correct nick
if (client.Nickname != nickname && client.GetIrcUser(nickname) == null)
if (client.GetIrcUser(nickname) == null)
client.RfcNick(nickname);
}
client.Listen();
}, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
}
catch (Exception e)
@@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -146,7 +147,7 @@ namespace Tgstation.Server.Host.Components
throw new JobException("Missing Repository!");
var repoSha = repo.Head;
revInfo = await databaseContext.RevisionInformations.Where(x => x.CommitSha == repoSha).Include(x => x.ActiveTestMerges).ThenInclude(x => x.TestMerge).FirstOrDefaultAsync().ConfigureAwait(false);
revInfo = await databaseContext.RevisionInformations.Where(x => x.CommitSha == repoSha && x.Instance.Id == metadata.Id).Include(x => x.ActiveTestMerges).ThenInclude(x => x.TestMerge).FirstOrDefaultAsync().ConfigureAwait(false);
if (revInfo == default)
{
@@ -202,7 +203,7 @@ namespace Tgstation.Server.Host.Components
StartedBy = user
};
var repositoryUpdateSuccess = false;
string deploySha = null;
await jobManager.RegisterOperation(repositoryUpdateJob, async (paramJob, databaseContext, progressReporter, jobCancellationToken) =>
{
var repositorySettingsTask = databaseContext.RepositorySettings.Where(x => x.InstanceId == metadata.Id).FirstAsync(jobCancellationToken);
@@ -230,47 +231,140 @@ namespace Tgstation.Server.Host.Components
return;
}
var startSha = repo.Head;
if (!repo.Tracking)
{
logger.LogTrace("Aborting repo update, not tracking origin!");
deploySha = startSha;
return;
}
var repositorySettings = await repositorySettingsTask.ConfigureAwait(false);
//the main point of auto update is to pull the remote
await repo.FetchOrigin(repositorySettings.AccessUser, repositorySettings.AccessToken, NextProgressReporter(), jobCancellationToken).ConfigureAwait(false);
var startSha = repo.Head;
RevisionInformation currentRevInfo = null;
bool hasDbChanges = false;
Task<RevisionInformation> LoadRevInfo() => databaseContext.RevisionInformations
.Where(x => x.CommitSha == startSha && x.Instance.Id == metadata.Id)
.Include(x => x.ActiveTestMerges).ThenInclude(x => x.TestMerge)
.FirstOrDefaultAsync(cancellationToken);
async Task UpdateRevInfo(string currentHead, bool onOrigin)
{
if(currentRevInfo == null)
currentRevInfo = await LoadRevInfo().ConfigureAwait(false);
if (currentRevInfo == default)
{
logger.LogWarning(Repository.Repository.OriginTrackingErrorTemplate, currentHead);
onOrigin = true;
}
var attachedInstance = new Models.Instance
{
Id = metadata.Id
};
var oldRevInfo = currentRevInfo;
currentRevInfo = new RevisionInformation
{
CommitSha = currentHead,
OriginCommitSha = onOrigin ? currentHead : oldRevInfo.OriginCommitSha,
Instance = attachedInstance
};
if (!onOrigin)
currentRevInfo.ActiveTestMerges = new List<RevInfoTestMerge>(oldRevInfo.ActiveTestMerges);
databaseContext.Instances.Attach(attachedInstance);
databaseContext.RevisionInformations.Add(currentRevInfo);
hasDbChanges = true;
}
//take appropriate auto update actions
bool shouldSyncTracked;
if (repositorySettings.AutoUpdatesKeepTestMerges.Value)
{
logger.LogTrace("Preserving test merges...");
var currentRevInfoTask = LoadRevInfo();
var result = await repo.MergeOrigin(repositorySettings.CommitterName, repositorySettings.CommitterEmail, NextProgressReporter(), jobCancellationToken).ConfigureAwait(false);
if (!result.HasValue)
throw new JobException("Merge conflict while preserving test merges!");
shouldSyncTracked = result.Value;
currentRevInfo = await currentRevInfoTask.ConfigureAwait(false);
var lastRevInfoWasOriginCommit = currentRevInfo == default || currentRevInfo.CommitSha == currentRevInfo.OriginCommitSha;
var stillOnOrigin = result.Value && lastRevInfoWasOriginCommit;
var currentHead = repo.Head;
if (currentHead != startSha)
{
await UpdateRevInfo(currentHead, stillOnOrigin).ConfigureAwait(false);
shouldSyncTracked = stillOnOrigin;
}
else
shouldSyncTracked = false;
}
else
{
logger.LogTrace("Not preserving test merges...");
await repo.ResetToOrigin(NextProgressReporter(), jobCancellationToken).ConfigureAwait(false);
var currentHead = repo.Head;
currentRevInfo = await databaseContext.RevisionInformations
.Where(x => x.CommitSha == currentHead && x.Instance.Id == metadata.Id)
.FirstOrDefaultAsync(jobCancellationToken).ConfigureAwait(false);
if (currentHead != startSha && currentRevInfo != default)
await UpdateRevInfo(currentHead, true).ConfigureAwait(false);
shouldSyncTracked = true;
}
//synch if necessary
if (repositorySettings.AutoUpdatesSynchronize.Value && startSha != repo.Head)
await repo.Sychronize(repositorySettings.AccessUser, repositorySettings.AccessToken, repositorySettings.CommitterName, repositorySettings.CommitterEmail, NextProgressReporter(), shouldSyncTracked, jobCancellationToken).ConfigureAwait(false);
{
var pushedOrigin = await repo.Sychronize(repositorySettings.AccessUser, repositorySettings.AccessToken, repositorySettings.CommitterName, repositorySettings.CommitterEmail, NextProgressReporter(), shouldSyncTracked, jobCancellationToken).ConfigureAwait(false);
var currentHead = repo.Head;
if (currentHead != currentRevInfo.CommitSha)
await UpdateRevInfo(currentHead, pushedOrigin).ConfigureAwait(false);
}
if(hasDbChanges)
try
{
await databaseContext.Save(cancellationToken).ConfigureAwait(false);
}
catch
{
await repo.ResetToSha(startSha, progressReporter, default).ConfigureAwait(false);
throw;
}
progressReporter(5 * ProgressStep);
deploySha = repo.Head;
}
repositoryUpdateSuccess = true;
}, cancellationToken).ConfigureAwait(false);
await jobManager.WaitForJobCompletion(repositoryUpdateJob, user, cancellationToken, default).ConfigureAwait(false);
if (!repositoryUpdateSuccess)
if (deploySha == null)
{
logger.LogTrace("Aborting auto update, repository error!");
continue;
}
if(deploySha == LatestCompileJob()?.RevisionInformation.CommitSha)
{
logger.LogTrace("Aborting auto update, same revision as latest CompileJob");
continue;
}
//finally set up the job
var compileProcessJob = new Job
{
@@ -122,8 +122,8 @@ namespace Tgstation.Server.Host.Components.Repository
/// <param name="synchronizeTrackedBranch">If the synchronizations should be made to the tracked reference as opposed to a temporary branch</param>
/// <param name="progressReporter"><see cref="Action{T1}"/> to report 0-100 <see cref="int"/> progress of the operation</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
/// <returns>A <see cref="Task"/> representing the running operation</returns>
Task Sychronize(string username, string password, string committerName, string committerEmail, Action<int> progressReporter, bool synchronizeTrackedBranch, CancellationToken cancellationToken);
/// <returns>A <see cref="Task{TResult}"/> resulting in <see langword="true"/> if commits were pushed to the tracked origin reference, <see langword="false"/> otherwise</returns>
Task<bool> Sychronize(string username, string password, string committerName, string committerEmail, Action<int> progressReporter, bool synchronizeTrackedBranch, CancellationToken cancellationToken);
/// <summary>
/// Copies the current working directory to a given <paramref name="path"/>
@@ -489,7 +489,7 @@ namespace Tgstation.Server.Host.Components.Repository
}
/// <inheritdoc />
public async Task Sychronize(string username, string password, string committerName, string committerEmail, Action<int> progressReporter, bool synchronizeTrackedBranch, CancellationToken cancellationToken)
public async Task<bool> Sychronize(string username, string password, string committerName, string committerEmail, Action<int> progressReporter, bool synchronizeTrackedBranch, CancellationToken cancellationToken)
{
if (committerName == null)
throw new ArgumentNullException(nameof(committerName));
@@ -501,7 +501,7 @@ namespace Tgstation.Server.Host.Components.Repository
if (username == null && password == null)
{
logger.LogTrace("Not synchronizing due to lack of credentials!");
return;
return false;
}
logger.LogTrace("Begin Synchronize...");
@@ -526,7 +526,7 @@ namespace Tgstation.Server.Host.Components.Repository
if (!await eventConsumer.HandleEvent(EventType.RepoPreSynchronize, new List<string> { ioMananger.ResolvePath(".") }, cancellationToken).ConfigureAwait(false))
{
logger.LogDebug("Aborted synchronize due to event handler response!");
return;
return false;
}
}
finally
@@ -548,32 +548,35 @@ namespace Tgstation.Server.Host.Components.Repository
if (!synchronizeTrackedBranch)
{
await PushHeadToTemporaryBranch(username, password, FinalReporter, cancellationToken).ConfigureAwait(false);
return;
return false;
}
var sameHead = Head == startHead;
if (sameHead || !Tracking)
{
logger.LogTrace("Aborted synchronize due to {0}!", sameHead ? "lack of changes" : "not being on tracked reference");
return;
return false;
}
logger.LogInformation("Synchronizing with origin...");
await Task.Factory.StartNew(() =>
return await Task.Factory.StartNew(() =>
{
var remote = repository.Network.Remotes.First();
try
{
repository.Network.Push(repository.Head, GeneratePushOptions(FinalReporter, username, password, cancellationToken));
return true;
}
catch (NonFastForwardException)
{
logger.LogInformation("Synchronize aborted, non-fast forward!");
return false;
}
catch (UserCancelledException)
catch (UserCancelledException e)
{
cancellationToken.ThrowIfCancellationRequested();
throw new InvalidOperationException("Caught UserCancelledException without cancellationToken triggering", e);
}
}, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
}
@@ -713,9 +713,11 @@ namespace Tgstation.Server.Host.Components.Watchdog
await Task.WhenAny(allTask, cancelTcs.Task).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
//both servers are now running, alpha is the active server, huzzah
//both servers are now running, alpha is the active server(unless reattach), huzzah
AlphaIsActive = doReattach ? reattachInfo?.AlphaIsActive ?? true : true;
LastLaunchResult = alphaLrt.Result;
(AlphaIsActive ? alphaServer : bravoServer).ClosePortOnReboot = true;
logger.LogInformation("Launched servers successfully");
Running = true;
@@ -53,7 +53,8 @@ namespace Tgstation.Server.Host.Controllers
IrcChannel = api.IrcChannel,
IsAdminChannel = api.IsAdminChannel ?? false,
IsWatchdogChannel = api.IsWatchdogChannel ?? false,
IsUpdatesChannel = api.IsUpdatesChannel ?? false
IsUpdatesChannel = api.IsUpdatesChannel ?? false,
Tag = api.Tag
};
/// <inheritdoc />
@@ -78,7 +78,7 @@ namespace Tgstation.Server.Host.Controllers
.FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); //search every rev info because LOL SHA COLLISIONS
if (revisionInfo == default)
revisionInfo = databaseContext.RevisionInformations.Local.Where(x => x.CommitSha == repoSha).FirstOrDefault();
revisionInfo = databaseContext.RevisionInformations.Local.Where(x => x.CommitSha == repoSha && x.Instance.Id == instance.Id).FirstOrDefault();
var needsDbUpdate = revisionInfo == default;
if (needsDbUpdate)
@@ -30,7 +30,8 @@ namespace Tgstation.Server.Host.Models
IsAdminChannel = IsAdminChannel,
IsWatchdogChannel = IsWatchdogChannel,
IsUpdatesChannel = IsUpdatesChannel,
IrcChannel = IrcChannel
IrcChannel = IrcChannel,
Tag = Tag
};
}
}