using System; using System.Management.Automation; using System.Threading; using System.Threading.Tasks; namespace Kubectl { /// /// Base class for Cmdlets that run asynchronously. /// /// /// Inherit from this class if your Cmdlet needs to use async / await functionality. /// public abstract class AsyncCmdlet : PSCmdlet, IDisposable { /// /// The source for cancellation tokens that can be used to cancel the operation. /// readonly CancellationTokenSource _cancellationSource = new CancellationTokenSource(); /// /// Initialise the . /// protected AsyncCmdlet() { } /// /// Finaliser for . /// ~AsyncCmdlet() { Dispose(false); } /// /// Dispose of resources being used by the Cmdlet. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Dispose of resources being used by the Cmdlet. /// /// /// Explicit disposal? /// protected virtual void Dispose(bool disposing) { if (disposing) _cancellationSource.Dispose(); } /// /// Asynchronously perform Cmdlet pre-processing. /// /// /// A representing the asynchronous operation. /// protected virtual Task BeginProcessingAsync() { return BeginProcessingAsync(_cancellationSource.Token); } /// /// Asynchronously perform Cmdlet pre-processing. /// /// /// A that can be used to cancel the asynchronous operation. /// /// /// A representing the asynchronous operation. /// protected virtual Task BeginProcessingAsync(CancellationToken cancellationToken) { return Task.CompletedTask; } /// /// Asynchronously perform Cmdlet processing. /// /// /// A representing the asynchronous operation. /// protected virtual Task ProcessRecordAsync() { return ProcessRecordAsync(_cancellationSource.Token); } /// /// Asynchronously perform Cmdlet processing. /// /// /// A that can be used to cancel the asynchronous operation. /// /// /// A representing the asynchronous operation. /// protected virtual Task ProcessRecordAsync(CancellationToken cancellationToken) { return Task.CompletedTask; } /// /// Asynchronously perform Cmdlet post-processing. /// /// /// A representing the asynchronous operation. /// protected virtual Task EndProcessingAsync() { return EndProcessingAsync(_cancellationSource.Token); } /// /// Asynchronously perform Cmdlet post-processing. /// /// /// A that can be used to cancel the asynchronous operation. /// /// /// A representing the asynchronous operation. /// protected virtual Task EndProcessingAsync(CancellationToken cancellationToken) { return Task.CompletedTask; } /// /// Perform Cmdlet pre-processing. /// protected sealed override void BeginProcessing() { ThreadAffinitiveSynchronizationContext.RunSynchronized( () => BeginProcessingAsync() ); } /// /// Perform Cmdlet processing. /// protected sealed override void ProcessRecord() { ThreadAffinitiveSynchronizationContext.RunSynchronized( () => ProcessRecordAsync() ); } /// /// Perform Cmdlet post-processing. /// protected sealed override void EndProcessing() { ThreadAffinitiveSynchronizationContext.RunSynchronized( () => EndProcessingAsync() ); } /// /// Interrupt Cmdlet processing (if possible). /// protected sealed override void StopProcessing() { _cancellationSource.Cancel(); base.StopProcessing(); } /// /// Write a progress record to the output stream, and as a verbose message. /// /// /// The progress record to write. /// protected void WriteVerboseProgress(ProgressRecord progressRecord) { if (progressRecord == null) throw new ArgumentNullException(nameof(progressRecord)); WriteProgress(progressRecord); WriteVerbose(progressRecord.StatusDescription); } /// /// Write a progress record to the output stream, and as a verbose message. /// /// /// The progress record to write. /// /// /// The message or message-format specifier. /// /// /// Optional format arguments. /// protected void WriteVerboseProgress(ProgressRecord progressRecord, string messageOrFormat, params object[] formatArguments) { if (progressRecord == null) throw new ArgumentNullException(nameof(progressRecord)); if (String.IsNullOrWhiteSpace(messageOrFormat)) throw new ArgumentException("Argument cannot be null, empty, or composed entirely of whitespace: 'messageOrFormat'.", nameof(messageOrFormat)); if (formatArguments == null) throw new ArgumentNullException(nameof(formatArguments)); progressRecord.StatusDescription = String.Format(messageOrFormat, formatArguments); WriteVerboseProgress(progressRecord); } /// /// Write a completed progress record to the output stream. /// /// /// The progress record to complete. /// /// /// The completion message or message-format specifier. /// /// /// Optional format arguments. /// protected void WriteProgressCompletion(ProgressRecord progressRecord, string completionMessageOrFormat, params object[] formatArguments) { if (progressRecord == null) throw new ArgumentNullException(nameof(progressRecord)); if (String.IsNullOrWhiteSpace(completionMessageOrFormat)) throw new ArgumentException("Argument cannot be null, empty, or composed entirely of whitespace: 'completionMessageOrFormat'.", nameof(completionMessageOrFormat)); if (formatArguments == null) throw new ArgumentNullException(nameof(formatArguments)); progressRecord.StatusDescription = String.Format(completionMessageOrFormat, formatArguments); progressRecord.PercentComplete = 100; progressRecord.RecordType = ProgressRecordType.Completed; WriteProgress(progressRecord); WriteVerbose(progressRecord.StatusDescription); } } }