From d470d9fb34297495020fc11563d04a88abb25ed9 Mon Sep 17 00:00:00 2001 From: Luke Butters Date: Thu, 20 Jun 2024 12:27:17 +1000 Subject: [PATCH 01/23] Try to make an event driven orchstrator --- .../EventDriven/ITicketForNextStatus.cs | 33 +++++ .../EventDrivenScriptExecutor.cs | 108 +++++++++++++++++ .../IEventDrivenScriptExecutor.cs | 41 +++++++ .../Scripts/IScriptOrchestrator.cs | 14 ++- .../Scripts/ObservingScriptOrchestrator.cs | 46 +++---- .../Scripts/ScriptOrchestratorFactory.cs | 114 +++--------------- .../Scripts/ScriptServicePicker.cs | 100 +++++++++++++++ .../Scripts/ScriptServiceV1Orchestrator.cs | 28 ++--- .../Scripts/ScriptServiceV2Orchestrator.cs | 33 +++-- .../Scripts/ScriptServiceVersion.cs | 3 +- .../ServiceHelpers/ClientsHolder.cs | 40 ++++++ .../Octopus.Tentacle.Client/TentacleClient.cs | 37 ++---- .../ScriptStatus.cs | 18 +++ 13 files changed, 418 insertions(+), 197 deletions(-) create mode 100644 source/Octopus.Tentacle.Client/EventDriven/ITicketForNextStatus.cs create mode 100644 source/Octopus.Tentacle.Client/EventDrivenScriptExecutor.cs create mode 100644 source/Octopus.Tentacle.Client/IEventDrivenScriptExecutor.cs create mode 100644 source/Octopus.Tentacle.Client/Scripts/ScriptServicePicker.cs create mode 100644 source/Octopus.Tentacle.Client/ServiceHelpers/ClientsHolder.cs create mode 100644 source/Octopus.Tentacle.Contracts/ScriptStatus.cs diff --git a/source/Octopus.Tentacle.Client/EventDriven/ITicketForNextStatus.cs b/source/Octopus.Tentacle.Client/EventDriven/ITicketForNextStatus.cs new file mode 100644 index 000000000..3d13572f0 --- /dev/null +++ b/source/Octopus.Tentacle.Client/EventDriven/ITicketForNextStatus.cs @@ -0,0 +1,33 @@ +using Octopus.Tentacle.Client.Scripts; +using Octopus.Tentacle.Contracts; + +namespace Octopus.Tentacle.Client.EventDriven +{ + public interface ITicketForNextStatus + { + + ScriptTicket ScriptTicket { get; } + long NextLogSequence { get; } + public ScriptServiceVersion WhichService { get; } + + // TODO does it actually make sense to all of these properties? Perhaps instead we should just expose this concept of + // serialize the entire thing. That way the driver only needs to know how to save something down for the next time it + // wants to continue script execution. + } + + public class DefaultTicketForNextStatus : ITicketForNextStatus + { + public DefaultTicketForNextStatus(ScriptTicket scriptTicket, + long nextLogSequence, + ScriptServiceVersion whichService) + { + ScriptTicket = scriptTicket; + NextLogSequence = nextLogSequence; + WhichService = whichService; + } + + public ScriptTicket ScriptTicket { get; } + public long NextLogSequence { get; } + public ScriptServiceVersion WhichService { get; } + } +} \ No newline at end of file diff --git a/source/Octopus.Tentacle.Client/EventDrivenScriptExecutor.cs b/source/Octopus.Tentacle.Client/EventDrivenScriptExecutor.cs new file mode 100644 index 000000000..84d49a789 --- /dev/null +++ b/source/Octopus.Tentacle.Client/EventDrivenScriptExecutor.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Halibut; +using Octopus.Tentacle.Client.EventDriven; +using Octopus.Tentacle.Client.Execution; +using Octopus.Tentacle.Client.Observability; +using Octopus.Tentacle.Client.Scripts; +using Octopus.Tentacle.Client.Scripts.Models; +using Octopus.Tentacle.Client.ServiceHelpers; +using Octopus.Tentacle.Contracts; +using Octopus.Tentacle.Contracts.Logging; +using Octopus.Tentacle.Contracts.Observability; + +namespace Octopus.Tentacle.Client +{ + public class EventDrivenScriptExecutor : IEventDrivenScriptExecutor + { + readonly ITentacleClientTaskLog logger; + readonly ITentacleClientObserver tentacleClientObserver; + readonly TentacleClientOptions clientOptions; + readonly ClientsHolder clientsHolder; + readonly RpcCallExecutor rpcCallExecutor; + readonly TimeSpan onCancellationAbandonCompleteScriptAfter; + + public EventDrivenScriptExecutor(ITentacleClientTaskLog logger, + ITentacleClientObserver tentacleClientOptions, + TentacleClientOptions clientOptions, + IHalibutRuntime halibutRuntime, + ServiceEndPoint serviceEndPoint, TimeSpan onCancellationAbandonCompleteScriptAfter) : this(logger, tentacleClientOptions, clientOptions, halibutRuntime, serviceEndPoint, null, onCancellationAbandonCompleteScriptAfter) + { + } + + internal EventDrivenScriptExecutor(ITentacleClientTaskLog logger, + ITentacleClientObserver tentacleClientOptions, + TentacleClientOptions clientOptions, + IHalibutRuntime halibutRuntime, + ServiceEndPoint serviceEndPoint, + ITentacleServiceDecoratorFactory? tentacleServicesDecoratorFactory, TimeSpan onCancellationAbandonCompleteScriptAfter) + { + this.logger = logger; + tentacleClientObserver = tentacleClientOptions; + this.clientOptions = clientOptions; + this.onCancellationAbandonCompleteScriptAfter = onCancellationAbandonCompleteScriptAfter; + clientsHolder = new ClientsHolder(halibutRuntime, serviceEndPoint, tentacleServicesDecoratorFactory); + rpcCallExecutor = RpcCallExecutorFactory.Create(this.clientOptions.RpcRetrySettings.RetryDuration, this.tentacleClientObserver) + } + + public async Task<(ScriptStatus, ITicketForNextStatus)> StartScript(ExecuteScriptCommand executeScriptCommand, + HasStartScriptBeenCalledBefore hasStartScriptBeenCalledBefore, + CancellationToken cancellationToken) + { + var operationMetricsBuilder = ClientOperationMetricsBuilder.Start(); + + // Pick what service to use. + var scriptServiceToUse = await new ScriptServicePicker(clientsHolder.CapabilitiesServiceV2, logger, rpcCallExecutor, clientOptions, operationMetricsBuilder) + .DetermineScriptServiceVersionToUse(cancellationToken); + + // And start the script + if (scriptServiceToUse == ScriptServiceVersion.ScriptServiceVersion1 && hasStartScriptBeenCalledBefore == HasStartScriptBeenCalledBefore.ItMayHaveBeen) + { + return (new ScriptStatus(ProcessState.Complete, ScriptExitCodes.UnknownScriptExitCode, new List()), new DefaultTicketForNextStatus(new ScriptTicket(Guid.NewGuid().ToString()), 0, ScriptServiceVersion.ScriptServiceVersion1)); + } + + var scriptOrchestratorFactory = new ScriptOrchestratorFactory(clientsHolder, + new DefaultScriptObserverBackoffStrategy(), + rpcCallExecutor, + operationMetricsBuilder, + status => { }, + token => Task.CompletedTask, + onCancellationAbandonCompleteScriptAfter, + clientOptions, + logger); + + var orchestrator = scriptOrchestratorFactory.CreateOrchestrator(scriptServiceToUse); + + orchestrator.s + + // and return stuff. + throw new System.NotImplementedException(); + } + + public Task<(ScriptStatus, ITicketForNextStatus)> GetStatus(ITicketForNextStatus ticketForNextNextStatus, CancellationToken cancellationToken) + { + + ScriptOrchestratorFactory scriptOrchestratorFactory = null!; + var orch = scriptOrchestratorFactory.CreateOrchestrator(ticketForNextNextStatus.WhichService); + + throw new System.NotImplementedException(); + } + + public Task<(ScriptStatus, ITicketForNextStatus)> CancelScript(ITicketForNextStatus ticketForNextNextStatus, CancellationToken cancellationToken) + { + throw new System.NotImplementedException(); + } + + public Task<(ScriptStatus, ITicketForNextStatus)> CancelScript(ScriptTicket scriptTicket, CancellationToken cancellationToken) + { + throw new System.NotImplementedException(); + } + + public Task CleanUpScript(ScriptTicket scriptTicket, CancellationToken cancellationToken) + { + throw new System.NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/source/Octopus.Tentacle.Client/IEventDrivenScriptExecutor.cs b/source/Octopus.Tentacle.Client/IEventDrivenScriptExecutor.cs new file mode 100644 index 000000000..d019ef24e --- /dev/null +++ b/source/Octopus.Tentacle.Client/IEventDrivenScriptExecutor.cs @@ -0,0 +1,41 @@ +using System.Threading; +using System.Threading.Tasks; +using Octopus.Tentacle.Client.EventDriven; +using Octopus.Tentacle.Client.Scripts.Models; +using Octopus.Tentacle.Contracts; + +namespace Octopus.Tentacle.Client +{ + public interface IEventDrivenScriptExecutor + { + Task<(ScriptStatus, ITicketForNextStatus)> StartScript(ExecuteScriptCommand executeScriptCommand, + HasStartScriptBeenCalledBefore hasStartScriptBeenCalledBefore, + CancellationToken cancellationToken); + + Task<(ScriptStatus, ITicketForNextStatus)> GetStatus(ITicketForNextStatus ticketForNextNextStatus, CancellationToken cancellationToken); + + /// + /// Cancel script will still send back the rest of the logs, hence the ticketForNextNextStatus argument. + /// + /// + /// + /// + Task<(ScriptStatus, ITicketForNextStatus)> CancelScript(ITicketForNextStatus ticketForNextNextStatus, CancellationToken cancellationToken); + + /// + /// Use this cancel method if only the ScriptTicket is known, e.g. we called StartScript but never got a response. + /// + /// + /// + /// + Task<(ScriptStatus, ITicketForNextStatus)> CancelScript(ScriptTicket scriptTicket, CancellationToken cancellationToken); + + Task CleanUpScript(ScriptTicket scriptTicket, CancellationToken cancellationToken); + } + + public enum HasStartScriptBeenCalledBefore + { + NoNever, + ItMayHaveBeen + } +} \ No newline at end of file diff --git a/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs index c0026d809..cdc70ffdd 100644 --- a/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs @@ -2,11 +2,23 @@ using System.Threading; using System.Threading.Tasks; using Octopus.Tentacle.Client.Scripts.Models; +using Octopus.Tentacle.Contracts; namespace Octopus.Tentacle.Client.Scripts { - interface IScriptOrchestrator + public interface IScriptOrchestrator { Task ExecuteScript(ExecuteScriptCommand command, CancellationToken scriptExecutionCancellationToken); } + + public interface IStructuredScriptOrchestrator { + TStartCommand Map(ExecuteScriptCommand command); + ScriptExecutionStatus MapToStatus(TScriptStatusResponse response); + ScriptExecutionResult MapToResult(TScriptStatusResponse response); + ProcessState GetState(TScriptStatusResponse response); + Task StartScript(TStartCommand command, CancellationToken scriptExecutionCancellationToken); + Task GetStatus(TScriptStatusResponse lastStatusResponse, CancellationToken scriptExecutionCancellationToken); + Task Cancel(TScriptStatusResponse lastStatusResponse, CancellationToken scriptExecutionCancellationToken); + Task Finish(TScriptStatusResponse lastStatusResponse, CancellationToken scriptExecutionCancellationToken); + } } \ No newline at end of file diff --git a/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs index 47d7fd64c..817d180cc 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs @@ -6,21 +6,21 @@ namespace Octopus.Tentacle.Client.Scripts { - abstract class ObservingScriptOrchestrator : IScriptOrchestrator + public sealed class ObservingScriptOrchestrator : IScriptOrchestrator { readonly IScriptObserverBackoffStrategy scriptObserverBackOffStrategy; readonly OnScriptStatusResponseReceived onScriptStatusResponseReceived; readonly OnScriptCompleted onScriptCompleted; - protected TentacleClientOptions ClientOptions { get; } + IStructuredScriptOrchestrator structuredScriptOrchestrator; - protected ObservingScriptOrchestrator( + public ObservingScriptOrchestrator( IScriptObserverBackoffStrategy scriptObserverBackOffStrategy, OnScriptStatusResponseReceived onScriptStatusResponseReceived, OnScriptCompleted onScriptCompleted, - TentacleClientOptions clientOptions) + IStructuredScriptOrchestrator structuredScriptOrchestrator) { - ClientOptions = clientOptions; + this.structuredScriptOrchestrator = structuredScriptOrchestrator; this.scriptObserverBackOffStrategy = scriptObserverBackOffStrategy; this.onScriptStatusResponseReceived = onScriptStatusResponseReceived; this.onScriptCompleted = onScriptCompleted; @@ -28,9 +28,9 @@ protected ObservingScriptOrchestrator( public async Task ExecuteScript(ExecuteScriptCommand command, CancellationToken scriptExecutionCancellationToken) { - var mappedStartCommand = Map(command); + var mappedStartCommand = structuredScriptOrchestrator.Map(command); - var scriptStatusResponse = await StartScript(mappedStartCommand, scriptExecutionCancellationToken).ConfigureAwait(false); + var scriptStatusResponse = await structuredScriptOrchestrator.StartScript(mappedStartCommand, scriptExecutionCancellationToken).ConfigureAwait(false); scriptStatusResponse = await ObserveUntilCompleteThenFinish(scriptStatusResponse, scriptExecutionCancellationToken).ConfigureAwait(false); @@ -40,12 +40,12 @@ public async Task ExecuteScript(ExecuteScriptCommand comm throw new OperationCanceledException("Script execution was cancelled"); } - var mappedResponse = MapToResult(scriptStatusResponse); + var mappedResponse = structuredScriptOrchestrator.MapToResult(scriptStatusResponse); return new ScriptExecutionResult(mappedResponse.State, mappedResponse.ExitCode); } - protected async Task ObserveUntilCompleteThenFinish( + async Task ObserveUntilCompleteThenFinish( TScriptStatusResponse scriptStatusResponse, CancellationToken scriptExecutionCancellationToken) { @@ -55,7 +55,9 @@ protected async Task ObserveUntilCompleteThenFinish( await onScriptCompleted(scriptExecutionCancellationToken).ConfigureAwait(false); - lastScriptStatus = await Finish(lastScriptStatus, scriptExecutionCancellationToken).ConfigureAwait(false); + lastScriptStatus = await structuredScriptOrchestrator.Finish(lastScriptStatus, scriptExecutionCancellationToken).ConfigureAwait(false); + + OnScriptStatusResponseReceived(lastScriptStatus); return lastScriptStatus; } @@ -68,17 +70,17 @@ async Task ObserveUntilComplete( var iteration = 0; var cancellationIteration = 0; - while (GetState(lastStatusResponse) != ProcessState.Complete) + while (structuredScriptOrchestrator.GetState(lastStatusResponse) != ProcessState.Complete) { if (scriptExecutionCancellationToken.IsCancellationRequested) { - lastStatusResponse = await Cancel(lastStatusResponse, scriptExecutionCancellationToken).ConfigureAwait(false); + lastStatusResponse = await structuredScriptOrchestrator.Cancel(lastStatusResponse, scriptExecutionCancellationToken).ConfigureAwait(false); } else { try { - lastStatusResponse = await GetStatus(lastStatusResponse, scriptExecutionCancellationToken).ConfigureAwait(false); + lastStatusResponse = await structuredScriptOrchestrator.GetStatus(lastStatusResponse, scriptExecutionCancellationToken).ConfigureAwait(false); } catch (Exception) { @@ -93,7 +95,7 @@ async Task ObserveUntilComplete( OnScriptStatusResponseReceived(lastStatusResponse); - if (GetState(lastStatusResponse) == ProcessState.Complete) + if (structuredScriptOrchestrator.GetState(lastStatusResponse) == ProcessState.Complete) { continue; } @@ -115,25 +117,11 @@ await Task.Delay(scriptObserverBackOffStrategy.GetBackoff(++iteration), scriptEx return lastStatusResponse; } - protected abstract TStartCommand Map(ExecuteScriptCommand command); - protected abstract ScriptExecutionStatus MapToStatus(TScriptStatusResponse response); - - protected abstract ScriptExecutionResult MapToResult(TScriptStatusResponse response); - - protected abstract ProcessState GetState(TScriptStatusResponse response); - - protected abstract Task StartScript(TStartCommand command, CancellationToken scriptExecutionCancellationToken); - - protected abstract Task GetStatus(TScriptStatusResponse lastStatusResponse, CancellationToken scriptExecutionCancellationToken); - - protected abstract Task Cancel(TScriptStatusResponse lastStatusResponse, CancellationToken scriptExecutionCancellationToken); - - protected abstract Task Finish(TScriptStatusResponse lastStatusResponse, CancellationToken scriptExecutionCancellationToken); protected void OnScriptStatusResponseReceived(TScriptStatusResponse scriptStatusResponse) { - onScriptStatusResponseReceived(MapToStatus(scriptStatusResponse)); + onScriptStatusResponseReceived(structuredScriptOrchestrator.MapToStatus(scriptStatusResponse)); } } } \ No newline at end of file diff --git a/source/Octopus.Tentacle.Client/Scripts/ScriptOrchestratorFactory.cs b/source/Octopus.Tentacle.Client/Scripts/ScriptOrchestratorFactory.cs index 211db51f2..37c27da17 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ScriptOrchestratorFactory.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ScriptOrchestratorFactory.cs @@ -1,15 +1,10 @@ using System; using System.Threading; using System.Threading.Tasks; -using Halibut.ServiceModel; -using Octopus.Tentacle.Client.Capabilities; using Octopus.Tentacle.Client.Execution; -using Octopus.Tentacle.Client.Kubernetes; using Octopus.Tentacle.Client.Observability; -using Octopus.Tentacle.Contracts.Capabilities; -using Octopus.Tentacle.Contracts.ClientServices; +using Octopus.Tentacle.Client.ServiceHelpers; using Octopus.Tentacle.Contracts.Logging; -using Octopus.Tentacle.Contracts.Observability; namespace Octopus.Tentacle.Client.Scripts { @@ -23,19 +18,12 @@ class ScriptOrchestratorFactory : IScriptOrchestratorFactory readonly TimeSpan onCancellationAbandonCompleteScriptAfter; readonly ITentacleClientTaskLog logger; - readonly IAsyncClientScriptService clientScriptServiceV1; - readonly IAsyncClientScriptServiceV2 clientScriptServiceV2; - readonly IAsyncClientKubernetesScriptServiceV1Alpha clientKubernetesScriptServiceV1Alpha; - readonly IAsyncClientKubernetesScriptServiceV1 clientKubernetesScriptServiceV1; - readonly IAsyncClientCapabilitiesServiceV2 clientCapabilitiesServiceV2; + readonly ClientsHolder clientsHolder; + readonly TentacleClientOptions clientOptions; public ScriptOrchestratorFactory( - IAsyncClientScriptService clientScriptServiceV1, - IAsyncClientScriptServiceV2 clientScriptServiceV2, - IAsyncClientKubernetesScriptServiceV1Alpha clientKubernetesScriptServiceV1Alpha, - IAsyncClientKubernetesScriptServiceV1 clientKubernetesScriptServiceV1, - IAsyncClientCapabilitiesServiceV2 clientCapabilitiesServiceV2, + ClientsHolder clientsHolder, IScriptObserverBackoffStrategy scriptObserverBackOffStrategy, RpcCallExecutor rpcCallExecutor, ClientOperationMetricsBuilder clientOperationMetricsBuilder, @@ -45,11 +33,7 @@ public ScriptOrchestratorFactory( TentacleClientOptions clientOptions, ITentacleClientTaskLog logger) { - this.clientScriptServiceV1 = clientScriptServiceV1; - this.clientScriptServiceV2 = clientScriptServiceV2; - this.clientKubernetesScriptServiceV1Alpha = clientKubernetesScriptServiceV1Alpha; - this.clientKubernetesScriptServiceV1 = clientKubernetesScriptServiceV1; - this.clientCapabilitiesServiceV2 = clientCapabilitiesServiceV2; + this.clientsHolder = clientsHolder; this.scriptObserverBackOffStrategy = scriptObserverBackOffStrategy; this.rpcCallExecutor = rpcCallExecutor; this.clientOperationMetricsBuilder = clientOperationMetricsBuilder; @@ -65,30 +49,32 @@ public async Task CreateOrchestrator(CancellationToken canc ScriptServiceVersion scriptServiceToUse; try { - scriptServiceToUse = await DetermineScriptServiceVersionToUse(cancellationToken); + var scriptServicePicker = new ScriptServicePicker(clientsHolder.CapabilitiesServiceV2, logger, rpcCallExecutor, clientOptions, clientOperationMetricsBuilder); + scriptServiceToUse = await scriptServicePicker.DetermineScriptServiceVersionToUse(cancellationToken); } catch (Exception ex) when (cancellationToken.IsCancellationRequested) { throw new OperationCanceledException("Script execution was cancelled", ex); } + return CreateOrchestrator(scriptServiceToUse); + } + + public IStructuredScriptOrchestrator CreateOrchestrator(ScriptServiceVersion scriptServiceToUse) + { if (scriptServiceToUse == ScriptServiceVersion.ScriptServiceVersion1) { return new ScriptServiceV1Orchestrator( - clientScriptServiceV1, - scriptObserverBackOffStrategy, + clientsHolder.ScriptServiceV1, rpcCallExecutor, clientOperationMetricsBuilder, - onScriptStatusResponseReceived, - onScriptCompleted, - clientOptions, logger); } if (scriptServiceToUse == ScriptServiceVersion.ScriptServiceVersion2) { return new ScriptServiceV2Orchestrator( - clientScriptServiceV2, + clientsHolder.ScriptServiceV2, scriptObserverBackOffStrategy, rpcCallExecutor, clientOperationMetricsBuilder, @@ -102,7 +88,7 @@ public async Task CreateOrchestrator(CancellationToken canc if (scriptServiceToUse == ScriptServiceVersion.KubernetesScriptServiceVersion1Alpha) { return new KubernetesScriptServiceV1AlphaOrchestrator( - clientKubernetesScriptServiceV1Alpha, + clientsHolder.KubernetesScriptServiceV1Alpha, scriptObserverBackOffStrategy, rpcCallExecutor, clientOperationMetricsBuilder, @@ -116,7 +102,7 @@ public async Task CreateOrchestrator(CancellationToken canc if (scriptServiceToUse == ScriptServiceVersion.KubernetesScriptServiceVersion1) { return new KubernetesScriptServiceV1Orchestrator( - clientKubernetesScriptServiceV1, + clientsHolder.KubernetesScriptServiceV1, scriptObserverBackOffStrategy, rpcCallExecutor, clientOperationMetricsBuilder, @@ -130,72 +116,6 @@ public async Task CreateOrchestrator(CancellationToken canc throw new InvalidOperationException($"Unknown ScriptServiceVersion {scriptServiceToUse}"); } - async Task DetermineScriptServiceVersionToUse(CancellationToken cancellationToken) - { - logger.Verbose("Determining ScriptService version to use"); - - async Task GetCapabilitiesFunc(CancellationToken ct) - { - var result = await clientCapabilitiesServiceV2.GetCapabilitiesAsync(new HalibutProxyRequestOptions(ct)); - - return result; - } - - var tentacleCapabilities = await rpcCallExecutor.Execute( - retriesEnabled: clientOptions.RpcRetrySettings.RetriesEnabled, - RpcCall.Create(nameof(ICapabilitiesServiceV2.GetCapabilities)), - GetCapabilitiesFunc, - logger, - clientOperationMetricsBuilder, - cancellationToken); - - logger.Verbose($"Discovered Tentacle capabilities: {string.Join(",", tentacleCapabilities.SupportedCapabilities)}"); - - // Check if we support any kubernetes script service. - // It's implied (and tested) that GetCapabilities will only return Kubernetes or non-Kubernetes script services, never a mix - if (tentacleCapabilities.HasAnyKubernetesScriptService()) - { - return DetermineKubernetesScriptServiceVersionToUse(tentacleCapabilities); - } - - return DetermineShellScriptServiceVersionToUse(tentacleCapabilities); - } - - ScriptServiceVersion DetermineShellScriptServiceVersionToUse(CapabilitiesResponseV2 tentacleCapabilities) - { - if (tentacleCapabilities.HasScriptServiceV2()) - { - logger.Verbose("Using ScriptServiceV2"); - logger.Verbose(clientOptions.RpcRetrySettings.RetriesEnabled - ? $"RPC call retries are enabled. Retry timeout {rpcCallExecutor.RetryTimeout.TotalSeconds} seconds" - : "RPC call retries are disabled."); - return ScriptServiceVersion.ScriptServiceVersion2; - } - - logger.Verbose("RPC call retries are enabled but will not be used for Script Execution as a compatible ScriptService was not found. Please upgrade Tentacle to enable this feature."); - logger.Verbose("Using ScriptServiceV1"); - return ScriptServiceVersion.ScriptServiceVersion1; - } - - ScriptServiceVersion DetermineKubernetesScriptServiceVersionToUse(CapabilitiesResponseV2 tentacleCapabilities) - { - if (tentacleCapabilities.HasKubernetesScriptServiceV1()) - { - logger.Verbose($"Using KubernetesScriptServiceV1"); - logger.Verbose(clientOptions.RpcRetrySettings.RetriesEnabled - ? $"RPC call retries are enabled. Retry timeout {rpcCallExecutor.RetryTimeout.TotalSeconds} seconds" - : "RPC call retries are disabled."); - - return ScriptServiceVersion.KubernetesScriptServiceVersion1; - } - - logger.Verbose($"Using KubernetesScriptServiceV1Alpha"); - logger.Verbose(clientOptions.RpcRetrySettings.RetriesEnabled - ? $"RPC call retries are enabled. Retry timeout {rpcCallExecutor.RetryTimeout.TotalSeconds} seconds" - : "RPC call retries are disabled."); - - //this is the only supported kubernetes script service - return ScriptServiceVersion.KubernetesScriptServiceVersion1Alpha; - } + } } \ No newline at end of file diff --git a/source/Octopus.Tentacle.Client/Scripts/ScriptServicePicker.cs b/source/Octopus.Tentacle.Client/Scripts/ScriptServicePicker.cs new file mode 100644 index 000000000..672391fe1 --- /dev/null +++ b/source/Octopus.Tentacle.Client/Scripts/ScriptServicePicker.cs @@ -0,0 +1,100 @@ +using System.Threading; +using System.Threading.Tasks; +using Halibut.ServiceModel; +using Octopus.Tentacle.Client.Capabilities; +using Octopus.Tentacle.Client.Execution; +using Octopus.Tentacle.Client.Kubernetes; +using Octopus.Tentacle.Client.Observability; +using Octopus.Tentacle.Contracts.Capabilities; +using Octopus.Tentacle.Contracts.ClientServices; +using Octopus.Tentacle.Contracts.Logging; +using Octopus.Tentacle.Contracts.Observability; + +namespace Octopus.Tentacle.Client.Scripts +{ + public class ScriptServicePicker + { + readonly ITentacleClientTaskLog logger; + readonly IAsyncClientCapabilitiesServiceV2 clientCapabilitiesServiceV2; + readonly RpcCallExecutor rpcCallExecutor; + readonly TentacleClientOptions clientOptions; + readonly ClientOperationMetricsBuilder clientOperationMetricsBuilder; + + internal ScriptServicePicker(IAsyncClientCapabilitiesServiceV2 clientCapabilitiesServiceV2, ITentacleClientTaskLog logger, RpcCallExecutor rpcCallExecutor, TentacleClientOptions clientOptions, ClientOperationMetricsBuilder clientOperationMetricsBuilder) + { + this.clientCapabilitiesServiceV2 = clientCapabilitiesServiceV2; + this.logger = logger; + this.rpcCallExecutor = rpcCallExecutor; + this.clientOptions = clientOptions; + this.clientOperationMetricsBuilder = clientOperationMetricsBuilder; + } + + public async Task DetermineScriptServiceVersionToUse(CancellationToken cancellationToken) + { + logger.Verbose("Determining ScriptService version to use"); + + async Task GetCapabilitiesFunc(CancellationToken ct) + { + var result = await clientCapabilitiesServiceV2.GetCapabilitiesAsync(new HalibutProxyRequestOptions(ct)); + + return result; + } + + var tentacleCapabilities = await rpcCallExecutor.Execute( + retriesEnabled: clientOptions.RpcRetrySettings.RetriesEnabled, + RpcCall.Create(nameof(ICapabilitiesServiceV2.GetCapabilities)), + GetCapabilitiesFunc, + logger, + clientOperationMetricsBuilder, + cancellationToken); + + logger.Verbose($"Discovered Tentacle capabilities: {string.Join(",", tentacleCapabilities.SupportedCapabilities)}"); + + // Check if we support any kubernetes script service. + // It's implied (and tested) that GetCapabilities will only return Kubernetes or non-Kubernetes script services, never a mix + if (tentacleCapabilities.HasAnyKubernetesScriptService()) + { + return DetermineKubernetesScriptServiceVersionToUse(tentacleCapabilities); + } + + return DetermineShellScriptServiceVersionToUse(tentacleCapabilities); + } + + ScriptServiceVersion DetermineShellScriptServiceVersionToUse(CapabilitiesResponseV2 tentacleCapabilities) + { + if (tentacleCapabilities.HasScriptServiceV2()) + { + logger.Verbose("Using ScriptServiceV2"); + logger.Verbose(clientOptions.RpcRetrySettings.RetriesEnabled + ? $"RPC call retries are enabled. Retry timeout {rpcCallExecutor.RetryTimeout.TotalSeconds} seconds" + : "RPC call retries are disabled."); + return ScriptServiceVersion.ScriptServiceVersion2; + } + + logger.Verbose("RPC call retries are enabled but will not be used for Script Execution as a compatible ScriptService was not found. Please upgrade Tentacle to enable this feature."); + logger.Verbose("Using ScriptServiceV1"); + return ScriptServiceVersion.ScriptServiceVersion1; + } + + ScriptServiceVersion DetermineKubernetesScriptServiceVersionToUse(CapabilitiesResponseV2 tentacleCapabilities) + { + if (tentacleCapabilities.HasKubernetesScriptServiceV1()) + { + logger.Verbose($"Using KubernetesScriptServiceV1"); + logger.Verbose(clientOptions.RpcRetrySettings.RetriesEnabled + ? $"RPC call retries are enabled. Retry timeout {rpcCallExecutor.RetryTimeout.TotalSeconds} seconds" + : "RPC call retries are disabled."); + + return ScriptServiceVersion.KubernetesScriptServiceVersion1; + } + + logger.Verbose($"Using KubernetesScriptServiceV1Alpha"); + logger.Verbose(clientOptions.RpcRetrySettings.RetriesEnabled + ? $"RPC call retries are enabled. Retry timeout {rpcCallExecutor.RetryTimeout.TotalSeconds} seconds" + : "RPC call retries are disabled."); + + //this is the only supported kubernetes script service + return ScriptServiceVersion.KubernetesScriptServiceVersion1Alpha; + } + } +} \ No newline at end of file diff --git a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs index 12b42f64f..96cdad24f 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs @@ -13,7 +13,7 @@ namespace Octopus.Tentacle.Client.Scripts { - class ScriptServiceV1Orchestrator : ObservingScriptOrchestrator + class ScriptServiceV1Orchestrator : IStructuredScriptOrchestrator { readonly RpcCallExecutor rpcCallExecutor; @@ -24,17 +24,9 @@ class ScriptServiceV1Orchestrator : ObservingScriptOrchestrator new(response.Logs); + public ScriptExecutionStatus MapToStatus(ScriptStatusResponse response) => new(response.Logs); - protected override ScriptExecutionResult MapToResult(ScriptStatusResponse response) - => new(response.State, response.ExitCode); + public ScriptExecutionResult MapToResult(ScriptStatusResponse response) => new(response.State, response.ExitCode); - protected override ProcessState GetState(ScriptStatusResponse response) => response.State; + public ProcessState GetState(ScriptStatusResponse response) => response.State; - protected override async Task StartScript(StartScriptCommand command, CancellationToken scriptExecutionCancellationToken) + public async Task StartScript(StartScriptCommand command, CancellationToken scriptExecutionCancellationToken) { var scriptTicket = await rpcCallExecutor.ExecuteWithNoRetries( RpcCall.Create(nameof(IScriptService.StartScript)), @@ -83,7 +73,7 @@ protected override async Task StartScript(StartScriptComma return new ScriptStatusResponse(scriptTicket, ProcessState.Pending, 0, new List(), 0); } - protected override async Task GetStatus(ScriptStatusResponse lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task GetStatus(ScriptStatusResponse lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { var scriptStatusResponseV1 = await rpcCallExecutor.ExecuteWithNoRetries( RpcCall.Create(nameof(IScriptService.GetStatus)), @@ -101,7 +91,7 @@ protected override async Task GetStatus(ScriptStatusRespon return scriptStatusResponseV1; } - protected override async Task Cancel(ScriptStatusResponse lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task Cancel(ScriptStatusResponse lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { var response = await rpcCallExecutor.ExecuteWithNoRetries( RpcCall.Create(nameof(IScriptService.CancelScript)), @@ -119,7 +109,7 @@ protected override async Task Cancel(ScriptStatusResponse return response; } - protected override async Task Finish(ScriptStatusResponse lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task Finish(ScriptStatusResponse lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { var response = await rpcCallExecutor.ExecuteWithNoRetries( RpcCall.Create(nameof(IScriptService.CompleteScript)), @@ -134,8 +124,6 @@ protected override async Task Finish(ScriptStatusResponse clientOperationMetricsBuilder, CancellationToken.None).ConfigureAwait(false); - OnScriptStatusResponseReceived(response); - return response; } } diff --git a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs index 149f7a6ae..64717c83e 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs @@ -15,37 +15,32 @@ namespace Octopus.Tentacle.Client.Scripts { - class ScriptServiceV2Orchestrator : ObservingScriptOrchestrator + class ScriptServiceV2Orchestrator : IStructuredScriptOrchestrator { readonly IAsyncClientScriptServiceV2 clientScriptServiceV2; readonly RpcCallExecutor rpcCallExecutor; readonly ClientOperationMetricsBuilder clientOperationMetricsBuilder; readonly TimeSpan onCancellationAbandonCompleteScriptAfter; readonly ITentacleClientTaskLog logger; + readonly TentacleClientOptions clientOptions; public ScriptServiceV2Orchestrator( IAsyncClientScriptServiceV2 clientScriptServiceV2, - IScriptObserverBackoffStrategy scriptObserverBackOffStrategy, RpcCallExecutor rpcCallExecutor, ClientOperationMetricsBuilder clientOperationMetricsBuilder, - OnScriptStatusResponseReceived onScriptStatusResponseReceived, - OnScriptCompleted onScriptCompleted, TimeSpan onCancellationAbandonCompleteScriptAfter, TentacleClientOptions clientOptions, ITentacleClientTaskLog logger) - : base(scriptObserverBackOffStrategy, - onScriptStatusResponseReceived, - onScriptCompleted, - clientOptions) { this.clientScriptServiceV2 = clientScriptServiceV2; this.rpcCallExecutor = rpcCallExecutor; this.clientOperationMetricsBuilder = clientOperationMetricsBuilder; this.onCancellationAbandonCompleteScriptAfter = onCancellationAbandonCompleteScriptAfter; + this.clientOptions = clientOptions; this.logger = logger; } - protected override StartScriptCommandV2 Map(ExecuteScriptCommand command) + public StartScriptCommandV2 Map(ExecuteScriptCommand command) { if (command is not ExecuteShellScriptCommand shellScriptCommand) throw new InvalidOperationException($"{nameof(ScriptServiceV2Orchestrator)} only supports commands of type {nameof(ExecuteShellScriptCommand)}."); @@ -63,15 +58,15 @@ protected override StartScriptCommandV2 Map(ExecuteScriptCommand command) shellScriptCommand.Files.ToArray()); } - protected override ScriptExecutionStatus MapToStatus(ScriptStatusResponseV2 response) + public ScriptExecutionStatus MapToStatus(ScriptStatusResponseV2 response) => new(response.Logs); - protected override ScriptExecutionResult MapToResult(ScriptStatusResponseV2 response) + public ScriptExecutionResult MapToResult(ScriptStatusResponseV2 response) => new(response.State, response.ExitCode); - protected override ProcessState GetState(ScriptStatusResponseV2 response) => response.State; + public ProcessState GetState(ScriptStatusResponseV2 response) => response.State; - protected override async Task StartScript(StartScriptCommandV2 command, CancellationToken scriptExecutionCancellationToken) + public async Task StartScript(StartScriptCommandV2 command, CancellationToken scriptExecutionCancellationToken) { ScriptStatusResponseV2 scriptStatusResponse; var startScriptCallsConnectedCount = 0; @@ -95,7 +90,7 @@ void OnErrorAction(Exception ex) } scriptStatusResponse = await rpcCallExecutor.Execute( - retriesEnabled: ClientOptions.RpcRetrySettings.RetriesEnabled, + retriesEnabled: clientOptions.RpcRetrySettings.RetriesEnabled, RpcCall.Create(nameof(IScriptServiceV2.StartScript)), StartScriptAction, OnErrorAction, @@ -146,7 +141,7 @@ void OnErrorAction(Exception ex) return scriptStatusResponse; } - protected override async Task GetStatus(ScriptStatusResponseV2 lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task GetStatus(ScriptStatusResponseV2 lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { try { @@ -159,7 +154,7 @@ async Task GetStatusAction(CancellationToken ct) } return await rpcCallExecutor.Execute( - retriesEnabled: ClientOptions.RpcRetrySettings.RetriesEnabled, + retriesEnabled: clientOptions.RpcRetrySettings.RetriesEnabled, RpcCall.Create(nameof(IScriptServiceV2.GetStatus)), GetStatusAction, logger, @@ -173,7 +168,7 @@ async Task GetStatusAction(CancellationToken ct) } } - protected override async Task Cancel(ScriptStatusResponseV2 lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task Cancel(ScriptStatusResponseV2 lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { async Task CancelScriptAction(CancellationToken ct) { @@ -188,7 +183,7 @@ async Task CancelScriptAction(CancellationToken ct) // We could potentially reduce the time to failure by not retrying the cancel RPC Call if the previous RPC call was already triggering RPC Retries. return await rpcCallExecutor.Execute( - retriesEnabled: ClientOptions.RpcRetrySettings.RetriesEnabled, + retriesEnabled: clientOptions.RpcRetrySettings.RetriesEnabled, RpcCall.Create(nameof(IScriptServiceV2.CancelScript)), CancelScriptAction, logger, @@ -197,7 +192,7 @@ async Task CancelScriptAction(CancellationToken ct) CancellationToken.None).ConfigureAwait(false); } - protected override async Task Finish(ScriptStatusResponseV2 lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task Finish(ScriptStatusResponseV2 lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { try { diff --git a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceVersion.cs b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceVersion.cs index 035ea0f53..4be59fbed 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceVersion.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceVersion.cs @@ -1,6 +1,7 @@ namespace Octopus.Tentacle.Client.Scripts { - record ScriptServiceVersion(string Value) + // TODO should not be public. + public record ScriptServiceVersion(string Value) { public static ScriptServiceVersion ScriptServiceVersion1 = new(nameof(ScriptServiceVersion1)); public static ScriptServiceVersion ScriptServiceVersion2 = new(nameof(ScriptServiceVersion2)); diff --git a/source/Octopus.Tentacle.Client/ServiceHelpers/ClientsHolder.cs b/source/Octopus.Tentacle.Client/ServiceHelpers/ClientsHolder.cs new file mode 100644 index 000000000..a178edb57 --- /dev/null +++ b/source/Octopus.Tentacle.Client/ServiceHelpers/ClientsHolder.cs @@ -0,0 +1,40 @@ +using Halibut; +using Octopus.Tentacle.Contracts; +using Octopus.Tentacle.Contracts.Capabilities; +using Octopus.Tentacle.Contracts.ClientServices; +using Octopus.Tentacle.Contracts.KubernetesScriptServiceV1; +using Octopus.Tentacle.Contracts.KubernetesScriptServiceV1Alpha; +using Octopus.Tentacle.Contracts.ScriptServiceV2; + +namespace Octopus.Tentacle.Client.ServiceHelpers +{ + public class ClientsHolder + { + public IAsyncClientScriptService ScriptServiceV1 { get; } + public IAsyncClientScriptServiceV2 ScriptServiceV2 { get; } + public IAsyncClientKubernetesScriptServiceV1Alpha KubernetesScriptServiceV1Alpha { get; } + public IAsyncClientKubernetesScriptServiceV1 KubernetesScriptServiceV1 { get; } + public IAsyncClientFileTransferService ClientFileTransferServiceV1 { get; } + public IAsyncClientCapabilitiesServiceV2 CapabilitiesServiceV2 { get; } + + internal ClientsHolder(IHalibutRuntime halibutRuntime, ServiceEndPoint serviceEndPoint, ITentacleServiceDecoratorFactory? tentacleServicesDecoratorFactory) + { + ScriptServiceV1 = halibutRuntime.CreateAsyncClient(serviceEndPoint); + ScriptServiceV2 = halibutRuntime.CreateAsyncClient(serviceEndPoint); + KubernetesScriptServiceV1Alpha = halibutRuntime.CreateAsyncClient(serviceEndPoint); + KubernetesScriptServiceV1 = halibutRuntime.CreateAsyncClient(serviceEndPoint); + ClientFileTransferServiceV1 = halibutRuntime.CreateAsyncClient(serviceEndPoint); + CapabilitiesServiceV2 = halibutRuntime.CreateAsyncClient(serviceEndPoint).WithBackwardsCompatability(); + + if (tentacleServicesDecoratorFactory != null) + { + ScriptServiceV1 = tentacleServicesDecoratorFactory.Decorate(ScriptServiceV1); + ScriptServiceV2 = tentacleServicesDecoratorFactory.Decorate(ScriptServiceV2); + KubernetesScriptServiceV1Alpha = tentacleServicesDecoratorFactory.Decorate(KubernetesScriptServiceV1Alpha); + KubernetesScriptServiceV1 = tentacleServicesDecoratorFactory.Decorate(KubernetesScriptServiceV1); + ClientFileTransferServiceV1 = tentacleServicesDecoratorFactory.Decorate(ClientFileTransferServiceV1); + CapabilitiesServiceV2 = tentacleServicesDecoratorFactory.Decorate(CapabilitiesServiceV2); + } + } + } +} \ No newline at end of file diff --git a/source/Octopus.Tentacle.Client/TentacleClient.cs b/source/Octopus.Tentacle.Client/TentacleClient.cs index bbd340ccf..8fe0b85e5 100644 --- a/source/Octopus.Tentacle.Client/TentacleClient.cs +++ b/source/Octopus.Tentacle.Client/TentacleClient.cs @@ -8,6 +8,7 @@ using Octopus.Tentacle.Client.Observability; using Octopus.Tentacle.Client.Scripts; using Octopus.Tentacle.Client.Scripts.Models; +using Octopus.Tentacle.Client.ServiceHelpers; using Octopus.Tentacle.Contracts; using Octopus.Tentacle.Contracts.Capabilities; using Octopus.Tentacle.Contracts.ClientServices; @@ -25,14 +26,9 @@ public class TentacleClient : ITentacleClient readonly IScriptObserverBackoffStrategy scriptObserverBackOffStrategy; readonly ITentacleClientObserver tentacleClientObserver; readonly RpcCallExecutor rpcCallExecutor; - - readonly IAsyncClientScriptService scriptServiceV1; - readonly IAsyncClientScriptServiceV2 scriptServiceV2; - readonly IAsyncClientKubernetesScriptServiceV1Alpha kubernetesScriptServiceV1Alpha; - readonly IAsyncClientKubernetesScriptServiceV1 kubernetesScriptServiceV1; - readonly IAsyncClientFileTransferService clientFileTransferServiceV1; - readonly IAsyncClientCapabilitiesServiceV2 capabilitiesServiceV2; + readonly TentacleClientOptions clientOptions; + readonly ClientsHolder clientsHolder; public static void CacheServiceWasNotFoundResponseMessages(IHalibutRuntime halibutRuntime) { @@ -79,22 +75,7 @@ internal TentacleClient( throw new ArgumentException("Ensure that TentacleClient.CacheServiceWasNotFoundResponseMessages has been called for the HalibutRuntime", nameof(halibutRuntime)); } - scriptServiceV1 = halibutRuntime.CreateAsyncClient(serviceEndPoint); - scriptServiceV2 = halibutRuntime.CreateAsyncClient(serviceEndPoint); - kubernetesScriptServiceV1Alpha = halibutRuntime.CreateAsyncClient(serviceEndPoint); - kubernetesScriptServiceV1 = halibutRuntime.CreateAsyncClient(serviceEndPoint); - clientFileTransferServiceV1 = halibutRuntime.CreateAsyncClient(serviceEndPoint); - capabilitiesServiceV2 = halibutRuntime.CreateAsyncClient(serviceEndPoint).WithBackwardsCompatability(); - - if (tentacleServicesDecoratorFactory != null) - { - scriptServiceV1 = tentacleServicesDecoratorFactory.Decorate(scriptServiceV1); - scriptServiceV2 = tentacleServicesDecoratorFactory.Decorate(scriptServiceV2); - kubernetesScriptServiceV1Alpha = tentacleServicesDecoratorFactory.Decorate(kubernetesScriptServiceV1Alpha); - kubernetesScriptServiceV1 = tentacleServicesDecoratorFactory.Decorate(kubernetesScriptServiceV1); - clientFileTransferServiceV1 = tentacleServicesDecoratorFactory.Decorate(clientFileTransferServiceV1); - capabilitiesServiceV2 = tentacleServicesDecoratorFactory.Decorate(capabilitiesServiceV2); - } + clientsHolder = new ClientsHolder(halibutRuntime, serviceEndPoint, tentacleServicesDecoratorFactory); rpcCallExecutor = RpcCallExecutorFactory.Create(this.clientOptions.RpcRetrySettings.RetryDuration, this.tentacleClientObserver); } @@ -108,7 +89,7 @@ public async Task UploadFile(string fileName, string path, DataStr async Task UploadFileAction(CancellationToken ct) { logger.Info($"Beginning upload of {fileName} to Tentacle"); - var result = await clientFileTransferServiceV1.UploadFileAsync(path, package, new HalibutProxyRequestOptions(ct)); + var result = await clientsHolder.ClientFileTransferServiceV1.UploadFileAsync(path, package, new HalibutProxyRequestOptions(ct)); logger.Info("Upload complete"); return result; @@ -143,7 +124,7 @@ async Task UploadFileAction(CancellationToken ct) async Task DownloadFileAction(CancellationToken ct) { logger.Info($"Beginning download of {Path.GetFileName(remotePath)} from Tentacle"); - var result = await clientFileTransferServiceV1.DownloadFileAsync(remotePath, new HalibutProxyRequestOptions(ct)); + var result = await clientsHolder.ClientFileTransferServiceV1.DownloadFileAsync(remotePath, new HalibutProxyRequestOptions(ct)); logger.Info("Download complete"); return result; @@ -182,11 +163,7 @@ public async Task ExecuteScript(ExecuteScriptCommand exec try { var factory = new ScriptOrchestratorFactory( - scriptServiceV1, - scriptServiceV2, - kubernetesScriptServiceV1Alpha, - kubernetesScriptServiceV1, - capabilitiesServiceV2, + clientsHolder, scriptObserverBackOffStrategy, rpcCallExecutor, operationMetricsBuilder, diff --git a/source/Octopus.Tentacle.Contracts/ScriptStatus.cs b/source/Octopus.Tentacle.Contracts/ScriptStatus.cs new file mode 100644 index 000000000..e56108d68 --- /dev/null +++ b/source/Octopus.Tentacle.Contracts/ScriptStatus.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; + +namespace Octopus.Tentacle.Contracts +{ + public class ScriptStatus + { + ProcessState state; + int? exitCode; + List logs; + + public ScriptStatus(ProcessState state, int? exitCode, List logs) + { + this.state = state; + this.exitCode = exitCode; + this.logs = logs; + } + } +} \ No newline at end of file From c912d0873c4701e358aa900b46dd73dc919fe1c6 Mon Sep 17 00:00:00 2001 From: Luke Butters Date: Thu, 20 Jun 2024 12:40:51 +1000 Subject: [PATCH 02/23] Drop start script type --- .../Scripts/IScriptOrchestrator.cs | 5 +-- ...ernetesScriptServiceV1AlphaOrchestrator.cs | 37 +++++++++---------- .../KubernetesScriptServiceV1Orchestrator.cs | 37 +++++++++---------- .../Scripts/ObservingScriptOrchestrator.cs | 12 +++--- .../Scripts/ScriptOrchestratorFactory.cs | 9 ----- .../Scripts/ScriptServiceV1Orchestrator.cs | 7 ++-- .../Scripts/ScriptServiceV2Orchestrator.cs | 10 +++-- .../ShortCutTakenHere.cs | 7 ++++ 8 files changed, 59 insertions(+), 65 deletions(-) create mode 100644 source/Octopus.Tentacle.Client/ShortCutTakenHere.cs diff --git a/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs index cdc70ffdd..baf150d83 100644 --- a/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs @@ -11,12 +11,11 @@ public interface IScriptOrchestrator Task ExecuteScript(ExecuteScriptCommand command, CancellationToken scriptExecutionCancellationToken); } - public interface IStructuredScriptOrchestrator { - TStartCommand Map(ExecuteScriptCommand command); + public interface IStructuredScriptOrchestrator { ScriptExecutionStatus MapToStatus(TScriptStatusResponse response); ScriptExecutionResult MapToResult(TScriptStatusResponse response); ProcessState GetState(TScriptStatusResponse response); - Task StartScript(TStartCommand command, CancellationToken scriptExecutionCancellationToken); + Task StartScript(ExecuteScriptCommand command, CancellationToken scriptExecutionCancellationToken); Task GetStatus(TScriptStatusResponse lastStatusResponse, CancellationToken scriptExecutionCancellationToken); Task Cancel(TScriptStatusResponse lastStatusResponse, CancellationToken scriptExecutionCancellationToken); Task Finish(TScriptStatusResponse lastStatusResponse, CancellationToken scriptExecutionCancellationToken); diff --git a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs index b35234d4a..2cdd89785 100644 --- a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs @@ -15,37 +15,32 @@ namespace Octopus.Tentacle.Client.Scripts { - class KubernetesScriptServiceV1AlphaOrchestrator : ObservingScriptOrchestrator + class KubernetesScriptServiceV1AlphaOrchestrator : IStructuredScriptOrchestrator { readonly IAsyncClientKubernetesScriptServiceV1Alpha clientKubernetesScriptServiceV1Alpha; readonly RpcCallExecutor rpcCallExecutor; readonly ClientOperationMetricsBuilder clientOperationMetricsBuilder; readonly TimeSpan onCancellationAbandonCompleteScriptAfter; readonly ITentacleClientTaskLog logger; + readonly TentacleClientOptions clientOptions; public KubernetesScriptServiceV1AlphaOrchestrator( IAsyncClientKubernetesScriptServiceV1Alpha clientKubernetesScriptServiceV1Alpha, - IScriptObserverBackoffStrategy scriptObserverBackOffStrategy, RpcCallExecutor rpcCallExecutor, ClientOperationMetricsBuilder clientOperationMetricsBuilder, - OnScriptStatusResponseReceived onScriptStatusResponseReceived, - OnScriptCompleted onScriptCompleted, TimeSpan onCancellationAbandonCompleteScriptAfter, TentacleClientOptions clientOptions, ITentacleClientTaskLog logger) - : base(scriptObserverBackOffStrategy, - onScriptStatusResponseReceived, - onScriptCompleted, - clientOptions) { this.clientKubernetesScriptServiceV1Alpha = clientKubernetesScriptServiceV1Alpha; this.rpcCallExecutor = rpcCallExecutor; this.clientOperationMetricsBuilder = clientOperationMetricsBuilder; this.onCancellationAbandonCompleteScriptAfter = onCancellationAbandonCompleteScriptAfter; this.logger = logger; + this.clientOptions = clientOptions; } - protected override StartKubernetesScriptCommandV1Alpha Map(ExecuteScriptCommand command) + StartKubernetesScriptCommandV1Alpha Map(ExecuteScriptCommand command) { if (command is not ExecuteKubernetesScriptCommand kubernetesScriptCommand) throw new InvalidOperationException($"Invalid execute script command received. Expected {nameof(ExecuteKubernetesScriptCommand)}, but received {command.GetType().Name}."); @@ -72,16 +67,17 @@ protected override StartKubernetesScriptCommandV1Alpha Map(ExecuteScriptCommand kubernetesScriptCommand.Files.ToArray()); } - protected override ScriptExecutionStatus MapToStatus(KubernetesScriptStatusResponseV1Alpha response) + public ScriptExecutionStatus MapToStatus(KubernetesScriptStatusResponseV1Alpha response) => new(response.Logs); - protected override ScriptExecutionResult MapToResult(KubernetesScriptStatusResponseV1Alpha response) + public ScriptExecutionResult MapToResult(KubernetesScriptStatusResponseV1Alpha response) => new(response.State, response.ExitCode); - protected override ProcessState GetState(KubernetesScriptStatusResponseV1Alpha response) => response.State; + public ProcessState GetState(KubernetesScriptStatusResponseV1Alpha response) => response.State; - protected override async Task StartScript(StartKubernetesScriptCommandV1Alpha command, CancellationToken scriptExecutionCancellationToken) + public async Task StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) { + var command = Map(executeScriptCommand); KubernetesScriptStatusResponseV1Alpha scriptStatusResponse; var startScriptCallsConnectedCount = 0; try @@ -104,7 +100,7 @@ void OnErrorAction(Exception ex) } scriptStatusResponse = await rpcCallExecutor.Execute( - retriesEnabled: ClientOptions.RpcRetrySettings.RetriesEnabled, + retriesEnabled: clientOptions.RpcRetrySettings.RetriesEnabled, RpcCall.Create(nameof(IKubernetesScriptServiceV1Alpha.StartScript)), StartScriptAction, OnErrorAction, @@ -135,7 +131,8 @@ void OnErrorAction(Exception ex) try { - await ObserveUntilCompleteThenFinish(scriptStatusResponse, scriptExecutionCancellationToken).ConfigureAwait(false); + new ShortCutTakenHere(); + //await ObserveUntilCompleteThenFinish(scriptStatusResponse, scriptExecutionCancellationToken).ConfigureAwait(false); } catch (Exception observerUntilCompleteException) { @@ -155,7 +152,7 @@ void OnErrorAction(Exception ex) return scriptStatusResponse; } - protected override async Task GetStatus(KubernetesScriptStatusResponseV1Alpha lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task GetStatus(KubernetesScriptStatusResponseV1Alpha lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { try { @@ -168,7 +165,7 @@ async Task GetStatusAction(CancellationTo } return await rpcCallExecutor.Execute( - retriesEnabled: ClientOptions.RpcRetrySettings.RetriesEnabled, + retriesEnabled: clientOptions.RpcRetrySettings.RetriesEnabled, RpcCall.Create(nameof(IKubernetesScriptServiceV1Alpha.GetStatus)), GetStatusAction, logger, @@ -182,7 +179,7 @@ async Task GetStatusAction(CancellationTo } } - protected override async Task Cancel(KubernetesScriptStatusResponseV1Alpha lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task Cancel(KubernetesScriptStatusResponseV1Alpha lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { async Task CancelScriptAction(CancellationToken ct) { @@ -197,7 +194,7 @@ async Task CancelScriptAction(Cancellatio // We could potentially reduce the time to failure by not retrying the cancel RPC Call if the previous RPC call was already triggering RPC Retries. return await rpcCallExecutor.Execute( - retriesEnabled: ClientOptions.RpcRetrySettings.RetriesEnabled, + retriesEnabled: clientOptions.RpcRetrySettings.RetriesEnabled, RpcCall.Create(nameof(IKubernetesScriptServiceV1Alpha.CancelScript)), CancelScriptAction, logger, @@ -206,7 +203,7 @@ async Task CancelScriptAction(Cancellatio CancellationToken.None).ConfigureAwait(false); } - protected override async Task Finish(KubernetesScriptStatusResponseV1Alpha lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task Finish(KubernetesScriptStatusResponseV1Alpha lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { try { diff --git a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1Orchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1Orchestrator.cs index b4e614c57..1917acb34 100644 --- a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1Orchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1Orchestrator.cs @@ -15,37 +15,32 @@ namespace Octopus.Tentacle.Client.Scripts { - class KubernetesScriptServiceV1Orchestrator : ObservingScriptOrchestrator + class KubernetesScriptServiceV1Orchestrator : IStructuredScriptOrchestrator { readonly IAsyncClientKubernetesScriptServiceV1 clientKubernetesScriptServiceV1; readonly RpcCallExecutor rpcCallExecutor; readonly ClientOperationMetricsBuilder clientOperationMetricsBuilder; readonly TimeSpan onCancellationAbandonCompleteScriptAfter; readonly ITentacleClientTaskLog logger; + readonly TentacleClientOptions clientOptions; public KubernetesScriptServiceV1Orchestrator( IAsyncClientKubernetesScriptServiceV1 clientKubernetesScriptServiceV1, - IScriptObserverBackoffStrategy scriptObserverBackOffStrategy, RpcCallExecutor rpcCallExecutor, ClientOperationMetricsBuilder clientOperationMetricsBuilder, - OnScriptStatusResponseReceived onScriptStatusResponseReceived, - OnScriptCompleted onScriptCompleted, TimeSpan onCancellationAbandonCompleteScriptAfter, TentacleClientOptions clientOptions, ITentacleClientTaskLog logger) - : base(scriptObserverBackOffStrategy, - onScriptStatusResponseReceived, - onScriptCompleted, - clientOptions) { this.clientKubernetesScriptServiceV1 = clientKubernetesScriptServiceV1; this.rpcCallExecutor = rpcCallExecutor; this.clientOperationMetricsBuilder = clientOperationMetricsBuilder; this.onCancellationAbandonCompleteScriptAfter = onCancellationAbandonCompleteScriptAfter; + this.clientOptions = clientOptions; this.logger = logger; } - protected override StartKubernetesScriptCommandV1 Map(ExecuteScriptCommand command) + StartKubernetesScriptCommandV1 Map(ExecuteScriptCommand command) { if (command is not ExecuteKubernetesScriptCommand kubernetesScriptCommand) throw new InvalidOperationException($"Invalid execute script command received. Expected {nameof(ExecuteKubernetesScriptCommand)}, but received {command.GetType().Name}."); @@ -73,16 +68,17 @@ protected override StartKubernetesScriptCommandV1 Map(ExecuteScriptCommand comma kubernetesScriptCommand.IsRawScript); } - protected override ScriptExecutionStatus MapToStatus(KubernetesScriptStatusResponseV1 response) + public ScriptExecutionStatus MapToStatus(KubernetesScriptStatusResponseV1 response) => new(response.Logs); - protected override ScriptExecutionResult MapToResult(KubernetesScriptStatusResponseV1 response) + public ScriptExecutionResult MapToResult(KubernetesScriptStatusResponseV1 response) => new(response.State, response.ExitCode); - protected override ProcessState GetState(KubernetesScriptStatusResponseV1 response) => response.State; + public ProcessState GetState(KubernetesScriptStatusResponseV1 response) => response.State; - protected override async Task StartScript(StartKubernetesScriptCommandV1 command, CancellationToken scriptExecutionCancellationToken) + public async Task StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) { + var command = Map(executeScriptCommand); KubernetesScriptStatusResponseV1 scriptStatusResponse; var startScriptCallsConnectedCount = 0; try @@ -105,7 +101,7 @@ void OnErrorAction(Exception ex) } scriptStatusResponse = await rpcCallExecutor.Execute( - retriesEnabled: ClientOptions.RpcRetrySettings.RetriesEnabled, + retriesEnabled: clientOptions.RpcRetrySettings.RetriesEnabled, RpcCall.Create(nameof(IKubernetesScriptServiceV1.StartScript)), StartScriptAction, OnErrorAction, @@ -136,7 +132,8 @@ void OnErrorAction(Exception ex) try { - await ObserveUntilCompleteThenFinish(scriptStatusResponse, scriptExecutionCancellationToken).ConfigureAwait(false); + new ShortCutTakenHere(); + //await ObserveUntilCompleteThenFinish(scriptStatusResponse, scriptExecutionCancellationToken).ConfigureAwait(false); } catch (Exception observerUntilCompleteException) { @@ -156,7 +153,7 @@ void OnErrorAction(Exception ex) return scriptStatusResponse; } - protected override async Task GetStatus(KubernetesScriptStatusResponseV1 lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task GetStatus(KubernetesScriptStatusResponseV1 lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { try { @@ -169,7 +166,7 @@ async Task GetStatusAction(CancellationToken c } return await rpcCallExecutor.Execute( - retriesEnabled: ClientOptions.RpcRetrySettings.RetriesEnabled, + retriesEnabled: clientOptions.RpcRetrySettings.RetriesEnabled, RpcCall.Create(nameof(IKubernetesScriptServiceV1.GetStatus)), GetStatusAction, logger, @@ -183,7 +180,7 @@ async Task GetStatusAction(CancellationToken c } } - protected override async Task Cancel(KubernetesScriptStatusResponseV1 lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task Cancel(KubernetesScriptStatusResponseV1 lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { async Task CancelScriptAction(CancellationToken ct) { @@ -198,7 +195,7 @@ async Task CancelScriptAction(CancellationToke // We could potentially reduce the time to failure by not retrying the cancel RPC Call if the previous RPC call was already triggering RPC Retries. return await rpcCallExecutor.Execute( - retriesEnabled: ClientOptions.RpcRetrySettings.RetriesEnabled, + retriesEnabled: clientOptions.RpcRetrySettings.RetriesEnabled, RpcCall.Create(nameof(IKubernetesScriptServiceV1.CancelScript)), CancelScriptAction, logger, @@ -207,7 +204,7 @@ async Task CancelScriptAction(CancellationToke CancellationToken.None).ConfigureAwait(false); } - protected override async Task Finish(KubernetesScriptStatusResponseV1 lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task Finish(KubernetesScriptStatusResponseV1 lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { try { diff --git a/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs index 817d180cc..e6e9e32b5 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs @@ -6,19 +6,19 @@ namespace Octopus.Tentacle.Client.Scripts { - public sealed class ObservingScriptOrchestrator : IScriptOrchestrator + public sealed class ObservingScriptOrchestrator : IScriptOrchestrator { readonly IScriptObserverBackoffStrategy scriptObserverBackOffStrategy; readonly OnScriptStatusResponseReceived onScriptStatusResponseReceived; readonly OnScriptCompleted onScriptCompleted; - IStructuredScriptOrchestrator structuredScriptOrchestrator; + IStructuredScriptOrchestrator structuredScriptOrchestrator; public ObservingScriptOrchestrator( IScriptObserverBackoffStrategy scriptObserverBackOffStrategy, OnScriptStatusResponseReceived onScriptStatusResponseReceived, OnScriptCompleted onScriptCompleted, - IStructuredScriptOrchestrator structuredScriptOrchestrator) + IStructuredScriptOrchestrator structuredScriptOrchestrator) { this.structuredScriptOrchestrator = structuredScriptOrchestrator; this.scriptObserverBackOffStrategy = scriptObserverBackOffStrategy; @@ -28,9 +28,8 @@ public ObservingScriptOrchestrator( public async Task ExecuteScript(ExecuteScriptCommand command, CancellationToken scriptExecutionCancellationToken) { - var mappedStartCommand = structuredScriptOrchestrator.Map(command); - var scriptStatusResponse = await structuredScriptOrchestrator.StartScript(mappedStartCommand, scriptExecutionCancellationToken).ConfigureAwait(false); + var scriptStatusResponse = await structuredScriptOrchestrator.StartScript(command, scriptExecutionCancellationToken).ConfigureAwait(false); scriptStatusResponse = await ObserveUntilCompleteThenFinish(scriptStatusResponse, scriptExecutionCancellationToken).ConfigureAwait(false); @@ -119,8 +118,9 @@ await Task.Delay(scriptObserverBackOffStrategy.GetBackoff(++iteration), scriptEx - protected void OnScriptStatusResponseReceived(TScriptStatusResponse scriptStatusResponse) + void OnScriptStatusResponseReceived(TScriptStatusResponse scriptStatusResponse) { + new ShortCutTakenHere(); onScriptStatusResponseReceived(structuredScriptOrchestrator.MapToStatus(scriptStatusResponse)); } } diff --git a/source/Octopus.Tentacle.Client/Scripts/ScriptOrchestratorFactory.cs b/source/Octopus.Tentacle.Client/Scripts/ScriptOrchestratorFactory.cs index 37c27da17..b5fe18287 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ScriptOrchestratorFactory.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ScriptOrchestratorFactory.cs @@ -75,11 +75,8 @@ public IStructuredScriptOrchestrator CreateOrchestrator(ScriptSe { return new ScriptServiceV2Orchestrator( clientsHolder.ScriptServiceV2, - scriptObserverBackOffStrategy, rpcCallExecutor, clientOperationMetricsBuilder, - onScriptStatusResponseReceived, - onScriptCompleted, onCancellationAbandonCompleteScriptAfter, clientOptions, logger); @@ -89,11 +86,8 @@ public IStructuredScriptOrchestrator CreateOrchestrator(ScriptSe { return new KubernetesScriptServiceV1AlphaOrchestrator( clientsHolder.KubernetesScriptServiceV1Alpha, - scriptObserverBackOffStrategy, rpcCallExecutor, clientOperationMetricsBuilder, - onScriptStatusResponseReceived, - onScriptCompleted, onCancellationAbandonCompleteScriptAfter, clientOptions, logger); @@ -103,11 +97,8 @@ public IStructuredScriptOrchestrator CreateOrchestrator(ScriptSe { return new KubernetesScriptServiceV1Orchestrator( clientsHolder.KubernetesScriptServiceV1, - scriptObserverBackOffStrategy, rpcCallExecutor, clientOperationMetricsBuilder, - onScriptStatusResponseReceived, - onScriptCompleted, onCancellationAbandonCompleteScriptAfter, clientOptions, logger); diff --git a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs index 96cdad24f..a8f6eea9c 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs @@ -13,7 +13,7 @@ namespace Octopus.Tentacle.Client.Scripts { - class ScriptServiceV1Orchestrator : IStructuredScriptOrchestrator + class ScriptServiceV1Orchestrator : IStructuredScriptOrchestrator { readonly RpcCallExecutor rpcCallExecutor; @@ -34,7 +34,7 @@ public ScriptServiceV1Orchestrator( this.logger = logger; } - protected override StartScriptCommand Map(ExecuteScriptCommand command) + private StartScriptCommand Map(ExecuteScriptCommand command) { if (command is not ExecuteShellScriptCommand shellScriptCommand) throw new InvalidOperationException($"{nameof(ScriptServiceV2Orchestrator)} only supports commands of type {nameof(ExecuteShellScriptCommand)}."); @@ -56,8 +56,9 @@ protected override StartScriptCommand Map(ExecuteScriptCommand command) public ProcessState GetState(ScriptStatusResponse response) => response.State; - public async Task StartScript(StartScriptCommand command, CancellationToken scriptExecutionCancellationToken) + public async Task StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) { + var command = Map(executeScriptCommand); var scriptTicket = await rpcCallExecutor.ExecuteWithNoRetries( RpcCall.Create(nameof(IScriptService.StartScript)), async ct => diff --git a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs index 64717c83e..8a52f4a23 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs @@ -15,7 +15,7 @@ namespace Octopus.Tentacle.Client.Scripts { - class ScriptServiceV2Orchestrator : IStructuredScriptOrchestrator + class ScriptServiceV2Orchestrator : IStructuredScriptOrchestrator { readonly IAsyncClientScriptServiceV2 clientScriptServiceV2; readonly RpcCallExecutor rpcCallExecutor; @@ -40,7 +40,7 @@ public ScriptServiceV2Orchestrator( this.logger = logger; } - public StartScriptCommandV2 Map(ExecuteScriptCommand command) + StartScriptCommandV2 Map(ExecuteScriptCommand command) { if (command is not ExecuteShellScriptCommand shellScriptCommand) throw new InvalidOperationException($"{nameof(ScriptServiceV2Orchestrator)} only supports commands of type {nameof(ExecuteShellScriptCommand)}."); @@ -66,8 +66,9 @@ public ScriptExecutionResult MapToResult(ScriptStatusResponseV2 response) public ProcessState GetState(ScriptStatusResponseV2 response) => response.State; - public async Task StartScript(StartScriptCommandV2 command, CancellationToken scriptExecutionCancellationToken) + public async Task StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) { + var command = Map(executeScriptCommand); ScriptStatusResponseV2 scriptStatusResponse; var startScriptCallsConnectedCount = 0; try @@ -121,7 +122,8 @@ void OnErrorAction(Exception ex) try { - await ObserveUntilCompleteThenFinish(scriptStatusResponse, scriptExecutionCancellationToken).ConfigureAwait(false); + new ShortCutTakenHere(); + //await ObserveUntilCompleteThenFinish(scriptStatusResponse, scriptExecutionCancellationToken).ConfigureAwait(false); } catch (Exception observerUntilCompleteException) { diff --git a/source/Octopus.Tentacle.Client/ShortCutTakenHere.cs b/source/Octopus.Tentacle.Client/ShortCutTakenHere.cs new file mode 100644 index 000000000..4a94e8f92 --- /dev/null +++ b/source/Octopus.Tentacle.Client/ShortCutTakenHere.cs @@ -0,0 +1,7 @@ +namespace Octopus.Tentacle.Client +{ + public class ShortCutTakenHere + { + + } +} \ No newline at end of file From 269dce8c89b20e0fa96453feddc8827cdabceb43 Mon Sep 17 00:00:00 2001 From: Luke Butters Date: Thu, 20 Jun 2024 12:52:59 +1000 Subject: [PATCH 03/23] Start to remove TScriptStatusResponse --- .../Scripts/IScriptOrchestrator.cs | 8 +++++++- .../KubernetesScriptServiceV1AlphaOrchestrator.cs | 5 ++--- .../KubernetesScriptServiceV1Orchestrator.cs | 5 ++--- .../Scripts/ObservingScriptOrchestrator.cs | 15 ++++++++++----- .../Scripts/ScriptServiceV1Orchestrator.cs | 2 +- .../Scripts/ScriptServiceV2Orchestrator.cs | 5 ++--- 6 files changed, 24 insertions(+), 16 deletions(-) diff --git a/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs index baf150d83..6224dc60a 100644 --- a/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs @@ -16,7 +16,13 @@ public interface IStructuredScriptOrchestrator { ScriptExecutionResult MapToResult(TScriptStatusResponse response); ProcessState GetState(TScriptStatusResponse response); Task StartScript(ExecuteScriptCommand command, CancellationToken scriptExecutionCancellationToken); - Task GetStatus(TScriptStatusResponse lastStatusResponse, CancellationToken scriptExecutionCancellationToken); + /// + /// Returns a status or null when scriptExecutionCancellationToken is null. + /// + /// + /// + /// + Task GetStatus(TScriptStatusResponse lastStatusResponse, CancellationToken scriptExecutionCancellationToken); Task Cancel(TScriptStatusResponse lastStatusResponse, CancellationToken scriptExecutionCancellationToken); Task Finish(TScriptStatusResponse lastStatusResponse, CancellationToken scriptExecutionCancellationToken); } diff --git a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs index 2cdd89785..7ecae3d2b 100644 --- a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs @@ -152,7 +152,7 @@ void OnErrorAction(Exception ex) return scriptStatusResponse; } - public async Task GetStatus(KubernetesScriptStatusResponseV1Alpha lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task GetStatus(KubernetesScriptStatusResponseV1Alpha lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { try { @@ -174,8 +174,7 @@ async Task GetStatusAction(CancellationTo } catch (Exception e) when (e is OperationCanceledException && scriptExecutionCancellationToken.IsCancellationRequested) { - // Return the last known response without logs when cancellation occurs and let the script execution go into the CancelScript and CompleteScript flow - return new KubernetesScriptStatusResponseV1Alpha(lastStatusResponse.ScriptTicket, lastStatusResponse.State, lastStatusResponse.ExitCode, new List(), lastStatusResponse.NextLogSequence); + return null; } } diff --git a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1Orchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1Orchestrator.cs index 1917acb34..0a367c34a 100644 --- a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1Orchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1Orchestrator.cs @@ -153,7 +153,7 @@ void OnErrorAction(Exception ex) return scriptStatusResponse; } - public async Task GetStatus(KubernetesScriptStatusResponseV1 lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task GetStatus(KubernetesScriptStatusResponseV1 lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { try { @@ -175,8 +175,7 @@ async Task GetStatusAction(CancellationToken c } catch (Exception e) when (e is OperationCanceledException && scriptExecutionCancellationToken.IsCancellationRequested) { - // Return the last known response without logs when cancellation occurs and let the script execution go into the CancelScript and CompleteScript flow - return new KubernetesScriptStatusResponseV1(lastStatusResponse.ScriptTicket, lastStatusResponse.State, lastStatusResponse.ExitCode, new List(), lastStatusResponse.NextLogSequence); + return null; } } diff --git a/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs index e6e9e32b5..4bf7be951 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs @@ -79,7 +79,14 @@ async Task ObserveUntilComplete( { try { - lastStatusResponse = await structuredScriptOrchestrator.GetStatus(lastStatusResponse, scriptExecutionCancellationToken).ConfigureAwait(false); + var receivedGetStatus = await structuredScriptOrchestrator.GetStatus(lastStatusResponse, scriptExecutionCancellationToken).ConfigureAwait(false); + if (scriptExecutionCancellationToken.IsCancellationRequested) + { + continue; // Enter cancellation mode. + } + + if (receivedGetStatus == null) throw new Exception("Script execution error, next status should not have been null"); + lastStatusResponse = receivedGetStatus; } catch (Exception) { @@ -116,12 +123,10 @@ await Task.Delay(scriptObserverBackOffStrategy.GetBackoff(++iteration), scriptEx return lastStatusResponse; } - - void OnScriptStatusResponseReceived(TScriptStatusResponse scriptStatusResponse) { - new ShortCutTakenHere(); - onScriptStatusResponseReceived(structuredScriptOrchestrator.MapToStatus(scriptStatusResponse)); + ScriptExecutionStatus scriptExecutionStatus = structuredScriptOrchestrator.MapToStatus(scriptStatusResponse); + onScriptStatusResponseReceived(scriptExecutionStatus); } } } \ No newline at end of file diff --git a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs index a8f6eea9c..1d0c1660e 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs @@ -74,7 +74,7 @@ public async Task StartScript(ExecuteScriptCommand execute return new ScriptStatusResponse(scriptTicket, ProcessState.Pending, 0, new List(), 0); } - public async Task GetStatus(ScriptStatusResponse lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task GetStatus(ScriptStatusResponse lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { var scriptStatusResponseV1 = await rpcCallExecutor.ExecuteWithNoRetries( RpcCall.Create(nameof(IScriptService.GetStatus)), diff --git a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs index 8a52f4a23..c789432d8 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs @@ -143,7 +143,7 @@ void OnErrorAction(Exception ex) return scriptStatusResponse; } - public async Task GetStatus(ScriptStatusResponseV2 lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task GetStatus(ScriptStatusResponseV2 lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { try { @@ -165,8 +165,7 @@ async Task GetStatusAction(CancellationToken ct) } catch (Exception e) when (e is OperationCanceledException && scriptExecutionCancellationToken.IsCancellationRequested) { - // Return the last known response without logs when cancellation occurs and let the script execution go into the CancelScript and CompleteScript flow - return new ScriptStatusResponseV2(lastStatusResponse.Ticket, lastStatusResponse.State, lastStatusResponse.ExitCode, new List(), lastStatusResponse.NextLogSequence); + return null; } } From 720b9c4fd8f0ec692ae8d7c292368e824be2bbeb Mon Sep 17 00:00:00 2001 From: Luke Butters Date: Thu, 20 Jun 2024 12:55:40 +1000 Subject: [PATCH 04/23] Drop nullable --- .../Scripts/IScriptOrchestrator.cs | 2 +- ...ernetesScriptServiceV1AlphaOrchestrator.cs | 33 +++++++----------- .../KubernetesScriptServiceV1Orchestrator.cs | 33 +++++++----------- .../Scripts/ScriptServiceV1Orchestrator.cs | 2 +- .../Scripts/ScriptServiceV2Orchestrator.cs | 34 ++++++++----------- 5 files changed, 42 insertions(+), 62 deletions(-) diff --git a/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs index 6224dc60a..4d35c7b25 100644 --- a/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs @@ -22,7 +22,7 @@ public interface IStructuredScriptOrchestrator { /// /// /// - Task GetStatus(TScriptStatusResponse lastStatusResponse, CancellationToken scriptExecutionCancellationToken); + Task GetStatus(TScriptStatusResponse lastStatusResponse, CancellationToken scriptExecutionCancellationToken); Task Cancel(TScriptStatusResponse lastStatusResponse, CancellationToken scriptExecutionCancellationToken); Task Finish(TScriptStatusResponse lastStatusResponse, CancellationToken scriptExecutionCancellationToken); } diff --git a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs index 7ecae3d2b..0dacc8fef 100644 --- a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs @@ -152,30 +152,23 @@ void OnErrorAction(Exception ex) return scriptStatusResponse; } - public async Task GetStatus(KubernetesScriptStatusResponseV1Alpha lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task GetStatus(KubernetesScriptStatusResponseV1Alpha lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { - try + async Task GetStatusAction(CancellationToken ct) { - async Task GetStatusAction(CancellationToken ct) - { - var request = new KubernetesScriptStatusRequestV1Alpha(lastStatusResponse.ScriptTicket, lastStatusResponse.NextLogSequence); - var result = await clientKubernetesScriptServiceV1Alpha.GetStatusAsync(request, new HalibutProxyRequestOptions(ct)); - - return result; - } + var request = new KubernetesScriptStatusRequestV1Alpha(lastStatusResponse.ScriptTicket, lastStatusResponse.NextLogSequence); + var result = await clientKubernetesScriptServiceV1Alpha.GetStatusAsync(request, new HalibutProxyRequestOptions(ct)); - return await rpcCallExecutor.Execute( - retriesEnabled: clientOptions.RpcRetrySettings.RetriesEnabled, - RpcCall.Create(nameof(IKubernetesScriptServiceV1Alpha.GetStatus)), - GetStatusAction, - logger, - clientOperationMetricsBuilder, - scriptExecutionCancellationToken).ConfigureAwait(false); - } - catch (Exception e) when (e is OperationCanceledException && scriptExecutionCancellationToken.IsCancellationRequested) - { - return null; + return result; } + + return await rpcCallExecutor.Execute( + retriesEnabled: clientOptions.RpcRetrySettings.RetriesEnabled, + RpcCall.Create(nameof(IKubernetesScriptServiceV1Alpha.GetStatus)), + GetStatusAction, + logger, + clientOperationMetricsBuilder, + scriptExecutionCancellationToken).ConfigureAwait(false); } public async Task Cancel(KubernetesScriptStatusResponseV1Alpha lastStatusResponse, CancellationToken scriptExecutionCancellationToken) diff --git a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1Orchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1Orchestrator.cs index 0a367c34a..8106cfe2b 100644 --- a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1Orchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1Orchestrator.cs @@ -153,30 +153,23 @@ void OnErrorAction(Exception ex) return scriptStatusResponse; } - public async Task GetStatus(KubernetesScriptStatusResponseV1 lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task GetStatus(KubernetesScriptStatusResponseV1 lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { - try + async Task GetStatusAction(CancellationToken ct) { - async Task GetStatusAction(CancellationToken ct) - { - var request = new KubernetesScriptStatusRequestV1(lastStatusResponse.ScriptTicket, lastStatusResponse.NextLogSequence); - var result = await clientKubernetesScriptServiceV1.GetStatusAsync(request, new HalibutProxyRequestOptions(ct)); - - return result; - } + var request = new KubernetesScriptStatusRequestV1(lastStatusResponse.ScriptTicket, lastStatusResponse.NextLogSequence); + var result = await clientKubernetesScriptServiceV1.GetStatusAsync(request, new HalibutProxyRequestOptions(ct)); - return await rpcCallExecutor.Execute( - retriesEnabled: clientOptions.RpcRetrySettings.RetriesEnabled, - RpcCall.Create(nameof(IKubernetesScriptServiceV1.GetStatus)), - GetStatusAction, - logger, - clientOperationMetricsBuilder, - scriptExecutionCancellationToken).ConfigureAwait(false); - } - catch (Exception e) when (e is OperationCanceledException && scriptExecutionCancellationToken.IsCancellationRequested) - { - return null; + return result; } + + return await rpcCallExecutor.Execute( + retriesEnabled: clientOptions.RpcRetrySettings.RetriesEnabled, + RpcCall.Create(nameof(IKubernetesScriptServiceV1.GetStatus)), + GetStatusAction, + logger, + clientOperationMetricsBuilder, + scriptExecutionCancellationToken).ConfigureAwait(false); } public async Task Cancel(KubernetesScriptStatusResponseV1 lastStatusResponse, CancellationToken scriptExecutionCancellationToken) diff --git a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs index 1d0c1660e..a8f6eea9c 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs @@ -74,7 +74,7 @@ public async Task StartScript(ExecuteScriptCommand execute return new ScriptStatusResponse(scriptTicket, ProcessState.Pending, 0, new List(), 0); } - public async Task GetStatus(ScriptStatusResponse lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task GetStatus(ScriptStatusResponse lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { var scriptStatusResponseV1 = await rpcCallExecutor.ExecuteWithNoRetries( RpcCall.Create(nameof(IScriptService.GetStatus)), diff --git a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs index c789432d8..f591a5431 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs @@ -143,30 +143,24 @@ void OnErrorAction(Exception ex) return scriptStatusResponse; } - public async Task GetStatus(ScriptStatusResponseV2 lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task GetStatus(ScriptStatusResponseV2 lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { - try + + async Task GetStatusAction(CancellationToken ct) { - async Task GetStatusAction(CancellationToken ct) - { - var request = new ScriptStatusRequestV2(lastStatusResponse.Ticket, lastStatusResponse.NextLogSequence); - var result = await clientScriptServiceV2.GetStatusAsync(request, new HalibutProxyRequestOptions(ct)); - - return result; - } + var request = new ScriptStatusRequestV2(lastStatusResponse.Ticket, lastStatusResponse.NextLogSequence); + var result = await clientScriptServiceV2.GetStatusAsync(request, new HalibutProxyRequestOptions(ct)); - return await rpcCallExecutor.Execute( - retriesEnabled: clientOptions.RpcRetrySettings.RetriesEnabled, - RpcCall.Create(nameof(IScriptServiceV2.GetStatus)), - GetStatusAction, - logger, - clientOperationMetricsBuilder, - scriptExecutionCancellationToken).ConfigureAwait(false); - } - catch (Exception e) when (e is OperationCanceledException && scriptExecutionCancellationToken.IsCancellationRequested) - { - return null; + return result; } + + return await rpcCallExecutor.Execute( + retriesEnabled: clientOptions.RpcRetrySettings.RetriesEnabled, + RpcCall.Create(nameof(IScriptServiceV2.GetStatus)), + GetStatusAction, + logger, + clientOperationMetricsBuilder, + scriptExecutionCancellationToken).ConfigureAwait(false); } public async Task Cancel(ScriptStatusResponseV2 lastStatusResponse, CancellationToken scriptExecutionCancellationToken) From 4336c4c9c7bdf303faa5fc5409ec9e34e9ccd19e Mon Sep 17 00:00:00 2001 From: Samdanae Imran Date: Thu, 20 Jun 2024 15:06:47 +1200 Subject: [PATCH 05/23] WIP --- .../Scripts/ObservingScriptOrchestrator.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs index 4bf7be951..8ff81c914 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs @@ -79,14 +79,7 @@ async Task ObserveUntilComplete( { try { - var receivedGetStatus = await structuredScriptOrchestrator.GetStatus(lastStatusResponse, scriptExecutionCancellationToken).ConfigureAwait(false); - if (scriptExecutionCancellationToken.IsCancellationRequested) - { - continue; // Enter cancellation mode. - } - - if (receivedGetStatus == null) throw new Exception("Script execution error, next status should not have been null"); - lastStatusResponse = receivedGetStatus; + lastStatusResponse = await structuredScriptOrchestrator.GetStatus(lastStatusResponse, scriptExecutionCancellationToken).ConfigureAwait(false); } catch (Exception) { From 254bd554e995e7c9a419f77879263c10ac64a1cf Mon Sep 17 00:00:00 2001 From: Samdanae Imran Date: Thu, 20 Jun 2024 15:47:58 +1200 Subject: [PATCH 06/23] Use new IStructuredScriptOrchestrator interface --- .../Scripts/IScriptOrchestrator.cs | 14 ++--- .../Scripts/ObservingScriptOrchestrator.cs | 54 +++++++++--------- .../Scripts/ScriptServiceV1Orchestrator.cs | 55 +++++++++++++++---- .../ScriptStatus.cs | 12 ++-- 4 files changed, 85 insertions(+), 50 deletions(-) diff --git a/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs index 4d35c7b25..39f8a7461 100644 --- a/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs @@ -1,6 +1,7 @@ using System; using System.Threading; using System.Threading.Tasks; +using Octopus.Tentacle.Client.EventDriven; using Octopus.Tentacle.Client.Scripts.Models; using Octopus.Tentacle.Contracts; @@ -11,19 +12,16 @@ public interface IScriptOrchestrator Task ExecuteScript(ExecuteScriptCommand command, CancellationToken scriptExecutionCancellationToken); } - public interface IStructuredScriptOrchestrator { - ScriptExecutionStatus MapToStatus(TScriptStatusResponse response); - ScriptExecutionResult MapToResult(TScriptStatusResponse response); - ProcessState GetState(TScriptStatusResponse response); - Task StartScript(ExecuteScriptCommand command, CancellationToken scriptExecutionCancellationToken); + public interface IStructuredScriptOrchestrator { + Task<(ScriptStatus, ITicketForNextStatus)> StartScript(ExecuteScriptCommand command, CancellationToken scriptExecutionCancellationToken); /// /// Returns a status or null when scriptExecutionCancellationToken is null. /// /// /// /// - Task GetStatus(TScriptStatusResponse lastStatusResponse, CancellationToken scriptExecutionCancellationToken); - Task Cancel(TScriptStatusResponse lastStatusResponse, CancellationToken scriptExecutionCancellationToken); - Task Finish(TScriptStatusResponse lastStatusResponse, CancellationToken scriptExecutionCancellationToken); + Task<(ScriptStatus, ITicketForNextStatus)> GetStatus(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken); + Task<(ScriptStatus, ITicketForNextStatus)> Cancel(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken); + Task<(ScriptStatus, ITicketForNextStatus)> Finish(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken); } } \ No newline at end of file diff --git a/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs index 8ff81c914..eb0043497 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs @@ -1,24 +1,25 @@ using System; using System.Threading; using System.Threading.Tasks; +using Octopus.Tentacle.Client.EventDriven; using Octopus.Tentacle.Client.Scripts.Models; using Octopus.Tentacle.Contracts; namespace Octopus.Tentacle.Client.Scripts { - public sealed class ObservingScriptOrchestrator : IScriptOrchestrator + public sealed class ObservingScriptOrchestrator : IScriptOrchestrator { readonly IScriptObserverBackoffStrategy scriptObserverBackOffStrategy; readonly OnScriptStatusResponseReceived onScriptStatusResponseReceived; readonly OnScriptCompleted onScriptCompleted; - IStructuredScriptOrchestrator structuredScriptOrchestrator; + IStructuredScriptOrchestrator structuredScriptOrchestrator; public ObservingScriptOrchestrator( IScriptObserverBackoffStrategy scriptObserverBackOffStrategy, OnScriptStatusResponseReceived onScriptStatusResponseReceived, OnScriptCompleted onScriptCompleted, - IStructuredScriptOrchestrator structuredScriptOrchestrator) + IStructuredScriptOrchestrator structuredScriptOrchestrator) { this.structuredScriptOrchestrator = structuredScriptOrchestrator; this.scriptObserverBackOffStrategy = scriptObserverBackOffStrategy; @@ -29,9 +30,9 @@ public ObservingScriptOrchestrator( public async Task ExecuteScript(ExecuteScriptCommand command, CancellationToken scriptExecutionCancellationToken) { - var scriptStatusResponse = await structuredScriptOrchestrator.StartScript(command, scriptExecutionCancellationToken).ConfigureAwait(false); + var (scriptStatus, ticketForNextStatus) = await structuredScriptOrchestrator.StartScript(command, scriptExecutionCancellationToken).ConfigureAwait(false); - scriptStatusResponse = await ObserveUntilCompleteThenFinish(scriptStatusResponse, scriptExecutionCancellationToken).ConfigureAwait(false); + (scriptStatus, ticketForNextStatus) = await ObserveUntilCompleteThenFinish(scriptStatus, ticketForNextStatus, scriptExecutionCancellationToken).ConfigureAwait(false); if (scriptExecutionCancellationToken.IsCancellationRequested) { @@ -39,47 +40,49 @@ public async Task ExecuteScript(ExecuteScriptCommand comm throw new OperationCanceledException("Script execution was cancelled"); } - var mappedResponse = structuredScriptOrchestrator.MapToResult(scriptStatusResponse); - - return new ScriptExecutionResult(mappedResponse.State, mappedResponse.ExitCode); + + return new ScriptExecutionResult(scriptStatus.State, scriptStatus.ExitCode!.Value); } - async Task ObserveUntilCompleteThenFinish( - TScriptStatusResponse scriptStatusResponse, + async Task<(ScriptStatus, ITicketForNextStatus)> ObserveUntilCompleteThenFinish( + ScriptStatus scriptStatus, + ITicketForNextStatus ticketForNextStatus, CancellationToken scriptExecutionCancellationToken) { - OnScriptStatusResponseReceived(scriptStatusResponse); + OnScriptStatusResponseReceived(scriptStatus); - var lastScriptStatus = await ObserveUntilComplete(scriptStatusResponse, scriptExecutionCancellationToken).ConfigureAwait(false); + var (lastStatusResponse, lastTicketForNextStatus) = await ObserveUntilComplete(scriptStatus, ticketForNextStatus, scriptExecutionCancellationToken).ConfigureAwait(false); await onScriptCompleted(scriptExecutionCancellationToken).ConfigureAwait(false); - lastScriptStatus = await structuredScriptOrchestrator.Finish(lastScriptStatus, scriptExecutionCancellationToken).ConfigureAwait(false); + (lastStatusResponse, lastTicketForNextStatus) = await structuredScriptOrchestrator.Finish(lastTicketForNextStatus, scriptExecutionCancellationToken).ConfigureAwait(false); - OnScriptStatusResponseReceived(lastScriptStatus); + OnScriptStatusResponseReceived(lastStatusResponse); - return lastScriptStatus; + return (lastStatusResponse, lastTicketForNextStatus); } - async Task ObserveUntilComplete( - TScriptStatusResponse scriptStatusResponse, + async Task<(ScriptStatus lastStatusResponse, ITicketForNextStatus lastTicketForNextStatus)> ObserveUntilComplete( + ScriptStatus scriptStatus, + ITicketForNextStatus ticketForNextStatus, CancellationToken scriptExecutionCancellationToken) { - var lastStatusResponse = scriptStatusResponse; + var lastTicketForNextStatus = ticketForNextStatus; + var lastStatusResponse = scriptStatus; var iteration = 0; var cancellationIteration = 0; - while (structuredScriptOrchestrator.GetState(lastStatusResponse) != ProcessState.Complete) + while (lastStatusResponse.State != ProcessState.Complete) { if (scriptExecutionCancellationToken.IsCancellationRequested) { - lastStatusResponse = await structuredScriptOrchestrator.Cancel(lastStatusResponse, scriptExecutionCancellationToken).ConfigureAwait(false); + (lastStatusResponse, lastTicketForNextStatus) = await structuredScriptOrchestrator.Cancel(lastTicketForNextStatus, scriptExecutionCancellationToken).ConfigureAwait(false); } else { try { - lastStatusResponse = await structuredScriptOrchestrator.GetStatus(lastStatusResponse, scriptExecutionCancellationToken).ConfigureAwait(false); + (lastStatusResponse, lastTicketForNextStatus) = await structuredScriptOrchestrator.GetStatus(lastTicketForNextStatus, scriptExecutionCancellationToken).ConfigureAwait(false); } catch (Exception) { @@ -94,7 +97,7 @@ async Task ObserveUntilComplete( OnScriptStatusResponseReceived(lastStatusResponse); - if (structuredScriptOrchestrator.GetState(lastStatusResponse) == ProcessState.Complete) + if (lastStatusResponse.State == ProcessState.Complete) { continue; } @@ -113,12 +116,13 @@ await Task.Delay(scriptObserverBackOffStrategy.GetBackoff(++iteration), scriptEx } } - return lastStatusResponse; + new ShortCutTakenHere(); + return (lastStatusResponse, lastTicketForNextStatus); } - void OnScriptStatusResponseReceived(TScriptStatusResponse scriptStatusResponse) + void OnScriptStatusResponseReceived(ScriptStatus scriptStatusResponse) { - ScriptExecutionStatus scriptExecutionStatus = structuredScriptOrchestrator.MapToStatus(scriptStatusResponse); + var scriptExecutionStatus = new ScriptExecutionStatus(scriptStatusResponse.Logs); onScriptStatusResponseReceived(scriptExecutionStatus); } } diff --git a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs index a8f6eea9c..d653a726b 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs @@ -3,6 +3,7 @@ using System.Threading; using System.Threading.Tasks; using Halibut.ServiceModel; +using Octopus.Tentacle.Client.EventDriven; using Octopus.Tentacle.Client.Execution; using Octopus.Tentacle.Client.Observability; using Octopus.Tentacle.Client.Scripts.Models; @@ -13,7 +14,7 @@ namespace Octopus.Tentacle.Client.Scripts { - class ScriptServiceV1Orchestrator : IStructuredScriptOrchestrator + class ScriptServiceV1Orchestrator : IStructuredScriptOrchestrator { readonly RpcCallExecutor rpcCallExecutor; @@ -50,13 +51,28 @@ private StartScriptCommand Map(ExecuteScriptCommand command) shellScriptCommand.Files.ToArray()); } - public ScriptExecutionStatus MapToStatus(ScriptStatusResponse response) => new(response.Logs); + private ScriptStatus MapToScriptStatus(ScriptStatusResponse scriptStatusResponse) + { + return new ScriptStatus(scriptStatusResponse.State, scriptStatusResponse.ExitCode, scriptStatusResponse.Logs); + } + + private ITicketForNextStatus MapToNextStatus(ScriptStatusResponse scriptStatusResponse) + { + return new DefaultTicketForNextStatus(scriptStatusResponse.Ticket, scriptStatusResponse.NextLogSequence, ScriptServiceVersion.ScriptServiceVersion1); + } - public ScriptExecutionResult MapToResult(ScriptStatusResponse response) => new(response.State, response.ExitCode); + public async Task<(ScriptStatus, ITicketForNextStatus)> StartScript(ExecuteScriptCommand command, CancellationToken scriptExecutionCancellationToken) + { + var r = await _StartScript(command, scriptExecutionCancellationToken); + return Map(r); + } - public ProcessState GetState(ScriptStatusResponse response) => response.State; + (ScriptStatus, ITicketForNextStatus) Map(ScriptStatusResponse r) + { + return (MapToScriptStatus(r), MapToNextStatus(r)); + } - public async Task StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) + private async Task _StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) { var command = Map(executeScriptCommand); var scriptTicket = await rpcCallExecutor.ExecuteWithNoRetries( @@ -74,13 +90,18 @@ public async Task StartScript(ExecuteScriptCommand execute return new ScriptStatusResponse(scriptTicket, ProcessState.Pending, 0, new List(), 0); } - public async Task GetStatus(ScriptStatusResponse lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ITicketForNextStatus)> GetStatus(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + { + return Map(await _GetStatus(lastStatusResponse, scriptExecutionCancellationToken)); + } + + private async Task _GetStatus(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { var scriptStatusResponseV1 = await rpcCallExecutor.ExecuteWithNoRetries( RpcCall.Create(nameof(IScriptService.GetStatus)), async ct => { - var request = new ScriptStatusRequest(lastStatusResponse.Ticket, lastStatusResponse.NextLogSequence); + var request = new ScriptStatusRequest(lastStatusResponse.ScriptTicket, lastStatusResponse.NextLogSequence); var result = await clientScriptServiceV1.GetStatusAsync(request, new HalibutProxyRequestOptions(ct)); return result; @@ -92,13 +113,18 @@ public async Task GetStatus(ScriptStatusResponse lastStatu return scriptStatusResponseV1; } - public async Task Cancel(ScriptStatusResponse lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ITicketForNextStatus)> Cancel(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + { + return Map(await _Cancel(lastStatusResponse, scriptExecutionCancellationToken)); + } + + private async Task _Cancel(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { var response = await rpcCallExecutor.ExecuteWithNoRetries( RpcCall.Create(nameof(IScriptService.CancelScript)), async ct => { - var request = new CancelScriptCommand(lastStatusResponse.Ticket, lastStatusResponse.NextLogSequence); + var request = new CancelScriptCommand(lastStatusResponse.ScriptTicket, lastStatusResponse.NextLogSequence); var result = await clientScriptServiceV1.CancelScriptAsync(request, new HalibutProxyRequestOptions(ct)); return result; @@ -110,13 +136,18 @@ public async Task Cancel(ScriptStatusResponse lastStatusRe return response; } - public async Task Finish(ScriptStatusResponse lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ITicketForNextStatus)> Finish(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + { + return Map(await _Finish(lastStatusResponse, scriptExecutionCancellationToken)); + } + + private async Task _Finish(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { var response = await rpcCallExecutor.ExecuteWithNoRetries( RpcCall.Create(nameof(IScriptService.CompleteScript)), async ct => { - var request = new CompleteScriptCommand(lastStatusResponse.Ticket, lastStatusResponse.NextLogSequence); + var request = new CompleteScriptCommand(lastStatusResponse.ScriptTicket, lastStatusResponse.NextLogSequence); var result = await clientScriptServiceV1.CompleteScriptAsync(request, new HalibutProxyRequestOptions(ct)); return result; @@ -127,5 +158,7 @@ public async Task Finish(ScriptStatusResponse lastStatusRe return response; } + + } } \ No newline at end of file diff --git a/source/Octopus.Tentacle.Contracts/ScriptStatus.cs b/source/Octopus.Tentacle.Contracts/ScriptStatus.cs index e56108d68..5ce9f80cf 100644 --- a/source/Octopus.Tentacle.Contracts/ScriptStatus.cs +++ b/source/Octopus.Tentacle.Contracts/ScriptStatus.cs @@ -4,15 +4,15 @@ namespace Octopus.Tentacle.Contracts { public class ScriptStatus { - ProcessState state; - int? exitCode; - List logs; + public ProcessState State; + public int? ExitCode; + public List Logs; public ScriptStatus(ProcessState state, int? exitCode, List logs) { - this.state = state; - this.exitCode = exitCode; - this.logs = logs; + this.State = state; + this.ExitCode = exitCode; + this.Logs = logs; } } } \ No newline at end of file From acac7fc309ea41f9657214f4ea6d8467c877b136 Mon Sep 17 00:00:00 2001 From: Luke Butters Date: Thu, 20 Jun 2024 14:03:25 +1000 Subject: [PATCH 07/23] Finish can return more logs --- .../Scripts/IScriptOrchestrator.cs | 2 +- ...ernetesScriptServiceV1AlphaOrchestrator.cs | 51 ++++++++++++++++--- .../Scripts/ObservingScriptOrchestrator.cs | 9 +++- .../Scripts/ScriptServiceV1Orchestrator.cs | 14 ++--- 4 files changed, 60 insertions(+), 16 deletions(-) diff --git a/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs index 39f8a7461..1c6869181 100644 --- a/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs @@ -22,6 +22,6 @@ public interface IStructuredScriptOrchestrator { /// Task<(ScriptStatus, ITicketForNextStatus)> GetStatus(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken); Task<(ScriptStatus, ITicketForNextStatus)> Cancel(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken); - Task<(ScriptStatus, ITicketForNextStatus)> Finish(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken); + Task Finish(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken); } } \ No newline at end of file diff --git a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs index 0dacc8fef..42d9857ea 100644 --- a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Halibut; using Halibut.ServiceModel; +using Octopus.Tentacle.Client.EventDriven; using Octopus.Tentacle.Client.Execution; using Octopus.Tentacle.Client.Observability; using Octopus.Tentacle.Client.Scripts.Models; @@ -15,7 +16,7 @@ namespace Octopus.Tentacle.Client.Scripts { - class KubernetesScriptServiceV1AlphaOrchestrator : IStructuredScriptOrchestrator + class KubernetesScriptServiceV1AlphaOrchestrator : IStructuredScriptOrchestrator { readonly IAsyncClientKubernetesScriptServiceV1Alpha clientKubernetesScriptServiceV1Alpha; readonly RpcCallExecutor rpcCallExecutor; @@ -74,8 +75,29 @@ public ScriptExecutionResult MapToResult(KubernetesScriptStatusResponseV1Alpha r => new(response.State, response.ExitCode); public ProcessState GetState(KubernetesScriptStatusResponseV1Alpha response) => response.State; + + + private ScriptStatus MapToScriptStatus(KubernetesScriptStatusResponseV1Alpha scriptStatusResponse) + { + return new ScriptStatus(scriptStatusResponse.State, scriptStatusResponse.ExitCode, scriptStatusResponse.Logs); + } + + private ITicketForNextStatus MapToNextStatus(KubernetesScriptStatusResponseV1Alpha scriptStatusResponse) + { + return new DefaultTicketForNextStatus(scriptStatusResponse.ScriptTicket, scriptStatusResponse.NextLogSequence, ScriptServiceVersion.ScriptServiceVersion1); + } + + (ScriptStatus, ITicketForNextStatus) Map(KubernetesScriptStatusResponseV1Alpha r) + { + return (MapToScriptStatus(r), MapToNextStatus(r)); + } - public async Task StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ITicketForNextStatus)> StartScript(ExecuteScriptCommand command, CancellationToken scriptExecutionCancellationToken) + { + return Map(await _StartScript(command, scriptExecutionCancellationToken)); + } + + private async Task _StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) { var command = Map(executeScriptCommand); KubernetesScriptStatusResponseV1Alpha scriptStatusResponse; @@ -152,7 +174,12 @@ void OnErrorAction(Exception ex) return scriptStatusResponse; } - public async Task GetStatus(KubernetesScriptStatusResponseV1Alpha lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ITicketForNextStatus)> GetStatus(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + { + return Map(await _GetStatus(lastStatusResponse, scriptExecutionCancellationToken)); + } + + async Task _GetStatus(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { async Task GetStatusAction(CancellationToken ct) { @@ -171,7 +198,12 @@ async Task GetStatusAction(CancellationTo scriptExecutionCancellationToken).ConfigureAwait(false); } - public async Task Cancel(KubernetesScriptStatusResponseV1Alpha lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ITicketForNextStatus)> Cancel(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + { + return Map(await _Cancel(lastStatusResponse, scriptExecutionCancellationToken)); + } + + async Task _Cancel(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { async Task CancelScriptAction(CancellationToken ct) { @@ -195,7 +227,14 @@ async Task CancelScriptAction(Cancellatio CancellationToken.None).ConfigureAwait(false); } - public async Task Finish(KubernetesScriptStatusResponseV1Alpha lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + + public async Task Finish(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + { + await _Finish(lastStatusResponse, scriptExecutionCancellationToken); + return null; + } + + async Task _Finish(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { try { @@ -222,7 +261,7 @@ await rpcCallExecutor.ExecuteWithNoRetries( logger.Verbose(ex); } - return lastStatusResponse; + return null; } } } \ No newline at end of file diff --git a/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs index eb0043497..c05330841 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs @@ -1,9 +1,11 @@ using System; using System.Threading; using System.Threading.Tasks; +using Halibut; using Octopus.Tentacle.Client.EventDriven; using Octopus.Tentacle.Client.Scripts.Models; using Octopus.Tentacle.Contracts; +using Octopus.Tentacle.Contracts.Logging; namespace Octopus.Tentacle.Client.Scripts { @@ -12,6 +14,7 @@ public sealed class ObservingScriptOrchestrator : IScriptOrchestrator readonly IScriptObserverBackoffStrategy scriptObserverBackOffStrategy; readonly OnScriptStatusResponseReceived onScriptStatusResponseReceived; readonly OnScriptCompleted onScriptCompleted; + readonly ITentacleClientTaskLog logger; IStructuredScriptOrchestrator structuredScriptOrchestrator; @@ -19,9 +22,10 @@ public ObservingScriptOrchestrator( IScriptObserverBackoffStrategy scriptObserverBackOffStrategy, OnScriptStatusResponseReceived onScriptStatusResponseReceived, OnScriptCompleted onScriptCompleted, - IStructuredScriptOrchestrator structuredScriptOrchestrator) + IStructuredScriptOrchestrator structuredScriptOrchestrator, ITentacleClientTaskLog logger) { this.structuredScriptOrchestrator = structuredScriptOrchestrator; + this.logger = logger; this.scriptObserverBackOffStrategy = scriptObserverBackOffStrategy; this.onScriptStatusResponseReceived = onScriptStatusResponseReceived; this.onScriptCompleted = onScriptCompleted; @@ -55,8 +59,9 @@ public async Task ExecuteScript(ExecuteScriptCommand comm await onScriptCompleted(scriptExecutionCancellationToken).ConfigureAwait(false); - (lastStatusResponse, lastTicketForNextStatus) = await structuredScriptOrchestrator.Finish(lastTicketForNextStatus, scriptExecutionCancellationToken).ConfigureAwait(false); + lastStatusResponse = await structuredScriptOrchestrator.Finish(lastTicketForNextStatus, scriptExecutionCancellationToken).ConfigureAwait(false) ?? lastStatusResponse; + OnScriptStatusResponseReceived(lastStatusResponse); return (lastStatusResponse, lastTicketForNextStatus); diff --git a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs index d653a726b..19e9f2277 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs @@ -61,17 +61,17 @@ private ITicketForNextStatus MapToNextStatus(ScriptStatusResponse scriptStatusRe return new DefaultTicketForNextStatus(scriptStatusResponse.Ticket, scriptStatusResponse.NextLogSequence, ScriptServiceVersion.ScriptServiceVersion1); } + (ScriptStatus, ITicketForNextStatus) Map(ScriptStatusResponse r) + { + return (MapToScriptStatus(r), MapToNextStatus(r)); + } + public async Task<(ScriptStatus, ITicketForNextStatus)> StartScript(ExecuteScriptCommand command, CancellationToken scriptExecutionCancellationToken) { var r = await _StartScript(command, scriptExecutionCancellationToken); return Map(r); } - (ScriptStatus, ITicketForNextStatus) Map(ScriptStatusResponse r) - { - return (MapToScriptStatus(r), MapToNextStatus(r)); - } - private async Task _StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) { var command = Map(executeScriptCommand); @@ -136,9 +136,9 @@ private async Task _Cancel(ITicketForNextStatus lastStatus return response; } - public async Task<(ScriptStatus, ITicketForNextStatus)> Finish(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task Finish(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { - return Map(await _Finish(lastStatusResponse, scriptExecutionCancellationToken)); + return MapToScriptStatus(await _Finish(lastStatusResponse, scriptExecutionCancellationToken)); } private async Task _Finish(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) From 3f0dfac66b8d7136d22872b40994c4621c777469 Mon Sep 17 00:00:00 2001 From: Samdanae Imran Date: Thu, 20 Jun 2024 16:07:02 +1200 Subject: [PATCH 08/23] Change over ScriptServiceV2Orchestrator to use new interface --- .../EventDrivenScriptExecutor.cs | 2 +- .../Scripts/ScriptServiceV1Orchestrator.cs | 3 +- .../Scripts/ScriptServiceV2Orchestrator.cs | 61 ++++++++++++++----- 3 files changed, 47 insertions(+), 19 deletions(-) diff --git a/source/Octopus.Tentacle.Client/EventDrivenScriptExecutor.cs b/source/Octopus.Tentacle.Client/EventDrivenScriptExecutor.cs index 84d49a789..0e6f21a46 100644 --- a/source/Octopus.Tentacle.Client/EventDrivenScriptExecutor.cs +++ b/source/Octopus.Tentacle.Client/EventDrivenScriptExecutor.cs @@ -44,7 +44,7 @@ internal EventDrivenScriptExecutor(ITentacleClientTaskLog logger, this.clientOptions = clientOptions; this.onCancellationAbandonCompleteScriptAfter = onCancellationAbandonCompleteScriptAfter; clientsHolder = new ClientsHolder(halibutRuntime, serviceEndPoint, tentacleServicesDecoratorFactory); - rpcCallExecutor = RpcCallExecutorFactory.Create(this.clientOptions.RpcRetrySettings.RetryDuration, this.tentacleClientObserver) + rpcCallExecutor = RpcCallExecutorFactory.Create(this.clientOptions.RpcRetrySettings.RetryDuration, this.tentacleClientObserver); } public async Task<(ScriptStatus, ITicketForNextStatus)> StartScript(ExecuteScriptCommand executeScriptCommand, diff --git a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs index 19e9f2277..58799c694 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs @@ -68,8 +68,7 @@ private ITicketForNextStatus MapToNextStatus(ScriptStatusResponse scriptStatusRe public async Task<(ScriptStatus, ITicketForNextStatus)> StartScript(ExecuteScriptCommand command, CancellationToken scriptExecutionCancellationToken) { - var r = await _StartScript(command, scriptExecutionCancellationToken); - return Map(r); + return Map(await _StartScript(command, scriptExecutionCancellationToken)); } private async Task _StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) diff --git a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs index f591a5431..e3467b7a9 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Halibut; using Halibut.ServiceModel; +using Octopus.Tentacle.Client.EventDriven; using Octopus.Tentacle.Client.Execution; using Octopus.Tentacle.Client.Observability; using Octopus.Tentacle.Client.Scripts.Models; @@ -15,7 +16,7 @@ namespace Octopus.Tentacle.Client.Scripts { - class ScriptServiceV2Orchestrator : IStructuredScriptOrchestrator + class ScriptServiceV2Orchestrator : IStructuredScriptOrchestrator { readonly IAsyncClientScriptServiceV2 clientScriptServiceV2; readonly RpcCallExecutor rpcCallExecutor; @@ -57,16 +58,29 @@ StartScriptCommandV2 Map(ExecuteScriptCommand command) shellScriptCommand.Scripts, shellScriptCommand.Files.ToArray()); } + + (ScriptStatus, ITicketForNextStatus) Map(ScriptStatusResponseV2 r) + { + return (MapToScriptStatus(r), MapToNextStatus(r)); + } + + private ScriptStatus MapToScriptStatus(ScriptStatusResponseV2 scriptStatusResponse) + { + return new ScriptStatus(scriptStatusResponse.State, scriptStatusResponse.ExitCode, scriptStatusResponse.Logs); + } - public ScriptExecutionStatus MapToStatus(ScriptStatusResponseV2 response) - => new(response.Logs); - - public ScriptExecutionResult MapToResult(ScriptStatusResponseV2 response) - => new(response.State, response.ExitCode); - + private ITicketForNextStatus MapToNextStatus(ScriptStatusResponseV2 scriptStatusResponse) + { + return new DefaultTicketForNextStatus(scriptStatusResponse.Ticket, scriptStatusResponse.NextLogSequence, ScriptServiceVersion.ScriptServiceVersion1); + } public ProcessState GetState(ScriptStatusResponseV2 response) => response.State; - public async Task StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ITicketForNextStatus)> StartScript(ExecuteScriptCommand command, CancellationToken scriptExecutionCancellationToken) + { + return Map(await _StartScript(command, scriptExecutionCancellationToken)); + } + + private async Task _StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) { var command = Map(executeScriptCommand); ScriptStatusResponseV2 scriptStatusResponse; @@ -143,12 +157,18 @@ void OnErrorAction(Exception ex) return scriptStatusResponse; } - public async Task GetStatus(ScriptStatusResponseV2 lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ITicketForNextStatus)> GetStatus(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + { + return Map(await _GetStatus(lastStatusResponse, scriptExecutionCancellationToken)); + + } + + private async Task _GetStatus(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { async Task GetStatusAction(CancellationToken ct) { - var request = new ScriptStatusRequestV2(lastStatusResponse.Ticket, lastStatusResponse.NextLogSequence); + var request = new ScriptStatusRequestV2(lastStatusResponse.ScriptTicket, lastStatusResponse.NextLogSequence); var result = await clientScriptServiceV2.GetStatusAsync(request, new HalibutProxyRequestOptions(ct)); return result; @@ -163,11 +183,16 @@ async Task GetStatusAction(CancellationToken ct) scriptExecutionCancellationToken).ConfigureAwait(false); } - public async Task Cancel(ScriptStatusResponseV2 lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ITicketForNextStatus)> Cancel(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + { + return Map(await _GetStatus(lastStatusResponse, scriptExecutionCancellationToken)); + } + + private async Task _Cancel(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { async Task CancelScriptAction(CancellationToken ct) { - var request = new CancelScriptCommandV2(lastStatusResponse.Ticket, lastStatusResponse.NextLogSequence); + var request = new CancelScriptCommandV2(lastStatusResponse.ScriptTicket, lastStatusResponse.NextLogSequence); var result = await clientScriptServiceV2.CancelScriptAsync(request, new HalibutProxyRequestOptions(ct)); return result; @@ -187,7 +212,13 @@ async Task CancelScriptAction(CancellationToken ct) CancellationToken.None).ConfigureAwait(false); } - public async Task Finish(ScriptStatusResponseV2 lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task Finish(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + { + await _Finish(lastStatusResponse, scriptExecutionCancellationToken); + return null; + } + + private async Task _Finish(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { try { @@ -202,7 +233,7 @@ await rpcCallExecutor.ExecuteWithNoRetries( RpcCall.Create(nameof(IScriptServiceV2.CompleteScript)), async ct => { - var request = new CompleteScriptCommandV2(lastStatusResponse.Ticket); + var request = new CompleteScriptCommandV2(lastStatusResponse.ScriptTicket); await clientScriptServiceV2.CompleteScriptAsync(request, new HalibutProxyRequestOptions(ct)); }, logger, @@ -214,8 +245,6 @@ await rpcCallExecutor.ExecuteWithNoRetries( logger.Warn("Failed to cleanup the script working directory on Tentacle"); logger.Verbose(ex); } - - return lastStatusResponse; } } } \ No newline at end of file From d80bc623bbaf45db8413f4ac2bc27b181a1152e8 Mon Sep 17 00:00:00 2001 From: Luke Butters Date: Thu, 20 Jun 2024 14:08:54 +1000 Subject: [PATCH 09/23] Fix factory --- .../Scripts/ScriptOrchestratorFactory.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/Octopus.Tentacle.Client/Scripts/ScriptOrchestratorFactory.cs b/source/Octopus.Tentacle.Client/Scripts/ScriptOrchestratorFactory.cs index b5fe18287..cfeef6fe2 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ScriptOrchestratorFactory.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ScriptOrchestratorFactory.cs @@ -57,10 +57,11 @@ public async Task CreateOrchestrator(CancellationToken canc throw new OperationCanceledException("Script execution was cancelled", ex); } - return CreateOrchestrator(scriptServiceToUse); + var structuredScriptOrchestrator = CreateOrchestrator(scriptServiceToUse); + return new ObservingScriptOrchestrator(scriptObserverBackOffStrategy, onScriptStatusResponseReceived, onScriptCompleted, structuredScriptOrchestrator, logger); } - public IStructuredScriptOrchestrator CreateOrchestrator(ScriptServiceVersion scriptServiceToUse) + public IStructuredScriptOrchestrator CreateOrchestrator(ScriptServiceVersion scriptServiceToUse) { if (scriptServiceToUse == ScriptServiceVersion.ScriptServiceVersion1) { From 11b96d46dabed647a3abcb600c40ff716e0387cf Mon Sep 17 00:00:00 2001 From: Samdanae Imran Date: Thu, 20 Jun 2024 16:14:08 +1200 Subject: [PATCH 10/23] Change KubernetesScriptServiceV1Orchestrator to use new interface --- .../KubernetesScriptServiceV1Orchestrator.cs | 52 ++++++++++++++----- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1Orchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1Orchestrator.cs index 8106cfe2b..716daaddd 100644 --- a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1Orchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1Orchestrator.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Halibut; using Halibut.ServiceModel; +using Octopus.Tentacle.Client.EventDriven; using Octopus.Tentacle.Client.Execution; using Octopus.Tentacle.Client.Observability; using Octopus.Tentacle.Client.Scripts.Models; @@ -15,7 +16,7 @@ namespace Octopus.Tentacle.Client.Scripts { - class KubernetesScriptServiceV1Orchestrator : IStructuredScriptOrchestrator + class KubernetesScriptServiceV1Orchestrator : IStructuredScriptOrchestrator { readonly IAsyncClientKubernetesScriptServiceV1 clientKubernetesScriptServiceV1; readonly RpcCallExecutor rpcCallExecutor; @@ -67,16 +68,27 @@ StartKubernetesScriptCommandV1 Map(ExecuteScriptCommand command) kubernetesScriptCommand.Files.ToArray(), kubernetesScriptCommand.IsRawScript); } + (ScriptStatus, ITicketForNextStatus) Map(KubernetesScriptStatusResponseV1 r) + { + return (MapToScriptStatus(r), MapToNextStatus(r)); + } + + private ScriptStatus MapToScriptStatus(KubernetesScriptStatusResponseV1 scriptStatusResponse) + { + return new ScriptStatus(scriptStatusResponse.State, scriptStatusResponse.ExitCode, scriptStatusResponse.Logs); + } - public ScriptExecutionStatus MapToStatus(KubernetesScriptStatusResponseV1 response) - => new(response.Logs); - - public ScriptExecutionResult MapToResult(KubernetesScriptStatusResponseV1 response) - => new(response.State, response.ExitCode); + private ITicketForNextStatus MapToNextStatus(KubernetesScriptStatusResponseV1 scriptStatusResponse) + { + return new DefaultTicketForNextStatus(scriptStatusResponse.ScriptTicket, scriptStatusResponse.NextLogSequence, ScriptServiceVersion.ScriptServiceVersion1); + } - public ProcessState GetState(KubernetesScriptStatusResponseV1 response) => response.State; + public async Task<(ScriptStatus, ITicketForNextStatus)> StartScript(ExecuteScriptCommand command, CancellationToken scriptExecutionCancellationToken) + { + return Map(await _StartScript(command, scriptExecutionCancellationToken)); + } - public async Task StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) + private async Task _StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) { var command = Map(executeScriptCommand); KubernetesScriptStatusResponseV1 scriptStatusResponse; @@ -153,7 +165,12 @@ void OnErrorAction(Exception ex) return scriptStatusResponse; } - public async Task GetStatus(KubernetesScriptStatusResponseV1 lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ITicketForNextStatus)> GetStatus(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + { + return Map(await _GetStatus(lastStatusResponse, scriptExecutionCancellationToken)); + } + + private async Task _GetStatus(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { async Task GetStatusAction(CancellationToken ct) { @@ -172,7 +189,12 @@ async Task GetStatusAction(CancellationToken c scriptExecutionCancellationToken).ConfigureAwait(false); } - public async Task Cancel(KubernetesScriptStatusResponseV1 lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ITicketForNextStatus)> Cancel(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + { + return Map(await _GetStatus(lastStatusResponse, scriptExecutionCancellationToken)); + } + + private async Task _Cancel(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { async Task CancelScriptAction(CancellationToken ct) { @@ -196,7 +218,13 @@ async Task CancelScriptAction(CancellationToke CancellationToken.None).ConfigureAwait(false); } - public async Task Finish(KubernetesScriptStatusResponseV1 lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task Finish(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + { + await _Finish(lastStatusResponse, scriptExecutionCancellationToken); + return null; + } + + private async Task _Finish(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { try { @@ -222,8 +250,6 @@ await rpcCallExecutor.ExecuteWithNoRetries( logger.Warn("Failed to cleanup the script working directory on Tentacle"); logger.Verbose(ex); } - - return lastStatusResponse; } } } \ No newline at end of file From 6a4b1f681275fe65bd818865733ff4faebe0decd Mon Sep 17 00:00:00 2001 From: Samdanae Imran Date: Thu, 20 Jun 2024 16:16:03 +1200 Subject: [PATCH 11/23] get building --- source/Octopus.Tentacle.Client/EventDrivenScriptExecutor.cs | 2 -- .../Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs | 2 -- 2 files changed, 4 deletions(-) diff --git a/source/Octopus.Tentacle.Client/EventDrivenScriptExecutor.cs b/source/Octopus.Tentacle.Client/EventDrivenScriptExecutor.cs index 0e6f21a46..35e9c247a 100644 --- a/source/Octopus.Tentacle.Client/EventDrivenScriptExecutor.cs +++ b/source/Octopus.Tentacle.Client/EventDrivenScriptExecutor.cs @@ -75,8 +75,6 @@ internal EventDrivenScriptExecutor(ITentacleClientTaskLog logger, var orchestrator = scriptOrchestratorFactory.CreateOrchestrator(scriptServiceToUse); - orchestrator.s - // and return stuff. throw new System.NotImplementedException(); } diff --git a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs index 42d9857ea..9a3c65c03 100644 --- a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs @@ -260,8 +260,6 @@ await rpcCallExecutor.ExecuteWithNoRetries( logger.Warn("Failed to cleanup the script working directory on Tentacle"); logger.Verbose(ex); } - - return null; } } } \ No newline at end of file From e99bc2a04c901b1c8e6f36b467937194fef4dab1 Mon Sep 17 00:00:00 2001 From: Samdanae Imran Date: Thu, 20 Jun 2024 16:50:39 +1200 Subject: [PATCH 12/23] WIP --- .../Scripts/IScriptOrchestrator.cs | 17 ++++++++- .../Scripts/ObservingScriptOrchestrator.cs | 12 +++++-- .../Scripts/ScriptServiceV2Orchestrator.cs | 36 ++++--------------- 3 files changed, 31 insertions(+), 34 deletions(-) diff --git a/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs index 1c6869181..8ef525154 100644 --- a/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs @@ -13,7 +13,7 @@ public interface IScriptOrchestrator } public interface IStructuredScriptOrchestrator { - Task<(ScriptStatus, ITicketForNextStatus)> StartScript(ExecuteScriptCommand command, CancellationToken scriptExecutionCancellationToken); + Task StartScript(ExecuteScriptCommand command, CancellationToken scriptExecutionCancellationToken); /// /// Returns a status or null when scriptExecutionCancellationToken is null. /// @@ -24,4 +24,19 @@ public interface IStructuredScriptOrchestrator { Task<(ScriptStatus, ITicketForNextStatus)> Cancel(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken); Task Finish(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken); } + + + public class StartScriptResult + { + public StartScriptResult(ScriptStatus status, ITicketForNextStatus ticketForNextStatus, bool scriptMayBeStarted) + { + Status = status; + TicketForNextStatus = ticketForNextStatus; + ScriptMayBeStarted = scriptMayBeStarted; + } + + public ScriptStatus Status { get; } + public ITicketForNextStatus TicketForNextStatus { get; } + public bool ScriptMayBeStarted { get; } + } } \ No newline at end of file diff --git a/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs index c05330841..ac90bd527 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs @@ -34,9 +34,15 @@ public ObservingScriptOrchestrator( public async Task ExecuteScript(ExecuteScriptCommand command, CancellationToken scriptExecutionCancellationToken) { - var (scriptStatus, ticketForNextStatus) = await structuredScriptOrchestrator.StartScript(command, scriptExecutionCancellationToken).ConfigureAwait(false); - - (scriptStatus, ticketForNextStatus) = await ObserveUntilCompleteThenFinish(scriptStatus, ticketForNextStatus, scriptExecutionCancellationToken).ConfigureAwait(false); + var startScriptResponse = await structuredScriptOrchestrator.StartScript(command, scriptExecutionCancellationToken).ConfigureAwait(false); + if (scriptExecutionCancellationToken.IsCancellationRequested) + { + if (!startScriptResponse.ScriptMayBeStarted) + { + scriptExecutionCancellationToken.ThrowIfCancellationRequested(); + } + } + var (scriptStatus, ticketForNextStatus) = await ObserveUntilCompleteThenFinish(startScriptResponse.Status, startScriptResponse.TicketForNextStatus, scriptExecutionCancellationToken).ConfigureAwait(false); if (scriptExecutionCancellationToken.IsCancellationRequested) { diff --git a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs index e3467b7a9..f9113e499 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs @@ -75,12 +75,7 @@ private ITicketForNextStatus MapToNextStatus(ScriptStatusResponseV2 scriptStatus } public ProcessState GetState(ScriptStatusResponseV2 response) => response.State; - public async Task<(ScriptStatus, ITicketForNextStatus)> StartScript(ExecuteScriptCommand command, CancellationToken scriptExecutionCancellationToken) - { - return Map(await _StartScript(command, scriptExecutionCancellationToken)); - } - - private async Task _StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) + public async Task StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) { var command = Map(executeScriptCommand); ScriptStatusResponseV2 scriptStatusResponse; @@ -112,6 +107,8 @@ void OnErrorAction(Exception ex) logger, clientOperationMetricsBuilder, scriptExecutionCancellationToken).ConfigureAwait(false); + + return new StartScriptResult(MapToScriptStatus(scriptStatusResponse), MapToNextStatus(scriptStatusResponse), true); } catch (Exception ex) when (scriptExecutionCancellationToken.IsCancellationRequested) { @@ -125,36 +122,15 @@ void OnErrorAction(Exception ex) if (!startScriptCallIsConnecting || startScriptCallIsBeingRetried) { - // We have to assume the script started executing and call CancelScript and CompleteScript - // We don't have a response so we need to create one to continue the execution flow - scriptStatusResponse = new ScriptStatusResponseV2( - command.ScriptTicket, - ProcessState.Pending, - ScriptExitCodes.RunningExitCode, - new List(), - 0); - - try - { - new ShortCutTakenHere(); - //await ObserveUntilCompleteThenFinish(scriptStatusResponse, scriptExecutionCancellationToken).ConfigureAwait(false); - } - catch (Exception observerUntilCompleteException) - { - // Throw an error so the caller knows that execution of the script was cancelled - throw new OperationCanceledException("Script execution was cancelled", observerUntilCompleteException); - } - - // Throw an error so the caller knows that execution of the script was cancelled - throw new OperationCanceledException("Script execution was cancelled"); + var scriptStatus = new ScriptStatus(ProcessState.Pending, null, new List()); + var defaultTicketForNextStatus = new DefaultTicketForNextStatus(command.ScriptTicket, 0, ScriptServiceVersion.ScriptServiceVersion2); + return new StartScriptResult(scriptStatus, defaultTicketForNextStatus, true); } // If the StartScript call was not in-flight or being retries then we know the script has not started executing on Tentacle // So can exit without calling CancelScript or CompleteScript throw new OperationCanceledException("Script execution was cancelled", ex); } - - return scriptStatusResponse; } public async Task<(ScriptStatus, ITicketForNextStatus)> GetStatus(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) From a5e0589643f48aca43b5117e5f28d00e77ea4a71 Mon Sep 17 00:00:00 2001 From: Samdanae Imran Date: Thu, 20 Jun 2024 17:01:22 +1200 Subject: [PATCH 13/23] Revert back to using tuples --- .../Scripts/IScriptOrchestrator.cs | 17 +---------------- .../Scripts/ObservingScriptOrchestrator.cs | 11 ++--------- .../Scripts/ScriptServiceV2Orchestrator.cs | 6 +++--- 3 files changed, 6 insertions(+), 28 deletions(-) diff --git a/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs index 8ef525154..1c6869181 100644 --- a/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs @@ -13,7 +13,7 @@ public interface IScriptOrchestrator } public interface IStructuredScriptOrchestrator { - Task StartScript(ExecuteScriptCommand command, CancellationToken scriptExecutionCancellationToken); + Task<(ScriptStatus, ITicketForNextStatus)> StartScript(ExecuteScriptCommand command, CancellationToken scriptExecutionCancellationToken); /// /// Returns a status or null when scriptExecutionCancellationToken is null. /// @@ -24,19 +24,4 @@ public interface IStructuredScriptOrchestrator { Task<(ScriptStatus, ITicketForNextStatus)> Cancel(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken); Task Finish(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken); } - - - public class StartScriptResult - { - public StartScriptResult(ScriptStatus status, ITicketForNextStatus ticketForNextStatus, bool scriptMayBeStarted) - { - Status = status; - TicketForNextStatus = ticketForNextStatus; - ScriptMayBeStarted = scriptMayBeStarted; - } - - public ScriptStatus Status { get; } - public ITicketForNextStatus TicketForNextStatus { get; } - public bool ScriptMayBeStarted { get; } - } } \ No newline at end of file diff --git a/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs index ac90bd527..cb4b10092 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs @@ -33,16 +33,9 @@ public ObservingScriptOrchestrator( public async Task ExecuteScript(ExecuteScriptCommand command, CancellationToken scriptExecutionCancellationToken) { + var (scriptStatus, ticketForNextStatus) = await structuredScriptOrchestrator.StartScript(command, scriptExecutionCancellationToken).ConfigureAwait(false); - var startScriptResponse = await structuredScriptOrchestrator.StartScript(command, scriptExecutionCancellationToken).ConfigureAwait(false); - if (scriptExecutionCancellationToken.IsCancellationRequested) - { - if (!startScriptResponse.ScriptMayBeStarted) - { - scriptExecutionCancellationToken.ThrowIfCancellationRequested(); - } - } - var (scriptStatus, ticketForNextStatus) = await ObserveUntilCompleteThenFinish(startScriptResponse.Status, startScriptResponse.TicketForNextStatus, scriptExecutionCancellationToken).ConfigureAwait(false); + (scriptStatus, _) = await ObserveUntilCompleteThenFinish(scriptStatus, ticketForNextStatus, scriptExecutionCancellationToken).ConfigureAwait(false); if (scriptExecutionCancellationToken.IsCancellationRequested) { diff --git a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs index f9113e499..9abcb5346 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs @@ -75,7 +75,7 @@ private ITicketForNextStatus MapToNextStatus(ScriptStatusResponseV2 scriptStatus } public ProcessState GetState(ScriptStatusResponseV2 response) => response.State; - public async Task StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ITicketForNextStatus)> StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) { var command = Map(executeScriptCommand); ScriptStatusResponseV2 scriptStatusResponse; @@ -108,7 +108,7 @@ void OnErrorAction(Exception ex) clientOperationMetricsBuilder, scriptExecutionCancellationToken).ConfigureAwait(false); - return new StartScriptResult(MapToScriptStatus(scriptStatusResponse), MapToNextStatus(scriptStatusResponse), true); + return (MapToScriptStatus(scriptStatusResponse), MapToNextStatus(scriptStatusResponse)); } catch (Exception ex) when (scriptExecutionCancellationToken.IsCancellationRequested) { @@ -124,7 +124,7 @@ void OnErrorAction(Exception ex) { var scriptStatus = new ScriptStatus(ProcessState.Pending, null, new List()); var defaultTicketForNextStatus = new DefaultTicketForNextStatus(command.ScriptTicket, 0, ScriptServiceVersion.ScriptServiceVersion2); - return new StartScriptResult(scriptStatus, defaultTicketForNextStatus, true); + return (scriptStatus, defaultTicketForNextStatus); } // If the StartScript call was not in-flight or being retries then we know the script has not started executing on Tentacle From e4ee43c1c68b5f69c89708a17811bec4c0361384 Mon Sep 17 00:00:00 2001 From: Luke Butters Date: Thu, 20 Jun 2024 15:02:10 +1000 Subject: [PATCH 14/23] Update k8s and v1 --- .../KubernetesScriptServiceV1Orchestrator.cs | 41 ++++--------------- .../Scripts/ScriptServiceV1Orchestrator.cs | 9 +--- 2 files changed, 10 insertions(+), 40 deletions(-) diff --git a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1Orchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1Orchestrator.cs index 716daaddd..54c413ff9 100644 --- a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1Orchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1Orchestrator.cs @@ -80,18 +80,12 @@ private ScriptStatus MapToScriptStatus(KubernetesScriptStatusResponseV1 scriptSt private ITicketForNextStatus MapToNextStatus(KubernetesScriptStatusResponseV1 scriptStatusResponse) { - return new DefaultTicketForNextStatus(scriptStatusResponse.ScriptTicket, scriptStatusResponse.NextLogSequence, ScriptServiceVersion.ScriptServiceVersion1); + return new DefaultTicketForNextStatus(scriptStatusResponse.ScriptTicket, scriptStatusResponse.NextLogSequence, ScriptServiceVersion.KubernetesScriptServiceVersion1); } - public async Task<(ScriptStatus, ITicketForNextStatus)> StartScript(ExecuteScriptCommand command, CancellationToken scriptExecutionCancellationToken) - { - return Map(await _StartScript(command, scriptExecutionCancellationToken)); - } - - private async Task _StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ITicketForNextStatus)> StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) { var command = Map(executeScriptCommand); - KubernetesScriptStatusResponseV1 scriptStatusResponse; var startScriptCallsConnectedCount = 0; try { @@ -112,7 +106,7 @@ void OnErrorAction(Exception ex) } } - scriptStatusResponse = await rpcCallExecutor.Execute( + var scriptStatusResponse = await rpcCallExecutor.Execute( retriesEnabled: clientOptions.RpcRetrySettings.RetriesEnabled, RpcCall.Create(nameof(IKubernetesScriptServiceV1.StartScript)), StartScriptAction, @@ -120,6 +114,8 @@ void OnErrorAction(Exception ex) logger, clientOperationMetricsBuilder, scriptExecutionCancellationToken).ConfigureAwait(false); + + return (MapToScriptStatus(scriptStatusResponse), MapToNextStatus(scriptStatusResponse)); } catch (Exception ex) when (scriptExecutionCancellationToken.IsCancellationRequested) { @@ -133,36 +129,15 @@ void OnErrorAction(Exception ex) if (!startScriptCallIsConnecting || startScriptCallIsBeingRetried) { - // We have to assume the script started executing and call CancelScript and CompleteScript - // We don't have a response so we need to create one to continue the execution flow - scriptStatusResponse = new KubernetesScriptStatusResponseV1( - command.ScriptTicket, - ProcessState.Pending, - ScriptExitCodes.RunningExitCode, - new List(), - 0); - - try - { - new ShortCutTakenHere(); - //await ObserveUntilCompleteThenFinish(scriptStatusResponse, scriptExecutionCancellationToken).ConfigureAwait(false); - } - catch (Exception observerUntilCompleteException) - { - // Throw an error so the caller knows that execution of the script was cancelled - throw new OperationCanceledException("Script execution was cancelled", observerUntilCompleteException); - } - - // Throw an error so the caller knows that execution of the script was cancelled - throw new OperationCanceledException("Script execution was cancelled"); + var scriptStatus = new ScriptStatus(ProcessState.Pending, null, new List()); + var defaultTicketForNextStatus = new DefaultTicketForNextStatus(command.ScriptTicket, 0, ScriptServiceVersion.KubernetesScriptServiceVersion1); + return (scriptStatus, defaultTicketForNextStatus); } // If the StartScript call was not in-flight or being retries then we know the script has not started executing on Tentacle // So can exit without calling CancelScript or CompleteScript throw new OperationCanceledException("Script execution was cancelled", ex); } - - return scriptStatusResponse; } public async Task<(ScriptStatus, ITicketForNextStatus)> GetStatus(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) diff --git a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs index 58799c694..551247882 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs @@ -66,12 +66,7 @@ private ITicketForNextStatus MapToNextStatus(ScriptStatusResponse scriptStatusRe return (MapToScriptStatus(r), MapToNextStatus(r)); } - public async Task<(ScriptStatus, ITicketForNextStatus)> StartScript(ExecuteScriptCommand command, CancellationToken scriptExecutionCancellationToken) - { - return Map(await _StartScript(command, scriptExecutionCancellationToken)); - } - - private async Task _StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ITicketForNextStatus)> StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) { var command = Map(executeScriptCommand); var scriptTicket = await rpcCallExecutor.ExecuteWithNoRetries( @@ -86,7 +81,7 @@ private async Task _StartScript(ExecuteScriptCommand execu clientOperationMetricsBuilder, scriptExecutionCancellationToken).ConfigureAwait(false); - return new ScriptStatusResponse(scriptTicket, ProcessState.Pending, 0, new List(), 0); + return Map(new ScriptStatusResponse(scriptTicket, ProcessState.Pending, 0, new List(), 0)); } public async Task<(ScriptStatus, ITicketForNextStatus)> GetStatus(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) From 9c0f285df784e024424cf8da5c18631a3ab2b66e Mon Sep 17 00:00:00 2001 From: Samdanae Imran Date: Thu, 20 Jun 2024 17:05:21 +1200 Subject: [PATCH 15/23] WIP --- ...ernetesScriptServiceV1AlphaOrchestrator.cs | 41 ++++--------------- 1 file changed, 8 insertions(+), 33 deletions(-) diff --git a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs index 9a3c65c03..365a1f9b1 100644 --- a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs @@ -84,7 +84,7 @@ private ScriptStatus MapToScriptStatus(KubernetesScriptStatusResponseV1Alpha scr private ITicketForNextStatus MapToNextStatus(KubernetesScriptStatusResponseV1Alpha scriptStatusResponse) { - return new DefaultTicketForNextStatus(scriptStatusResponse.ScriptTicket, scriptStatusResponse.NextLogSequence, ScriptServiceVersion.ScriptServiceVersion1); + return new DefaultTicketForNextStatus(scriptStatusResponse.ScriptTicket, scriptStatusResponse.NextLogSequence, ScriptServiceVersion.KubernetesScriptServiceVersion1Alpha); } (ScriptStatus, ITicketForNextStatus) Map(KubernetesScriptStatusResponseV1Alpha r) @@ -92,15 +92,9 @@ private ITicketForNextStatus MapToNextStatus(KubernetesScriptStatusResponseV1Alp return (MapToScriptStatus(r), MapToNextStatus(r)); } - public async Task<(ScriptStatus, ITicketForNextStatus)> StartScript(ExecuteScriptCommand command, CancellationToken scriptExecutionCancellationToken) - { - return Map(await _StartScript(command, scriptExecutionCancellationToken)); - } - - private async Task _StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ITicketForNextStatus)> StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) { var command = Map(executeScriptCommand); - KubernetesScriptStatusResponseV1Alpha scriptStatusResponse; var startScriptCallsConnectedCount = 0; try { @@ -121,7 +115,7 @@ void OnErrorAction(Exception ex) } } - scriptStatusResponse = await rpcCallExecutor.Execute( + var scriptStatusResponse = await rpcCallExecutor.Execute( retriesEnabled: clientOptions.RpcRetrySettings.RetriesEnabled, RpcCall.Create(nameof(IKubernetesScriptServiceV1Alpha.StartScript)), StartScriptAction, @@ -129,6 +123,8 @@ void OnErrorAction(Exception ex) logger, clientOperationMetricsBuilder, scriptExecutionCancellationToken).ConfigureAwait(false); + + return (MapToScriptStatus(scriptStatusResponse), MapToNextStatus(scriptStatusResponse)); } catch (Exception ex) when (scriptExecutionCancellationToken.IsCancellationRequested) { @@ -142,36 +138,15 @@ void OnErrorAction(Exception ex) if (!startScriptCallIsConnecting || startScriptCallIsBeingRetried) { - // We have to assume the script started executing and call CancelScript and CompleteScript - // We don't have a response so we need to create one to continue the execution flow - scriptStatusResponse = new KubernetesScriptStatusResponseV1Alpha( - command.ScriptTicket, - ProcessState.Pending, - ScriptExitCodes.RunningExitCode, - new List(), - 0); - - try - { - new ShortCutTakenHere(); - //await ObserveUntilCompleteThenFinish(scriptStatusResponse, scriptExecutionCancellationToken).ConfigureAwait(false); - } - catch (Exception observerUntilCompleteException) - { - // Throw an error so the caller knows that execution of the script was cancelled - throw new OperationCanceledException("Script execution was cancelled", observerUntilCompleteException); - } - - // Throw an error so the caller knows that execution of the script was cancelled - throw new OperationCanceledException("Script execution was cancelled"); + var scriptStatus = new ScriptStatus(ProcessState.Pending, null, new List()); + var defaultTicketForNextStatus = new DefaultTicketForNextStatus(command.ScriptTicket, 0, ScriptServiceVersion.KubernetesScriptServiceVersion1Alpha); + return (scriptStatus, defaultTicketForNextStatus); } // If the StartScript call was not in-flight or being retries then we know the script has not started executing on Tentacle // So can exit without calling CancelScript or CompleteScript throw new OperationCanceledException("Script execution was cancelled", ex); } - - return scriptStatusResponse; } public async Task<(ScriptStatus, ITicketForNextStatus)> GetStatus(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) From 4796b056c950c2dd4ef9dd18ba14d3bc91e81542 Mon Sep 17 00:00:00 2001 From: Luke Butters Date: Thu, 20 Jun 2024 21:42:11 +1000 Subject: [PATCH 16/23] Fix ssv2 --- .../Scripts/ObservingScriptOrchestrator.cs | 1 - .../Scripts/ScriptServiceV2Orchestrator.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs index cb4b10092..d304290bb 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs @@ -57,7 +57,6 @@ public async Task ExecuteScript(ExecuteScriptCommand comm var (lastStatusResponse, lastTicketForNextStatus) = await ObserveUntilComplete(scriptStatus, ticketForNextStatus, scriptExecutionCancellationToken).ConfigureAwait(false); await onScriptCompleted(scriptExecutionCancellationToken).ConfigureAwait(false); - lastStatusResponse = await structuredScriptOrchestrator.Finish(lastTicketForNextStatus, scriptExecutionCancellationToken).ConfigureAwait(false) ?? lastStatusResponse; diff --git a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs index 9abcb5346..45c8d87a9 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs @@ -161,7 +161,7 @@ async Task GetStatusAction(CancellationToken ct) public async Task<(ScriptStatus, ITicketForNextStatus)> Cancel(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { - return Map(await _GetStatus(lastStatusResponse, scriptExecutionCancellationToken)); + return Map(await _Cancel(lastStatusResponse, scriptExecutionCancellationToken)); } private async Task _Cancel(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) From 33bb1b43111196c7871daaeb51557eb0a4c79c76 Mon Sep 17 00:00:00 2001 From: Samdanae Imran Date: Fri, 21 Jun 2024 12:47:19 +1200 Subject: [PATCH 17/23] Implement EventDrivenScriptExecutor --- .../EventDrivenScriptExecutor.cs | 61 ++++++++++++------- .../IEventDrivenScriptExecutor.cs | 2 +- 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/source/Octopus.Tentacle.Client/EventDrivenScriptExecutor.cs b/source/Octopus.Tentacle.Client/EventDrivenScriptExecutor.cs index 35e9c247a..eb44efc22 100644 --- a/source/Octopus.Tentacle.Client/EventDrivenScriptExecutor.cs +++ b/source/Octopus.Tentacle.Client/EventDrivenScriptExecutor.cs @@ -63,44 +63,63 @@ internal EventDrivenScriptExecutor(ITentacleClientTaskLog logger, return (new ScriptStatus(ProcessState.Complete, ScriptExitCodes.UnknownScriptExitCode, new List()), new DefaultTicketForNextStatus(new ScriptTicket(Guid.NewGuid().ToString()), 0, ScriptServiceVersion.ScriptServiceVersion1)); } - var scriptOrchestratorFactory = new ScriptOrchestratorFactory(clientsHolder, - new DefaultScriptObserverBackoffStrategy(), - rpcCallExecutor, - operationMetricsBuilder, - status => { }, - token => Task.CompletedTask, - onCancellationAbandonCompleteScriptAfter, - clientOptions, - logger); + var scriptOrchestratorFactory = GetNewScriptOrchestratorFactory(operationMetricsBuilder); var orchestrator = scriptOrchestratorFactory.CreateOrchestrator(scriptServiceToUse); - - // and return stuff. - throw new System.NotImplementedException(); + return await orchestrator.StartScript(executeScriptCommand, cancellationToken); } - public Task<(ScriptStatus, ITicketForNextStatus)> GetStatus(ITicketForNextStatus ticketForNextNextStatus, CancellationToken cancellationToken) + public async Task<(ScriptStatus, ITicketForNextStatus)> GetStatus(ITicketForNextStatus ticketForNextNextStatus, CancellationToken cancellationToken) { - - ScriptOrchestratorFactory scriptOrchestratorFactory = null!; - var orch = scriptOrchestratorFactory.CreateOrchestrator(ticketForNextNextStatus.WhichService); + var operationMetricsBuilder = ClientOperationMetricsBuilder.Start(); - throw new System.NotImplementedException(); + var scriptOrchestratorFactory = GetNewScriptOrchestratorFactory(operationMetricsBuilder); + + var orchestrator = scriptOrchestratorFactory.CreateOrchestrator(ticketForNextNextStatus.WhichService); + + return await orchestrator.GetStatus(ticketForNextNextStatus, cancellationToken); } - public Task<(ScriptStatus, ITicketForNextStatus)> CancelScript(ITicketForNextStatus ticketForNextNextStatus, CancellationToken cancellationToken) + public async Task<(ScriptStatus, ITicketForNextStatus)> CancelScript(ITicketForNextStatus ticketForNextNextStatus, CancellationToken cancellationToken) { - throw new System.NotImplementedException(); + var operationMetricsBuilder = ClientOperationMetricsBuilder.Start(); + + var scriptOrchestratorFactory = GetNewScriptOrchestratorFactory(operationMetricsBuilder); + + var orchestrator = scriptOrchestratorFactory.CreateOrchestrator(ticketForNextNextStatus.WhichService); + + return await orchestrator.Cancel(ticketForNextNextStatus, cancellationToken); } public Task<(ScriptStatus, ITicketForNextStatus)> CancelScript(ScriptTicket scriptTicket, CancellationToken cancellationToken) { + var operationMetricsBuilder = ClientOperationMetricsBuilder.Start(); throw new System.NotImplementedException(); } - public Task CleanUpScript(ScriptTicket scriptTicket, CancellationToken cancellationToken) + public async Task CleanUpScript(ITicketForNextStatus ticketForNextNextStatus, CancellationToken cancellationToken) { - throw new System.NotImplementedException(); + var operationMetricsBuilder = ClientOperationMetricsBuilder.Start(); + + var scriptOrchestratorFactory = GetNewScriptOrchestratorFactory(operationMetricsBuilder); + + var orchestrator = scriptOrchestratorFactory.CreateOrchestrator(ticketForNextNextStatus.WhichService); + + await orchestrator.Finish(ticketForNextNextStatus, cancellationToken); } + + ScriptOrchestratorFactory GetNewScriptOrchestratorFactory(ClientOperationMetricsBuilder operationMetricsBuilder) + { + return new ScriptOrchestratorFactory(clientsHolder, + new DefaultScriptObserverBackoffStrategy(), + rpcCallExecutor, + operationMetricsBuilder, + status => { }, + token => Task.CompletedTask, + onCancellationAbandonCompleteScriptAfter, + clientOptions, + logger); + } + } } \ No newline at end of file diff --git a/source/Octopus.Tentacle.Client/IEventDrivenScriptExecutor.cs b/source/Octopus.Tentacle.Client/IEventDrivenScriptExecutor.cs index d019ef24e..dd2f39475 100644 --- a/source/Octopus.Tentacle.Client/IEventDrivenScriptExecutor.cs +++ b/source/Octopus.Tentacle.Client/IEventDrivenScriptExecutor.cs @@ -30,7 +30,7 @@ public interface IEventDrivenScriptExecutor /// Task<(ScriptStatus, ITicketForNextStatus)> CancelScript(ScriptTicket scriptTicket, CancellationToken cancellationToken); - Task CleanUpScript(ScriptTicket scriptTicket, CancellationToken cancellationToken); + Task CleanUpScript(ITicketForNextStatus ticketForNextNextStatus, CancellationToken cancellationToken); } public enum HasStartScriptBeenCalledBefore From 9003b32ae5ad7d37217071808083ce47dd71c3c6 Mon Sep 17 00:00:00 2001 From: Luke Butters Date: Fri, 21 Jun 2024 14:42:30 +1000 Subject: [PATCH 18/23] minor refactor --- ...ketForNextStatus.cs => ICommandContext.cs} | 6 ++-- .../EventDrivenScriptExecutor.cs | 12 ++++---- .../IEventDrivenScriptExecutor.cs | 10 +++---- .../Scripts/IScriptOrchestrator.cs | 15 ---------- .../Scripts/IStructuredScriptExecutor.cs | 22 +++++++++++++++ ...ernetesScriptServiceV1AlphaOrchestrator.cs | 26 ++++++++--------- .../KubernetesScriptServiceV1Orchestrator.cs | 26 ++++++++--------- .../Scripts/ObservingScriptOrchestrator.cs | 26 ++++++++--------- .../Scripts/ScriptOrchestratorFactory.cs | 10 +++---- ...estrator.cs => ScriptServiceV1Executor.cs} | 26 ++++++++--------- .../Scripts/ScriptServiceV2Orchestrator.cs | 28 +++++++++---------- 11 files changed, 107 insertions(+), 100 deletions(-) rename source/Octopus.Tentacle.Client/EventDriven/{ITicketForNextStatus.cs => ICommandContext.cs} (84%) create mode 100644 source/Octopus.Tentacle.Client/Scripts/IStructuredScriptExecutor.cs rename source/Octopus.Tentacle.Client/Scripts/{ScriptServiceV1Orchestrator.cs => ScriptServiceV1Executor.cs} (77%) diff --git a/source/Octopus.Tentacle.Client/EventDriven/ITicketForNextStatus.cs b/source/Octopus.Tentacle.Client/EventDriven/ICommandContext.cs similarity index 84% rename from source/Octopus.Tentacle.Client/EventDriven/ITicketForNextStatus.cs rename to source/Octopus.Tentacle.Client/EventDriven/ICommandContext.cs index 3d13572f0..beeee8887 100644 --- a/source/Octopus.Tentacle.Client/EventDriven/ITicketForNextStatus.cs +++ b/source/Octopus.Tentacle.Client/EventDriven/ICommandContext.cs @@ -3,7 +3,7 @@ namespace Octopus.Tentacle.Client.EventDriven { - public interface ITicketForNextStatus + public interface ICommandContext { ScriptTicket ScriptTicket { get; } @@ -15,9 +15,9 @@ public interface ITicketForNextStatus // wants to continue script execution. } - public class DefaultTicketForNextStatus : ITicketForNextStatus + public class DefaultCommandContext : ICommandContext { - public DefaultTicketForNextStatus(ScriptTicket scriptTicket, + public DefaultCommandContext(ScriptTicket scriptTicket, long nextLogSequence, ScriptServiceVersion whichService) { diff --git a/source/Octopus.Tentacle.Client/EventDrivenScriptExecutor.cs b/source/Octopus.Tentacle.Client/EventDrivenScriptExecutor.cs index eb44efc22..b43ccaf75 100644 --- a/source/Octopus.Tentacle.Client/EventDrivenScriptExecutor.cs +++ b/source/Octopus.Tentacle.Client/EventDrivenScriptExecutor.cs @@ -47,7 +47,7 @@ internal EventDrivenScriptExecutor(ITentacleClientTaskLog logger, rpcCallExecutor = RpcCallExecutorFactory.Create(this.clientOptions.RpcRetrySettings.RetryDuration, this.tentacleClientObserver); } - public async Task<(ScriptStatus, ITicketForNextStatus)> StartScript(ExecuteScriptCommand executeScriptCommand, + public async Task<(ScriptStatus, ICommandContext)> StartScript(ExecuteScriptCommand executeScriptCommand, HasStartScriptBeenCalledBefore hasStartScriptBeenCalledBefore, CancellationToken cancellationToken) { @@ -60,7 +60,7 @@ internal EventDrivenScriptExecutor(ITentacleClientTaskLog logger, // And start the script if (scriptServiceToUse == ScriptServiceVersion.ScriptServiceVersion1 && hasStartScriptBeenCalledBefore == HasStartScriptBeenCalledBefore.ItMayHaveBeen) { - return (new ScriptStatus(ProcessState.Complete, ScriptExitCodes.UnknownScriptExitCode, new List()), new DefaultTicketForNextStatus(new ScriptTicket(Guid.NewGuid().ToString()), 0, ScriptServiceVersion.ScriptServiceVersion1)); + return (new ScriptStatus(ProcessState.Complete, ScriptExitCodes.UnknownScriptExitCode, new List()), new DefaultCommandContext(new ScriptTicket(Guid.NewGuid().ToString()), 0, ScriptServiceVersion.ScriptServiceVersion1)); } var scriptOrchestratorFactory = GetNewScriptOrchestratorFactory(operationMetricsBuilder); @@ -69,7 +69,7 @@ internal EventDrivenScriptExecutor(ITentacleClientTaskLog logger, return await orchestrator.StartScript(executeScriptCommand, cancellationToken); } - public async Task<(ScriptStatus, ITicketForNextStatus)> GetStatus(ITicketForNextStatus ticketForNextNextStatus, CancellationToken cancellationToken) + public async Task<(ScriptStatus, ICommandContext)> GetStatus(ICommandContext ticketForNextNextStatus, CancellationToken cancellationToken) { var operationMetricsBuilder = ClientOperationMetricsBuilder.Start(); @@ -80,7 +80,7 @@ internal EventDrivenScriptExecutor(ITentacleClientTaskLog logger, return await orchestrator.GetStatus(ticketForNextNextStatus, cancellationToken); } - public async Task<(ScriptStatus, ITicketForNextStatus)> CancelScript(ITicketForNextStatus ticketForNextNextStatus, CancellationToken cancellationToken) + public async Task<(ScriptStatus, ICommandContext)> CancelScript(ICommandContext ticketForNextNextStatus, CancellationToken cancellationToken) { var operationMetricsBuilder = ClientOperationMetricsBuilder.Start(); @@ -91,13 +91,13 @@ internal EventDrivenScriptExecutor(ITentacleClientTaskLog logger, return await orchestrator.Cancel(ticketForNextNextStatus, cancellationToken); } - public Task<(ScriptStatus, ITicketForNextStatus)> CancelScript(ScriptTicket scriptTicket, CancellationToken cancellationToken) + public Task<(ScriptStatus, ICommandContext)> CancelScript(ScriptTicket scriptTicket, CancellationToken cancellationToken) { var operationMetricsBuilder = ClientOperationMetricsBuilder.Start(); throw new System.NotImplementedException(); } - public async Task CleanUpScript(ITicketForNextStatus ticketForNextNextStatus, CancellationToken cancellationToken) + public async Task CleanUpScript(ICommandContext ticketForNextNextStatus, CancellationToken cancellationToken) { var operationMetricsBuilder = ClientOperationMetricsBuilder.Start(); diff --git a/source/Octopus.Tentacle.Client/IEventDrivenScriptExecutor.cs b/source/Octopus.Tentacle.Client/IEventDrivenScriptExecutor.cs index dd2f39475..24e22991d 100644 --- a/source/Octopus.Tentacle.Client/IEventDrivenScriptExecutor.cs +++ b/source/Octopus.Tentacle.Client/IEventDrivenScriptExecutor.cs @@ -8,11 +8,11 @@ namespace Octopus.Tentacle.Client { public interface IEventDrivenScriptExecutor { - Task<(ScriptStatus, ITicketForNextStatus)> StartScript(ExecuteScriptCommand executeScriptCommand, + Task<(ScriptStatus, ICommandContext)> StartScript(ExecuteScriptCommand executeScriptCommand, HasStartScriptBeenCalledBefore hasStartScriptBeenCalledBefore, CancellationToken cancellationToken); - Task<(ScriptStatus, ITicketForNextStatus)> GetStatus(ITicketForNextStatus ticketForNextNextStatus, CancellationToken cancellationToken); + Task<(ScriptStatus, ICommandContext)> GetStatus(ICommandContext ticketForNextNextStatus, CancellationToken cancellationToken); /// /// Cancel script will still send back the rest of the logs, hence the ticketForNextNextStatus argument. @@ -20,7 +20,7 @@ public interface IEventDrivenScriptExecutor /// /// /// - Task<(ScriptStatus, ITicketForNextStatus)> CancelScript(ITicketForNextStatus ticketForNextNextStatus, CancellationToken cancellationToken); + Task<(ScriptStatus, ICommandContext)> CancelScript(ICommandContext ticketForNextNextStatus, CancellationToken cancellationToken); /// /// Use this cancel method if only the ScriptTicket is known, e.g. we called StartScript but never got a response. @@ -28,9 +28,9 @@ public interface IEventDrivenScriptExecutor /// /// /// - Task<(ScriptStatus, ITicketForNextStatus)> CancelScript(ScriptTicket scriptTicket, CancellationToken cancellationToken); + Task<(ScriptStatus, ICommandContext)> CancelScript(ScriptTicket scriptTicket, CancellationToken cancellationToken); - Task CleanUpScript(ITicketForNextStatus ticketForNextNextStatus, CancellationToken cancellationToken); + Task CleanUpScript(ICommandContext ticketForNextNextStatus, CancellationToken cancellationToken); } public enum HasStartScriptBeenCalledBefore diff --git a/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs index 1c6869181..9bf1ce458 100644 --- a/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/IScriptOrchestrator.cs @@ -1,9 +1,7 @@ using System; using System.Threading; using System.Threading.Tasks; -using Octopus.Tentacle.Client.EventDriven; using Octopus.Tentacle.Client.Scripts.Models; -using Octopus.Tentacle.Contracts; namespace Octopus.Tentacle.Client.Scripts { @@ -11,17 +9,4 @@ public interface IScriptOrchestrator { Task ExecuteScript(ExecuteScriptCommand command, CancellationToken scriptExecutionCancellationToken); } - - public interface IStructuredScriptOrchestrator { - Task<(ScriptStatus, ITicketForNextStatus)> StartScript(ExecuteScriptCommand command, CancellationToken scriptExecutionCancellationToken); - /// - /// Returns a status or null when scriptExecutionCancellationToken is null. - /// - /// - /// - /// - Task<(ScriptStatus, ITicketForNextStatus)> GetStatus(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken); - Task<(ScriptStatus, ITicketForNextStatus)> Cancel(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken); - Task Finish(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken); - } } \ No newline at end of file diff --git a/source/Octopus.Tentacle.Client/Scripts/IStructuredScriptExecutor.cs b/source/Octopus.Tentacle.Client/Scripts/IStructuredScriptExecutor.cs new file mode 100644 index 000000000..049bccc7b --- /dev/null +++ b/source/Octopus.Tentacle.Client/Scripts/IStructuredScriptExecutor.cs @@ -0,0 +1,22 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Octopus.Tentacle.Client.EventDriven; +using Octopus.Tentacle.Client.Scripts.Models; +using Octopus.Tentacle.Contracts; + +namespace Octopus.Tentacle.Client.Scripts +{ + public interface IStructuredScriptExecutor { + Task<(ScriptStatus, ICommandContext)> StartScript(ExecuteScriptCommand command, CancellationToken scriptExecutionCancellationToken); + /// + /// Returns a status or null when scriptExecutionCancellationToken is null. + /// + /// + /// + /// + Task<(ScriptStatus, ICommandContext)> GetStatus(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken); + Task<(ScriptStatus, ICommandContext)> Cancel(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken); + Task Finish(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken); + } +} \ No newline at end of file diff --git a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs index 365a1f9b1..48e59c8ff 100644 --- a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs @@ -16,7 +16,7 @@ namespace Octopus.Tentacle.Client.Scripts { - class KubernetesScriptServiceV1AlphaOrchestrator : IStructuredScriptOrchestrator + class KubernetesScriptServiceV1AlphaExecutor : IStructuredScriptExecutor { readonly IAsyncClientKubernetesScriptServiceV1Alpha clientKubernetesScriptServiceV1Alpha; readonly RpcCallExecutor rpcCallExecutor; @@ -25,7 +25,7 @@ class KubernetesScriptServiceV1AlphaOrchestrator : IStructuredScriptOrchestrator readonly ITentacleClientTaskLog logger; readonly TentacleClientOptions clientOptions; - public KubernetesScriptServiceV1AlphaOrchestrator( + public KubernetesScriptServiceV1AlphaExecutor( IAsyncClientKubernetesScriptServiceV1Alpha clientKubernetesScriptServiceV1Alpha, RpcCallExecutor rpcCallExecutor, ClientOperationMetricsBuilder clientOperationMetricsBuilder, @@ -82,17 +82,17 @@ private ScriptStatus MapToScriptStatus(KubernetesScriptStatusResponseV1Alpha scr return new ScriptStatus(scriptStatusResponse.State, scriptStatusResponse.ExitCode, scriptStatusResponse.Logs); } - private ITicketForNextStatus MapToNextStatus(KubernetesScriptStatusResponseV1Alpha scriptStatusResponse) + private ICommandContext MapToNextStatus(KubernetesScriptStatusResponseV1Alpha scriptStatusResponse) { - return new DefaultTicketForNextStatus(scriptStatusResponse.ScriptTicket, scriptStatusResponse.NextLogSequence, ScriptServiceVersion.KubernetesScriptServiceVersion1Alpha); + return new DefaultCommandContext(scriptStatusResponse.ScriptTicket, scriptStatusResponse.NextLogSequence, ScriptServiceVersion.KubernetesScriptServiceVersion1Alpha); } - (ScriptStatus, ITicketForNextStatus) Map(KubernetesScriptStatusResponseV1Alpha r) + (ScriptStatus, ICommandContext) Map(KubernetesScriptStatusResponseV1Alpha r) { return (MapToScriptStatus(r), MapToNextStatus(r)); } - public async Task<(ScriptStatus, ITicketForNextStatus)> StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ICommandContext)> StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) { var command = Map(executeScriptCommand); var startScriptCallsConnectedCount = 0; @@ -139,7 +139,7 @@ void OnErrorAction(Exception ex) if (!startScriptCallIsConnecting || startScriptCallIsBeingRetried) { var scriptStatus = new ScriptStatus(ProcessState.Pending, null, new List()); - var defaultTicketForNextStatus = new DefaultTicketForNextStatus(command.ScriptTicket, 0, ScriptServiceVersion.KubernetesScriptServiceVersion1Alpha); + var defaultTicketForNextStatus = new DefaultCommandContext(command.ScriptTicket, 0, ScriptServiceVersion.KubernetesScriptServiceVersion1Alpha); return (scriptStatus, defaultTicketForNextStatus); } @@ -149,12 +149,12 @@ void OnErrorAction(Exception ex) } } - public async Task<(ScriptStatus, ITicketForNextStatus)> GetStatus(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ICommandContext)> GetStatus(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { return Map(await _GetStatus(lastStatusResponse, scriptExecutionCancellationToken)); } - async Task _GetStatus(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + async Task _GetStatus(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { async Task GetStatusAction(CancellationToken ct) { @@ -173,12 +173,12 @@ async Task GetStatusAction(CancellationTo scriptExecutionCancellationToken).ConfigureAwait(false); } - public async Task<(ScriptStatus, ITicketForNextStatus)> Cancel(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ICommandContext)> Cancel(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { return Map(await _Cancel(lastStatusResponse, scriptExecutionCancellationToken)); } - async Task _Cancel(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + async Task _Cancel(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { async Task CancelScriptAction(CancellationToken ct) { @@ -203,13 +203,13 @@ async Task CancelScriptAction(Cancellatio } - public async Task Finish(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task Finish(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { await _Finish(lastStatusResponse, scriptExecutionCancellationToken); return null; } - async Task _Finish(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + async Task _Finish(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { try { diff --git a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1Orchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1Orchestrator.cs index 54c413ff9..93a043ca4 100644 --- a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1Orchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1Orchestrator.cs @@ -16,7 +16,7 @@ namespace Octopus.Tentacle.Client.Scripts { - class KubernetesScriptServiceV1Orchestrator : IStructuredScriptOrchestrator + class KubernetesScriptServiceV1Executor : IStructuredScriptExecutor { readonly IAsyncClientKubernetesScriptServiceV1 clientKubernetesScriptServiceV1; readonly RpcCallExecutor rpcCallExecutor; @@ -25,7 +25,7 @@ class KubernetesScriptServiceV1Orchestrator : IStructuredScriptOrchestrator readonly ITentacleClientTaskLog logger; readonly TentacleClientOptions clientOptions; - public KubernetesScriptServiceV1Orchestrator( + public KubernetesScriptServiceV1Executor( IAsyncClientKubernetesScriptServiceV1 clientKubernetesScriptServiceV1, RpcCallExecutor rpcCallExecutor, ClientOperationMetricsBuilder clientOperationMetricsBuilder, @@ -68,7 +68,7 @@ StartKubernetesScriptCommandV1 Map(ExecuteScriptCommand command) kubernetesScriptCommand.Files.ToArray(), kubernetesScriptCommand.IsRawScript); } - (ScriptStatus, ITicketForNextStatus) Map(KubernetesScriptStatusResponseV1 r) + (ScriptStatus, ICommandContext) Map(KubernetesScriptStatusResponseV1 r) { return (MapToScriptStatus(r), MapToNextStatus(r)); } @@ -78,12 +78,12 @@ private ScriptStatus MapToScriptStatus(KubernetesScriptStatusResponseV1 scriptSt return new ScriptStatus(scriptStatusResponse.State, scriptStatusResponse.ExitCode, scriptStatusResponse.Logs); } - private ITicketForNextStatus MapToNextStatus(KubernetesScriptStatusResponseV1 scriptStatusResponse) + private ICommandContext MapToNextStatus(KubernetesScriptStatusResponseV1 scriptStatusResponse) { - return new DefaultTicketForNextStatus(scriptStatusResponse.ScriptTicket, scriptStatusResponse.NextLogSequence, ScriptServiceVersion.KubernetesScriptServiceVersion1); + return new DefaultCommandContext(scriptStatusResponse.ScriptTicket, scriptStatusResponse.NextLogSequence, ScriptServiceVersion.KubernetesScriptServiceVersion1); } - public async Task<(ScriptStatus, ITicketForNextStatus)> StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ICommandContext)> StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) { var command = Map(executeScriptCommand); var startScriptCallsConnectedCount = 0; @@ -130,7 +130,7 @@ void OnErrorAction(Exception ex) if (!startScriptCallIsConnecting || startScriptCallIsBeingRetried) { var scriptStatus = new ScriptStatus(ProcessState.Pending, null, new List()); - var defaultTicketForNextStatus = new DefaultTicketForNextStatus(command.ScriptTicket, 0, ScriptServiceVersion.KubernetesScriptServiceVersion1); + var defaultTicketForNextStatus = new DefaultCommandContext(command.ScriptTicket, 0, ScriptServiceVersion.KubernetesScriptServiceVersion1); return (scriptStatus, defaultTicketForNextStatus); } @@ -140,12 +140,12 @@ void OnErrorAction(Exception ex) } } - public async Task<(ScriptStatus, ITicketForNextStatus)> GetStatus(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ICommandContext)> GetStatus(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { return Map(await _GetStatus(lastStatusResponse, scriptExecutionCancellationToken)); } - private async Task _GetStatus(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + private async Task _GetStatus(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { async Task GetStatusAction(CancellationToken ct) { @@ -164,12 +164,12 @@ async Task GetStatusAction(CancellationToken c scriptExecutionCancellationToken).ConfigureAwait(false); } - public async Task<(ScriptStatus, ITicketForNextStatus)> Cancel(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ICommandContext)> Cancel(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { return Map(await _GetStatus(lastStatusResponse, scriptExecutionCancellationToken)); } - private async Task _Cancel(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + private async Task _Cancel(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { async Task CancelScriptAction(CancellationToken ct) { @@ -193,13 +193,13 @@ async Task CancelScriptAction(CancellationToke CancellationToken.None).ConfigureAwait(false); } - public async Task Finish(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task Finish(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { await _Finish(lastStatusResponse, scriptExecutionCancellationToken); return null; } - private async Task _Finish(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + private async Task _Finish(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { try { diff --git a/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs index d304290bb..0e3caab86 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs @@ -16,15 +16,15 @@ public sealed class ObservingScriptOrchestrator : IScriptOrchestrator readonly OnScriptCompleted onScriptCompleted; readonly ITentacleClientTaskLog logger; - IStructuredScriptOrchestrator structuredScriptOrchestrator; + IStructuredScriptExecutor structuredScriptExecutor; public ObservingScriptOrchestrator( IScriptObserverBackoffStrategy scriptObserverBackOffStrategy, OnScriptStatusResponseReceived onScriptStatusResponseReceived, OnScriptCompleted onScriptCompleted, - IStructuredScriptOrchestrator structuredScriptOrchestrator, ITentacleClientTaskLog logger) + IStructuredScriptExecutor structuredScriptExecutor, ITentacleClientTaskLog logger) { - this.structuredScriptOrchestrator = structuredScriptOrchestrator; + this.structuredScriptExecutor = structuredScriptExecutor; this.logger = logger; this.scriptObserverBackOffStrategy = scriptObserverBackOffStrategy; this.onScriptStatusResponseReceived = onScriptStatusResponseReceived; @@ -33,7 +33,7 @@ public ObservingScriptOrchestrator( public async Task ExecuteScript(ExecuteScriptCommand command, CancellationToken scriptExecutionCancellationToken) { - var (scriptStatus, ticketForNextStatus) = await structuredScriptOrchestrator.StartScript(command, scriptExecutionCancellationToken).ConfigureAwait(false); + var (scriptStatus, ticketForNextStatus) = await structuredScriptExecutor.StartScript(command, scriptExecutionCancellationToken).ConfigureAwait(false); (scriptStatus, _) = await ObserveUntilCompleteThenFinish(scriptStatus, ticketForNextStatus, scriptExecutionCancellationToken).ConfigureAwait(false); @@ -47,30 +47,30 @@ public async Task ExecuteScript(ExecuteScriptCommand comm return new ScriptExecutionResult(scriptStatus.State, scriptStatus.ExitCode!.Value); } - async Task<(ScriptStatus, ITicketForNextStatus)> ObserveUntilCompleteThenFinish( + async Task<(ScriptStatus, ICommandContext)> ObserveUntilCompleteThenFinish( ScriptStatus scriptStatus, - ITicketForNextStatus ticketForNextStatus, + ICommandContext commandContext, CancellationToken scriptExecutionCancellationToken) { OnScriptStatusResponseReceived(scriptStatus); - var (lastStatusResponse, lastTicketForNextStatus) = await ObserveUntilComplete(scriptStatus, ticketForNextStatus, scriptExecutionCancellationToken).ConfigureAwait(false); + var (lastStatusResponse, lastTicketForNextStatus) = await ObserveUntilComplete(scriptStatus, commandContext, scriptExecutionCancellationToken).ConfigureAwait(false); await onScriptCompleted(scriptExecutionCancellationToken).ConfigureAwait(false); - lastStatusResponse = await structuredScriptOrchestrator.Finish(lastTicketForNextStatus, scriptExecutionCancellationToken).ConfigureAwait(false) ?? lastStatusResponse; + lastStatusResponse = await structuredScriptExecutor.Finish(lastTicketForNextStatus, scriptExecutionCancellationToken).ConfigureAwait(false) ?? lastStatusResponse; OnScriptStatusResponseReceived(lastStatusResponse); return (lastStatusResponse, lastTicketForNextStatus); } - async Task<(ScriptStatus lastStatusResponse, ITicketForNextStatus lastTicketForNextStatus)> ObserveUntilComplete( + async Task<(ScriptStatus lastStatusResponse, ICommandContext lastTicketForNextStatus)> ObserveUntilComplete( ScriptStatus scriptStatus, - ITicketForNextStatus ticketForNextStatus, + ICommandContext commandContext, CancellationToken scriptExecutionCancellationToken) { - var lastTicketForNextStatus = ticketForNextStatus; + var lastTicketForNextStatus = commandContext; var lastStatusResponse = scriptStatus; var iteration = 0; var cancellationIteration = 0; @@ -79,13 +79,13 @@ public async Task ExecuteScript(ExecuteScriptCommand comm { if (scriptExecutionCancellationToken.IsCancellationRequested) { - (lastStatusResponse, lastTicketForNextStatus) = await structuredScriptOrchestrator.Cancel(lastTicketForNextStatus, scriptExecutionCancellationToken).ConfigureAwait(false); + (lastStatusResponse, lastTicketForNextStatus) = await structuredScriptExecutor.Cancel(lastTicketForNextStatus, scriptExecutionCancellationToken).ConfigureAwait(false); } else { try { - (lastStatusResponse, lastTicketForNextStatus) = await structuredScriptOrchestrator.GetStatus(lastTicketForNextStatus, scriptExecutionCancellationToken).ConfigureAwait(false); + (lastStatusResponse, lastTicketForNextStatus) = await structuredScriptExecutor.GetStatus(lastTicketForNextStatus, scriptExecutionCancellationToken).ConfigureAwait(false); } catch (Exception) { diff --git a/source/Octopus.Tentacle.Client/Scripts/ScriptOrchestratorFactory.cs b/source/Octopus.Tentacle.Client/Scripts/ScriptOrchestratorFactory.cs index cfeef6fe2..9dd78a1f0 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ScriptOrchestratorFactory.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ScriptOrchestratorFactory.cs @@ -61,11 +61,11 @@ public async Task CreateOrchestrator(CancellationToken canc return new ObservingScriptOrchestrator(scriptObserverBackOffStrategy, onScriptStatusResponseReceived, onScriptCompleted, structuredScriptOrchestrator, logger); } - public IStructuredScriptOrchestrator CreateOrchestrator(ScriptServiceVersion scriptServiceToUse) + public IStructuredScriptExecutor CreateOrchestrator(ScriptServiceVersion scriptServiceToUse) { if (scriptServiceToUse == ScriptServiceVersion.ScriptServiceVersion1) { - return new ScriptServiceV1Orchestrator( + return new ScriptServiceV1Executor( clientsHolder.ScriptServiceV1, rpcCallExecutor, clientOperationMetricsBuilder, @@ -74,7 +74,7 @@ public IStructuredScriptOrchestrator CreateOrchestrator(ScriptServiceVersion scr if (scriptServiceToUse == ScriptServiceVersion.ScriptServiceVersion2) { - return new ScriptServiceV2Orchestrator( + return new ScriptServiceV2Executor( clientsHolder.ScriptServiceV2, rpcCallExecutor, clientOperationMetricsBuilder, @@ -85,7 +85,7 @@ public IStructuredScriptOrchestrator CreateOrchestrator(ScriptServiceVersion scr if (scriptServiceToUse == ScriptServiceVersion.KubernetesScriptServiceVersion1Alpha) { - return new KubernetesScriptServiceV1AlphaOrchestrator( + return new KubernetesScriptServiceV1AlphaExecutor( clientsHolder.KubernetesScriptServiceV1Alpha, rpcCallExecutor, clientOperationMetricsBuilder, @@ -96,7 +96,7 @@ public IStructuredScriptOrchestrator CreateOrchestrator(ScriptServiceVersion scr if (scriptServiceToUse == ScriptServiceVersion.KubernetesScriptServiceVersion1) { - return new KubernetesScriptServiceV1Orchestrator( + return new KubernetesScriptServiceV1Executor( clientsHolder.KubernetesScriptServiceV1, rpcCallExecutor, clientOperationMetricsBuilder, diff --git a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Executor.cs similarity index 77% rename from source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs rename to source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Executor.cs index 551247882..681d9b57f 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Orchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Executor.cs @@ -14,7 +14,7 @@ namespace Octopus.Tentacle.Client.Scripts { - class ScriptServiceV1Orchestrator : IStructuredScriptOrchestrator + class ScriptServiceV1Executor : IStructuredScriptExecutor { readonly RpcCallExecutor rpcCallExecutor; @@ -23,7 +23,7 @@ class ScriptServiceV1Orchestrator : IStructuredScriptOrchestrator readonly IAsyncClientScriptService clientScriptServiceV1; - public ScriptServiceV1Orchestrator( + public ScriptServiceV1Executor( IAsyncClientScriptService clientScriptServiceV1, RpcCallExecutor rpcCallExecutor, ClientOperationMetricsBuilder clientOperationMetricsBuilder, @@ -38,7 +38,7 @@ public ScriptServiceV1Orchestrator( private StartScriptCommand Map(ExecuteScriptCommand command) { if (command is not ExecuteShellScriptCommand shellScriptCommand) - throw new InvalidOperationException($"{nameof(ScriptServiceV2Orchestrator)} only supports commands of type {nameof(ExecuteShellScriptCommand)}."); + throw new InvalidOperationException($"{nameof(ScriptServiceV2Executor)} only supports commands of type {nameof(ExecuteShellScriptCommand)}."); return new StartScriptCommand( shellScriptCommand.ScriptBody, @@ -56,17 +56,17 @@ private ScriptStatus MapToScriptStatus(ScriptStatusResponse scriptStatusResponse return new ScriptStatus(scriptStatusResponse.State, scriptStatusResponse.ExitCode, scriptStatusResponse.Logs); } - private ITicketForNextStatus MapToNextStatus(ScriptStatusResponse scriptStatusResponse) + private ICommandContext MapToNextStatus(ScriptStatusResponse scriptStatusResponse) { - return new DefaultTicketForNextStatus(scriptStatusResponse.Ticket, scriptStatusResponse.NextLogSequence, ScriptServiceVersion.ScriptServiceVersion1); + return new DefaultCommandContext(scriptStatusResponse.Ticket, scriptStatusResponse.NextLogSequence, ScriptServiceVersion.ScriptServiceVersion1); } - (ScriptStatus, ITicketForNextStatus) Map(ScriptStatusResponse r) + (ScriptStatus, ICommandContext) Map(ScriptStatusResponse r) { return (MapToScriptStatus(r), MapToNextStatus(r)); } - public async Task<(ScriptStatus, ITicketForNextStatus)> StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ICommandContext)> StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) { var command = Map(executeScriptCommand); var scriptTicket = await rpcCallExecutor.ExecuteWithNoRetries( @@ -84,12 +84,12 @@ private ITicketForNextStatus MapToNextStatus(ScriptStatusResponse scriptStatusRe return Map(new ScriptStatusResponse(scriptTicket, ProcessState.Pending, 0, new List(), 0)); } - public async Task<(ScriptStatus, ITicketForNextStatus)> GetStatus(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ICommandContext)> GetStatus(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { return Map(await _GetStatus(lastStatusResponse, scriptExecutionCancellationToken)); } - private async Task _GetStatus(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + private async Task _GetStatus(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { var scriptStatusResponseV1 = await rpcCallExecutor.ExecuteWithNoRetries( RpcCall.Create(nameof(IScriptService.GetStatus)), @@ -107,12 +107,12 @@ private async Task _GetStatus(ITicketForNextStatus lastSta return scriptStatusResponseV1; } - public async Task<(ScriptStatus, ITicketForNextStatus)> Cancel(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ICommandContext)> Cancel(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { return Map(await _Cancel(lastStatusResponse, scriptExecutionCancellationToken)); } - private async Task _Cancel(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + private async Task _Cancel(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { var response = await rpcCallExecutor.ExecuteWithNoRetries( RpcCall.Create(nameof(IScriptService.CancelScript)), @@ -130,12 +130,12 @@ private async Task _Cancel(ITicketForNextStatus lastStatus return response; } - public async Task Finish(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task Finish(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { return MapToScriptStatus(await _Finish(lastStatusResponse, scriptExecutionCancellationToken)); } - private async Task _Finish(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + private async Task _Finish(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { var response = await rpcCallExecutor.ExecuteWithNoRetries( RpcCall.Create(nameof(IScriptService.CompleteScript)), diff --git a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs index 45c8d87a9..7f3764aa6 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs @@ -16,7 +16,7 @@ namespace Octopus.Tentacle.Client.Scripts { - class ScriptServiceV2Orchestrator : IStructuredScriptOrchestrator + class ScriptServiceV2Executor : IStructuredScriptExecutor { readonly IAsyncClientScriptServiceV2 clientScriptServiceV2; readonly RpcCallExecutor rpcCallExecutor; @@ -25,7 +25,7 @@ class ScriptServiceV2Orchestrator : IStructuredScriptOrchestrator readonly ITentacleClientTaskLog logger; readonly TentacleClientOptions clientOptions; - public ScriptServiceV2Orchestrator( + public ScriptServiceV2Executor( IAsyncClientScriptServiceV2 clientScriptServiceV2, RpcCallExecutor rpcCallExecutor, ClientOperationMetricsBuilder clientOperationMetricsBuilder, @@ -44,7 +44,7 @@ public ScriptServiceV2Orchestrator( StartScriptCommandV2 Map(ExecuteScriptCommand command) { if (command is not ExecuteShellScriptCommand shellScriptCommand) - throw new InvalidOperationException($"{nameof(ScriptServiceV2Orchestrator)} only supports commands of type {nameof(ExecuteShellScriptCommand)}."); + throw new InvalidOperationException($"{nameof(ScriptServiceV2Executor)} only supports commands of type {nameof(ExecuteShellScriptCommand)}."); return new StartScriptCommandV2( shellScriptCommand.ScriptBody, @@ -59,7 +59,7 @@ StartScriptCommandV2 Map(ExecuteScriptCommand command) shellScriptCommand.Files.ToArray()); } - (ScriptStatus, ITicketForNextStatus) Map(ScriptStatusResponseV2 r) + (ScriptStatus, ICommandContext) Map(ScriptStatusResponseV2 r) { return (MapToScriptStatus(r), MapToNextStatus(r)); } @@ -69,13 +69,13 @@ private ScriptStatus MapToScriptStatus(ScriptStatusResponseV2 scriptStatusRespon return new ScriptStatus(scriptStatusResponse.State, scriptStatusResponse.ExitCode, scriptStatusResponse.Logs); } - private ITicketForNextStatus MapToNextStatus(ScriptStatusResponseV2 scriptStatusResponse) + private ICommandContext MapToNextStatus(ScriptStatusResponseV2 scriptStatusResponse) { - return new DefaultTicketForNextStatus(scriptStatusResponse.Ticket, scriptStatusResponse.NextLogSequence, ScriptServiceVersion.ScriptServiceVersion1); + return new DefaultCommandContext(scriptStatusResponse.Ticket, scriptStatusResponse.NextLogSequence, ScriptServiceVersion.ScriptServiceVersion1); } public ProcessState GetState(ScriptStatusResponseV2 response) => response.State; - public async Task<(ScriptStatus, ITicketForNextStatus)> StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ICommandContext)> StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) { var command = Map(executeScriptCommand); ScriptStatusResponseV2 scriptStatusResponse; @@ -123,7 +123,7 @@ void OnErrorAction(Exception ex) if (!startScriptCallIsConnecting || startScriptCallIsBeingRetried) { var scriptStatus = new ScriptStatus(ProcessState.Pending, null, new List()); - var defaultTicketForNextStatus = new DefaultTicketForNextStatus(command.ScriptTicket, 0, ScriptServiceVersion.ScriptServiceVersion2); + var defaultTicketForNextStatus = new DefaultCommandContext(command.ScriptTicket, 0, ScriptServiceVersion.ScriptServiceVersion2); return (scriptStatus, defaultTicketForNextStatus); } @@ -133,13 +133,13 @@ void OnErrorAction(Exception ex) } } - public async Task<(ScriptStatus, ITicketForNextStatus)> GetStatus(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ICommandContext)> GetStatus(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { return Map(await _GetStatus(lastStatusResponse, scriptExecutionCancellationToken)); } - private async Task _GetStatus(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + private async Task _GetStatus(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { async Task GetStatusAction(CancellationToken ct) @@ -159,12 +159,12 @@ async Task GetStatusAction(CancellationToken ct) scriptExecutionCancellationToken).ConfigureAwait(false); } - public async Task<(ScriptStatus, ITicketForNextStatus)> Cancel(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ICommandContext)> Cancel(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { return Map(await _Cancel(lastStatusResponse, scriptExecutionCancellationToken)); } - private async Task _Cancel(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + private async Task _Cancel(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { async Task CancelScriptAction(CancellationToken ct) { @@ -188,13 +188,13 @@ async Task CancelScriptAction(CancellationToken ct) CancellationToken.None).ConfigureAwait(false); } - public async Task Finish(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task Finish(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { await _Finish(lastStatusResponse, scriptExecutionCancellationToken); return null; } - private async Task _Finish(ITicketForNextStatus lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + private async Task _Finish(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { try { From 28f0cccc835c6c44d7917cf32b48c9a0675b0dc4 Mon Sep 17 00:00:00 2001 From: Luke Butters Date: Fri, 21 Jun 2024 15:08:43 +1000 Subject: [PATCH 19/23] ssv2 fix --- .../Scripts/ScriptServiceV2Orchestrator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs index 7f3764aa6..14cf3393a 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs @@ -71,7 +71,7 @@ private ScriptStatus MapToScriptStatus(ScriptStatusResponseV2 scriptStatusRespon private ICommandContext MapToNextStatus(ScriptStatusResponseV2 scriptStatusResponse) { - return new DefaultCommandContext(scriptStatusResponse.Ticket, scriptStatusResponse.NextLogSequence, ScriptServiceVersion.ScriptServiceVersion1); + return new DefaultCommandContext(scriptStatusResponse.Ticket, scriptStatusResponse.NextLogSequence, ScriptServiceVersion.ScriptServiceVersion2); } public ProcessState GetState(ScriptStatusResponseV2 response) => response.State; From 87ccc83aec205d7ac6dba27709f91603897a3565 Mon Sep 17 00:00:00 2001 From: Luke Butters Date: Fri, 21 Jun 2024 15:10:27 +1000 Subject: [PATCH 20/23] . --- source/Octopus.Tentacle.Client/EventDrivenScriptExecutor.cs | 4 ++-- source/Octopus.Tentacle.Client/IEventDrivenScriptExecutor.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/Octopus.Tentacle.Client/EventDrivenScriptExecutor.cs b/source/Octopus.Tentacle.Client/EventDrivenScriptExecutor.cs index b43ccaf75..9d9fb7832 100644 --- a/source/Octopus.Tentacle.Client/EventDrivenScriptExecutor.cs +++ b/source/Octopus.Tentacle.Client/EventDrivenScriptExecutor.cs @@ -97,7 +97,7 @@ internal EventDrivenScriptExecutor(ITentacleClientTaskLog logger, throw new System.NotImplementedException(); } - public async Task CleanUpScript(ICommandContext ticketForNextNextStatus, CancellationToken cancellationToken) + public async Task CleanUpScript(ICommandContext ticketForNextNextStatus, CancellationToken cancellationToken) { var operationMetricsBuilder = ClientOperationMetricsBuilder.Start(); @@ -105,7 +105,7 @@ public async Task CleanUpScript(ICommandContext ticketForNextNextS var orchestrator = scriptOrchestratorFactory.CreateOrchestrator(ticketForNextNextStatus.WhichService); - await orchestrator.Finish(ticketForNextNextStatus, cancellationToken); + return await orchestrator.Finish(ticketForNextNextStatus, cancellationToken); } ScriptOrchestratorFactory GetNewScriptOrchestratorFactory(ClientOperationMetricsBuilder operationMetricsBuilder) diff --git a/source/Octopus.Tentacle.Client/IEventDrivenScriptExecutor.cs b/source/Octopus.Tentacle.Client/IEventDrivenScriptExecutor.cs index 24e22991d..63cf8108f 100644 --- a/source/Octopus.Tentacle.Client/IEventDrivenScriptExecutor.cs +++ b/source/Octopus.Tentacle.Client/IEventDrivenScriptExecutor.cs @@ -30,7 +30,7 @@ public interface IEventDrivenScriptExecutor /// Task<(ScriptStatus, ICommandContext)> CancelScript(ScriptTicket scriptTicket, CancellationToken cancellationToken); - Task CleanUpScript(ICommandContext ticketForNextNextStatus, CancellationToken cancellationToken); + Task CleanUpScript(ICommandContext ticketForNextNextStatus, CancellationToken cancellationToken); } public enum HasStartScriptBeenCalledBefore From cd5086b3492e85999356c85a09f3215e226ba258 Mon Sep 17 00:00:00 2001 From: Luke Butters Date: Mon, 24 Jun 2024 11:21:06 +1000 Subject: [PATCH 21/23] Push knowledge around SSV1 into its executor --- .../EventDrivenScriptExecutor.cs | 10 ++-------- .../IEventDrivenScriptExecutor.cs | 8 +------- .../Scripts/IStructuredScriptExecutor.cs | 12 +++++++----- ...bernetesScriptServiceV1AlphaOrchestrator.cs | 8 +++++--- .../KubernetesScriptServiceV1Orchestrator.cs | 8 +++++--- .../Scripts/ObservingScriptOrchestrator.cs | 4 +++- .../Scripts/ScriptServiceV1Executor.cs | 18 +++++++++++++++--- .../Scripts/ScriptServiceV2Orchestrator.cs | 8 +++++--- .../StartScriptIsBeingReAttempted.cs | 10 ++++++++++ 9 files changed, 53 insertions(+), 33 deletions(-) create mode 100644 source/Octopus.Tentacle.Client/StartScriptIsBeingReAttempted.cs diff --git a/source/Octopus.Tentacle.Client/EventDrivenScriptExecutor.cs b/source/Octopus.Tentacle.Client/EventDrivenScriptExecutor.cs index 9d9fb7832..d560c028f 100644 --- a/source/Octopus.Tentacle.Client/EventDrivenScriptExecutor.cs +++ b/source/Octopus.Tentacle.Client/EventDrivenScriptExecutor.cs @@ -48,7 +48,7 @@ internal EventDrivenScriptExecutor(ITentacleClientTaskLog logger, } public async Task<(ScriptStatus, ICommandContext)> StartScript(ExecuteScriptCommand executeScriptCommand, - HasStartScriptBeenCalledBefore hasStartScriptBeenCalledBefore, + StartScriptIsBeingReAttempted startScriptIsBeingReAttempted, CancellationToken cancellationToken) { var operationMetricsBuilder = ClientOperationMetricsBuilder.Start(); @@ -56,17 +56,11 @@ internal EventDrivenScriptExecutor(ITentacleClientTaskLog logger, // Pick what service to use. var scriptServiceToUse = await new ScriptServicePicker(clientsHolder.CapabilitiesServiceV2, logger, rpcCallExecutor, clientOptions, operationMetricsBuilder) .DetermineScriptServiceVersionToUse(cancellationToken); - - // And start the script - if (scriptServiceToUse == ScriptServiceVersion.ScriptServiceVersion1 && hasStartScriptBeenCalledBefore == HasStartScriptBeenCalledBefore.ItMayHaveBeen) - { - return (new ScriptStatus(ProcessState.Complete, ScriptExitCodes.UnknownScriptExitCode, new List()), new DefaultCommandContext(new ScriptTicket(Guid.NewGuid().ToString()), 0, ScriptServiceVersion.ScriptServiceVersion1)); - } var scriptOrchestratorFactory = GetNewScriptOrchestratorFactory(operationMetricsBuilder); var orchestrator = scriptOrchestratorFactory.CreateOrchestrator(scriptServiceToUse); - return await orchestrator.StartScript(executeScriptCommand, cancellationToken); + return await orchestrator.StartScript(executeScriptCommand, startScriptIsBeingReAttempted, cancellationToken); } public async Task<(ScriptStatus, ICommandContext)> GetStatus(ICommandContext ticketForNextNextStatus, CancellationToken cancellationToken) diff --git a/source/Octopus.Tentacle.Client/IEventDrivenScriptExecutor.cs b/source/Octopus.Tentacle.Client/IEventDrivenScriptExecutor.cs index 63cf8108f..c938bbc91 100644 --- a/source/Octopus.Tentacle.Client/IEventDrivenScriptExecutor.cs +++ b/source/Octopus.Tentacle.Client/IEventDrivenScriptExecutor.cs @@ -9,7 +9,7 @@ namespace Octopus.Tentacle.Client public interface IEventDrivenScriptExecutor { Task<(ScriptStatus, ICommandContext)> StartScript(ExecuteScriptCommand executeScriptCommand, - HasStartScriptBeenCalledBefore hasStartScriptBeenCalledBefore, + StartScriptIsBeingReAttempted startScriptIsBeingReAttempted, CancellationToken cancellationToken); Task<(ScriptStatus, ICommandContext)> GetStatus(ICommandContext ticketForNextNextStatus, CancellationToken cancellationToken); @@ -32,10 +32,4 @@ public interface IEventDrivenScriptExecutor Task CleanUpScript(ICommandContext ticketForNextNextStatus, CancellationToken cancellationToken); } - - public enum HasStartScriptBeenCalledBefore - { - NoNever, - ItMayHaveBeen - } } \ No newline at end of file diff --git a/source/Octopus.Tentacle.Client/Scripts/IStructuredScriptExecutor.cs b/source/Octopus.Tentacle.Client/Scripts/IStructuredScriptExecutor.cs index 049bccc7b..7e73fec02 100644 --- a/source/Octopus.Tentacle.Client/Scripts/IStructuredScriptExecutor.cs +++ b/source/Octopus.Tentacle.Client/Scripts/IStructuredScriptExecutor.cs @@ -8,15 +8,17 @@ namespace Octopus.Tentacle.Client.Scripts { public interface IStructuredScriptExecutor { - Task<(ScriptStatus, ICommandContext)> StartScript(ExecuteScriptCommand command, CancellationToken scriptExecutionCancellationToken); + Task<(ScriptStatus, ICommandContext)> StartScript(ExecuteScriptCommand command, + StartScriptIsBeingReAttempted startScriptIsBeingReAttempted, + CancellationToken scriptExecutionCancellationToken); /// /// Returns a status or null when scriptExecutionCancellationToken is null. /// - /// + /// /// /// - Task<(ScriptStatus, ICommandContext)> GetStatus(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken); - Task<(ScriptStatus, ICommandContext)> Cancel(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken); - Task Finish(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken); + Task<(ScriptStatus, ICommandContext)> GetStatus(ICommandContext commandContext, CancellationToken scriptExecutionCancellationToken); + Task<(ScriptStatus, ICommandContext)> Cancel(ICommandContext commandContext, CancellationToken scriptExecutionCancellationToken); + Task Finish(ICommandContext commandContext, CancellationToken scriptExecutionCancellationToken); } } \ No newline at end of file diff --git a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs index 48e59c8ff..fab1fd498 100644 --- a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs @@ -92,7 +92,9 @@ private ICommandContext MapToNextStatus(KubernetesScriptStatusResponseV1Alpha sc return (MapToScriptStatus(r), MapToNextStatus(r)); } - public async Task<(ScriptStatus, ICommandContext)> StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ICommandContext)> StartScript(ExecuteScriptCommand executeScriptCommand, + StartScriptIsBeingReAttempted startScriptIsBeingReAttempted, + CancellationToken scriptExecutionCancellationToken) { var command = Map(executeScriptCommand); var startScriptCallsConnectedCount = 0; @@ -149,9 +151,9 @@ void OnErrorAction(Exception ex) } } - public async Task<(ScriptStatus, ICommandContext)> GetStatus(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ICommandContext)> GetStatus(ICommandContext commandContext, CancellationToken scriptExecutionCancellationToken) { - return Map(await _GetStatus(lastStatusResponse, scriptExecutionCancellationToken)); + return Map(await _GetStatus(commandContext, scriptExecutionCancellationToken)); } async Task _GetStatus(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) diff --git a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1Orchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1Orchestrator.cs index 93a043ca4..e89a9ce83 100644 --- a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1Orchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1Orchestrator.cs @@ -83,7 +83,9 @@ private ICommandContext MapToNextStatus(KubernetesScriptStatusResponseV1 scriptS return new DefaultCommandContext(scriptStatusResponse.ScriptTicket, scriptStatusResponse.NextLogSequence, ScriptServiceVersion.KubernetesScriptServiceVersion1); } - public async Task<(ScriptStatus, ICommandContext)> StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ICommandContext)> StartScript(ExecuteScriptCommand executeScriptCommand, + StartScriptIsBeingReAttempted startScriptIsBeingReAttempted, + CancellationToken scriptExecutionCancellationToken) { var command = Map(executeScriptCommand); var startScriptCallsConnectedCount = 0; @@ -140,9 +142,9 @@ void OnErrorAction(Exception ex) } } - public async Task<(ScriptStatus, ICommandContext)> GetStatus(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ICommandContext)> GetStatus(ICommandContext commandContext, CancellationToken scriptExecutionCancellationToken) { - return Map(await _GetStatus(lastStatusResponse, scriptExecutionCancellationToken)); + return Map(await _GetStatus(commandContext, scriptExecutionCancellationToken)); } private async Task _GetStatus(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) diff --git a/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs index 0e3caab86..7b1bfaad6 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs @@ -33,7 +33,9 @@ public ObservingScriptOrchestrator( public async Task ExecuteScript(ExecuteScriptCommand command, CancellationToken scriptExecutionCancellationToken) { - var (scriptStatus, ticketForNextStatus) = await structuredScriptExecutor.StartScript(command, scriptExecutionCancellationToken).ConfigureAwait(false); + var (scriptStatus, ticketForNextStatus) = await structuredScriptExecutor.StartScript(command, + StartScriptIsBeingReAttempted.FirstAttempt, // This is not re-entrant so this should be true. + scriptExecutionCancellationToken).ConfigureAwait(false); (scriptStatus, _) = await ObserveUntilCompleteThenFinish(scriptStatus, ticketForNextStatus, scriptExecutionCancellationToken).ConfigureAwait(false); diff --git a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Executor.cs b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Executor.cs index 681d9b57f..02502e66d 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Executor.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Executor.cs @@ -66,8 +66,20 @@ private ICommandContext MapToNextStatus(ScriptStatusResponse scriptStatusRespons return (MapToScriptStatus(r), MapToNextStatus(r)); } - public async Task<(ScriptStatus, ICommandContext)> StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ICommandContext)> StartScript(ExecuteScriptCommand executeScriptCommand, + StartScriptIsBeingReAttempted startScriptIsBeingReAttempted, + CancellationToken scriptExecutionCancellationToken) { + // Script Service v1 is not idempotent, do not allow it to be re-attempted as it may run a second time. + if (startScriptIsBeingReAttempted == StartScriptIsBeingReAttempted.PossiblyBeingReAttempted) + { + return (new ScriptStatus(ProcessState.Complete, + ScriptExitCodes.UnknownScriptExitCode, + new List()), + // TODO: We should try to encourage a DefaultCommandContext which will do nothing perhaps set the ScriptServiceVersion to + // one that always returns the above exit code. + new DefaultCommandContext(new ScriptTicket(Guid.NewGuid().ToString()), 0, ScriptServiceVersion.ScriptServiceVersion1)); + } var command = Map(executeScriptCommand); var scriptTicket = await rpcCallExecutor.ExecuteWithNoRetries( RpcCall.Create(nameof(IScriptService.StartScript)), @@ -84,9 +96,9 @@ private ICommandContext MapToNextStatus(ScriptStatusResponse scriptStatusRespons return Map(new ScriptStatusResponse(scriptTicket, ProcessState.Pending, 0, new List(), 0)); } - public async Task<(ScriptStatus, ICommandContext)> GetStatus(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ICommandContext)> GetStatus(ICommandContext commandContext, CancellationToken scriptExecutionCancellationToken) { - return Map(await _GetStatus(lastStatusResponse, scriptExecutionCancellationToken)); + return Map(await _GetStatus(commandContext, scriptExecutionCancellationToken)); } private async Task _GetStatus(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) diff --git a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs index 14cf3393a..4dfaf33eb 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs @@ -75,7 +75,9 @@ private ICommandContext MapToNextStatus(ScriptStatusResponseV2 scriptStatusRespo } public ProcessState GetState(ScriptStatusResponseV2 response) => response.State; - public async Task<(ScriptStatus, ICommandContext)> StartScript(ExecuteScriptCommand executeScriptCommand, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ICommandContext)> StartScript(ExecuteScriptCommand executeScriptCommand, + StartScriptIsBeingReAttempted startScriptIsBeingReAttempted, + CancellationToken scriptExecutionCancellationToken) { var command = Map(executeScriptCommand); ScriptStatusResponseV2 scriptStatusResponse; @@ -133,9 +135,9 @@ void OnErrorAction(Exception ex) } } - public async Task<(ScriptStatus, ICommandContext)> GetStatus(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ICommandContext)> GetStatus(ICommandContext commandContext, CancellationToken scriptExecutionCancellationToken) { - return Map(await _GetStatus(lastStatusResponse, scriptExecutionCancellationToken)); + return Map(await _GetStatus(commandContext, scriptExecutionCancellationToken)); } diff --git a/source/Octopus.Tentacle.Client/StartScriptIsBeingReAttempted.cs b/source/Octopus.Tentacle.Client/StartScriptIsBeingReAttempted.cs new file mode 100644 index 000000000..e9ab7ac44 --- /dev/null +++ b/source/Octopus.Tentacle.Client/StartScriptIsBeingReAttempted.cs @@ -0,0 +1,10 @@ +using System; + +namespace Octopus.Tentacle.Client +{ + public enum StartScriptIsBeingReAttempted + { + FirstAttempt, + PossiblyBeingReAttempted + } +} \ No newline at end of file From d8c2f320bf571cd694a0378888c2e64e0ba19413 Mon Sep 17 00:00:00 2001 From: Luke Butters Date: Mon, 24 Jun 2024 12:07:28 +1000 Subject: [PATCH 22/23] Create the AggregateScriptExecutor --- ...Executor.cs => AggregateScriptExecutor.cs} | 52 +++++++++++++------ .../IEventDrivenScriptExecutor.cs | 35 ------------- ...edScriptExecutor.cs => IScriptExecutor.cs} | 17 ++++-- ...ernetesScriptServiceV1AlphaOrchestrator.cs | 14 ++--- .../KubernetesScriptServiceV1Orchestrator.cs | 16 +++--- .../Scripts/ObservingScriptOrchestrator.cs | 15 +++--- .../Scripts/ScriptOrchestratorFactory.cs | 34 ++---------- .../Scripts/ScriptServiceV1Executor.cs | 35 ++++++------- .../Scripts/ScriptServiceV2Orchestrator.cs | 14 ++--- .../Octopus.Tentacle.Client/TentacleClient.cs | 31 ++++++----- 10 files changed, 108 insertions(+), 155 deletions(-) rename source/Octopus.Tentacle.Client/{EventDrivenScriptExecutor.cs => AggregateScriptExecutor.cs} (70%) delete mode 100644 source/Octopus.Tentacle.Client/IEventDrivenScriptExecutor.cs rename source/Octopus.Tentacle.Client/Scripts/{IStructuredScriptExecutor.cs => IScriptExecutor.cs} (52%) diff --git a/source/Octopus.Tentacle.Client/EventDrivenScriptExecutor.cs b/source/Octopus.Tentacle.Client/AggregateScriptExecutor.cs similarity index 70% rename from source/Octopus.Tentacle.Client/EventDrivenScriptExecutor.cs rename to source/Octopus.Tentacle.Client/AggregateScriptExecutor.cs index d560c028f..7e384ec75 100644 --- a/source/Octopus.Tentacle.Client/EventDrivenScriptExecutor.cs +++ b/source/Octopus.Tentacle.Client/AggregateScriptExecutor.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Halibut; @@ -15,7 +14,10 @@ namespace Octopus.Tentacle.Client { - public class EventDrivenScriptExecutor : IEventDrivenScriptExecutor + /// + /// Executes scripts, on the best available script service. + /// + public class AggregateScriptExecutor : IScriptExecutor { readonly ITentacleClientTaskLog logger; readonly ITentacleClientObserver tentacleClientObserver; @@ -24,23 +26,24 @@ public class EventDrivenScriptExecutor : IEventDrivenScriptExecutor readonly RpcCallExecutor rpcCallExecutor; readonly TimeSpan onCancellationAbandonCompleteScriptAfter; - public EventDrivenScriptExecutor(ITentacleClientTaskLog logger, - ITentacleClientObserver tentacleClientOptions, + public AggregateScriptExecutor(ITentacleClientTaskLog logger, + ITentacleClientObserver tentacleClientObserver, TentacleClientOptions clientOptions, IHalibutRuntime halibutRuntime, - ServiceEndPoint serviceEndPoint, TimeSpan onCancellationAbandonCompleteScriptAfter) : this(logger, tentacleClientOptions, clientOptions, halibutRuntime, serviceEndPoint, null, onCancellationAbandonCompleteScriptAfter) + ServiceEndPoint serviceEndPoint, TimeSpan onCancellationAbandonCompleteScriptAfter) : this(logger, tentacleClientObserver, clientOptions, halibutRuntime, serviceEndPoint, null, onCancellationAbandonCompleteScriptAfter) { } - internal EventDrivenScriptExecutor(ITentacleClientTaskLog logger, - ITentacleClientObserver tentacleClientOptions, + internal AggregateScriptExecutor(ITentacleClientTaskLog logger, + ITentacleClientObserver tentacleClientObserver, TentacleClientOptions clientOptions, IHalibutRuntime halibutRuntime, ServiceEndPoint serviceEndPoint, - ITentacleServiceDecoratorFactory? tentacleServicesDecoratorFactory, TimeSpan onCancellationAbandonCompleteScriptAfter) + ITentacleServiceDecoratorFactory? tentacleServicesDecoratorFactory, + TimeSpan onCancellationAbandonCompleteScriptAfter) { this.logger = logger; - tentacleClientObserver = tentacleClientOptions; + this.tentacleClientObserver = tentacleClientObserver; this.clientOptions = clientOptions; this.onCancellationAbandonCompleteScriptAfter = onCancellationAbandonCompleteScriptAfter; clientsHolder = new ClientsHolder(halibutRuntime, serviceEndPoint, tentacleServicesDecoratorFactory); @@ -54,8 +57,8 @@ internal EventDrivenScriptExecutor(ITentacleClientTaskLog logger, var operationMetricsBuilder = ClientOperationMetricsBuilder.Start(); // Pick what service to use. - var scriptServiceToUse = await new ScriptServicePicker(clientsHolder.CapabilitiesServiceV2, logger, rpcCallExecutor, clientOptions, operationMetricsBuilder) - .DetermineScriptServiceVersionToUse(cancellationToken); + var scriptServiceToUse = await DetermineScriptServiceVersionToUse(cancellationToken, operationMetricsBuilder); + var scriptOrchestratorFactory = GetNewScriptOrchestratorFactory(operationMetricsBuilder); @@ -63,6 +66,19 @@ internal EventDrivenScriptExecutor(ITentacleClientTaskLog logger, return await orchestrator.StartScript(executeScriptCommand, startScriptIsBeingReAttempted, cancellationToken); } + async Task DetermineScriptServiceVersionToUse(CancellationToken cancellationToken, ClientOperationMetricsBuilder operationMetricsBuilder) + { + try + { + return await new ScriptServicePicker(clientsHolder.CapabilitiesServiceV2, logger, rpcCallExecutor, clientOptions, operationMetricsBuilder) + .DetermineScriptServiceVersionToUse(cancellationToken); + } + catch (Exception ex) when (cancellationToken.IsCancellationRequested) + { + throw new OperationCanceledException("Script execution was cancelled", ex); + } + } + public async Task<(ScriptStatus, ICommandContext)> GetStatus(ICommandContext ticketForNextNextStatus, CancellationToken cancellationToken) { var operationMetricsBuilder = ClientOperationMetricsBuilder.Start(); @@ -82,7 +98,12 @@ internal EventDrivenScriptExecutor(ITentacleClientTaskLog logger, var orchestrator = scriptOrchestratorFactory.CreateOrchestrator(ticketForNextNextStatus.WhichService); - return await orchestrator.Cancel(ticketForNextNextStatus, cancellationToken); + return await orchestrator.CancelScript(ticketForNextNextStatus, cancellationToken); + } + + public Task Finish(ICommandContext commandContext, CancellationToken scriptExecutionCancellationToken) + { + throw new NotImplementedException(); } public Task<(ScriptStatus, ICommandContext)> CancelScript(ScriptTicket scriptTicket, CancellationToken cancellationToken) @@ -99,17 +120,14 @@ internal EventDrivenScriptExecutor(ITentacleClientTaskLog logger, var orchestrator = scriptOrchestratorFactory.CreateOrchestrator(ticketForNextNextStatus.WhichService); - return await orchestrator.Finish(ticketForNextNextStatus, cancellationToken); + return await orchestrator.CleanUpScript(ticketForNextNextStatus, cancellationToken); } ScriptOrchestratorFactory GetNewScriptOrchestratorFactory(ClientOperationMetricsBuilder operationMetricsBuilder) { return new ScriptOrchestratorFactory(clientsHolder, - new DefaultScriptObserverBackoffStrategy(), rpcCallExecutor, - operationMetricsBuilder, - status => { }, - token => Task.CompletedTask, + operationMetricsBuilder, onCancellationAbandonCompleteScriptAfter, clientOptions, logger); diff --git a/source/Octopus.Tentacle.Client/IEventDrivenScriptExecutor.cs b/source/Octopus.Tentacle.Client/IEventDrivenScriptExecutor.cs deleted file mode 100644 index c938bbc91..000000000 --- a/source/Octopus.Tentacle.Client/IEventDrivenScriptExecutor.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using Octopus.Tentacle.Client.EventDriven; -using Octopus.Tentacle.Client.Scripts.Models; -using Octopus.Tentacle.Contracts; - -namespace Octopus.Tentacle.Client -{ - public interface IEventDrivenScriptExecutor - { - Task<(ScriptStatus, ICommandContext)> StartScript(ExecuteScriptCommand executeScriptCommand, - StartScriptIsBeingReAttempted startScriptIsBeingReAttempted, - CancellationToken cancellationToken); - - Task<(ScriptStatus, ICommandContext)> GetStatus(ICommandContext ticketForNextNextStatus, CancellationToken cancellationToken); - - /// - /// Cancel script will still send back the rest of the logs, hence the ticketForNextNextStatus argument. - /// - /// - /// - /// - Task<(ScriptStatus, ICommandContext)> CancelScript(ICommandContext ticketForNextNextStatus, CancellationToken cancellationToken); - - /// - /// Use this cancel method if only the ScriptTicket is known, e.g. we called StartScript but never got a response. - /// - /// - /// - /// - Task<(ScriptStatus, ICommandContext)> CancelScript(ScriptTicket scriptTicket, CancellationToken cancellationToken); - - Task CleanUpScript(ICommandContext ticketForNextNextStatus, CancellationToken cancellationToken); - } -} \ No newline at end of file diff --git a/source/Octopus.Tentacle.Client/Scripts/IStructuredScriptExecutor.cs b/source/Octopus.Tentacle.Client/Scripts/IScriptExecutor.cs similarity index 52% rename from source/Octopus.Tentacle.Client/Scripts/IStructuredScriptExecutor.cs rename to source/Octopus.Tentacle.Client/Scripts/IScriptExecutor.cs index 7e73fec02..877135b3a 100644 --- a/source/Octopus.Tentacle.Client/Scripts/IStructuredScriptExecutor.cs +++ b/source/Octopus.Tentacle.Client/Scripts/IScriptExecutor.cs @@ -7,10 +7,11 @@ namespace Octopus.Tentacle.Client.Scripts { - public interface IStructuredScriptExecutor { + public interface IScriptExecutor { Task<(ScriptStatus, ICommandContext)> StartScript(ExecuteScriptCommand command, StartScriptIsBeingReAttempted startScriptIsBeingReAttempted, CancellationToken scriptExecutionCancellationToken); + /// /// Returns a status or null when scriptExecutionCancellationToken is null. /// @@ -18,7 +19,17 @@ public interface IStructuredScriptExecutor { /// /// Task<(ScriptStatus, ICommandContext)> GetStatus(ICommandContext commandContext, CancellationToken scriptExecutionCancellationToken); - Task<(ScriptStatus, ICommandContext)> Cancel(ICommandContext commandContext, CancellationToken scriptExecutionCancellationToken); - Task Finish(ICommandContext commandContext, CancellationToken scriptExecutionCancellationToken); + + Task<(ScriptStatus, ICommandContext)> CancelScript(ICommandContext commandContext, CancellationToken scriptExecutionCancellationToken); + + /// + /// Use this cancel method if only the ScriptTicket is known, e.g. we called StartScript but never got a response. + /// Inheritors + /// + /// + /// + /// + // Task<(ScriptStatus, ICommandContext)> CancelScript(ScriptTicket scriptTicket, CancellationToken cancellationToken); + Task CleanUpScript(ICommandContext commandContext, CancellationToken scriptExecutionCancellationToken); } } \ No newline at end of file diff --git a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs index fab1fd498..f0c1b2de4 100644 --- a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1AlphaOrchestrator.cs @@ -16,7 +16,7 @@ namespace Octopus.Tentacle.Client.Scripts { - class KubernetesScriptServiceV1AlphaExecutor : IStructuredScriptExecutor + class KubernetesScriptServiceV1AlphaExecutor : IScriptExecutor { readonly IAsyncClientKubernetesScriptServiceV1Alpha clientKubernetesScriptServiceV1Alpha; readonly RpcCallExecutor rpcCallExecutor; @@ -175,7 +175,7 @@ async Task GetStatusAction(CancellationTo scriptExecutionCancellationToken).ConfigureAwait(false); } - public async Task<(ScriptStatus, ICommandContext)> Cancel(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ICommandContext)> CancelScript(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { return Map(await _Cancel(lastStatusResponse, scriptExecutionCancellationToken)); } @@ -205,13 +205,7 @@ async Task CancelScriptAction(Cancellatio } - public async Task Finish(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) - { - await _Finish(lastStatusResponse, scriptExecutionCancellationToken); - return null; - } - - async Task _Finish(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task CleanUpScript(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { try { @@ -237,6 +231,8 @@ await rpcCallExecutor.ExecuteWithNoRetries( logger.Warn("Failed to cleanup the script working directory on Tentacle"); logger.Verbose(ex); } + + return null; } } } \ No newline at end of file diff --git a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1Orchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1Orchestrator.cs index e89a9ce83..92cb7c4a1 100644 --- a/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1Orchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1Orchestrator.cs @@ -16,7 +16,7 @@ namespace Octopus.Tentacle.Client.Scripts { - class KubernetesScriptServiceV1Executor : IStructuredScriptExecutor + class KubernetesScriptServiceV1Executor : IScriptExecutor { readonly IAsyncClientKubernetesScriptServiceV1 clientKubernetesScriptServiceV1; readonly RpcCallExecutor rpcCallExecutor; @@ -166,12 +166,12 @@ async Task GetStatusAction(CancellationToken c scriptExecutionCancellationToken).ConfigureAwait(false); } - public async Task<(ScriptStatus, ICommandContext)> Cancel(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ICommandContext)> CancelScript(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { return Map(await _GetStatus(lastStatusResponse, scriptExecutionCancellationToken)); } - private async Task _Cancel(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + async Task _CancelScript(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { async Task CancelScriptAction(CancellationToken ct) { @@ -195,13 +195,7 @@ async Task CancelScriptAction(CancellationToke CancellationToken.None).ConfigureAwait(false); } - public async Task Finish(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) - { - await _Finish(lastStatusResponse, scriptExecutionCancellationToken); - return null; - } - - private async Task _Finish(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task CleanUpScript(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { try { @@ -227,6 +221,8 @@ await rpcCallExecutor.ExecuteWithNoRetries( logger.Warn("Failed to cleanup the script working directory on Tentacle"); logger.Verbose(ex); } + + return null; } } } \ No newline at end of file diff --git a/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs index 7b1bfaad6..8f3d0cff6 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs @@ -1,7 +1,6 @@ using System; using System.Threading; using System.Threading.Tasks; -using Halibut; using Octopus.Tentacle.Client.EventDriven; using Octopus.Tentacle.Client.Scripts.Models; using Octopus.Tentacle.Contracts; @@ -16,15 +15,15 @@ public sealed class ObservingScriptOrchestrator : IScriptOrchestrator readonly OnScriptCompleted onScriptCompleted; readonly ITentacleClientTaskLog logger; - IStructuredScriptExecutor structuredScriptExecutor; + IScriptExecutor scriptExecutor; public ObservingScriptOrchestrator( IScriptObserverBackoffStrategy scriptObserverBackOffStrategy, OnScriptStatusResponseReceived onScriptStatusResponseReceived, OnScriptCompleted onScriptCompleted, - IStructuredScriptExecutor structuredScriptExecutor, ITentacleClientTaskLog logger) + IScriptExecutor scriptExecutor, ITentacleClientTaskLog logger) { - this.structuredScriptExecutor = structuredScriptExecutor; + this.scriptExecutor = scriptExecutor; this.logger = logger; this.scriptObserverBackOffStrategy = scriptObserverBackOffStrategy; this.onScriptStatusResponseReceived = onScriptStatusResponseReceived; @@ -33,7 +32,7 @@ public ObservingScriptOrchestrator( public async Task ExecuteScript(ExecuteScriptCommand command, CancellationToken scriptExecutionCancellationToken) { - var (scriptStatus, ticketForNextStatus) = await structuredScriptExecutor.StartScript(command, + var (scriptStatus, ticketForNextStatus) = await scriptExecutor.StartScript(command, StartScriptIsBeingReAttempted.FirstAttempt, // This is not re-entrant so this should be true. scriptExecutionCancellationToken).ConfigureAwait(false); @@ -60,7 +59,7 @@ public async Task ExecuteScript(ExecuteScriptCommand comm await onScriptCompleted(scriptExecutionCancellationToken).ConfigureAwait(false); - lastStatusResponse = await structuredScriptExecutor.Finish(lastTicketForNextStatus, scriptExecutionCancellationToken).ConfigureAwait(false) ?? lastStatusResponse; + lastStatusResponse = await scriptExecutor.CleanUpScript(lastTicketForNextStatus, scriptExecutionCancellationToken).ConfigureAwait(false) ?? lastStatusResponse; OnScriptStatusResponseReceived(lastStatusResponse); @@ -81,13 +80,13 @@ public async Task ExecuteScript(ExecuteScriptCommand comm { if (scriptExecutionCancellationToken.IsCancellationRequested) { - (lastStatusResponse, lastTicketForNextStatus) = await structuredScriptExecutor.Cancel(lastTicketForNextStatus, scriptExecutionCancellationToken).ConfigureAwait(false); + (lastStatusResponse, lastTicketForNextStatus) = await scriptExecutor.CancelScript(lastTicketForNextStatus, scriptExecutionCancellationToken).ConfigureAwait(false); } else { try { - (lastStatusResponse, lastTicketForNextStatus) = await structuredScriptExecutor.GetStatus(lastTicketForNextStatus, scriptExecutionCancellationToken).ConfigureAwait(false); + (lastStatusResponse, lastTicketForNextStatus) = await scriptExecutor.GetStatus(lastTicketForNextStatus, scriptExecutionCancellationToken).ConfigureAwait(false); } catch (Exception) { diff --git a/source/Octopus.Tentacle.Client/Scripts/ScriptOrchestratorFactory.cs b/source/Octopus.Tentacle.Client/Scripts/ScriptOrchestratorFactory.cs index 9dd78a1f0..bb0f7452d 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ScriptOrchestratorFactory.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ScriptOrchestratorFactory.cs @@ -1,6 +1,4 @@ using System; -using System.Threading; -using System.Threading.Tasks; using Octopus.Tentacle.Client.Execution; using Octopus.Tentacle.Client.Observability; using Octopus.Tentacle.Client.ServiceHelpers; @@ -8,60 +6,34 @@ namespace Octopus.Tentacle.Client.Scripts { - class ScriptOrchestratorFactory : IScriptOrchestratorFactory + // TODO: this is not an orchestrator factory. + class ScriptOrchestratorFactory { - readonly IScriptObserverBackoffStrategy scriptObserverBackOffStrategy; readonly RpcCallExecutor rpcCallExecutor; readonly ClientOperationMetricsBuilder clientOperationMetricsBuilder; - readonly OnScriptStatusResponseReceived onScriptStatusResponseReceived; - readonly OnScriptCompleted onScriptCompleted; readonly TimeSpan onCancellationAbandonCompleteScriptAfter; readonly ITentacleClientTaskLog logger; readonly ClientsHolder clientsHolder; - readonly TentacleClientOptions clientOptions; public ScriptOrchestratorFactory( ClientsHolder clientsHolder, - IScriptObserverBackoffStrategy scriptObserverBackOffStrategy, RpcCallExecutor rpcCallExecutor, ClientOperationMetricsBuilder clientOperationMetricsBuilder, - OnScriptStatusResponseReceived onScriptStatusResponseReceived, - OnScriptCompleted onScriptCompleted, TimeSpan onCancellationAbandonCompleteScriptAfter, TentacleClientOptions clientOptions, ITentacleClientTaskLog logger) { this.clientsHolder = clientsHolder; - this.scriptObserverBackOffStrategy = scriptObserverBackOffStrategy; this.rpcCallExecutor = rpcCallExecutor; this.clientOperationMetricsBuilder = clientOperationMetricsBuilder; - this.onScriptStatusResponseReceived = onScriptStatusResponseReceived; - this.onScriptCompleted = onScriptCompleted; this.onCancellationAbandonCompleteScriptAfter = onCancellationAbandonCompleteScriptAfter; this.clientOptions = clientOptions; this.logger = logger; } - public async Task CreateOrchestrator(CancellationToken cancellationToken) - { - ScriptServiceVersion scriptServiceToUse; - try - { - var scriptServicePicker = new ScriptServicePicker(clientsHolder.CapabilitiesServiceV2, logger, rpcCallExecutor, clientOptions, clientOperationMetricsBuilder); - scriptServiceToUse = await scriptServicePicker.DetermineScriptServiceVersionToUse(cancellationToken); - } - catch (Exception ex) when (cancellationToken.IsCancellationRequested) - { - throw new OperationCanceledException("Script execution was cancelled", ex); - } - - var structuredScriptOrchestrator = CreateOrchestrator(scriptServiceToUse); - return new ObservingScriptOrchestrator(scriptObserverBackOffStrategy, onScriptStatusResponseReceived, onScriptCompleted, structuredScriptOrchestrator, logger); - } - - public IStructuredScriptExecutor CreateOrchestrator(ScriptServiceVersion scriptServiceToUse) + public IScriptExecutor CreateOrchestrator(ScriptServiceVersion scriptServiceToUse) { if (scriptServiceToUse == ScriptServiceVersion.ScriptServiceVersion1) { diff --git a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Executor.cs b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Executor.cs index 02502e66d..fcc0aa5c2 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Executor.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Executor.cs @@ -14,9 +14,8 @@ namespace Octopus.Tentacle.Client.Scripts { - class ScriptServiceV1Executor : IStructuredScriptExecutor + class ScriptServiceV1Executor : IScriptExecutor { - readonly RpcCallExecutor rpcCallExecutor; readonly ClientOperationMetricsBuilder clientOperationMetricsBuilder; readonly ITentacleClientTaskLog logger; @@ -35,7 +34,7 @@ public ScriptServiceV1Executor( this.logger = logger; } - private StartScriptCommand Map(ExecuteScriptCommand command) + StartScriptCommand Map(ExecuteScriptCommand command) { if (command is not ExecuteShellScriptCommand shellScriptCommand) throw new InvalidOperationException($"{nameof(ScriptServiceV2Executor)} only supports commands of type {nameof(ExecuteShellScriptCommand)}."); @@ -51,12 +50,12 @@ private StartScriptCommand Map(ExecuteScriptCommand command) shellScriptCommand.Files.ToArray()); } - private ScriptStatus MapToScriptStatus(ScriptStatusResponse scriptStatusResponse) + ScriptStatus MapToScriptStatus(ScriptStatusResponse scriptStatusResponse) { return new ScriptStatus(scriptStatusResponse.State, scriptStatusResponse.ExitCode, scriptStatusResponse.Logs); } - private ICommandContext MapToNextStatus(ScriptStatusResponse scriptStatusResponse) + ICommandContext MapToNextStatus(ScriptStatusResponse scriptStatusResponse) { return new DefaultCommandContext(scriptStatusResponse.Ticket, scriptStatusResponse.NextLogSequence, ScriptServiceVersion.ScriptServiceVersion1); } @@ -65,21 +64,19 @@ private ICommandContext MapToNextStatus(ScriptStatusResponse scriptStatusRespons { return (MapToScriptStatus(r), MapToNextStatus(r)); } - + public async Task<(ScriptStatus, ICommandContext)> StartScript(ExecuteScriptCommand executeScriptCommand, StartScriptIsBeingReAttempted startScriptIsBeingReAttempted, CancellationToken scriptExecutionCancellationToken) { // Script Service v1 is not idempotent, do not allow it to be re-attempted as it may run a second time. if (startScriptIsBeingReAttempted == StartScriptIsBeingReAttempted.PossiblyBeingReAttempted) - { - return (new ScriptStatus(ProcessState.Complete, - ScriptExitCodes.UnknownScriptExitCode, - new List()), + return (new ScriptStatus(ProcessState.Complete, + ScriptExitCodes.UnknownScriptExitCode, + new List()), // TODO: We should try to encourage a DefaultCommandContext which will do nothing perhaps set the ScriptServiceVersion to // one that always returns the above exit code. new DefaultCommandContext(new ScriptTicket(Guid.NewGuid().ToString()), 0, ScriptServiceVersion.ScriptServiceVersion1)); - } var command = Map(executeScriptCommand); var scriptTicket = await rpcCallExecutor.ExecuteWithNoRetries( RpcCall.Create(nameof(IScriptService.StartScript)), @@ -101,7 +98,7 @@ private ICommandContext MapToNextStatus(ScriptStatusResponse scriptStatusRespons return Map(await _GetStatus(commandContext, scriptExecutionCancellationToken)); } - private async Task _GetStatus(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + async Task _GetStatus(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { var scriptStatusResponseV1 = await rpcCallExecutor.ExecuteWithNoRetries( RpcCall.Create(nameof(IScriptService.GetStatus)), @@ -119,12 +116,12 @@ private async Task _GetStatus(ICommandContext lastStatusRe return scriptStatusResponseV1; } - public async Task<(ScriptStatus, ICommandContext)> Cancel(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ICommandContext)> CancelScript(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { return Map(await _Cancel(lastStatusResponse, scriptExecutionCancellationToken)); } - - private async Task _Cancel(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + + async Task _Cancel(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { var response = await rpcCallExecutor.ExecuteWithNoRetries( RpcCall.Create(nameof(IScriptService.CancelScript)), @@ -142,12 +139,12 @@ private async Task _Cancel(ICommandContext lastStatusRespo return response; } - public async Task Finish(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task CleanUpScript(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { - return MapToScriptStatus(await _Finish(lastStatusResponse, scriptExecutionCancellationToken)); + return MapToScriptStatus(await _CleanUpScript(lastStatusResponse, scriptExecutionCancellationToken)); } - private async Task _Finish(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + async Task _CleanUpScript(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { var response = await rpcCallExecutor.ExecuteWithNoRetries( RpcCall.Create(nameof(IScriptService.CompleteScript)), @@ -164,7 +161,5 @@ private async Task _Finish(ICommandContext lastStatusRespo return response; } - - } } \ No newline at end of file diff --git a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs index 4dfaf33eb..6e2d238ae 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV2Orchestrator.cs @@ -16,7 +16,7 @@ namespace Octopus.Tentacle.Client.Scripts { - class ScriptServiceV2Executor : IStructuredScriptExecutor + class ScriptServiceV2Executor : IScriptExecutor { readonly IAsyncClientScriptServiceV2 clientScriptServiceV2; readonly RpcCallExecutor rpcCallExecutor; @@ -161,7 +161,7 @@ async Task GetStatusAction(CancellationToken ct) scriptExecutionCancellationToken).ConfigureAwait(false); } - public async Task<(ScriptStatus, ICommandContext)> Cancel(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task<(ScriptStatus, ICommandContext)> CancelScript(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { return Map(await _Cancel(lastStatusResponse, scriptExecutionCancellationToken)); } @@ -190,13 +190,7 @@ async Task CancelScriptAction(CancellationToken ct) CancellationToken.None).ConfigureAwait(false); } - public async Task Finish(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) - { - await _Finish(lastStatusResponse, scriptExecutionCancellationToken); - return null; - } - - private async Task _Finish(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) + public async Task CleanUpScript(ICommandContext lastStatusResponse, CancellationToken scriptExecutionCancellationToken) { try { @@ -223,6 +217,8 @@ await rpcCallExecutor.ExecuteWithNoRetries( logger.Warn("Failed to cleanup the script working directory on Tentacle"); logger.Verbose(ex); } + + return null; } } } \ No newline at end of file diff --git a/source/Octopus.Tentacle.Client/TentacleClient.cs b/source/Octopus.Tentacle.Client/TentacleClient.cs index 8fe0b85e5..66d114a01 100644 --- a/source/Octopus.Tentacle.Client/TentacleClient.cs +++ b/source/Octopus.Tentacle.Client/TentacleClient.cs @@ -11,23 +11,22 @@ using Octopus.Tentacle.Client.ServiceHelpers; using Octopus.Tentacle.Contracts; using Octopus.Tentacle.Contracts.Capabilities; -using Octopus.Tentacle.Contracts.ClientServices; -using Octopus.Tentacle.Contracts.KubernetesScriptServiceV1; -using Octopus.Tentacle.Contracts.KubernetesScriptServiceV1Alpha; using Octopus.Tentacle.Contracts.Logging; using Octopus.Tentacle.Contracts.Observability; -using Octopus.Tentacle.Contracts.ScriptServiceV2; using ITentacleClientObserver = Octopus.Tentacle.Contracts.Observability.ITentacleClientObserver; namespace Octopus.Tentacle.Client { public class TentacleClient : ITentacleClient { + readonly ServiceEndPoint serviceEndPoint; + readonly IHalibutRuntime halibutRuntime; readonly IScriptObserverBackoffStrategy scriptObserverBackOffStrategy; readonly ITentacleClientObserver tentacleClientObserver; readonly RpcCallExecutor rpcCallExecutor; readonly TentacleClientOptions clientOptions; + readonly ITentacleServiceDecoratorFactory? tentacleServicesDecoratorFactory; readonly ClientsHolder clientsHolder; public static void CacheServiceWasNotFoundResponseMessages(IHalibutRuntime halibutRuntime) @@ -63,10 +62,13 @@ internal TentacleClient( TentacleClientOptions clientOptions, ITentacleServiceDecoratorFactory? tentacleServicesDecoratorFactory) { + this.serviceEndPoint = serviceEndPoint; + this.halibutRuntime = halibutRuntime; this.scriptObserverBackOffStrategy = scriptObserverBackOffStrategy; this.tentacleClientObserver = tentacleClientObserver.DecorateWithNonThrowingTentacleClientObserver(); this.clientOptions = clientOptions; + this.tentacleServicesDecoratorFactory = tentacleServicesDecoratorFactory; if (halibutRuntime.OverrideErrorResponseMessageCaching == null) { @@ -162,19 +164,21 @@ public async Task ExecuteScript(ExecuteScriptCommand exec try { - var factory = new ScriptOrchestratorFactory( - clientsHolder, - scriptObserverBackOffStrategy, - rpcCallExecutor, - operationMetricsBuilder, + + var eventDrivenScriptExecutor = new AggregateScriptExecutor(logger, + tentacleClientObserver, + clientOptions, + halibutRuntime, + serviceEndPoint, + tentacleServicesDecoratorFactory, + OnCancellationAbandonCompleteScriptAfter); + + var orchestrator = new ObservingScriptOrchestrator(scriptObserverBackOffStrategy, onScriptStatusResponseReceived, onScriptCompleted, - OnCancellationAbandonCompleteScriptAfter, - clientOptions, + eventDrivenScriptExecutor, logger); - var orchestrator = await factory.CreateOrchestrator(scriptExecutionCancellationToken); - var result = await orchestrator.ExecuteScript(executeScriptCommand, scriptExecutionCancellationToken); return new ScriptExecutionResult(result.State, result.ExitCode); @@ -186,6 +190,7 @@ public async Task ExecuteScript(ExecuteScriptCommand exec } finally { + // TODO handle this in the new pipeline. var operationMetrics = operationMetricsBuilder.Build(); tentacleClientObserver.ExecuteScriptCompleted(operationMetrics, logger); } From 228af09c767bea4f542ba995307d84cf70a726bd Mon Sep 17 00:00:00 2001 From: Luke Butters Date: Fri, 5 Jul 2024 11:08:59 +1000 Subject: [PATCH 23/23] . --- source/Octopus.Tentacle.Client/AggregateScriptExecutor.cs | 8 ++++---- source/Octopus.Tentacle.Client/Scripts/IScriptExecutor.cs | 2 ++ .../Scripts/ObservingScriptOrchestrator.cs | 3 ++- .../Scripts/ScriptOrchestratorFactory.cs | 2 +- .../Scripts/ScriptServiceV1Executor.cs | 3 +++ 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/source/Octopus.Tentacle.Client/AggregateScriptExecutor.cs b/source/Octopus.Tentacle.Client/AggregateScriptExecutor.cs index 7e384ec75..533b06fbb 100644 --- a/source/Octopus.Tentacle.Client/AggregateScriptExecutor.cs +++ b/source/Octopus.Tentacle.Client/AggregateScriptExecutor.cs @@ -62,7 +62,7 @@ internal AggregateScriptExecutor(ITentacleClientTaskLog logger, var scriptOrchestratorFactory = GetNewScriptOrchestratorFactory(operationMetricsBuilder); - var orchestrator = scriptOrchestratorFactory.CreateOrchestrator(scriptServiceToUse); + var orchestrator = scriptOrchestratorFactory.CreateScriptExecutor(scriptServiceToUse); return await orchestrator.StartScript(executeScriptCommand, startScriptIsBeingReAttempted, cancellationToken); } @@ -85,7 +85,7 @@ async Task DetermineScriptServiceVersionToUse(Cancellation var scriptOrchestratorFactory = GetNewScriptOrchestratorFactory(operationMetricsBuilder); - var orchestrator = scriptOrchestratorFactory.CreateOrchestrator(ticketForNextNextStatus.WhichService); + var orchestrator = scriptOrchestratorFactory.CreateScriptExecutor(ticketForNextNextStatus.WhichService); return await orchestrator.GetStatus(ticketForNextNextStatus, cancellationToken); } @@ -96,7 +96,7 @@ async Task DetermineScriptServiceVersionToUse(Cancellation var scriptOrchestratorFactory = GetNewScriptOrchestratorFactory(operationMetricsBuilder); - var orchestrator = scriptOrchestratorFactory.CreateOrchestrator(ticketForNextNextStatus.WhichService); + var orchestrator = scriptOrchestratorFactory.CreateScriptExecutor(ticketForNextNextStatus.WhichService); return await orchestrator.CancelScript(ticketForNextNextStatus, cancellationToken); } @@ -118,7 +118,7 @@ async Task DetermineScriptServiceVersionToUse(Cancellation var scriptOrchestratorFactory = GetNewScriptOrchestratorFactory(operationMetricsBuilder); - var orchestrator = scriptOrchestratorFactory.CreateOrchestrator(ticketForNextNextStatus.WhichService); + var orchestrator = scriptOrchestratorFactory.CreateScriptExecutor(ticketForNextNextStatus.WhichService); return await orchestrator.CleanUpScript(ticketForNextNextStatus, cancellationToken); } diff --git a/source/Octopus.Tentacle.Client/Scripts/IScriptExecutor.cs b/source/Octopus.Tentacle.Client/Scripts/IScriptExecutor.cs index 877135b3a..03c53a14b 100644 --- a/source/Octopus.Tentacle.Client/Scripts/IScriptExecutor.cs +++ b/source/Octopus.Tentacle.Client/Scripts/IScriptExecutor.cs @@ -30,6 +30,8 @@ public interface IScriptExecutor { /// /// // Task<(ScriptStatus, ICommandContext)> CancelScript(ScriptTicket scriptTicket, CancellationToken cancellationToken); + + Task CleanUpScript(ICommandContext commandContext, CancellationToken scriptExecutionCancellationToken); } } \ No newline at end of file diff --git a/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs b/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs index 8f3d0cff6..c00f5fcaa 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ObservingScriptOrchestrator.cs @@ -21,7 +21,8 @@ public ObservingScriptOrchestrator( IScriptObserverBackoffStrategy scriptObserverBackOffStrategy, OnScriptStatusResponseReceived onScriptStatusResponseReceived, OnScriptCompleted onScriptCompleted, - IScriptExecutor scriptExecutor, ITentacleClientTaskLog logger) + IScriptExecutor scriptExecutor, + ITentacleClientTaskLog logger) { this.scriptExecutor = scriptExecutor; this.logger = logger; diff --git a/source/Octopus.Tentacle.Client/Scripts/ScriptOrchestratorFactory.cs b/source/Octopus.Tentacle.Client/Scripts/ScriptOrchestratorFactory.cs index bb0f7452d..55d1cd5fe 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ScriptOrchestratorFactory.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ScriptOrchestratorFactory.cs @@ -33,7 +33,7 @@ public ScriptOrchestratorFactory( this.logger = logger; } - public IScriptExecutor CreateOrchestrator(ScriptServiceVersion scriptServiceToUse) + public IScriptExecutor CreateScriptExecutor(ScriptServiceVersion scriptServiceToUse) { if (scriptServiceToUse == ScriptServiceVersion.ScriptServiceVersion1) { diff --git a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Executor.cs b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Executor.cs index fcc0aa5c2..f1a7b873c 100644 --- a/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Executor.cs +++ b/source/Octopus.Tentacle.Client/Scripts/ScriptServiceV1Executor.cs @@ -71,12 +71,15 @@ ICommandContext MapToNextStatus(ScriptStatusResponse scriptStatusResponse) { // Script Service v1 is not idempotent, do not allow it to be re-attempted as it may run a second time. if (startScriptIsBeingReAttempted == StartScriptIsBeingReAttempted.PossiblyBeingReAttempted) + { return (new ScriptStatus(ProcessState.Complete, ScriptExitCodes.UnknownScriptExitCode, new List()), // TODO: We should try to encourage a DefaultCommandContext which will do nothing perhaps set the ScriptServiceVersion to // one that always returns the above exit code. new DefaultCommandContext(new ScriptTicket(Guid.NewGuid().ToString()), 0, ScriptServiceVersion.ScriptServiceVersion1)); + } + var command = Map(executeScriptCommand); var scriptTicket = await rpcCallExecutor.ExecuteWithNoRetries( RpcCall.Create(nameof(IScriptService.StartScript)),