Version 17.0 + Generate 1.33 (#1626)

* generated based on 1.33

* Update version to 17.0 in version.json

* Remove extra API endpoint from swagger.json

* Remove ModelConverter and related AutoMapper components

* Update package versions

* Refactor code to use ConfigureAwait(false) for asynchronous calls and update target framework to net9.0

* Remove ConfigureAwait(false) from OidcAuthTests for consistency in async calls

* Update SDK version in README to reflect support for net8.0 and net9.0

* Update dotnet SDK version to 9.0.x in build workflow

* Revert Fractions package version to 7.3.0 in Directory.Packages.props

* Update target framework to netstandard2.1 for improved compatibility

* Update package references for Microsoft.CodeAnalysis in Directory.Packages.props and LibKubernetesGenerator.target

* Refactor Worker class constructor documentation and standardize Dictionary type declaration in Program.cs
This commit is contained in:
Boshi Lian
2025-04-27 12:55:24 -07:00
committed by GitHub
parent dfdf0b70d2
commit 001189de77
30 changed files with 10666 additions and 864 deletions

View File

@@ -50,7 +50,7 @@ jobs:
- name: Setup dotnet SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
dotnet-version: '9.0.x'
- name: Restore nugets (msbuild)
run: msbuild .\src\KubernetesClient\ -t:restore -p:RestorePackagesConfig=true
- name: Build (msbuild)

View File

@@ -1,53 +1,53 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="AutoMapper" Version="13.0.1" />
<PackageVersion Include="BouncyCastle.Cryptography" Version="2.5.0" />
<PackageVersion Include="FluentAssertions" Version="7.0.0" />
<PackageVersion Include="Fractions" Version="7.3.0" />
<PackageVersion Include="JsonPatch.Net" Version="2.1.0" />
<PackageVersion Include="MartinCostello.Logging.XUnit" Version="0.5.1" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageVersion Include="Microsoft.TestPlatform.ObjectModel" Version="17.12.0" />
<PackageVersion Include="Moq" Version="4.20.72" />
<PackageVersion Include="Nito.AsyncEx" Version="5.1.2" />
<PackageVersion Include="Nito.AsyncEx.Coordination" Version="5.1.2" />
<PackageVersion Include="OpenTelemetry.Exporter.Console" Version="1.7.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.8.1" />
<PackageVersion Include="Portable.BouncyCastle" Version="1.9.0" />
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="9.0.0" />
<PackageVersion Include="System.IO.Abstractions.TestingHelpers" Version="21.2.1" />
<PackageVersion Include="System.Reactive" Version="6.0.1" />
<PackageVersion Include="System.Text.Json" Version="9.0.0" />
<PackageVersion Include="Vecc.YamlDotNet.Analyzers.StaticGenerator" Version="16.3.0" />
<PackageVersion Include="Wiremock.Net" Version="1.7.4" />
<PackageVersion Include="xunit" Version="2.9.2" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.0.0" />
<PackageVersion Include="Xunit.StaFact" Version="1.1.11" />
<PackageVersion Include="YamlDotNet" Version="16.3.0" />
</ItemGroup>
<ItemGroup>
<PackageVersion Include="Autofac" Version="8.2.0" />
<PackageVersion Include="CaseExtensions" Version="1.1.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.4.0" />
<PackageVersion Include="Namotion.Reflection" Version="3.0.1" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="NJsonSchema" Version="10.9.0" />
<PackageVersion Include="NSwag.Core" Version="13.20.0" />
<PackageVersion Include="Scriban" Version="5.9.1" />
</ItemGroup>
<ItemGroup>
<GlobalPackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0" />
<GlobalPackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
<GlobalPackageReference Include="Microsoft.VisualStudio.SlnGen" Version="12.0.3" />
<GlobalPackageReference Include="Nerdbank.GitVersioning" Version="3.7.112" />
<GlobalPackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
</ItemGroup>
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="BouncyCastle.Cryptography" Version="2.5.1" />
<PackageVersion Include="FluentAssertions" Version="8.2.0" />
<PackageVersion Include="Fractions" Version="7.3.0" />
<PackageVersion Include="JsonPatch.Net" Version="3.3.0" />
<PackageVersion Include="MartinCostello.Logging.XUnit" Version="0.5.1" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.4" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
<PackageVersion Include="Microsoft.TestPlatform.ObjectModel" Version="17.13.0" />
<PackageVersion Include="Moq" Version="4.20.72" />
<PackageVersion Include="Nito.AsyncEx" Version="5.1.2" />
<PackageVersion Include="Nito.AsyncEx.Coordination" Version="5.1.2" />
<PackageVersion Include="OpenTelemetry.Exporter.Console" Version="1.11.2" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.11.1" />
<PackageVersion Include="Portable.BouncyCastle" Version="1.9.0" />
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="8.1.1" />
<PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="9.0.4" />
<PackageVersion Include="System.IO.Abstractions.TestingHelpers" Version="22.0.14" />
<PackageVersion Include="System.Reactive" Version="6.0.1" />
<PackageVersion Include="System.Text.Json" Version="9.0.4" />
<PackageVersion Include="Vecc.YamlDotNet.Analyzers.StaticGenerator" Version="16.3.0" />
<PackageVersion Include="Wiremock.Net" Version="1.7.4" />
<PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.0.2" />
<PackageVersion Include="Xunit.StaFact" Version="1.2.69" />
<PackageVersion Include="YamlDotNet" Version="16.3.0" />
</ItemGroup>
<ItemGroup>
<PackageVersion Include="Autofac" Version="8.2.1" />
<PackageVersion Include="CaseExtensions" Version="1.1.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.13.0" />
<PackageVersion Include="Namotion.Reflection" Version="3.3.0" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="NJsonSchema" Version="11.2.0" />
<PackageVersion Include="NJsonSchema.Annotations" Version="11.2.0" />
<PackageVersion Include="NSwag.Core" Version="14.3.0" />
<PackageVersion Include="Scriban" Version="6.2.1" />
</ItemGroup>
<ItemGroup>
<GlobalPackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0" />
<GlobalPackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
<GlobalPackageReference Include="Microsoft.VisualStudio.SlnGen" Version="12.0.3" />
<GlobalPackageReference Include="Nerdbank.GitVersioning" Version="3.7.112" />
<GlobalPackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
</ItemGroup>
</Project>

View File

@@ -154,6 +154,7 @@ ${GEN_DIR}/openapi/csharp.sh ${REPO_DIR}/src/KubernetesClient ${REPO_DIR}/csharp
| SDK Version | Kubernetes Version | .NET Targeting |
|-------------|--------------------|-----------------------------------------------------|
| 17.0 | 1.33 | net8.0;net9.0;net48*;netstandard2.0* |
| 16.0 | 1.32 | net8.0;net9.0;net48*;netstandard2.0* |
| 15.0 | 1.31 | net6.0;net8.0;net48*;netstandard2.0* |
| 14.0 | 1.30 | net6.0;net8.0;net48*;netstandard2.0* |

View File

@@ -1,3 +1,3 @@
export KUBERNETES_BRANCH=v1.32.0
export KUBERNETES_BRANCH=v1.33.0
export CLIENT_VERSION=0.0.1
export PACKAGE_NAME=k8s

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\Directory.Build.props" />
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
</Project>

View File

@@ -1,4 +1,5 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\Directory.Build.targets" />
<ItemGroup>
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\src\KubernetesClient\KubernetesClient.csproj" Condition="'$(PublishAot)' != 'true'" />
</ItemGroup>

View File

@@ -1,4 +1,4 @@
using ICSharpCode.SharpZipLib.Tar;
using ICSharpCode.SharpZipLib.Tar;
using k8s;
using System;
using System.IO;
@@ -7,110 +7,104 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace cp
namespace cp;
internal class Cp
{
internal class Cp
private static IKubernetes client;
private static async Task Main(string[] args)
{
private static IKubernetes client;
var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
client = new Kubernetes(config);
private static async Task Main(string[] args)
var pods = client.CoreV1.ListNamespacedPod("default", null, null, null, $"job-name=upload-demo");
var pod = pods.Items.First();
await CopyFileToPodAsync(pod.Metadata.Name, "default", "upload-demo", args[0], $"home/{args[1]}").ConfigureAwait(false);
}
private static void ValidatePathParameters(string sourcePath, string destinationPath)
{
if (string.IsNullOrWhiteSpace(sourcePath))
{
var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
client = new Kubernetes(config);
var pods = client.CoreV1.ListNamespacedPod("default", null, null, null, $"job-name=upload-demo");
var pod = pods.Items.First();
await CopyFileToPodAsync(pod.Metadata.Name, "default", "upload-demo", args[0], $"home/{args[1]}");
throw new ArgumentException($"{nameof(sourcePath)} cannot be null or whitespace");
}
private static void ValidatePathParameters(string sourcePath, string destinationPath)
if (string.IsNullOrWhiteSpace(destinationPath))
{
if (string.IsNullOrWhiteSpace(sourcePath))
{
throw new ArgumentException($"{nameof(sourcePath)} cannot be null or whitespace");
}
if (string.IsNullOrWhiteSpace(destinationPath))
{
throw new ArgumentException($"{nameof(destinationPath)} cannot be null or whitespace");
}
throw new ArgumentException($"{nameof(destinationPath)} cannot be null or whitespace");
}
}
public static async Task<int> CopyFileToPodAsync(string name, string @namespace, string container, string sourceFilePath, string destinationFilePath, CancellationToken cancellationToken = default(CancellationToken))
public static async Task<int> CopyFileToPodAsync(string name, string @namespace, string container, string sourceFilePath, string destinationFilePath, CancellationToken cancellationToken = default(CancellationToken))
{
// All other parameters are being validated by MuxedStreamNamespacedPodExecAsync called by NamespacedPodExecAsync
ValidatePathParameters(sourceFilePath, destinationFilePath);
// The callback which processes the standard input, standard output and standard error of exec method
var handler = new ExecAsyncCallback(async (stdIn, stdOut, stdError) =>
{
// All other parameters are being validated by MuxedStreamNamespacedPodExecAsync called by NamespacedPodExecAsync
ValidatePathParameters(sourceFilePath, destinationFilePath);
// The callback which processes the standard input, standard output and standard error of exec method
var handler = new ExecAsyncCallback(async (stdIn, stdOut, stdError) =>
var fileInfo = new FileInfo(destinationFilePath);
try
{
var fileInfo = new FileInfo(destinationFilePath);
try
using (var memoryStream = new MemoryStream())
{
using (var memoryStream = new MemoryStream())
using (var inputFileStream = File.OpenRead(sourceFilePath))
using (var tarOutputStream = new TarOutputStream(memoryStream, Encoding.Default))
{
using (var inputFileStream = File.OpenRead(sourceFilePath))
using (var tarOutputStream = new TarOutputStream(memoryStream, Encoding.Default))
{
tarOutputStream.IsStreamOwner = false;
tarOutputStream.IsStreamOwner = false;
var fileSize = inputFileStream.Length;
var entry = TarEntry.CreateTarEntry(fileInfo.Name);
var fileSize = inputFileStream.Length;
var entry = TarEntry.CreateTarEntry(fileInfo.Name);
entry.Size = fileSize;
entry.Size = fileSize;
tarOutputStream.PutNextEntry(entry);
await inputFileStream.CopyToAsync(tarOutputStream);
tarOutputStream.CloseEntry();
}
memoryStream.Position = 0;
await memoryStream.CopyToAsync(stdIn);
await stdIn.FlushAsync();
tarOutputStream.PutNextEntry(entry);
await inputFileStream.CopyToAsync(tarOutputStream).ConfigureAwait(false);
tarOutputStream.CloseEntry();
}
memoryStream.Position = 0;
await memoryStream.CopyToAsync(stdIn).ConfigureAwait(false);
await stdIn.FlushAsync().ConfigureAwait(false);
}
catch (Exception ex)
{
throw new IOException($"Copy command failed: {ex.Message}");
}
}
catch (Exception ex)
{
throw new IOException($"Copy command failed: {ex.Message}");
}
using StreamReader streamReader = new StreamReader(stdError);
while (streamReader.EndOfStream == false)
{
string error = await streamReader.ReadToEndAsync();
throw new IOException($"Copy command failed: {error}");
}
});
using StreamReader streamReader = new StreamReader(stdError);
while (streamReader.EndOfStream == false)
{
string error = await streamReader.ReadToEndAsync().ConfigureAwait(false);
throw new IOException($"Copy command failed: {error}");
}
});
string destinationFolder = GetFolderName(destinationFilePath);
string destinationFolder = GetFolderName(destinationFilePath);
return await client.NamespacedPodExecAsync(
name,
@namespace,
container,
new string[] { "sh", "-c", $"tar xmf - -C {destinationFolder}" },
false,
handler,
cancellationToken);
}
return await client.NamespacedPodExecAsync(
name,
@namespace,
container,
new string[] { "sh", "-c", $"tar xmf - -C {destinationFolder}" },
false,
handler,
cancellationToken).ConfigureAwait(false);
}
private static string GetFolderName(string filePath)
{
var folderName = Path.GetDirectoryName(filePath);
return string.IsNullOrEmpty(folderName) ? "." : folderName;
}
private static string GetFolderName(string filePath)
{
var folderName = Path.GetDirectoryName(filePath);
return string.IsNullOrEmpty(folderName) ? "." : folderName;
}
}

View File

@@ -1,4 +1,4 @@
using Json.Patch;
using Json.Patch;
using k8s;
using k8s.Models;
using System.Net;
@@ -44,34 +44,34 @@ var request = new V1CertificateSigningRequest
Kind = "CertificateSigningRequest",
Metadata = new V1ObjectMeta
{
Name = name
Name = name,
},
Spec = new V1CertificateSigningRequestSpec
{
Request = encodedCsr,
SignerName = "kubernetes.io/kube-apiserver-client",
Usages = new List<string> { "client auth" },
ExpirationSeconds = 600 // minimum should be 10 minutes
}
ExpirationSeconds = 600, // minimum should be 10 minutes
},
};
await client.CertificatesV1.CreateCertificateSigningRequestAsync(request);
await client.CertificatesV1.CreateCertificateSigningRequestAsync(request).ConfigureAwait(false);
var serializeOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true
WriteIndented = true,
};
var readCert = await client.CertificatesV1.ReadCertificateSigningRequestAsync(name);
var readCert = await client.CertificatesV1.ReadCertificateSigningRequestAsync(name).ConfigureAwait(false);
var old = JsonSerializer.SerializeToDocument(readCert, serializeOptions);
var replace = new List<V1CertificateSigningRequestCondition>
{
new("True", "Approved", DateTime.UtcNow, DateTime.UtcNow, "This certificate was approved by k8s client", "Approve")
new("True", "Approved", DateTime.UtcNow, DateTime.UtcNow, "This certificate was approved by k8s client", "Approve"),
};
readCert.Status.Conditions = replace;
var expected = JsonSerializer.SerializeToDocument(readCert, serializeOptions);
var patch = old.CreatePatch(expected);
await client.CertificatesV1.PatchCertificateSigningRequestApprovalAsync(new V1Patch(patch, V1Patch.PatchType.JsonPatch), name);
await client.CertificatesV1.PatchCertificateSigningRequestApprovalAsync(new V1Patch(patch, V1Patch.PatchType.JsonPatch), name).ConfigureAwait(false);

View File

@@ -29,6 +29,7 @@ foreach (var item in list.Items)
{
Console.WriteLine(item.Metadata.Name);
}
// Or empty if there are no pods
if (list.Items.Count == 0)
{

View File

@@ -28,5 +28,6 @@ static void PrintLabels(V1Pod pod)
{
Console.WriteLine($"{k} : {v}");
}
Console.WriteLine("=-=-=-=-=-=-=-=-=-=-=");
}

View File

@@ -18,13 +18,13 @@ namespace portforward
var list = client.CoreV1.ListNamespacedPod("default");
var pod = list.Items[0];
await Forward(client, pod);
await Forward(client, pod).ConfigureAwait(false);
}
private static async Task Forward(IKubernetes client, V1Pod pod)
{
// Note this is single-threaded, it won't handle concurrent requests well...
var webSocket = await client.WebSocketNamespacedPodPortForwardAsync(pod.Metadata.Name, "default", new int[] { 80 }, "v4.channel.k8s.io");
var webSocket = await client.WebSocketNamespacedPodPortForwardAsync(pod.Metadata.Name, "default", new int[] { 80 }, "v4.channel.k8s.io").ConfigureAwait(false);
var demux = new StreamDemuxer(webSocket, StreamType.PortForward);
demux.Start();
@@ -67,12 +67,13 @@ namespace portforward
}
});
await accept;
await copy;
await accept.ConfigureAwait(false);
await copy.ConfigureAwait(false);
if (handler != null)
{
handler.Close();
}
listener.Close();
}
}

View File

@@ -1,17 +1,17 @@
using Json.Patch;
using Json.Patch;
using k8s;
using k8s.Models;
using System.Text.Json;
async Task RestartDaemonSetAsync(string name, string @namespace, IKubernetes client)
{
var daemonSet = await client.AppsV1.ReadNamespacedDaemonSetAsync(name, @namespace);
var daemonSet = await client.AppsV1.ReadNamespacedDaemonSetAsync(name, @namespace).ConfigureAwait(false);
var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = true };
var old = JsonSerializer.SerializeToDocument(daemonSet, options);
var now = DateTimeOffset.Now.ToUnixTimeSeconds();
var restart = new Dictionary<string, string>
{
["date"] = now.ToString()
["date"] = now.ToString(),
};
daemonSet.Spec.Template.Metadata.Annotations = restart;
@@ -19,18 +19,18 @@ async Task RestartDaemonSetAsync(string name, string @namespace, IKubernetes cli
var expected = JsonSerializer.SerializeToDocument(daemonSet);
var patch = old.CreatePatch(expected);
await client.AppsV1.PatchNamespacedDaemonSetAsync(new V1Patch(patch, V1Patch.PatchType.JsonPatch), name, @namespace);
await client.AppsV1.PatchNamespacedDaemonSetAsync(new V1Patch(patch, V1Patch.PatchType.JsonPatch), name, @namespace).ConfigureAwait(false);
}
async Task RestartDeploymentAsync(string name, string @namespace, IKubernetes client)
{
var deployment = await client.AppsV1.ReadNamespacedDeploymentAsync(name, @namespace);
var deployment = await client.AppsV1.ReadNamespacedDeploymentAsync(name, @namespace).ConfigureAwait(false);
var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = true };
var old = JsonSerializer.SerializeToDocument(deployment, options);
var now = DateTimeOffset.Now.ToUnixTimeSeconds();
var restart = new Dictionary<string, string>
{
["date"] = now.ToString()
["date"] = now.ToString(),
};
deployment.Spec.Template.Metadata.Annotations = restart;
@@ -38,18 +38,18 @@ async Task RestartDeploymentAsync(string name, string @namespace, IKubernetes cl
var expected = JsonSerializer.SerializeToDocument(deployment);
var patch = old.CreatePatch(expected);
await client.AppsV1.PatchNamespacedDeploymentAsync(new V1Patch(patch, V1Patch.PatchType.JsonPatch), name, @namespace);
await client.AppsV1.PatchNamespacedDeploymentAsync(new V1Patch(patch, V1Patch.PatchType.JsonPatch), name, @namespace).ConfigureAwait(false);
}
async Task RestartStatefulSetAsync(string name, string @namespace, IKubernetes client)
{
var deployment = await client.AppsV1.ReadNamespacedStatefulSetAsync(name, @namespace);
var deployment = await client.AppsV1.ReadNamespacedStatefulSetAsync(name, @namespace).ConfigureAwait(false);
var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = true };
var old = JsonSerializer.SerializeToDocument(deployment, options);
var now = DateTimeOffset.Now.ToUnixTimeSeconds();
var restart = new Dictionary<string, string>
{
["date"] = now.ToString()
["date"] = now.ToString(),
};
deployment.Spec.Template.Metadata.Annotations = restart;
@@ -57,12 +57,12 @@ async Task RestartStatefulSetAsync(string name, string @namespace, IKubernetes c
var expected = JsonSerializer.SerializeToDocument(deployment);
var patch = old.CreatePatch(expected);
await client.AppsV1.PatchNamespacedStatefulSetAsync(new V1Patch(patch, V1Patch.PatchType.JsonPatch), name, @namespace);
await client.AppsV1.PatchNamespacedStatefulSetAsync(new V1Patch(patch, V1Patch.PatchType.JsonPatch), name, @namespace).ConfigureAwait(false);
}
var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
IKubernetes client = new Kubernetes(config);
await RestartDeploymentAsync("event-exporter", "monitoring", client);
await RestartDaemonSetAsync("prometheus-exporter", "monitoring", client);
await RestartStatefulSetAsync("argocd-application-controlle", "argocd", client);
await RestartDeploymentAsync("event-exporter", "monitoring", client).ConfigureAwait(false);
await RestartDaemonSetAsync("prometheus-exporter", "monitoring", client).ConfigureAwait(false);
await RestartStatefulSetAsync("argocd-application-controlle", "argocd", client).ConfigureAwait(false);

View File

@@ -16,7 +16,7 @@ namespace watch
var podlistResp = client.CoreV1.ListNamespacedPodWithHttpMessagesAsync("default", watch: true);
// C# 8 required https://docs.microsoft.com/en-us/archive/msdn-magazine/2019/november/csharp-iterating-with-async-enumerables-in-csharp-8
await foreach (var (type, item) in podlistResp.WatchAsync<V1Pod, V1PodList>())
await foreach (var (type, item) in podlistResp.WatchAsync<V1Pod, V1PodList>().ConfigureAwait(false))
{
Console.WriteLine("==on watch event==");
Console.WriteLine(type);
@@ -28,7 +28,9 @@ namespace watch
// WatchUsingCallback(client);
}
#pragma warning disable IDE0051 // Remove unused private members
private static void WatchUsingCallback(IKubernetes client)
#pragma warning restore IDE0051 // Remove unused private members
{
var podlistResp = client.CoreV1.ListNamespacedPodWithHttpMessagesAsync("default", watch: true);
using (podlistResp.Watch<V1Pod, V1PodList>((type, item) =>

View File

@@ -14,4 +14,4 @@ IHost host = Host.CreateDefaultBuilder(args)
})
.Build();
await host.RunAsync();
await host.RunAsync().ConfigureAwait(false);

View File

@@ -8,10 +8,11 @@ namespace workerServiceDependencyInjection
private readonly IKubernetes kubernetesClient;
/// <summary>
/// Initializes a new instance of the <see cref="Worker"/> class.
/// Inject in the constructor the IKubernetes interface.
/// </summary>
/// <param name="logger"></param>
/// <param name="kubernetesClient"></param>
/// <param name="logger">The logger instance used for logging information.</param>
/// <param name="kubernetesClient">The Kubernetes client used to interact with the Kubernetes API.</param>
public Worker(ILogger<Worker> logger, IKubernetes kubernetesClient)
{
this.logger = logger;
@@ -33,7 +34,7 @@ namespace workerServiceDependencyInjection
Console.WriteLine(pod.Metadata.Name);
}
await Task.Delay(1000, stoppingToken);
await Task.Delay(1000, stoppingToken).ConfigureAwait(false);
}
}
}

View File

@@ -10,14 +10,14 @@ namespace yaml
{
private static async Task Main(string[] args)
{
var typeMap = new Dictionary<String, Type>
var typeMap = new Dictionary<string, Type>
{
{ "v1/Pod", typeof(V1Pod) },
{ "v1/Service", typeof(V1Service) },
{ "apps/v1/Deployment", typeof(V1Deployment) }
{ "apps/v1/Deployment", typeof(V1Deployment) },
};
var objects = await KubernetesYaml.LoadAllFromFileAsync(args[0], typeMap);
var objects = await KubernetesYaml.LoadAllFromFileAsync(args[0], typeMap).ConfigureAwait(false);
foreach (var obj in objects)
{

View File

@@ -1,17 +0,0 @@
using static k8s.Models.ModelVersionConverter;
namespace k8s.ModelConverter.AutoMapper;
public class AutoMapperModelVersionConverter : IModelVersionConverter
{
public static IModelVersionConverter Instance { get; } = new AutoMapperModelVersionConverter();
private AutoMapperModelVersionConverter()
{
}
public TTo Convert<TFrom, TTo>(TFrom from)
{
return VersionConverter.Mapper.Map<TTo>(from);
}
}

View File

@@ -1,58 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace k8s.ModelConverter.AutoMapper;
internal class KubernetesVersionComparer : IComparer<string>
{
public static KubernetesVersionComparer Instance { get; } = new KubernetesVersionComparer();
private static readonly Regex KubernetesVersionRegex = new Regex(@"^v(?<major>[0-9]+)((?<stream>alpha|beta)(?<minor>[0-9]+))?$", RegexOptions.Compiled);
internal KubernetesVersionComparer()
{
}
public int Compare(string x, string y)
{
if (x == null || y == null)
{
return StringComparer.CurrentCulture.Compare(x, y);
}
var matchX = KubernetesVersionRegex.Match(x);
if (!matchX.Success)
{
return StringComparer.CurrentCulture.Compare(x, y);
}
var matchY = KubernetesVersionRegex.Match(y);
if (!matchY.Success)
{
return StringComparer.CurrentCulture.Compare(x, y);
}
var versionX = ExtractVersion(matchX);
var versionY = ExtractVersion(matchY);
return versionX.CompareTo(versionY);
}
private Version ExtractVersion(Match match)
{
var major = int.Parse(match.Groups["major"].Value);
if (!Enum.TryParse<Stream>(match.Groups["stream"].Value, true, out var stream))
{
stream = Stream.Final;
}
_ = int.TryParse(match.Groups["minor"].Value, out var minor);
return new Version(major, (int)stream, minor);
}
private enum Stream
{
Alpha = 1,
Beta = 2,
Final = 3,
}
}

View File

@@ -1,176 +0,0 @@
// WARNING: DO NOT LEAVE COMMENTED CODE IN THIS FILE. IT GETS SCANNED BY GEN PROJECT SO IT CAN EXCLUDE ANY MANUALLY DEFINED MAPS
using AutoMapper;
#if NET6_0_OR_GREATER
using AutoMapper.Internal;
#endif
using k8s.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace k8s.ModelConverter.AutoMapper;
/// <summary>
/// Provides mappers that converts Kubernetes models between different versions
/// </summary>
internal static partial class VersionConverter
{
static VersionConverter()
{
UpdateMappingConfiguration(expression => { });
}
public static IMapper Mapper { get; private set; }
internal static MapperConfiguration MapperConfiguration { get; private set; }
/// <summary>
/// Two level lookup of model types by Kind and then Version
/// </summary>
internal static Dictionary<string, Dictionary<string, Type>> KindVersionsMap { get; private set; }
public static Type GetTypeForVersion<T>(string version)
{
return GetTypeForVersion(typeof(T), version);
}
public static Type GetTypeForVersion(Type type, string version)
{
return KindVersionsMap[type.GetKubernetesTypeMetadata().Kind][version];
}
public static void UpdateMappingConfiguration(Action<IMapperConfigurationExpression> configuration)
{
MapperConfiguration = new MapperConfiguration(cfg =>
{
GetConfigurations(cfg);
configuration(cfg);
});
Mapper = MapperConfiguration
#if NET6_0_OR_GREATER
.Internal()
#endif
.CreateMapper();
KindVersionsMap = MapperConfiguration
#if NET6_0_OR_GREATER
.Internal()
#endif
.GetAllTypeMaps()
.SelectMany(x => new[] { x.Types.SourceType, x.Types.DestinationType })
.Where(x => x.GetCustomAttribute<KubernetesEntityAttribute>() != null)
.Select(x =>
{
var attr = GetKubernetesEntityAttribute(x);
return new { attr.Kind, attr.ApiVersion, Type = x };
})
.GroupBy(x => x.Kind)
.ToDictionary(x => x.Key, kindGroup => kindGroup
.GroupBy(x => x.ApiVersion)
.ToDictionary(
x => x.Key,
versionGroup => versionGroup.Select(x => x.Type).Distinct().Single())); // should only be one type for each Kind/Version combination
}
public static object ConvertToVersion(object source, string apiVersion)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
var type = source.GetType();
var attr = GetKubernetesEntityAttribute(type);
if (attr.ApiVersion == apiVersion)
{
return source;
}
if (!KindVersionsMap.TryGetValue(attr.Kind, out var kindVersions))
{
throw new InvalidOperationException($"Version converter does not have any registered types for Kind `{attr.Kind}`");
}
if (!kindVersions.TryGetValue(apiVersion, out var targetType) || !kindVersions.TryGetValue(attr.ApiVersion, out var sourceType) ||
MapperConfiguration
#if NET6_0_OR_GREATER
.Internal()
#endif
.FindTypeMapFor(sourceType, targetType) == null)
{
throw new InvalidOperationException($"There is no conversion mapping registered for Kind `{attr.Kind}` from ApiVersion {attr.ApiVersion} to {apiVersion}");
}
return Mapper.Map(source, sourceType, targetType);
}
private static KubernetesEntityAttribute GetKubernetesEntityAttribute(Type type)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
var attr = type.GetCustomAttribute<KubernetesEntityAttribute>();
if (attr == null)
{
throw new InvalidOperationException($"Type {type} does not have {nameof(KubernetesEntityAttribute)}");
}
return attr;
}
internal static void GetConfigurations(IMapperConfigurationExpression cfg)
{
AutoConfigurations(cfg);
ManualConfigurations(cfg);
}
private static void ManualConfigurations(IMapperConfigurationExpression cfg)
{
cfg.AllowNullCollections = true;
cfg.DisableConstructorMapping();
cfg
#if NET6_0_OR_GREATER
.Internal()
#endif
.ForAllMaps((typeMap, opt) =>
{
if (!typeof(IKubernetesObject).IsAssignableFrom(typeMap.Types.DestinationType))
{
return;
}
var metadata = typeMap.Types.DestinationType.GetKubernetesTypeMetadata();
opt.ForMember(nameof(IKubernetesObject.ApiVersion), x => x.Ignore());
opt.ForMember(nameof(IKubernetesObject.Kind), x => x.Ignore());
opt.AfterMap((from, to) =>
{
var obj = (IKubernetesObject)to;
obj.ApiVersion = !string.IsNullOrEmpty(metadata.Group) ? $"{metadata.Group}/{metadata.ApiVersion}" : metadata.ApiVersion;
obj.Kind = metadata.Kind;
});
});
cfg.CreateMap<V1HorizontalPodAutoscalerSpec, V2HorizontalPodAutoscalerSpec>()
.ForMember(dest => dest.Metrics, opt => opt.Ignore())
.ForMember(dest => dest.Behavior, opt => opt.Ignore())
.ReverseMap();
cfg.CreateMap<V1HorizontalPodAutoscalerStatus, V2HorizontalPodAutoscalerStatus>()
.ForMember(dest => dest.Conditions, opt => opt.Ignore())
.ForMember(dest => dest.CurrentMetrics, opt => opt.Ignore())
.ReverseMap();
cfg.CreateMap<V1alpha3ResourceClaim, V1ResourceClaim>()
.ForMember(dest => dest.Name, opt => opt.Ignore())
.ForMember(dest => dest.Request, opt => opt.Ignore())
.ReverseMap();
cfg.CreateMap<V1beta1ResourceClaim, V1ResourceClaim>()
.ForMember(dest => dest.Name, opt => opt.Ignore())
.ForMember(dest => dest.Request, opt => opt.Ignore())
.ReverseMap();
}
}

View File

@@ -1,20 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
<RootNamespace>k8s.ModelConverter</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\LibKubernetesGenerator\generators\LibKubernetesGenerator.Automapper\LibKubernetesGenerator.Automapper.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\KubernetesClient\KubernetesClient.csproj" />
<PackageReference Include="AutoMapper" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="KubernetesClient.Tests" />
</ItemGroup>
</Project>

View File

@@ -1,12 +1,6 @@
using Autofac;
using Microsoft.CodeAnalysis;
using NSwag;
#if GENERATE_AUTOMAPPER
using System.Collections.Generic;
using System;
using System.IO;
using System.Linq;
#endif
namespace LibKubernetesGenerator
{
@@ -68,7 +62,6 @@ namespace LibKubernetesGenerator
builder.RegisterType<ModelGenerator>();
builder.RegisterType<ApiGenerator>();
builder.RegisterType<VersionConverterStubGenerator>();
builder.RegisterType<VersionConverterAutoMapperGenerator>();
builder.RegisterType<VersionGenerator>();
return builder.Build();
@@ -90,33 +83,6 @@ namespace LibKubernetesGenerator
});
#endif
#if GENERATE_AUTOMAPPER
var automappersrc = generatorContext.CompilationProvider.Select((c, _) => c.SyntaxTrees.First(s => PathSuffixMath(s.FilePath, "AutoMapper/VersionConverter.cs")));
generatorContext.RegisterSourceOutput(automappersrc, (ctx, srctree) =>
{
var (swagger, container) = BuildContainer();
container.Resolve<VersionConverterAutoMapperGenerator>().Generate(swagger, ctx, srctree);
});
#endif
}
#if GENERATE_AUTOMAPPER
private IEnumerable<string> PathSplit(string path)
{
var p = path;
while (!string.IsNullOrEmpty(p))
{
yield return Path.GetFileName(p);
p = Path.GetDirectoryName(p);
}
}
private bool PathSuffixMath(string path, string suffix)
{
var s = PathSplit(suffix).ToList();
return PathSplit(path).Take(s.Count).SequenceEqual(s);
}
#endif
}
}

View File

@@ -12,7 +12,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Common" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
@@ -30,8 +30,10 @@
<!-- NSwag -->
<PackageReference Include="NSwag.Core" GeneratePathProperty="true" PrivateAssets="all" />
<PackageReference Include="NJsonSchema" GeneratePathProperty="true" PrivateAssets="all" />
<PackageReference Include="NJsonSchema.Annotations" GeneratePathProperty="true" PrivateAssets="all" />
<PackageReference Include="Newtonsoft.Json" GeneratePathProperty="true" PrivateAssets="all" />
<PackageReference Include="Namotion.Reflection" GeneratePathProperty="true" PrivateAssets="all" />
<PackageReference Include="System.Text.Json" GeneratePathProperty="true" PrivateAssets="all" />
</ItemGroup>
<PropertyGroup>
@@ -48,8 +50,10 @@
<TargetPathWithTargetPlatformMoniker Include="$(PKGNSwag_Core)\lib\netstandard2.0\NSwag.Core.dll" IncludeRuntimeDependency="false" />
<TargetPathWithTargetPlatformMoniker Include="$(PKGNJsonSchema)\lib\netstandard2.0\NJsonSchema.dll" IncludeRuntimeDependency="false" />
<TargetPathWithTargetPlatformMoniker Include="$(PKGNJsonSchema_Annotations)\lib\netstandard2.0\NJsonSchema.Annotations.dll" IncludeRuntimeDependency="false" />
<TargetPathWithTargetPlatformMoniker Include="$(PKGNewtonsoft_Json)\lib\netstandard1.0\Newtonsoft.Json.dll" IncludeRuntimeDependency="false" />
<TargetPathWithTargetPlatformMoniker Include="$(PKGNamotion_Reflection)\lib\netstandard2.0\Namotion.Reflection.dll" IncludeRuntimeDependency="false" />
<TargetPathWithTargetPlatformMoniker Include="$(PKGSystem_Text_Json)\lib\netstandard2.0\System.Text.Json.dll" IncludeRuntimeDependency="false" />
</ItemGroup>
</Target>

View File

@@ -1,82 +0,0 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using NSwag;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace LibKubernetesGenerator
{
internal class VersionConverterAutoMapperGenerator
{
private readonly ClassNameHelper classNameHelper;
public VersionConverterAutoMapperGenerator(ClassNameHelper classNameHelper)
{
this.classNameHelper = classNameHelper;
}
public void Generate(OpenApiDocument swagger, SourceProductionContext context, SyntaxTree manualconverter)
{
var allGeneratedModelClassNames = new List<string>();
foreach (var kv in swagger.Definitions)
{
var def = kv.Value;
var clz = classNameHelper.GetClassNameForSchemaDefinition(def);
allGeneratedModelClassNames.Add(clz);
}
var manualMaps = new List<(string, string)>();
manualMaps = Regex.Matches(manualconverter.GetText().ToString(), @"\.CreateMap<(?<T1>.+?),\s?(?<T2>.+?)>")
.OfType<Match>()
.Select(x => (x.Groups["T1"].Value, x.Groups["T2"].Value))
.ToList();
var versionRegex = @"(^V|v)[0-9]+((alpha|beta)[0-9]+)?";
var typePairs = allGeneratedModelClassNames
.OrderBy(x => x)
.Select(x => new
{
Version = Regex.Match(x, versionRegex).Value?.ToLower(),
Kinda = Regex.Replace(x, versionRegex, string.Empty),
Type = x,
})
.Where(x => !string.IsNullOrEmpty(x.Version))
.GroupBy(x => x.Kinda)
.Where(x => x.Count() > 1)
.SelectMany(x =>
x.SelectMany((value, index) => x.Skip(index + 1), (first, second) => new { first, second }))
.OrderBy(x => x.first.Kinda)
.ThenBy(x => x.first.Version)
.Select(x => (x.first.Type, x.second.Type))
.ToList();
var versionConverterPairs = typePairs.Except(manualMaps).ToList();
var sbversion = new StringBuilder(@"// <auto-generated />
using AutoMapper;
using k8s.Models;
namespace k8s.ModelConverter.AutoMapper;
internal static partial class VersionConverter
{
private static void AutoConfigurations(IMapperConfigurationExpression cfg)
{
");
foreach (var (t0, t1) in versionConverterPairs)
{
sbversion.AppendLine($@"cfg.CreateMap<{t0}, {t1}>().ReverseMap();");
}
sbversion.AppendLine("}}");
context.AddSource($"VersionConverter.g.cs", SourceText.From(sbversion.ToString(), Encoding.UTF8));
}
}
}

View File

@@ -1,8 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<DefineConstants>$(DefineConstants);GENERATE_AUTOMAPPER;</DefineConstants>
</PropertyGroup>
<Import Project="../../LibKubernetesGenerator.target" />
</Project>

View File

@@ -3,6 +3,5 @@
<ProjectReference Include="KubernetesClient/KubernetesClient.csproj" />
<ProjectReference Include="KubernetesClient.Classic/KubernetesClient.Classic.csproj" />
<ProjectReference Include="KubernetesClient.Aot/KubernetesClient.Aot.csproj" />
<ProjectReference Include="KubernetesClient.ModelConverter/KubernetesClient.ModelConverter.csproj" />
</ItemGroup>
</Project>

10731
swagger.json

File diff suppressed because it is too large Load Diff

View File

@@ -26,6 +26,10 @@
<PackageReference Include="Xunit.StaFact" />
<PackageReference Include="Moq" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Net.Http" Condition="'$(TargetFramework)' == 'net48'" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\KubernetesClient.Classic\KubernetesClient.Classic.csproj" />

View File

@@ -1,47 +0,0 @@
using AutoMapper;
using FluentAssertions;
using k8s.ModelConverter.AutoMapper;
using k8s.Models;
using Xunit;
namespace k8s.Tests
{
public class AutoMapperVersionConverterTests
{
[Fact]
public void ConfigurationsAreValid()
{
var config = new MapperConfiguration(VersionConverter.GetConfigurations);
config.AssertConfigurationIsValid();
}
[Theory]
[InlineData("v1", "v1beta1", 1)]
[InlineData("v1beta1", "v1", -1)]
[InlineData("v1beta1", "v1alpha1", 1)]
[InlineData("v1alpha1", "v1beta1", -1)]
[InlineData("v1", "v1alpha1", 1)]
[InlineData("v2alpha1", "v1", 1)]
[InlineData("v1", "v2alpha1", -1)]
[InlineData("v1", "v1", 0)]
[InlineData("v2", "v2", 0)]
[InlineData("v1beta1", "v1beta1", 0)]
[InlineData("v1beta2", "v1beta2", 0)]
[InlineData("v2beta2", "v2beta2", 0)]
public void KubernetesVersionCompare(string x, string y, int expected)
{
KubernetesVersionComparer.Instance.Compare(x, y).Should().Be(expected);
}
[Fact]
public void ObjectMapAreValid()
{
ModelVersionConverter.Converter = AutoMapperModelVersionConverter.Instance;
var from = new V2HorizontalPodAutoscalerSpec(); // TODO shuold auto load all objects
from.MaxReplicas = 234;
var to = (V1HorizontalPodAutoscalerSpec)from;
Assert.Equal(from.MaxReplicas, to.MaxReplicas);
}
}
}

View File

@@ -33,6 +33,5 @@
<ItemGroup>
<ProjectReference Include="..\..\src\KubernetesClient\KubernetesClient.csproj" />
<ProjectReference Include="..\..\src\KubernetesClient.ModelConverter\KubernetesClient.ModelConverter.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,6 +1,6 @@
{
"$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
"version": "16.0",
"version": "17.0",
"publicReleaseRefSpec": [
"^refs/heads/master$",
"^refs/tags/v\\d+\\.\\d+\\.\\d+"