From 8888a16b585ac42c2fcf727319d3bd2a1afec52a Mon Sep 17 00:00:00 2001 From: Boshi Lian Date: Thu, 29 Sep 2022 10:02:39 -0700 Subject: [PATCH] add patch and replace to generic (#1040) --- src/KubernetesClient/GenericClient.cs | 29 ++++++++ tests/E2E.Tests/MinikubeTests.cs | 101 +++++++++++++++++--------- 2 files changed, 96 insertions(+), 34 deletions(-) diff --git a/src/KubernetesClient/GenericClient.cs b/src/KubernetesClient/GenericClient.cs index 277c6be..e6c48c0 100644 --- a/src/KubernetesClient/GenericClient.cs +++ b/src/KubernetesClient/GenericClient.cs @@ -1,3 +1,4 @@ +using k8s.Models; using System.Threading; using System.Threading.Tasks; @@ -86,6 +87,34 @@ namespace k8s return KubernetesJson.Deserialize(resp.Body.ToString()); } + public async Task PatchAsync(V1Patch patch, string name, CancellationToken cancel = default) + where T : IKubernetesObject + { + var resp = await kubernetes.CustomObjects.PatchClusterCustomObjectWithHttpMessagesAsync(patch, group, version, plural, name, cancellationToken: cancel).ConfigureAwait(false); + return KubernetesJson.Deserialize(resp.Body.ToString()); + } + + public async Task PatchNamespacedAsync(V1Patch patch, string ns, string name, CancellationToken cancel = default) + where T : IKubernetesObject + { + var resp = await kubernetes.CustomObjects.PatchNamespacedCustomObjectWithHttpMessagesAsync(patch, group, version, ns, plural, name, cancellationToken: cancel).ConfigureAwait(false); + return KubernetesJson.Deserialize(resp.Body.ToString()); + } + + public async Task ReplaceAsync(T obj, string name, CancellationToken cancel = default) + where T : IKubernetesObject + { + var resp = await kubernetes.CustomObjects.ReplaceClusterCustomObjectWithHttpMessagesAsync(obj, group, version, plural, name, cancellationToken: cancel).ConfigureAwait(false); + return KubernetesJson.Deserialize(resp.Body.ToString()); + } + + public async Task ReplaceNamespacedAsync(T obj, string ns, string name, CancellationToken cancel = default) + where T : IKubernetesObject + { + var resp = await kubernetes.CustomObjects.ReplaceNamespacedCustomObjectWithHttpMessagesAsync(obj, group, version, ns, plural, name, cancellationToken: cancel).ConfigureAwait(false); + return KubernetesJson.Deserialize(resp.Body.ToString()); + } + public void Dispose() { Dispose(true); diff --git a/tests/E2E.Tests/MinikubeTests.cs b/tests/E2E.Tests/MinikubeTests.cs index 1ee33a4..de68c2f 100644 --- a/tests/E2E.Tests/MinikubeTests.cs +++ b/tests/E2E.Tests/MinikubeTests.cs @@ -504,45 +504,78 @@ namespace k8s.E2E { 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) + // create + list { - try - { - await genericPods.DeleteNamespacedAsync(namespaceParameter, podName).ConfigureAwait(false); - } - catch (HttpOperationException e) - { - if (e.Response.StatusCode == System.Net.HttpStatusCode.NotFound) + await genericPods.CreateNamespacedAsync( + new V1Pod() { - return; - } - } + 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); - 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); + var pods = await genericPods.ListNamespacedAsync(namespaceParameter).ConfigureAwait(false); + Assert.Contains(pods.Items, p => p.Metadata.Name == podName); } - Assert.DoesNotContain(pods.Items, p => p.Metadata.Name == podName); + // replace + get + { + var pod = await genericPods.ReadNamespacedAsync(namespaceParameter, podName).ConfigureAwait(false); + var old = JsonSerializer.SerializeToDocument(pod); + + var newlabels = new Dictionary(pod.Metadata.Labels) { ["test"] = "generic-test-jsonpatch" }; + pod.Metadata.Labels = newlabels; + + var expected = JsonSerializer.SerializeToDocument(pod); + var patch = old.CreatePatch(expected); + + await genericPods.PatchNamespacedAsync(new V1Patch(patch, V1Patch.PatchType.JsonPatch), namespaceParameter, podName).ConfigureAwait(false); + var pods = await genericPods.ListNamespacedAsync(namespaceParameter).ConfigureAwait(false); + Assert.Contains(pods.Items, p => p.Labels().Contains(new KeyValuePair("test", "generic-test-jsonpatch"))); + } + + // replace + get + { + var pod = await genericPods.ReadNamespacedAsync(namespaceParameter, podName).ConfigureAwait(false); + pod.Spec.Containers[0].Image = "httpd"; + await genericPods.ReplaceNamespacedAsync(pod, namespaceParameter, podName).ConfigureAwait(false); + + pod = await genericPods.ReadNamespacedAsync(namespaceParameter, podName).ConfigureAwait(false); + Assert.Equal("httpd", pod.Spec.Containers[0].Image); + } + + // delete + list + { + V1PodList pods = new V1PodList(); + 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 {