Skip to content

Commit f515fb4

Browse files
authored
add generic create (#744)
* add generic create * flaky test on test env
1 parent 4ff1ea4 commit f515fb4

File tree

4 files changed

+127
-18
lines changed

4 files changed

+127
-18
lines changed

examples/generic/Generic.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@ internal class Generic
1010
private static async Task Main(string[] args)
1111
{
1212
var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
13-
var generic = new GenericClient(config, "", "v1", "nodes");
13+
IKubernetes client = new Kubernetes(config);
14+
var generic = new GenericClient(client, "", "v1", "nodes");
1415
var node = await generic.ReadAsync<V1Node>("kube0").ConfigureAwait(false);
1516
Console.WriteLine(node.Metadata.Name);
1617

17-
var genericPods = new GenericClient(config, "", "v1", "pods");
18+
var genericPods = new GenericClient(client, "", "v1", "pods");
1819
var pods = await genericPods.ListNamespacedAsync<V1PodList>("default").ConfigureAwait(false);
1920
foreach (var pod in pods.Items)
2021
{

kubernetes-client.sln

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
# Visual Studio Version 16
4-
VisualStudioVersion = 16.0.29230.47
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.0.31903.59
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{B70AFB57-57C9-46DC-84BE-11B7DDD34B40}"
77
EndProject
@@ -43,7 +43,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "customResource", "examples\
4343
EndProject
4444
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KubernetesGenerator", "gen\KubernetesGenerator\KubernetesGenerator.csproj", "{79BA7C4A-98AA-467E-80D4-0E4F03EE6DDE}"
4545
EndProject
46-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GenericKubernetesApi", "examples\GenericKubernetesApi\GenericKubernetesApi.csproj", "{F81AE4C4-E044-4225-BD76-385A0DE621FD}"
46+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GenericKubernetesApi", "examples\GenericKubernetesApi\GenericKubernetesApi.csproj", "{F81AE4C4-E044-4225-BD76-385A0DE621FD}"
47+
EndProject
48+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "generic", "examples\generic\generic.csproj", "{F06D4C3A-7825-43A8-832B-6BDE3D355486}"
4749
EndProject
4850
Global
4951
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -259,6 +261,18 @@ Global
259261
{F81AE4C4-E044-4225-BD76-385A0DE621FD}.Release|x64.Build.0 = Release|Any CPU
260262
{F81AE4C4-E044-4225-BD76-385A0DE621FD}.Release|x86.ActiveCfg = Release|Any CPU
261263
{F81AE4C4-E044-4225-BD76-385A0DE621FD}.Release|x86.Build.0 = Release|Any CPU
264+
{F06D4C3A-7825-43A8-832B-6BDE3D355486}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
265+
{F06D4C3A-7825-43A8-832B-6BDE3D355486}.Debug|Any CPU.Build.0 = Debug|Any CPU
266+
{F06D4C3A-7825-43A8-832B-6BDE3D355486}.Debug|x64.ActiveCfg = Debug|Any CPU
267+
{F06D4C3A-7825-43A8-832B-6BDE3D355486}.Debug|x64.Build.0 = Debug|Any CPU
268+
{F06D4C3A-7825-43A8-832B-6BDE3D355486}.Debug|x86.ActiveCfg = Debug|Any CPU
269+
{F06D4C3A-7825-43A8-832B-6BDE3D355486}.Debug|x86.Build.0 = Debug|Any CPU
270+
{F06D4C3A-7825-43A8-832B-6BDE3D355486}.Release|Any CPU.ActiveCfg = Release|Any CPU
271+
{F06D4C3A-7825-43A8-832B-6BDE3D355486}.Release|Any CPU.Build.0 = Release|Any CPU
272+
{F06D4C3A-7825-43A8-832B-6BDE3D355486}.Release|x64.ActiveCfg = Release|Any CPU
273+
{F06D4C3A-7825-43A8-832B-6BDE3D355486}.Release|x64.Build.0 = Release|Any CPU
274+
{F06D4C3A-7825-43A8-832B-6BDE3D355486}.Release|x86.ActiveCfg = Release|Any CPU
275+
{F06D4C3A-7825-43A8-832B-6BDE3D355486}.Release|x86.Build.0 = Release|Any CPU
262276
EndGlobalSection
263277
GlobalSection(SolutionProperties) = preSolution
264278
HideSolutionNode = FALSE
@@ -281,6 +295,7 @@ Global
281295
{95672061-5799-4454-ACDB-D6D330DB1EC4} = {B70AFB57-57C9-46DC-84BE-11B7DDD34B40}
282296
{79BA7C4A-98AA-467E-80D4-0E4F03EE6DDE} = {879F8787-C3BB-43F3-A92D-6D4C7D3A5285}
283297
{F81AE4C4-E044-4225-BD76-385A0DE621FD} = {B70AFB57-57C9-46DC-84BE-11B7DDD34B40}
298+
{F06D4C3A-7825-43A8-832B-6BDE3D355486} = {B70AFB57-57C9-46DC-84BE-11B7DDD34B40}
284299
EndGlobalSection
285300
GlobalSection(ExtensibilityGlobals) = postSolution
286301
SolutionGuid = {049A763A-C891-4E8D-80CF-89DD3E22ADC7}

src/KubernetesClient/GenericClient.cs

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,45 +33,59 @@ public GenericClient(IKubernetes kubernetes, string group, string version, strin
3333
this.kubernetes = kubernetes;
3434
}
3535

36-
public async Task<T> ListAsync<T>(CancellationToken cancel = default(CancellationToken))
36+
public async Task<T> CreateAsync<T>(T obj, CancellationToken cancel = default)
3737
where T : IKubernetesObject
3838
{
39-
var resp = await this.kubernetes.ListClusterCustomObjectWithHttpMessagesAsync(this.group, this.version, this.plural, cancellationToken: cancel).ConfigureAwait(false);
39+
var resp = await kubernetes.CreateClusterCustomObjectWithHttpMessagesAsync(obj, group, version, plural, cancellationToken: cancel).ConfigureAwait(false);
4040
return SafeJsonConvert.DeserializeObject<T>(resp.Body.ToString());
4141
}
4242

43-
public async Task<T> ListNamespacedAsync<T>(string ns, CancellationToken cancel = default(CancellationToken))
43+
public async Task<T> CreateNamespacedAsync<T>(T obj, string ns, CancellationToken cancel = default)
4444
where T : IKubernetesObject
4545
{
46-
var resp = await this.kubernetes.ListNamespacedCustomObjectWithHttpMessagesAsync(this.group, this.version, ns, this.plural, cancellationToken: cancel).ConfigureAwait(false);
46+
var resp = await kubernetes.CreateNamespacedCustomObjectWithHttpMessagesAsync(obj, group, version, ns, plural, cancellationToken: cancel).ConfigureAwait(false);
4747
return SafeJsonConvert.DeserializeObject<T>(resp.Body.ToString());
4848
}
4949

50-
public async Task<T> ReadNamespacedAsync<T>(string ns, string name, CancellationToken cancel = default(CancellationToken))
50+
public async Task<T> ListAsync<T>(CancellationToken cancel = default)
5151
where T : IKubernetesObject
5252
{
53-
var resp = await this.kubernetes.GetNamespacedCustomObjectWithHttpMessagesAsync(this.group, this.version, ns, this.plural, name, cancellationToken: cancel).ConfigureAwait(false);
53+
var resp = await kubernetes.ListClusterCustomObjectWithHttpMessagesAsync(group, version, plural, cancellationToken: cancel).ConfigureAwait(false);
5454
return SafeJsonConvert.DeserializeObject<T>(resp.Body.ToString());
5555
}
5656

57-
public async Task<T> ReadAsync<T>(string name, CancellationToken cancel = default(CancellationToken))
57+
public async Task<T> ListNamespacedAsync<T>(string ns, CancellationToken cancel = default)
5858
where T : IKubernetesObject
5959
{
60-
var resp = await this.kubernetes.GetClusterCustomObjectWithHttpMessagesAsync(this.group, this.version, this.plural, name, cancellationToken: cancel).ConfigureAwait(false);
60+
var resp = await kubernetes.ListNamespacedCustomObjectWithHttpMessagesAsync(group, version, ns, plural, cancellationToken: cancel).ConfigureAwait(false);
6161
return SafeJsonConvert.DeserializeObject<T>(resp.Body.ToString());
6262
}
6363

64-
public async Task<T> DeleteAsync<T>(string name, CancellationToken cancel = default(CancellationToken))
64+
public async Task<T> ReadNamespacedAsync<T>(string ns, string name, CancellationToken cancel = default)
6565
where T : IKubernetesObject
6666
{
67-
var resp = await this.kubernetes.DeleteClusterCustomObjectWithHttpMessagesAsync(this.group, this.version, this.plural, name, cancellationToken: cancel).ConfigureAwait(false);
67+
var resp = await kubernetes.GetNamespacedCustomObjectWithHttpMessagesAsync(group, version, ns, plural, name, cancellationToken: cancel).ConfigureAwait(false);
6868
return SafeJsonConvert.DeserializeObject<T>(resp.Body.ToString());
6969
}
7070

71-
public async Task<T> DeleteNamespacedAsync<T>(string ns, string name, CancellationToken cancel = default(CancellationToken))
71+
public async Task<T> ReadAsync<T>(string name, CancellationToken cancel = default)
7272
where T : IKubernetesObject
7373
{
74-
var resp = await this.kubernetes.DeleteNamespacedCustomObjectWithHttpMessagesAsync(this.group, this.version, ns, this.plural, name, cancellationToken: cancel).ConfigureAwait(false);
74+
var resp = await kubernetes.GetClusterCustomObjectWithHttpMessagesAsync(group, version, plural, name, cancellationToken: cancel).ConfigureAwait(false);
75+
return SafeJsonConvert.DeserializeObject<T>(resp.Body.ToString());
76+
}
77+
78+
public async Task<T> DeleteAsync<T>(string name, CancellationToken cancel = default)
79+
where T : IKubernetesObject
80+
{
81+
var resp = await kubernetes.DeleteClusterCustomObjectWithHttpMessagesAsync(group, version, plural, name, cancellationToken: cancel).ConfigureAwait(false);
82+
return SafeJsonConvert.DeserializeObject<T>(resp.Body.ToString());
83+
}
84+
85+
public async Task<T> DeleteNamespacedAsync<T>(string ns, string name, CancellationToken cancel = default)
86+
where T : IKubernetesObject
87+
{
88+
var resp = await kubernetes.DeleteNamespacedCustomObjectWithHttpMessagesAsync(group, version, ns, plural, name, cancellationToken: cancel).ConfigureAwait(false);
7589
return SafeJsonConvert.DeserializeObject<T>(resp.Body.ToString());
7690
}
7791

@@ -83,7 +97,7 @@ public void Dispose()
8397

8498
protected virtual void Dispose(bool disposing)
8599
{
86-
this.kubernetes.Dispose();
100+
kubernetes.Dispose();
87101
}
88102
}
89103
}

tests/E2E.Tests/MnikubeTests.cs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,85 @@ await kubernetes.CreateNamespacedEventAsync(
465465
reportingInstance: "38"), "default").ConfigureAwait(false);
466466
}
467467

468+
[MinikubeFact]
469+
public async void GenericTest()
470+
{
471+
var namespaceParameter = "default";
472+
var podName = "k8scsharp-e2e-generic-pod";
473+
474+
var client = CreateClient();
475+
var genericPods = new GenericClient(client, "", "v1", "pods");
476+
477+
void Cleanup()
478+
{
479+
var pods = client.ListNamespacedPod(namespaceParameter);
480+
while (pods.Items.Any(p => p.Metadata.Name == podName))
481+
{
482+
try
483+
{
484+
client.DeleteNamespacedPod(podName, namespaceParameter);
485+
}
486+
catch (HttpOperationException e)
487+
{
488+
if (e.Response.StatusCode == System.Net.HttpStatusCode.NotFound)
489+
{
490+
return;
491+
}
492+
}
493+
}
494+
}
495+
496+
try
497+
{
498+
Cleanup();
499+
500+
await genericPods.CreateNamespacedAsync(
501+
new V1Pod()
502+
{
503+
Metadata = new V1ObjectMeta { Name = podName, },
504+
Spec = new V1PodSpec
505+
{
506+
Containers = new[] { new V1Container() { Name = "k8scsharp-e2e", Image = "nginx", }, },
507+
},
508+
},
509+
namespaceParameter).ConfigureAwait(false);
510+
511+
var pods = await genericPods.ListNamespacedAsync<V1PodList>(namespaceParameter).ConfigureAwait(false);
512+
Assert.Contains(pods.Items, p => p.Metadata.Name == podName);
513+
514+
int retry = 5;
515+
while (retry-- > 0)
516+
{
517+
try
518+
{
519+
await genericPods.DeleteNamespacedAsync<V1Pod>(namespaceParameter, podName).ConfigureAwait(false);
520+
}
521+
catch (HttpOperationException e)
522+
{
523+
if (e.Response.StatusCode == System.Net.HttpStatusCode.NotFound)
524+
{
525+
return;
526+
}
527+
}
528+
529+
pods = await genericPods.ListNamespacedAsync<V1PodList>(namespaceParameter).ConfigureAwait(false);
530+
if (!pods.Items.Any(p => p.Metadata.Name == podName))
531+
{
532+
break;
533+
}
534+
535+
await Task.Delay(TimeSpan.FromSeconds(2.5)).ConfigureAwait(false);
536+
}
537+
538+
Assert.DoesNotContain(pods.Items, p => p.Metadata.Name == podName);
539+
}
540+
finally
541+
{
542+
Cleanup();
543+
}
544+
}
545+
546+
468547
private static IKubernetes CreateClient()
469548
{
470549
return new Kubernetes(KubernetesClientConfiguration.BuildDefaultConfig());

0 commit comments

Comments
 (0)