internal StreamingMessage ProcessWorkerInitRequest(StreamingMessage request)
        {
            StreamingMessage response = NewStreamingMessageTemplate(
                request.RequestId,
                StreamingMessage.ContentOneofCase.WorkerInitResponse,
                out StatusResult status);

            try
            {
                _powerShellManager.InitializeRunspace();
            }
            catch (Exception e)
            {
                status.Status    = StatusResult.Types.Status.Failure;
                status.Exception = e.ToRpcException();
            }

            return(response);
        }
Beispiel #2
0
        internal void InvocationRequestHandler(StreamingMessage request)
        {
            StreamingMessage response = NewStreamingMessageTemplate(
                request.RequestId,
                StreamingMessage.ContentOneofCase.InvocationResponse,
                out StatusResult status);

            response.InvocationResponse.InvocationId = request.InvocationRequest.InvocationId;
            RpcHttp rpcHttp = new RpcHttp()
            {
                StatusCode = "200"
            };
            TypedData typedData = new TypedData();

            typedData.Http = rpcHttp;
            response.InvocationResponse.ReturnValue = typedData;

            _blockingCollectionQueue.Add(response);
        }
Beispiel #3
0
        /// <summary>
        /// Checkout an idle PowerShellManager instance in a non-blocking asynchronous way.
        /// </summary>
        internal PowerShellManager CheckoutIdleWorker(StreamingMessage request, AzFunctionInfo functionInfo)
        {
            PowerShellManager psManager    = null;
            string            requestId    = request.RequestId;
            string            invocationId = request.InvocationRequest?.InvocationId;

            // If the pool has an idle one, just use it.
            if (!_pool.TryTake(out psManager))
            {
                // The pool doesn't have an idle one.
                if (_poolSize < _upperBound)
                {
                    int id = Interlocked.Increment(ref _poolSize);
                    if (id <= _upperBound)
                    {
                        // If the pool hasn't reached its bounded capacity yet, then
                        // we create a new item and return it.
                        var logger = new RpcLogger(_msgStream);
                        logger.SetContext(requestId, invocationId);
                        psManager = new PowerShellManager(logger, id);

                        RpcLogger.WriteSystemLog(string.Format(PowerShellWorkerStrings.LogNewPowerShellManagerCreated, id.ToString()));
                    }
                }

                if (psManager == null)
                {
                    // If the pool has reached its bounded capacity, then the thread
                    // should be blocked until an idle one becomes available.
                    psManager = _pool.Take();
                }
            }

            // Finish the initialization if not yet.
            // This applies only to the very first PowerShellManager instance, whose initialization was deferred.
            psManager.Initialize();

            // Register the function with the Runspace before returning the idle PowerShellManager.
            FunctionMetadata.RegisterFunctionMetadata(psManager.InstanceId, functionInfo);
            psManager.Logger.SetContext(requestId, invocationId);

            return(psManager);
        }
        /// <summary>
        /// Method to process a InvocationRequest.
        /// This method checks out a worker from the pool, and then starts the actual invocation in a threadpool thread.
        /// </summary>
        internal StreamingMessage ProcessInvocationRequest(StreamingMessage request)
        {
            try
            {
                var stopwatch = new FunctionInvocationPerformanceStopwatch();
                stopwatch.OnStart();

                // Will block if installing dependencies is required
                _dependencyManager.WaitForDependenciesAvailability(
                    () =>
                {
                    var rpcLogger = new RpcLogger(_msgStream);
                    rpcLogger.SetContext(request.RequestId, request.InvocationRequest?.InvocationId);
                    return(rpcLogger);
                });

                stopwatch.OnCheckpoint(FunctionInvocationPerformanceStopwatch.Checkpoint.DependenciesAvailable);

                AzFunctionInfo    functionInfo = FunctionLoader.GetFunctionInfo(request.InvocationRequest.FunctionId);
                PowerShellManager psManager    = _powershellPool.CheckoutIdleWorker(request, functionInfo);
                stopwatch.OnCheckpoint(FunctionInvocationPerformanceStopwatch.Checkpoint.RunspaceAvailable);

                // When the concurrency upper bound is more than 1, we have to handle the invocation in a worker
                // thread, so multiple invocations can make progress at the same time, even though by time-sharing.
                Task.Run(() => ProcessInvocationRequestImpl(request, functionInfo, psManager, stopwatch));
            }
            catch (Exception e)
            {
                StreamingMessage response = NewStreamingMessageTemplate(
                    request.RequestId,
                    StreamingMessage.ContentOneofCase.InvocationResponse,
                    out StatusResult status);

                response.InvocationResponse.InvocationId = request.InvocationRequest.InvocationId;
                status.Status    = StatusResult.Types.Status.Failure;
                status.Exception = e.ToRpcException();

                return(response);
            }

            return(null);
        }
Beispiel #5
0
        internal StreamingMessage ProcessWorkerInitRequest(StreamingMessage request)
        {
            StreamingMessage response = NewStreamingMessageTemplate(
                request.RequestId,
                StreamingMessage.ContentOneofCase.WorkerInitResponse,
                out StatusResult status);

            // If the environment variable is set, spin up the custom named pipe server.
            // This is typically used for debugging. It will throw a friendly exception if the
            // pipe name is not a valid pipename.
            string pipeName = Environment.GetEnvironmentVariable("PSWorkerCustomPipeName");

            if (!string.IsNullOrEmpty(pipeName))
            {
                RpcLogger.WriteSystemLog(LogLevel.Trace, string.Format(PowerShellWorkerStrings.SpecifiedCustomPipeName, pipeName));
                RemoteSessionNamedPipeServer.CreateCustomNamedPipeServer(pipeName);
            }

            return(response);
        }
Beispiel #6
0
        public async Task WriteAsync(StreamingMessage message)
        {
            if (isDisposed)
            {
                return;
            }

            // Wait for the handle to be released because we can't have
            // more than one message being sent at the same time
            await _writeSemaphore.WaitAsync();

            try
            {
                await _call.RequestStream.WriteAsync(message);
            }
            finally
            {
                _writeSemaphore.Release();
            }
        }
Beispiel #7
0
        public void SendWorkerInitRequest_PublishesOutboundEvent_V2Compatable()
        {
            _testEnvironment.SetEnvironmentVariable(EnvironmentSettingNames.FunctionsV2CompatibilityModeKey, "true");
            StartStream startStream = new StartStream()
            {
                WorkerId = _workerId
            };
            StreamingMessage startStreamMessage = new StreamingMessage()
            {
                StartStream = startStream
            };
            RpcEvent rpcEvent = new RpcEvent(_workerId, startStreamMessage);

            _workerChannel.SendWorkerInitRequest(rpcEvent);
            _testFunctionRpcService.PublishWorkerInitResponseEvent();
            var traces = _logger.GetLogMessages();

            Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, _expectedLogMsg)));
            Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, "Worker and host running in V2 compatibility mode")));
        }
Beispiel #8
0
        public async Task <WorkerStatus> GetWorkerStatusAsync()
        {
            var workerStatus = new WorkerStatus();

            if (!string.IsNullOrEmpty(_workerCapabilities.GetCapabilityState(RpcWorkerConstants.WorkerStatus)))
            {
                // get the worker's current status
                // this will include the OOP worker's channel latency in the request, which can be used upstream
                // to make scale decisions
                var message = new StreamingMessage
                {
                    RequestId           = Guid.NewGuid().ToString(),
                    WorkerStatusRequest = new WorkerStatusRequest()
                };

                var sw  = Stopwatch.StartNew();
                var tcs = new TaskCompletionSource <bool>();
                if (_workerStatusRequests.TryAdd(message.RequestId, tcs))
                {
                    SendStreamingMessage(message);
                    await tcs.Task;
                    sw.Stop();
                    workerStatus.Latency = sw.Elapsed;
                    _workerChannelLogger.LogDebug($"[HostMonitor] Worker status request took {sw.ElapsedMilliseconds}ms");
                }
            }

            // get the process stats for the worker
            var workerProcessStats = _rpcWorkerProcess.GetStats();

            workerStatus.ProcessStats = workerProcessStats;

            if (workerProcessStats.CpuLoadHistory.Any())
            {
                string formattedLoadHistory   = string.Join(",", workerProcessStats.CpuLoadHistory);
                int    executingFunctionCount = FunctionInputBuffers.Sum(p => p.Value.Count);
                _workerChannelLogger.LogDebug($"[HostMonitor] Worker process stats: EffectiveCores={_environment.GetEffectiveCoresCount()}, ProcessId={_rpcWorkerProcess.Id}, ExecutingFunctions={executingFunctionCount}, CpuLoadHistory=({formattedLoadHistory}), AvgLoad={workerProcessStats.CpuLoadHistory.Average()}, MaxLoad={workerProcessStats.CpuLoadHistory.Max()}");
            }

            return(workerStatus);
        }
        /// <summary>
        /// Create an object of 'StreamingMessage' as a template, for further update.
        /// </summary>
        private StreamingMessage NewStreamingMessageTemplate(string requestId, StreamingMessage.ContentOneofCase msgType, out StatusResult status)
        {
            // Assume success. The state of the status object can be changed in the caller.
            status = new StatusResult()
            {
                Status = StatusResult.Types.Status.Success
            };
            var response = new StreamingMessage()
            {
                RequestId = requestId
            };

            switch (msgType)
            {
            case StreamingMessage.ContentOneofCase.WorkerInitResponse:
                response.WorkerInitResponse = new WorkerInitResponse()
                {
                    Result = status
                };
                break;

            case StreamingMessage.ContentOneofCase.FunctionLoadResponse:
                response.FunctionLoadResponse = new FunctionLoadResponse()
                {
                    Result = status
                };
                break;

            case StreamingMessage.ContentOneofCase.InvocationResponse:
                response.InvocationResponse = new InvocationResponse()
                {
                    Result = status
                };
                break;

            default:
                throw new InvalidOperationException("Unreachable code.");
            }

            return(response);
        }
Beispiel #10
0
        private async Task ProcessRequestCoreAsync(StreamingMessage request)
        {
            StreamingMessage responseMessage = new StreamingMessage
            {
                RequestId = request.RequestId
            };

            if (request.ContentCase == MsgType.InvocationRequest)
            {
                responseMessage.InvocationResponse = await InvocationRequestHandlerAsync(request.InvocationRequest, _application,
                                                                                         _invocationFeaturesFactory, _serializer, _outputBindingsInfoProvider);
            }
            else if (request.ContentCase == MsgType.WorkerInitRequest)
            {
                responseMessage.WorkerInitResponse = WorkerInitRequestHandler(request.WorkerInitRequest);
            }
            else if (request.ContentCase == MsgType.FunctionLoadRequest)
            {
                responseMessage.FunctionLoadResponse = FunctionLoadRequestHandler(request.FunctionLoadRequest, _application, _methodInfoLocator);
            }
            else if (request.ContentCase == MsgType.FunctionEnvironmentReloadRequest)
            {
                // No-op for now, but return a response.
                responseMessage.FunctionEnvironmentReloadResponse = new FunctionEnvironmentReloadResponse
                {
                    Result = new StatusResult {
                        Status = StatusResult.Types.Status.Success
                    }
                };
            }
            else
            {
                // TODO: Trace failure here.
                return;
            }

            await _outputWriter.WriteAsync(responseMessage);
        }
Beispiel #11
0
        public void Log(LogLevel logLevel, string message, Exception exception = null, bool isUserLog = false)
        {
            if (isUserLog)
            {
                // For user logs, we send them over Rpc with details about the invocation.
                var logMessage = new StreamingMessage
                {
                    RequestId = _requestId,
                    RpcLog    = new RpcLog()
                    {
                        Exception    = exception?.ToRpcException(),
                        InvocationId = _invocationId ?? "N/A",
                        Level        = logLevel,
                        Message      = message
                    }
                };

                _msgStream.Write(logMessage);
            }
            else
            {
                // For system logs, we log to stdio with a prefix of LanguageWorkerConsoleLog.
                // These are picked up by the Functions Host
                _systemLogMsg.Append(SystemLogPrefix).AppendLine("System Log: {");
                if (!string.IsNullOrEmpty(_requestId))
                {
                    _systemLogMsg.AppendLine($"  Request-Id: {_requestId}");
                }
                if (!string.IsNullOrEmpty(_invocationId))
                {
                    _systemLogMsg.AppendLine($"  Invocation-Id: {_invocationId}");
                }
                _systemLogMsg.AppendLine($"  Log-Message: {message}").AppendLine("}");

                Console.WriteLine(_systemLogMsg.ToString());
                _systemLogMsg.Clear();
            }
        }
Beispiel #12
0
        public async Task <bool> RpcStream()
        {
            StartStream str = new StartStream()
            {
                WorkerId = _workerId
            };
            StreamingMessage startStream = new StreamingMessage()
            {
                StartStream = str
            };
            await _call.RequestStream.WriteAsync(startStream);

            var consumer = Task.Run(async() =>
            {
                foreach (var rpcWriteMsg in _blockingCollectionQueue.GetConsumingEnumerable())
                {
                    await _call.RequestStream.WriteAsync(rpcWriteMsg);
                }
            });
            await consumer;

            return(true);
        }
Beispiel #13
0
        public void SharedMemoryDataTransferSetting_VerifyDisabledIfWorkerCapabilityAbsent()
        {
            // Enable shared memory data transfer in the environment
            _testEnvironment.SetEnvironmentVariable(RpcWorkerConstants.FunctionsWorkerSharedMemoryDataTransferEnabledSettingName, "1");

            StartStream startStream = new StartStream()
            {
                WorkerId = _workerId
            };

            StreamingMessage startStreamMessage = new StreamingMessage()
            {
                StartStream = startStream
            };

            // Send worker init request and enable the capabilities
            GrpcEvent rpcEvent = new GrpcEvent(_workerId, startStreamMessage);

            _workerChannel.SendWorkerInitRequest(rpcEvent);
            _testFunctionRpcService.PublishWorkerInitResponseEvent();

            Assert.False(_workerChannel.IsSharedMemoryDataTransferEnabled());
        }
Beispiel #14
0
        public void Log(LogLevel logLevel, string message, Exception exception = null, bool isUserLog = false)
        {
            if (isUserLog)
            {
                // For user logs, we send them over Rpc with details about the invocation.
                var logMessage = new StreamingMessage
                {
                    RequestId = _requestId,
                    RpcLog    = new RpcLog()
                    {
                        Exception    = exception?.ToRpcException(),
                        InvocationId = _invocationId ?? "N/A",
                        Level        = logLevel,
                        Message      = message
                    }
                };

                _msgStream.Write(logMessage);
            }
            else
            {
                WriteSystemLog(message, _systemLogMsg, _requestId, _invocationId);
            }
        }
Beispiel #15
0
        public void PublishWorkerInitResponseEvent(IDictionary <string, string> capabilities = null)
        {
            StatusResult statusResult = new StatusResult()
            {
                Status = StatusResult.Types.Status.Success
            };

            WorkerInitResponse initResponse = new WorkerInitResponse()
            {
                Result = statusResult
            };

            if (capabilities != null)
            {
                initResponse.Capabilities.Add(capabilities);
            }

            StreamingMessage responseMessage = new StreamingMessage()
            {
                WorkerInitResponse = initResponse
            };

            _eventManager.Publish(new InboundGrpcEvent(_workerId, responseMessage));
        }
        /// <summary>
        /// Method to process a InvocationRequest.
        /// InvocationRequest should be processed in parallel eventually.
        /// </summary>
        internal StreamingMessage ProcessInvocationRequest(StreamingMessage request)
        {
            InvocationRequest invocationRequest = request.InvocationRequest;

            StreamingMessage response = NewStreamingMessageTemplate(
                request.RequestId,
                StreamingMessage.ContentOneofCase.InvocationResponse,
                out StatusResult status);

            response.InvocationResponse.InvocationId = invocationRequest.InvocationId;

            // Invoke powershell logic and return hashtable of out binding data
            try
            {
                // Load information about the function
                var functionInfo = _functionLoader.GetFunctionInfo(invocationRequest.FunctionId);
                _powerShellManager.RegisterFunctionMetadata(functionInfo);

                Hashtable results = functionInfo.Type == AzFunctionType.OrchestrationFunction
                    ? InvokeOrchestrationFunction(functionInfo, invocationRequest)
                    : InvokeSingleActivityFunction(functionInfo, invocationRequest);

                BindOutputFromResult(response.InvocationResponse, functionInfo, results);
            }
            catch (Exception e)
            {
                status.Status    = StatusResult.Types.Status.Failure;
                status.Exception = e.ToRpcException();
            }
            finally
            {
                _powerShellManager.UnregisterFunctionMetadata();
            }

            return(response);
        }
        /// <summary>
        /// Entry point of the language worker.
        /// </summary>
        public async static Task Main(string[] args)
        {
            RpcLogger.WriteSystemLog(
                LogLevel.Information,
                string.Format(PowerShellWorkerStrings.PowerShellWorkerVersion, typeof(Worker).Assembly.GetName().Version));

            WorkerArguments arguments = null;

            Parser.Default.ParseArguments <WorkerArguments>(args)
            .WithParsed(ops => arguments = ops)
            .WithNotParsed(err => Environment.Exit(1));

            // 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 firstPowerShellInstance = Utils.NewPwshInstance();

            LogPowerShellVersion(firstPowerShellInstance);
            WarmUpPowerShell(firstPowerShellInstance);

            var msgStream        = new MessagingStream(arguments.Host, arguments.Port);
            var requestProcessor = new RequestProcessor(msgStream, firstPowerShellInstance);

            // Send StartStream message
            var startedMessage = new StreamingMessage()
            {
                RequestId   = arguments.RequestId,
                StartStream = new StartStream()
                {
                    WorkerId = arguments.WorkerId
                }
            };

            msgStream.Write(startedMessage);
            await requestProcessor.ProcessRequestLoop();
        }
        /// <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);
        }
Beispiel #19
0
        /// <summary>
        /// Entry point of the language worker.
        /// </summary>
        public async static Task Main(string[] args)
        {
            WorkerArguments arguments = null;

            Parser.Default.ParseArguments <WorkerArguments>(args)
            .WithParsed(ops => arguments = ops)
            .WithNotParsed(err => Environment.Exit(1));

            var msgStream        = new MessagingStream(arguments.Host, arguments.Port);
            var requestProcessor = new RequestProcessor(msgStream);

            // Send StartStream message
            var startedMessage = new StreamingMessage()
            {
                RequestId   = arguments.RequestId,
                StartStream = new StartStream()
                {
                    WorkerId = arguments.WorkerId
                }
            };

            msgStream.Write(startedMessage);
            await requestProcessor.ProcessRequestLoop();
        }
Beispiel #20
0
        /// <summary>
        /// Processes the dependency download request
        /// </summary>
        /// <param name="msgStream">The protobuf messaging stream</param>
        /// <param name="request">The StreamingMessage request for function load</param>
        /// <param name="pwsh">The PowerShell instance used to download modules</param>
        internal void ProcessDependencyDownload(MessagingStream msgStream, StreamingMessage request, PowerShell pwsh)
        {
            if (request.FunctionLoadRequest.ManagedDependencyEnabled)
            {
                var rpcLogger = new RpcLogger(msgStream);
                rpcLogger.SetContext(request.RequestId, null);
                if (Dependencies.Count == 0)
                {
                    // If there are no dependencies to install, log and return.
                    rpcLogger.Log(LogLevel.Trace, PowerShellWorkerStrings.FunctionAppDoesNotHaveDependentModulesToInstall, isUserLog: true);
                    return;
                }

                if (!_shouldUpdateFunctionAppDependencies)
                {
                    // The function app already has the latest dependencies installed.
                    rpcLogger.Log(LogLevel.Trace, PowerShellWorkerStrings.LatestFunctionAppDependenciesAlreadyInstalled, isUserLog: true);
                    return;
                }

                //Start dependency download on a separate thread
                _dependencyDownloadTask = Task.Run(() => InstallFunctionAppDependencies(pwsh, rpcLogger));
            }
        }
Beispiel #21
0
 internal StreamingMessage ProcessWorkerTerminateRequest(StreamingMessage request)
 {
     return(null);
 }
 private void Send(StreamingMessage msg)
 {
     _eventManager.Publish(new OutboundEvent(_workerId, msg));
 }
        /// <summary>
        /// Method to process a InvocationRequest.
        /// This method checks out a worker from the pool, and then starts the actual invocation in a threadpool thread.
        /// </summary>
        internal StreamingMessage ProcessInvocationRequest(StreamingMessage request)
        {
            AzFunctionInfo    functionInfo = null;
            PowerShellManager psManager    = null;

            try
            {
                if (_dependencyManager.DependencyDownloadTask != null &&
                    (_dependencyManager.DependencyDownloadTask.Status != TaskStatus.Canceled ||
                     _dependencyManager.DependencyDownloadTask.Status != TaskStatus.Faulted ||
                     _dependencyManager.DependencyDownloadTask.Status != TaskStatus.RanToCompletion))
                {
                    var rpcLogger = new RpcLogger(_msgStream);
                    rpcLogger.SetContext(request.RequestId, request.InvocationRequest?.InvocationId);
                    rpcLogger.Log(LogLevel.Information, PowerShellWorkerStrings.DependencyDownloadInProgress, isUserLog: true);
                    _dependencyManager.WaitOnDependencyDownload();
                }

                if (_dependencyManager.DependencyError != null)
                {
                    StreamingMessage response = NewStreamingMessageTemplate(request.RequestId,
                                                                            StreamingMessage.ContentOneofCase.InvocationResponse,
                                                                            out StatusResult status);
                    status.Status    = StatusResult.Types.Status.Failure;
                    status.Exception = _dependencyManager.DependencyError.ToRpcException();
                    response.InvocationResponse.InvocationId = request.InvocationRequest.InvocationId;
                    return(response);
                }

                functionInfo = _functionLoader.GetFunctionInfo(request.InvocationRequest.FunctionId);
                psManager    = _powershellPool.CheckoutIdleWorker(request, functionInfo);

                if (_powershellPool.UpperBound == 1)
                {
                    // When the concurrency upper bound is 1, we can handle only one invocation at a time anyways,
                    // so it's better to just do it on the current thread to reduce the required synchronization.
                    ProcessInvocationRequestImpl(request, functionInfo, psManager);
                }
                else
                {
                    // When the concurrency upper bound is more than 1, we have to handle the invocation in a worker
                    // thread, so multiple invocations can make progress at the same time, even though by time-sharing.
                    Task.Run(() => ProcessInvocationRequestImpl(request, functionInfo, psManager));
                }
            }
            catch (Exception e)
            {
                _powershellPool.ReclaimUsedWorker(psManager);

                StreamingMessage response = NewStreamingMessageTemplate(
                    request.RequestId,
                    StreamingMessage.ContentOneofCase.InvocationResponse,
                    out StatusResult status);

                response.InvocationResponse.InvocationId = request.InvocationRequest.InvocationId;
                status.Status    = StatusResult.Types.Status.Failure;
                status.Exception = e.ToRpcException();

                return(response);
            }

            return(null);
        }
Beispiel #24
0
 internal StreamingMessage ProcessFileChangeEventRequest(StreamingMessage request)
 {
     return(null);
 }
Beispiel #25
0
        private static async Task HandleInvocationRequest(InvocationRequest invokeRequest)
        {
            var status = new StatusResult()
            {
                Status = StatusResult.Types.Status.Success
            };
            var response = new StreamingMessage()
            {
                RequestId          = s_requestId,
                InvocationResponse = new InvocationResponse()
                {
                    InvocationId = invokeRequest.InvocationId,
                    Result       = status
                }
            };

            var metadata = s_loadedFunctions[invokeRequest.FunctionId];

            // Not exactly sure what to do with bindings yet, so only handles 'httpTrigger-in' + 'http-out'
            string outHttpName = null;

            foreach (var binding in metadata.Bindings)
            {
                if (binding.Value.Direction == BindingInfo.Types.Direction.In)
                {
                    continue;
                }

                if (binding.Value.Type == "http")
                {
                    outHttpName = binding.Key;
                    break;
                }
            }

            if (outHttpName == null)
            {
                status.Status = StatusResult.Types.Status.Failure;
                status.Result = "PowerShell worker only handles http out binding for now.";
            }
            else
            {
                object argument = null;
                foreach (var input in invokeRequest.InputData)
                {
                    if (input.Data != null && input.Data.Http != null)
                    {
                        argument = input.Data.Http.Query.GetValueOrDefault("name", "Azure Functions");
                    }
                }

                s_ps.AddCommand(metadata.ScriptFile);
                if (argument != null)
                {
                    s_ps.AddArgument(argument);
                }

                TypedData retValue;
                try
                {
                    var results = s_ps.Invoke <string>();
                    retValue = new TypedData()
                    {
                        String = String.Join(',', results)
                    };
                }
                finally
                {
                    s_ps.Commands.Clear();
                }

                // This is just mimic what nodejs worker does
                var paramBinding = new ParameterBinding()
                {
                    Name = outHttpName,
                    Data = new TypedData()
                    {
                        Http = new RpcHttp()
                        {
                            StatusCode = "200",
                            Body       = retValue
                        }
                    }
                };

                // Not exactly sure which one to use for what scenario, so just set both.
                response.InvocationResponse.OutputData.Add(paramBinding);
                response.InvocationResponse.ReturnValue = retValue;
            }

            await s_call.RequestStream.WriteAsync(response);
        }
Beispiel #26
0
 internal StreamingMessage ProcessInvocationCancelRequest(StreamingMessage request)
 {
     return(null);
 }
Beispiel #27
0
        /// <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);
        }
Beispiel #28
0
 /// <summary>
 /// Write the outgoing message.
 /// </summary>
 internal void Write(StreamingMessage message) => WriteImplAsync(message).ConfigureAwait(false);
 public OutboundEvent(string workerId, StreamingMessage message) : base(workerId, message, MessageOrigin.Host)
 {
 }
Beispiel #30
0
 public Task ProcessRequestAsync(StreamingMessage request)
 {
     // Dispatch and return.
     Task.Run(() => ProcessRequestCoreAsync(request));
     return(Task.CompletedTask);
 }