Add another couple of constructors to Fix #279 (#302)

* Add support for HttpClientFactory with KubernetesClientConfiguration

* Add an example with http client factory

* ensure sample uses the latest version of C#
This commit is contained in:
David Parks
2019-09-23 21:33:25 -07:00
committed by Kubernetes Prow Robot
parent fcd8c166da
commit e00f67abd4
5 changed files with 192 additions and 71 deletions

View File

@@ -0,0 +1,43 @@
using k8s;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace httpClientFactory
{
// Learn more about IHostedServices at https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-2.2&tabs=visual-studio
internal class PodListHostedService : IHostedService
{
private readonly IKubernetes _kubernetesClient;
private readonly ILogger<PodListHostedService> _logger;
public PodListHostedService(IKubernetes kubernetesClient, ILogger<PodListHostedService> logger)
{
_kubernetesClient = kubernetesClient ?? throw new ArgumentNullException(nameof(kubernetesClient));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public async Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Starting Request!");
var list = await _kubernetesClient.ListNamespacedPodAsync("default", cancellationToken: cancellationToken);
foreach (var item in list.Items)
{
_logger.LogInformation(item.Metadata.Name);
}
if (list.Items.Count == 0)
{
_logger.LogInformation("Empty!");
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
// Nothing to stop
return Task.CompletedTask;
}
}
}

View File

@@ -0,0 +1,46 @@
using k8s;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using System.Threading.Tasks;
namespace httpClientFactory
{
internal class Program
{
public static async Task Main(string[] args)
{
// Learn more about generic hosts at https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host
using (var host = new HostBuilder()
.ConfigureLogging((logging) =>
{
logging.AddConsole();
})
.ConfigureServices((hostBuilderContext, services) =>
{
// Ideally this config would be read from the .net core config constructs,
// but that has not been implemented in the KubernetesClient library at
// the time this sample was created.
var config = KubernetesClientConfiguration.BuildDefaultConfig();
services.AddSingleton(config);
// Setup the http client
services.AddHttpClient("K8s")
.AddTypedClient<IKubernetes>((httpClient, serviceProvider) =>
{
return new Kubernetes(
serviceProvider.GetRequiredService<KubernetesClientConfiguration>(),
httpClient);
});
// Add the class that uses the client
services.AddHostedService<PodListHostedService>();
})
.Build())
{
await host.StartAsync().ConfigureAwait(false);
await host.StopAsync().ConfigureAwait(false);
}
}
}
}

View File

@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.2</TargetFramework>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Http" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.2.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\KubernetesClient\KubernetesClient.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,7 +1,7 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15 # Visual Studio Version 16
VisualStudioVersion = 15.0.26430.16 VisualStudioVersion = 16.0.29230.47
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{B70AFB57-57C9-46DC-84BE-11B7DDD34B40}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{B70AFB57-57C9-46DC-84BE-11B7DDD34B40}"
EndProject EndProject
@@ -31,7 +31,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{879F8787-C3B
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KubernetesWatchGenerator", "gen\KubernetesWatchGenerator\KubernetesWatchGenerator.csproj", "{542DC30E-FDF7-4A35-B026-6C21F435E8B1}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KubernetesWatchGenerator", "gen\KubernetesWatchGenerator\KubernetesWatchGenerator.csproj", "{542DC30E-FDF7-4A35-B026-6C21F435E8B1}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "patch", "examples\patch\patch.csproj", "{04DE2C84-117D-4E21-8B45-B7AE627697BD}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "patch", "examples\patch\patch.csproj", "{04DE2C84-117D-4E21-8B45-B7AE627697BD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "httpClientFactory", "examples\httpClientFactory\httpClientFactory.csproj", "{A07314A0-02E8-4F36-B233-726D59D28F08}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -175,6 +177,18 @@ Global
{04DE2C84-117D-4E21-8B45-B7AE627697BD}.Release|x64.Build.0 = Release|Any CPU {04DE2C84-117D-4E21-8B45-B7AE627697BD}.Release|x64.Build.0 = Release|Any CPU
{04DE2C84-117D-4E21-8B45-B7AE627697BD}.Release|x86.ActiveCfg = Release|Any CPU {04DE2C84-117D-4E21-8B45-B7AE627697BD}.Release|x86.ActiveCfg = Release|Any CPU
{04DE2C84-117D-4E21-8B45-B7AE627697BD}.Release|x86.Build.0 = Release|Any CPU {04DE2C84-117D-4E21-8B45-B7AE627697BD}.Release|x86.Build.0 = Release|Any CPU
{A07314A0-02E8-4F36-B233-726D59D28F08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A07314A0-02E8-4F36-B233-726D59D28F08}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A07314A0-02E8-4F36-B233-726D59D28F08}.Debug|x64.ActiveCfg = Debug|Any CPU
{A07314A0-02E8-4F36-B233-726D59D28F08}.Debug|x64.Build.0 = Debug|Any CPU
{A07314A0-02E8-4F36-B233-726D59D28F08}.Debug|x86.ActiveCfg = Debug|Any CPU
{A07314A0-02E8-4F36-B233-726D59D28F08}.Debug|x86.Build.0 = Debug|Any CPU
{A07314A0-02E8-4F36-B233-726D59D28F08}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A07314A0-02E8-4F36-B233-726D59D28F08}.Release|Any CPU.Build.0 = Release|Any CPU
{A07314A0-02E8-4F36-B233-726D59D28F08}.Release|x64.ActiveCfg = Release|Any CPU
{A07314A0-02E8-4F36-B233-726D59D28F08}.Release|x64.Build.0 = Release|Any CPU
{A07314A0-02E8-4F36-B233-726D59D28F08}.Release|x86.ActiveCfg = Release|Any CPU
{A07314A0-02E8-4F36-B233-726D59D28F08}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@@ -191,6 +205,7 @@ Global
{806AD0E5-833F-42FB-A870-4BCEE7F4B17F} = {8AF4A5C2-F0CE-47D5-A4C5-FE4AB83CA509} {806AD0E5-833F-42FB-A870-4BCEE7F4B17F} = {8AF4A5C2-F0CE-47D5-A4C5-FE4AB83CA509}
{542DC30E-FDF7-4A35-B026-6C21F435E8B1} = {879F8787-C3BB-43F3-A92D-6D4C7D3A5285} {542DC30E-FDF7-4A35-B026-6C21F435E8B1} = {879F8787-C3BB-43F3-A92D-6D4C7D3A5285}
{04DE2C84-117D-4E21-8B45-B7AE627697BD} = {B70AFB57-57C9-46DC-84BE-11B7DDD34B40} {04DE2C84-117D-4E21-8B45-B7AE627697BD} = {B70AFB57-57C9-46DC-84BE-11B7DDD34B40}
{A07314A0-02E8-4F36-B233-726D59D28F08} = {B70AFB57-57C9-46DC-84BE-11B7DDD34B40}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {049A763A-C891-4E8D-80CF-89DD3E22ADC7} SolutionGuid = {049A763A-C891-4E8D-80CF-89DD3E22ADC7}

View File

@@ -13,84 +13,68 @@ namespace k8s
{ {
public partial class Kubernetes public partial class Kubernetes
{ {
#if MONOANDROID8_1
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Kubernetes" /> class. /// Initializes a new instance of the <see cref="Kubernetes" /> class.
/// </summary> /// </summary>
/// <param name='config'> /// <param name='config'>
/// Optional. The delegating handlers to add to the http client pipeline. /// The kube config to use.
/// </param> /// </param>
/// <param name="handlers"> /// <param name="httpClient">
/// Optional. The delegating handlers to add to the http client pipeline. /// The <see cref="HttpClient" /> to use for all requests.
/// </param> /// </param>
public Kubernetes(KubernetesClientConfiguration config, params DelegatingHandler[] handlers) : this(new Xamarin.Android.Net.AndroidClientHandler(), handlers) public Kubernetes(KubernetesClientConfiguration config, HttpClient httpClient) : this(config, httpClient, false)
{ {
if (string.IsNullOrWhiteSpace(config.Host))
{
throw new KubeConfigException("Host url must be set");
}
try
{
BaseUri = new Uri(config.Host);
}
catch (UriFormatException e)
{
throw new KubeConfigException("Bad host url", e);
}
CaCerts = config.SslCaCerts;
SkipTlsVerify = config.SkipTlsVerify;
if (BaseUri.Scheme == "https")
{
if (config.SkipTlsVerify)
{
System.Net.ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) =>
{
return true;
};
}
else
{
if (CaCerts == null)
{
throw new KubeConfigException("a CA must be set when SkipTlsVerify === false");
}
var certList = new System.Collections.Generic.List<Java.Security.Cert.Certificate>();
foreach (X509Certificate2 caCert in CaCerts)
{
using (System.IO.MemoryStream certStream = new System.IO.MemoryStream(caCert.RawData))
{
Java.Security.Cert.Certificate cert = Java.Security.Cert.CertificateFactory.GetInstance("X509").GenerateCertificate(certStream);
certList.Add(cert);
}
}
Xamarin.Android.Net.AndroidClientHandler handler = (Xamarin.Android.Net.AndroidClientHandler)this.HttpClientHandler;
handler.TrustedCerts = certList;
}
}
// set credentails for the kubernernet client
SetCredentials(config, HttpClientHandler);
} }
#else
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Kubernetes" /> class. /// Initializes a new instance of the <see cref="Kubernetes" /> class.
/// </summary> /// </summary>
/// <param name='config'> /// <param name='config'>
/// Optional. The delegating handlers to add to the http client pipeline. /// The kube config to use.
/// </param>
/// <param name="httpClient">
/// The <see cref="HttpClient" /> to use for all requests.
/// </param>
/// <param name="disposeHttpClient">
/// Whether or not the <see cref="Kubernetes"/> object should own the lifetime of <paramref name="httpClient"/>.
/// </param>
public Kubernetes(KubernetesClientConfiguration config, HttpClient httpClient, bool disposeHttpClient) : this(httpClient, disposeHttpClient)
{
ValidateConfig(config);
CaCerts = config.SslCaCerts;
SkipTlsVerify = config.SkipTlsVerify;
InitializeFromConfig(config);
}
/// <summary>
/// Initializes a new instance of the <see cref="Kubernetes" /> class.
/// </summary>
/// <param name='config'>
/// The kube config to use.
/// </param> /// </param>
/// <param name="handlers"> /// <param name="handlers">
/// Optional. The delegating handlers to add to the http client pipeline. /// Optional. The delegating handlers to add to the http client pipeline.
/// </param> /// </param>
public Kubernetes(KubernetesClientConfiguration config, params DelegatingHandler[] handlers) : this(handlers) public Kubernetes(KubernetesClientConfiguration config, params DelegatingHandler[] handlers)
#if MONOANDROID8_1
: this(new Xamarin.Android.Net.AndroidClientHandler(), handlers)
#else
: this(handlers)
#endif
{ {
ValidateConfig(config);
CaCerts = config.SslCaCerts;
SkipTlsVerify = config.SkipTlsVerify;
InitializeFromConfig(config);
}
private void ValidateConfig(KubernetesClientConfiguration config)
{
if (config == null)
{
throw new KubeConfigException("KubeConfig must be provided");
}
if (string.IsNullOrWhiteSpace(config.Host)) if (string.IsNullOrWhiteSpace(config.Host))
{ {
throw new KubeConfigException("Host url must be set"); throw new KubeConfigException("Host url must be set");
@@ -104,10 +88,10 @@ namespace k8s
{ {
throw new KubeConfigException("Bad host url", e); throw new KubeConfigException("Bad host url", e);
} }
}
CaCerts = config.SslCaCerts; private void InitializeFromConfig(KubernetesClientConfiguration config)
SkipTlsVerify = config.SkipTlsVerify; {
if (BaseUri.Scheme == "https") if (BaseUri.Scheme == "https")
{ {
if (config.SkipTlsVerify) if (config.SkipTlsVerify)
@@ -115,7 +99,7 @@ namespace k8s
#if NET452 #if NET452
((WebRequestHandler) HttpClientHandler).ServerCertificateValidationCallback = ((WebRequestHandler) HttpClientHandler).ServerCertificateValidationCallback =
(sender, certificate, chain, sslPolicyErrors) => true; (sender, certificate, chain, sslPolicyErrors) => true;
#elif XAMARINIOS1_0 #elif XAMARINIOS1_0 || MONOANDROID8_1
System.Net.ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => System.Net.ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) =>
{ {
return true; return true;
@@ -129,9 +113,8 @@ namespace k8s
{ {
if (CaCerts == null) if (CaCerts == null)
{ {
throw new KubeConfigException("a CA must be set when SkipTlsVerify === false"); throw new KubeConfigException("A CA must be set when SkipTlsVerify === false");
} }
#if NET452 #if NET452
((WebRequestHandler) HttpClientHandler).ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => ((WebRequestHandler) HttpClientHandler).ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
{ {
@@ -143,6 +126,22 @@ namespace k8s
var cert = new X509Certificate2(certificate); var cert = new X509Certificate2(certificate);
return Kubernetes.CertificateValidationCallBack(sender, CaCerts, cert, chain, sslPolicyErrors); return Kubernetes.CertificateValidationCallBack(sender, CaCerts, cert, chain, sslPolicyErrors);
}; };
#elif MONOANDROID8_1
var certList = new System.Collections.Generic.List<Java.Security.Cert.Certificate>();
foreach (X509Certificate2 caCert in CaCerts)
{
using (var certStream = new System.IO.MemoryStream(caCert.RawData))
{
Java.Security.Cert.Certificate cert = Java.Security.Cert.CertificateFactory.GetInstance("X509").GenerateCertificate(certStream);
certList.Add(cert);
}
}
var handler = (Xamarin.Android.Net.AndroidClientHandler)this.HttpClientHandler;
handler.TrustedCerts = certList;
#else #else
HttpClientHandler.ServerCertificateCustomValidationCallback = (sender, certificate, chain, sslPolicyErrors) => HttpClientHandler.ServerCertificateCustomValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
{ {
@@ -155,7 +154,6 @@ namespace k8s
// set credentails for the kubernernet client // set credentails for the kubernernet client
SetCredentials(config, HttpClientHandler); SetCredentials(config, HttpClientHandler);
} }
#endif
private X509Certificate2Collection CaCerts { get; } private X509Certificate2Collection CaCerts { get; }