CanSupportMe.Operator
1.0.0-nightly.9
See the version list below for details.
dotnet add package CanSupportMe.Operator --version 1.0.0-nightly.9
NuGet\Install-Package CanSupportMe.Operator -Version 1.0.0-nightly.9
<PackageReference Include="CanSupportMe.Operator" Version="1.0.0-nightly.9" />
paket add CanSupportMe.Operator --version 1.0.0-nightly.9
#r "nuget: CanSupportMe.Operator, 1.0.0-nightly.9"
// Install CanSupportMe.Operator as a Cake Addin #addin nuget:?package=CanSupportMe.Operator&version=1.0.0-nightly.9&prerelease // Install CanSupportMe.Operator as a Cake Tool #tool nuget:?package=CanSupportMe.Operator&version=1.0.0-nightly.9&prerelease
CanSupportMe.Operator
CAUTION
This is still in development and is not yet ready for production use.
The biggest issue is a timeout on the connection to the Kubernetes API that is not yet resolving itself.
This is being investigated.
Overview
What is this?
This package will allow you to create a simple Kubernetes Operator in .NET.
It can pick up changes to CRDs, ConfigMaps or Secrets and say if it was added, modified or deleted.
It can check if a CRD exists as well as retrieve, create, replace and delete configmaps and secrets.
It can also update the status of any manifest that supports it.
What isn't this?
It is not a full blown Kubernetes Operator SDK. These exist and, if desired, this package can be used in conjunction with them. It has been developed to do what the project required and nothing more however this will likely fit the requirements of many similar projects too.
Getting Started
Prerequisites
- .NET 8 or later
- A Kubernetes cluster (either local or remote)
- Sufficient access to the cluster to read and watch secrets (for the demo)
- The ability to create a service account and cluster role binding with access to the resources you will be watching or creating
- A suitable pair of test secrets, created using the following bash script:
kubectl apply -f - <<EOF apiVersion: v1 kind: Secret metadata: name: test-secret-with-label namespace: default labels: app.kubernetes.io/managed-by: DemoOperator stringData: notImportant: SomeValue type: Opaque --- apiVersion: v1 kind: Secret metadata: name: test-secret-without-label namespace: default stringData: notImportant: SomeValue type: Opaque EOF
Create a new .NET console application
Create a new .NET 8 console application and call it CanSupportMe.Operator.Demo
.
Install the package
dotnet add package CanSupportMe.Operator
Create a simple operator
Where you are defining your services in Program.cs
add the following:
services.AddOperator(options =>
{
options.OperatorName = "DemoOperator";
options.Group = "";
options.Kind = "Secret";
options.Version = "v1";
options.Plural = "secrets";
options.Scope = ResourceScope.Namespaced;
# This limits the response to just secrets labelled with "app.kubernetes.io/managed-by=DemoOperator"
options.LabelFilters.Add("app.kubernetes.io/managed-by", "DemoOperator");
options.OnAdded = async (kind, name, @namespace, item, dataObjectManager) =>
{
Log.Information("A secret called {Name} in {Namespace} of type {Type} with {ItemCount} item(s) was added", name, @namespace, item.Type, item.Data?.Count);
};
# Note this is only required if pointing at a cluster/user that doesn't contain a token in KUBECONFIG
options.FailoverToken = FAILOVER_TOKEN;
});
Running the project will show it connecting to the cluster and watching for changes to secrets. It will then show test-secret-with-label
as being added and not mention test-secret-without-label
.
Retrieve service token
If a options.FailoverToken
is required, one can be generated using the following bash script:
export SERVICEACCOUNT="cansupportme-operator"
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
name: ${SERVICEACCOUNT}-token
namespace: default
annotations:
kubernetes.io/service-account.name: $SERVICEACCOUNT
type: kubernetes.io/service-account-token
EOF
export TOKEN=$(kubectl -n default get secret ${SERVICEACCOUNT}-token -o jsonpath='{.data.token}' | base64 --decode)
echo $TOKEN
Listening for Custom Resource Definition (CRD) Changes
The more likely scenario for an operator is to listen out for CRD changes. The following example shows the simple file you need to create and how to configure the operator. Setting up a CRD is beyond the scope of this package's documentation.
Create a new file called MyCrd.cs
in the root of the project and add the following:
using CanSupportMe.Operator.Interfaces;
using CanSupportMe.Operator.Models;
using System.Text.Json.Serialization;
namespace CanSupportMe.Operator.Demo;
public class MyCrd : KubernetesResource<MySpec, MyStatus>
{
}
public class MySpec : IKubernetesSpec
{
[JsonPropertyName("secretName")]
public required string SecretName { get; set; }
}
public class MyStatus : IKubernetesStatus
{
[JsonPropertyName("status")]
public required string Status { get; set; }
}
Then, in Program.cs
add the following:
services.AddOperator<MySpec, MyStatus>(options =>
{
options.OperatorName = "SeqApiKeyOperator";
options.Group = "demokeys.cansupport.me";
options.Kind = "DemoApiKey";
options.Version = "v1alpha1";
options.Plural = "demokeys";
options.Scope = ResourceScope.Namespaced;
options.OnAdded = async (kind, name, @namespace, item, dataObjectManager) =>
{
Log.Information("On {kind} Add: {Name} to {Namespace} to create secret called {SecretName}", kind, name, @namespace, item.Spec.SecretName);
await dataObjectManager.UpdateStatus("Reconciling", name, @namespace);
var existingSecret = await dataObjectManager.Get("Secret", item.Spec.SecretName, @namespace);
if (existingSecret.IsSuccess)
{
Log.Debug("Secret {SecretName} already exists in {Namespace} and has a value of {ExistingValue}",
existingSecret.Value.Metadata.Name,
existingSecret.Value.Metadata.Namespace,
existingSecret.Value.Data["seqApiKey"]);
}
else
{
Log.Debug("Secret {SecretName} does not exist in {Namespace}", item.Spec.SecretName, @namespace);
await dataObjectManager.Create("Secret", item.Spec.SecretName, @namespace, new Dictionary<string, string>
{
{ "seqApiKey", $"Some random string at {DateTime.UtcNow}" }
}, labels: new()
{
{ "cansupport.me/owned-by", name }
});
}
await dataObjectManager.UpdateStatus("Ready", name, @namespace);
};
options.FailoverToken = FAILOVER_TOKEN;
});
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
-
net8.0
- DateTimeProvider (>= 1.0.106)
- FluentResults (>= 3.15.2)
- KubernetesClient (>= 13.0.1)
- Microsoft.Extensions.Hosting.Abstractions (>= 8.0.0)
- System.IO.Abstractions (>= 20.0.4)
- Throw (>= 1.4.0)
- YamlDotNet (>= 13.7.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated | |
---|---|---|---|
1.0.0 | 150 | 2/7/2024 | |
1.0.0-nightly.33 | 66 | 2/7/2024 | |
1.0.0-nightly.32 | 68 | 2/7/2024 | |
1.0.0-nightly.31 | 63 | 2/4/2024 | |
1.0.0-nightly.30 | 67 | 2/4/2024 | |
1.0.0-nightly.29 | 60 | 2/4/2024 | |
1.0.0-nightly.28 | 60 | 2/3/2024 | |
1.0.0-nightly.27 | 74 | 1/24/2024 | |
1.0.0-nightly.26 | 62 | 1/23/2024 | |
1.0.0-nightly.25 | 57 | 1/23/2024 | |
1.0.0-nightly.24 | 60 | 1/22/2024 | |
1.0.0-nightly.23 | 54 | 1/22/2024 | |
1.0.0-nightly.22 | 65 | 1/22/2024 | |
1.0.0-nightly.21 | 59 | 1/22/2024 | |
1.0.0-nightly.20 | 56 | 1/21/2024 | |
1.0.0-nightly.19 | 61 | 1/20/2024 | |
1.0.0-nightly.18 | 55 | 1/20/2024 | |
1.0.0-nightly.17 | 62 | 1/20/2024 | |
1.0.0-nightly.16 | 66 | 1/20/2024 | |
1.0.0-nightly.15 | 60 | 1/19/2024 | |
1.0.0-nightly.14 | 55 | 1/19/2024 | |
1.0.0-nightly.13 | 57 | 1/19/2024 | |
1.0.0-nightly.12 | 55 | 1/18/2024 | |
1.0.0-nightly.11 | 64 | 1/17/2024 | |
1.0.0-nightly.10 | 71 | 1/17/2024 | |
1.0.0-nightly.9 | 77 | 1/17/2024 | |
1.0.0-nightly.7 | 62 | 1/16/2024 | |
1.0.0-nightly.6 | 61 | 1/16/2024 | |
1.0.0-nightly | 112 | 1/15/2024 |