Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions examples/prometheus/Prometheus.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using k8s;
using Prometheus;
using System;
using System.Net.Http;
using System.Threading;

namespace prom
Expand All @@ -12,7 +11,7 @@ private static void Main(string[] args)
{
var config = KubernetesClientConfiguration.BuildDefaultConfig();
var handler = new PrometheusHandler();
IKubernetes client = new Kubernetes(config, new DelegatingHandler[] { handler });
IKubernetes client = new Kubernetes(config, handler);

var server = new MetricServer(hostname: "localhost", port: 1234);
server.Start();
Expand Down
28 changes: 22 additions & 6 deletions src/KubernetesClient.Models/KubernetesJson.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,20 +66,36 @@ static KubernetesJson()
JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
}

public static TValue Deserialize<TValue>(string json)
/// <summary>
/// Configures <see cref="JsonSerializerOptions"/> for the <see cref="JsonSerializer"/>.
/// To override existing converters, add them to the top of the <see cref="JsonSerializerOptions.Converters"/> list
/// e.g. as follows: <code>options.Converters.Insert(index: 0, new JsonStringEnumConverter(JsonNamingPolicy.CamelCase));</code>
/// </summary>
/// <param name="configure">An <see cref="Action"/> to configure the <see cref="JsonSerializerOptions"/>.</param>
public static void AddJsonOptions(Action<JsonSerializerOptions> configure)
{
return JsonSerializer.Deserialize<TValue>(json, JsonSerializerOptions);
if (configure is null)
{
throw new ArgumentNullException(nameof(configure));
}

configure(JsonSerializerOptions);
}

public static TValue Deserialize<TValue>(string json, JsonSerializerOptions jsonSerializerOptions = null)
{
return JsonSerializer.Deserialize<TValue>(json, jsonSerializerOptions ?? JsonSerializerOptions);
}

public static TValue Deserialize<TValue>(Stream json)
public static TValue Deserialize<TValue>(Stream json, JsonSerializerOptions jsonSerializerOptions = null)
{
return JsonSerializer.Deserialize<TValue>(json, JsonSerializerOptions);
return JsonSerializer.Deserialize<TValue>(json, jsonSerializerOptions ?? JsonSerializerOptions);
}


public static string Serialize(object value)
public static string Serialize(object value, JsonSerializerOptions jsonSerializerOptions = null)
{
return JsonSerializer.Serialize(value, JsonSerializerOptions);
return JsonSerializer.Serialize(value, jsonSerializerOptions ?? JsonSerializerOptions);
}
}
}
5 changes: 3 additions & 2 deletions src/KubernetesClient/Kubernetes.ConfigInit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ namespace k8s
{
public partial class Kubernetes
{
private readonly JsonSerializerOptions jsonSerializerOptions;

/// <summary>
/// Initializes a new instance of the <see cref="Kubernetes" /> class.
/// </summary>
Expand All @@ -27,6 +29,7 @@ public Kubernetes(KubernetesClientConfiguration config, params DelegatingHandler
CreateHttpClient(handlers, config);
InitializeFromConfig(config);
HttpClientTimeout = config.HttpClientTimeout;
jsonSerializerOptions = config.JsonSerializerOptions;
#if NETSTANDARD2_1_OR_GREATER || NET5_0_OR_GREATER
DisableHttp2 = config.DisableHttp2;
#endif
Expand Down Expand Up @@ -155,8 +158,6 @@ private void CreateHttpClient(DelegatingHandler[] handlers, KubernetesClientConf
};
}



/// <summary>
/// Set credentials for the Client
/// </summary>
Expand Down
4 changes: 2 additions & 2 deletions src/KubernetesClient/Kubernetes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ protected override async Task<HttpOperationResponse<T>> CreateResultAsync<T>(Htt
using (Stream stream = await httpResponse.Content.ReadAsStreamAsync().ConfigureAwait(false))
#endif
{
result.Body = KubernetesJson.Deserialize<T>(stream);
result.Body = KubernetesJson.Deserialize<T>(stream, jsonSerializerOptions);
}
}
catch (JsonException)
Expand Down Expand Up @@ -126,7 +126,7 @@ protected override Task<HttpResponseMessage> SendRequest<T>(string relativeUri,

if (body != null)
{
var requestContent = KubernetesJson.Serialize(body);
var requestContent = KubernetesJson.Serialize(body, jsonSerializerOptions);
httpRequest.Content = new StringContent(requestContent, System.Text.Encoding.UTF8);
httpRequest.Content.Headers.ContentType = GetHeader(body);
return SendRequestRaw(requestContent, httpRequest, cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@ public static Process CreateRunnableExternalProcess(ExternalExecution config)
process.StartInfo.RedirectStandardError = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;

return process;
}

Expand Down
35 changes: 35 additions & 0 deletions src/KubernetesClient/KubernetesClientConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ namespace k8s
/// </summary>
public partial class KubernetesClientConfiguration
{
private JsonSerializerOptions jsonSerializerOptions;

/// <summary>
/// Gets current namespace
/// </summary>
Expand Down Expand Up @@ -108,5 +110,38 @@ public partial class KubernetesClientConfiguration
/// Do not use http2 even it is available
/// </summary>
public bool DisableHttp2 { get; set; } = false;

/// <summary>
/// Options for the <see cref="JsonSerializer"/> to override the default ones.
/// </summary>
public JsonSerializerOptions JsonSerializerOptions
{
get
{
// If not yet set, use defaults from KubernetesJson.
if (jsonSerializerOptions is null)
{
KubernetesJson.AddJsonOptions(options =>
{
jsonSerializerOptions = new JsonSerializerOptions(options);
});
}

return jsonSerializerOptions;
}

private set => jsonSerializerOptions = value;
}

/// <inheritdoc cref="KubernetesJson.AddJsonOptions(Action{JsonSerializerOptions})"/>
public void AddJsonOptions(Action<JsonSerializerOptions> configure)
{
if (configure is null)
{
throw new ArgumentNullException(nameof(configure));
}

configure(JsonSerializerOptions);
}
}
}
79 changes: 79 additions & 0 deletions tests/KubernetesClient.Tests/SerializationTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using k8s.Tests.Mock;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;

namespace k8s.Tests
{
public class SerializationTests
{
private readonly ITestOutputHelper testOutput;

private enum Animals
{
Dog,
Cat,
Mouse,
}

public SerializationTests(ITestOutputHelper testOutput)
{
this.testOutput = testOutput;
}

[Fact]
public async Task SerializeEnumUsingPascalCase()
{
using var server = new MockKubeApiServer(testOutput);

var config = new KubernetesClientConfiguration { Host = server.Uri.ToString() };
config.AddJsonOptions(options =>
{
// Insert the converter at the front of the list so it overrides any others.
options.Converters.Insert(index: 0, new JsonStringEnumConverter());
});
var client = new Kubernetes(config);

var customObject = Animals.Dog;

var result = await client.CustomObjects.CreateNamespacedCustomObjectWithHttpMessagesAsync(customObject, "TestGroup", "TestVersion", "TestNamespace", "TestPlural").ConfigureAwait(false);
var content = await result.Request.Content.ReadAsStringAsync().ConfigureAwait(false);

// Assert that the client serializes using the default options.
Assert.Equal(@"""Dog""", content);

// Assert that the underlying KubernetesJson serializes using the default options.
string animal = KubernetesJson.Serialize(Animals.Cat);
Assert.Equal(@"""Cat""", animal);
}

[Fact]
public async Task SerializeEnumUsingCamelCase()
{
using var server = new MockKubeApiServer(testOutput);

var config = new KubernetesClientConfiguration { Host = server.Uri.ToString() };
config.AddJsonOptions(options =>
{
// Insert the converter at the front of the list so it overrides
// the default JsonStringEnumConverter without namingPolicy.
options.Converters.Insert(index: 0, new JsonStringEnumConverter(JsonNamingPolicy.CamelCase));
});
var client = new Kubernetes(config);

var customObject = Animals.Dog;

var result = await client.CustomObjects.CreateNamespacedCustomObjectWithHttpMessagesAsync(customObject, "TestGroup", "TestVersion", "TestNamespace", "TestPlural").ConfigureAwait(false);
var content = await result.Request.Content.ReadAsStringAsync().ConfigureAwait(false);

// Assert that the client serializes using the specified options.
Assert.Equal(@"""dog""", content);

// Assert that the underlying KubernetesJson serializes using the default options.
string animal = KubernetesJson.Serialize(Animals.Cat);
Assert.Equal(@"""Cat""", animal);
}
}
}
13 changes: 7 additions & 6 deletions tests/KubernetesClient.Tests/WatchTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -445,8 +445,8 @@ public async Task TestWatchWithHandlers()
var handler1 = new DummyHandler();
var handler2 = new DummyHandler();

var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() }, handler1,
handler2);
var client = new Kubernetes(
new KubernetesClientConfiguration { Host = server.Uri.ToString() }, handler1, handler2);

Assert.False(handler1.Called);
Assert.False(handler2.Called);
Expand Down Expand Up @@ -732,12 +732,13 @@ public async Task MustHttp2VersionSet()
return false;
});

var h = new CheckHeaderDelegatingHandler();
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() }, h);
var handler = new CheckHeaderDelegatingHandler();
var client = new Kubernetes(
new KubernetesClientConfiguration { Host = server.Uri.ToString() }, handler);

Assert.Null(h.Version);
Assert.Null(handler.Version);
await client.CoreV1.ListNamespacedPodWithHttpMessagesAsync("default", watch: true).ConfigureAwait(false);
Assert.Equal(HttpVersion.Version20, h.Version);
Assert.Equal(HttpVersion.Version20, handler.Version);
}
}
}