diff --git a/src/KubernetesClient.Kubectl/Beta/AsyncKubectl.Cordon.cs b/src/KubernetesClient.Kubectl/Beta/AsyncKubectl.Cordon.cs new file mode 100644 index 0000000..60ab978 --- /dev/null +++ b/src/KubernetesClient.Kubectl/Beta/AsyncKubectl.Cordon.cs @@ -0,0 +1,30 @@ +using Json.Patch; +using k8s.Models; +using System.Text.Json; + +namespace k8s.kubectl.beta; + +public partial class AsyncKubectl +{ + public async Task Cordon(string nodeName, CancellationToken cancellationToken = default) + { + await PatchNodeUnschedulable(nodeName, true, cancellationToken).ConfigureAwait(false); + } + + public async Task Uncordon(string nodeName, CancellationToken cancellationToken = default) + { + await PatchNodeUnschedulable(nodeName, false, cancellationToken).ConfigureAwait(false); + } + + private async Task PatchNodeUnschedulable(string nodeName, bool desired, CancellationToken cancellationToken = default) + { + var node = await client.CoreV1.ReadNodeAsync(nodeName, cancellationToken: cancellationToken).ConfigureAwait(false); + + var old = JsonSerializer.SerializeToDocument(node); + node.Spec.Unschedulable = desired; + + var patch = old.CreatePatch(node); + + await client.CoreV1.PatchNodeAsync(new V1Patch(patch, V1Patch.PatchType.JsonPatch), nodeName, cancellationToken: cancellationToken).ConfigureAwait(false); + } +} diff --git a/src/KubernetesClient.Kubectl/Beta/Kubectl.Cordon.cs b/src/KubernetesClient.Kubectl/Beta/Kubectl.Cordon.cs new file mode 100644 index 0000000..c1d85a5 --- /dev/null +++ b/src/KubernetesClient.Kubectl/Beta/Kubectl.Cordon.cs @@ -0,0 +1,14 @@ +namespace k8s.kubectl.beta; + +public partial class Kubectl +{ + public void Cordon(string nodeName) + { + client.Cordon(nodeName).GetAwaiter().GetResult(); + } + + public void Uncordon(string nodeName) + { + client.Uncordon(nodeName).GetAwaiter().GetResult(); + } +} diff --git a/src/KubernetesClient.Kubectl/KubernetesClient.Kubectl.csproj b/src/KubernetesClient.Kubectl/KubernetesClient.Kubectl.csproj index 21e1d2b..fc67b21 100644 --- a/src/KubernetesClient.Kubectl/KubernetesClient.Kubectl.csproj +++ b/src/KubernetesClient.Kubectl/KubernetesClient.Kubectl.csproj @@ -7,6 +7,9 @@ k8s.kubectl + + + diff --git a/tests/E2E.Tests/E2E.Tests.csproj b/tests/E2E.Tests/E2E.Tests.csproj index f0ad226..5aef1a2 100644 --- a/tests/E2E.Tests/E2E.Tests.csproj +++ b/tests/E2E.Tests/E2E.Tests.csproj @@ -24,6 +24,7 @@ + diff --git a/tests/E2E.Tests/KubectlTests.cs b/tests/E2E.Tests/KubectlTests.cs new file mode 100644 index 0000000..ecb1de8 --- /dev/null +++ b/tests/E2E.Tests/KubectlTests.cs @@ -0,0 +1,32 @@ +using k8s.kubectl.beta; +using System.Linq; +using Xunit; + +namespace k8s.E2E; + +[Collection(nameof(Onebyone))] +public class KubectlTests +{ + [MinikubeFact] + public void CordonTest() + { + var client = MinikubeTests.CreateClient(); + + var node = client.CoreV1.ListNode().Items.First(); + var nodeName = node.Metadata.Name; + + var kubectl = new Kubectl(client); + + // cordon + kubectl.Cordon(nodeName); + + // check node status + var cordonNode = client.CoreV1.ReadNode(nodeName); + Assert.True(cordonNode.Spec.Unschedulable); + + // uncordon + kubectl.Uncordon(nodeName); + cordonNode = client.CoreV1.ReadNode(nodeName); + Assert.True(cordonNode.Spec.Unschedulable == null || cordonNode.Spec.Unschedulable == false); + } +} diff --git a/tests/E2E.Tests/MinikubeTests.cs b/tests/E2E.Tests/MinikubeTests.cs index 508bbd8..0ea8300 100644 --- a/tests/E2E.Tests/MinikubeTests.cs +++ b/tests/E2E.Tests/MinikubeTests.cs @@ -20,6 +20,7 @@ using Xunit; namespace k8s.E2E { + [Collection(nameof(Onebyone))] public class MinikubeTests { [MinikubeFact] diff --git a/tests/E2E.Tests/Onebyone.cs b/tests/E2E.Tests/Onebyone.cs new file mode 100644 index 0000000..f3ebdf2 --- /dev/null +++ b/tests/E2E.Tests/Onebyone.cs @@ -0,0 +1,8 @@ +using Xunit; + +namespace k8s.E2E; + +[CollectionDefinition(nameof(Onebyone), DisableParallelization = true)] +public class Onebyone +{ +}