diff --git a/Build-SFPKGs.ps1 b/Build-SFPKGs.ps1 index 28fdf30a..30b97f8e 100644 --- a/Build-SFPKGs.ps1 +++ b/Build-SFPKGs.ps1 @@ -23,11 +23,11 @@ function Build-SFPkg { try { Push-Location $scriptPath - Build-SFPkg "Microsoft.ServiceFabricApps.FabricHealer.Linux.SelfContained.1.0.11" "$scriptPath\bin\release\FabricHealer\linux-x64\self-contained\FabricHealerType" - Build-SFPkg "Microsoft.ServiceFabricApps.FabricHealer.Linux.FrameworkDependent.1.0.11" "$scriptPath\bin\release\FabricHealer\linux-x64\framework-dependent\FabricHealerType" + Build-SFPkg "Microsoft.ServiceFabricApps.FabricHealer.Linux.SelfContained.1.0.12" "$scriptPath\bin\release\FabricHealer\linux-x64\self-contained\FabricHealerType" + Build-SFPkg "Microsoft.ServiceFabricApps.FabricHealer.Linux.FrameworkDependent.1.0.12" "$scriptPath\bin\release\FabricHealer\linux-x64\framework-dependent\FabricHealerType" - Build-SFPkg "Microsoft.ServiceFabricApps.FabricHealer.Windows.SelfContained.1.0.11" "$scriptPath\bin\release\FabricHealer\win-x64\self-contained\FabricHealerType" - Build-SFPkg "Microsoft.ServiceFabricApps.FabricHealer.Windows.FrameworkDependent.1.0.11" "$scriptPath\bin\release\FabricHealer\win-x64\framework-dependent\FabricHealerType" + Build-SFPkg "Microsoft.ServiceFabricApps.FabricHealer.Windows.SelfContained.1.0.12" "$scriptPath\bin\release\FabricHealer\win-x64\self-contained\FabricHealerType" + Build-SFPkg "Microsoft.ServiceFabricApps.FabricHealer.Windows.FrameworkDependent.1.0.12" "$scriptPath\bin\release\FabricHealer\win-x64\framework-dependent\FabricHealerType" } finally { Pop-Location diff --git a/FabricHealer.nuspec.template b/FabricHealer.nuspec.template index 7d105a8f..675bd46f 100644 --- a/FabricHealer.nuspec.template +++ b/FabricHealer.nuspec.template @@ -2,12 +2,10 @@ %PACKAGE_ID% - 1.0.11 + 1.0.12 -- Code enhancements. -- Test improvements. -- Tested with latest version of Guan (1.0.3). -- Removed unnecessary package reference (System.Text.Json). +- Bug fix in Disk Repair feature. +- Added new named argument to DeleteFiles external predicate: SearchPattern, for use in searching specified folders for files with names matching specified pattern. Microsoft MIT diff --git a/FabricHealer/FabricHealer.csproj b/FabricHealer/FabricHealer.csproj index a31b74c1..730313dc 100644 --- a/FabricHealer/FabricHealer.csproj +++ b/FabricHealer/FabricHealer.csproj @@ -11,8 +11,8 @@ linux-x64;win-x64 FabricHealer FabricHealer - 1.0.11 - 1.0.11 + 1.0.12 + 1.0.12 true true FabricHealer.Program diff --git a/FabricHealer/FabricHealerManager.cs b/FabricHealer/FabricHealerManager.cs index e29cea41..44fc6868 100644 --- a/FabricHealer/FabricHealerManager.cs +++ b/FabricHealer/FabricHealerManager.cs @@ -28,7 +28,7 @@ public sealed class FabricHealerManager : IDisposable internal static RepairData RepairHistory; // Folks often use their own version numbers. This is for internal diagnostic telemetry. - private const string InternalVersionNumber = "1.0.11"; + private const string InternalVersionNumber = "1.0.12"; private static FabricHealerManager singleton; private bool disposedValue; private readonly StatelessServiceContext serviceContext; diff --git a/FabricHealer/PackageRoot/Config/LogicRules/DiskRules.config.txt b/FabricHealer/PackageRoot/Config/LogicRules/DiskRules.config.txt index 87b8f536..23df1924 100644 --- a/FabricHealer/PackageRoot/Config/LogicRules/DiskRules.config.txt +++ b/FabricHealer/PackageRoot/Config/LogicRules/DiskRules.config.txt @@ -5,30 +5,38 @@ ## Mitigate() :- CheckInsideRunInterval(02:00:00), !. -## The sample rule below checks the folder size of SF query trace logs, fabricobserver logs, E:\temp. If the computed size exceeds a supplied threshold, then try and delete the files in the directory. -## The CheckFolderSize external predicate takes a folder path string argument and either a MaxFolderSizeGB or MaxFolderSizeMB argument. -## Either size arg must be supplied as a positive whole number. If the specified folder is larger than the supplied unsigned integer value for size, then the DeleteFiles external predicate will run. -## NOTE: You can supply optional arguments (named, non-positional) to the DeleteFiles external predicate. -## Optional (named, non-positional) arguments for DeleteFiles: +## DeleteFiles external predicate takes 1 required positional argument (position 0), which is the full path to the directory to be cleaned, and 4 optional named arguments. +## Optional (named, position 1 to n in argument list, first arg (0) is reserved for folder path) arguments for DeleteFiles: ## SortOrder - File sort order, Ascending or Descending. Defaults to Ascending (oldest to newest)). ## MaxFilesToDelete - The maximum number of files to delete. If not specified (or 0) it will be interpreted to mean delete all files. ## RecurseSubdirectories - Delete files in child folders of specified directory. Defaults to false if not specified. +## SearchPattern - Delete only files with file names that match the specified search pattern. Default is "*" (all files). -## Required (positional) arguments for CheckFolderSize external predicate: -## 0 (first argument): The full path to the folder to be cleaned. +## The CheckFolderSize external predicate takes a folder path string argument and either a MaxFolderSizeGB or MaxFolderSizeMB argument. +## Required positional arguments for CheckFolderSize external predicate: +## First argument (0 position in argument list): The full path to the folder to be cleaned. -## Optional (named, non-positional) arguments for CheckFolderSize external predicate: -## MaxFolderSizezGB (default if not supplied): max size of folder in gigabytes. +## Required named arguments (1 through n position in argument list) for CheckFolderSize external predicate: +## MaxFolderSizezGB: max size of folder in gigabytes. ## MaxFolderSizezMB: max size of folder in megabytes. +## If CheckFolderSize is true, then the next sub-goal will run, which in the rules below is the DeleteFiles external predicate. + +## Single rule for different directories and size constraints, but where DeleteFiles takes the same optional arguments (and values). ## Iterate over a list of folders with a system predicate, member (defined and implemented in Guan) and an internal predicate, -## config (an internal predicate needs no backing impl, it only exists in this logic). -## You can just write a rule for each folder should you need to have MB max size values for some folders and GB max size values for others. -## In the case below, all values are GB so it makes sense to use enumeration for convenience. -## The only supported disk repair is file deletion and this is related explicitly to DiskSpace* metrics. There are no repairs for queue length issues, for example. -## Note the use of the match system predicate to ensure the metric name supplied by FabricObserver contains "DiskSpace". +## config (an internal predicate needs no backing impl, it only exists in this logic). This is useful if all folder targets share enough +## of the same argument values (less rules to write..). Mitigate(MetricName=?MetricName) :- match(?MetricName, "DiskSpace"), GetRepairHistory(?repairCount, 08:00:00), ?repairCount < 4, - member(config(?X,?Y), [config("C:\SFDevCluster\Log\QueryTraces", 50), config("C:\observer_logs", 1), config("E:\temp", 40)]), - CheckFolderSize(?X, MaxFolderSizezGB=?Y), - DeleteFiles(?X, SortOrder=Ascending, MaxFilesToDelete=10, RecurseSubdirectories=true). \ No newline at end of file + member(config(?X,?Y), [config("C:\SFDevCluster\Log\QueryTraces", 50), config("C:\fabric_observer_logs", 1), config("E:\temp", 10)]), + CheckFolderSize(?X, MaxFolderSizeGB=?Y), + DeleteFiles(?X, SortOrder=Ascending, MaxFilesToDelete=10, RecurseSubdirectories=true). + +## Single rule for one directory with all optional args supplied, including a search pattern for target file name matching. +## Please note: when specifying search pattern string, you must enclose it in quotes since the special characters used can also be interpreted as mathematical operators (like / or *) by Guan +## if they are not quoted strings. This is true for Guan across the board. Keep this in mind when you pass arguments in rules that contain special characters +## that are also mathematical operators (*,+,/,-,%, etc...). +Mitigate(MetricName=?MetricName) :- match(?MetricName, "DiskSpace"), GetRepairHistory(?repairCount, 08:00:00), + ?repairCount < 4, + CheckFolderSize("C:\SFDevCluster\Log\Traces", MaxFolderSizeGB=20), + DeleteFiles("C:\SFDevCluster\Log\Traces", SortOrder=Ascending, MaxFilesToDelete=10, RecurseSubdirectories=true, SearchPattern="lease_traces*"). \ No newline at end of file diff --git a/FabricHealer/PackageRoot/Config/LogicRules/SystemAppRules.config.txt b/FabricHealer/PackageRoot/Config/LogicRules/SystemAppRules.config.txt index 51f9bd53..0e3c54c2 100644 --- a/FabricHealer/PackageRoot/Config/LogicRules/SystemAppRules.config.txt +++ b/FabricHealer/PackageRoot/Config/LogicRules/SystemAppRules.config.txt @@ -49,7 +49,7 @@ TimeScopedRestartFabricSystemProcess(?count, ?time) :- GetRepairHistory(?repairC RestartFabricSystemProcess(MaxWaitTimeForHealthStateOk=00:00:30). -## Mitigation rules for multiple metrics and targets. NOTE: Do not restart Fabric or FabricHost processes unless you want to take the node down. For the latter (restart node), +## Mitigation rules for multiple metrics and targets. NOTE: Do not restart Fabric or FabricHost processes unless you want to take the Fabric node down. For the latter (restart node), ## use TimeScopedRestartFabricNode (or RestartFabricNode predicate directly), which employs a safe Fabric node restart workflow (with deactivation step), not just a process kill. diff --git a/FabricHealer/PackageRoot/ServiceManifest.xml b/FabricHealer/PackageRoot/ServiceManifest.xml index ae153fbc..ccd6c99a 100644 --- a/FabricHealer/PackageRoot/ServiceManifest.xml +++ b/FabricHealer/PackageRoot/ServiceManifest.xml @@ -1,6 +1,6 @@  @@ -11,7 +11,7 @@ - + FabricHealer @@ -21,5 +21,5 @@ - + \ No newline at end of file diff --git a/FabricHealer/Repair/DiskRepairPolicy.cs b/FabricHealer/Repair/DiskRepairPolicy.cs index f4088902..c2f5c170 100644 --- a/FabricHealer/Repair/DiskRepairPolicy.cs +++ b/FabricHealer/Repair/DiskRepairPolicy.cs @@ -26,6 +26,11 @@ public FileSortOrder FileAgeSortOrder { get; set; } + + public string FileSearchPattern + { + get; set; + } } public enum FileSortOrder diff --git a/FabricHealer/Repair/Guan/CheckFolderSizePredicateType.cs b/FabricHealer/Repair/Guan/CheckFolderSizePredicateType.cs index 3c3f5abc..39997213 100644 --- a/FabricHealer/Repair/Guan/CheckFolderSizePredicateType.cs +++ b/FabricHealer/Repair/Guan/CheckFolderSizePredicateType.cs @@ -9,6 +9,7 @@ using FabricHealer.Utilities; using FabricHealer.Utilities.Telemetry; using System.Threading.Tasks; +using System; namespace FabricHealer.Repair.Guan { @@ -36,6 +37,7 @@ protected override async Task CheckAsync() } string folderPath = Input.Arguments[0].Value.GetEffectiveTerm().GetStringValue(); + long maxFolderSizeGB = 0; long maxFolderSizeMB = 0; @@ -52,32 +54,31 @@ protected override async Task CheckAsync() break; default: - maxFolderSizeGB = (long)Input.Arguments[i].Value.GetEffectiveTerm().GetObjectValue(); - break; + throw new GuanException($"Unrecognized argument supplied: {Input.Arguments[i].Name}"); } } if (!Directory.Exists(folderPath)) { await RepairTaskManager.TelemetryUtilities.EmitTelemetryEtwHealthEventAsync( - LogLevel.Info, - "CheckFolderSizePredicate::DirectoryNotFound", - $"Directory {folderPath} does not exist.", - RepairTaskManager.Token).ConfigureAwait(false); + LogLevel.Info, + "CheckFolderSizePredicate::DirectoryNotFound", + $"Directory {folderPath} does not exist.", + RepairTaskManager.Token).ConfigureAwait(false); return false; } if (Directory.GetFiles(folderPath, "*", new EnumerationOptions { RecurseSubdirectories = true }).Length == 0) { await RepairTaskManager.TelemetryUtilities.EmitTelemetryEtwHealthEventAsync( - LogLevel.Info, - "CheckFolderSizePredicate::NoFilesFound", - $"Directory {folderPath} does not contain any files.", - RepairTaskManager.Token).ConfigureAwait(false); - return false; + LogLevel.Info, + "CheckFolderSizePredicate::NoFilesFound", + $"Directory {folderPath} does not contain any files.", + RepairTaskManager.Token).ConfigureAwait(false); + return false; } - long size = 0; + double size = 0.0; if (maxFolderSizeGB > 0) { @@ -99,28 +100,28 @@ await RepairTaskManager.TelemetryUtilities.EmitTelemetryEtwHealthEventAsync( } string message = - $"Repair {FOHealthData.RepairId}: Supplied Maximum folder size value ({(maxFolderSizeGB > 0 ? maxFolderSizeGB + "GB" : maxFolderSizeMB + "MB")}) " + - $"for path {folderPath} is less than computed folder size ({size}{(maxFolderSizeGB > 0 ? "GB" : "MB")}). " + - "Will not attempt repair."; + $"Repair {FOHealthData.RepairId}: Supplied Maximum folder size value ({(maxFolderSizeGB > 0 ? maxFolderSizeGB + "GB" : maxFolderSizeMB + "MB")}) " + + $"for path {folderPath} is less than computed folder size ({size}{(maxFolderSizeGB > 0 ? "GB" : "MB")}). " + + "Will not attempt repair."; await RepairTaskManager.TelemetryUtilities.EmitTelemetryEtwHealthEventAsync( - LogLevel.Info, - "CheckFolderSizePredicate", - message, - RepairTaskManager.Token).ConfigureAwait(false); + LogLevel.Info, + "CheckFolderSizePredicate", + message, + RepairTaskManager.Token).ConfigureAwait(false); return false; } - private static async Task GetFolderSizeAsync(string path, SizeUnit unit) + private static async Task GetFolderSizeAsync(string path, SizeUnit unit) { var dir = new DirectoryInfo(path); - var folderSizeInBytes = dir.EnumerateFiles("*", SearchOption.AllDirectories).Sum(fi => fi.Length); + double folderSizeInBytes = Convert.ToDouble(dir.EnumerateFiles("*", SearchOption.AllDirectories).Sum(fi => fi.Length)); await RepairTaskManager.TelemetryUtilities.EmitTelemetryEtwHealthEventAsync( - LogLevel.Info, - "CheckFolderSizePredicate::Size", - $"Directory {path} size: {folderSizeInBytes} bytes.", - RepairTaskManager.Token).ConfigureAwait(false); + LogLevel.Info, + "CheckFolderSizePredicate::Size", + $"Directory {path} size: {folderSizeInBytes} bytes.", + RepairTaskManager.Token).ConfigureAwait(false); if (unit == SizeUnit.GB) { return folderSizeInBytes / 1024 / 1024 / 1024; diff --git a/FabricHealer/Repair/Guan/DeleteFilesPredicateType.cs b/FabricHealer/Repair/Guan/DeleteFilesPredicateType.cs index fb3df6f1..3ea5b919 100644 --- a/FabricHealer/Repair/Guan/DeleteFilesPredicateType.cs +++ b/FabricHealer/Repair/Guan/DeleteFilesPredicateType.cs @@ -8,6 +8,7 @@ using FabricHealer.Utilities; using FabricHealer.Utilities.Telemetry; using System.Threading.Tasks; +using System.IO; namespace FabricHealer.Repair.Guan { @@ -47,10 +48,21 @@ protected override async Task CheckAsync() } bool recurseSubDirectories = false; - string path = Input.Arguments[0].Value.GetEffectiveTerm().GetStringValue(); ; + string path = Input.Arguments[0].Value.GetEffectiveTerm().GetStringValue(); + + if (string.IsNullOrWhiteSpace(path)) + { + throw new GuanException("You must specify a full folder path as the first argument of DeleteFiles predicate."); + } + + if (!Directory.Exists(path)) + { + throw new GuanException($"{path} does not exist."); + } // default as 0 means delete all files. long maxFilesToDelete = 0; + string searchPattern = null; FileSortOrder direction = FileSortOrder.Ascending; int count = Input.Arguments.Count; @@ -70,31 +82,46 @@ protected override async Task CheckAsync() _ = bool.TryParse(Input.Arguments[i].Value.GetStringValue(), out recurseSubDirectories); break; + case "searchpattern": + searchPattern = Input.Arguments[i].Value.GetStringValue(); + break; + default: throw new GuanException($"Unsupported input: {Input.Arguments[i].Name}"); } } - if (string.IsNullOrWhiteSpace(path)) + if (searchPattern != null) { - throw new GuanException("Your DiskRepair logic rule must specify a FolderPath argument."); + if (!ValidateFileSearchPattern(searchPattern, path)) + { + await RepairTaskManager.TelemetryUtilities.EmitTelemetryEtwHealthEventAsync( + LogLevel.Info, + "DeleteFilesPredicateType::NoFilesMatchSearchPattern", + $"Specified search pattern, {searchPattern}, does not match any files in {path}.", + RepairTaskManager.Token).ConfigureAwait(false); + return false; + } } - // RepairPolicy + // RepairPolicy (base) repairConfiguration.RepairPolicy.RepairAction = RepairActionType.DeleteFiles; - ((DiskRepairPolicy)repairConfiguration.RepairPolicy).FolderPath = path; - repairConfiguration.RepairPolicy.RepairId = FOHealthData.RepairId; - ((DiskRepairPolicy)repairConfiguration.RepairPolicy).MaxNumberOfFilesToDelete = maxFilesToDelete; - ((DiskRepairPolicy)repairConfiguration.RepairPolicy).FileAgeSortOrder = direction; repairConfiguration.RepairPolicy.TargetType = RepairTargetType.VirtualMachine; - ((DiskRepairPolicy)repairConfiguration.RepairPolicy).RecurseSubdirectories = recurseSubDirectories; + repairConfiguration.RepairPolicy.RepairId = FOHealthData.RepairId; + + // DiskRepairPolicy (derives from RepairPolicy) + (repairConfiguration.RepairPolicy as DiskRepairPolicy).FolderPath = path; + (repairConfiguration.RepairPolicy as DiskRepairPolicy).MaxNumberOfFilesToDelete = maxFilesToDelete; + (repairConfiguration.RepairPolicy as DiskRepairPolicy).FileAgeSortOrder = direction; + (repairConfiguration.RepairPolicy as DiskRepairPolicy).RecurseSubdirectories = recurseSubDirectories; + (repairConfiguration.RepairPolicy as DiskRepairPolicy).FileSearchPattern = !string.IsNullOrWhiteSpace(searchPattern) ? searchPattern : "*"; // Try to schedule repair with RM. var repairTask = await FabricClientRetryHelper.ExecuteFabricActionWithRetryAsync( - () => RepairTaskManager.ScheduleFabricHealerRepairTaskAsync( - repairConfiguration, - RepairTaskManager.Token), - RepairTaskManager.Token).ConfigureAwait(false); + () => RepairTaskManager.ScheduleFabricHealerRepairTaskAsync( + repairConfiguration, + RepairTaskManager.Token), + RepairTaskManager.Token).ConfigureAwait(false); if (repairTask == null) { @@ -120,15 +147,30 @@ public static DeleteFilesPredicateType Singleton(string name, RepairTaskManager return Instance ??= new DeleteFilesPredicateType(name); } + public override PredicateResolver CreateResolver(CompoundTerm input, Constraint constraint, QueryContext context) + { + return new Resolver(input, constraint, context); + } + private DeleteFilesPredicateType(string name) - : base(name, true, 1) + : base(name, true, 1, 5) { - + } - public override PredicateResolver CreateResolver(CompoundTerm input, Constraint constraint, QueryContext context) + private static bool ValidateFileSearchPattern(string searchPattern, string path) { - return new Resolver(input, constraint, context); + if (string.IsNullOrWhiteSpace(searchPattern) || string.IsNullOrWhiteSpace(path) || !Directory.Exists(path)) + { + return false; + } + + if (Directory.GetFiles(path, searchPattern).Length > 0) + { + return true; + } + + return false; } } } diff --git a/FabricHealer/Repair/RepairExecutor.cs b/FabricHealer/Repair/RepairExecutor.cs index 3f56f2fb..58ad680f 100644 --- a/FabricHealer/Repair/RepairExecutor.cs +++ b/FabricHealer/Repair/RepairExecutor.cs @@ -780,7 +780,7 @@ await telemetryUtilities.EmitTelemetryEtwHealthEventAsync( internal async Task DeleteFilesAsync(RepairConfiguration repairConfiguration, CancellationToken cancellationToken) { string actionMessage = - $"Attempting to delete files in folder {((DiskRepairPolicy)repairConfiguration.RepairPolicy).FolderPath} " + + $"Attempting to delete files in folder {(repairConfiguration.RepairPolicy as DiskRepairPolicy).FolderPath} " + $"on node {repairConfiguration.NodeName}."; await telemetryUtilities.EmitTelemetryEtwHealthEventAsync( @@ -791,28 +791,36 @@ await telemetryUtilities.EmitTelemetryEtwHealthEventAsync( repairConfiguration, FabricHealerManager.ConfigSettings.EnableVerboseLogging).ConfigureAwait(false); - string targetFolderPath = ((DiskRepairPolicy)repairConfiguration.RepairPolicy).FolderPath; + string targetFolderPath = (repairConfiguration.RepairPolicy as DiskRepairPolicy).FolderPath; if (!Directory.Exists(targetFolderPath)) { + await telemetryUtilities.EmitTelemetryEtwHealthEventAsync( + LogLevel.Info, + "RepairExecutor.DeleteFilesAsync::DirectoryDoesNotExist", + $"The specified directory, {targetFolderPath}, does not exist.", + cancellationToken, + repairConfiguration, + FabricHealerManager.ConfigSettings.EnableVerboseLogging).ConfigureAwait(false); return false; } var dirInfo = new DirectoryInfo(targetFolderPath); - FileSortOrder direction = ((DiskRepairPolicy)repairConfiguration.RepairPolicy).FileAgeSortOrder; + FileSortOrder direction = (repairConfiguration.RepairPolicy as DiskRepairPolicy).FileAgeSortOrder; + string searchPattern = (repairConfiguration.RepairPolicy as DiskRepairPolicy).FileSearchPattern; List files = direction switch { - FileSortOrder.Ascending => (from file in dirInfo.EnumerateFiles("*", + FileSortOrder.Ascending => (from file in dirInfo.EnumerateFiles(searchPattern, new EnumerationOptions { - RecurseSubdirectories = ((DiskRepairPolicy) repairConfiguration.RepairPolicy).RecurseSubdirectories + RecurseSubdirectories = (repairConfiguration.RepairPolicy as DiskRepairPolicy).RecurseSubdirectories }) orderby file.LastWriteTimeUtc ascending select file.FullName).Distinct().ToList(), - FileSortOrder.Descending => (from file in dirInfo.EnumerateFiles("*", + FileSortOrder.Descending => (from file in dirInfo.EnumerateFiles(searchPattern, new EnumerationOptions { - RecurseSubdirectories = ((DiskRepairPolicy) repairConfiguration.RepairPolicy).RecurseSubdirectories + RecurseSubdirectories = (repairConfiguration.RepairPolicy as DiskRepairPolicy).RecurseSubdirectories }) orderby file.LastAccessTimeUtc descending select file.FullName).Distinct().ToList(), @@ -823,10 +831,17 @@ orderby file.LastAccessTimeUtc descending { int initialCount = files.Count; int deletedFiles = 0; - long maxFiles = ((DiskRepairPolicy)repairConfiguration.RepairPolicy).MaxNumberOfFilesToDelete; + long maxFiles = (repairConfiguration.RepairPolicy as DiskRepairPolicy).MaxNumberOfFilesToDelete; if (initialCount == 0) { + await telemetryUtilities.EmitTelemetryEtwHealthEventAsync( + LogLevel.Info, + "RepairExecutor.DeleteFilesAsync::NoFilesMatchSearchPattern", + $"No files match specified search pattern, {searchPattern}, in {targetFolderPath}. Nothing to do here.", + cancellationToken, + repairConfiguration, + FabricHealerManager.ConfigSettings.EnableVerboseLogging).ConfigureAwait(false); return false; } @@ -844,7 +859,7 @@ orderby file.LastAccessTimeUtc descending File.Delete(file); deletedFiles++; } - catch (Exception e) when (e is IOException || e is SecurityException) + catch (Exception e) when (e is ArgumentException || e is IOException || e is UnauthorizedAccessException) { await telemetryUtilities.EmitTelemetryEtwHealthEventAsync( LogLevel.Info, diff --git a/FabricHealerApp/ApplicationPackageRoot/ApplicationManifest.xml b/FabricHealerApp/ApplicationPackageRoot/ApplicationManifest.xml index aed43f85..312b8548 100644 --- a/FabricHealerApp/ApplicationPackageRoot/ApplicationManifest.xml +++ b/FabricHealerApp/ApplicationPackageRoot/ApplicationManifest.xml @@ -1,5 +1,5 @@  - + @@ -25,7 +25,7 @@ should match the Name and Version attributes of the ServiceManifest element defined in the ServiceManifest.xml file. --> - +