Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaomi7732 committed Feb 13, 2023
2 parents 3f209ad + c41f74e commit 6e05e21
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,18 @@ public static partial class ApplicationInsightsExtensions
/// Enables Application Insights for Kubernetes on the Default TelemetryConfiguration in the dependency injection container with custom options.
/// </summary>
/// <param name="services">Collection of service descriptors.</param>
/// <param name="applyOptions">Action to customize the configuration of Application Insights for Kubernetes.</param>
/// <param name="diagnosticLogLevel">Sets the diagnostics log levels for the enricher.</param>
/// <param name="disableBackgroundService">If true, the Application Insights enricher library will not use any background (hosted) services,
/// and Kubernetes information will not be fetched automatically. Hosted services are not allowed in some environments, e.g. Azure Function.
/// For more information see https://github.com/Azure/azure-functions-host/issues/5447#issuecomment-575368316</param>
/// <param name="applyOptions">Action to customize the configuration of Application Insights for Kubernetes.</param>
/// <param name="clusterCheck">Provides a custom implementation to check whether it is inside kubernetes cluster or not.</param>
/// <returns>The service collection for chaining the next operation.</returns>
public static IServiceCollection AddApplicationInsightsKubernetesEnricher(
this IServiceCollection services,
Action<AppInsightsForKubernetesOptions>? applyOptions = default,
LogLevel? diagnosticLogLevel = LogLevel.None,
bool disableBackgroundService = false,
Action<AppInsightsForKubernetesOptions>? applyOptions = default,
IClusterEnvironmentCheck? clusterCheck = default)
{
diagnosticLogLevel ??= LogLevel.None; // Default to None.
Expand All @@ -37,7 +41,7 @@ public static IServiceCollection AddApplicationInsightsKubernetesEnricher(

if (!KubernetesTelemetryInitializerExists(services))
{
services.ConfigureKubernetesTelemetryInitializer(applyOptions, clusterCheck);
services.ConfigureKubernetesTelemetryInitializer(applyOptions, clusterCheck, disableBackgroundService);
}
return services;
}
Expand All @@ -48,7 +52,7 @@ public static IServiceCollection AddApplicationInsightsKubernetesEnricher(
public static void StartApplicationInsightsKubernetesEnricher(this IServiceProvider serviceProvider)
{
IK8sInfoBootstrap? k8sInfoBootstrap = serviceProvider.GetService<IK8sInfoBootstrap>();
if(k8sInfoBootstrap is null)
if (k8sInfoBootstrap is null)
{
_logger.LogInformation("No service registered by type {0}. Either not running in a Kubernetes cluster or `{1}()` wasn't called on the service collection.", nameof(IK8sInfoBootstrap), nameof(AddApplicationInsightsKubernetesEnricher));
return;
Expand All @@ -69,9 +73,10 @@ private static bool KubernetesTelemetryInitializerExists(IServiceCollection serv
internal static void ConfigureKubernetesTelemetryInitializer(
this IServiceCollection services,
Action<AppInsightsForKubernetesOptions>? overwriteOptions,
IClusterEnvironmentCheck? clusterCheck)
IClusterEnvironmentCheck? clusterCheck,
bool skipRegisterBackendService = false)
{
IKubernetesServiceCollectionBuilder kubernetesServiceCollectionBuilder = new KubernetesServiceCollectionBuilder(overwriteOptions, clusterCheck);
IKubernetesServiceCollectionBuilder kubernetesServiceCollectionBuilder = new KubernetesServiceCollectionBuilder(overwriteOptions, clusterCheck, skipRegisterBackendService);
_ = kubernetesServiceCollectionBuilder.RegisterServices(services);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ namespace Microsoft.Extensions.DependencyInjection;
internal class KubernetesServiceCollectionBuilder : IKubernetesServiceCollectionBuilder
{
private readonly IClusterEnvironmentCheck _clusterCheck;
private readonly bool _skipRegisterBackendService;
private readonly Action<AppInsightsForKubernetesOptions>? _customizeOptions;
private readonly ApplicationInsightsKubernetesDiagnosticSource _logger = ApplicationInsightsKubernetesDiagnosticSource.Instance;

Expand All @@ -29,10 +30,12 @@ internal class KubernetesServiceCollectionBuilder : IKubernetesServiceCollection
/// </param>
public KubernetesServiceCollectionBuilder(
Action<AppInsightsForKubernetesOptions>? customizeOptions,
IClusterEnvironmentCheck? clusterCheck)
IClusterEnvironmentCheck? clusterCheck,
bool skipRegisterBackendService)
{
_customizeOptions = customizeOptions;
_clusterCheck = clusterCheck ?? new ClusterEnvironmentCheck();
_skipRegisterBackendService = skipRegisterBackendService;
}

/// <summary>
Expand Down Expand Up @@ -142,7 +145,13 @@ protected virtual void RegisterK8sEnvironmentFactory(IServiceCollection serviceC
serviceCollection.TryAddScoped<IK8sEnvironmentFactory, K8sEnvironmentFactory>();
serviceCollection.TryAddSingleton<IK8sEnvironmentHolder>(_ => K8sEnvironmentHolder.Instance);

_logger.LogTrace("Registering bootstrap and hosted service.");
serviceCollection.TryAddSingleton<IK8sInfoBootstrap, K8sInfoBootstrap>();
serviceCollection.AddHostedService<K8sInfoBackgroundService>();
if (!_skipRegisterBackendService)
{
_logger.LogInformation("Skip registering {0} by user configuration.", nameof(K8sInfoBackgroundService));
serviceCollection.AddHostedService<K8sInfoBackgroundService>();
}
_logger.LogTrace("Registered bootstrap and hosted service.");
}
}
2 changes: 1 addition & 1 deletion src/ApplicationInsights.Kubernetes/IK8sInfoBootstrap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Microsoft.ApplicationInsights.Kubernetes;
/// The intention is for the client to have a handle to start getting Kubernetes info to be consumed by the <see cref="IK8sInfoService" />.
/// Remark: This is supposed to only be used in Console Application. Do NOT use this in ASP.NET or Worker, where the hosted service exists.
/// </summary>
internal interface IK8sInfoBootstrap
public interface IK8sInfoBootstrap
{
/// <summary>
/// Bootstrap the fetch of Kubernetes information.
Expand Down
8 changes: 4 additions & 4 deletions tests/UnitTests/AppInsightsKubernetesOptionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public void ShouldHaveDefaultOptions()

clusterCheck.Setup(c => c.IsInCluster).Returns(true);

KubernetesServiceCollectionBuilder builder = new KubernetesServiceCollectionBuilder(customizeOptions: default, clusterCheck.Object);
KubernetesServiceCollectionBuilder builder = new KubernetesServiceCollectionBuilder(customizeOptions: default, clusterCheck.Object, skipRegisterBackendService: false);

IServiceCollection services = new ServiceCollection();
IConfiguration configuration = (new ConfigurationBuilder()).Build();
Expand All @@ -44,7 +44,7 @@ public void ShouldTakeOptionFromIConfiguration()

clusterCheck.Setup(c => c.IsInCluster).Returns(true);

KubernetesServiceCollectionBuilder builder = new KubernetesServiceCollectionBuilder(customizeOptions: default, clusterCheck.Object);
KubernetesServiceCollectionBuilder builder = new KubernetesServiceCollectionBuilder(customizeOptions: default, clusterCheck.Object, skipRegisterBackendService: false);

IServiceCollection services = new ServiceCollection();
IConfiguration configuration = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary<string, string>()
Expand All @@ -71,7 +71,7 @@ public void ShouldTakeDelegateOverwriteForSettings()
KubernetesServiceCollectionBuilder builder = new KubernetesServiceCollectionBuilder(customizeOptions: opt =>
{
opt.InitializationTimeout = TimeSpan.FromSeconds(10); // The user settings through code will take precedence.
}, clusterCheck.Object);
}, clusterCheck.Object, skipRegisterBackendService: false);

IServiceCollection services = new ServiceCollection();
IConfiguration configuration = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary<string, string>()
Expand Down Expand Up @@ -101,7 +101,7 @@ public void ShouldAllowSetTelemetryKeyProcessorByCode()
KubernetesServiceCollectionBuilder builder = new KubernetesServiceCollectionBuilder(customizeOptions: opt =>
{
opt.TelemetryKeyProcessor = keyTransformer;
}, clusterCheck.Object);
}, clusterCheck.Object, skipRegisterBackendService: false);

IServiceCollection services = new ServiceCollection();
IConfiguration configuration = new ConfigurationBuilder().Build();
Expand Down
35 changes: 32 additions & 3 deletions tests/UnitTests/ApplicationInsightsExtensionsTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using System;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;

namespace Microsoft.ApplicationInsights.Kubernetes.Tests;
Expand Down Expand Up @@ -29,7 +32,7 @@ public void ShouldAllowOverwriteOptions()
IServiceCollection collection = new ServiceCollection();

// If there's compile error, check if the signature of AddApplicationInsightsKubernetesEnricher was changed.
collection = collection.AddApplicationInsightsKubernetesEnricher(opt => opt.InitializationTimeout = TimeSpan.FromMinutes(15));
collection = collection.AddApplicationInsightsKubernetesEnricher(applyOptions: opt => opt.InitializationTimeout = TimeSpan.FromMinutes(15));

Assert.NotNull(collection);
}
Expand All @@ -52,9 +55,35 @@ public void ShouldAllowOverwritingOptionsAndDiagnosticLoggingLevel()

// If there's compile error, check if the signature of AddApplicationInsightsKubernetesEnricher was changed.
collection = collection.AddApplicationInsightsKubernetesEnricher(
opt => opt.InitializationTimeout = TimeSpan.FromMinutes(15),
diagnosticLogLevel: LogLevel.Trace);
diagnosticLogLevel: LogLevel.Trace,
applyOptions: opt => opt.InitializationTimeout = TimeSpan.FromMinutes(15));

Assert.NotNull(collection);
}

[Theory]
[InlineData(false, true)]
[InlineData(true, false)]
public void ShouldNotRegisterHostedServiceWhenSet(bool disableBackgroundService, bool expectServiceRegistered)
{
IServiceCollection collection = new ServiceCollection();

Mock<IClusterEnvironmentCheck> clusterCheck = new();
clusterCheck.Setup(c => c.IsInCluster).Returns(true);

// If there's compile error, check if the signature of AddApplicationInsightsKubernetesEnricher was changed.
collection = collection.AddApplicationInsightsKubernetesEnricher(disableBackgroundService: disableBackgroundService, clusterCheck: clusterCheck.Object);

Assert.NotNull(collection);
bool registered = collection.Any(serviceDescriptor => serviceDescriptor.ServiceType == typeof(IHostedService));

if (expectServiceRegistered)
{
Assert.True(registered);
}
else
{
Assert.False(registered);
}
}
}
2 changes: 1 addition & 1 deletion tests/UnitTests/KubernetesEnablementTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public void ServicesRegistered()

clusterCheckMock.Setup(c => c.IsInCluster).Returns(true);

KubernetesServiceCollectionBuilder target = new KubernetesServiceCollectionBuilder(customizeOptions: null, clusterCheckMock.Object);
KubernetesServiceCollectionBuilder target = new KubernetesServiceCollectionBuilder(customizeOptions: null, clusterCheckMock.Object, skipRegisterBackendService: false);
target.RegisterServices(services);

Assert.NotNull(services.FirstOrDefault(sd => sd.ImplementationType == typeof(KubernetesTelemetryInitializer)));
Expand Down

0 comments on commit 6e05e21

Please sign in to comment.