/// <summary> /// Method to process a FunctionLoadRequest. /// FunctionLoadRequest should be processed sequentially. There is no point to process FunctionLoadRequest /// concurrently as a FunctionApp doesn't include a lot functions in general. Having this step sequential /// will make the Runspace-level initialization easier and more predictable. /// </summary> internal StreamingMessage ProcessFunctionLoadRequest(StreamingMessage request) { FunctionLoadRequest functionLoadRequest = request.FunctionLoadRequest; StreamingMessage response = NewStreamingMessageTemplate( request.RequestId, StreamingMessage.ContentOneofCase.FunctionLoadResponse, out StatusResult status); response.FunctionLoadResponse.FunctionId = functionLoadRequest.FunctionId; try { // Ideally, the initialization should happen when processing 'WorkerInitRequest', however, the 'WorkerInitRequest' // message doesn't provide the file path of the FunctionApp. That information is not available until the first // 'FunctionLoadRequest' comes in. Therefore, we run initialization here. if (!_isFunctionAppInitialized) { FunctionLoader.SetupWellKnownPaths(functionLoadRequest); _powerShellManager.PerformWorkerLevelInitialization(); _powerShellManager.PerformRunspaceLevelInitialization(); _isFunctionAppInitialized = true; } // Load the metadata of the function. _functionLoader.LoadFunction(functionLoadRequest); } catch (Exception e) { status.Status = StatusResult.Types.Status.Failure; status.Exception = e.ToRpcException(); } return(response); }
/// <summary> /// Method to process a FunctionLoadRequest. /// FunctionLoadRequest should be processed sequentially. There is no point to process FunctionLoadRequest /// concurrently as a FunctionApp doesn't include a lot functions in general. Having this step sequential /// will make the Runspace-level initialization easier and more predictable. /// </summary> internal StreamingMessage ProcessFunctionLoadRequest(StreamingMessage request) { FunctionLoadRequest functionLoadRequest = request.FunctionLoadRequest; StreamingMessage response = NewStreamingMessageTemplate( request.RequestId, StreamingMessage.ContentOneofCase.FunctionLoadResponse, out StatusResult status); response.FunctionLoadResponse.FunctionId = functionLoadRequest.FunctionId; // The worker may occasionally receive multiple function load requests with // the same FunctionId. In order to make function load request idempotent, // the worker should ignore the duplicates. if (FunctionLoader.IsLoaded(functionLoadRequest.FunctionId)) { // If FunctionLoader considers this function loaded, this means // the previous request was successful, so respond accordingly. return(response); } // When a functionLoadRequest comes in, we check to see if a dependency download has failed in a previous call // or if PowerShell could not be initialized. If this is the case, mark this as a failed request // and submit the exception to the Host (runtime). if (_initTerminatingError != null) { status.Status = StatusResult.Types.Status.Failure; status.Exception = _initTerminatingError.ToRpcException(); return(response); } // Ideally, the initialization should happen when processing 'WorkerInitRequest', however, the 'WorkerInitRequest' // message doesn't provide information about the FunctionApp. That information is not available until the first // 'FunctionLoadRequest' comes in. Therefore, we run initialization here. // Also, we receive a FunctionLoadRequest when a proxy is configured. Proxies don't have the Metadata.Directory set // which would cause initialization issues with the PSModulePath. Since they don't have that set, we skip over them. if (!_isFunctionAppInitialized && !functionLoadRequest.Metadata.IsProxy) { try { _isFunctionAppInitialized = true; var rpcLogger = new RpcLogger(_msgStream); rpcLogger.SetContext(request.RequestId, null); _dependencyManager = new DependencyManager(request.FunctionLoadRequest.Metadata.Directory); var managedDependenciesPath = _dependencyManager.Initialize(request, rpcLogger); // Setup the FunctionApp root path and module path. FunctionLoader.SetupWellKnownPaths(functionLoadRequest, managedDependenciesPath); // Create the very first Runspace so the debugger has the target to attach to. // This PowerShell instance is shared by the first PowerShellManager instance created in the pool, // and the dependency manager (used to download dependent modules if needed). var pwsh = Utils.NewPwshInstance(); _powershellPool.Initialize(pwsh); // Start the download asynchronously if needed. _dependencyManager.StartDependencyInstallationIfNeeded(request, pwsh, rpcLogger); } catch (Exception e) { // Failure that happens during this step is terminating and we will need to return a failure response to // all subsequent 'FunctionLoadRequest'. Cache the exception so we can reuse it in future calls. _initTerminatingError = e; status.Status = StatusResult.Types.Status.Failure; status.Exception = e.ToRpcException(); return(response); } } try { // Load the metadata of the function. FunctionLoader.LoadFunction(functionLoadRequest); } catch (Exception e) { status.Status = StatusResult.Types.Status.Failure; status.Exception = e.ToRpcException(); } return(response); }
internal RequestProcessor(MessagingStream msgStream) { _msgStream = msgStream; _powershellPool = new PowerShellManagerPool(msgStream); _functionLoader = new FunctionLoader(); }
/// <summary> /// Method to process a FunctionLoadRequest. /// FunctionLoadRequest should be processed sequentially. There is no point to process FunctionLoadRequest /// concurrently as a FunctionApp doesn't include a lot functions in general. Having this step sequential /// will make the Runspace-level initialization easier and more predictable. /// </summary> internal StreamingMessage ProcessFunctionLoadRequest(StreamingMessage request) { FunctionLoadRequest functionLoadRequest = request.FunctionLoadRequest; StreamingMessage response = NewStreamingMessageTemplate( request.RequestId, StreamingMessage.ContentOneofCase.FunctionLoadResponse, out StatusResult status); response.FunctionLoadResponse.FunctionId = functionLoadRequest.FunctionId; // When a functionLoadRequest comes in, we check to see if a dependency download has failed in a previous call // or if PowerShell could not be initialized. If this is the case, mark this as a failed request // and submit the exception to the Host (runtime). if (_initTerminatingError != null) { status.Status = StatusResult.Types.Status.Failure; status.Exception = _initTerminatingError.ToRpcException(); return(response); } // Ideally, the initialization should happen when processing 'WorkerInitRequest', however, the 'WorkerInitRequest' // message doesn't provide information about the FunctionApp. That information is not available until the first // 'FunctionLoadRequest' comes in. Therefore, we run initialization here. if (!_isFunctionAppInitialized) { try { _isFunctionAppInitialized = true; _dependencyManager.Initialize(functionLoadRequest); // Setup the FunctionApp root path and module path. FunctionLoader.SetupWellKnownPaths(functionLoadRequest); // Create the very first Runspace so the debugger has the target to attach to. // This PowerShell instance is shared by the first PowerShellManager instance created in the pool, // and the dependency manager (used to download dependent modules if needed). var pwsh = Utils.NewPwshInstance(); _powershellPool.Initialize(pwsh); // Start the download asynchronously if needed. _dependencyManager.ProcessDependencyDownload(_msgStream, request, pwsh); } catch (Exception e) { // Failure that happens during this step is terminating and we will need to return a failure response to // all subsequent 'FunctionLoadRequest'. Cache the exception so we can reuse it in future calls. _initTerminatingError = e; status.Status = StatusResult.Types.Status.Failure; status.Exception = e.ToRpcException(); return(response); } } try { _functionLoader.LoadFunction(functionLoadRequest); } catch (Exception e) { status.Status = StatusResult.Types.Status.Failure; status.Exception = e.ToRpcException(); } return(response); }