Skip to content

Commit

Permalink
Improved handling of env reload request - handles older version of wo…
Browse files Browse the repository at this point in the history
…rker sdk (#1955)

* Handling specialization not supported case.

* Handling older worker version for environment reload request.

* Fix application path.

* package version bump

* PR feedback:
Switched to async overload for deserialization.
Updated global.json to use rc2

* PR feedback - type renames as suggested.
  • Loading branch information
kshyju committed Oct 11, 2023
1 parent f730055 commit da4e1f2
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 60 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

namespace Microsoft.Azure.Functions.Worker
{
/// <summary>
/// The exception that is thrown when the current function app payload does not support environment reload.
/// </summary>
public sealed class EnvironmentReloadNotSupportedException : NotSupportedException
{
public EnvironmentReloadNotSupportedException() { }

public EnvironmentReloadNotSupportedException(string message) : base(message) { }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

namespace Microsoft.Azure.Functions.Worker
{
/// <summary>
/// The exception that is thrown when there is no function app payload found.
/// </summary>
public sealed class FunctionAppPayloadNotFoundException : Exception
{
public FunctionAppPayloadNotFoundException() { }

public FunctionAppPayloadNotFoundException(string message) : base(message) { }
}
}
21 changes: 16 additions & 5 deletions host/src/FunctionsNetHost/Grpc/IncomingGrpcMessageHandler.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Grpc.Messages;

namespace FunctionsNetHost.Grpc
Expand Down Expand Up @@ -50,16 +51,26 @@ private async Task Process(StreamingMessage msg)
Logger.LogTrace("Specialization request received.");

var envReloadRequest = msg.FunctionEnvironmentReloadRequest;
var applicationExePath = PathUtils.GetApplicationExePath(envReloadRequest.FunctionAppDirectory);
Logger.LogTrace($"application path {applicationExePath}");

if (string.IsNullOrWhiteSpace(applicationExePath))
var workerConfig = await WorkerConfigUtils.GetWorkerConfig(envReloadRequest.FunctionAppDirectory);

if (workerConfig?.Description is null)
{
responseMessage.FunctionEnvironmentReloadResponse = BuildFailedEnvironmentReloadResponse(new FunctionAppPayloadNotFoundException());
break;
}

// function app payload which uses an older version of Microsoft.Azure.Functions.Worker package does not support specialization.
if (!workerConfig.Description.CanUsePlaceholder)
{
var ex = new InvalidOperationException($"Unable to find a valid function app payload at '{envReloadRequest.FunctionAppDirectory}'");
responseMessage.FunctionEnvironmentReloadResponse = BuildFailedEnvironmentReloadResponse(ex);
Logger.LogTrace("App payload uses an older version of worker package which does not support specialization.");
responseMessage.FunctionEnvironmentReloadResponse = BuildFailedEnvironmentReloadResponse(new EnvironmentReloadNotSupportedException());
break;
}

var applicationExePath = Path.Combine(envReloadRequest.FunctionAppDirectory, workerConfig.Description.DefaultWorkerPath!);
Logger.LogTrace($"application path {applicationExePath}");

foreach (var kv in envReloadRequest.EnvironmentVariables)
{
EnvironmentUtils.SetValue(kv.Key, kv.Value);
Expand Down
53 changes: 0 additions & 53 deletions host/src/FunctionsNetHost/Grpc/PathUtils.cs

This file was deleted.

13 changes: 13 additions & 0 deletions host/src/FunctionsNetHost/Grpc/WorkerConfig/WorkerConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

namespace FunctionsNetHost.Grpc
{
/// <summary>
/// Represents a worker configuration instance.
/// </summary>
public sealed class WorkerConfig
{
public WorkerDescription? Description { set; get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System.Text.Json.Serialization;

namespace FunctionsNetHost.Grpc
{
[JsonSerializable(typeof(WorkerConfig))]
[JsonSourceGenerationOptions(IncludeFields = true, PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
internal partial class WorkerConfigSerializerContext : JsonSerializerContext { }
}
39 changes: 39 additions & 0 deletions host/src/FunctionsNetHost/Grpc/WorkerConfig/WorkerConfigUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System.Text.Json;

namespace FunctionsNetHost.Grpc
{
internal static class WorkerConfigUtils
{
/// <summary>
/// Builds and returns an instance of <see cref="WorkerConfig"/> from the worker.config.json file if present in the application directory.
/// </summary>
/// <param name="applicationDirectory">The directory where function app deployed payload is present.</param>
internal static async Task<WorkerConfig?> GetWorkerConfig(string applicationDirectory)
{
string workerConfigPath = string.Empty;

try
{
workerConfigPath = Path.Combine(applicationDirectory, "worker.config.json");

using Stream stream = File.OpenRead(workerConfigPath);
var workerConfig = await JsonSerializer.DeserializeAsync(stream, WorkerConfigSerializerContext.Default.WorkerConfig);

return workerConfig;
}
catch (FileNotFoundException)
{
Logger.Log($"worker.config.json not found at {workerConfigPath}. This may indicate missing app payload.");
return null;
}
catch (Exception ex)
{
Logger.Log($"Error in WorkerConfigUtils.GetWorkerConfig.{ex}");
return null;
}
}
}
}
12 changes: 12 additions & 0 deletions host/src/FunctionsNetHost/Grpc/WorkerConfig/WorkerDescription.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

namespace FunctionsNetHost.Grpc
{
public sealed class WorkerDescription
{
public string? DefaultWorkerPath { set; get; }

public bool CanUsePlaceholder { set; get; }
}
}
2 changes: 1 addition & 1 deletion host/src/FunctionsNetHost/global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"sdk": {
"version": "8.0.100-rc.1.23455.8",
"version": "8.0.100-rc.2.23502.2",
"allowPrerelease": true,
"rollForward": "latestMinor"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<id>Microsoft.Azure.Functions.DotNetIsolatedNativeHost</id>
<title>Microsoft Azure Functions dotnet-isolated native host</title>
<tags>dotnet-isolated azure-functions azure</tags>
<version>1.0.0</version>
<version>1.0.1</version>
<authors>Microsoft</authors>
<owners>Microsoft</owners>
<projectUrl>https://github.com/Azure/azure-functions-dotnet-worker</projectUrl>
Expand Down

0 comments on commit da4e1f2

Please sign in to comment.