1414using Microsoft . Azure . Functions . PowerShellWorker . PowerShell ;
1515using Microsoft . Azure . Functions . PowerShellWorker . Utility ;
1616using LogLevel = Microsoft . Azure . WebJobs . Script . Grpc . Messages . RpcLog . Types . Level ;
17+ using Microsoft . Azure . Functions . PowerShellWorker . Messaging ;
1718
1819namespace Microsoft . Azure . Functions . PowerShellWorker . DependencyManagement
1920{
2021 using System . Management . Automation ;
2122 using System . Management . Automation . Language ;
23+ using System . Management . Automation . Runspaces ;
24+ using System . Threading . Tasks ;
2225
2326 internal class DependencyManager
2427 {
@@ -28,6 +31,11 @@ internal class DependencyManager
2831 // This is the location where the dependent modules will be installed.
2932 internal static string DependenciesPath { get ; private set ; }
3033
34+ internal Exception DependencyError => _dependencyError ;
35+
36+ //The dependency download task
37+ internal Task DependencyDownloadTask => _dependencyDownloadTask ;
38+
3139 // Az module name.
3240 private const string AzModuleName = "Az" ;
3341
@@ -51,6 +59,12 @@ internal class DependencyManager
5159 // Managed Dependencies folder name.
5260 private const string ManagedDependenciesFolderName = "ManagedDependencies" ;
5361
62+ //Set when any error occurs while downloading dependencies
63+ private Exception _dependencyError ;
64+
65+ //Dependency download task
66+ private Task _dependencyDownloadTask ;
67+
5468 // This flag is used to figure out if we need to install/reinstall all the function app dependencies.
5569 // If we do, we use it to clean up the module destination path.
5670 private bool _shouldUpdateFunctionAppDependencies ;
@@ -60,6 +74,74 @@ internal DependencyManager()
6074 Dependencies = new List < DependencyInfo > ( ) ;
6175 }
6276
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+
63145 /// <summary>
64146 /// Initializes the dependency manger and performs the following:
65147 /// - Parse functionAppRoot\requirements.psd1 file and create a list of dependencies to install.
@@ -83,8 +165,8 @@ internal void Initialize(FunctionLoadRequest request)
83165 foreach ( DictionaryEntry entry in entries )
84166 {
85167 // 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 ;
88170
89171 // Validates that the module name is a supported dependency.
90172 ValidateModuleName ( name ) ;
@@ -124,20 +206,6 @@ internal void InstallFunctionAppDependencies(PowerShell pwsh, ILogger logger)
124206 {
125207 try
126208 {
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-
141209 // Install the function dependencies.
142210 logger . Log ( LogLevel . Trace , PowerShellWorkerStrings . InstallingFunctionAppDependentModules , isUserLog : true ) ;
143211
0 commit comments