From f515fb4d76e535b03fc6c099b098b154dbc0fd84 Mon Sep 17 00:00:00 2001 From: Boshi Lian Date: Fri, 12 Nov 2021 12:06:28 -0800 Subject: [PATCH] add generic create (#744) * add generic create * flaky test on test env --- examples/generic/Generic.cs | 5 +- kubernetes-client.sln | 21 ++++++- src/KubernetesClient/GenericClient.cs | 40 +++++++++----- tests/E2E.Tests/MnikubeTests.cs | 79 +++++++++++++++++++++++++++ 4 files changed, 127 insertions(+), 18 deletions(-) diff --git a/examples/generic/Generic.cs b/examples/generic/Generic.cs index af11992..de4525c 100644 --- a/examples/generic/Generic.cs +++ b/examples/generic/Generic.cs @@ -10,11 +10,12 @@ namespace exec private static async Task Main(string[] args) { var config = KubernetesClientConfiguration.BuildConfigFromConfigFile(); - var generic = new GenericClient(config, "", "v1", "nodes"); + IKubernetes client = new Kubernetes(config); + var generic = new GenericClient(client, "", "v1", "nodes"); var node = await generic.ReadAsync("kube0").ConfigureAwait(false); Console.WriteLine(node.Metadata.Name); - var genericPods = new GenericClient(config, "", "v1", "pods"); + var genericPods = new GenericClient(client, "", "v1", "pods"); var pods = await genericPods.ListNamespacedAsync("default").ConfigureAwait(false); foreach (var pod in pods.Items) { diff --git a/kubernetes-client.sln b/kubernetes-client.sln index 1acaff9..bb0940b 100644 --- a/kubernetes-client.sln +++ b/kubernetes-client.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29230.47 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{B70AFB57-57C9-46DC-84BE-11B7DDD34B40}" EndProject @@ -43,7 +43,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "customResource", "examples\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KubernetesGenerator", "gen\KubernetesGenerator\KubernetesGenerator.csproj", "{79BA7C4A-98AA-467E-80D4-0E4F03EE6DDE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GenericKubernetesApi", "examples\GenericKubernetesApi\GenericKubernetesApi.csproj", "{F81AE4C4-E044-4225-BD76-385A0DE621FD}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GenericKubernetesApi", "examples\GenericKubernetesApi\GenericKubernetesApi.csproj", "{F81AE4C4-E044-4225-BD76-385A0DE621FD}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "generic", "examples\generic\generic.csproj", "{F06D4C3A-7825-43A8-832B-6BDE3D355486}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -259,6 +261,18 @@ Global {F81AE4C4-E044-4225-BD76-385A0DE621FD}.Release|x64.Build.0 = Release|Any CPU {F81AE4C4-E044-4225-BD76-385A0DE621FD}.Release|x86.ActiveCfg = Release|Any CPU {F81AE4C4-E044-4225-BD76-385A0DE621FD}.Release|x86.Build.0 = Release|Any CPU + {F06D4C3A-7825-43A8-832B-6BDE3D355486}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F06D4C3A-7825-43A8-832B-6BDE3D355486}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F06D4C3A-7825-43A8-832B-6BDE3D355486}.Debug|x64.ActiveCfg = Debug|Any CPU + {F06D4C3A-7825-43A8-832B-6BDE3D355486}.Debug|x64.Build.0 = Debug|Any CPU + {F06D4C3A-7825-43A8-832B-6BDE3D355486}.Debug|x86.ActiveCfg = Debug|Any CPU + {F06D4C3A-7825-43A8-832B-6BDE3D355486}.Debug|x86.Build.0 = Debug|Any CPU + {F06D4C3A-7825-43A8-832B-6BDE3D355486}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F06D4C3A-7825-43A8-832B-6BDE3D355486}.Release|Any CPU.Build.0 = Release|Any CPU + {F06D4C3A-7825-43A8-832B-6BDE3D355486}.Release|x64.ActiveCfg = Release|Any CPU + {F06D4C3A-7825-43A8-832B-6BDE3D355486}.Release|x64.Build.0 = Release|Any CPU + {F06D4C3A-7825-43A8-832B-6BDE3D355486}.Release|x86.ActiveCfg = Release|Any CPU + {F06D4C3A-7825-43A8-832B-6BDE3D355486}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -281,6 +295,7 @@ Global {95672061-5799-4454-ACDB-D6D330DB1EC4} = {B70AFB57-57C9-46DC-84BE-11B7DDD34B40} {79BA7C4A-98AA-467E-80D4-0E4F03EE6DDE} = {879F8787-C3BB-43F3-A92D-6D4C7D3A5285} {F81AE4C4-E044-4225-BD76-385A0DE621FD} = {B70AFB57-57C9-46DC-84BE-11B7DDD34B40} + {F06D4C3A-7825-43A8-832B-6BDE3D355486} = {B70AFB57-57C9-46DC-84BE-11B7DDD34B40} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {049A763A-C891-4E8D-80CF-89DD3E22ADC7} diff --git a/src/KubernetesClient/GenericClient.cs b/src/KubernetesClient/GenericClient.cs index b0d2184..7e7180f 100644 --- a/src/KubernetesClient/GenericClient.cs +++ b/src/KubernetesClient/GenericClient.cs @@ -33,45 +33,59 @@ namespace k8s this.kubernetes = kubernetes; } - public async Task ListAsync(CancellationToken cancel = default(CancellationToken)) + public async Task CreateAsync(T obj, CancellationToken cancel = default) where T : IKubernetesObject { - var resp = await this.kubernetes.ListClusterCustomObjectWithHttpMessagesAsync(this.group, this.version, this.plural, cancellationToken: cancel).ConfigureAwait(false); + var resp = await kubernetes.CreateClusterCustomObjectWithHttpMessagesAsync(obj, group, version, plural, cancellationToken: cancel).ConfigureAwait(false); return SafeJsonConvert.DeserializeObject(resp.Body.ToString()); } - public async Task ListNamespacedAsync(string ns, CancellationToken cancel = default(CancellationToken)) + public async Task CreateNamespacedAsync(T obj, string ns, CancellationToken cancel = default) where T : IKubernetesObject { - var resp = await this.kubernetes.ListNamespacedCustomObjectWithHttpMessagesAsync(this.group, this.version, ns, this.plural, cancellationToken: cancel).ConfigureAwait(false); + var resp = await kubernetes.CreateNamespacedCustomObjectWithHttpMessagesAsync(obj, group, version, ns, plural, cancellationToken: cancel).ConfigureAwait(false); return SafeJsonConvert.DeserializeObject(resp.Body.ToString()); } - public async Task ReadNamespacedAsync(string ns, string name, CancellationToken cancel = default(CancellationToken)) + public async Task ListAsync(CancellationToken cancel = default) where T : IKubernetesObject { - var resp = await this.kubernetes.GetNamespacedCustomObjectWithHttpMessagesAsync(this.group, this.version, ns, this.plural, name, cancellationToken: cancel).ConfigureAwait(false); + var resp = await kubernetes.ListClusterCustomObjectWithHttpMessagesAsync(group, version, plural, cancellationToken: cancel).ConfigureAwait(false); return SafeJsonConvert.DeserializeObject(resp.Body.ToString()); } - public async Task ReadAsync(string name, CancellationToken cancel = default(CancellationToken)) + public async Task ListNamespacedAsync(string ns, CancellationToken cancel = default) where T : IKubernetesObject { - var resp = await this.kubernetes.GetClusterCustomObjectWithHttpMessagesAsync(this.group, this.version, this.plural, name, cancellationToken: cancel).ConfigureAwait(false); + var resp = await kubernetes.ListNamespacedCustomObjectWithHttpMessagesAsync(group, version, ns, plural, cancellationToken: cancel).ConfigureAwait(false); return SafeJsonConvert.DeserializeObject(resp.Body.ToString()); } - public async Task DeleteAsync(string name, CancellationToken cancel = default(CancellationToken)) + public async Task ReadNamespacedAsync(string ns, string name, CancellationToken cancel = default) where T : IKubernetesObject { - var resp = await this.kubernetes.DeleteClusterCustomObjectWithHttpMessagesAsync(this.group, this.version, this.plural, name, cancellationToken: cancel).ConfigureAwait(false); + var resp = await kubernetes.GetNamespacedCustomObjectWithHttpMessagesAsync(group, version, ns, plural, name, cancellationToken: cancel).ConfigureAwait(false); return SafeJsonConvert.DeserializeObject(resp.Body.ToString()); } - public async Task DeleteNamespacedAsync(string ns, string name, CancellationToken cancel = default(CancellationToken)) + public async Task ReadAsync(string name, CancellationToken cancel = default) where T : IKubernetesObject { - var resp = await this.kubernetes.DeleteNamespacedCustomObjectWithHttpMessagesAsync(this.group, this.version, ns, this.plural, name, cancellationToken: cancel).ConfigureAwait(false); + var resp = await kubernetes.GetClusterCustomObjectWithHttpMessagesAsync(group, version, plural, name, cancellationToken: cancel).ConfigureAwait(false); + return SafeJsonConvert.DeserializeObject(resp.Body.ToString()); + } + + public async Task DeleteAsync(string name, CancellationToken cancel = default) + where T : IKubernetesObject + { + var resp = await kubernetes.DeleteClusterCustomObjectWithHttpMessagesAsync(group, version, plural, name, cancellationToken: cancel).ConfigureAwait(false); + return SafeJsonConvert.DeserializeObject(resp.Body.ToString()); + } + + public async Task DeleteNamespacedAsync(string ns, string name, CancellationToken cancel = default) + where T : IKubernetesObject + { + var resp = await kubernetes.DeleteNamespacedCustomObjectWithHttpMessagesAsync(group, version, ns, plural, name, cancellationToken: cancel).ConfigureAwait(false); return SafeJsonConvert.DeserializeObject(resp.Body.ToString()); } @@ -83,7 +97,7 @@ namespace k8s protected virtual void Dispose(bool disposing) { - this.kubernetes.Dispose(); + kubernetes.Dispose(); } } } diff --git a/tests/E2E.Tests/MnikubeTests.cs b/tests/E2E.Tests/MnikubeTests.cs index cb43915..3833821 100644 --- a/tests/E2E.Tests/MnikubeTests.cs +++ b/tests/E2E.Tests/MnikubeTests.cs @@ -465,6 +465,85 @@ namespace k8s.E2E reportingInstance: "38"), "default").ConfigureAwait(false); } + [MinikubeFact] + public async void GenericTest() + { + var namespaceParameter = "default"; + var podName = "k8scsharp-e2e-generic-pod"; + + var client = CreateClient(); + var genericPods = new GenericClient(client, "", "v1", "pods"); + + void Cleanup() + { + var pods = client.ListNamespacedPod(namespaceParameter); + while (pods.Items.Any(p => p.Metadata.Name == podName)) + { + try + { + client.DeleteNamespacedPod(podName, namespaceParameter); + } + catch (HttpOperationException e) + { + if (e.Response.StatusCode == System.Net.HttpStatusCode.NotFound) + { + return; + } + } + } + } + + try + { + Cleanup(); + + await genericPods.CreateNamespacedAsync( + new V1Pod() + { + Metadata = new V1ObjectMeta { Name = podName, }, + Spec = new V1PodSpec + { + Containers = new[] { new V1Container() { Name = "k8scsharp-e2e", Image = "nginx", }, }, + }, + }, + namespaceParameter).ConfigureAwait(false); + + var pods = await genericPods.ListNamespacedAsync(namespaceParameter).ConfigureAwait(false); + Assert.Contains(pods.Items, p => p.Metadata.Name == podName); + + int retry = 5; + while (retry-- > 0) + { + try + { + await genericPods.DeleteNamespacedAsync(namespaceParameter, podName).ConfigureAwait(false); + } + catch (HttpOperationException e) + { + if (e.Response.StatusCode == System.Net.HttpStatusCode.NotFound) + { + return; + } + } + + pods = await genericPods.ListNamespacedAsync(namespaceParameter).ConfigureAwait(false); + if (!pods.Items.Any(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 + { + Cleanup(); + } + } + + private static IKubernetes CreateClient() { return new Kubernetes(KubernetesClientConfiguration.BuildDefaultConfig());