45
examples/customResource/CustomResourceDefinition.cs
Normal file
45
examples/customResource/CustomResourceDefinition.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System.Collections.Generic;
|
||||
using k8s;
|
||||
using k8s.Models;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "CA1724:TypeNamesShouldNotMatchNamespaces", Justification = "This is just an example.")]
|
||||
[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "This is just an example.")]
|
||||
|
||||
namespace customResource
|
||||
{
|
||||
public class CustomResourceDefinition
|
||||
{
|
||||
public string Version { get; set; }
|
||||
|
||||
public string Group { get; set; }
|
||||
|
||||
public string PluralName { get; set; }
|
||||
|
||||
public string Kind { get; set; }
|
||||
|
||||
public string Namespace { get; set; }
|
||||
}
|
||||
|
||||
public abstract class CustomResource : KubernetesObject
|
||||
{
|
||||
[JsonProperty(PropertyName = "metadata")]
|
||||
public V1ObjectMeta Metadata { get; set; }
|
||||
}
|
||||
|
||||
public abstract class CustomResource<TSpec, TStatus> : CustomResource
|
||||
{
|
||||
[JsonProperty(PropertyName = "spec")]
|
||||
public TSpec Spec { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "CStatus")]
|
||||
public TStatus CStatus { get; set; }
|
||||
}
|
||||
|
||||
public class CustomResourceList<T> : KubernetesObject
|
||||
where T : CustomResource
|
||||
{
|
||||
public V1ListMeta Metadata { get; set; }
|
||||
public List<T> Items { get; set; }
|
||||
}
|
||||
}
|
||||
103
examples/customResource/Program.cs
Normal file
103
examples/customResource/Program.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using k8s;
|
||||
using k8s.Models;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.JsonPatch;
|
||||
|
||||
|
||||
namespace customResource
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
private static async Task Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("strating main()...");
|
||||
|
||||
// creating the k8s client
|
||||
var k8SClientConfig = KubernetesClientConfiguration.BuildConfigFromConfigFile();
|
||||
IKubernetes client = new Kubernetes(k8SClientConfig);
|
||||
|
||||
// creating a K8s client for the CRD
|
||||
var myCRD = Utils.MakeCRD();
|
||||
Console.WriteLine("working with CRD: {0}.{1}", myCRD.PluralName, myCRD.Group);
|
||||
var generic = new GenericClient(k8SClientConfig, myCRD.Group, myCRD.Version, myCRD.PluralName);
|
||||
|
||||
// creating a sample custom resource content
|
||||
var myCr = Utils.MakeCResource();
|
||||
|
||||
try
|
||||
{
|
||||
Console.WriteLine("creating CR {0}", myCr.Metadata.Name);
|
||||
var response = await client.CreateNamespacedCustomObjectWithHttpMessagesAsync(
|
||||
myCr,
|
||||
myCRD.Group, myCRD.Version,
|
||||
myCr.Metadata.NamespaceProperty ?? "default",
|
||||
myCRD.PluralName).ConfigureAwait(false);
|
||||
}
|
||||
catch (Microsoft.Rest.HttpOperationException httpOperationException) when (httpOperationException.Message.Contains("422"))
|
||||
{
|
||||
var phase = httpOperationException.Response.ReasonPhrase;
|
||||
var content = httpOperationException.Response.Content;
|
||||
Console.WriteLine("response content: {0}", content);
|
||||
Console.WriteLine("response phase: {0}", phase);
|
||||
}
|
||||
catch (Microsoft.Rest.HttpOperationException)
|
||||
{
|
||||
}
|
||||
|
||||
// listing the cr instances
|
||||
Console.WriteLine("CR list:");
|
||||
var crs = await generic.ListNamespacedAsync<CustomResourceList<CResource>>(myCr.Metadata.NamespaceProperty ?? "default").ConfigureAwait(false);
|
||||
foreach (var cr in crs.Items)
|
||||
{
|
||||
Console.WriteLine("- CR Item {0} = {1}", crs.Items.IndexOf(cr), cr.Metadata.Name);
|
||||
}
|
||||
|
||||
// updating the custom resource
|
||||
myCr.Metadata.Labels.TryAdd("newKey", "newValue");
|
||||
var patch = new JsonPatchDocument<CResource>();
|
||||
patch.Replace(x => x.Metadata.Labels, myCr.Metadata.Labels);
|
||||
patch.Operations.ForEach(x => x.path = x.path.ToLower());
|
||||
var crPatch = new V1Patch(patch, V1Patch.PatchType.JsonPatch);
|
||||
try
|
||||
{
|
||||
var patchResponse = await client.PatchNamespacedCustomObjectAsync(
|
||||
crPatch,
|
||||
myCRD.Group,
|
||||
myCRD.Version,
|
||||
myCr.Metadata.NamespaceProperty ?? "default",
|
||||
myCRD.PluralName,
|
||||
myCr.Metadata.Name).ConfigureAwait(false);
|
||||
}
|
||||
catch (Microsoft.Rest.HttpOperationException httpOperationException)
|
||||
{
|
||||
var phase = httpOperationException.Response.ReasonPhrase;
|
||||
var content = httpOperationException.Response.Content;
|
||||
Console.WriteLine("response content: {0}", content);
|
||||
Console.WriteLine("response phase: {0}", phase);
|
||||
}
|
||||
|
||||
// getting the updated custom resource
|
||||
var fetchedCR = await generic.ReadNamespacedAsync<CResource>(
|
||||
myCr.Metadata.NamespaceProperty ?? "default",
|
||||
myCr.Metadata.Name).ConfigureAwait(false);
|
||||
|
||||
Console.WriteLine("fetchedCR = {0}", fetchedCR.ToString());
|
||||
|
||||
// deleting the custom resource
|
||||
try
|
||||
{
|
||||
myCr = await generic.DeleteNamespacedAsync<CResource>(
|
||||
myCr.Metadata.NamespaceProperty ?? "default",
|
||||
myCr.Metadata.Name).ConfigureAwait(false);
|
||||
|
||||
Console.WriteLine("Deleted the CR");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Console.WriteLine("Exception type {0}", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
53
examples/customResource/README.md
Normal file
53
examples/customResource/README.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# Custom Resource Client Example
|
||||
|
||||
This example demonstrates how to use the C# Kubernetes Client library to create, get and list custom resources.
|
||||
|
||||
## Pre-requisits
|
||||
|
||||
Make sure your have added the library package
|
||||
|
||||
```shell
|
||||
dotnet add package KubernetesClient
|
||||
```
|
||||
|
||||
## Create Custom Resource Definition (CRD)
|
||||
|
||||
Make sure the [CRD](./config/crd.yaml) is created, in order to create an instance of it after.
|
||||
|
||||
```shell
|
||||
kubectl create -f ./crd.yaml
|
||||
```
|
||||
|
||||
You can test that the CRD is successfully added, by creating an [instance](./config/yaml-cr-instance.yaml) of it using kubectl:
|
||||
|
||||
```shell
|
||||
kubectl create -f ./config/yaml-cr-instance.yaml
|
||||
```
|
||||
|
||||
```shell
|
||||
kubectl get customresources.csharp.com
|
||||
```
|
||||
|
||||
## Execute the code
|
||||
|
||||
The client uses the `BuildConfigFromConfigFile()` function. If the KUBECONFIG environment variable is set, then that path to the k8s config file will be used.
|
||||
|
||||
`dotnet run`
|
||||
|
||||
Expected output:
|
||||
|
||||
```
|
||||
strating main()...
|
||||
working with CRD: customresources.csharp.com
|
||||
creating CR cr-instance-london
|
||||
CR list:
|
||||
- CR Item 0 = cr-instance-london
|
||||
- CR Item 1 = cr-instance-paris
|
||||
fetchedCR = cr-instance-london (Labels: {identifier : city, newKey : newValue}), Spec: London
|
||||
Deleted the CR
|
||||
```
|
||||
|
||||
## Under the hood
|
||||
|
||||
For more details, you can look at the Generic client [implementation](https://github.com/kubernetes-client/csharp/blob/master/src/KubernetesClient/GenericClient.cs)
|
||||
|
||||
48
examples/customResource/Utils.cs
Normal file
48
examples/customResource/Utils.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using k8s.Models;
|
||||
using System.Collections.Generic;
|
||||
namespace customResource
|
||||
{
|
||||
public class Utils
|
||||
{
|
||||
// creats a CRD definition
|
||||
public static CustomResourceDefinition MakeCRD()
|
||||
{
|
||||
var myCRD = new CustomResourceDefinition()
|
||||
{
|
||||
Kind = "CResource",
|
||||
Group = "csharp.com",
|
||||
Version = "v1alpha1",
|
||||
PluralName = "customresources",
|
||||
};
|
||||
|
||||
return myCRD;
|
||||
}
|
||||
|
||||
// creats a CR instance
|
||||
public static CResource MakeCResource()
|
||||
{
|
||||
var myCResource = new CResource()
|
||||
{
|
||||
Kind = "CResource",
|
||||
ApiVersion = "csharp.com/v1alpha1",
|
||||
Metadata = new V1ObjectMeta
|
||||
{
|
||||
Name = "cr-instance-london",
|
||||
NamespaceProperty = "default",
|
||||
Labels = new Dictionary<string, string>
|
||||
{
|
||||
{
|
||||
"identifier", "city"
|
||||
},
|
||||
},
|
||||
},
|
||||
// spec
|
||||
Spec = new CResourceSpec
|
||||
{
|
||||
CityName = "London",
|
||||
},
|
||||
};
|
||||
return myCResource;
|
||||
}
|
||||
}
|
||||
}
|
||||
35
examples/customResource/cResource.cs
Normal file
35
examples/customResource/cResource.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using k8s.Models;
|
||||
using System.Collections.Generic;
|
||||
using k8s;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace customResource
|
||||
{
|
||||
public class CResource : CustomResource<CResourceSpec, CResourceStatus>
|
||||
{
|
||||
public override string ToString()
|
||||
{
|
||||
var labels = "{";
|
||||
foreach (var kvp in Metadata.Labels)
|
||||
{
|
||||
labels += kvp.Key + " : " + kvp.Value + ", ";
|
||||
}
|
||||
|
||||
labels = labels.TrimEnd(',', ' ') + "}";
|
||||
|
||||
return $"{Metadata.Name} (Labels: {labels}), Spec: {Spec.CityName}";
|
||||
}
|
||||
}
|
||||
|
||||
public class CResourceSpec
|
||||
{
|
||||
[JsonProperty(PropertyName = "cityName")]
|
||||
public string CityName { get; set; }
|
||||
}
|
||||
|
||||
public class CResourceStatus : V1Status
|
||||
{
|
||||
[JsonProperty(PropertyName = "temperature", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string Temperature { get; set; }
|
||||
}
|
||||
}
|
||||
11
examples/customResource/config/crd.yaml
Normal file
11
examples/customResource/config/crd.yaml
Normal file
@@ -0,0 +1,11 @@
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: customresources.csharp.com
|
||||
spec:
|
||||
group: csharp.com
|
||||
version: v1alpha1
|
||||
names:
|
||||
kind: CResource
|
||||
plural: customresources
|
||||
scope: Namespaced
|
||||
8
examples/customResource/config/yaml-cr-instance.yaml
Normal file
8
examples/customResource/config/yaml-cr-instance.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
apiVersion: csharp.com/v1alpha1
|
||||
kind: CResource
|
||||
metadata:
|
||||
name: cr-instance-paris
|
||||
namespace: default
|
||||
spec:
|
||||
cityName: Paris
|
||||
|
||||
15
examples/customResource/customResource.csproj
Normal file
15
examples/customResource/customResource.csproj
Normal file
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="KubernetesClient" Version="3.0.16" />
|
||||
<PackageReference Include="Microsoft.Build" Version="16.8.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.JsonPatch" Version="5.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -41,6 +41,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "E2E.Tests", "tests\E2E.Test
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SkipTestLogger", "tests\SkipTestLogger\SkipTestLogger.csproj", "{4D2AE427-F856-49E5-B61D-EA6B17D89051}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "customResource", "examples\customResource\customResource.csproj", "{95672061-5799-4454-ACDB-D6D330DB1EC4}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -231,6 +233,18 @@ Global
|
||||
{4D2AE427-F856-49E5-B61D-EA6B17D89051}.Release|x64.Build.0 = Release|Any CPU
|
||||
{4D2AE427-F856-49E5-B61D-EA6B17D89051}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{4D2AE427-F856-49E5-B61D-EA6B17D89051}.Release|x86.Build.0 = Release|Any CPU
|
||||
{95672061-5799-4454-ACDB-D6D330DB1EC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{95672061-5799-4454-ACDB-D6D330DB1EC4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{95672061-5799-4454-ACDB-D6D330DB1EC4}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{95672061-5799-4454-ACDB-D6D330DB1EC4}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{95672061-5799-4454-ACDB-D6D330DB1EC4}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{95672061-5799-4454-ACDB-D6D330DB1EC4}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{95672061-5799-4454-ACDB-D6D330DB1EC4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{95672061-5799-4454-ACDB-D6D330DB1EC4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{95672061-5799-4454-ACDB-D6D330DB1EC4}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{95672061-5799-4454-ACDB-D6D330DB1EC4}.Release|x64.Build.0 = Release|Any CPU
|
||||
{95672061-5799-4454-ACDB-D6D330DB1EC4}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{95672061-5799-4454-ACDB-D6D330DB1EC4}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -251,6 +265,7 @@ Global
|
||||
{B9647AD4-F6B0-406F-8B79-6781E31600EC} = {B70AFB57-57C9-46DC-84BE-11B7DDD34B40}
|
||||
{5056C4A2-5E12-4C16-8DA7-8835DA58BFF2} = {8AF4A5C2-F0CE-47D5-A4C5-FE4AB83CA509}
|
||||
{4D2AE427-F856-49E5-B61D-EA6B17D89051} = {8AF4A5C2-F0CE-47D5-A4C5-FE4AB83CA509}
|
||||
{95672061-5799-4454-ACDB-D6D330DB1EC4} = {B70AFB57-57C9-46DC-84BE-11B7DDD34B40}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {049A763A-C891-4E8D-80CF-89DD3E22ADC7}
|
||||
|
||||
Reference in New Issue
Block a user