Skip to content

Commit 189d819

Browse files
authored
Enable external DF SDK (#839)
1 parent dd7864c commit 189d819

17 files changed

+589
-180
lines changed

src/DurableSDK/ExternalInvoker.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//
2+
// Copyright (c) Microsoft. All rights reserved.
3+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
4+
//
5+
6+
namespace Microsoft.Azure.Functions.PowerShellWorker.Durable
7+
{
8+
using System;
9+
using System.Collections;
10+
using System.Management.Automation;
11+
12+
// Contract for the orchestration invoker in the external Durable Functions SDK
13+
internal class ExternalInvoker : IExternalOrchestrationInvoker
14+
{
15+
private readonly Func<PowerShell, object> _externalSDKInvokerFunction;
16+
17+
public ExternalInvoker(Func<PowerShell, object> invokerFunction)
18+
{
19+
_externalSDKInvokerFunction = invokerFunction;
20+
}
21+
22+
// Invokes an orchestration using the external Durable SDK
23+
public Hashtable Invoke(IPowerShellServices powerShellServices)
24+
{
25+
return (Hashtable)_externalSDKInvokerFunction.Invoke(powerShellServices.GetPowerShell());
26+
}
27+
}
28+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//
2+
// Copyright (c) Microsoft. All rights reserved.
3+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
4+
//
5+
6+
namespace Microsoft.Azure.Functions.PowerShellWorker.Durable
7+
{
8+
using System.Collections;
9+
10+
// Contract interface for the orchestration invoker in the external Durable Functions SDK
11+
internal interface IExternalOrchestrationInvoker
12+
{
13+
// Invokes an orchestration using the external Durable SDK
14+
Hashtable Invoke(IPowerShellServices powerShellServices);
15+
}
16+
}

src/DurableSDK/IOrchestrationInvoker.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.Durable
1010
internal interface IOrchestrationInvoker
1111
{
1212
Hashtable Invoke(OrchestrationBindingInfo orchestrationBindingInfo, IPowerShellServices pwsh);
13+
void SetExternalInvoker(IExternalOrchestrationInvoker externalInvoker);
1314
}
1415
}

src/DurableSDK/IPowerShellServices.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,30 @@
55

66
namespace Microsoft.Azure.Functions.PowerShellWorker.Durable
77
{
8+
using Microsoft.Azure.WebJobs.Script.Grpc.Messages;
89
using System;
910
using System.Management.Automation;
1011

1112
internal interface IPowerShellServices
1213
{
14+
PowerShell GetPowerShell();
15+
16+
bool HasExternalDurableSDK();
17+
18+
bool isExternalDurableSdkLoaded();
19+
20+
void EnableExternalDurableSDK();
21+
1322
void SetDurableClient(object durableClient);
1423

15-
void SetOrchestrationContext(OrchestrationContext orchestrationContext);
24+
OrchestrationBindingInfo SetOrchestrationContext(ParameterBinding context, out IExternalOrchestrationInvoker externalInvoker);
1625

1726
void ClearOrchestrationContext();
1827

28+
void TracePipelineObject();
29+
30+
void AddParameter(string name, object value);
31+
1932
IAsyncResult BeginInvoke(PSDataCollection<object> output);
2033

2134
void EndInvoke(IAsyncResult asyncResult);

src/DurableSDK/OrchestrationInvoker.cs

Lines changed: 78 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -11,58 +11,97 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.Durable
1111
using System.Linq;
1212
using System.Management.Automation;
1313

14-
using PowerShellWorker.Utility;
1514
using Microsoft.Azure.Functions.PowerShellWorker.Durable.Actions;
15+
using Microsoft.Azure.Functions.PowerShellWorker.Utility;
1616

1717
internal class OrchestrationInvoker : IOrchestrationInvoker
1818
{
19-
public Hashtable Invoke(OrchestrationBindingInfo orchestrationBindingInfo, IPowerShellServices pwsh)
19+
private IExternalOrchestrationInvoker externalInvoker;
20+
21+
public Hashtable Invoke(
22+
OrchestrationBindingInfo orchestrationBindingInfo,
23+
IPowerShellServices powerShellServices)
2024
{
2125
try
2226
{
23-
var outputBuffer = new PSDataCollection<object>();
24-
var context = orchestrationBindingInfo.Context;
27+
if (powerShellServices.HasExternalDurableSDK())
28+
{
29+
return InvokeExternalDurableSDK(powerShellServices);
30+
}
31+
return InvokeInternalDurableSDK(orchestrationBindingInfo, powerShellServices);
32+
}
33+
catch (Exception ex)
34+
{
35+
ex.Data.Add(Utils.IsOrchestrationFailureKey, true);
36+
throw;
37+
}
38+
finally
39+
{
40+
powerShellServices.ClearStreamsAndCommands();
41+
}
42+
}
43+
44+
public Hashtable InvokeExternalDurableSDK(IPowerShellServices powerShellServices)
45+
{
46+
return externalInvoker.Invoke(powerShellServices);
47+
}
48+
49+
public Hashtable InvokeInternalDurableSDK(
50+
OrchestrationBindingInfo orchestrationBindingInfo,
51+
IPowerShellServices powerShellServices)
52+
{
53+
var outputBuffer = new PSDataCollection<object>();
54+
var context = orchestrationBindingInfo.Context;
55+
56+
// context.History should never be null when initializing CurrentUtcDateTime
57+
var orchestrationStart = context.History.First(
58+
e => e.EventType == HistoryEventType.OrchestratorStarted);
59+
context.CurrentUtcDateTime = orchestrationStart.Timestamp.ToUniversalTime();
2560

26-
// context.History should never be null when initializing CurrentUtcDateTime
27-
var orchestrationStart = context.History.First(
28-
e => e.EventType == HistoryEventType.OrchestratorStarted);
29-
context.CurrentUtcDateTime = orchestrationStart.Timestamp.ToUniversalTime();
61+
// Marks the first OrchestratorStarted event as processed
62+
orchestrationStart.IsProcessed = true;
3063

31-
// Marks the first OrchestratorStarted event as processed
32-
orchestrationStart.IsProcessed = true;
33-
34-
var asyncResult = pwsh.BeginInvoke(outputBuffer);
64+
// Finish initializing the Function invocation
65+
powerShellServices.AddParameter(orchestrationBindingInfo.ParameterName, context);
66+
powerShellServices.TracePipelineObject();
3567

36-
var (shouldStop, actions) =
37-
orchestrationBindingInfo.Context.OrchestrationActionCollector.WaitForActions(asyncResult.AsyncWaitHandle);
68+
var asyncResult = powerShellServices.BeginInvoke(outputBuffer);
3869

39-
if (shouldStop)
70+
var (shouldStop, actions) =
71+
orchestrationBindingInfo.Context.OrchestrationActionCollector.WaitForActions(asyncResult.AsyncWaitHandle);
72+
73+
if (shouldStop)
74+
{
75+
// The orchestration function should be stopped and restarted
76+
powerShellServices.StopInvoke();
77+
return CreateOrchestrationResult(isDone: false, actions, output: null, context.CustomStatus);
78+
}
79+
else
80+
{
81+
try
4082
{
41-
// The orchestration function should be stopped and restarted
42-
pwsh.StopInvoke();
43-
return CreateOrchestrationResult(isDone: false, actions, output: null, context.CustomStatus);
83+
// The orchestration function completed
84+
powerShellServices.EndInvoke(asyncResult);
85+
var result = CreateReturnValueFromFunctionOutput(outputBuffer);
86+
return CreateOrchestrationResult(isDone: true, actions, output: result, context.CustomStatus);
4487
}
45-
else
88+
catch (Exception e)
4689
{
47-
try
48-
{
49-
// The orchestration function completed
50-
pwsh.EndInvoke(asyncResult);
51-
var result = FunctionReturnValueBuilder.CreateReturnValueFromFunctionOutput(outputBuffer);
52-
return CreateOrchestrationResult(isDone: true, actions, output: result, context.CustomStatus);
53-
}
54-
catch (Exception e)
55-
{
56-
// The orchestrator code has thrown an unhandled exception:
57-
// this should be treated as an entire orchestration failure
58-
throw new OrchestrationFailureException(actions, context.CustomStatus, e);
59-
}
90+
// The orchestrator code has thrown an unhandled exception:
91+
// this should be treated as an entire orchestration failure
92+
throw new OrchestrationFailureException(actions, context.CustomStatus, e);
6093
}
6194
}
62-
finally
95+
}
96+
97+
public static object CreateReturnValueFromFunctionOutput(IList<object> pipelineItems)
98+
{
99+
if (pipelineItems == null || pipelineItems.Count <= 0)
63100
{
64-
pwsh.ClearStreamsAndCommands();
101+
return null;
65102
}
103+
104+
return pipelineItems.Count == 1 ? pipelineItems[0] : pipelineItems.ToArray();
66105
}
67106

68107
private static Hashtable CreateOrchestrationResult(
@@ -74,5 +113,10 @@ private static Hashtable CreateOrchestrationResult(
74113
var orchestrationMessage = new OrchestrationMessage(isDone, actions, output, customStatus);
75114
return new Hashtable { { AzFunctionInfo.DollarReturn, orchestrationMessage } };
76115
}
116+
117+
public void SetExternalInvoker(IExternalOrchestrationInvoker externalInvoker)
118+
{
119+
this.externalInvoker = externalInvoker;
120+
}
77121
}
78122
}

0 commit comments

Comments
 (0)