14
14
using Microsoft . Azure . Functions . PowerShellWorker . PowerShell ;
15
15
using Microsoft . Azure . Functions . PowerShellWorker . Utility ;
16
16
using LogLevel = Microsoft . Azure . WebJobs . Script . Grpc . Messages . RpcLog . Types . Level ;
17
+ using Microsoft . Azure . Functions . PowerShellWorker . Messaging ;
17
18
18
19
namespace Microsoft . Azure . Functions . PowerShellWorker . DependencyManagement
19
20
{
20
21
using System . Management . Automation ;
21
22
using System . Management . Automation . Language ;
23
+ using System . Management . Automation . Runspaces ;
24
+ using System . Threading . Tasks ;
22
25
23
26
internal class DependencyManager
24
27
{
@@ -28,6 +31,11 @@ internal class DependencyManager
28
31
// This is the location where the dependent modules will be installed.
29
32
internal static string DependenciesPath { get ; private set ; }
30
33
34
+ internal Exception DependencyError => _dependencyError ;
35
+
36
+ //The dependency download task
37
+ internal Task DependencyDownloadTask => _dependencyDownloadTask ;
38
+
31
39
// Az module name.
32
40
private const string AzModuleName = "Az" ;
33
41
@@ -51,6 +59,12 @@ internal class DependencyManager
51
59
// Managed Dependencies folder name.
52
60
private const string ManagedDependenciesFolderName = "ManagedDependencies" ;
53
61
62
+ //Set when any error occurs while downloading dependencies
63
+ private Exception _dependencyError ;
64
+
65
+ //Dependency download task
66
+ private Task _dependencyDownloadTask ;
67
+
54
68
// This flag is used to figure out if we need to install/reinstall all the function app dependencies.
55
69
// If we do, we use it to clean up the module destination path.
56
70
private bool _shouldUpdateFunctionAppDependencies ;
@@ -60,6 +74,74 @@ internal DependencyManager()
60
74
Dependencies = new List < DependencyInfo > ( ) ;
61
75
}
62
76
77
+ /// <summary>
78
+ /// Processes the dependency download request
79
+ /// </summary>
80
+ /// <param name="msgStream">The protobuf messaging stream</param>
81
+ /// <param name="request">The StreamingMessage request for function load</param>
82
+ internal void ProcessDependencyDownload ( MessagingStream msgStream , StreamingMessage request )
83
+ {
84
+ if ( request . FunctionLoadRequest . ManagedDependencyEnabled )
85
+ {
86
+ var rpcLogger = new RpcLogger ( msgStream ) ;
87
+ rpcLogger . SetContext ( request . RequestId , null ) ;
88
+ if ( Dependencies . Count == 0 )
89
+ {
90
+ // If there are no dependencies to install, log and return.
91
+ rpcLogger . Log ( LogLevel . Trace , PowerShellWorkerStrings . FunctionAppDoesNotHaveDependentModulesToInstall , isUserLog : true ) ;
92
+ return ;
93
+ }
94
+
95
+ if ( ! _shouldUpdateFunctionAppDependencies )
96
+ {
97
+ // The function app already has the latest dependencies installed.
98
+ rpcLogger . Log ( LogLevel . Trace , PowerShellWorkerStrings . LatestFunctionAppDependenciesAlreadyInstalled , isUserLog : true ) ;
99
+ return ;
100
+ }
101
+
102
+ //Start dependency download on a separate thread
103
+ _dependencyDownloadTask = Task . Run ( ( ) => ProcessDependencies ( rpcLogger ) ) ;
104
+ }
105
+ }
106
+
107
+ /// <summary>
108
+ /// Waits for the dependency download task to finish
109
+ /// and sets it's reference to null to be picked for cleanup by next run of GC
110
+ /// </summary>
111
+ internal void WaitOnDependencyDownload ( )
112
+ {
113
+ if ( _dependencyDownloadTask != null )
114
+ {
115
+ _dependencyDownloadTask . Wait ( ) ;
116
+ _dependencyDownloadTask = null ;
117
+ }
118
+ }
119
+
120
+ private void ProcessDependencies ( RpcLogger rpcLogger )
121
+ {
122
+ try
123
+ {
124
+ _dependencyError = null ;
125
+ var initialSessionState = InitialSessionState . CreateDefault ( ) ;
126
+ initialSessionState . ThreadOptions = PSThreadOptions . UseCurrentThread ;
127
+ initialSessionState . EnvironmentVariables . Add ( new SessionStateVariableEntry ( "PSModulePath" , FunctionLoader . FunctionModulePath , null ) ) ;
128
+ // Setting the execution policy on macOS and Linux throws an exception so only update it on Windows
129
+ if ( Platform . IsWindows )
130
+ {
131
+ initialSessionState . ExecutionPolicy = Microsoft . PowerShell . ExecutionPolicy . Unrestricted ;
132
+ }
133
+
134
+ using ( PowerShell powerShellInstance = PowerShell . Create ( initialSessionState ) )
135
+ {
136
+ InstallFunctionAppDependencies ( powerShellInstance , rpcLogger ) ;
137
+ }
138
+ }
139
+ catch ( Exception e )
140
+ {
141
+ _dependencyError = e ;
142
+ }
143
+ }
144
+
63
145
/// <summary>
64
146
/// Initializes the dependency manger and performs the following:
65
147
/// - Parse functionAppRoot\requirements.psd1 file and create a list of dependencies to install.
@@ -83,8 +165,8 @@ internal void Initialize(FunctionLoadRequest request)
83
165
foreach ( DictionaryEntry entry in entries )
84
166
{
85
167
// A valid entry is of the form: 'ModuleName'='MajorVersion.*"
86
- string name = ( string ) entry . Key ;
87
- string version = ( string ) entry . Value ;
168
+ string name = ( string ) entry . Key ;
169
+ string version = ( string ) entry . Value ;
88
170
89
171
// Validates that the module name is a supported dependency.
90
172
ValidateModuleName ( name ) ;
@@ -124,20 +206,6 @@ internal void InstallFunctionAppDependencies(PowerShell pwsh, ILogger logger)
124
206
{
125
207
try
126
208
{
127
- if ( Dependencies . Count == 0 )
128
- {
129
- // If there are no dependencies to install, log and return.
130
- logger . Log ( LogLevel . Trace , PowerShellWorkerStrings . FunctionAppDoesNotHaveDependentModulesToInstall , isUserLog : true ) ;
131
- return ;
132
- }
133
-
134
- if ( ! _shouldUpdateFunctionAppDependencies )
135
- {
136
- // The function app already has the latest dependencies installed.
137
- logger . Log ( LogLevel . Trace , PowerShellWorkerStrings . LatestFunctionAppDependenciesAlreadyInstalled , isUserLog : true ) ;
138
- return ;
139
- }
140
-
141
209
// Install the function dependencies.
142
210
logger . Log ( LogLevel . Trace , PowerShellWorkerStrings . InstallingFunctionAppDependentModules , isUserLog : true ) ;
143
211
0 commit comments