• Stars
    star
    190
  • Rank 203,739 (Top 5 %)
  • Language
    C#
  • License
    MIT License
  • Created almost 7 years ago
  • Updated about 2 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A Kubernetes API client for .NET Standard / .NET Core

KubeClient

Build Status (Travis CI)

KubeClient is an extensible Kubernetes API client for .NET Core (targets netstandard1.4).

Note - there is also an official .NET client for Kubernetes (both clients actually share code in a couple of places). These two clients are philosophically-different (from a design perspective) but either can be bent to fit your needs. For more information about how KubeClient differs from the official client, see the section below on extensibility.

Prerequisites

Note: If you need WebSocket / exec you'll need to target netcoreapp2.1.

Packages

  • KubeClient (netstandard1.4 or newer)
    The main client and models.
    KubeClient

  • KubeClient.Extensions.Configuration (netstandard2.0 or newer)
    Support for sourcing Microsoft.Extensions.Configuration data from Kubernetes Secrets and ConfigMaps.
    KubeClient.Extensions.KubeConfig

  • KubeClient.Extensions.DependencyInjection (netstandard2.0 or newer)
    Dependency-injection support.
    KubeClient.Extensions.KubeConfig

  • KubeClient.Extensions.KubeConfig (netstandard1.4 or newer)
    Support for loading and parsing configuration from ~/.kube/config.
    KubeClient.Extensions.KubeConfig

  • KubeClient.Extensions.WebSockets (netstandard2.1 or newer)
    Support for multiplexed WebSocket connections used by Kubernetes APIs (such as exec).
    This package also extends resource clients to add support for those APIs.

    Note that, due to a dependency on the new managed WebSockets implementation in .NET Core, this package targets netcoreapp2.1 (which requires SDK version 2.1.300 or newer) and therefore only works on .NET Core 2.1 or newer (it won't work on the full .NET Framework / UWP / Xamarin until they support netstandard2.1).
    KubeClient.Extensions.WebSockets

If you want to use the latest (development) builds of KubeClient, add the following feed to NuGet.config: https://www.myget.org/F/dotnet-kube-client/api/v3/index.json

Usage

The client can be used directly or injected via Microsoft.Extensions.DependencyInjection.

Use the client directly

The simplest way to create a client is to call KubeApiClient.Create(). There are overloads if you want to provide an access token, client certificate, or customise validation of the server's certificate:

// Assumes you're using "kubectl proxy", and no authentication is required.
KubeApiClient client = KubeApiClient.Create("http://localhost:8001");

PodListV1 pods = await client.PodsV1().List(
    labelSelector: "k8s-app=my-app"
);

For more flexible configuration, use the overload that takes KubeClientOptions:

KubeApiClient client = KubeApiClient.Create(new KubeClientOptions
{
    ApiEndPoint = new Uri("http://localhost:8001"),
    AuthStrategy = KubeAuthStrategy.BearerToken,
    AccessToken = "my-access-token",
    AllowInsecure = true // Don't validate server certificate
});

You can enable logging of requests and responses by passing an ILoggerFactory to KubeApiClient.Create() or KubeClientOptions.LoggerFactory:

ILoggerFactory loggers = new LoggerFactory();
loggers.AddConsole();

KubeApiClient client = KubeApiClient.Create("http://localhost:8001", loggers);

Configure the client from ~/.kube/config

using KubeClient.Extensions.KubeConfig;

KubeClientOptions clientOptions = K8sConfig.Load(kubeConfigFile).ToKubeClientOptions(
    kubeContextName: "my-cluster",
    defaultKubeNamespace: "kube-system"
);

KubeApiClient client = KubeApiClient.Create(clientOptions);

Make the client available for dependency injection

The client can be configured for dependency injection in a variety of ways.

To use a fixed set of options for the client, use the overload of AddKubeClient() that takes KubeClientoptions:

void ConfigureServices(IServiceCollection services)
{
    services.AddKubeClient(new KubeClientOptions
    {
        ApiEndPoint = new Uri("http://localhost:8001"),
        AuthStrategy = KubeAuthStrategy.BearerToken,
        AccessToken = "my-access-token",
        AllowInsecure = true // Don't validate server certificate
    });
}

To add a named instance of the client:

void ConfigureServices(IServiceCollection services)
{
    services.AddNamedKubeClients();
    services.AddKubeClientOptions("my-cluster", clientOptions =>
    {
        clientOptions.ApiEndPoint = new Uri("http://localhost:8001");
        clientOptions.AuthStrategy = KubeAuthStrategy.BearerToken;
        clientOptions.AccessToken = "my-access-token";
        clientOptions.AllowInsecure = true; // Don't validate server certificate
    });
    
    // OR:

    services.AddKubeClient("my-cluster", clientOptions =>
    {
        clientOptions.ApiEndPoint = new Uri("http://localhost:8001");
        clientOptions.AuthStrategy = KubeAuthStrategy.BearerToken;
        clientOptions.AccessToken = "my-access-token";
        clientOptions.AllowInsecure = true; // Don't validate server certificate
    });
}

// To use named instances of KubeApiClient, inject INamedKubeClients.

class MyClass
{
    public MyClass(INamedKubeClients namedKubeClients)
    {
        KubeClient1 = namedKubeClients.Get("my-cluster");
        KubeClient2 = namedKubeClients.Get("another-cluster");
    }

    IKubeApiClient KubeClient1 { get; }
    IKubeApiClient KubeClient2 { get; }
}

Design philosophy

Use of code generation is limited; generated clients tend to wind up being non-idiomatic and, for a Swagger spec as large as that of Kubernetes, wind up placing too many methods directly on the client class.

KubeClient's approach is to generate model classes (see src/swagger for the Python script that does this) and hand-code the actual operation methods to provide an improved consumer experience (i.e. useful and consistent exception types).

KubeResultV1

Some operations in the Kubernetes API can return a different response depending on the arguments passed in. For example, a request to delete a v1/Pod returns the existing v1/Pod (as a PodV1 model) if the caller specifies DeletePropagationPolicy.Foreground but returns a v1/Status (as a StatusV1 model) if any other type of DeletePropagationPolicy is specified.

To handle this type of polymorphic response KubeClient uses the KubeResultV1 model (and its derived implementations, KubeResourceResultV1<TResource> and KubeResourceListResultV1<TResource>).

KubeResourceResultV1<TResource> can be implicitly cast to a TResource or a StatusV1, so consuming code can continue to use the client as if it expects an operation to return only a resource or expects it to return only a StatusV1:

PodV1 existingPod = await client.PodsV1().Delete("mypod", propagationPolicy: DeletePropagationPolicy.Foreground);
// OR:
StatusV1 deleteStatus = await client.PodsV1().Delete("mypod", propagationPolicy: DeletePropagationPolicy.Background);

If an attempt is made to cast a KubeResourceResultV1<TResource> that contains a non-success StatusV1 to a TResource, a KubeApiException is thrown, based on the information in the StatusV1:

PodV1 existingPod;

try
{
    existingPod = await client.PodsV1().Delete("mypod", propagationPolicy: DeletePropagationPolicy.Foreground);
}
catch (KubeApiException kubeApiError)
{
    Log.Error(kubeApiError, "Failed to delete Pod: {ErrorMessage}", kubeApiError.Status.Message);
}

For more information about the behaviour of KubeResultV1 and its derived implementations, see KubeResultTests.cs.

Extensibility

KubeClient is designed to be easily extensible. The KubeApiClient provides the top-level entry point for the Kubernetes API and extension methods are used to expose more specific resource clients.

Simplified version of PodClientV1.cs:

public class PodClientV1 : KubeResourceClient
{
    public PodClientV1(KubeApiClient client) : base(client)
    {
    }

    public async Task<List<PodV1>> List(string labelSelector = null, string kubeNamespace = null, CancellationToken cancellationToken = default)
    {
        PodListV1 matchingPods =
            await Http.GetAsync(
                Requests.Collection.WithTemplateParameters(new
                {
                    Namespace = kubeNamespace ?? KubeClient.DefaultNamespace,
                    LabelSelector = labelSelector
                }),
                cancellationToken: cancellationToken
            )
            .ReadContentAsObjectV1Async<PodListV1>();

        return matchingPods.Items;
    }

    public static class Requests
    {
        public static readonly HttpRequest Collection = KubeRequest.Create("api/v1/namespaces/{Namespace}/pods?labelSelector={LabelSelector?}&watch={Watch?}");
    }
}

Simplified version of ClientFactoryExtensions.cs:

public static PodClientV1 PodsV1(this KubeApiClient kubeClient)
{
    return kubeClient.ResourceClient(
        client => new PodClientV1(client)
    );
}

This enables the following usage of KubeApiClient:

KubeApiClient client;
PodListV1 pods = await client.PodsV1().List(kubeNamespace: "kube-system");

Through the use of extension methods, resource clients (or additional operations) can be declared in any assembly and used as if they are part of the KubeApiClient. For example, the KubeClient.Extensions.WebSockets package adds an ExecAndConnect method to PodClientV1.

Simplified version of ResourceClientWebSocketExtensions.cs:

public static async Task<K8sMultiplexer> ExecAndConnect(this IPodClientV1 podClient, string podName, string command, bool stdin = false, bool stdout = true, bool stderr = false, bool tty = false, string container = null, string kubeNamespace = null, CancellationToken cancellation = default)
{
    byte[] outputStreamIndexes = stdin ? new byte[1] { 0 } : new byte[0];
    byte[] inputStreamIndexes;
    if (stdout && stderr)
        inputStreamIndexes = new byte[2] { 1, 2 };
    else if (stdout)
        inputStreamIndexes = new byte[1] { 1 };
    else if (stderr)
        inputStreamIndexes = new byte[1] { 2 };
    else if (!stdin)
        throw new InvalidOperationException("Must specify at least one of STDIN, STDOUT, or STDERR.");
    else
        inputStreamIndexes = new byte[0];
    
    return await podClient.KubeClient
        .ConnectWebSocket("api/v1/namespaces/{KubeNamespace}/pods/{PodName}/exec?stdin={StdIn?}&stdout={StdOut?}&stderr={StdErr?}&tty={TTY?}&command={Command}&container={Container?}", new
        {
            PodName = podName,
            Command = command,
            StdIn = stdin,
            StdOut = stdout,
            StdErr = stderr,
            TTY = tty,
            Container = container,
            KubeNamespace = kubeNamespace ?? podClient.KubeClient.DefaultNamespace
        }, cancellation)
        .Multiplexed(inputStreamIndexes, outputStreamIndexes,
            loggerFactory: podClient.KubeClient.LoggerFactory()
        );
}

Example usage of ExecAndConnect:

KubeApiClient client;
K8sMultiplexer connection = await client.PodsV1().ExecAndConnect(
    podName: "my-pod",
    command: "/bin/bash",
    stdin: true,
    stdout: true,
    tty: true
);
using (connection)
using (StreamWriter stdin = new StreamWriter(connection.GetOutputStream(0), Encoding.UTF8))
using (StreamReader stdout = new StreamReader(connection.GetInputStream(1), Encoding.UTF8))
{
    await stdin.WriteLineAsync("ls -l /");
    await stdin.WriteLineAsync("exit");

    // Read from STDOUT until process terminates.
    string line;
    while ((line = await stdout.ReadLineAsync()) != null)
    {
        Console.WriteLine(line);
    }
}

For information about HttpRequest, UriTemplate, and other features used to implement the client take a look at the HTTPlease documentation.

Working out what APIs to call

If you want to replicate the behaviour of a kubectl command you can pass the flag --v=10 to kubectl and it will dump out (for each request that it makes) the request URI, request body, and response body.

Building

You will need to use v2.1.300 (or newer) of the .NET Core SDK to build KubeClient.

Questions / feedback

Feel free to get in touch if you have questions, feedback, or would like to contribute.

More Repositories

1

msbuild-project-tools-vscode

VS Code extension for MSBuild intellisense (including PackageReference completion).
TypeScript
82
star
2

msbuild-project-tools-server

Language server for MSBuild intellisense (including PackageReference completion).
C#
56
star
3

rx-akkadotnet

Reactive Extensions (Rx) integration for Akka.NET
C#
21
star
4

ps-core-module-template

"dotnet new" template for a binary PowerShell Core module
C#
21
star
5

dotnet-template-templates

Templates for "dotnet new" for creating project and item templates
16
star
6

seqlog

Python logging integration for Seq (https://getseq.net)
Python
15
star
7

dotnet-language-client

.NET client for the Language Server Protocol (LSP)
C#
12
star
8

msbuild-extension-template

A project template for "dotnet new" that creates a new MSBuild extension package
C#
11
star
9

docker-machine-driver-terraform

Docker Machine driver that uses Terraform for infrastructure provisioning
Go
11
star
10

HTTPlease

HTTP client library using immutable templates with lazily-resolved parameters
C#
8
star
11

logstash-output-seq

Seq output plugin for Logstash
Ruby
5
star
12

akka-websockets

WebSockets using Akka HTTP
Scala
3
star
13

aykay-deekay

AK/DK - an Akka.NET extension for driving / reacting to the Docker API
C#
3
star
14

docfx-assistant

DocFX assistant for VS Code
TypeScript
2
star
15

ps-reptile

A MAML-format help generator for binary PowerShell modules.
C#
2
star
16

docfx-razor

A quick-and-dirty demo of how to use MVC and Razor as your DocFX templating engine
CSS
2
star
17

first-akka

My first attempt at native (scala-based) Akka
Scala
1
star
18

kube-traefik-ingress-demo

A quick demonstration of using Traefik for Ingress on Kubernetes.
1
star
19

nuget-client-nodejs

A NodeJS client for the NuGet API
TypeScript
1
star
20

7lang7-ruby

Self-study notes for "Seven Languages in Seven Weeks" (Ruby).
Ruby
1
star
21

PropterHoc

Propter Hoc: A causality toolkit
C#
1
star
22

akka-n2j-bridge-demo

A quick demo of connecting actors in JVM Akka to actors in Akka.NET over a TCP connection (using their respective I/O frameworks).
C#
1
star
23

terraform-variables

Quick-and-dirty tool to generate JSON representing the variables in a Terraform configuration
Makefile
1
star
24

elixir-agents-tutorial

Following the Elixir OTP/Agents tutorial
Elixir
1
star
25

wamp2-netcore-demo

A demo of WAMP 2.x client and server on .NET Core using WampSharp.
C#
1
star
26

dev-tools

Adam's development tools / environment scripts
PowerShell
1
star
27

docker-terraform-ansible

A quick demo of service-catalog item deployment using Docker, Terraform, and Ansible
Python
1
star
28

scala-signalr-client

Quick-and-dirty prototype to demonstrate a slightly more Scala-ish client API for SignalR.Yes, I'm sure it's hideous if you're an experienced Scala dev, but we all have to start somewhere.
Scala
1
star
29

dotnet-launcher

A cross-platform launcher for framework-dependent deployments of .NET Core applications
Go
1
star
30

tfs-publish-trx

Publish Visual Studio test result (TRX) files to TFS.
JavaScript
1
star
31

CompactSessionSecurityTokens

Improved implementation of Windows Identity Foundation's SessionSecurityTokenHandler that produces more compact tokens than the original.
C#
1
star