Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose AddTcpEndpointProbe #4892

Merged
merged 3 commits into from
Jan 20, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Microsoft.Extensions.Diagnostics.Probes;
/// <summary>
/// Options for Kubernetes probes.
/// </summary>
public partial class KubernetesProbesOptions
public class KubernetesProbesOptions
{
private const int DefaultLivenessProbePort = 2305;
private const int DefaultStartupProbePort = 2306;
Expand All @@ -18,7 +18,7 @@ public partial class KubernetesProbesOptions
/// <remarks>
/// Default port is 2305.
/// </remarks>
public EndpointOptions LivenessProbe { get; set; } = new EndpointOptions
public TcpEndpointProbesOptions LivenessProbe { get; set; } = new TcpEndpointProbesOptions
{
TcpPort = DefaultLivenessProbePort,
};
Expand All @@ -29,7 +29,7 @@ public partial class KubernetesProbesOptions
/// <remarks>
/// Default port is 2306.
/// </remarks>
public EndpointOptions StartupProbe { get; set; } = new EndpointOptions
public TcpEndpointProbesOptions StartupProbe { get; set; } = new TcpEndpointProbesOptions
{
TcpPort = DefaultStartupProbePort,
};
Expand All @@ -40,7 +40,7 @@ public partial class KubernetesProbesOptions
/// <remarks>
/// Default port is 2307.
/// </remarks>
public EndpointOptions ReadinessProbe { get; set; } = new EndpointOptions
public TcpEndpointProbesOptions ReadinessProbe { get; set; } = new TcpEndpointProbesOptions
{
TcpPort = DefaultReadinessProbePort,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,25 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Diagnostics.Probes;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Microsoft.Shared.Diagnostics;

namespace Microsoft.Extensions.Diagnostics.Probes;
namespace Microsoft.Extensions.DependencyInjection;

/// <summary>
/// Extension methods for <see cref="TcpEndpointHealthCheckService" /> for <see cref="IServiceCollection" />.
/// Extension methods for setting up TCP-based health check probes.
/// </summary>
internal static class TcpEndpointHealthCheckExtensions
public static class TcpEndpointProbesExtensions
{
/// <summary>
/// Registers health status reporting using a TCP port
/// if service is considered as healthy <see cref="IHealthCheck"/>.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection" /> to add the services to.</param>
/// <returns>The value of <paramref name="services"/>.</returns>
internal static IServiceCollection AddTcpEndpointHealthCheck(this IServiceCollection services)
public static IServiceCollection AddTcpEndpointHealthCheck(this IServiceCollection services)
{
_ = Throw.IfNull(services);

Expand All @@ -36,19 +37,19 @@ internal static IServiceCollection AddTcpEndpointHealthCheck(this IServiceCollec
/// <param name="services">The <see cref="IServiceCollection" /> to add the services to.</param>
/// <param name="name">Name used to retrieve the options.</param>
/// <returns>The value of <paramref name="services"/>.</returns>
internal static IServiceCollection AddTcpEndpointHealthCheck(this IServiceCollection services, string name)
public static IServiceCollection AddTcpEndpointHealthCheck(this IServiceCollection services, string name)
joegoldman2 marked this conversation as resolved.
Show resolved Hide resolved
{
_ = Throw.IfNull(services);

_ = services.AddHealthChecks();

_ = services
.AddOptionsWithValidateOnStart<KubernetesProbesOptions.EndpointOptions, EndpointOptionsValidator>(name);
.AddOptionsWithValidateOnStart<TcpEndpointProbesOptions, TcpEndpointProbesOptionsValidator>(name);

_ = services.AddSingleton<IHostedService>(provider =>
{
var options = provider.GetRequiredService<IOptionsMonitor<KubernetesProbesOptions.EndpointOptions>>().Get(name);
return ActivatorUtilities.CreateInstance<TcpEndpointHealthCheckService>(provider, options);
var options = provider.GetRequiredService<IOptionsMonitor<TcpEndpointProbesOptions>>().Get(name);
return ActivatorUtilities.CreateInstance<TcpEndpointProbesService>(provider, options);
});

return services;
Expand All @@ -59,11 +60,11 @@ internal static IServiceCollection AddTcpEndpointHealthCheck(this IServiceCollec
/// if service is considered as healthy <see cref="IHealthCheck"/>.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection" /> to add the services to.</param>
/// <param name="configure">Configuration for <see cref="KubernetesProbesOptions.EndpointOptions"/>.</param>
/// <param name="configure">Configuration for <see cref="TcpEndpointProbesOptions"/>.</param>
/// <returns>The value of <paramref name="services"/>.</returns>
internal static IServiceCollection AddTcpEndpointHealthCheck(
public static IServiceCollection AddTcpEndpointHealthCheck(
this IServiceCollection services,
Action<KubernetesProbesOptions.EndpointOptions> configure)
Action<TcpEndpointProbesOptions> configure)
{
_ = Throw.IfNull(services);
_ = Throw.IfNull(configure);
Expand All @@ -79,12 +80,12 @@ internal static IServiceCollection AddTcpEndpointHealthCheck(
/// </summary>
/// <param name="services">The <see cref="IServiceCollection" /> to add the services to.</param>
/// <param name="name">Name for the options.</param>
/// <param name="configure">Configuration for <see cref="KubernetesProbesOptions.EndpointOptions"/>.</param>
/// <param name="configure">Configuration for <see cref="TcpEndpointProbesOptions"/>.</param>
/// <returns>The value of <paramref name="services"/>.</returns>
internal static IServiceCollection AddTcpEndpointHealthCheck(
public static IServiceCollection AddTcpEndpointHealthCheck(
this IServiceCollection services,
string name,
Action<KubernetesProbesOptions.EndpointOptions> configure)
Action<TcpEndpointProbesOptions> configure)
{
_ = Throw.IfNull(services);
_ = Throw.IfNull(configure);
Expand All @@ -99,16 +100,16 @@ internal static IServiceCollection AddTcpEndpointHealthCheck(
/// if service is considered as healthy <see cref="IHealthCheck"/>.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection" /> to add the services to.</param>
/// <param name="configurationSection">Configuration for <see cref="KubernetesProbesOptions.EndpointOptions"/>.</param>
/// <param name="configurationSection">Configuration for <see cref="TcpEndpointProbesOptions"/>.</param>
/// <returns>The value of <paramref name="services"/>.</returns>
internal static IServiceCollection AddTcpEndpointHealthCheck(
public static IServiceCollection AddTcpEndpointHealthCheck(
this IServiceCollection services,
IConfigurationSection configurationSection)
{
_ = Throw.IfNull(services);
_ = Throw.IfNull(configurationSection);

_ = services.Configure<KubernetesProbesOptions.EndpointOptions>(configurationSection);
_ = services.Configure<TcpEndpointProbesOptions>(configurationSection);

return services.AddTcpEndpointHealthCheck();
}
Expand All @@ -119,17 +120,17 @@ internal static IServiceCollection AddTcpEndpointHealthCheck(
/// </summary>
/// <param name="services">The <see cref="IServiceCollection" /> to add the services to.</param>
/// <param name="name">Name for the options.</param>
/// <param name="configurationSection">Configuration for <see cref="KubernetesProbesOptions.EndpointOptions"/>.</param>
/// <param name="configurationSection">Configuration for <see cref="TcpEndpointProbesOptions"/>.</param>
/// <returns>The value of <paramref name="services"/>.</returns>
internal static IServiceCollection AddTcpEndpointHealthCheck(
public static IServiceCollection AddTcpEndpointHealthCheck(
this IServiceCollection services,
string name,
IConfigurationSection configurationSection)
{
_ = Throw.IfNull(services);
_ = Throw.IfNull(configurationSection);

_ = services.Configure<KubernetesProbesOptions.EndpointOptions>(name, configurationSection);
_ = services.Configure<TcpEndpointProbesOptions>(name, configurationSection);

return services.AddTcpEndpointHealthCheck(name);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Shared.Data.Validation;

namespace Microsoft.Extensions.Diagnostics.Probes;

/// <summary>
/// Options to control TCP-based health check probes.
/// </summary>
[SuppressMessage("Major Code Smell", "S109:Magic numbers should not be used", Justification = "In place numbers make the ranges cleaner")]
public class TcpEndpointProbesOptions
{
private const int DefaultMaxPendingConnections = 10;
private const int DefaultTcpPort = 2305;

/// <summary>
/// Gets or sets the TCP port that gets opened if the service is healthy and closed otherwise.
/// </summary>
/// <value>
/// The default value is 2305.
/// </value>
[Range(1, 65535)]
public int TcpPort { get; set; } = DefaultTcpPort;

/// <summary>
/// Gets or sets the maximum length of the pending connections queue.
/// </summary>
/// <value>
/// The default value is 10.
/// </value>
[Range(1, 10000)]
public int MaxPendingConnections { get; set; } = DefaultMaxPendingConnections;

/// <summary>
/// Gets or sets the interval at which the health of the application is assessed.
/// </summary>
/// <value>
/// The default value is 30 seconds.
/// </value>
[TimeSpan("00:00:05", "00:05:00")]
public TimeSpan HealthAssessmentPeriod { get; set; } = TimeSpan.FromSeconds(30);

/// <summary>
/// Gets or sets a predicate that is used to include health checks based on user-defined criteria.
/// </summary>
/// <value>
/// The default value is <see langword="null" />, which has the effect of enabling all health checks.
/// </value>
public Func<HealthCheckRegistration, bool>? FilterChecks { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
namespace Microsoft.Extensions.Diagnostics.Probes;

[OptionsValidator]
internal sealed partial class EndpointOptionsValidator : IValidateOptions<KubernetesProbesOptions.EndpointOptions>
internal sealed partial class TcpEndpointProbesOptionsValidator : IValidateOptions<TcpEndpointProbesOptions>
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,18 @@ namespace Microsoft.Extensions.Diagnostics.Probes;
/// <summary>
/// Opens a TCP port if the service is healthy and closes it otherwise.
/// </summary>
internal sealed class TcpEndpointHealthCheckService : BackgroundService
internal sealed class TcpEndpointProbesService : BackgroundService
{
internal TimeProvider TimeProvider { get; set; } = TimeProvider.System;

private readonly ILogger<TcpEndpointHealthCheckService> _logger;
private readonly ILogger<TcpEndpointProbesService> _logger;
private readonly HealthCheckService _healthCheckService;
private readonly KubernetesProbesOptions.EndpointOptions _options;
#pragma warning disable CA2213 // 'TcpEndpointHealthCheckService' contains field '_listener' that is of IDisposable type 'TcpListener'
private readonly TcpEndpointProbesOptions _options;
#pragma warning disable CA2213 // 'TcpEndpointProbesService' contains field '_listener' that is of IDisposable type 'TcpListener'
private readonly TcpListener _listener;
#pragma warning restore CA2213

public TcpEndpointHealthCheckService(ILogger<TcpEndpointHealthCheckService> logger, HealthCheckService healthCheckService, KubernetesProbesOptions.EndpointOptions options)
public TcpEndpointProbesService(ILogger<TcpEndpointProbesService> logger, HealthCheckService healthCheckService, TcpEndpointProbesOptions options)
{
_logger = logger;
_healthCheckService = healthCheckService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ public void AddKubernetesProbes_RegistersAllProbes()
services.AddKubernetesProbes().AddHealthChecks();
});

var hostedServices = host.Services.GetServices<IHostedService>().Where(service => service.GetType().Name == "TcpEndpointHealthCheckService");
var configurations = host.Services.GetServices<IOptionsMonitor<KubernetesProbesOptions.EndpointOptions>>();
var hostedServices = host.Services.GetServices<IHostedService>().Where(service => service.GetType().Name == "TcpEndpointProbesService");
var configurations = host.Services.GetServices<IOptionsMonitor<TcpEndpointProbesOptions>>();

Assert.Equal(3, hostedServices.Count());
Assert.Single(configurations);
Expand Down Expand Up @@ -74,8 +74,8 @@ public void AddKubernetesProbes_WithConfigureAction_RegistersAllProbes()
}).AddHealthChecks();
});

var hostedServices = host.Services.GetServices<IHostedService>().Where(service => service.GetType().Name == "TcpEndpointHealthCheckService");
var configurations = host.Services.GetServices<IOptionsMonitor<KubernetesProbesOptions.EndpointOptions>>();
var hostedServices = host.Services.GetServices<IHostedService>().Where(service => service.GetType().Name == "TcpEndpointProbesService");
var configurations = host.Services.GetServices<IOptionsMonitor<TcpEndpointProbesOptions>>();

Assert.Equal(3, hostedServices.Count());
Assert.Single(configurations);
Expand All @@ -102,8 +102,8 @@ public void AddKubernetesProbes_WithConfigurationSection_RegistersAllProbes()
services.AddKubernetesProbes(configuration.GetSection("KubernetesProbes")).AddHealthChecks();
});

var hostedServices = host.Services.GetServices<IHostedService>().Where(service => service.GetType().Name == "TcpEndpointHealthCheckService");
var configurations = host.Services.GetServices<IOptionsMonitor<KubernetesProbesOptions.EndpointOptions>>();
var hostedServices = host.Services.GetServices<IHostedService>().Where(service => service.GetType().Name == "TcpEndpointProbesService");
var configurations = host.Services.GetServices<IOptionsMonitor<TcpEndpointProbesOptions>>();

Assert.Equal(3, hostedServices.Count());
Assert.Single(configurations);
Expand Down
Loading