diff --git a/examples/httpClientFactory/PodListHostedService.cs b/examples/httpClientFactory/PodListHostedService.cs new file mode 100644 index 0000000..3b2c942 --- /dev/null +++ b/examples/httpClientFactory/PodListHostedService.cs @@ -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 _logger; + + public PodListHostedService(IKubernetes kubernetesClient, ILogger 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; + } + } +} diff --git a/examples/httpClientFactory/Program.cs b/examples/httpClientFactory/Program.cs new file mode 100644 index 0000000..d167a03 --- /dev/null +++ b/examples/httpClientFactory/Program.cs @@ -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((httpClient, serviceProvider) => + { + return new Kubernetes( + serviceProvider.GetRequiredService(), + httpClient); + }); + + // Add the class that uses the client + services.AddHostedService(); + }) + .Build()) + { + await host.StartAsync().ConfigureAwait(false); + await host.StopAsync().ConfigureAwait(false); + } + } + } +} diff --git a/examples/httpClientFactory/httpClientFactory.csproj b/examples/httpClientFactory/httpClientFactory.csproj new file mode 100644 index 0000000..5a81b68 --- /dev/null +++ b/examples/httpClientFactory/httpClientFactory.csproj @@ -0,0 +1,19 @@ + + + + Exe + netcoreapp2.2 + latest + + + + + + + + + + + + + diff --git a/kubernetes-client.sln b/kubernetes-client.sln index 67bc686..7627fbd 100644 --- a/kubernetes-client.sln +++ b/kubernetes-client.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26430.16 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29230.47 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{B70AFB57-57C9-46DC-84BE-11B7DDD34B40}" EndProject @@ -31,7 +31,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{879F8787-C3B EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KubernetesWatchGenerator", "gen\KubernetesWatchGenerator\KubernetesWatchGenerator.csproj", "{542DC30E-FDF7-4A35-B026-6C21F435E8B1}" 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 Global 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|x86.ActiveCfg = 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 GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -191,6 +205,7 @@ Global {806AD0E5-833F-42FB-A870-4BCEE7F4B17F} = {8AF4A5C2-F0CE-47D5-A4C5-FE4AB83CA509} {542DC30E-FDF7-4A35-B026-6C21F435E8B1} = {879F8787-C3BB-43F3-A92D-6D4C7D3A5285} {04DE2C84-117D-4E21-8B45-B7AE627697BD} = {B70AFB57-57C9-46DC-84BE-11B7DDD34B40} + {A07314A0-02E8-4F36-B233-726D59D28F08} = {B70AFB57-57C9-46DC-84BE-11B7DDD34B40} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {049A763A-C891-4E8D-80CF-89DD3E22ADC7} diff --git a/src/KubernetesClient/Kubernetes.ConfigInit.cs b/src/KubernetesClient/Kubernetes.ConfigInit.cs index 47f0342..ca01729 100644 --- a/src/KubernetesClient/Kubernetes.ConfigInit.cs +++ b/src/KubernetesClient/Kubernetes.ConfigInit.cs @@ -13,84 +13,68 @@ namespace k8s { public partial class Kubernetes { -#if MONOANDROID8_1 /// /// Initializes a new instance of the class. /// /// - /// Optional. The delegating handlers to add to the http client pipeline. + /// The kube config to use. /// - /// - /// Optional. The delegating handlers to add to the http client pipeline. + /// + /// The to use for all requests. /// - 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(); - - 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 + /// /// Initializes a new instance of the class. /// /// - /// Optional. The delegating handlers to add to the http client pipeline. + /// The kube config to use. + /// + /// + /// The to use for all requests. + /// + /// + /// Whether or not the object should own the lifetime of . + /// + public Kubernetes(KubernetesClientConfiguration config, HttpClient httpClient, bool disposeHttpClient) : this(httpClient, disposeHttpClient) + { + ValidateConfig(config); + CaCerts = config.SslCaCerts; + SkipTlsVerify = config.SkipTlsVerify; + InitializeFromConfig(config); + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The kube config to use. /// /// /// Optional. The delegating handlers to add to the http client pipeline. /// - 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)) { throw new KubeConfigException("Host url must be set"); @@ -104,10 +88,10 @@ namespace k8s { throw new KubeConfigException("Bad host url", e); } + } - CaCerts = config.SslCaCerts; - SkipTlsVerify = config.SkipTlsVerify; - + private void InitializeFromConfig(KubernetesClientConfiguration config) + { if (BaseUri.Scheme == "https") { if (config.SkipTlsVerify) @@ -115,7 +99,7 @@ namespace k8s #if NET452 ((WebRequestHandler) HttpClientHandler).ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true; -#elif XAMARINIOS1_0 +#elif XAMARINIOS1_0 || MONOANDROID8_1 System.Net.ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => { return true; @@ -129,9 +113,8 @@ namespace k8s { 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 ((WebRequestHandler) HttpClientHandler).ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => { @@ -143,6 +126,22 @@ namespace k8s var cert = new X509Certificate2(certificate); return Kubernetes.CertificateValidationCallBack(sender, CaCerts, cert, chain, sslPolicyErrors); }; +#elif MONOANDROID8_1 + var certList = new System.Collections.Generic.List(); + + 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 HttpClientHandler.ServerCertificateCustomValidationCallback = (sender, certificate, chain, sslPolicyErrors) => { @@ -155,7 +154,6 @@ namespace k8s // set credentails for the kubernernet client SetCredentials(config, HttpClientHandler); } -#endif private X509Certificate2Collection CaCerts { get; }