Skip to content

Commit

Permalink
fix package update check message, optimize some things
Browse files Browse the repository at this point in the history
- fix an issue where the package update check message would only contain an asterisk for a host if there was a pending restart but all packages were up-to-date
- create an object for holding update check results
- remove `bool isPeriodicCheck` argument from `PackageUpdateCheck()`; move logic to timed task instead
- adjust package update check output format in `/debug checks`
  • Loading branch information
FloatingMilkshake committed Apr 9, 2024
1 parent 69c7ea7 commit 8de8d12
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 39 deletions.
66 changes: 37 additions & 29 deletions Checks/PackageUpdateChecks.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
namespace MechanicalMilkshake.Checks;
using Microsoft.VisualBasic.CompilerServices;

namespace MechanicalMilkshake.Checks;

public class PackageUpdateChecks
{
public static async Task<(int numhostsChecked, int totalNumHosts, string checkResult)>
PackageUpdateCheck(bool isPeriodicCheck)
public static async Task<(int numhostsChecked, int totalNumHosts, string checkResult)> PackageUpdateCheck()
{
var numHostsChecked = 0;
var totalNumHosts = Program.ConfigJson.Base.SshHosts.Length;
if (totalNumHosts == 0) return (0, 0, "");

List<UpdateCheckResult> updateCheckResults = [];

var updatesAvailableResponse = "";

var updatesAvailable = false;
var restartRequired = false;
var numPackageUpdates = 0;

foreach (var rawHost in Program.ConfigJson.Base.SshHosts)
{
Expand Down Expand Up @@ -42,31 +46,27 @@ public class PackageUpdateChecks
"[PackageUpdateCheck] Error:\n{Error}", cmdResult.Error);

if (string.IsNullOrWhiteSpace(cmdResult.Output)) continue;

// Get hostname of machine
Regex hostnamePattern = new(@"[A-Za-z0-9-]+\@([A-Za-z0-9-]+)");
var hostnameMatch = hostnamePattern.Match(host);
var hostname = hostnameMatch.Groups[1].Value;

if (cmdResult.Output.Contains(" can be upgraded"))
{
// Get hostname of machine
Regex hostnamePattern = new(@"[A-Za-z0-9-]+\@([A-Za-z0-9-]+)");
var hostnameMatch = hostnamePattern.Match(host);
var hostname = hostnameMatch.Groups[1].Value;

// Get number of packages that can be upgraded
Regex numPackageUpdatesPattern = new(@"([0-9]+) packages can be upgraded");
Regex numPackageUpdatesPattern = new(@"([0-9]+) package[s]? can be upgraded");
var numPackageUpdatesMatch = numPackageUpdatesPattern.Match(cmdResult.Output);
var numPackageUpdates = numPackageUpdatesMatch.Groups[1].Value;

updatesAvailableResponse +=
$"{hostname}: {numPackageUpdates} package{(int.Parse(numPackageUpdates) > 1 ? "s" : "")}";
var numPackageUpdatesStr = numPackageUpdatesMatch.Groups[1].Value;
numPackageUpdates = numPackageUpdatesStr == "" ? 0 : int.Parse(numPackageUpdatesStr);
updatesAvailable = true;
}

if (cmdResult.Output.Contains("System restart required"))
{
updatesAvailableResponse += "*";
restartRequired = true;
}

updatesAvailableResponse += "\n";

// assemble result & add to list
updateCheckResults.Add(new UpdateCheckResult(hostname, numPackageUpdates, restartRequired));

numHostsChecked++;
}
Expand All @@ -76,19 +76,27 @@ public class PackageUpdateChecks

if (updatesAvailable || restartRequired)
{
string restartRequiredMessage = "";
if (restartRequired)
restartRequiredMessage = " Hosts marked with a * require a restart to apply updates.";
var restartRequiredMessage = restartRequired
? "A restart is required on some hosts (\\*) to apply updates."
: "";

if (updatesAvailable)
updatesAvailableResponse = $"Package updates are available.{restartRequiredMessage}\n{updatesAvailableResponse}";

var ownerMention =
Program.Discord.CurrentApplication.Owners.Aggregate("",
(current, user) => current + user.Mention + " ");
var updatesAvailableMessage = updatesAvailable
? "Package updates are available."
: "";

var response = updatesAvailableResponse;
if (isPeriodicCheck) await Program.HomeChannel.SendMessageAsync($"{ownerMention.Trim()}\n{response}");
var hostsWithUpdates = "";
foreach (var result in updateCheckResults)
{
if (result.UpdateCount > 0 || result.RestartRequired)
{
hostsWithUpdates += $"{result.Hostname}{(result.RestartRequired ? "\\*" : "")}";

if (result.UpdateCount > 0)
hostsWithUpdates += $": {result.UpdateCount} update{(result.UpdateCount > 1 ? "s" : "")}";
}
}

updatesAvailableResponse = $"{updatesAvailableMessage} {restartRequiredMessage}\n{hostsWithUpdates}";
}

return (numHostsChecked, totalNumHosts, updatesAvailableResponse);
Expand Down
12 changes: 3 additions & 9 deletions Commands/Owner/HomeServerCommands/DebugCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ public static async Task DebugChecks(InteractionContext ctx,
case "all":
(numRemindersBefore, numRemindersAfter, numRemindersSent, numRemindersFailed, numRemindersWithNullTime) =
await ReminderChecks.CheckRemindersAsync();
(numHostsChecked, totalNumHosts, checkResult) = await PackageUpdateChecks.PackageUpdateCheck(false);
(numHostsChecked, totalNumHosts, checkResult) = await PackageUpdateChecks.PackageUpdateCheck();
dbPing = await DatabaseChecks.CheckDatabaseConnectionAsync();
break;
case "reminders":
Expand All @@ -168,7 +168,7 @@ public static async Task DebugChecks(InteractionContext ctx,
dbPing = await DatabaseChecks.CheckDatabaseConnectionAsync();
break;
case "packageUpdates":
(numHostsChecked, totalNumHosts, checkResult) = await PackageUpdateChecks.PackageUpdateCheck(false);
(numHostsChecked, totalNumHosts, checkResult) = await PackageUpdateChecks.PackageUpdateCheck();
break;
}

Expand All @@ -188,15 +188,9 @@ public static async Task DebugChecks(InteractionContext ctx,
// package updates
var packageUpdateCheckResultMessage = "**Package Updates:** " + (totalNumHosts == 0
? "No hosts to check for package updates.\n"
: $"Checked `{numHostsChecked}`/`{totalNumHosts}` hosts for package updates.\n");
: $"Checked `{numHostsChecked}`/`{totalNumHosts}` hosts for package updates.\n> ");
if (numHostsChecked != 0 && checkResult is not null)
{
if (packageUpdateCheckResultMessage.EndsWith('\n'))
packageUpdateCheckResultMessage = packageUpdateCheckResultMessage[..^1] + " ";
packageUpdateCheckResultMessage += $"{checkResult.Replace("\n", "\n> ")}";
if (packageUpdateCheckResultMessage.EndsWith("\n> "))
packageUpdateCheckResultMessage = packageUpdateCheckResultMessage[..^3];
}

// set up response msg content
// include relevant check results (see variables)
Expand Down
14 changes: 14 additions & 0 deletions Entities/UpdateCheckResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace MechanicalMilkshake.Entities;

public class UpdateCheckResult
{
public UpdateCheckResult(string hostname, int updateCount, bool restartRequired)
{
Hostname = hostname;
UpdateCount = updateCount;
RestartRequired = restartRequired;
}
public string Hostname { get; }
public int UpdateCount { get; }
public bool RestartRequired { get; }
}
6 changes: 5 additions & 1 deletion Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,11 @@ internal static async Task Main()
{
while (true)
{
await PackageUpdateChecks.PackageUpdateCheck(true);
var (_, _, checkResult) = await PackageUpdateChecks.PackageUpdateCheck();
var ownerMention =
Discord.CurrentApplication.Owners.Aggregate("", (current, user) => current + user.Mention + " ");
await HomeChannel.SendMessageAsync($"{ownerMention.Trim()}\n{checkResult}");
await Task.Delay(259200000); // 3 days
}
// ReSharper disable once FunctionNeverReturns
Expand Down

0 comments on commit 8de8d12

Please sign in to comment.