예제 #1
0
        protected RpcServerBase(
            IRpcServicePublisher servicePublisher, IRpcServiceActivator serviceActivator,
            IRpcServiceDefinitionsProvider definitionsProvider, IRpcServerOptions?options,
            ILoggerFactory?loggerFactory)
        {
            this.ServicePublisher           = servicePublisher ?? throw new ArgumentNullException(nameof(servicePublisher));
            this.ServiceActivator           = serviceActivator ?? throw new ArgumentNullException(nameof(serviceActivator));
            this.ServiceDefinitionsProvider = definitionsProvider ?? throw new ArgumentNullException(nameof(definitionsProvider));
            this.LoggerFactory = loggerFactory;
            this.Logger        = loggerFactory?.CreateLogger(this.GetType()) ?? RpcLogger.CreateLogger(this.GetType());

            if (options != null)
            {
                this.serializer       = options.Serializer;
                this.AllowDiscovery   = options.AllowDiscovery ?? true;
                this.AllowAutoPublish = options.AllowAutoPublish ?? false;

                this.CallInterceptors    = options.Interceptors.ToImmutableArrayList();
                this.ExceptionConverters = options.ExceptionConverters.ToImmutableArrayList();
            }

            if (this.ExceptionConverters.Count > 0)
            {
                this.CustomFaultHandler = new RpcServerFaultHandler(null, this.ExceptionConverters, null);
            }
            else
            {
                this.CustomFaultHandler = RpcServerFaultHandler.Default;
            }
        }
        /// <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 (!_shouldUpdateFunctionAppDependencies)
                {
                    if (!string.IsNullOrEmpty(_dependenciesNotUpdatedMessage))
                    {
                        // We were not able to update the function app dependencies.
                        // However, there is a previous installation, so continue with the function app execution.
                        rpcLogger.Log(LogLevel.Warning, _dependenciesNotUpdatedMessage, isUserLog: true);
                    }
                    else
                    {
                        // The function app already has the latest dependencies installed.
                        rpcLogger.Log(LogLevel.Trace, PowerShellWorkerStrings.LatestFunctionAppDependenciesAlreadyInstalled, isUserLog: true);
                    }

                    return;
                }

                if (Dependencies.Count == 0)
                {
                    // If there are no dependencies to install, log and return.
                    rpcLogger.Log(LogLevel.Trace, PowerShellWorkerStrings.FunctionAppDoesNotHaveDependentModulesToInstall, isUserLog: true);
                    return;
                }

                //Start dependency download on a separate thread
                _dependencyDownloadTask = Task.Run(() => InstallFunctionAppDependencies(pwsh, rpcLogger));
            }
        }
 internal RequestProcessor(MessagingStream msgStream)
 {
     _msgStream         = msgStream;
     _logger            = new RpcLogger(msgStream);
     _powerShellManager = new PowerShellManager(_logger);
     _functionLoader    = new FunctionLoader();
 }
예제 #4
0
        /// <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));

            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();
        }
        /// <summary>
        /// This method invokes the FunctionApp's profile.ps1.
        /// </summary>
        internal void InvokeProfile(string profilePath)
        {
            Exception exception = null;

            if (profilePath == null)
            {
                RpcLogger.WriteSystemLog(string.Format(PowerShellWorkerStrings.FileNotFound, "profile.ps1", FunctionLoader.FunctionAppRootPath));
                return;
            }

            try
            {
                // Import-Module on a .ps1 file will evaluate the script in the global scope.
                _pwsh.AddCommand(Utils.ImportModuleCmdletInfo)
                .AddParameter("Name", profilePath)
                .AddParameter("PassThru", true)
                .AddCommand(Utils.RemoveModuleCmdletInfo)
                .AddParameter("Force", true)
                .AddParameter("ErrorAction", "SilentlyContinue")
                .InvokeAndClearCommands();
            }
            catch (Exception e)
            {
                exception = e;
                throw;
            }
            finally
            {
                if (_pwsh.HadErrors)
                {
                    string errorMsg = string.Format(PowerShellWorkerStrings.FailToRunProfile, profilePath);
                    _logger.Log(LogLevel.Error, errorMsg, exception, isUserLog: true);
                }
            }
        }
예제 #6
0
        private RpcLogger CreateLoggerWithContext(string requestId, string invocationId)
        {
            var logger = new RpcLogger(_msgStream);

            logger.SetContext(requestId, invocationId);
            return(logger);
        }
예제 #7
0
        protected override async Task <RpcResponseMessage> SendAsync(RpcRequestMessage request,
                                                                     string route = null,
                                                                     CancellationToken cancellationToken = default(CancellationToken))
        {
            var logger = new RpcLogger(_log);

            try
            {
                lock (_lockingObject)
                {
                    var rpcRequestJson = JsonConvert.SerializeObject(request, JsonSerializerSettings);
                    var requestBytes   = Encoding.UTF8.GetBytes(rpcRequestJson);
                    logger.LogRequest(rpcRequestJson);
                    var client = GetSocket();
                    client.SendBufferSize = requestBytes.Length;
                    var val = client.SendAsync(new ArraySegment <byte>(requestBytes, 0, requestBytes.Length), SocketFlags.None).Result;
                    using (var memoryStream = ReceiveFullResponse(client))
                    {
                        memoryStream.Position = 0;
                        using (var streamReader = new StreamReader(memoryStream))
                            using (var reader = new JsonTextReader(streamReader))
                            {
                                var serializer = JsonSerializer.Create(JsonSerializerSettings);
                                var message    = serializer.Deserialize <RpcResponseMessage>(reader);
                                logger.LogResponse(message);
                                return(message);
                            }
                    }
                }
            } catch (Exception ex) {
                logger.LogException(ex);
                throw new RpcClientUnknownException("Error occurred when trying to send ipc requests(s)", ex);
            }
        }
예제 #8
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 &&
                    Interlocked.Increment(ref _poolSize) <= _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);
                }
                else
                {
                    // If the pool has reached its bounded capacity, then the thread
                    // should be blocked until an idle one becomes available.
                    psManager = _pool.Take();
                }
            }

            // Register the function with the Runspace before returning the idle PowerShellManager.
            FunctionMetadata.RegisterFunctionMetadata(psManager.InstanceId, functionInfo);
            psManager.Logger.SetContext(requestId, invocationId);
            return(psManager);
        }
예제 #9
0
        protected override Task <RpcResponseMessage> SendAsync(RpcRequestMessage request, string route = null)
        {
            var logger = new RpcLogger(_log);

            try
            {
                lock (_lockingObject)
                {
                    var rpcRequestJson = JsonConvert.SerializeObject(request, JsonSerializerSettings);
                    var requestBytes   = Encoding.UTF8.GetBytes(rpcRequestJson);
                    logger.LogRequest(rpcRequestJson);
                    GetPipeClient().Write(requestBytes, 0, requestBytes.Length);

                    using (var memoryData = ReceiveFullResponse(GetPipeClient()))
                    {
                        memoryData.Position = 0;
                        using (StreamReader streamReader = new StreamReader(memoryData))
                            using (JsonTextReader reader = new JsonTextReader(streamReader))
                            {
                                var serializer = JsonSerializer.Create(JsonSerializerSettings);
                                var message    = serializer.Deserialize <RpcResponseMessage>(reader);
                                logger.LogResponse(message);
                                return(Task.FromResult(message));
                            }
                    }
                }
            }
            catch (Exception ex)
            {
                var exception = new RpcClientUnknownException("Error occurred when trying to send ipc requests(s)", ex);
                logger.LogException(exception);
                throw exception;
            }
        }
예제 #10
0
        internal StreamingMessage ProcessWorkerInitRequest(StreamingMessage request)
        {
            var workerInitRequest = request.WorkerInitRequest;

            Environment.SetEnvironmentVariable("AZUREPS_HOST_ENVIRONMENT", $"AzureFunctions/{workerInitRequest.HostVersion}");

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

            response.WorkerInitResponse.Capabilities.Add("RpcHttpBodyOnly", "true");

            // 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);
        }
예제 #11
0
        private async Task HandleIncomingMessagesAsync()
        {
            var logger = new RpcLogger(_log);

            while (!_cancellationTokenSource.IsCancellationRequested)
            {
                try
                {
                    if (_clientWebSocket != null && _clientWebSocket.State == WebSocketState.Open && AnyQueueRequests())
                    {
                        using (var memoryData = await ReceiveFullResponseAsync(_clientWebSocket).ConfigureAwait(false))
                        {
                            if (memoryData.Length == 0)
                            {
                                return;
                            }
                            memoryData.Position = 0;
                            using (var streamReader = new StreamReader(memoryData))
                                using (var reader = new JsonTextReader(streamReader))
                                {
                                    var serializer = JsonSerializer.Create(JsonSerializerSettings);
                                    var message    = serializer.Deserialize <RpcStreamingResponseMessage>(reader);
                                    HandleResponse(message);
                                    logger.LogResponse(message);
                                }
                        }
                    }
                }
                catch (Exception ex)
                {
                    HandleError(ex);
                    logger.LogException(ex);
                }
            }
        }
예제 #12
0
        protected async Task <TResponse> SendAsync <TRequest, TResponse>(TRequest request) where TResponse : RpcResponseMessage
        {
            var logger = new RpcLogger(_log);

            try
            {
                //lock (_lockingObject)
                // {
                var rpcRequestJson = JsonConvert.SerializeObject(request, JsonSerializerSettings);
                var requestBytes   = new ArraySegment <byte>(Encoding.UTF8.GetBytes(rpcRequestJson));
                logger.LogRequest(rpcRequestJson);
                //add timeout;
                var webSocket = GetClientWebSocket();
                await webSocket.SendAsync(requestBytes, WebSocketMessageType.Text, true, CancellationToken.None).ConfigureAwait(false);

                using (var memoryData = await ReceiveFullResponseAsync(webSocket))
                {
                    memoryData.Position = 0;
                    using (StreamReader streamReader = new StreamReader(memoryData))
                        using (JsonTextReader reader = new JsonTextReader(streamReader))
                        {
                            var serializer = JsonSerializer.Create(JsonSerializerSettings);
                            var message    = serializer.Deserialize <TResponse>(reader);
                            logger.LogResponse(message);
                            return(message);
                        }
                }
                //}
            }
            catch (Exception ex)
            {
                logger.LogException(ex);
                throw new RpcClientUnknownException("Error occurred when trying to send ipc requests(s)", ex);
            }
        }
예제 #13
0
        internal StreamingMessage ProcessFunctionEnvironmentReloadRequest(StreamingMessage request)
        {
            var stopwatch = new Stopwatch();

            stopwatch.Start();

            var environmentReloadRequest = request.FunctionEnvironmentReloadRequest;

            var rpcLogger = new RpcLogger(_msgStream);

            rpcLogger.SetContext(request.RequestId, null);

            var functionsEnvironmentReloader = new FunctionsEnvironmentReloader(rpcLogger);

            functionsEnvironmentReloader.ReloadEnvironment(
                environmentReloadRequest.EnvironmentVariables,
                environmentReloadRequest.FunctionAppDirectory);

            rpcLogger.Log(isUserOnlyLog: false, LogLevel.Trace, string.Format(PowerShellWorkerStrings.EnvironmentReloadCompleted, stopwatch.ElapsedMilliseconds));

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

            return(response);
        }
예제 #14
0
        public async Task SendRequestAsync(RpcRequestMessage request, IRpcStreamingResponseHandler requestResponseHandler, string route = null)
        {
            var logger = new RpcLogger(_log);

            try
            {
                await _semaphoreSlim.WaitAsync().ConfigureAwait(false);

                var rpcRequestJson = JsonConvert.SerializeObject(request, JsonSerializerSettings);
                var requestBytes   = new ArraySegment <byte>(Encoding.UTF8.GetBytes(rpcRequestJson));
                logger.LogRequest(rpcRequestJson);
                var timeoutCancellationTokenSource = new CancellationTokenSource();
                timeoutCancellationTokenSource.CancelAfter(ConnectionTimeout);

                var webSocket = _clientWebSocket;
                await webSocket.SendAsync(requestBytes, WebSocketMessageType.Text, true, timeoutCancellationTokenSource.Token)
                .ConfigureAwait(false);

                HandleRequest(request, requestResponseHandler);
            }
            catch (Exception ex)
            {
                logger.LogException(ex);
                throw new RpcClientUnknownException("Error occurred trying to send web socket requests(s)", ex);
            }
            finally
            {
                _semaphoreSlim.Release();
            }
        }
        /// <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)
        {
            Exception error = null;

            try
            {
                if (_dependencyManager.DependencyDownloadTask != null &&
                    !_dependencyManager.DependencyDownloadTask.IsCompleted)
                {
                    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)
                {
                    error = _dependencyManager.DependencyError;
                }
                else
                {
                    AzFunctionInfo    functionInfo = FunctionLoader.GetFunctionInfo(request.InvocationRequest.FunctionId);
                    PowerShellManager 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)
            {
                error = e;
            }

            if (error != null)
            {
                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 = error.ToRpcException();

                return(response);
            }

            return(null);
        }
예제 #16
0
        /// <summary>
        /// Populate the pool with the very first PowerShellManager instance.
        /// We instantiate PowerShellManager instances in a lazy way, starting from size 1 and increase the number of workers as needed.
        /// </summary>
        internal void Initialize(PowerShell pwsh)
        {
            var logger    = new RpcLogger(_msgStream);
            var psManager = new PowerShellManager(logger, pwsh);

            _pool.Add(psManager);
            _poolSize = 1;
        }
예제 #17
0
        private async Task HandleIncomingMessagesAsync()
        {
            var logger = new RpcLogger(_log);

            while (_cancellationTokenSource != null && !_cancellationTokenSource.IsCancellationRequested)
            {
                try
                {
                    await _semaphoreSlimListener.WaitAsync().ConfigureAwait(false);

                    if (_clientWebSocket != null && _clientWebSocket.State == WebSocketState.Open && AnyQueueRequests())
                    {
                        using (var memoryData = await ReceiveFullResponseAsync(_clientWebSocket).ConfigureAwait(false))
                        {
                            if (memoryData.Length == 0)
                            {
                                return;
                            }
                            memoryData.Position = 0;
                            using (var streamReader = new StreamReader(memoryData))
                                using (var reader = new JsonTextReader(streamReader))
                                {
                                    var serializer = JsonSerializer.Create(JsonSerializerSettings);
                                    var message    = serializer.Deserialize <RpcStreamingResponseMessage>(reader);
                                    //Random random = new Random();
                                    //var x = random.Next(0, 50);
                                    //if (x == 5) throw new Exception("Error");

                                    HandleResponse(message);
                                    logger.LogResponse(message);
                                }
                        }
                    }
                }
                catch (Exception ex)
                {
                    //HandleError should not be called from any of the functions that are called from this function
                    //which also rethrow the exception to avoid calling the error handlers twice for the same error.
                    //if cancellation requested ignore it as we will end up in a loop
                    if (!_cancellationTokenSource.IsCancellationRequested)
                    {
                        HandleError(ex);
                        logger.LogException(ex);
                    }
                }
                finally
                {
                    //release the semaphore for the listener so it can be disposed
                    _semaphoreSlimListener.Release();
                }
            }
        }
예제 #18
0
        /// <summary>
        /// Constructor of the pool.
        /// </summary>
        internal PowerShellManagerPool(MessagingStream msgStream)
        {
            string upperBound = Environment.GetEnvironmentVariable("PSWorkerInProcConcurrencyUpperBound");

            if (string.IsNullOrEmpty(upperBound) || !int.TryParse(upperBound, out _upperBound))
            {
                _upperBound = 1;
            }

            _msgStream = msgStream;
            _pool      = new BlockingCollection <PowerShellManager>(_upperBound);
            RpcLogger.WriteSystemLog(LogLevel.Information, string.Format(PowerShellWorkerStrings.LogConcurrencyUpperBound, _upperBound.ToString()));
        }
예제 #19
0
        /// <summary>
        /// Initialize the pool and populate it with PowerShellManager instances.
        /// We instantiate PowerShellManager instances in a lazy way, starting from size 1 and increase the number of workers as needed.
        /// </summary>
        internal void Initialize(string requestId)
        {
            var logger = new RpcLogger(_msgStream);

            try
            {
                logger.SetContext(requestId, invocationId: null);
                _pool.Add(new PowerShellManager(logger));
                _poolSize = 1;
            }
            finally
            {
                logger.ResetContext();
            }
        }
예제 #20
0
        /// <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.RequestId,
                    request.InvocationRequest?.InvocationId,
                    functionInfo.FuncName,
                    functionInfo.OutputBindings);

                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);
        }
예제 #21
0
        protected override async Task <RpcResponseMessage> SendAsync(RpcRequestMessage request, string route = null)
        {
            RpcLogger          rpcLogger = new RpcLogger(_log);
            RpcResponseMessage rpcResponseMessage;

            try
            {
                var cancellationTokenSource = new CancellationTokenSource();
                cancellationTokenSource.CancelAfter(ConnectionTimeout);

                using (var pipeStream = new NamedPipeClientStream(IpcPath))
                {
                    await pipeStream.ConnectAsync(cancellationTokenSource.Token);

                    string str   = JsonConvert.SerializeObject(request, JsonSerializerSettings);
                    byte[] bytes = Encoding.UTF8.GetBytes(str);
                    rpcLogger.LogRequest(str);
                    await pipeStream.WriteAsync(bytes, 0, bytes.Length, cancellationTokenSource.Token);

                    using (MemoryStream fullResponse = await ReceiveFullResponseAsync(pipeStream, cancellationTokenSource.Token))
                    {
                        fullResponse.Position = 0L;
                        using (StreamReader streamReader = new StreamReader(fullResponse))
                        {
                            using (JsonTextReader jsonTextReader = new JsonTextReader(streamReader))
                            {
                                RpcResponseMessage responseMessage = JsonSerializer.Create(JsonSerializerSettings).Deserialize <RpcResponseMessage>(jsonTextReader);
                                rpcLogger.LogResponse(responseMessage);
                                rpcResponseMessage = responseMessage;
                            }
                        }
                    }
                }
            }
            catch (TaskCanceledException ex)
            {
                var exception = new RpcClientTimeoutException($"Rpc timeout after {ConnectionTimeout.TotalMilliseconds} milliseconds", ex);
                rpcLogger.LogException(exception);
                throw exception;
            }
            catch (Exception ex)
            {
                var unknownException = new RpcClientUnknownException("Error occurred when trying to send ipc requests(s)", ex);
                rpcLogger.LogException(unknownException);
                throw unknownException;
            }
            return(rpcResponseMessage);
        }
        /// <summary>
        /// Checkout an idle PowerShellManager instance in a non-blocking asynchronous way.
        /// </summary>
        internal PowerShellManager CheckoutIdleWorker(
            string requestId,
            string invocationId,
            string functionName,
            ReadOnlyDictionary <string, ReadOnlyBindingInfo> outputBindings)
        {
            PowerShellManager psManager = null;

            // 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 = CreateLoggerWithContext(requestId, invocationId);
                        psManager = new PowerShellManager(logger, id);

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

                if (psManager == null)
                {
                    var logger = CreateLoggerWithContext(requestId, invocationId);
                    logger.Log(isUserOnlyLog: true, LogLevel.Warning, string.Format(PowerShellWorkerStrings.FunctionQueuingRequest, functionName));

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

            psManager.Logger.SetContext(requestId, invocationId);

            // 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, outputBindings);

            return(psManager);
        }
예제 #23
0
        /// <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
            {
                // Will block if installing dependencies is required
                _dependencyManager.WaitForDependenciesAvailability(
                    () =>
                {
                    var rpcLogger = new RpcLogger(_msgStream);
                    rpcLogger.SetContext(request.RequestId, request.InvocationRequest?.InvocationId);
                    return(rpcLogger);
                });

                AzFunctionInfo    functionInfo = FunctionLoader.GetFunctionInfo(request.InvocationRequest.FunctionId);
                PowerShellManager 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)
            {
                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);
        }
예제 #24
0
        protected override async Task <RpcResponseMessage> SendAsync(RpcRequestMessage request, string route = null)
        {
            var logger = new RpcLogger(_log);

            try
            {
                await semaphoreSlim.WaitAsync().ConfigureAwait(false);

                var rpcRequestJson = JsonConvert.SerializeObject(request, JsonSerializerSettings);
                var requestBytes   = new ArraySegment <byte>(Encoding.UTF8.GetBytes(rpcRequestJson));
                logger.LogRequest(rpcRequestJson);
                var cancellationTokenSource = new CancellationTokenSource();
                cancellationTokenSource.CancelAfter(ConnectionTimeout);

                using (var webSocket = await GetClientWebSocketAsync().ConfigureAwait(false))
                {
                    await webSocket.SendAsync(requestBytes, WebSocketMessageType.Text, true, cancellationTokenSource.Token)
                    .ConfigureAwait(false);

                    using (var memoryData = await ReceiveFullResponseAsync(webSocket).ConfigureAwait(false))
                    {
                        memoryData.Position = 0;
                        using (var streamReader = new StreamReader(memoryData))
                            using (var reader = new JsonTextReader(streamReader))
                            {
                                var serializer = JsonSerializer.Create(JsonSerializerSettings);
                                var message    = serializer.Deserialize <RpcResponseMessage>(reader);
                                logger.LogResponse(message);
                                return(message);
                            }
                    }
                }
            }
            catch (Exception ex)
            {
                var exception = new RpcClientUnknownException("Error occurred when trying to web socket requests(s)", ex);
                logger.LogException(exception);
                throw exception;
            }
            finally
            {
                semaphoreSlim.Release();
            }
        }
예제 #25
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(LogLevel.Trace, 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);
        }
예제 #26
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);
        }
예제 #27
0
        protected override async Task SendAsync(RpcRequestMessage request, string route = null)
        {
            var logger = new RpcLogger(_log);

            try
            {
                await semaphoreSlim.WaitAsync().ConfigureAwait(false);

                var rpcRequestJson = JsonConvert.SerializeObject(request, JsonSerializerSettings);
                var requestBytes   = new ArraySegment <byte>(Encoding.UTF8.GetBytes(rpcRequestJson));
                logger.LogRequest(rpcRequestJson);
                var cancellationTokenSource = new CancellationTokenSource();
                cancellationTokenSource.CancelAfter(ConnectionTimeout);

                var webSocket = await GetClientWebSocketAsync().ConfigureAwait(false);

                await webSocket.SendAsync(requestBytes, WebSocketMessageType.Text, true, cancellationTokenSource.Token)
                .ConfigureAwait(false);

                if (listener != null)
                {
                    cancellationTokenSource.Cancel();
                    cancellationTokenSource.Dispose();
                    cancellationTokenSource = new CancellationTokenSource();
                }

                listener = Task.Factory.StartNew(async() =>
                {
                    await HandleIncomingMessagesAsync(_clientWebSocket, CancellationToken.None);
                }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Current);
            }
            catch (Exception ex)
            {
                logger.LogException(ex);
                throw new RpcClientUnknownException("Error occurred trying to send web socket requests(s)", ex);
            }
            finally
            {
                semaphoreSlim.Release();
            }
        }
예제 #28
0
        internal async Task ProcessRequestLoop()
        {
            StreamingMessage request, response;

            while (await _msgStream.MoveNext())
            {
                request = _msgStream.GetCurrentMessage();

                if (_requestHandlers.TryGetValue(request.ContentCase, out Func <StreamingMessage, StreamingMessage> requestFunc))
                {
                    response = requestFunc(request);
                }
                else
                {
                    RpcLogger.WriteSystemLog(LogLevel.Warning, string.Format(PowerShellWorkerStrings.UnsupportedMessage, request.ContentCase));
                    continue;
                }

                if (response != null)
                {
                    _msgStream.Write(response);
                }
            }
        }
예제 #29
0
        internal PowerShellManager(RpcLogger logger)
        {
            var initialSessionState = InitialSessionState.CreateDefault();

            // Setting the execution policy on macOS and Linux throws an exception so only update it on Windows
            if (Platform.IsWindows)
            {
                // This sets the execution policy on Windows to Unrestricted which is required to run the user's function scripts on
                // Windows client versions. This is needed if a user is testing their function locally with the func CLI
                initialSessionState.ExecutionPolicy = Microsoft.PowerShell.ExecutionPolicy.Unrestricted;
            }
            _pwsh   = PowerShell.Create(initialSessionState);
            _logger = logger;

            // Setup Stream event listeners
            var streamHandler = new StreamHandler(logger);

            _pwsh.Streams.Debug.DataAdding       += streamHandler.DebugDataAdding;
            _pwsh.Streams.Error.DataAdding       += streamHandler.ErrorDataAdding;
            _pwsh.Streams.Information.DataAdding += streamHandler.InformationDataAdding;
            _pwsh.Streams.Progress.DataAdding    += streamHandler.ProgressDataAdding;
            _pwsh.Streams.Verbose.DataAdding     += streamHandler.VerboseDataAdding;
            _pwsh.Streams.Warning.DataAdding     += streamHandler.WarningDataAdding;
        }
예제 #30
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));
            }
        }