7.1 remove old autorest (#785)

* init removal

* clean up warning

* bump ver to 7.1

* fix build

* custom http client no long supported

* cleanup unused types

* fix npe

* remove service client

* modern ssl settings

* fix test

* fix client cert null

* fix ctor

* cleanup
This commit is contained in:
Boshi Lian
2022-02-25 13:33:23 -08:00
committed by GitHub
parent ef7d226ab0
commit 0d719f1fc6
48 changed files with 907 additions and 590 deletions

View File

@@ -2,7 +2,7 @@ using System;
using System.Threading.Tasks;
using k8s;
using k8s.Models;
using Microsoft.Rest;
using k8s.Autorest;
namespace attach
{
@@ -10,8 +10,6 @@ namespace attach
{
private static async Task Main(string[] args)
{
ServiceClientTracing.IsEnabled = true;
var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
IKubernetes client = new Kubernetes(config);
Console.WriteLine("Starting Request!");

View File

@@ -1,4 +1,5 @@
using k8s;
using k8s.Autorest;
using k8s.Models;
using Microsoft.AspNetCore.JsonPatch;
using System;
@@ -35,14 +36,14 @@ namespace customResource
myCr.Metadata.NamespaceProperty ?? "default",
myCRD.PluralName).ConfigureAwait(false);
}
catch (Microsoft.Rest.HttpOperationException httpOperationException) when (httpOperationException.Message.Contains("422"))
catch (HttpOperationException httpOperationException) when (httpOperationException.Message.Contains("422"))
{
var phase = httpOperationException.Response.ReasonPhrase;
var content = httpOperationException.Response.Content;
Console.WriteLine("response content: {0}", content);
Console.WriteLine("response phase: {0}", phase);
}
catch (Microsoft.Rest.HttpOperationException)
catch (HttpOperationException)
{
}
@@ -70,7 +71,7 @@ namespace customResource
myCRD.PluralName,
myCr.Metadata.Name).ConfigureAwait(false);
}
catch (Microsoft.Rest.HttpOperationException httpOperationException)
catch (HttpOperationException httpOperationException)
{
var phase = httpOperationException.Response.ReasonPhrase;
var content = httpOperationException.Response.Content;

View File

@@ -1,44 +0,0 @@
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
public 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).ConfigureAwait(false);
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

@@ -1,44 +0,0 @@
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);
})
.ConfigurePrimaryHttpMessageHandler(config.CreateDefaultHttpClientHandler);
// Add the class that uses the client
services.AddHostedService<PodListHostedService>();
})
.Build())
{
await host.StartAsync().ConfigureAwait(false);
await host.StopAsync().ConfigureAwait(false);
}
}
}
}

View File

@@ -1,18 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\KubernetesClient\KubernetesClient.csproj" />
</ItemGroup>
</Project>

View File

@@ -35,9 +35,9 @@ namespace @namespace
{
foreach (var innerEx in ex.InnerExceptions)
{
if (innerEx is Microsoft.Rest.HttpOperationException)
if (innerEx is k8s.Autorest.HttpOperationException)
{
var code = ((Microsoft.Rest.HttpOperationException)innerEx).Response.StatusCode;
var code = ((k8s.Autorest.HttpOperationException)innerEx).Response.StatusCode;
if (code == HttpStatusCode.NotFound)
{
return;
@@ -47,7 +47,7 @@ namespace @namespace
}
}
}
catch (Microsoft.Rest.HttpOperationException ex)
catch (k8s.Autorest.HttpOperationException ex)
{
if (ex.Response.StatusCode == HttpStatusCode.NotFound)
{

View File

@@ -3,7 +3,7 @@ using System.Threading;
using System.Threading.Tasks;
using k8s;
using k8s.Models;
using Microsoft.Rest;
using k8s.Autorest;
namespace watch
{

View File

@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>10.0</LangVersion>
@@ -19,6 +19,7 @@
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="1.1.0" GeneratePathProperty="true" PrivateAssets="all" />
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" GeneratePathProperty="true" PrivateAssets="all" />
<PackageReference Include="Namotion.Reflection" Version="2.0.9" GeneratePathProperty="true" PrivateAssets="all" />
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="4.7.1" GeneratePathProperty="true" PrivateAssets="all" />
</ItemGroup>
<PropertyGroup>
@@ -35,6 +36,7 @@
<TargetPathWithTargetPlatformMoniker Include="$(PKGMicrosoft_Bcl_AsyncInterfaces)\lib\netstandard2.0\Microsoft.Bcl.AsyncInterfaces.dll" IncludeRuntimeDependency="false" />
<TargetPathWithTargetPlatformMoniker Include="$(PKGNewtonsoft_Json)\lib\netstandard1.0\Newtonsoft.Json.dll" IncludeRuntimeDependency="false" />
<TargetPathWithTargetPlatformMoniker Include="$(PKGNamotion_Reflection)\lib\netstandard2.0\Namotion.Reflection.dll" IncludeRuntimeDependency="false" />
<TargetPathWithTargetPlatformMoniker Include="$(PKGSystem_Diagnostics_DiagnosticSource)\lib\netstandard1.3\System.Diagnostics.DiagnosticSource.dll" IncludeRuntimeDependency="false" />
</ItemGroup>
</Target>

View File

@@ -6,7 +6,7 @@
namespace k8s
{
using Microsoft.Rest;
using k8s.Autorest;
using Models;
using System.Collections;
@@ -17,19 +17,8 @@ namespace k8s
/// <summary>
/// </summary>
public partial interface IKubernetes : System.IDisposable
public partial interface IKubernetes
{
/// <summary>
/// The base URI of the service.
/// </summary>
System.Uri BaseUri { get; set; }
/// <summary>
/// Subscription credentials which uniquely identify client
/// subscription.
/// </summary>
ServiceClientCredentials Credentials { get; }
{{#.}}
/// <summary>
/// {{ToXmlDoc operation.description}}

View File

@@ -6,7 +6,7 @@
namespace k8s
{
using Microsoft.Rest;
using k8s.Autorest;
using Models;
using System.Collections.Generic;
using System.IO;
@@ -15,7 +15,7 @@ namespace k8s
using System.Threading;
using System.Threading.Tasks;
public partial class Kubernetes : ServiceClient<Kubernetes>, IKubernetes
public partial class Kubernetes : IKubernetes
{
{{#.}}
/// <inheritdoc/>

View File

@@ -31,8 +31,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{879F8787-C3B
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "patch", "examples\patch\patch.csproj", "{04DE2C84-117D-4E21-8B45-B7AE627697BD}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "httpClientFactory", "examples\httpClientFactory\httpClientFactory.csproj", "{A07314A0-02E8-4F36-B233-726D59D28F08}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "metrics", "examples\metrics\metrics.csproj", "{B9647AD4-F6B0-406F-8B79-6781E31600EC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "E2E.Tests", "tests\E2E.Tests\E2E.Tests.csproj", "{5056C4A2-5E12-4C16-8DA7-8835DA58BFF2}"
@@ -177,18 +175,6 @@ 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
{B9647AD4-F6B0-406F-8B79-6781E31600EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B9647AD4-F6B0-406F-8B79-6781E31600EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B9647AD4-F6B0-406F-8B79-6781E31600EC}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -288,7 +274,6 @@ Global
{35DD7248-F9EC-4272-A32C-B0C59E5A6FA7} = {3D1864AA-1FFC-4512-BB13-46055E410F73}
{806AD0E5-833F-42FB-A870-4BCEE7F4B17F} = {8AF4A5C2-F0CE-47D5-A4C5-FE4AB83CA509}
{04DE2C84-117D-4E21-8B45-B7AE627697BD} = {B70AFB57-57C9-46DC-84BE-11B7DDD34B40}
{A07314A0-02E8-4F36-B233-726D59D28F08} = {B70AFB57-57C9-46DC-84BE-11B7DDD34B40}
{B9647AD4-F6B0-406F-8B79-6781E31600EC} = {B70AFB57-57C9-46DC-84BE-11B7DDD34B40}
{5056C4A2-5E12-4C16-8DA7-8835DA58BFF2} = {8AF4A5C2-F0CE-47D5-A4C5-FE4AB83CA509}
{4D2AE427-F856-49E5-B61D-EA6B17D89051} = {8AF4A5C2-F0CE-47D5-A4C5-FE4AB83CA509}

View File

@@ -4,7 +4,7 @@ using System.Threading;
using System.Threading.Tasks;
using k8s.Exceptions;
using k8s.KubeConfigModels;
using Microsoft.Rest;
using k8s.Autorest;
namespace k8s.Authentication
{

View File

@@ -3,7 +3,7 @@ using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using k8s.Exceptions;
using Microsoft.Rest;
using k8s.Autorest;
namespace k8s.Authentication
{

View File

@@ -1,6 +1,6 @@
using IdentityModel.OidcClient;
using k8s.Exceptions;
using Microsoft.Rest;
using k8s.Autorest;
using System.IdentityModel.Tokens.Jwt;
using System.Net.Http.Headers;
using System.Threading;

View File

@@ -2,7 +2,7 @@ using System.Net.Http.Headers;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Rest;
using k8s.Autorest;
namespace k8s.Authentication
{

View File

@@ -0,0 +1,55 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using System;
using System.Globalization;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace k8s.Autorest
{
/// <summary>
/// Basic Auth credentials for use with a REST Service Client.
/// </summary>
public class BasicAuthenticationCredentials : ServiceClientCredentials
{
/// <summary>
/// Basic auth UserName.
/// </summary>
public string UserName { get; set; }
/// <summary>
/// Basic auth password.
/// </summary>
public string Password { get; set; }
/// <summary>
/// Add the Basic Authentication Header to each outgoing request
/// </summary>
/// <param name="request">The outgoing request</param>
/// <param name="cancellationToken">A token to cancel the operation</param>
/// <returns>void</returns>
public override Task ProcessHttpRequestAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
if (request == null)
{
throw new ArgumentNullException("request");
}
// Add username and password to "Basic" header of each request.
request.Headers.Authorization = new AuthenticationHeaderValue(
"Basic",
Convert.ToBase64String(Encoding.UTF8.GetBytes(string.Format(
CultureInfo.InvariantCulture,
"{0}:{1}",
UserName,
Password).ToCharArray())));
return Task.FromResult<object>(null);
}
}
}

View File

@@ -0,0 +1,45 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
namespace k8s.Autorest
{
/// <summary>
/// Extensions for manipulating HTTP request and response objects.
/// </summary>
internal static class HttpExtensions
{
/// <summary>
/// Get the content headers of an HtttRequestMessage.
/// </summary>
/// <param name="request">The request message.</param>
/// <returns>The content headers.</returns>
public static HttpHeaders GetContentHeaders(this HttpRequestMessage request)
{
if (request != null && request.Content != null)
{
return request.Content.Headers;
}
return null;
}
/// <summary>
/// Get the content headers of an HttpResponseMessage.
/// </summary>
/// <param name="response">The response message.</param>
/// <returns>The content headers.</returns>
public static HttpHeaders GetContentHeaders(this HttpResponseMessage response)
{
if (response != null && response.Content != null)
{
return response.Content.Headers;
}
return null;
}
}
}

View File

@@ -0,0 +1,59 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
namespace k8s.Autorest
{
/// <summary>
/// Base class used to wrap HTTP requests and responses to preserve data after disposal of
/// HttpClient.
/// </summary>
public abstract class HttpMessageWrapper
{
/// <summary>
/// Initializes a new instance of the <see cref="HttpMessageWrapper"/> class.
/// </summary>
protected HttpMessageWrapper()
{
Headers = new Dictionary<string, IEnumerable<string>>();
}
/// <summary>
/// Exposes the HTTP message contents.
/// </summary>
public string Content { get; set; }
/// <summary>
/// Gets the collection of HTTP headers.
/// </summary>
public IDictionary<string, IEnumerable<string>> Headers { get; private set; }
/// <summary>
/// Copies HTTP message headers to the error object.
/// </summary>
/// <param name="headers">Collection of HTTP headers.</param>
protected void CopyHeaders(HttpHeaders headers)
{
if (headers != null)
{
foreach (KeyValuePair<string, IEnumerable<string>> header in headers)
{
IEnumerable<string> values = null;
if (Headers.TryGetValue(header.Key, out values))
{
values = Enumerable.Concat(values, header.Value);
}
else
{
values = header.Value;
}
Headers[header.Key] = values;
}
}
}
}
}

View File

@@ -0,0 +1,68 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using System;
using System.Net.Http;
using System.Runtime.Serialization;
using System.Security.Permissions;
namespace k8s.Autorest
{
/// <summary>
/// Exception thrown for an invalid response with custom error information.
/// </summary>
[Serializable]
public class HttpOperationException : RestException
{
/// <summary>
/// Gets information about the associated HTTP request.
/// </summary>
public HttpRequestMessageWrapper Request { get; set; }
/// <summary>
/// Gets information about the associated HTTP response.
/// </summary>
public HttpResponseMessageWrapper Response { get; set; }
/// <summary>
/// Gets or sets the response object.
/// </summary>
public object Body { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="HttpOperationException"/> class.
/// </summary>
public HttpOperationException()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="HttpOperationException"/> class.
/// </summary>
/// <param name="message">The exception message.</param>
public HttpOperationException(string message)
: this(message, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="HttpOperationException"/> class.
/// </summary>
/// <param name="message">The exception message.</param>
/// <param name="innerException">Inner exception.</param>
public HttpOperationException(string message, Exception innerException)
: base(message, innerException)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="HttpOperationException"/> class.
/// </summary>
/// <param name="info">Serialization info.</param>
/// <param name="context">Streaming context.</param>
protected HttpOperationException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
}
}

View File

@@ -0,0 +1,133 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using System;
using System.Net.Http;
namespace k8s.Autorest
{
/// <summary>
/// Represents the base return type of all ServiceClient REST operations without response body.
/// </summary>
#pragma warning disable SA1649 // File name should match first type name
public interface IHttpOperationResponse
#pragma warning restore SA1649 // File name should match first type name
{
/// <summary>
/// Gets information about the associated HTTP request.
/// </summary>
HttpRequestMessage Request { get; set; }
/// <summary>
/// Gets information about the associated HTTP response.
/// </summary>
HttpResponseMessage Response { get; set; }
}
/// <summary>
/// Represents the base return type of all ServiceClient REST operations with response body.
/// </summary>
#pragma warning disable SA1618 // Generic type parameters should be documented
public interface IHttpOperationResponse<T> : IHttpOperationResponse
#pragma warning restore SA1618 // Generic type parameters should be documented
{
/// <summary>
/// Gets or sets the response object.
/// </summary>
T Body { get; set; }
}
#pragma warning disable SA1622 // Generic type parameter documentation should have text
/// <summary>
/// Represents the base return type of all ServiceClient REST operations with a header response.
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IHttpOperationHeaderResponse<T> : IHttpOperationResponse
#pragma warning restore SA1622 // Generic type parameter documentation should have text
{
/// <summary>
/// Gets or sets the response header object.
/// </summary>
T Headers { get; set; }
}
/// <summary>
/// Represents the base return type of all ServiceClient REST operations with response body and header.
/// </summary>
#pragma warning disable SA1618 // Generic type parameters should be documented
public interface IHttpOperationResponse<TBody, THeader> : IHttpOperationResponse<TBody>, IHttpOperationHeaderResponse<THeader>
#pragma warning restore SA1618 // Generic type parameters should be documented
{
}
/// <summary>
/// Represents the base return type of all ServiceClient REST operations without response body.
/// </summary>
public class HttpOperationResponse : IHttpOperationResponse, IDisposable
{
/// <summary>
/// Indicates whether the HttpOperationResponse has been disposed.
/// </summary>
private bool _disposed;
/// <summary>
/// Gets information about the associated HTTP request.
/// </summary>
public HttpRequestMessage Request { get; set; }
/// <summary>
/// Gets information about the associated HTTP response.
/// </summary>
public HttpResponseMessage Response { get; set; }
/// <summary>
/// Dispose the HttpOperationResponse.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Dispose the HttpClient and Handlers.
/// </summary>
/// <param name="disposing">True to release both managed and unmanaged resources; false to releases only unmanaged resources.</param>
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
_disposed = true;
// Dispose the request and response
if (Request != null)
{
Request.Dispose();
}
if (Response != null)
{
Response.Dispose();
}
Request = null;
Response = null;
}
}
}
/// <summary>
/// Represents the base return type of all ServiceClient REST operations.
/// </summary>
#pragma warning disable SA1402 // File may only contain a single type
#pragma warning disable SA1618 // Generic type parameters should be documented
public class HttpOperationResponse<T> : HttpOperationResponse, IHttpOperationResponse<T>
#pragma warning restore SA1618 // Generic type parameters should be documented
#pragma warning restore SA1402 // File may only contain a single type
{
/// <summary>
/// Gets or sets the response object.
/// </summary>
public T Body { get; set; }
}
}

View File

@@ -0,0 +1,62 @@
// Copyright (c) Microsoft Corporation. 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.Http;
namespace k8s.Autorest
{
/// <summary>
/// Wrapper around HttpRequestMessage type that copies properties of HttpRequestMessage so that
/// they are available after the HttpClient gets disposed.
/// </summary>
public class HttpRequestMessageWrapper : HttpMessageWrapper
{
/// <summary>
/// Initializes a new instance of the <see cref="HttpRequestMessageWrapper"/> class from HttpRequestMessage.
/// and content.
/// </summary>
#pragma warning disable SA1611 // Element parameters should be documented
public HttpRequestMessageWrapper(HttpRequestMessage httpRequest, string content)
#pragma warning restore SA1611 // Element parameters should be documented
{
if (httpRequest == null)
{
throw new ArgumentNullException("httpRequest");
}
this.CopyHeaders(httpRequest.Headers);
this.CopyHeaders(httpRequest.GetContentHeaders());
this.Content = content;
this.Method = httpRequest.Method;
this.RequestUri = httpRequest.RequestUri;
#pragma warning disable CS0618 // Type or member is obsolete
if (httpRequest.Properties != null)
{
Properties = new Dictionary<string, object>();
foreach (KeyValuePair<string, object> pair in httpRequest.Properties)
#pragma warning restore CS0618 // Type or member is obsolete
{
this.Properties[pair.Key] = pair.Value;
}
}
}
/// <summary>
/// Gets or sets the HTTP method used by the HTTP request message.
/// </summary>
public HttpMethod Method { get; protected set; }
/// <summary>
/// Gets or sets the Uri used for the HTTP request.
/// </summary>
public Uri RequestUri { get; protected set; }
/// <summary>
/// Gets a set of properties for the HTTP request.
/// </summary>
public IDictionary<string, object> Properties { get; private set; }
}
}

View File

@@ -0,0 +1,47 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using System;
using System.Net;
using System.Net.Http;
namespace k8s.Autorest
{
/// <summary>
/// Wrapper around HttpResponseMessage type that copies properties of HttpResponseMessage so that
/// they are available after the HttpClient gets disposed.
/// </summary>
public class HttpResponseMessageWrapper : HttpMessageWrapper
{
/// <summary>
/// Initializes a new instance of the <see cref="HttpResponseMessageWrapper"/> class from HttpResponseMessage.
/// and content.
/// </summary>
#pragma warning disable SA1611 // Element parameters should be documented
public HttpResponseMessageWrapper(HttpResponseMessage httpResponse, string content)
#pragma warning restore SA1611 // Element parameters should be documented
{
if (httpResponse == null)
{
throw new ArgumentNullException("httpResponse");
}
this.CopyHeaders(httpResponse.Headers);
this.CopyHeaders(httpResponse.GetContentHeaders());
this.Content = content;
this.StatusCode = httpResponse.StatusCode;
this.ReasonPhrase = httpResponse.ReasonPhrase;
}
/// <summary>
/// Gets or sets the status code of the HTTP response.
/// </summary>
public HttpStatusCode StatusCode { get; protected set; }
/// <summary>
/// Exposes the reason phrase, typically sent along with the status code.
/// </summary>
public string ReasonPhrase { get; protected set; }
}
}

View File

@@ -0,0 +1,26 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
#pragma warning disable SA1606
#pragma warning disable SA1614
namespace k8s.Autorest
{
/// <summary>
/// Interface to a source of access tokens.
/// </summary>
public interface ITokenProvider
{
/// <summary>
///
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns>AuthenticationHeaderValue</returns>
Task<AuthenticationHeaderValue> GetAuthenticationHeaderAsync(CancellationToken cancellationToken);
}
}
#pragma warning restore SA1614
#pragma warning restore SA1606

View File

@@ -0,0 +1,51 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using System;
using System.Runtime.Serialization;
namespace k8s.Autorest
{
/// <summary>
/// Generic exception for Microsoft Rest Client.
/// </summary>
[Serializable]
public class RestException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="RestException"/> class.
/// </summary>
public RestException()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="RestException"/> class.
/// </summary>
/// <param name="message">The exception message.</param>
public RestException(string message)
: this(message, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="RestException"/> class.
/// </summary>
/// <param name="message">The exception message.</param>
/// <param name="innerException">Inner exception.</param>
public RestException(string message, Exception innerException)
: base(message, innerException)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="RestException"/> class.
/// </summary>
/// <param name="info">Serialization info.</param>
/// <param name="context">Streaming context.</param>
protected RestException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
}
}

View File

@@ -0,0 +1,29 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace k8s.Autorest
{
/// <summary>
/// ServiceClientCredentials is the abstraction for credentials used by ServiceClients accessing REST services.
/// </summary>
public abstract class ServiceClientCredentials
{
/// <summary>
/// Apply the credentials to the HTTP request.
/// </summary>
/// <param name="request">The HTTP request message.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>
/// Task that will complete when processing has finished.
/// </returns>
public virtual Task ProcessHttpRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// Return an empty task by default
return Task.FromResult<object>(null);
}
}
}

View File

@@ -0,0 +1,50 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
namespace k8s.Autorest
{
/// <summary>
/// A simple token provider that always provides a static access token.
/// </summary>
public sealed class StringTokenProvider : ITokenProvider
{
private string _accessToken;
private string _type;
/// <summary>
/// Initializes a new instance of the <see cref="StringTokenProvider"/> class.
/// Create a token provider for the given token type that returns the given
/// access token.
/// </summary>
/// <param name="accessToken">The access token to return.</param>
/// <param name="tokenType">The token type of the given access token.</param>
public StringTokenProvider(string accessToken, string tokenType)
{
_accessToken = accessToken;
_type = tokenType;
}
/// <summary>
/// Gets the token type of this access token.
/// </summary>
public string TokenType
{
get { return _type; }
}
/// <summary>
/// Returns the static access token.
/// </summary>
/// <param name="cancellationToken">The cancellation token for this action.
/// This will not be used since the returned token is static.</param>
/// <returns>The access token.</returns>
public Task<AuthenticationHeaderValue> GetAuthenticationHeaderAsync(CancellationToken cancellationToken)
{
return Task.FromResult(new AuthenticationHeaderValue(_type, _accessToken));
}
}
}

View File

@@ -0,0 +1,122 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace k8s.Autorest
{
/// <summary>
/// Token based credentials for use with a REST Service Client.
/// </summary>
public class TokenCredentials : ServiceClientCredentials
{
/// <summary>
/// The bearer token type, as serialized in an http Authentication header.
/// </summary>
private const string BearerTokenType = "Bearer";
/// <summary>
/// Gets secure token used to authenticate against Microsoft Azure API.
/// No anonymous requests are allowed.
/// </summary>
protected ITokenProvider TokenProvider { get; private set; }
/// <summary>
/// Gets Tenant ID
/// </summary>
public string TenantId { get; private set; }
/// <summary>
/// Gets UserInfo.DisplayableId
/// </summary>
public string CallerId { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="TokenCredentials"/>
/// class with the given 'Bearer' token.
/// </summary>
/// <param name="token">Valid JSON Web Token (JWT).</param>
public TokenCredentials(string token)
: this(token, BearerTokenType)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="TokenCredentials"/>
/// class with the given token and token type.
/// </summary>
/// <param name="token">Valid JSON Web Token (JWT).</param>
/// <param name="tokenType">The token type of the given token.</param>
public TokenCredentials(string token, string tokenType)
: this(new StringTokenProvider(token, tokenType))
{
if (string.IsNullOrEmpty(token))
{
throw new ArgumentNullException("token");
}
if (string.IsNullOrEmpty(tokenType))
{
throw new ArgumentNullException("tokenType");
}
}
/// <summary>
/// Initializes a new instance of the <see cref="TokenCredentials"/> class.
/// Create an access token credentials object, given an interface to a token source.
/// </summary>
/// <param name="tokenProvider">The source of tokens for these credentials.</param>
public TokenCredentials(ITokenProvider tokenProvider)
{
if (tokenProvider == null)
{
throw new ArgumentNullException("tokenProvider");
}
this.TokenProvider = tokenProvider;
}
/// <summary>
/// Initializes a new instance of the <see cref="TokenCredentials"/> class.
/// Create an access token credentials object, given an interface to a token source.
/// </summary>
/// <param name="tokenProvider">The source of tokens for these credentials.</param>
/// <param name="tenantId">Tenant ID from AuthenticationResult</param>
/// <param name="callerId">UserInfo.DisplayableId field from AuthenticationResult</param>
public TokenCredentials(ITokenProvider tokenProvider, string tenantId, string callerId)
: this(tokenProvider)
{
this.TenantId = tenantId;
this.CallerId = callerId;
}
/// <summary>
/// Apply the credentials to the HTTP request.
/// </summary>
/// <param name="request">The HTTP request.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>
/// Task that will complete when processing has completed.
/// </returns>
public async override Task ProcessHttpRequestAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
if (TokenProvider == null)
{
throw new ArgumentNullException(nameof(TokenProvider));
}
request.Headers.Authorization = await TokenProvider.GetAuthenticationHeaderAsync(cancellationToken).ConfigureAwait(false);
await base.ProcessHttpRequestAsync(request, cancellationToken).ConfigureAwait(false);
}
}
}

View File

@@ -1,12 +0,0 @@
using System.Net.Http;
namespace k8s
{
public partial interface IKubernetes
{
/// <summary>
/// Gets the <see cref="HttpClient"/> used for making HTTP requests.
/// </summary>
HttpClient HttpClient { get; }
}
}

View File

@@ -0,0 +1,9 @@
namespace k8s;
public partial interface IKubernetes : IDisposable
{
/// <summary>
/// The base URI of the service.
/// </summary>
Uri BaseUri { get; set; }
}

View File

@@ -4,12 +4,12 @@ namespace k8s.KubeConfigModels
{
public class ExecStatus
{
#nullable enable
#nullable enable
public DateTime? Expiry { get; set; }
public string? Token { get; set; }
public string? ClientCertificateData { get; set; }
public string? ClientKeyData { get; set; }
#nullable disable
#nullable disable
public bool IsValid()
{

View File

@@ -7,7 +7,7 @@ using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using k8s.Exceptions;
using Microsoft.Rest;
using k8s.Autorest;
namespace k8s
{
@@ -20,43 +20,6 @@ namespace k8s
/// <value>timeout</value>
public TimeSpan HttpClientTimeout { get; set; } = TimeSpan.FromSeconds(100);
/// <summary>
/// Initializes a new instance of the <see cref="Kubernetes" /> class.
/// </summary>
/// <param name='config'>
/// The kube config to use.
/// </param>
/// <param name="httpClient">
/// The <see cref="HttpClient" /> to use for all requests.
/// </param>
public Kubernetes(KubernetesClientConfiguration config, HttpClient httpClient)
: this(config, httpClient, false)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Kubernetes" /> class.
/// </summary>
/// <param name='config'>
/// 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;
ClientCert = CertUtils.GetClientCert(config);
SetCredentials(config);
}
/// <summary>
/// Initializes a new instance of the <see cref="Kubernetes" /> class.
/// </summary>
@@ -105,7 +68,11 @@ namespace k8s
{
if (config.SkipTlsVerify)
{
#if NET5_0_OR_GREATER
HttpClientHandler.SslOptions.RemoteCertificateValidationCallback =
#else
HttpClientHandler.ServerCertificateCustomValidationCallback =
#endif
(sender, certificate, chain, sslPolicyErrors) => true;
}
else
@@ -115,7 +82,11 @@ namespace k8s
throw new KubeConfigException("A CA must be set when SkipTlsVerify === false");
}
#if NET5_0_OR_GREATER
HttpClientHandler.SslOptions.RemoteCertificateValidationCallback =
#else
HttpClientHandler.ServerCertificateCustomValidationCallback =
#endif
(sender, certificate, chain, sslPolicyErrors) =>
{
return CertificateValidationCallBack(sender, CaCerts, certificate, chain,
@@ -126,122 +97,44 @@ namespace k8s
// set credentails for the kubernetes client
SetCredentials(config);
config.AddCertificates(HttpClientHandler);
var clientCert = CertUtils.GetClientCert(config);
if (clientCert != null)
{
#if NET5_0_OR_GREATER
HttpClientHandler.SslOptions.ClientCertificates.Add(clientCert);
#else
HttpClientHandler.ClientCertificates.Add(clientCert);
#endif
}
}
private X509Certificate2Collection CaCerts { get; }
private X509Certificate2 ClientCert { get; }
private bool SkipTlsVerify { get; }
private void CustomInitialize()
{
}
/// <summary>A <see cref="DelegatingHandler"/> that simply forwards a request with no further processing.</summary>
private sealed class ForwardingHandler : DelegatingHandler
{
public ForwardingHandler(HttpMessageHandler handler)
: base(handler)
{
}
}
private void AppendDelegatingHandler<T>()
where T : DelegatingHandler, new()
{
var cur = FirstMessageHandler as DelegatingHandler;
while (cur != null)
{
var next = cur.InnerHandler as DelegatingHandler;
if (next == null)
{
// last one
// append watcher handler between to last handler
cur.InnerHandler = new T { InnerHandler = cur.InnerHandler };
break;
}
cur = next;
}
}
// NOTE: this method replicates the logic that the base ServiceClient uses except that it doesn't insert the RetryDelegatingHandler
// and it does insert the WatcherDelegatingHandler. we don't want the RetryDelegatingHandler because it has a very broad definition
// of what requests have failed. it considers everything outside 2xx to be failed, including 1xx (e.g. 101 Switching Protocols) and
// 3xx. in particular, this prevents upgraded connections and certain generic/custom requests from working.
private void CreateHttpClient(DelegatingHandler[] handlers, KubernetesClientConfiguration config)
{
FirstMessageHandler = HttpClientHandler = CreateRootHandler();
#if NET5_0_OR_GREATER
// https://github.com/kubernetes-client/csharp/issues/587
// let user control if tcp keep alive until better fix
if (config.TcpKeepAlive)
{
// https://github.com/kubernetes-client/csharp/issues/533
// net5 only
// this is a temp fix to attach SocketsHttpHandler to HttpClient in order to set SO_KEEPALIVE
// https://tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/
//
// _underlyingHandler is not a public accessible field
// src of net5 HttpClientHandler and _underlyingHandler field defined here
// https://github.com/dotnet/runtime/blob/79ae74f5ca5c8a6fe3a48935e85bd7374959c570/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs#L22
//
// Should remove after better solution
var sh = new SocketsHttpHandler
FirstMessageHandler = HttpClientHandler = new SocketsHttpHandler
{
KeepAlivePingPolicy = HttpKeepAlivePingPolicy.WithActiveRequests,
KeepAlivePingDelay = TimeSpan.FromMinutes(3),
KeepAlivePingTimeout = TimeSpan.FromSeconds(30),
};
sh.ConnectCallback = async (context, token) =>
{
var socket = new Socket(SocketType.Stream, ProtocolType.Tcp)
{
NoDelay = true,
};
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
var host = context.DnsEndPoint.Host;
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
// https://github.com/dotnet/runtime/issues/24917
// GetHostAddresses will return {host} if host is an ip
var ips = Dns.GetHostAddresses(host);
if (ips.Length == 0)
{
throw new Exception($"{host} DNS lookup failed");
}
host = ips[new Random().Next(ips.Length)].ToString();
}
await socket.ConnectAsync(host, context.DnsEndPoint.Port, token).ConfigureAwait(false);
return new NetworkStream(socket, ownsSocket: true);
};
// set HttpClientHandler's cert callback before replace _underlyingHandler
// force HttpClientHandler use our callback
InitializeFromConfig(config);
var p = HttpClientHandler.GetType().GetField("_underlyingHandler", BindingFlags.NonPublic | BindingFlags.Instance);
p.SetValue(HttpClientHandler, (sh));
}
HttpClientHandler.SslOptions.ClientCertificates = new X509Certificate2Collection();
#else
FirstMessageHandler = HttpClientHandler = new HttpClientHandler();
#endif
if (handlers == null || handlers.Length == 0)
{
// ensure we have at least one DelegatingHandler so AppendDelegatingHandler will work
FirstMessageHandler = new ForwardingHandler(HttpClientHandler);
}
else
if (handlers != null)
{
for (int i = handlers.Length - 1; i >= 0; i--)
{

View File

@@ -1,5 +1,5 @@
using k8s.Models;
using Microsoft.Rest;
using k8s.Autorest;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

View File

@@ -1,5 +1,5 @@
using k8s.Models;
using Microsoft.Rest;
using k8s.Autorest;
using System.Net;
using System.Net.Http;
using System.Net.WebSockets;
@@ -237,7 +237,11 @@ namespace k8s
if (this.HttpClientHandler != null)
{
#if NET5_0_OR_GREATER
foreach (var cert in this.HttpClientHandler.SslOptions.ClientCertificates.OfType<X509Certificate2>())
#else
foreach (var cert in this.HttpClientHandler.ClientCertificates.OfType<X509Certificate2>())
#endif
{
webSocketBuilder.AddClientCertificate(cert);
}

View File

@@ -3,7 +3,7 @@ using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Rest;
using k8s.Autorest;
namespace k8s
{
@@ -29,197 +29,40 @@ namespace k8s
/// </summary>
public ServiceClientCredentials Credentials { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="Kubernetes"/> class.
/// </summary>
/// <param name='httpClient'>
/// HttpClient to be used
/// </param>
/// <param name='disposeHttpClient'>
/// True: will dispose the provided httpClient on calling Kubernetes.Dispose(). False: will not dispose provided httpClient</param>
protected Kubernetes(HttpClient httpClient, bool disposeHttpClient)
: base(httpClient, disposeHttpClient)
public HttpClient HttpClient { get; protected set; }
private IEnumerable<HttpMessageHandler> HttpMessageHandlers
{
Initialize();
get
{
var handler = FirstMessageHandler;
while (handler != null)
{
yield return handler;
DelegatingHandler delegating = handler as DelegatingHandler;
handler = delegating != null ? delegating.InnerHandler : null;
}
}
}
/// <summary>
/// Initializes a new instance of the <see cref="Kubernetes"/> class.
/// Reference to the first HTTP handler (which is the start of send HTTP
/// pipeline).
/// </summary>
/// <param name='handlers'>
/// Optional. The delegating handlers to add to the http client pipeline.
/// </param>
protected Kubernetes(params DelegatingHandler[] handlers)
: base(handlers)
{
Initialize();
}
private HttpMessageHandler FirstMessageHandler { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="Kubernetes"/> class.
/// Reference to the innermost HTTP handler (which is the end of send HTTP
/// pipeline).
/// </summary>
/// <param name='rootHandler'>
/// Optional. The http client handler used to handle http transport.
/// </param>
/// <param name='handlers'>
/// Optional. The delegating handlers to add to the http client pipeline.
/// </param>
protected Kubernetes(HttpClientHandler rootHandler, params DelegatingHandler[] handlers)
: base(rootHandler, handlers)
{
Initialize();
}
#if NET5_0_OR_GREATER
private SocketsHttpHandler HttpClientHandler { get; set; }
#else
private HttpClientHandler HttpClientHandler { get; set; }
#endif
/// <summary>
/// Initializes a new instance of the <see cref="Kubernetes"/> class.
/// </summary>
/// <param name='baseUri'>
/// Optional. The base URI of the service.
/// </param>
/// <param name='handlers'>
/// Optional. The delegating handlers to add to the http client pipeline.
/// </param>
/// <exception cref="ArgumentNullException">
/// Thrown when a required parameter is null
/// </exception>
protected Kubernetes(Uri baseUri, params DelegatingHandler[] handlers)
: this(handlers)
{
BaseUri = baseUri ?? throw new ArgumentNullException(nameof(baseUri));
}
/// <summary>
/// Initializes a new instance of the <see cref="Kubernetes"/> class.
/// </summary>
/// <param name='baseUri'>
/// Optional. The base URI of the service.
/// </param>
/// <param name='rootHandler'>
/// Optional. The http client handler used to handle http transport.
/// </param>
/// <param name='handlers'>
/// Optional. The delegating handlers to add to the http client pipeline.
/// </param>
/// <exception cref="ArgumentNullException">
/// Thrown when a required parameter is null
/// </exception>
protected Kubernetes(Uri baseUri, HttpClientHandler rootHandler, params DelegatingHandler[] handlers)
: this(rootHandler, handlers)
{
BaseUri = baseUri ?? throw new ArgumentNullException(nameof(baseUri));
}
/// <summary>
/// Initializes a new instance of the <see cref="Kubernetes"/> class.
/// </summary>
/// <param name='credentials'>
/// Required. Subscription credentials which uniquely identify client subscription.
/// </param>
/// <param name='handlers'>
/// Optional. The delegating handlers to add to the http client pipeline.
/// </param>
/// <exception cref="ArgumentNullException">
/// Thrown when a required parameter is null
/// </exception>
public Kubernetes(ServiceClientCredentials credentials, params DelegatingHandler[] handlers)
: this(handlers)
{
Credentials = credentials ?? throw new ArgumentNullException(nameof(credentials));
Credentials.InitializeServiceClient(this);
}
/// <summary>
/// Initializes a new instance of the <see cref="Kubernetes"/> class.
/// </summary>
/// <param name='credentials'>
/// Required. Subscription credentials which uniquely identify client subscription.
/// </param>
/// <param name='httpClient'>
/// HttpClient to be used
/// </param>
/// <param name='disposeHttpClient'>
/// True: will dispose the provided httpClient on calling Kubernetes.Dispose(). False: will not dispose provided httpClient</param>
/// <exception cref="ArgumentNullException">
/// Thrown when a required parameter is null
/// </exception>
[Obsolete]
public Kubernetes(ServiceClientCredentials credentials, HttpClient httpClient, bool disposeHttpClient)
: this(httpClient, disposeHttpClient)
{
Credentials = credentials ?? throw new ArgumentNullException(nameof(credentials));
Credentials.InitializeServiceClient(this);
}
/// <summary>
/// Initializes a new instance of the <see cref="Kubernetes"/> class.
/// </summary>
/// <param name='credentials'>
/// Required. Subscription credentials which uniquely identify client subscription.
/// </param>
/// <param name='rootHandler'>
/// Optional. The http client handler used to handle http transport.
/// </param>
/// <param name='handlers'>
/// Optional. The delegating handlers to add to the http client pipeline.
/// </param>
/// <exception cref="ArgumentNullException">
/// Thrown when a required parameter is null
/// </exception>
public Kubernetes(ServiceClientCredentials credentials, HttpClientHandler rootHandler, params DelegatingHandler[] handlers)
: this(rootHandler, handlers)
{
Credentials = credentials ?? throw new ArgumentNullException(nameof(credentials));
Credentials.InitializeServiceClient(this);
}
/// <summary>
/// Initializes a new instance of the <see cref="Kubernetes"/> class.
/// </summary>
/// <param name='baseUri'>
/// Optional. The base URI of the service.
/// </param>
/// <param name='credentials'>
/// Required. Subscription credentials which uniquely identify client subscription.
/// </param>
/// <param name='handlers'>
/// Optional. The delegating handlers to add to the http client pipeline.
/// </param>
/// <exception cref="ArgumentNullException">
/// Thrown when a required parameter is null
/// </exception>
public Kubernetes(Uri baseUri, ServiceClientCredentials credentials, params DelegatingHandler[] handlers)
: this(handlers)
{
BaseUri = baseUri ?? throw new ArgumentNullException(nameof(baseUri));
Credentials = credentials ?? throw new ArgumentNullException(nameof(credentials));
Credentials.InitializeServiceClient(this);
}
/// <summary>
/// Initializes a new instance of the <see cref="Kubernetes"/> class.
/// </summary>
/// <param name='baseUri'>
/// Optional. The base URI of the service.
/// </param>
/// <param name='credentials'>
/// Required. Subscription credentials which uniquely identify client subscription.
/// </param>
/// <param name='rootHandler'>
/// Optional. The http client handler used to handle http transport.
/// </param>
/// <param name='handlers'>
/// Optional. The delegating handlers to add to the http client pipeline.
/// </param>
/// <exception cref="ArgumentNullException">
/// Thrown when a required parameter is null
/// </exception>
public Kubernetes(Uri baseUri, ServiceClientCredentials credentials, HttpClientHandler rootHandler, params DelegatingHandler[] handlers)
: this(rootHandler, handlers)
{
BaseUri = baseUri ?? throw new ArgumentNullException(nameof(baseUri));
Credentials = credentials ?? throw new ArgumentNullException(nameof(credentials));
Credentials.InitializeServiceClient(this);
}
/// <summary>
/// Initializes client properties.
@@ -227,7 +70,6 @@ namespace k8s
private void Initialize()
{
BaseUri = new Uri("http://localhost");
CustomInitialize();
}
private async Task<HttpOperationResponse<T>> CreateResultAsync<T>(HttpRequestMessage httpRequest, HttpResponseMessage httpResponse, bool? watch, CancellationToken cancellationToken)
@@ -250,11 +92,11 @@ namespace k8s
result.Body = KubernetesJson.Deserialize<T>(stream);
}
}
catch (JsonException ex)
catch (JsonException)
{
httpRequest.Dispose();
httpResponse.Dispose();
throw new SerializationException("Unable to deserialize the response.", ex);
throw;
}
return result;
@@ -370,5 +212,37 @@ namespace k8s
return httpResponse;
}
/// <summary>
/// Indicates whether the ServiceClient has been disposed.
/// </summary>
private bool _disposed;
/// <summary>
/// Dispose the ServiceClient.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Dispose the HttpClient and Handlers.
/// </summary>
/// <param name="disposing">True to release both managed and unmanaged resources; false to releases only unmanaged resources.</param>
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
_disposed = true;
// Dispose the client
HttpClient?.Dispose();
HttpClient = null;
FirstMessageHandler = null;
HttpClientHandler = null;
}
}
}
}

View File

@@ -46,7 +46,6 @@
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
<PackageReference Include="IdentityModel.OidcClient" Version="4.0.0" />
<PackageReference Include="Microsoft.Rest.ClientRuntime" Version="2.3.23" />
<PackageReference Include="Portable.BouncyCastle" Version="1.8.10" Condition="'$(TargetFramework)' == 'netstandard2.1'" />
<PackageReference Include="System.Text.Json" Version="6.0.0" Coondition="'$(TargetFramework)' == 'netstandard2.1'" />

View File

@@ -1,50 +0,0 @@
using System.Net.Http;
namespace k8s
{
public partial class KubernetesClientConfiguration
{
public HttpClientHandler CreateDefaultHttpClientHandler()
{
var httpClientHandler = new HttpClientHandler();
var uriScheme = new Uri(this.Host).Scheme;
if (uriScheme == "https")
{
if (SkipTlsVerify)
{
httpClientHandler.ServerCertificateCustomValidationCallback =
(sender, certificate, chain, sslPolicyErrors) => true;
}
else
{
httpClientHandler.ServerCertificateCustomValidationCallback =
(sender, certificate, chain, sslPolicyErrors) =>
{
return Kubernetes.CertificateValidationCallBack(sender, SslCaCerts, certificate, chain,
sslPolicyErrors);
};
}
}
AddCertificates(httpClientHandler);
return httpClientHandler;
}
public void AddCertificates(HttpClientHandler handler)
{
if (handler == null)
{
throw new ArgumentNullException(nameof(handler));
}
var clientCert = CertUtils.GetClientCert(this);
if (clientCert != null)
{
handler.ClientCertificates.Add(clientCert);
}
}
}
}

View File

@@ -1,5 +1,5 @@
using System.Security.Cryptography.X509Certificates;
using Microsoft.Rest;
using k8s.Autorest;
namespace k8s
{
@@ -83,14 +83,6 @@ namespace k8s
/// <value>The access token.</value>
public ITokenProvider TokenProvider { get; set; }
/// <summary>
/// Set true to enable tcp keep alive
/// You have to set https://tldp.org/HOWTO/TCP-Keepalive-HOWTO/usingkeepalive.html as well
/// </summary>
/// <value>true or false</value>
public bool TcpKeepAlive { get; set; } = true;
/// <summary>
/// Timeout of REST calls to Kubernetes server
/// Does not apply to watch related api

View File

@@ -1,4 +1,4 @@
using Microsoft.Rest;
using k8s.Autorest;
namespace k8s.Models
{

View File

@@ -1,7 +1,7 @@
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Rest;
using k8s.Autorest;
namespace k8s.LeaderElection
{

View File

@@ -1,7 +1,7 @@
using System.Threading;
using System.Threading.Tasks;
using k8s.Models;
using Microsoft.Rest;
using k8s.Autorest;
namespace k8s.LeaderElection.ResourceLock

View File

@@ -2,7 +2,7 @@ using System.Threading;
using System.Threading.Tasks;
using k8s.Models;
using k8s.Util.Common.Generic.Options;
using Microsoft.Rest;
using k8s.Autorest;
namespace k8s.Util.Common.Generic
{
@@ -43,13 +43,6 @@ namespace k8s.Util.Common.Generic
_client = apiClient ?? new Kubernetes(KubernetesClientConfiguration.BuildDefaultConfig());
}
public TimeSpan ClientTimeout => _client.HttpClient.Timeout;
public void SetClientTimeout(TimeSpan value)
{
_client.HttpClient.Timeout = value;
}
/// <summary>
/// Get kubernetes object.
/// </summary>

View File

@@ -1,7 +1,7 @@
using System.IO;
using System.Threading.Tasks;
using k8s.Exceptions;
using Microsoft.Rest;
using k8s.Autorest;
namespace k8s
{

View File

@@ -11,7 +11,7 @@ using Json.Patch;
using k8s.LeaderElection;
using k8s.LeaderElection.ResourceLock;
using k8s.Models;
using Microsoft.Rest;
using k8s.Autorest;
using Nito.AsyncEx;
using Xunit;

View File

@@ -16,7 +16,7 @@ using k8s.Models;
using k8s.Tests.Mock;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel.Https;
using Microsoft.Rest;
using k8s.Autorest;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security;

View File

@@ -3,7 +3,7 @@
*/
using k8s.Tests.Mock;
using Microsoft.Rest;
using k8s.Autorest;
using System;
using System.Collections.Generic;
using System.Threading;
@@ -24,13 +24,12 @@ namespace k8s.Tests
[Fact]
public async Task WebSocketNamespacedPodExecAsync()
{
var credentials = new BasicAuthenticationCredentials()
Kubernetes client = new Kubernetes(new KubernetesClientConfiguration()
{
UserName = "my-user",
Host = "http://localhost",
Username = "my-user",
Password = "my-secret-password",
};
Kubernetes client = new Kubernetes(credentials);
});
client.BaseUri = new Uri("http://localhost");
MockWebSocketBuilder mockWebSocketBuilder = new MockWebSocketBuilder();
@@ -71,14 +70,12 @@ namespace k8s.Tests
[Fact]
public async Task WebSocketNamespacedPodPortForwardAsync()
{
var credentials = new BasicAuthenticationCredentials()
Kubernetes client = new Kubernetes(new KubernetesClientConfiguration()
{
UserName = "my-user",
Host = "http://localhost",
Username = "my-user",
Password = "my-secret-password",
};
Kubernetes client = new Kubernetes(credentials);
client.BaseUri = new Uri("http://localhost");
});
MockWebSocketBuilder mockWebSocketBuilder = new MockWebSocketBuilder();
client.CreateWebSocketBuilder = () => mockWebSocketBuilder;
@@ -112,13 +109,12 @@ namespace k8s.Tests
[Fact]
public async Task WebSocketNamespacedPodAttachAsync()
{
var credentials = new BasicAuthenticationCredentials()
Kubernetes client = new Kubernetes(new KubernetesClientConfiguration()
{
UserName = "my-user",
Host = "http://localhost",
Username = "my-user",
Password = "my-secret-password",
};
Kubernetes client = new Kubernetes(credentials);
});
client.BaseUri = new Uri("http://localhost");
MockWebSocketBuilder mockWebSocketBuilder = new MockWebSocketBuilder();

View File

@@ -3,7 +3,7 @@
*/
using k8s.Models;
using Microsoft.Rest;
using k8s.Autorest;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -193,7 +193,7 @@ namespace k8s.Tests
muxedStream.Setup(m => m.GetStream(ChannelIndex.Error, null)).Returns(errorStream);
var kubernetesMock = new Moq.Mock<Kubernetes>(
new object[] { Moq.Mock.Of<ServiceClientCredentials>(), new DelegatingHandler[] { } })
new object[] { new KubernetesClientConfiguration() { Host = "http://localhost" }, new DelegatingHandler[] { } })
{ CallBase = true };
var command = new string[] { "/bin/bash", "-c", "echo Hello, World!" };
@@ -216,7 +216,7 @@ namespace k8s.Tests
public async Task NamespacedPodExecAsyncHttpExceptionWithStatus()
{
var kubernetesMock = new Moq.Mock<Kubernetes>(
new object[] { Moq.Mock.Of<ServiceClientCredentials>(), new DelegatingHandler[] { } })
new object[] { new KubernetesClientConfiguration() { Host = "http://localhost" }, new DelegatingHandler[] { } })
{ CallBase = true };
var command = new string[] { "/bin/bash", "-c", "echo Hello, World!" };
var handler = new ExecAsyncCallback((stdIn, stdOut, stdError) => Task.CompletedTask);
@@ -241,7 +241,7 @@ namespace k8s.Tests
public async Task NamespacedPodExecAsyncHttpExceptionNoStatus()
{
var kubernetesMock = new Moq.Mock<Kubernetes>(
new object[] { Moq.Mock.Of<ServiceClientCredentials>(), new DelegatingHandler[] { } })
new object[] { new KubernetesClientConfiguration() { Host = "http://localhost" }, new DelegatingHandler[] { } })
{ CallBase = true };
var command = new string[] { "/bin/bash", "-c", "echo Hello, World!" };
var handler = new ExecAsyncCallback((stdIn, stdOut, stdError) => Task.CompletedTask);
@@ -265,7 +265,7 @@ namespace k8s.Tests
public async Task NamespacedPodExecAsyncGenericException()
{
var kubernetesMock = new Moq.Mock<Kubernetes>(
new object[] { Moq.Mock.Of<ServiceClientCredentials>(), new DelegatingHandler[] { } })
new object[] { new KubernetesClientConfiguration() { Host = "http://localhost" }, new DelegatingHandler[] { } })
{ CallBase = true };
var command = new string[] { "/bin/bash", "-c", "echo Hello, World!" };
var handler = new ExecAsyncCallback((stdIn, stdOut, stdError) => Task.CompletedTask);
@@ -319,7 +319,7 @@ namespace k8s.Tests
muxedStream.Setup(m => m.GetStream(ChannelIndex.Error, null)).Returns(errorStream);
var kubernetesMock = new Moq.Mock<Kubernetes>(
new object[] { Moq.Mock.Of<ServiceClientCredentials>(), new DelegatingHandler[] { } })
new object[] { new KubernetesClientConfiguration() { Host = "http://localhost" }, new DelegatingHandler[] { } })
{ CallBase = true };
var command = new string[] { "/bin/bash", "-c", "echo Hello, World!" };

View File

@@ -4,7 +4,7 @@ using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Rest;
using k8s.Autorest;
using System;
using System.IO;
using System.Net.WebSockets;
@@ -132,7 +132,10 @@ namespace k8s.Tests
/// </returns>
protected virtual Kubernetes CreateTestClient(ServiceClientCredentials credentials = null)
{
return new Kubernetes(credentials ?? AnonymousClientCredentials.Instance) { BaseUri = ServerBaseAddress };
return new Kubernetes(new KubernetesClientConfiguration()
{
Host = ServerBaseAddress.ToString(),
});
}
/// <summary>

View File

@@ -1,6 +1,6 @@
{
"$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
"version": "7.0",
"version": "7.1",
"publicReleaseRefSpec": [
"^refs/heads/master$", // we release out of master
],