From 040fae8fe644b708604b9abeaa710e393e2c3e4e Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Wed, 22 Jun 2016 12:58:19 -0500 Subject: [PATCH] (GH-822) Provide pending override and file wait When a pending file is created, it needs to be at least 10 seconds old before choco will automatically remove the unfinished pacakge. This allows running choco functions inside of a package (as long as they are done quickly). If someone needs to override the functionality of the removal of a package due to the pending file, choco will also look for a .chocolateyPendingSkip and skip the removal if that file is found. --- .../registration/ContainerBinding.cs | 6 ++--- .../tasks/RemovePendingPackagesTask.cs | 26 +++++++++++++++++-- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs b/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs index 8ef7ddcb68..ddcdb44104 100644 --- a/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs +++ b/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs @@ -52,6 +52,8 @@ public void RegisterComponents(Container container) container.Register(() => configuration, Lifestyle.Singleton); container.Register(Lifestyle.Singleton); container.Register(Lifestyle.Singleton); + container.Register(Lifestyle.Singleton); + //nuget container.Register(Lifestyle.Singleton); container.Register(Lifestyle.Singleton); @@ -119,14 +121,12 @@ public void RegisterComponents(Container container) { var list = new List { - new RemovePendingPackagesTask(container.GetInstance()) + new RemovePendingPackagesTask(container.GetInstance(), container.GetInstance()) }; return list.AsReadOnly(); }, Lifestyle.Singleton); - - container.Register(Lifestyle.Singleton); } } diff --git a/src/chocolatey/infrastructure.app/tasks/RemovePendingPackagesTask.cs b/src/chocolatey/infrastructure.app/tasks/RemovePendingPackagesTask.cs index e68b2454f4..cdba4f9a14 100644 --- a/src/chocolatey/infrastructure.app/tasks/RemovePendingPackagesTask.cs +++ b/src/chocolatey/infrastructure.app/tasks/RemovePendingPackagesTask.cs @@ -21,6 +21,7 @@ namespace chocolatey.infrastructure.app.tasks using events; using filesystem; using infrastructure.events; + using infrastructure.services; using infrastructure.tasks; using logging; using tolerance; @@ -28,11 +29,15 @@ namespace chocolatey.infrastructure.app.tasks public class RemovePendingPackagesTask : ITask { private readonly IFileSystem _fileSystem; + private readonly IDateTimeService _dateTimeService; private IDisposable _subscription; + private const int PENDING_FILE_AGE_SECONDS = 10; + private const string PENDING_SKIP_FILE = ".chocolateyPendingSkip"; - public RemovePendingPackagesTask(IFileSystem fileSystem) + public RemovePendingPackagesTask(IFileSystem fileSystem, IDateTimeService dateTimeService) { _fileSystem = fileSystem; + _dateTimeService = dateTimeService; } public void initialize() @@ -54,8 +59,25 @@ private void handle_message(PreRunMessage message) foreach (var pendingFile in pendingFiles.or_empty_list_if_null()) { var packageFolder = _fileSystem.get_directory_name(pendingFile); - this.Log().Warn("[Pending] Removing incomplete install for '{0}'".format_with(_fileSystem.get_directory_info_for(packageFolder).Name)); + var packageFolderName = _fileSystem.get_directory_info_for(packageFolder).Name; + var pendingSkipFiles = _fileSystem.get_files(packageFolder, PENDING_SKIP_FILE, SearchOption.AllDirectories).ToList(); + if (pendingSkipFiles.Count != 0) + { + this.Log().Warn("Pending file found for {0}, but a {1} file was also found. Skipping removal".format_with(packageFolderName, PENDING_SKIP_FILE)); + continue; + } + + // wait for the file to be at least x seconds old + // this allows commands running from the package for configuring sources, etc + var fileInfo = _fileSystem.get_file_info_for(pendingFile); + if (fileInfo.CreationTimeUtc.AddSeconds(PENDING_FILE_AGE_SECONDS) > _dateTimeService.get_current_date_time()) + { + this.Log().Debug("Pending file found for {0}, but the file is not {1} seconds old yet.".format_with(packageFolderName, PENDING_FILE_AGE_SECONDS)); + continue; + } + + this.Log().Warn("[Pending] Removing incomplete install for '{0}'".format_with(packageFolderName)); FaultTolerance.retry(2, () => _fileSystem.delete_directory_if_exists(packageFolder, recursive: true, overrideAttributes: true, isSilent: true), 500, isSilent: true); } }