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); }
/// <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); RpcLogger.WriteSystemLog(string.Format(PowerShellWorkerStrings.LogNewPowerShellManagerCreated, _poolSize.ToString())); } 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); }
/// <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); } } }
/// <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> /// 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())); }
/// <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); }
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); }
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); } } }
/// <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> /// Constructor of the pool. /// </summary> internal PowerShellManagerPool(MessagingStream msgStream) { _msgStream = msgStream; _pool = new BlockingCollection <PowerShellManager>(UpperBound); RpcLogger.WriteSystemLog(LogLevel.Information, string.Format(PowerShellWorkerStrings.LogConcurrencyUpperBound, UpperBound.ToString())); }
/// <summary> /// Constructor of the pool. /// </summary> internal PowerShellManagerPool(Func <ILogger> createLogger) { _createLogger = createLogger; _pool = new BlockingCollection <PowerShellManager>(UpperBound); RpcLogger.WriteSystemLog(LogLevel.Information, string.Format(PowerShellWorkerStrings.LogConcurrencyUpperBound, UpperBound.ToString())); }
private static void LogPowerShellVersion(System.Management.Automation.PowerShell pwsh) { var message = string.Format(PowerShellWorkerStrings.PowerShellVersion, Utils.GetPowerShellVersion(pwsh)); RpcLogger.WriteSystemLog(LogLevel.Information, message); }