Ejemplo n.º 1
0
        internal void AuthenticateToAzure()
        {
            // Try to authenticate to Azure
            // TODO: The Azure Functions Host might supply these differently. This might change but works for the demo
            string applicationId     = Environment.GetEnvironmentVariable("ApplicationId");
            string applicationSecret = Environment.GetEnvironmentVariable("ApplicationSecret");
            string tenantId          = Environment.GetEnvironmentVariable("TenantId");

            if (string.IsNullOrEmpty(applicationId) ||
                string.IsNullOrEmpty(applicationSecret) ||
                string.IsNullOrEmpty(tenantId))
            {
                _logger.Log(LogLevel.Warning, "Required environment variables to authenticate to Azure were not present");
                return;
            }

            // Build SecureString
            var secureString = new SecureString();

            foreach (char item in applicationSecret)
            {
                secureString.AppendChar(item);
            }

            using (ExecutionTimer.Start(_logger, "Authentication to Azure completed."))
            {
                _pwsh.AddCommand("Connect-AzureRmAccount")
                .AddParameter("Credential", new PSCredential(applicationId, secureString))
                .AddParameter("ServicePrincipal")
                .AddParameter("TenantId", tenantId)
                .InvokeAndClearCommands();
            }
        }
        /// <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));
            }
        }
Ejemplo n.º 3
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);
        }
        /// <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);
        }
Ejemplo n.º 5
0
 public void DebugDataAdding(object sender, DataAddingEventArgs e)
 {
     if (e.ItemAdded is DebugRecord record)
     {
         _logger.Log(LogLevel.Debug, $"DEBUG: {record.Message}", isUserLog: true);
     }
 }
Ejemplo n.º 6
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));
            }
        }
Ejemplo n.º 7
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)
        {
            var stopwatch = new Stopwatch();

            stopwatch.Start();

            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, logger: rpcLogger);
                    var managedDependenciesPath = _dependencyManager.Initialize(request, rpcLogger);

                    SetupAppRootPathAndModulePath(functionLoadRequest, managedDependenciesPath);

                    _powershellPool.Initialize(_firstPwshInstance);

                    // Start the download asynchronously if needed.
                    _dependencyManager.StartDependencyInstallationIfNeeded(request, _firstPwshInstance, rpcLogger);

                    rpcLogger.Log(isUserOnlyLog: false, LogLevel.Trace, string.Format(PowerShellWorkerStrings.FirstFunctionLoadCompleted, stopwatch.ElapsedMilliseconds));
                }
                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);
        }
Ejemplo n.º 8
0
        private static void LogPowerShellVersion(RpcLogger rpcLogger, System.Management.Automation.PowerShell pwsh)
        {
            var message = string.Format(PowerShellWorkerStrings.PowerShellVersion, Utils.GetPowerShellVersion(pwsh));

            rpcLogger.Log(isUserOnlyLog: false, LogLevel.Information, message);
        }