diff --git a/src/WebJobs.Script.WebHost/Controllers/KeysController.cs b/src/WebJobs.Script.WebHost/Controllers/KeysController.cs index fc927d9bdd..099b135087 100644 --- a/src/WebJobs.Script.WebHost/Controllers/KeysController.cs +++ b/src/WebJobs.Script.WebHost/Controllers/KeysController.cs @@ -69,7 +69,7 @@ public async Task Get() // Extensions that are webhook providers create their default system keys // as part of host initialization (when those keys aren't already present). // So we must delay key retrieval until host initialization is complete. - await _hostManager.DelayUntilHostReady(); + await _hostManager.DelayUntilHostReadyAsync(); } Dictionary keys = await GetHostSecretsByScope(hostKeyScope); diff --git a/src/WebJobs.Script.WebHost/Diagnostics/HealthChecks/HealthCheckResponseWriter.cs b/src/WebJobs.Script.WebHost/Diagnostics/HealthChecks/HealthCheckResponseWriter.cs new file mode 100644 index 0000000000..72e40e8a0f --- /dev/null +++ b/src/WebJobs.Script.WebHost/Diagnostics/HealthChecks/HealthCheckResponseWriter.cs @@ -0,0 +1,43 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using System.Text.Json; +using System.Threading.Tasks; +using HealthChecks.UI.Client; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Primitives; + +namespace Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics.HealthChecks +{ + public class HealthCheckResponseWriter + { + public static Task WriteResponseAsync(HttpContext httpContext, HealthReport report) + { + ArgumentNullException.ThrowIfNull(httpContext); + ArgumentNullException.ThrowIfNull(report); + + // We will write a detailed report if ?expand=true is present. + if (httpContext.Request.Query.TryGetValue("expand", out StringValues value) + && bool.TryParse(value, out bool expand) && expand) + { + return UIResponseWriter.WriteHealthCheckUIResponse(httpContext, report); + } + + return WriteMinimalResponseAsync(httpContext, report); + } + + private static Task WriteMinimalResponseAsync(HttpContext httpContext, HealthReport report) + { + MinimalResponse body = new(report.Status); + return JsonSerializer.SerializeAsync( + httpContext.Response.Body, body, JsonSerializerOptionsProvider.Options, httpContext.RequestAborted); + } + + internal readonly struct MinimalResponse(HealthStatus status) + { + public HealthStatus Status { get; } = status; + } + } +} diff --git a/src/WebJobs.Script.WebHost/Diagnostics/HealthChecks/HealthCheckWaitMiddleware.cs b/src/WebJobs.Script.WebHost/Diagnostics/HealthChecks/HealthCheckWaitMiddleware.cs new file mode 100644 index 0000000000..f308ee49f5 --- /dev/null +++ b/src/WebJobs.Script.WebHost/Diagnostics/HealthChecks/HealthCheckWaitMiddleware.cs @@ -0,0 +1,40 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Azure.WebJobs.Script.WebHost.Models; +using Microsoft.Extensions.Primitives; + +namespace Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics.HealthChecks +{ + public sealed class HealthCheckWaitMiddleware(RequestDelegate next, IScriptHostManager manager) + { + private readonly RequestDelegate _next = next ?? throw new ArgumentNullException(nameof(next)); + private readonly IScriptHostManager _manager = manager ?? throw new ArgumentNullException(nameof(manager)); + + public async Task InvokeAsync(HttpContext context) + { + ArgumentNullException.ThrowIfNull(context); + + // If specified, the ?wait={seconds} query param will wait for an + // active script host for that duration. This is to avoid excessive polling + // when waiting for the initial readiness probe. + if (context.Request.Query.TryGetValue("wait", out StringValues wait)) + { + if (!int.TryParse(wait.ToString(), out int waitSeconds) || waitSeconds < 0) + { + context.Response.StatusCode = StatusCodes.Status400BadRequest; + await context.Response.WriteAsJsonAsync( + ErrorResponse.BadArgument("'wait' query param must be a positive integer", $"wait={wait}")); + return; + } + + await _manager.DelayUntilHostReadyAsync(waitSeconds); + } + + await _next(context); + } + } +} diff --git a/src/WebJobs.Script.WebHost/Extensions/HttpContextExtensions.cs b/src/WebJobs.Script.WebHost/Extensions/HttpContextExtensions.cs index 6895067d8b..71443d79e4 100644 --- a/src/WebJobs.Script.WebHost/Extensions/HttpContextExtensions.cs +++ b/src/WebJobs.Script.WebHost/Extensions/HttpContextExtensions.cs @@ -23,7 +23,7 @@ public static async Task WaitForRunningHostAsync(this HttpContext httpContext, I // If the host is not ready, we'll wait a bit for it to initialize. // This might happen if http requests come in while the host is starting // up for the first time, or if it is restarting. - bool hostReady = await hostManager.DelayUntilHostReady(timeoutSeconds, pollingIntervalMilliseconds); + bool hostReady = await hostManager.DelayUntilHostReadyAsync(timeoutSeconds, pollingIntervalMilliseconds); if (!hostReady) { diff --git a/src/WebJobs.Script.WebHost/Extensions/ScriptHostManagerExtensions.cs b/src/WebJobs.Script.WebHost/Extensions/ScriptHostManagerExtensions.cs index 0770bbc058..86889e1446 100644 --- a/src/WebJobs.Script.WebHost/Extensions/ScriptHostManagerExtensions.cs +++ b/src/WebJobs.Script.WebHost/Extensions/ScriptHostManagerExtensions.cs @@ -7,7 +7,7 @@ namespace Microsoft.Azure.WebJobs.Script { public static class ScriptHostManagerExtensions { - public static async Task DelayUntilHostReady(this IScriptHostManager hostManager, int timeoutSeconds = ScriptConstants.HostTimeoutSeconds, int pollingIntervalMilliseconds = ScriptConstants.HostPollingIntervalMilliseconds) + public static async Task DelayUntilHostReadyAsync(this IScriptHostManager hostManager, int timeoutSeconds = ScriptConstants.HostTimeoutSeconds, int pollingIntervalMilliseconds = ScriptConstants.HostPollingIntervalMilliseconds) { if (HostIsInitialized(hostManager)) { diff --git a/src/WebJobs.Script.WebHost/Middleware/HostAvailabilityCheckMiddleware.cs b/src/WebJobs.Script.WebHost/Middleware/HostAvailabilityCheckMiddleware.cs index 2dc4917c55..4ef5095da7 100644 --- a/src/WebJobs.Script.WebHost/Middleware/HostAvailabilityCheckMiddleware.cs +++ b/src/WebJobs.Script.WebHost/Middleware/HostAvailabilityCheckMiddleware.cs @@ -54,7 +54,7 @@ private static async Task InvokeAwaitingHost(HttpContext context, RequestDelegat { Logger.InitiatingHostAvailabilityCheck(logger); - bool hostReady = await scriptHostManager.DelayUntilHostReady(); + bool hostReady = await scriptHostManager.DelayUntilHostReadyAsync(); if (!hostReady) { Logger.HostUnavailableAfterCheck(logger); diff --git a/src/WebJobs.Script.WebHost/Middleware/HostWarmupMiddleware.cs b/src/WebJobs.Script.WebHost/Middleware/HostWarmupMiddleware.cs index 188524ca74..a03ce77952 100644 --- a/src/WebJobs.Script.WebHost/Middleware/HostWarmupMiddleware.cs +++ b/src/WebJobs.Script.WebHost/Middleware/HostWarmupMiddleware.cs @@ -161,7 +161,7 @@ public async Task HostWarmupAsync(HttpRequest request) await _hostManager.RestartHostAsync(CancellationToken.None); // This call is here for sanity, but we should be fully initialized. - await _hostManager.DelayUntilHostReady(); + await _hostManager.DelayUntilHostReadyAsync(); } } diff --git a/src/WebJobs.Script.WebHost/Models/ApiErrorModel.cs b/src/WebJobs.Script.WebHost/Models/ApiErrorModel.cs deleted file mode 100644 index 45359586cd..0000000000 --- a/src/WebJobs.Script.WebHost/Models/ApiErrorModel.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Net; -using Newtonsoft.Json; - -namespace Microsoft.Azure.WebJobs.Script.WebHost.Models -{ - public class ApiErrorModel - { - public ApiErrorModel(HttpStatusCode status) - : this() - { - StatusCode = status; - } - - public ApiErrorModel() - { - Id = Guid.NewGuid().ToString(); - } - - [JsonProperty("id")] - public string Id { get; set; } - - [JsonProperty("requestId")] - public string RequestId { get; set; } - - [JsonProperty("statusCode")] - public HttpStatusCode StatusCode { get; set; } - - [JsonProperty("errorCode")] - public int ErrorCode { get; set; } - - [JsonProperty("message", NullValueHandling = NullValueHandling.Ignore)] - public string Message { get; set; } - - [JsonProperty("errorDetails", NullValueHandling = NullValueHandling.Ignore)] - public string ErrorDetails { get; set; } - - [JsonProperty("arguments", NullValueHandling = NullValueHandling.Ignore)] - public IDictionary Arguments { get; set; } - } -} \ No newline at end of file diff --git a/src/WebJobs.Script.WebHost/Models/ErrorResponse.cs b/src/WebJobs.Script.WebHost/Models/ErrorResponse.cs new file mode 100644 index 0000000000..8f8704255d --- /dev/null +++ b/src/WebJobs.Script.WebHost/Models/ErrorResponse.cs @@ -0,0 +1,63 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Text.Json.Serialization; +using Newtonsoft.Json; + +namespace Microsoft.Azure.WebJobs.Script.WebHost.Models +{ + /// + /// Represents an error response. + /// See https://github.com/Azure/azure-resource-manager-rpc/blob/master/v1.0/common-api-details.md#error-response-content. + /// + /// + /// The error code. This is NOT the HTTP status code. + /// Unlocalized string which can be used to programmatically identify the error. + /// The code should be Pascal-cased, and should serve to uniquely identify a particular class of error, + /// for example "BadArgument". + /// + /// + /// The error message. Describes the error in detail and provides debugging information. + /// If Accept-Language is set in the request, it must be localized to that language. + /// ] + public record ErrorResponse( + [property: JsonProperty("code")][property: JsonPropertyName("code")] string Code, + [property: JsonProperty("message")][property: JsonPropertyName("message")] string Message) + { + /// + /// Gets the target of the particular error. For example, the name of the property in error. + /// + [JsonProperty("target")] + [JsonPropertyName("target")] + public string Target { get; init; } + + /// + /// Gets the details of this error. + /// + [JsonProperty("details")] + [JsonPropertyName("details")] + public IEnumerable Details { get; init; } = []; + + /// + /// Gets the additional information for this error. + /// + [JsonProperty("additionalInfo")] + [JsonPropertyName("additionalInfo")] + public IEnumerable AdditionalInfo { get; init; } = []; + + public static ErrorResponse BadArgument(string message, string target = null) + { + return new("BadArgument", message) { Target = target }; + } + } + + /// + /// Represents additional information for an error. + /// + /// The type of additional information. + /// The additional error information. + public record ErrorAdditionalInfo( + [property: JsonProperty("type")][property: JsonPropertyName("type")] string Type, + [property: JsonProperty("info")][property: JsonPropertyName("info")] object Info); +} \ No newline at end of file diff --git a/src/WebJobs.Script.WebHost/Standby/StandbyManager.cs b/src/WebJobs.Script.WebHost/Standby/StandbyManager.cs index e60f8be35f..74a0e98dab 100644 --- a/src/WebJobs.Script.WebHost/Standby/StandbyManager.cs +++ b/src/WebJobs.Script.WebHost/Standby/StandbyManager.cs @@ -124,7 +124,7 @@ public async Task SpecializeHostCoreAsync() using (_metricsLogger.LatencyEvent(MetricEventNames.SpecializationDelayUntilHostReady)) { - await _scriptHostManager.DelayUntilHostReady(); + await _scriptHostManager.DelayUntilHostReadyAsync(); } } diff --git a/src/WebJobs.Script.WebHost/WebJobs.Script.WebHost.csproj b/src/WebJobs.Script.WebHost/WebJobs.Script.WebHost.csproj index 7be370720c..6de8369bba 100644 --- a/src/WebJobs.Script.WebHost/WebJobs.Script.WebHost.csproj +++ b/src/WebJobs.Script.WebHost/WebJobs.Script.WebHost.csproj @@ -50,6 +50,7 @@ + diff --git a/src/WebJobs.Script/JsonSerializerOptionsProvider.cs b/src/WebJobs.Script/JsonSerializerOptionsProvider.cs new file mode 100644 index 0000000000..7f6c0fe693 --- /dev/null +++ b/src/WebJobs.Script/JsonSerializerOptionsProvider.cs @@ -0,0 +1,33 @@ +// 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; +using System.Text.Json.Serialization; + +namespace Microsoft.Azure.WebJobs.Script +{ + /// + /// Provides constants related to JSON serialization options used. + /// + public static class JsonSerializerOptionsProvider + { + /// + /// Gets the default JSON serializer options used across the functions host. + /// + public static readonly JsonSerializerOptions Options = CreateJsonOptions(); + + private static JsonSerializerOptions CreateJsonOptions() + { + var options = new JsonSerializerOptions + { + AllowTrailingCommas = true, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; + + options.Converters.Add(new JsonStringEnumConverter()); + + return options; + } + } +} diff --git a/src/WebJobs.Script/runtimeassemblies.json b/src/WebJobs.Script/runtimeassemblies.json index bdd53b5e22..8bc5f7b933 100644 --- a/src/WebJobs.Script/runtimeassemblies.json +++ b/src/WebJobs.Script/runtimeassemblies.json @@ -71,6 +71,14 @@ "name": "Grpc.Net.Common", "resolutionPolicy": "private" }, + { + "name": "HealthChecks.UI.Client", + "resolutionPolicy": "private" + }, + { + "name": "HealthChecks.UI.Core", + "resolutionPolicy": "private" + }, { "name": "Microsoft.AI.DependencyCollector", "resolutionPolicy": "private" @@ -607,6 +615,10 @@ "name": "Microsoft.Extensions.Caching.Memory", "resolutionPolicy": "minorMatchOrLower" }, + { + "name": "Microsoft.Extensions.Compliance.Abstractions", + "resolutionPolicy": "minorMatchOrLower" + }, { "name": "Microsoft.Extensions.Configuration", "resolutionPolicy": "minorMatchOrLower" @@ -779,6 +791,10 @@ "name": "Microsoft.Extensions.Primitives", "resolutionPolicy": "minorMatchOrLower" }, + { + "name": "Microsoft.Extensions.Telemetry.Abstractions", + "resolutionPolicy": "minorMatchOrLower" + }, { "name": "Microsoft.Extensions.WebEncoders", "resolutionPolicy": "minorMatchOrLower" diff --git a/test/WebJobs.Script.Tests.Integration/Controllers/ControllerScenarioTestFixture.cs b/test/WebJobs.Script.Tests.Integration/Controllers/ControllerScenarioTestFixture.cs index b3824a593a..58d987ecdf 100644 --- a/test/WebJobs.Script.Tests.Integration/Controllers/ControllerScenarioTestFixture.cs +++ b/test/WebJobs.Script.Tests.Integration/Controllers/ControllerScenarioTestFixture.cs @@ -76,7 +76,7 @@ public virtual async Task InitializeAsync() HttpClient.BaseAddress = new Uri("https://localhost/"); var manager = HttpServer.Host.Services.GetService(); - await manager.DelayUntilHostReady(); + await manager.DelayUntilHostReadyAsync(); } public Task DisposeAsync() diff --git a/test/WebJobs.Script.Tests.Shared/TestHelpers.cs b/test/WebJobs.Script.Tests.Shared/TestHelpers.cs index ca98a157e7..aa80fa8452 100644 --- a/test/WebJobs.Script.Tests.Shared/TestHelpers.cs +++ b/test/WebJobs.Script.Tests.Shared/TestHelpers.cs @@ -35,6 +35,28 @@ public static partial class TestHelpers private const string Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; private static readonly Random Random = new Random(); + /// + /// Helper method to inline an action delegate. + /// + /// The action. + /// The provided action. + /// + /// This is intended to be used with a fluent assertion. + /// Act(() => { }).Should().Something();. + /// + public static Action Act(Action act) => act; + + /// + /// Helper method to inline an action delegate. + /// + /// The action. + /// The provided action. + /// + /// This is intended to be used with a fluent assertion. + /// Act(() => { }).Should().Something();. + /// + public static Func Act(Func act) => act; + public static Task WaitOneAsync(this WaitHandle waitHandle) { ArgumentNullException.ThrowIfNull(waitHandle); diff --git a/test/WebJobs.Script.Tests/Diagnostics/HealthChecks/HealthCheckResponseWriterTests.cs b/test/WebJobs.Script.Tests/Diagnostics/HealthChecks/HealthCheckResponseWriterTests.cs new file mode 100644 index 0000000000..9aeabcf7b3 --- /dev/null +++ b/test/WebJobs.Script.Tests/Diagnostics/HealthChecks/HealthCheckResponseWriterTests.cs @@ -0,0 +1,120 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Threading.Tasks; +using FluentAssertions; +using HealthChecks.UI.Client; +using HealthChecks.UI.Core; +using Microsoft.AspNetCore.Http; +using Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics.HealthChecks; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Moq; +using Xunit; + +namespace Microsoft.Azure.WebJobs.Script.Tests.Diagnostics.HealthChecks +{ + public class HealthCheckResponseWriterTests + { + [Fact] + public async Task WriteResponseAsync_NullHttpContext_Throws() + { + HealthReport report = new HealthReport(null, HealthStatus.Healthy, TimeSpan.Zero); + await TestHelpers.Act(() => + HealthCheckResponseWriter.WriteResponseAsync(null, report)) + .Should().ThrowAsync().WithParameterName("httpContext"); + } + + [Fact] + public async Task WriteResponseAsync_NullReport_Throws() + { + await TestHelpers.Act(() => + HealthCheckResponseWriter.WriteResponseAsync(Mock.Of(), null)) + .Should().ThrowAsync().WithParameterName("report"); + } + + [Fact] + public async Task WriteResponseAsync_ExpandTrue_CallsUIResponseWriter() + { + // Arrange + DefaultHttpContext context = new(); + context.Request.QueryString = new QueryString("?expand=true"); + using MemoryStream stream = new(); + context.Response.Body = stream; + + Dictionary checks = new() + { + ["test.check.1"] = new HealthReportEntry( + HealthStatus.Healthy, null, TimeSpan.FromMilliseconds(10), null, null, ["test.tag.1"]), + ["test.check.2"] = new HealthReportEntry( + HealthStatus.Unhealthy, + "Test unhealthy check", + TimeSpan.FromSeconds(1), + new Exception("Error! Error!"), + new Dictionary() { ["test.data.1"] = "test value 1" }, + ["test.tag.1", "test.tag.2"]), + }; + + HealthReport report = new(checks, TimeSpan.FromSeconds(1)); + + // Act + await HealthCheckResponseWriter.WriteResponseAsync(context, report); + + // Assert + JsonObject expected = new() + { + ["status"] = "Unhealthy", + ["totalDuration"] = "00:00:01", + ["entries"] = new JsonObject + { + ["test.check.1"] = new JsonObject + { + ["data"] = new JsonObject(), + ["duration"] = "00:00:00.0100000", + ["status"] = "Healthy", + ["tags"] = new JsonArray { "test.tag.1" } + }, + ["test.check.2"] = new JsonObject + { + ["data"] = new JsonObject { ["test.data.1"] = "test value 1" }, + ["description"] = "Test unhealthy check", + ["duration"] = "00:00:01", + ["exception"] = "Error! Error!", + ["status"] = "Unhealthy", + ["tags"] = new JsonArray { "test.tag.1", "test.tag.2" } + } + } + }; + + stream.Position = 0; + string actual = await new StreamReader(stream).ReadToEndAsync(); + string expectedJson = expected.ToJsonString(); + + actual.Should().Be(expectedJson); + } + + [Fact] + public async Task WriteResponseAsync_ExpandNotTrue_WritesMinimalResponse() + { + // Arrange + DefaultHttpContext context = new(); + context.Request.QueryString = new QueryString(string.Empty); // no expand + HealthReport report = new(null, HealthStatus.Healthy, TimeSpan.Zero); + using MemoryStream stream = new(); + context.Response.Body = stream; + + // Act + await HealthCheckResponseWriter.WriteResponseAsync(context, report); + + // Assert + stream.Position = 0; + string actual = await new StreamReader(stream).ReadToEndAsync(); + string expected = "{\"status\":\"Healthy\"}"; + actual.Should().Be(expected); + } + } +} diff --git a/test/WebJobs.Script.Tests/Diagnostics/HealthChecks/HealthCheckWaitMiddlewareTests.cs b/test/WebJobs.Script.Tests/Diagnostics/HealthChecks/HealthCheckWaitMiddlewareTests.cs new file mode 100644 index 0000000000..94a2e2a4d5 --- /dev/null +++ b/test/WebJobs.Script.Tests/Diagnostics/HealthChecks/HealthCheckWaitMiddlewareTests.cs @@ -0,0 +1,118 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Text.Json; +using System.Threading.Tasks; +using FluentAssertions; +using Microsoft.AspNetCore.Http; +using Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics.HealthChecks; +using Microsoft.Azure.WebJobs.Script.WebHost.Models; +using Moq; +using Xunit; + +namespace Microsoft.Azure.WebJobs.Script.Tests.Diagnostics.HealthChecks +{ + public class HealthCheckWaitMiddlewareTests + { + [Fact] + public void Constructor_NullNext_ThrowsArgumentNullException() + { + TestHelpers.Act(() => new HealthCheckWaitMiddleware(null, Mock.Of())) + .Should().Throw().WithParameterName("next"); + } + + [Fact] + public void Constructor_NullManager_ThrowsArgumentNullException() + { + TestHelpers.Act(() => new HealthCheckWaitMiddleware(Mock.Of(), null)) + .Should().Throw().WithParameterName("manager"); + } + + [Theory] + [InlineData(null)] + [InlineData("?not_a_known_query_value=true")] + public async Task InvokeAsync_NoWaitQuery_Continues(string query) + { + // arrange + Mock next = new(); + Mock manager = new(MockBehavior.Strict); + HttpContext context = CreateContext(query, null); + HealthCheckWaitMiddleware middleware = new(next.Object, manager.Object); + + // act + await middleware.InvokeAsync(context); + + // assert + next.Verify(m => m(context), Times.Once); + next.VerifyNoOtherCalls(); + } + + [Theory] + [InlineData("10s")] + [InlineData("true")] + [InlineData("-10")] + public async Task InvokeAsync_InvalidWaitQuery_BadRequest(string wait) + { + // arrange + using MemoryStream stream = new(); + Mock next = new(); + Mock manager = new(MockBehavior.Strict); + HttpContext context = CreateContext($"?wait={wait}", stream); + HealthCheckWaitMiddleware middleware = new(next.Object, manager.Object); + + // act + await middleware.InvokeAsync(context); + stream.Position = 0; + + // assert + next.Verify(m => m(context), Times.Never); + next.VerifyNoOtherCalls(); + + context.Response.StatusCode.Should().Be(StatusCodes.Status400BadRequest); + ErrorResponse error = await JsonSerializer.DeserializeAsync(stream); + error.Code.Should().Be("BadArgument"); + } + + [Fact] + public async Task InvokeAsync_ValidWaitQuery_CallsDelayAndNext() + { + // arrange + // each failed loop is 3 calls, plus 1 for the final call. + int neededCalls = (Random.Shared.Next(1, 10) * 3) + 1; + int calls = 0; + TaskCompletionSource tcs = new(); + Mock next = new(); + Mock manager = new(MockBehavior.Strict); + manager.Setup(m => m.State).Returns(() => + { + // The check calls this 2 times per loop. + return calls++ < neededCalls ? ScriptHostState.Starting : ScriptHostState.Running; + }); + + HttpContext context = CreateContext($"?wait=5", null); + var middleware = new HealthCheckWaitMiddleware(next.Object, manager.Object); + + // act + await middleware.InvokeAsync(context); + + // assert + manager.Verify(m => m.State, Times.Exactly(neededCalls + 3)); + next.Verify(m => m(context), Times.Once); + next.VerifyNoOtherCalls(); + } + + private static DefaultHttpContext CreateContext(string query, Stream body) + { + DefaultHttpContext context = new(); + context.Request.QueryString = new(query); + if (body is not null) + { + context.Response.Body = body; + } + + return context; + } + } +} \ No newline at end of file diff --git a/test/WebJobs.Script.Tests/Microsoft.Azure.WebJobs.Script.WebHost.deps.json b/test/WebJobs.Script.Tests/Microsoft.Azure.WebJobs.Script.WebHost.deps.json index 3403280b35..e534acc546 100644 --- a/test/WebJobs.Script.Tests/Microsoft.Azure.WebJobs.Script.WebHost.deps.json +++ b/test/WebJobs.Script.Tests/Microsoft.Azure.WebJobs.Script.WebHost.deps.json @@ -6,8 +6,9 @@ "compilationOptions": {}, "targets": { ".NETCoreApp,Version=v8.0": { - "Microsoft.Azure.WebJobs.Script.WebHost/4.1040.100-pr.25225.14": { + "Microsoft.Azure.WebJobs.Script.WebHost/4.1042.100-pr.25364.8": { "dependencies": { + "AspNetCore.HealthChecks.UI.Client": "9.0.0", "Azure.Data.Tables": "12.8.3", "Azure.Identity": "1.11.4", "Azure.Security.KeyVault.Secrets": "4.6.0", @@ -22,18 +23,18 @@ "Microsoft.Azure.AppService.Middleware.Functions": "1.5.5", "Microsoft.Azure.AppService.Proxy.Client": "2.3.20240307.67", "Microsoft.Azure.Functions.DotNetIsolatedNativeHost": "1.0.12", - "Microsoft.Azure.Functions.JavaWorker": "2.19.0", + "Microsoft.Azure.Functions.JavaWorker": "2.19.2", "Microsoft.Azure.Functions.NodeJsWorker": "3.10.1", "Microsoft.Azure.Functions.Platform.Metrics.LinuxConsumption": "1.0.5", "Microsoft.Azure.Functions.PowerShellWorker.PS7.0": "4.0.3148", "Microsoft.Azure.Functions.PowerShellWorker.PS7.2": "4.0.4025", "Microsoft.Azure.Functions.PowerShellWorker.PS7.4": "4.0.4206", - "Microsoft.Azure.Functions.PythonWorker": "4.37.0", + "Microsoft.Azure.Functions.PythonWorker": "4.38.0", "Microsoft.Azure.Storage.File": "11.1.7", "Microsoft.Azure.WebJobs": "3.0.41", "Microsoft.Azure.WebJobs.Host.Storage": "5.0.1", - "Microsoft.Azure.WebJobs.Script": "4.1040.100-pr.25225.14", - "Microsoft.Azure.WebJobs.Script.Grpc": "4.1040.100-pr.25225.14", + "Microsoft.Azure.WebJobs.Script": "4.1042.100-pr.25364.8", + "Microsoft.Azure.WebJobs.Script.Grpc": "4.1042.100-pr.25364.8", "Microsoft.Azure.WebSites.DataProtection": "2.1.91-alpha", "Microsoft.IdentityModel.Protocols.OpenIdConnect": "6.35.0", "Microsoft.IdentityModel.Tokens": "6.35.0", @@ -44,13 +45,35 @@ "System.IdentityModel.Tokens.Jwt": "6.35.0", "System.Memory.Data": "8.0.1", "System.Net.NameResolution": "4.3.0", - "Microsoft.Azure.WebJobs.Script.Reference": "4.1040.0.0", - "Microsoft.Azure.WebJobs.Script.Grpc.Reference": "4.1040.0.0" + "Microsoft.Azure.WebJobs.Script.Reference": "4.1042.0.0", + "Microsoft.Azure.WebJobs.Script.Grpc.Reference": "4.1042.0.0" }, "runtime": { "Microsoft.Azure.WebJobs.Script.WebHost.dll": {} } }, + "AspNetCore.HealthChecks.UI.Client/9.0.0": { + "dependencies": { + "AspNetCore.HealthChecks.UI.Core": "9.0.0" + }, + "runtime": { + "lib/net8.0/HealthChecks.UI.Client.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.0.0" + } + } + }, + "AspNetCore.HealthChecks.UI.Core/9.0.0": { + "dependencies": { + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.11" + }, + "runtime": { + "lib/net8.0/HealthChecks.UI.Core.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.0.0" + } + } + }, "Autofac/4.6.2": { "dependencies": { "NETStandard.Library": "2.0.1", @@ -237,7 +260,7 @@ "Grpc.Net.Client/2.55.0": { "dependencies": { "Grpc.Net.Common": "2.55.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0" + "Microsoft.Extensions.Logging.Abstractions": "8.0.2" }, "runtime": { "lib/net7.0/Grpc.Net.Client.dll": { @@ -341,7 +364,7 @@ "Microsoft.ApplicationInsights.SnapshotCollector/1.4.4": { "dependencies": { "Microsoft.ApplicationInsights": "2.22.0", - "Microsoft.Extensions.Options": "8.0.0", + "Microsoft.Extensions.Options": "8.0.2", "System.IO.FileSystem.AccessControl": "5.0.0" }, "runtime": { @@ -402,8 +425,8 @@ "Microsoft.AspNetCore.Authentication.Abstractions/2.2.0": { "dependencies": { "Microsoft.AspNetCore.Http.Abstractions": "2.2.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0" + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.AspNetCore.Authentication.Core/2.2.0": { @@ -426,8 +449,8 @@ }, "Microsoft.AspNetCore.Authorization/2.2.0": { "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0" + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.AspNetCore.Authorization.Policy/2.2.0": { @@ -440,9 +463,9 @@ "dependencies": { "Microsoft.AspNetCore.Http.Extensions": "2.2.0", "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.AspNetCore.Cryptography.Internal/2.2.0": {}, @@ -451,9 +474,9 @@ "Microsoft.AspNetCore.Cryptography.Internal": "2.2.0", "Microsoft.AspNetCore.DataProtection.Abstractions": "2.2.0", "Microsoft.AspNetCore.Hosting.Abstractions": "2.2.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2", "Microsoft.Win32.Registry": "4.5.0", "System.Security.Cryptography.Xml": "4.7.1", "System.Security.Principal.Windows": "5.0.0" @@ -471,9 +494,9 @@ "Microsoft.Extensions.Configuration.FileExtensions": "3.1.0", "Microsoft.Extensions.DependencyInjection": "8.0.0", "Microsoft.Extensions.FileProviders.Physical": "3.1.0", - "Microsoft.Extensions.Hosting.Abstractions": "8.0.0", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", "Microsoft.Extensions.Logging": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0", + "Microsoft.Extensions.Options": "8.0.2", "System.Diagnostics.DiagnosticSource": "8.0.0", "System.Reflection.Metadata": "1.6.0" } @@ -482,7 +505,7 @@ "dependencies": { "Microsoft.AspNetCore.Hosting.Server.Abstractions": "2.2.0", "Microsoft.AspNetCore.Http.Abstractions": "2.2.0", - "Microsoft.Extensions.Hosting.Abstractions": "8.0.0" + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1" } }, "Microsoft.AspNetCore.Hosting.Server.Abstractions/2.2.0": { @@ -501,7 +524,7 @@ "Microsoft.AspNetCore.Http.Abstractions": "2.2.0", "Microsoft.AspNetCore.WebUtilities": "2.2.0", "Microsoft.Extensions.ObjectPool": "2.2.0", - "Microsoft.Extensions.Options": "8.0.0", + "Microsoft.Extensions.Options": "8.0.2", "Microsoft.Net.Http.Headers": "2.2.0" } }, @@ -540,8 +563,8 @@ "dependencies": { "Microsoft.AspNetCore.Http.Extensions": "2.2.0", "Microsoft.Extensions.Localization.Abstractions": "2.2.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0" + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.AspNetCore.Mvc/2.2.0": { @@ -587,7 +610,7 @@ "Microsoft.Extensions.DependencyInjection": "8.0.0", "Microsoft.Extensions.DependencyModel": "2.1.0", "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", "System.Diagnostics.DiagnosticSource": "8.0.0", "System.Threading.Tasks.Extensions": "4.5.4" } @@ -725,9 +748,9 @@ "dependencies": { "Microsoft.AspNetCore.Http.Extensions": "2.2.0", "Microsoft.AspNetCore.Routing.Abstractions": "2.2.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", "Microsoft.Extensions.ObjectPool": "2.2.0", - "Microsoft.Extensions.Options": "8.0.0" + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.AspNetCore.Routing.Abstractions/2.2.0": { @@ -773,7 +796,7 @@ "Microsoft.Extensions.Caching.Memory": "5.0.0", "Microsoft.Extensions.Configuration": "8.0.0", "Microsoft.Extensions.Logging": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", "Microsoft.IdentityModel.Validators": "6.32.0", "Newtonsoft.Json": "13.0.3", "System.IdentityModel.Tokens.Jwt": "6.35.0", @@ -823,7 +846,7 @@ "Microsoft.AspNetCore.Http": "2.2.2", "Microsoft.AspNetCore.Mvc": "2.2.0", "Microsoft.AspNetCore.Razor.Language": "2.2.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", "Newtonsoft.Json": "13.0.3", "System.ComponentModel.Annotations": "4.5.0", "System.Configuration.ConfigurationManager": "6.0.0", @@ -851,7 +874,7 @@ } }, "Microsoft.Azure.Functions.DotNetIsolatedNativeHost/1.0.12": {}, - "Microsoft.Azure.Functions.JavaWorker/2.19.0": {}, + "Microsoft.Azure.Functions.JavaWorker/2.19.2": {}, "Microsoft.Azure.Functions.NodeJsWorker/3.10.1": {}, "Microsoft.Azure.Functions.Platform.Metrics.LinuxConsumption/1.0.5": { "dependencies": { @@ -868,7 +891,7 @@ "Microsoft.Azure.Functions.PowerShellWorker.PS7.0/4.0.3148": {}, "Microsoft.Azure.Functions.PowerShellWorker.PS7.2/4.0.4025": {}, "Microsoft.Azure.Functions.PowerShellWorker.PS7.4/4.0.4206": {}, - "Microsoft.Azure.Functions.PythonWorker/4.37.0": {}, + "Microsoft.Azure.Functions.PythonWorker/4.38.0": {}, "Microsoft.Azure.KeyVault.Core/2.0.4": { "dependencies": { "System.Runtime": "4.3.1", @@ -915,7 +938,7 @@ "Microsoft.Extensions.Configuration.Json": "3.1.0", "Microsoft.Extensions.Hosting": "2.1.0", "Microsoft.Extensions.Logging": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", "Microsoft.Extensions.Logging.Configuration": "8.0.0", "Newtonsoft.Json": "13.0.3", "System.Memory.Data": "8.0.1", @@ -1008,7 +1031,7 @@ "Microsoft.AspNetCore.Http.Abstractions": "2.2.0", "Microsoft.AspNetCore.Http.Extensions": "2.2.0", "Microsoft.Azure.WebJobs": "3.0.41", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", "System.Diagnostics.TraceSource": "4.3.0", "System.Threading": "4.3.0", "System.Threading.Thread": "4.3.0" @@ -1325,9 +1348,9 @@ "Azure.Identity": "1.11.4", "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", "Microsoft.Extensions.Configuration.Binder": "8.0.1", - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" }, "runtime": { "lib/netstandard2.0/Microsoft.Extensions.Azure.dll": { @@ -1344,9 +1367,9 @@ "Microsoft.Extensions.Caching.Memory/5.0.0": { "dependencies": { "Microsoft.Extensions.Caching.Abstractions": "5.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2", "Microsoft.Extensions.Primitives": "8.0.0" } }, @@ -1391,10 +1414,17 @@ }, "Microsoft.Extensions.DependencyInjection/8.0.0": { "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions/8.0.2": { + "runtime": { + "lib/net8.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": { + "assemblyVersion": "8.0.0.0", + "fileVersion": "8.0.1024.46610" + } } }, - "Microsoft.Extensions.DependencyInjection.Abstractions/8.0.0": {}, "Microsoft.Extensions.DependencyModel/2.1.0": { "dependencies": { "Microsoft.DotNet.PlatformAbstractions": "2.1.0", @@ -1410,11 +1440,38 @@ } } }, - "Microsoft.Extensions.Diagnostics.Abstractions/8.0.0": { + "Microsoft.Extensions.Diagnostics.Abstractions/8.0.1": { "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0", - "System.Diagnostics.DiagnosticSource": "8.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" + }, + "runtime": { + "lib/net8.0/Microsoft.Extensions.Diagnostics.Abstractions.dll": { + "assemblyVersion": "8.0.0.0", + "fileVersion": "8.0.1024.46610" + } + } + }, + "Microsoft.Extensions.Diagnostics.HealthChecks/8.0.11": { + "dependencies": { + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.11", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" + }, + "runtime": { + "lib/net8.0/Microsoft.Extensions.Diagnostics.HealthChecks.dll": { + "assemblyVersion": "8.0.0.0", + "fileVersion": "8.0.1124.52116" + } + } + }, + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions/8.0.11": { + "runtime": { + "lib/net8.0/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.dll": { + "assemblyVersion": "8.0.0.0", + "fileVersion": "8.0.1124.52116" + } } }, "Microsoft.Extensions.FileProviders.Abstractions/8.0.0": { @@ -1439,45 +1496,57 @@ "Microsoft.Extensions.Configuration": "8.0.0", "Microsoft.Extensions.DependencyInjection": "8.0.0", "Microsoft.Extensions.FileProviders.Physical": "3.1.0", - "Microsoft.Extensions.Hosting.Abstractions": "8.0.0", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", "Microsoft.Extensions.Logging": "8.0.0" } }, - "Microsoft.Extensions.Hosting.Abstractions/8.0.0": { + "Microsoft.Extensions.Hosting.Abstractions/8.0.1": { "dependencies": { "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.1", "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0" + "Microsoft.Extensions.Logging.Abstractions": "8.0.2" + }, + "runtime": { + "lib/net8.0/Microsoft.Extensions.Hosting.Abstractions.dll": { + "assemblyVersion": "8.0.0.0", + "fileVersion": "8.0.1024.46610" + } } }, "Microsoft.Extensions.Http/3.0.3": { "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", "Microsoft.Extensions.Logging": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0" + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.Extensions.Localization/2.2.0": { "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", "Microsoft.Extensions.Localization.Abstractions": "2.2.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0" + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.Extensions.Localization.Abstractions/2.2.0": {}, "Microsoft.Extensions.Logging/8.0.0": { "dependencies": { "Microsoft.Extensions.DependencyInjection": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0" + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" } }, - "Microsoft.Extensions.Logging.Abstractions/8.0.0": { + "Microsoft.Extensions.Logging.Abstractions/8.0.2": { "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" + }, + "runtime": { + "lib/net8.0/Microsoft.Extensions.Logging.Abstractions.dll": { + "assemblyVersion": "8.0.0.0", + "fileVersion": "8.0.1024.46610" + } } }, "Microsoft.Extensions.Logging.ApplicationInsights/2.22.0": { @@ -1497,44 +1566,50 @@ "Microsoft.Extensions.Configuration": "8.0.0", "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", "Microsoft.Extensions.Configuration.Binder": "8.0.1", - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", "Microsoft.Extensions.Logging": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2", "Microsoft.Extensions.Options.ConfigurationExtensions": "8.0.0" } }, "Microsoft.Extensions.Logging.Console/8.0.0": { "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", "Microsoft.Extensions.Logging": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", "Microsoft.Extensions.Logging.Configuration": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0", + "Microsoft.Extensions.Options": "8.0.2", "System.Text.Json": "8.0.5" } }, "Microsoft.Extensions.ObjectPool/2.2.0": {}, - "Microsoft.Extensions.Options/8.0.0": { + "Microsoft.Extensions.Options/8.0.2": { "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", "Microsoft.Extensions.Primitives": "8.0.0" + }, + "runtime": { + "lib/net8.0/Microsoft.Extensions.Options.dll": { + "assemblyVersion": "8.0.0.0", + "fileVersion": "8.0.224.6711" + } } }, "Microsoft.Extensions.Options.ConfigurationExtensions/8.0.0": { "dependencies": { "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", "Microsoft.Extensions.Configuration.Binder": "8.0.1", - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2", "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Primitives/8.0.0": {}, "Microsoft.Extensions.WebEncoders/2.2.0": { "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2", "System.Text.Encodings.Web": "8.0.0" } }, @@ -1920,7 +1995,7 @@ }, "OpenTelemetry/1.9.0": { "dependencies": { - "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.0", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.1", "Microsoft.Extensions.Logging.Configuration": "8.0.0", "OpenTelemetry.Api.ProviderBuilderExtensions": "1.9.0" }, @@ -1944,7 +2019,7 @@ }, "OpenTelemetry.Api.ProviderBuilderExtensions/1.9.0": { "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", "OpenTelemetry.Api": "1.9.0" }, "runtime": { @@ -1983,7 +2058,7 @@ }, "OpenTelemetry.Extensions.Hosting/1.9.0": { "dependencies": { - "Microsoft.Extensions.Hosting.Abstractions": "8.0.0", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", "OpenTelemetry": "1.9.0" }, "runtime": { @@ -2007,7 +2082,7 @@ "OpenTelemetry.Instrumentation.Http/1.9.0": { "dependencies": { "Microsoft.Extensions.Configuration": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0", + "Microsoft.Extensions.Options": "8.0.2", "OpenTelemetry.Api.ProviderBuilderExtensions": "1.9.0" }, "runtime": { @@ -2911,7 +2986,7 @@ } } }, - "Microsoft.Azure.WebJobs.Script/4.1040.100-pr.25225.14": { + "Microsoft.Azure.WebJobs.Script/4.1042.100-pr.25364.8": { "dependencies": { "Azure.Core": "1.45.0", "Azure.Data.Tables": "12.8.3", @@ -2934,7 +3009,7 @@ "Microsoft.Azure.WebJobs.Script.Abstractions": "1.0.4-preview", "Microsoft.CodeAnalysis.CSharp.Scripting": "3.3.1", "Microsoft.Extensions.Azure": "1.7.1", - "Microsoft.Extensions.Hosting.Abstractions": "8.0.0", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", "Microsoft.Extensions.Logging.Console": "8.0.0", "Mono.Posix.NETStandard": "1.0.0", "Newtonsoft.Json": "13.0.3", @@ -2958,13 +3033,14 @@ "System.Text.Encodings.Web": "8.0.0", "System.Text.Json": "8.0.5", "System.Text.RegularExpressions": "4.3.1", - "System.Threading.Channels": "8.0.0" + "System.Threading.Channels": "8.0.0", + "Yarp.ReverseProxy": "2.0.1" }, "runtime": { "Microsoft.Azure.WebJobs.Script.dll": {} } }, - "Microsoft.Azure.WebJobs.Script.Grpc/4.1040.100-pr.25225.14": { + "Microsoft.Azure.WebJobs.Script.Grpc/4.1042.100-pr.25364.8": { "dependencies": { "Grpc.AspNetCore": "2.55.0", "Microsoft.ApplicationInsights": "2.22.0", @@ -2973,39 +3049,52 @@ "Microsoft.ApplicationInsights.WindowsServer": "2.22.0", "Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel": "2.22.0", "Microsoft.Azure.WebJobs.Rpc.Core": "3.0.37", - "Microsoft.Azure.WebJobs.Script": "4.1040.100-pr.25225.14", + "Microsoft.Azure.WebJobs.Script": "4.1042.100-pr.25364.8", "System.IO.FileSystem.Primitives": "4.3.0", - "System.Threading.Channels": "8.0.0", - "Yarp.ReverseProxy": "2.0.1" + "System.Threading.Channels": "8.0.0" }, "runtime": { "Microsoft.Azure.WebJobs.Script.Grpc.dll": {} } }, - "Microsoft.Azure.WebJobs.Script.Reference/4.1040.0.0": { + "Microsoft.Azure.WebJobs.Script.Reference/4.1042.0.0": { "runtime": { "Microsoft.Azure.WebJobs.Script.dll": { - "assemblyVersion": "4.1040.0.0", - "fileVersion": "4.1040.100.25225" + "assemblyVersion": "4.1042.0.0", + "fileVersion": "4.1042.100.25364" } } }, - "Microsoft.Azure.WebJobs.Script.Grpc.Reference/4.1040.0.0": { + "Microsoft.Azure.WebJobs.Script.Grpc.Reference/4.1042.0.0": { "runtime": { "Microsoft.Azure.WebJobs.Script.Grpc.dll": { - "assemblyVersion": "4.1040.0.0", - "fileVersion": "4.1040.100.25225" + "assemblyVersion": "4.1042.0.0", + "fileVersion": "4.1042.100.25364" } } } } }, "libraries": { - "Microsoft.Azure.WebJobs.Script.WebHost/4.1040.100-pr.25225.14": { + "Microsoft.Azure.WebJobs.Script.WebHost/4.1042.100-pr.25364.8": { "type": "project", "serviceable": false, "sha512": "" }, + "AspNetCore.HealthChecks.UI.Client/9.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-1Ub3Wvvbz7CMuFNWgLEc9qqQibiMoovDML/WHrwr5J83RPgtI20giCR92s/ipLgu7IIuqw+W/y7WpIeHqAICxg==", + "path": "aspnetcore.healthchecks.ui.client/9.0.0", + "hashPath": "aspnetcore.healthchecks.ui.client.9.0.0.nupkg.sha512" + }, + "AspNetCore.HealthChecks.UI.Core/9.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-TVriy4hgYnhfqz6NAzv8qe62Q8wf82iKUL6WV9selqeFZTq1ILi39Sic6sFQegRysvAVcnxKP/vY8z9Fk8x6XQ==", + "path": "aspnetcore.healthchecks.ui.core/9.0.0", + "hashPath": "aspnetcore.healthchecks.ui.core.9.0.0.nupkg.sha512" + }, "Autofac/4.6.2": { "type": "package", "serviceable": true, @@ -3573,12 +3662,12 @@ "path": "microsoft.azure.functions.dotnetisolatednativehost/1.0.12", "hashPath": "microsoft.azure.functions.dotnetisolatednativehost.1.0.12.nupkg.sha512" }, - "Microsoft.Azure.Functions.JavaWorker/2.19.0": { + "Microsoft.Azure.Functions.JavaWorker/2.19.2": { "type": "package", "serviceable": true, - "sha512": "sha512-sZbpZQQ+4CSmoKCwZTryI1oJ8rvU2TwvA5u4+Mn/VOt5C1wTZLSiRX2w7fCY34LF5PUhJsi++YzQg8+RGt0LdA==", - "path": "microsoft.azure.functions.javaworker/2.19.0", - "hashPath": "microsoft.azure.functions.javaworker.2.19.0.nupkg.sha512" + "sha512": "sha512-wxlvZ8c4rofeOY2d3CXvjQwI+HhGKZ/OmZpPu6iYm78hKsOhishO8TWeDQUISz077vgLQsqUG+a7EW769YBqqw==", + "path": "microsoft.azure.functions.javaworker/2.19.2", + "hashPath": "microsoft.azure.functions.javaworker.2.19.2.nupkg.sha512" }, "Microsoft.Azure.Functions.NodeJsWorker/3.10.1": { "type": "package", @@ -3615,12 +3704,12 @@ "path": "microsoft.azure.functions.powershellworker.ps7.4/4.0.4206", "hashPath": "microsoft.azure.functions.powershellworker.ps7.4.4.0.4206.nupkg.sha512" }, - "Microsoft.Azure.Functions.PythonWorker/4.37.0": { + "Microsoft.Azure.Functions.PythonWorker/4.38.0": { "type": "package", "serviceable": true, - "sha512": "sha512-QWKmhG7xBdDEDBwkXLT7idrKWAtj5w8dDnhB1g3GdFtVXgfJo7EVg14crNf3eBxAn7S7wdDeQj0sKU2HkkarhQ==", - "path": "microsoft.azure.functions.pythonworker/4.37.0", - "hashPath": "microsoft.azure.functions.pythonworker.4.37.0.nupkg.sha512" + "sha512": "sha512-EYInb3chrmhVADbgWxLkD6clfEYY00nqquKfxITuZi1tjZJ6PtflHAVwM4h6FzvIDR9nDL6/2Lhd+/P4uDc+kQ==", + "path": "microsoft.azure.functions.pythonworker/4.38.0", + "hashPath": "microsoft.azure.functions.pythonworker.4.38.0.nupkg.sha512" }, "Microsoft.Azure.KeyVault.Core/2.0.4": { "type": "package", @@ -3653,7 +3742,7 @@ "Microsoft.Azure.WebJobs.Core/3.0.41": { "type": "package", "serviceable": true, - "sha512": "sha512-nNW4I8m5GEhOxxD/NVZGjT6ZARGSy7wi8q+ihvKDin4IY4zYLpTy/GakZeGgbi7vPxcPHL5Z65n9DAV+goasqA==", + "sha512": "sha512-VtmzMUEMhhY7+lxhXxphPbn/1du+QFKU+VtH3UHzmn/sS2JwXJNtQNk/SiC+wad69GSGzgKH5x1aN2qTnEXh+Q==", "path": "microsoft.azure.webjobs.core/3.0.41", "hashPath": "microsoft.azure.webjobs.core.3.0.41.nupkg.sha512" }, @@ -3846,12 +3935,12 @@ "path": "microsoft.extensions.dependencyinjection/8.0.0", "hashPath": "microsoft.extensions.dependencyinjection.8.0.0.nupkg.sha512" }, - "Microsoft.Extensions.DependencyInjection.Abstractions/8.0.0": { + "Microsoft.Extensions.DependencyInjection.Abstractions/8.0.2": { "type": "package", "serviceable": true, - "sha512": "sha512-cjWrLkJXK0rs4zofsK4bSdg+jhDLTaxrkXu4gS6Y7MAlCvRyNNgwY/lJi5RDlQOnSZweHqoyvgvbdvQsRIW+hg==", - "path": "microsoft.extensions.dependencyinjection.abstractions/8.0.0", - "hashPath": "microsoft.extensions.dependencyinjection.abstractions.8.0.0.nupkg.sha512" + "sha512": "sha512-3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==", + "path": "microsoft.extensions.dependencyinjection.abstractions/8.0.2", + "hashPath": "microsoft.extensions.dependencyinjection.abstractions.8.0.2.nupkg.sha512" }, "Microsoft.Extensions.DependencyModel/2.1.0": { "type": "package", @@ -3860,12 +3949,26 @@ "path": "microsoft.extensions.dependencymodel/2.1.0", "hashPath": "microsoft.extensions.dependencymodel.2.1.0.nupkg.sha512" }, - "Microsoft.Extensions.Diagnostics.Abstractions/8.0.0": { + "Microsoft.Extensions.Diagnostics.Abstractions/8.0.1": { + "type": "package", + "serviceable": true, + "sha512": "sha512-elH2vmwNmsXuKmUeMQ4YW9ldXiF+gSGDgg1vORksob5POnpaI6caj1Hu8zaYbEuibhqCoWg0YRWDazBY3zjBfg==", + "path": "microsoft.extensions.diagnostics.abstractions/8.0.1", + "hashPath": "microsoft.extensions.diagnostics.abstractions.8.0.1.nupkg.sha512" + }, + "Microsoft.Extensions.Diagnostics.HealthChecks/8.0.11": { + "type": "package", + "serviceable": true, + "sha512": "sha512-zLgN22Zp9pk8RHlwssRTexw4+a6wqOnKWN+VejdPn5Yhjql4XiBhkFo35Nu8mmqHIk/UEmmCnMGLWq75aFfkOw==", + "path": "microsoft.extensions.diagnostics.healthchecks/8.0.11", + "hashPath": "microsoft.extensions.diagnostics.healthchecks.8.0.11.nupkg.sha512" + }, + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions/8.0.11": { "type": "package", "serviceable": true, - "sha512": "sha512-JHYCQG7HmugNYUhOl368g+NMxYE/N/AiclCYRNlgCY9eVyiBkOHMwK4x60RYMxv9EL3+rmj1mqHvdCiPpC+D4Q==", - "path": "microsoft.extensions.diagnostics.abstractions/8.0.0", - "hashPath": "microsoft.extensions.diagnostics.abstractions.8.0.0.nupkg.sha512" + "sha512": "sha512-So3JUdRxozRjvQ3cxU6F3nI/i4emDnjane6yMYcJhvTTTu29ltlIdoXjkFGRceIWz8yKvuEpzXItZ0x5GvN2nQ==", + "path": "microsoft.extensions.diagnostics.healthchecks.abstractions/8.0.11", + "hashPath": "microsoft.extensions.diagnostics.healthchecks.abstractions.8.0.11.nupkg.sha512" }, "Microsoft.Extensions.FileProviders.Abstractions/8.0.0": { "type": "package", @@ -3902,12 +4005,12 @@ "path": "microsoft.extensions.hosting/2.1.0", "hashPath": "microsoft.extensions.hosting.2.1.0.nupkg.sha512" }, - "Microsoft.Extensions.Hosting.Abstractions/8.0.0": { + "Microsoft.Extensions.Hosting.Abstractions/8.0.1": { "type": "package", "serviceable": true, - "sha512": "sha512-AG7HWwVRdCHlaA++1oKDxLsXIBxmDpMPb3VoyOoAghEWnkUvEAdYQUwnV4jJbAaa/nMYNiEh5ByoLauZBEiovg==", - "path": "microsoft.extensions.hosting.abstractions/8.0.0", - "hashPath": "microsoft.extensions.hosting.abstractions.8.0.0.nupkg.sha512" + "sha512": "sha512-nHwq9aPBdBPYXPti6wYEEfgXddfBrYC+CQLn+qISiwQq5tpfaqDZSKOJNxoe9rfQxGf1c+2wC/qWFe1QYJPYqw==", + "path": "microsoft.extensions.hosting.abstractions/8.0.1", + "hashPath": "microsoft.extensions.hosting.abstractions.8.0.1.nupkg.sha512" }, "Microsoft.Extensions.Http/3.0.3": { "type": "package", @@ -3937,12 +4040,12 @@ "path": "microsoft.extensions.logging/8.0.0", "hashPath": "microsoft.extensions.logging.8.0.0.nupkg.sha512" }, - "Microsoft.Extensions.Logging.Abstractions/8.0.0": { + "Microsoft.Extensions.Logging.Abstractions/8.0.2": { "type": "package", "serviceable": true, - "sha512": "sha512-arDBqTgFCyS0EvRV7O3MZturChstm50OJ0y9bDJvAcmEPJm0FFpFyjU/JLYyStNGGey081DvnQYlncNX5SJJGA==", - "path": "microsoft.extensions.logging.abstractions/8.0.0", - "hashPath": "microsoft.extensions.logging.abstractions.8.0.0.nupkg.sha512" + "sha512": "sha512-nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==", + "path": "microsoft.extensions.logging.abstractions/8.0.2", + "hashPath": "microsoft.extensions.logging.abstractions.8.0.2.nupkg.sha512" }, "Microsoft.Extensions.Logging.ApplicationInsights/2.22.0": { "type": "package", @@ -3972,12 +4075,12 @@ "path": "microsoft.extensions.objectpool/2.2.0", "hashPath": "microsoft.extensions.objectpool.2.2.0.nupkg.sha512" }, - "Microsoft.Extensions.Options/8.0.0": { + "Microsoft.Extensions.Options/8.0.2": { "type": "package", "serviceable": true, - "sha512": "sha512-JOVOfqpnqlVLUzINQ2fox8evY2SKLYJ3BV8QDe/Jyp21u1T7r45x/R/5QdteURMR5r01GxeJSBBUOCOyaNXA3g==", - "path": "microsoft.extensions.options/8.0.0", - "hashPath": "microsoft.extensions.options.8.0.0.nupkg.sha512" + "sha512": "sha512-dWGKvhFybsaZpGmzkGCbNNwBD1rVlWzrZKANLW/CcbFJpCEceMCGzT7zZwHOGBCbwM0SzBuceMj5HN1LKV1QqA==", + "path": "microsoft.extensions.options/8.0.2", + "hashPath": "microsoft.extensions.options.8.0.2.nupkg.sha512" }, "Microsoft.Extensions.Options.ConfigurationExtensions/8.0.0": { "type": "package", @@ -5022,22 +5125,22 @@ "path": "yarp.reverseproxy/2.0.1", "hashPath": "yarp.reverseproxy.2.0.1.nupkg.sha512" }, - "Microsoft.Azure.WebJobs.Script/4.1040.100-pr.25225.14": { + "Microsoft.Azure.WebJobs.Script/4.1042.100-pr.25364.8": { "type": "project", "serviceable": false, "sha512": "" }, - "Microsoft.Azure.WebJobs.Script.Grpc/4.1040.100-pr.25225.14": { + "Microsoft.Azure.WebJobs.Script.Grpc/4.1042.100-pr.25364.8": { "type": "project", "serviceable": false, "sha512": "" }, - "Microsoft.Azure.WebJobs.Script.Reference/4.1040.0.0": { + "Microsoft.Azure.WebJobs.Script.Reference/4.1042.0.0": { "type": "reference", "serviceable": false, "sha512": "" }, - "Microsoft.Azure.WebJobs.Script.Grpc.Reference/4.1040.0.0": { + "Microsoft.Azure.WebJobs.Script.Grpc.Reference/4.1042.0.0": { "type": "reference", "serviceable": false, "sha512": "" diff --git a/test/WebJobs.Script.Tests/WebJobs.Script.Tests.csproj b/test/WebJobs.Script.Tests/WebJobs.Script.Tests.csproj index 353a664a30..427e426762 100644 --- a/test/WebJobs.Script.Tests/WebJobs.Script.Tests.csproj +++ b/test/WebJobs.Script.Tests/WebJobs.Script.Tests.csproj @@ -27,7 +27,7 @@ - + diff --git a/tools/ExtensionsMetadataGenerator/test/ExtensionsMetadataGeneratorTests/ExistingRuntimeAssemblies.txt b/tools/ExtensionsMetadataGenerator/test/ExtensionsMetadataGeneratorTests/ExistingRuntimeAssemblies.txt index 7f8faf537c..77f6af7d93 100644 --- a/tools/ExtensionsMetadataGenerator/test/ExtensionsMetadataGeneratorTests/ExistingRuntimeAssemblies.txt +++ b/tools/ExtensionsMetadataGenerator/test/ExtensionsMetadataGeneratorTests/ExistingRuntimeAssemblies.txt @@ -98,6 +98,7 @@ Microsoft.CSharp.dll Microsoft.DotNet.PlatformAbstractions.dll Microsoft.Extensions.Caching.Abstractions.dll Microsoft.Extensions.Caching.Memory.dll +Microsoft.Extensions.Compliance.Abstractions.dll Microsoft.Extensions.Configuration.dll Microsoft.Extensions.Configuration.Abstractions.dll Microsoft.Extensions.Configuration.Binder.dll @@ -140,6 +141,7 @@ Microsoft.Extensions.Options.dll Microsoft.Extensions.Options.ConfigurationExtensions.dll Microsoft.Extensions.Options.DataAnnotations.dll Microsoft.Extensions.Primitives.dll +Microsoft.Extensions.Telemetry.Abstractions.dll Microsoft.Extensions.WebEncoders.dll Microsoft.IdentityModel.Logging.dll Microsoft.IdentityModel.Protocols.dll