diff --git a/protobuf/README.md b/protobuf/README.md index b22f0bb4..a307acfc 100644 --- a/protobuf/README.md +++ b/protobuf/README.md @@ -1,4 +1,4 @@ -# Azure Functions Languge Worker Protobuf +# Azure Functions Language Worker Protobuf This repository contains the protobuf definition file which defines the gRPC service which is used between the [Azure Functions Host](https://github.com/Azure/azure-functions-host) and the Azure Functions language workers. This repo is shared across many repos in many languages (for each worker) by using git commands. @@ -39,7 +39,7 @@ From within the Azure Functions language worker repo: ## Releasing a Language Worker Protobuf version 1. Draft a release in the GitHub UI - - Be sure to inculde details of the release + - Be sure to include details of the release 2. Create a release version, following semantic versioning guidelines ([semver.org](https://semver.org/)) 3. Tag the version with the pattern: `v..

-protofile` (example: `v1.1.0-protofile`) 3. Merge `dev` to `master` diff --git a/protobuf/src/proto/FunctionRpc.proto b/protobuf/src/proto/FunctionRpc.proto index 2bec1203..67a871de 100644 --- a/protobuf/src/proto/FunctionRpc.proto +++ b/protobuf/src/proto/FunctionRpc.proto @@ -96,6 +96,9 @@ message WorkerInitRequest { // inform worker of supported categories and their levels // i.e. Worker = Verbose, Function.MyFunc = None map log_categories = 3; + + // Full path of worker.config.json location + string worker_directory = 4; } // Worker responds with the result of initializing itself @@ -189,6 +192,8 @@ message WorkerStatusResponse { message FunctionEnvironmentReloadRequest { // Environment variables from the current process map environment_variables = 1; + // Current directory of function app + string function_app_directory = 2; } message FunctionEnvironmentReloadResponse { @@ -429,11 +434,12 @@ message RpcException { // Http cookie type. Note that only name and value are used for Http requests message RpcHttpCookie { - // Enum that lets servers require that a cookie shouoldn't be sent with cross-site requests + // Enum that lets servers require that a cookie shouldn't be sent with cross-site requests enum SameSite { None = 0; Lax = 1; Strict = 2; + ExplicitNone = 3; } // Cookie name diff --git a/src/FunctionsEnvironmentReloader.cs b/src/FunctionsEnvironmentReloader.cs new file mode 100644 index 00000000..86852246 --- /dev/null +++ b/src/FunctionsEnvironmentReloader.cs @@ -0,0 +1,52 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System; +using System.Collections.Generic; +using System.IO; +using Microsoft.Azure.Functions.PowerShellWorker.Utility; +using LogLevel = Microsoft.Azure.WebJobs.Script.Grpc.Messages.RpcLog.Types.Level; + +namespace Microsoft.Azure.Functions.PowerShellWorker +{ + internal class FunctionsEnvironmentReloader + { + private readonly ILogger _logger; + private readonly Action _setEnvironmentVariable; + private readonly Action _setCurrentDirectory; + + public FunctionsEnvironmentReloader(ILogger logger) + : this(logger, Environment.SetEnvironmentVariable, Directory.SetCurrentDirectory) + { + } + + internal FunctionsEnvironmentReloader( + ILogger logger, + Action setEnvironmentVariable, + Action setCurrentDirectory) + { + this._logger = logger ?? throw new ArgumentNullException(nameof(logger)); + this._setEnvironmentVariable = setEnvironmentVariable; + this._setCurrentDirectory = setCurrentDirectory; + } + + public void ReloadEnvironment( + IEnumerable> environmentVariables, + string functionAppDirectory) + { + foreach (var (name, value) in environmentVariables) + { + this._setEnvironmentVariable(name, value); + } + + if (functionAppDirectory != null) + { + var setCurrentDirMessage = string.Format(PowerShellWorkerStrings.SettingCurrentDirectory, functionAppDirectory); + _logger.Log(isUserOnlyLog: false, LogLevel.Trace, setCurrentDirMessage); + _setCurrentDirectory(functionAppDirectory); + } + } + } +} diff --git a/src/RequestProcessor.cs b/src/RequestProcessor.cs index e1a89dee..e39b585b 100644 --- a/src/RequestProcessor.cs +++ b/src/RequestProcessor.cs @@ -20,6 +20,7 @@ namespace Microsoft.Azure.Functions.PowerShellWorker { using System.Diagnostics; + using System.IO; using LogLevel = Microsoft.Azure.WebJobs.Script.Grpc.Messages.RpcLog.Types.Level; internal class RequestProcessor @@ -338,13 +339,15 @@ internal StreamingMessage ProcessFunctionEnvironmentReloadRequest(StreamingMessa stopwatch.Start(); var environmentReloadRequest = request.FunctionEnvironmentReloadRequest; - foreach (var (name, value) in environmentReloadRequest.EnvironmentVariables) - { - Environment.SetEnvironmentVariable(name, value); - } 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( diff --git a/src/resources/PowerShellWorkerStrings.resx b/src/resources/PowerShellWorkerStrings.resx index 59cc53c1..081a795f 100644 --- a/src/resources/PowerShellWorkerStrings.resx +++ b/src/resources/PowerShellWorkerStrings.resx @@ -316,4 +316,7 @@ Environment reload completed in {0} ms. + + Setting current directory to '{0}'. + \ No newline at end of file diff --git a/test/Unit/Function/FunctionsEnvironmentReloaderTests.cs b/test/Unit/Function/FunctionsEnvironmentReloaderTests.cs new file mode 100644 index 00000000..6826b49b --- /dev/null +++ b/test/Unit/Function/FunctionsEnvironmentReloaderTests.cs @@ -0,0 +1,65 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using Moq; +using Xunit; +using Microsoft.Azure.Functions.PowerShellWorker.Utility; +using System.Collections.Generic; + +namespace Microsoft.Azure.Functions.PowerShellWorker.Test +{ + public class FunctionsEnvironmentReloaderTests + { + private readonly Mock _mockLogger = new Mock(); + + [Fact] + public void SetsEnvironmentVariables() + { + var actualEnvironmentVariables = new List>(); + + var reloader = new FunctionsEnvironmentReloader( + logger: _mockLogger.Object, + setEnvironmentVariable: (name, value) => { actualEnvironmentVariables.Add(new KeyValuePair(name, value)); }, + setCurrentDirectory: directory => { }); + + var requestedEnvironmentVariables = new[] { + new KeyValuePair( "name1", "valueA" ), + new KeyValuePair( "name2", "valueB" ), + new KeyValuePair( "name3", "valueC" ), + }; + + reloader.ReloadEnvironment(requestedEnvironmentVariables, functionAppDirectory: null); + + Assert.Equal(requestedEnvironmentVariables, actualEnvironmentVariables); + } + + [Fact] + public void SetsFunctionAppDirectoryIfRequested() + { + const string RequestedNewDirectory = "new app directory"; + string actualNewDirectory = null; + + var reloader = new FunctionsEnvironmentReloader( + logger: _mockLogger.Object, + setEnvironmentVariable: (name, value) => { }, + setCurrentDirectory: directory => { actualNewDirectory = directory; }); + + reloader.ReloadEnvironment(new List>(), RequestedNewDirectory); + + Assert.Equal(RequestedNewDirectory, actualNewDirectory); + } + + [Fact] + public void DoesNotSetFunctionAppDirectoryIfNotRequested() + { + var reloader = new FunctionsEnvironmentReloader( + logger: _mockLogger.Object, + setEnvironmentVariable: (name, value) => { }, + setCurrentDirectory: directory => { Assert.True(false, "Unexpected invocation"); }); + + reloader.ReloadEnvironment(new List>(), functionAppDirectory: null); + } + } +}