diff --git a/Directory.Packages.props b/Directory.Packages.props
index 45f27d6cf..2e1b01456 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -50,4 +50,4 @@
-
\ No newline at end of file
+
diff --git a/examples/clientset/Program.cs b/examples/clientset/Program.cs
new file mode 100644
index 000000000..9c2cac461
--- /dev/null
+++ b/examples/clientset/Program.cs
@@ -0,0 +1,26 @@
+// See https://aka.ms/new-console-template for more information
+using k8s;
+using k8s.ClientSets;
+using System.Threading.Tasks;
+
+namespace clientset
+{
+ internal class Program
+ {
+ private static async Task Main(string[] args)
+ {
+ var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
+ var client = new Kubernetes(config);
+
+ ClientSet clientSet = new ClientSet(client);
+ var list = await clientSet.CoreV1.Pod.ListAsync("default").ConfigureAwait(false);
+ foreach (var item in list)
+ {
+ System.Console.WriteLine(item.Metadata.Name);
+ }
+
+ var pod = await clientSet.CoreV1.Pod.GetAsync("test","default").ConfigureAwait(false);
+ System.Console.WriteLine(pod?.Metadata?.Name);
+ }
+ }
+}
diff --git a/examples/clientset/clientset.csproj b/examples/clientset/clientset.csproj
new file mode 100644
index 000000000..4274ceb02
--- /dev/null
+++ b/examples/clientset/clientset.csproj
@@ -0,0 +1,6 @@
+
+
+ Exe
+
+
+
diff --git a/src/KubernetesClient.Aot/KubernetesClient.Aot.csproj b/src/KubernetesClient.Aot/KubernetesClient.Aot.csproj
index 226f818e6..88fe09488 100644
--- a/src/KubernetesClient.Aot/KubernetesClient.Aot.csproj
+++ b/src/KubernetesClient.Aot/KubernetesClient.Aot.csproj
@@ -46,6 +46,10 @@
+
+
+
+
@@ -107,4 +111,4 @@
-
\ No newline at end of file
+
diff --git a/src/KubernetesClient.Classic/KubernetesClient.Classic.csproj b/src/KubernetesClient.Classic/KubernetesClient.Classic.csproj
index bc8ad033a..254e745f3 100644
--- a/src/KubernetesClient.Classic/KubernetesClient.Classic.csproj
+++ b/src/KubernetesClient.Classic/KubernetesClient.Classic.csproj
@@ -75,7 +75,10 @@
-
+
+
+
+
diff --git a/src/KubernetesClient/ClientSets/ClientSet.cs b/src/KubernetesClient/ClientSets/ClientSet.cs
new file mode 100644
index 000000000..53aa37df9
--- /dev/null
+++ b/src/KubernetesClient/ClientSets/ClientSet.cs
@@ -0,0 +1,16 @@
+namespace k8s.ClientSets
+{
+ ///
+ /// Represents a base class for clients that interact with Kubernetes resources.
+ /// Provides shared functionality for derived resource-specific clients.
+ ///
+ public partial class ClientSet
+ {
+ private readonly Kubernetes _kubernetes;
+
+ public ClientSet(Kubernetes kubernetes)
+ {
+ _kubernetes = kubernetes;
+ }
+ }
+}
diff --git a/src/KubernetesClient/ClientSets/ResourceClient.cs b/src/KubernetesClient/ClientSets/ResourceClient.cs
new file mode 100644
index 000000000..bbf1c43e8
--- /dev/null
+++ b/src/KubernetesClient/ClientSets/ResourceClient.cs
@@ -0,0 +1,16 @@
+namespace k8s.ClientSets
+{
+ ///
+ /// Represents a set of Kubernetes clients for interacting with the Kubernetes API.
+ /// This class provides access to various client implementations for managing Kubernetes resources.
+ ///
+ public abstract class ResourceClient
+ {
+ protected Kubernetes Client { get; }
+
+ public ResourceClient(Kubernetes kubernetes)
+ {
+ Client = kubernetes;
+ }
+ }
+}
diff --git a/src/KubernetesClient/Kubernetes.WebSocket.cs b/src/KubernetesClient/Kubernetes.WebSocket.cs
index a3a5bc908..aeca29708 100644
--- a/src/KubernetesClient/Kubernetes.WebSocket.cs
+++ b/src/KubernetesClient/Kubernetes.WebSocket.cs
@@ -18,11 +18,11 @@ public partial class Kubernetes
///
public Task WebSocketNamespacedPodExecAsync(string name, string @namespace = "default",
string command = null, string container = null, bool stderr = true, bool stdin = true, bool stdout = true,
- bool tty = true, string webSocketSubProtol = null, Dictionary> customHeaders = null,
+ bool tty = true, string webSocketSubProtocol = null, Dictionary> customHeaders = null,
CancellationToken cancellationToken = default)
{
return WebSocketNamespacedPodExecAsync(name, @namespace, new string[] { command }, container, stderr, stdin,
- stdout, tty, webSocketSubProtol, customHeaders, cancellationToken);
+ stdout, tty, webSocketSubProtocol, customHeaders, cancellationToken);
}
///
@@ -30,7 +30,7 @@ public virtual async Task MuxedStreamNamespacedPodExecAsync(
string name,
string @namespace = "default", IEnumerable command = null, string container = null,
bool stderr = true, bool stdin = true, bool stdout = true, bool tty = true,
- string webSocketSubProtol = WebSocketProtocol.V4BinaryWebsocketProtocol,
+ string webSocketSubProtocol = WebSocketProtocol.V4BinaryWebsocketProtocol,
Dictionary> customHeaders = null,
CancellationToken cancellationToken = default)
{
@@ -45,7 +45,7 @@ public virtual async Task MuxedStreamNamespacedPodExecAsync(
public virtual Task WebSocketNamespacedPodExecAsync(string name, string @namespace = "default",
IEnumerable command = null, string container = null, bool stderr = true, bool stdin = true,
bool stdout = true, bool tty = true,
- string webSocketSubProtol = WebSocketProtocol.V4BinaryWebsocketProtocol,
+ string webSocketSubProtocol = WebSocketProtocol.V4BinaryWebsocketProtocol,
Dictionary> customHeaders = null,
CancellationToken cancellationToken = default)
{
@@ -114,7 +114,7 @@ public virtual Task WebSocketNamespacedPodExecAsync(string name, stri
uriBuilder.Query =
query.ToString(1, query.Length - 1); // UriBuilder.Query doesn't like leading '?' chars, so trim it
- return StreamConnectAsync(uriBuilder.Uri, webSocketSubProtol, customHeaders,
+ return StreamConnectAsync(uriBuilder.Uri, webSocketSubProtocol, customHeaders,
cancellationToken);
}
@@ -173,7 +173,7 @@ public Task WebSocketNamespacedPodPortForwardAsync(string name, strin
///
public Task WebSocketNamespacedPodAttachAsync(string name, string @namespace,
string container = default, bool stderr = true, bool stdin = false, bool stdout = true,
- bool tty = false, string webSocketSubProtol = null, Dictionary> customHeaders = null,
+ bool tty = false, string webSocketSubProtocol = null, Dictionary> customHeaders = null,
CancellationToken cancellationToken = default)
{
if (name == null)
@@ -208,7 +208,7 @@ public Task WebSocketNamespacedPodAttachAsync(string name, string @na
uriBuilder.Query =
query.ToString(1, query.Length - 1); // UriBuilder.Query doesn't like leading '?' chars, so trim it
- return StreamConnectAsync(uriBuilder.Uri, webSocketSubProtol, customHeaders,
+ return StreamConnectAsync(uriBuilder.Uri, webSocketSubProtocol, customHeaders,
cancellationToken);
}
diff --git a/src/LibKubernetesGenerator/ClientSetGenerator.cs b/src/LibKubernetesGenerator/ClientSetGenerator.cs
new file mode 100644
index 000000000..a0b392d8b
--- /dev/null
+++ b/src/LibKubernetesGenerator/ClientSetGenerator.cs
@@ -0,0 +1,103 @@
+using CaseExtensions;
+using Microsoft.CodeAnalysis;
+using NSwag;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace LibKubernetesGenerator
+{
+ internal class ClientSetGenerator
+ {
+ private readonly ScriptObjectFactory _scriptObjectFactory;
+
+ public ClientSetGenerator(ScriptObjectFactory scriptObjectFactory)
+ {
+ _scriptObjectFactory = scriptObjectFactory;
+ }
+
+ public void Generate(OpenApiDocument swagger, IncrementalGeneratorPostInitializationContext context)
+ {
+ var data = swagger.Operations
+ .Where(o => o.Method != OpenApiOperationMethod.Options)
+ .Select(o =>
+ {
+ var ps = o.Operation.ActualParameters.OrderBy(p => !p.IsRequired).ToArray();
+
+ o.Operation.Parameters.Clear();
+
+ var name = new HashSet();
+
+ var i = 1;
+ foreach (var p in ps)
+ {
+ if (name.Contains(p.Name))
+ {
+ p.Name += i++;
+ }
+
+ o.Operation.Parameters.Add(p);
+ name.Add(p.Name);
+ }
+
+ return o;
+ })
+ .Select(o =>
+ {
+ o.Path = o.Path.TrimStart('/');
+ o.Method = char.ToUpper(o.Method[0]) + o.Method.Substring(1);
+ return o;
+ })
+ .ToArray();
+
+ var sc = _scriptObjectFactory.CreateScriptObject();
+
+ var groups = new List();
+ var apiGroups = new Dictionary();
+
+ foreach (var grouped in data.Where(d => HasKubernetesAction(d.Operation?.ExtensionData))
+ .GroupBy(d => d.Operation.Tags.First()))
+ {
+ var clients = new List();
+ var name = grouped.Key.ToPascalCase();
+ groups.Add(name);
+ var apis = grouped.Select(x =>
+ {
+ var groupVersionKindElements = x.Operation?.ExtensionData?["x-kubernetes-group-version-kind"];
+ var groupVersionKind = (Dictionary)groupVersionKindElements;
+
+ return new { Kind = groupVersionKind?["kind"] as string, Api = x };
+ });
+
+ foreach (var item in apis.GroupBy(x => x.Kind))
+ {
+ var kind = item.Key;
+ apiGroups[kind] = item.Select(x => x.Api).ToArray();
+ clients.Add(kind);
+ }
+
+ sc.SetValue("clients", clients, true);
+ sc.SetValue("name", name, true);
+ context.RenderToContext("GroupClient.cs.template", sc, $"{name}GroupClient.g.cs");
+ }
+
+ foreach (var apiGroup in apiGroups)
+ {
+ var name = apiGroup.Key;
+ var apis = apiGroup.Value.ToArray();
+ var group = apis.Select(x => x.Operation.Tags[0]).First();
+ sc.SetValue("apis", apis, true);
+ sc.SetValue("name", name, true);
+ sc.SetValue("group", group.ToPascalCase(), true);
+ context.RenderToContext("Client.cs.template", sc, $"{name}Client.g.cs");
+ }
+
+ sc = _scriptObjectFactory.CreateScriptObject();
+ sc.SetValue("groups", groups, true);
+
+ context.RenderToContext("ClientSet.cs.template", sc, $"ClientSet.g.cs");
+ }
+
+ private bool HasKubernetesAction(IDictionary extensionData) =>
+ extensionData?.ContainsKey("x-kubernetes-action") ?? false;
+ }
+}
diff --git a/src/LibKubernetesGenerator/GeneralNameHelper.cs b/src/LibKubernetesGenerator/GeneralNameHelper.cs
index c9b5cf5af..15194bb90 100644
--- a/src/LibKubernetesGenerator/GeneralNameHelper.cs
+++ b/src/LibKubernetesGenerator/GeneralNameHelper.cs
@@ -21,7 +21,8 @@ public GeneralNameHelper(ClassNameHelper classNameHelper)
public void RegisterHelper(ScriptObject scriptObject)
{
scriptObject.Import(nameof(GetInterfaceName), new Func(GetInterfaceName));
- scriptObject.Import(nameof(GetMethodName), new Func(GetMethodName));
+ scriptObject.Import(nameof(GetOperationId), new Func(GetOperationId));
+ scriptObject.Import(nameof(GetActionName), new Func(GetActionName));
scriptObject.Import(nameof(GetDotNetName), new Func(GetDotNetName));
scriptObject.Import(nameof(GetDotNetNameOpenApiParameter), new Func(GetDotNetNameOpenApiParameter));
}
@@ -138,7 +139,7 @@ public string GetDotNetName(string jsonName, string style = "parameter")
return jsonName.ToCamelCase();
}
- public static string GetMethodName(OpenApiOperation watchOperation, string suffix)
+ public static string GetOperationId(OpenApiOperation watchOperation, string suffix)
{
var tag = watchOperation.Tags[0];
tag = tag.Replace("_", string.Empty);
@@ -162,5 +163,28 @@ public static string GetMethodName(OpenApiOperation watchOperation, string suffi
return methodName;
}
+
+ public static string GetActionName(OpenApiOperation apiOperation, string resource, string suffix)
+ {
+ var operationId = apiOperation.OperationId.ToPascalCase();
+ var replacements = new Dictionary(StringComparer.OrdinalIgnoreCase)
+ {
+ { "Replace", "Update" },
+ { "Read", "Get" },
+ };
+
+ foreach (var replacement in replacements)
+ {
+ operationId = Regex.Replace(operationId, replacement.Key, replacement.Value, RegexOptions.IgnoreCase);
+ }
+
+ var resources = new[] { resource, "ForAllNamespaces", "Namespaced" };
+ var pattern = string.Join("|", Array.ConvertAll(resources, Regex.Escape));
+ var actionName = pattern.Length > 0
+ ? Regex.Replace(operationId, pattern, string.Empty, RegexOptions.IgnoreCase)
+ : operationId;
+
+ return $"{actionName}{suffix}";
+ }
}
}
diff --git a/src/LibKubernetesGenerator/KubernetesClientSourceGenerator.cs b/src/LibKubernetesGenerator/KubernetesClientSourceGenerator.cs
index 0e6278fb4..6ea216f28 100644
--- a/src/LibKubernetesGenerator/KubernetesClientSourceGenerator.cs
+++ b/src/LibKubernetesGenerator/KubernetesClientSourceGenerator.cs
@@ -61,6 +61,7 @@ private static IContainer BuildContainer(OpenApiDocument swagger)
builder.RegisterType();
builder.RegisterType();
builder.RegisterType();
+ builder.RegisterType();
builder.RegisterType();
builder.RegisterType();
@@ -80,6 +81,7 @@ public void Initialize(IncrementalGeneratorInitializationContext generatorContex
container.Resolve().Generate(swagger, ctx);
container.Resolve().Generate(swagger, ctx);
container.Resolve().Generate(swagger, ctx);
+ container.Resolve().Generate(swagger, ctx);
});
#endif
diff --git a/src/LibKubernetesGenerator/LibKubernetesGenerator.target b/src/LibKubernetesGenerator/LibKubernetesGenerator.target
index d9240bcdd..916c1dd19 100644
--- a/src/LibKubernetesGenerator/LibKubernetesGenerator.target
+++ b/src/LibKubernetesGenerator/LibKubernetesGenerator.target
@@ -16,6 +16,7 @@
+
diff --git a/src/LibKubernetesGenerator/templates/Client.cs.template b/src/LibKubernetesGenerator/templates/Client.cs.template
new file mode 100644
index 000000000..02c55d7b1
--- /dev/null
+++ b/src/LibKubernetesGenerator/templates/Client.cs.template
@@ -0,0 +1,100 @@
+//
+// Code generated by https://github.com/kubernetes-client/csharp/tree/master/src/LibKubernetesGenerator
+// Changes may cause incorrect behavior and will be lost if the code is
+// regenerated.
+//
+using System.Net.Http;
+using System.Net.Http.Headers;
+
+namespace k8s.ClientSets;
+
+///
+///
+public partial class {{name}}Client : ResourceClient
+{
+ public {{name}}Client(Kubernetes kubernetes) : base(kubernetes)
+ {
+ }
+
+ {{for api in apis }}
+ ///
+ /// {{ToXmlDoc api.operation.description}}
+ ///
+ {{ for parameter in api.operation.parameters}}
+ ///
+ /// {{ToXmlDoc parameter.description}}
+ ///
+ {{end}}
+ ///
+ /// A which can be used to cancel the asynchronous operation.
+ ///
+ public async Task{{GetReturnType api.operation "<>"}} {{GetActionName api.operation name "Async"}}(
+ {{ for parameter in api.operation.parameters}}
+ {{GetDotNetTypeOpenApiParameter parameter}} {{GetDotNetNameOpenApiParameter parameter "true"}},
+ {{ end }}
+ CancellationToken cancellationToken = default(CancellationToken))
+ {
+ {{if IfReturnType api.operation "stream"}}
+ var _result = await Client.{{group}}.{{GetOperationId api.operation "WithHttpMessagesAsync"}}(
+ {{ for parameter in api.operation.parameters}}
+ {{GetDotNetNameOpenApiParameter parameter "false"}},
+ {{end}}
+ null,
+ cancellationToken);
+ _result.Request.Dispose();
+ {{GetReturnType api.operation "_result.Body"}};
+ {{end}}
+ {{if IfReturnType api.operation "obj"}}
+ using (var _result = await Client.{{group}}.{{GetOperationId api.operation "WithHttpMessagesAsync"}}(
+ {{ for parameter in api.operation.parameters}}
+ {{GetDotNetNameOpenApiParameter parameter "false"}},
+ {{end}}
+ null,
+ cancellationToken).ConfigureAwait(false))
+ {
+ {{GetReturnType api.operation "_result.Body"}};
+ }
+ {{end}}
+ {{if IfReturnType api.operation "void"}}
+ using (var _result = await Client.{{group}}.{{GetOperationId api.operation "WithHttpMessagesAsync"}}(
+ {{ for parameter in api.operation.parameters}}
+ {{GetDotNetNameOpenApiParameter parameter "false"}},
+ {{end}}
+ null,
+ cancellationToken).ConfigureAwait(false))
+ {
+ }
+ {{end}}
+ }
+
+ {{if IfReturnType api.operation "object"}}
+ ///
+ /// {{ToXmlDoc api.operation.description}}
+ ///
+ {{ for parameter in api.operation.parameters}}
+ ///
+ /// {{ToXmlDoc parameter.description}}
+ ///
+ {{end}}
+ ///
+ /// A which can be used to cancel the asynchronous operation.
+ ///
+ public async Task {{GetActionName api.operation name "Async"}}(
+ {{ for parameter in api.operation.parameters}}
+ {{GetDotNetTypeOpenApiParameter parameter}} {{GetDotNetNameOpenApiParameter parameter "false"}},
+ {{ end }}
+ CancellationToken cancellationToken = default(CancellationToken))
+ {
+ using (var _result = await Client.{{group}}.{{GetOperationId api.operation "WithHttpMessagesAsync"}}(
+ {{ for parameter in api.operation.parameters}}
+ {{GetDotNetNameOpenApiParameter parameter "false"}},
+ {{end}}
+ null,
+ cancellationToken).ConfigureAwait(false))
+ {
+ return _result.Body;
+ }
+ }
+ {{end}}
+ {{end}}
+}
diff --git a/src/LibKubernetesGenerator/templates/ClientSet.cs.template b/src/LibKubernetesGenerator/templates/ClientSet.cs.template
new file mode 100644
index 000000000..c358b2b3c
--- /dev/null
+++ b/src/LibKubernetesGenerator/templates/ClientSet.cs.template
@@ -0,0 +1,16 @@
+//
+// Code generated by https://github.com/kubernetes-client/csharp/tree/master/src/LibKubernetesGenerator
+// Changes may cause incorrect behavior and will be lost if the code is
+// regenerated.
+//
+
+namespace k8s.ClientSets;
+
+///
+///
+public partial class ClientSet
+{
+ {{for group in groups}}
+ public {{group}}GroupClient {{group}} => new {{group}}GroupClient(_kubernetes);
+ {{end}}
+}
diff --git a/src/LibKubernetesGenerator/templates/GroupClient.cs.template b/src/LibKubernetesGenerator/templates/GroupClient.cs.template
new file mode 100644
index 000000000..45f219e55
--- /dev/null
+++ b/src/LibKubernetesGenerator/templates/GroupClient.cs.template
@@ -0,0 +1,24 @@
+//
+// Code generated by https://github.com/kubernetes-client/csharp/tree/master/src/LibKubernetesGenerator
+// Changes may cause incorrect behavior and will be lost if the code is
+// regenerated.
+//
+
+namespace k8s.ClientSets;
+
+
+///
+///
+public partial class {{name}}GroupClient
+{
+ private readonly Kubernetes _kubernetes;
+
+ {{for client in clients}}
+ public {{client}}Client {{client}} => new {{client}}Client(_kubernetes);
+ {{end}}
+
+ public {{name}}GroupClient(Kubernetes kubernetes)
+ {
+ _kubernetes = kubernetes;
+ }
+}
diff --git a/src/LibKubernetesGenerator/templates/IOperations.cs.template b/src/LibKubernetesGenerator/templates/IOperations.cs.template
index c310acf9f..6904b8b91 100644
--- a/src/LibKubernetesGenerator/templates/IOperations.cs.template
+++ b/src/LibKubernetesGenerator/templates/IOperations.cs.template
@@ -25,7 +25,7 @@ public partial interface I{{name}}Operations
///
/// A which can be used to cancel the asynchronous operation.
///
- Task"}}> {{GetMethodName api.operation "WithHttpMessagesAsync"}}(
+ Task"}}> {{GetOperationId api.operation "WithHttpMessagesAsync"}}(
{{ for parameter in api.operation.parameters}}
{{GetDotNetTypeOpenApiParameter parameter}} {{GetDotNetNameOpenApiParameter parameter "true"}},
{{ end }}
@@ -47,7 +47,7 @@ public partial interface I{{name}}Operations
///
/// A which can be used to cancel the asynchronous operation.
///
- Task> {{GetMethodName api.operation "WithHttpMessagesAsync"}}(
+ Task> {{GetOperationId api.operation "WithHttpMessagesAsync"}}(
{{ for parameter in api.operation.parameters}}
{{GetDotNetTypeOpenApiParameter parameter}} {{GetDotNetNameOpenApiParameter parameter "true"}},
{{ end }}
diff --git a/src/LibKubernetesGenerator/templates/Operations.cs.template b/src/LibKubernetesGenerator/templates/Operations.cs.template
index 876ed8a16..d98337ada 100644
--- a/src/LibKubernetesGenerator/templates/Operations.cs.template
+++ b/src/LibKubernetesGenerator/templates/Operations.cs.template
@@ -10,13 +10,13 @@ public partial class AbstractKubernetes : I{{name}}Operations
{
{{for api in apis }}
{{if IfReturnType api.operation "void"}}
- private async Task I{{name}}Operations_{{GetMethodName api.operation "WithHttpMessagesAsync"}}(
+ private async Task I{{name}}Operations_{{GetOperationId api.operation "WithHttpMessagesAsync"}}(
{{end}}
{{if IfReturnType api.operation "obj"}}
- private async Task> I{{name}}Operations_{{GetMethodName api.operation "WithHttpMessagesAsync"}}(
+ private async Task> I{{name}}Operations_{{GetOperationId api.operation "WithHttpMessagesAsync"}}(
{{end}}
{{if IfReturnType api.operation "stream"}}
- private async Task> I{{name}}Operations_{{GetMethodName api.operation "WithHttpMessagesAsync"}}(
+ private async Task> I{{name}}Operations_{{GetOperationId api.operation "WithHttpMessagesAsync"}}(
{{end}}
{{ for parameter in api.operation.parameters}}
{{GetDotNetTypeOpenApiParameter parameter}} {{GetDotNetNameOpenApiParameter parameter "false"}},
@@ -90,7 +90,7 @@ public partial class AbstractKubernetes : I{{name}}Operations
}
///
- async Task"}}> I{{name}}Operations.{{GetMethodName api.operation "WithHttpMessagesAsync"}}(
+ async Task"}}> I{{name}}Operations.{{GetOperationId api.operation "WithHttpMessagesAsync"}}(
{{ for parameter in api.operation.parameters}}
{{GetDotNetTypeOpenApiParameter parameter}} {{GetDotNetNameOpenApiParameter parameter "false"}},
{{end}}
@@ -98,7 +98,7 @@ public partial class AbstractKubernetes : I{{name}}Operations
CancellationToken cancellationToken)
{
{{if IfReturnType api.operation "void"}}
- return await I{{name}}Operations_{{GetMethodName api.operation "WithHttpMessagesAsync"}}(
+ return await I{{name}}Operations_{{GetOperationId api.operation "WithHttpMessagesAsync"}}(
{{ for parameter in api.operation.parameters}}
{{GetDotNetNameOpenApiParameter parameter "false"}},
{{end}}
@@ -106,7 +106,7 @@ public partial class AbstractKubernetes : I{{name}}Operations
cancellationToken).ConfigureAwait(false);
{{end}}
{{if IfReturnType api.operation "obj"}}
- return await I{{name}}Operations_{{GetMethodName api.operation "WithHttpMessagesAsync"}}{{GetReturnType api.operation "<>"}}(
+ return await I{{name}}Operations_{{GetOperationId api.operation "WithHttpMessagesAsync"}}{{GetReturnType api.operation "<>"}}(
{{ for parameter in api.operation.parameters}}
{{GetDotNetNameOpenApiParameter parameter "false"}},
{{end}}
@@ -114,7 +114,7 @@ public partial class AbstractKubernetes : I{{name}}Operations
cancellationToken).ConfigureAwait(false);
{{end}}
{{if IfReturnType api.operation "stream"}}
- return await I{{name}}Operations_{{GetMethodName api.operation "WithHttpMessagesAsync"}}(
+ return await I{{name}}Operations_{{GetOperationId api.operation "WithHttpMessagesAsync"}}(
{{ for parameter in api.operation.parameters}}
{{GetDotNetNameOpenApiParameter parameter "false"}},
{{end}}
@@ -125,14 +125,14 @@ public partial class AbstractKubernetes : I{{name}}Operations
{{if IfReturnType api.operation "object"}}
///
- async Task> I{{name}}Operations.{{GetMethodName api.operation "WithHttpMessagesAsync"}}(
+ async Task> I{{name}}Operations.{{GetOperationId api.operation "WithHttpMessagesAsync"}}(
{{ for parameter in api.operation.parameters}}
{{GetDotNetTypeOpenApiParameter parameter}} {{GetDotNetNameOpenApiParameter parameter "false"}},
{{end}}
IReadOnlyDictionary> customHeaders,
CancellationToken cancellationToken)
{
- return await I{{name}}Operations_{{GetMethodName api.operation "WithHttpMessagesAsync"}}(
+ return await I{{name}}Operations_{{GetOperationId api.operation "WithHttpMessagesAsync"}}(
{{ for parameter in api.operation.parameters}}
{{GetDotNetNameOpenApiParameter parameter "false"}},
{{end}}
diff --git a/src/LibKubernetesGenerator/templates/OperationsExtensions.cs.template b/src/LibKubernetesGenerator/templates/OperationsExtensions.cs.template
index 98c304ede..b05f0e243 100644
--- a/src/LibKubernetesGenerator/templates/OperationsExtensions.cs.template
+++ b/src/LibKubernetesGenerator/templates/OperationsExtensions.cs.template
@@ -23,14 +23,14 @@ public static partial class {{name}}OperationsExtensions
/// {{ToXmlDoc api.description}}
///
{{ end }}
- public static {{GetReturnType api.operation "void"}} {{GetMethodName api.operation ""}}(
+ public static {{GetReturnType api.operation "void"}} {{GetOperationId api.operation ""}}(
this I{{name}}Operations operations
{{ for parameter in api.operation.parameters}}
,{{GetDotNetTypeOpenApiParameter parameter}} {{GetDotNetNameOpenApiParameter parameter "true"}}
{{end}}
)
{
- {{GetReturnType api.operation "return"}} operations.{{GetMethodName api.operation "Async"}}(
+ {{GetReturnType api.operation "return"}} operations.{{GetOperationId api.operation "Async"}}(
{{ for parameter in api.operation.parameters}}
{{GetDotNetNameOpenApiParameter parameter "false"}},
{{end}}
@@ -50,14 +50,14 @@ public static partial class {{name}}OperationsExtensions
/// {{ToXmlDoc parameter.description}}
///
{{end}}
- public static T {{GetMethodName api.operation ""}}(
+ public static T {{GetOperationId api.operation ""}}(
this I{{name}}Operations operations
{{ for parameter in api.operation.parameters}}
,{{GetDotNetTypeOpenApiParameter parameter}} {{GetDotNetNameOpenApiParameter parameter "true"}}
{{end}}
)
{
- return operations.{{GetMethodName api.operation "Async"}}(
+ return operations.{{GetOperationId api.operation "Async"}}(
{{ for parameter in api.operation.parameters}}
{{GetDotNetNameOpenApiParameter parameter "false"}},
{{end}}
@@ -80,7 +80,7 @@ public static partial class {{name}}OperationsExtensions
///
/// A which can be used to cancel the asynchronous operation.
///
- public static async Task{{GetReturnType api.operation "<>"}} {{GetMethodName api.operation "Async"}}(
+ public static async Task{{GetReturnType api.operation "<>"}} {{GetOperationId api.operation "Async"}}(
this I{{name}}Operations operations,
{{ for parameter in api.operation.parameters}}
{{GetDotNetTypeOpenApiParameter parameter}} {{GetDotNetNameOpenApiParameter parameter "true"}},
@@ -88,7 +88,7 @@ public static partial class {{name}}OperationsExtensions
CancellationToken cancellationToken = default(CancellationToken))
{
{{if IfReturnType api.operation "stream"}}
- var _result = await operations.{{GetMethodName api.operation "WithHttpMessagesAsync"}}(
+ var _result = await operations.{{GetOperationId api.operation "WithHttpMessagesAsync"}}(
{{ for parameter in api.operation.parameters}}
{{GetDotNetNameOpenApiParameter parameter "false"}},
{{end}}
@@ -98,7 +98,7 @@ public static partial class {{name}}OperationsExtensions
{{GetReturnType api.operation "_result.Body"}};
{{end}}
{{if IfReturnType api.operation "obj"}}
- using (var _result = await operations.{{GetMethodName api.operation "WithHttpMessagesAsync"}}(
+ using (var _result = await operations.{{GetOperationId api.operation "WithHttpMessagesAsync"}}(
{{ for parameter in api.operation.parameters}}
{{GetDotNetNameOpenApiParameter parameter "false"}},
{{end}}
@@ -109,7 +109,7 @@ public static partial class {{name}}OperationsExtensions
}
{{end}}
{{if IfReturnType api.operation "void"}}
- using (var _result = await operations.{{GetMethodName api.operation "WithHttpMessagesAsync"}}(
+ using (var _result = await operations.{{GetOperationId api.operation "WithHttpMessagesAsync"}}(
{{ for parameter in api.operation.parameters}}
{{GetDotNetNameOpenApiParameter parameter "false"}},
{{end}}
@@ -135,14 +135,14 @@ public static partial class {{name}}OperationsExtensions
///
/// A which can be used to cancel the asynchronous operation.
///
- public static async Task {{GetMethodName api.operation "Async"}}(
+ public static async Task {{GetOperationId api.operation "Async"}}(
this I{{name}}Operations operations,
{{ for parameter in api.operation.parameters}}
{{GetDotNetTypeOpenApiParameter parameter}} {{GetDotNetNameOpenApiParameter parameter "true"}},
{{ end }}
CancellationToken cancellationToken = default(CancellationToken))
{
- using (var _result = await operations.{{GetMethodName api.operation "WithHttpMessagesAsync"}}(
+ using (var _result = await operations.{{GetOperationId api.operation "WithHttpMessagesAsync"}}(
{{ for parameter in api.operation.parameters}}
{{GetDotNetNameOpenApiParameter parameter "false"}},
{{end}}
diff --git a/tests/E2E.Tests/MinikubeTests.cs b/tests/E2E.Tests/MinikubeTests.cs
index fc5136d5a..547ee90ea 100644
--- a/tests/E2E.Tests/MinikubeTests.cs
+++ b/tests/E2E.Tests/MinikubeTests.cs
@@ -16,6 +16,7 @@
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
+using k8s.ClientSets;
using Xunit;
namespace k8s.E2E
@@ -584,6 +585,118 @@ await genericPods.CreateNamespacedAsync(
}
}
+ [MinikubeFact]
+ public async Task ClientSetTest()
+ {
+ var namespaceParameter = "default";
+ var podName = "k8scsharp-e2e-clientset-pod";
+
+ using var kubernetes = CreateClient();
+
+ var clientSet = new ClientSet((Kubernetes)kubernetes);
+ async Task Cleanup()
+ {
+ var pods = await clientSet.CoreV1.Pod.ListAsync(namespaceParameter).ConfigureAwait(false);
+ while (pods.Items.Any(p => p.Metadata.Name == podName))
+ {
+ try
+ {
+ await clientSet.CoreV1.Pod.DeleteAsync(podName, namespaceParameter).ConfigureAwait(false);
+ }
+ catch (HttpOperationException e)
+ {
+ if (e.Response.StatusCode == System.Net.HttpStatusCode.NotFound)
+ {
+ return;
+ }
+ }
+ }
+ }
+
+ try
+ {
+ await Cleanup().ConfigureAwait(false);
+
+ // create + list
+ {
+ await clientSet.CoreV1.Pod.CreateAsync(
+ new V1Pod()
+ {
+ Metadata = new V1ObjectMeta { Name = podName, Labels = new Dictionary { { "place", "holder" }, }, },
+ Spec = new V1PodSpec
+ {
+ Containers = new[] { new V1Container() { Name = "k8scsharp-e2e", Image = "nginx", }, },
+ },
+ },
+ namespaceParameter).ConfigureAwait(false);
+
+ var pods = await clientSet.CoreV1.Pod.ListAsync(namespaceParameter).ConfigureAwait(false);
+ Assert.Contains(pods.Items, p => p.Metadata.Name == podName);
+ }
+
+ // replace + get
+ {
+ var pod = await clientSet.CoreV1.Pod.GetAsync(podName, namespaceParameter).ConfigureAwait(false);
+ var old = JsonSerializer.SerializeToDocument(pod);
+
+ var newLabels = new Dictionary(pod.Metadata.Labels) { ["test"] = "clientset-test-jsonpatch" };
+ pod.Metadata.Labels = newLabels;
+
+ var expected = JsonSerializer.SerializeToDocument(pod);
+ var patch = old.CreatePatch(expected);
+
+ await clientSet.CoreV1.Pod
+ .PatchAsync(new V1Patch(patch, V1Patch.PatchType.JsonPatch), podName, namespaceParameter)
+ .ConfigureAwait(false);
+ var pods = await clientSet.CoreV1.Pod.ListAsync(namespaceParameter).ConfigureAwait(false);
+ Assert.Contains(pods.Items, p => p.Labels().Contains(new KeyValuePair("test", "clientset-test-jsonpatch")));
+ }
+
+ // replace + get
+ {
+ var pod = await clientSet.CoreV1.Pod.GetAsync(podName, namespaceParameter).ConfigureAwait(false);
+ pod.Spec.Containers[0].Image = "httpd";
+ await clientSet.CoreV1.Pod.UpdateAsync(pod, podName, namespaceParameter).ConfigureAwait(false);
+
+ pod = await clientSet.CoreV1.Pod.GetAsync(podName, namespaceParameter).ConfigureAwait(false);
+ Assert.Equal("httpd", pod.Spec.Containers[0].Image);
+ }
+
+ // delete + list
+ {
+ var pods = new V1PodList();
+ var retry = 5;
+ while (retry-- > 0)
+ {
+ try
+ {
+ await clientSet.CoreV1.Pod.DeleteAsync(podName, namespaceParameter).ConfigureAwait(false);
+ }
+ catch (HttpOperationException e)
+ {
+ if (e.Response.StatusCode == System.Net.HttpStatusCode.NotFound)
+ {
+ return;
+ }
+ }
+
+ pods = await clientSet.CoreV1.Pod.ListAsync(namespaceParameter).ConfigureAwait(false);
+ if (pods.Items.All(p => p.Metadata.Name != podName))
+ {
+ break;
+ }
+
+ await Task.Delay(TimeSpan.FromSeconds(2.5)).ConfigureAwait(false);
+ }
+
+ Assert.DoesNotContain(pods.Items, p => p.Metadata.Name == podName);
+ }
+ }
+ finally
+ {
+ await Cleanup().ConfigureAwait(false);
+ }
+ }
[MinikubeFact]
public async Task CopyToPodTestAsync()
diff --git a/tests/KubernetesClient.Tests/PodExecTests.cs b/tests/KubernetesClient.Tests/PodExecTests.cs
index 5a6276e8d..597be477b 100644
--- a/tests/KubernetesClient.Tests/PodExecTests.cs
+++ b/tests/KubernetesClient.Tests/PodExecTests.cs
@@ -66,7 +66,7 @@ public async Task ExecDefaultContainerStdOut()
false,
false,
true,
- webSocketSubProtol: WebSocketProtocol.ChannelWebSocketProtocol,
+ webSocketSubProtocol: WebSocketProtocol.ChannelWebSocketProtocol,
cancellationToken: TestCancellation).ConfigureAwait(true);
Assert.Equal(
WebSocketProtocol.ChannelWebSocketProtocol,