Compare commits
10 Commits
d693a621dd
...
6b1da78877
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b1da78877 | ||
|
|
70b6386441 | ||
|
|
292262755a | ||
|
|
6d27bd900b | ||
|
|
ca5d9f4793 | ||
|
|
5de1c25cf1 | ||
|
|
96955064cb | ||
|
|
11a9641fbe | ||
|
|
a99fbe4693 | ||
|
|
885b9ae09d |
14
.github/workflows/buildtest.yaml
vendored
14
.github/workflows/buildtest.yaml
vendored
@@ -12,17 +12,13 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Setup dotnet
|
- name: Setup dotnet
|
||||||
uses: actions/setup-dotnet@v4
|
uses: actions/setup-dotnet@v5
|
||||||
with:
|
with:
|
||||||
dotnet-version: |
|
dotnet-version: |
|
||||||
8.0.x
|
8.0.x
|
||||||
9.0.x
|
9.0.x
|
||||||
# - name: Check Format
|
|
||||||
# # don't check formatting on Windows b/c of CRLF issues.
|
|
||||||
# if: matrix.os == 'ubuntu-latest'
|
|
||||||
# run: dotnet format --severity error --verify-no-changes --exclude ./src/KubernetesClient/generated/
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: dotnet build --configuration Release -v detailed
|
run: dotnet build --configuration Release
|
||||||
- name: Test
|
- name: Test
|
||||||
run: dotnet test --configuration Release --collect:"Code Coverage;Format=Cobertura" --logger trx --results-directory TestResults --settings CodeCoverage.runsettings --no-build
|
run: dotnet test --configuration Release --collect:"Code Coverage;Format=Cobertura" --logger trx --results-directory TestResults --settings CodeCoverage.runsettings --no-build
|
||||||
- name: Upload coverage to Codecov
|
- name: Upload coverage to Codecov
|
||||||
@@ -31,7 +27,7 @@ jobs:
|
|||||||
directory: ./TestResults
|
directory: ./TestResults
|
||||||
files: '*.cobertura.xml'
|
files: '*.cobertura.xml'
|
||||||
- name: Upload test results
|
- name: Upload test results
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v5
|
||||||
with:
|
with:
|
||||||
name: test-results-${{ matrix.os }}
|
name: test-results-${{ matrix.os }}
|
||||||
path: ./TestResults
|
path: ./TestResults
|
||||||
@@ -48,7 +44,7 @@ jobs:
|
|||||||
- name: Add msbuild to PATH
|
- name: Add msbuild to PATH
|
||||||
uses: microsoft/setup-msbuild@v2
|
uses: microsoft/setup-msbuild@v2
|
||||||
- name: Setup dotnet SDK
|
- name: Setup dotnet SDK
|
||||||
uses: actions/setup-dotnet@v4
|
uses: actions/setup-dotnet@v5
|
||||||
with:
|
with:
|
||||||
dotnet-version: '9.0.x'
|
dotnet-version: '9.0.x'
|
||||||
- name: Restore nugets (msbuild)
|
- name: Restore nugets (msbuild)
|
||||||
@@ -63,7 +59,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Setup dotnet
|
- name: Setup dotnet
|
||||||
uses: actions/setup-dotnet@v4
|
uses: actions/setup-dotnet@v5
|
||||||
with:
|
with:
|
||||||
dotnet-version: |
|
dotnet-version: |
|
||||||
8.0.x
|
8.0.x
|
||||||
|
|||||||
6
.github/workflows/codeql-analysis.yml
vendored
6
.github/workflows/codeql-analysis.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Setup dotnet
|
- name: Setup dotnet
|
||||||
uses: actions/setup-dotnet@v4
|
uses: actions/setup-dotnet@v5
|
||||||
with:
|
with:
|
||||||
dotnet-version: |
|
dotnet-version: |
|
||||||
8.0.x
|
8.0.x
|
||||||
@@ -39,7 +39,7 @@ jobs:
|
|||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v3
|
uses: github/codeql-action/init@v4
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
@@ -59,4 +59,4 @@ jobs:
|
|||||||
run: dotnet build --configuration Debug --no-restore
|
run: dotnet build --configuration Debug --no-restore
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v3
|
uses: github/codeql-action/analyze@v4
|
||||||
|
|||||||
2
.github/workflows/docfx.yaml
vendored
2
.github/workflows/docfx.yaml
vendored
@@ -30,7 +30,7 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Setup dotnet
|
- name: Setup dotnet
|
||||||
uses: actions/setup-dotnet@v4
|
uses: actions/setup-dotnet@v5
|
||||||
with:
|
with:
|
||||||
dotnet-version: |
|
dotnet-version: |
|
||||||
8.0.x
|
8.0.x
|
||||||
|
|||||||
2
.github/workflows/draft.yaml
vendored
2
.github/workflows/draft.yaml
vendored
@@ -18,7 +18,7 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Setup dotnet
|
- name: Setup dotnet
|
||||||
uses: actions/setup-dotnet@v4
|
uses: actions/setup-dotnet@v5
|
||||||
with:
|
with:
|
||||||
dotnet-version: |
|
dotnet-version: |
|
||||||
8.0.x
|
8.0.x
|
||||||
|
|||||||
2
.github/workflows/nuget.yaml
vendored
2
.github/workflows/nuget.yaml
vendored
@@ -15,7 +15,7 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Setup dotnet
|
- name: Setup dotnet
|
||||||
uses: actions/setup-dotnet@v4
|
uses: actions/setup-dotnet@v5
|
||||||
with:
|
with:
|
||||||
dotnet-version: |
|
dotnet-version: |
|
||||||
8.0.x
|
8.0.x
|
||||||
|
|||||||
@@ -3,51 +3,52 @@
|
|||||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageVersion Include="BouncyCastle.Cryptography" Version="2.5.1" />
|
<PackageVersion Include="BouncyCastle.Cryptography" Version="2.6.2" />
|
||||||
<PackageVersion Include="FluentAssertions" Version="8.2.0" />
|
<PackageVersion Include="FluentAssertions" Version="8.6.0" />
|
||||||
<PackageVersion Include="Fractions" Version="7.3.0" />
|
<PackageVersion Include="Fractions" Version="7.3.0" />
|
||||||
<PackageVersion Include="JsonPatch.Net" Version="3.3.0" />
|
<PackageVersion Include="JsonPatch.Net" Version="3.3.0" />
|
||||||
<PackageVersion Include="MartinCostello.Logging.XUnit" Version="0.5.1" />
|
<PackageVersion Include="MartinCostello.Logging.XUnit" Version="0.6.0" />
|
||||||
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.4" />
|
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.8" />
|
||||||
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.4" />
|
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.8" />
|
||||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.4" />
|
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.8" />
|
||||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
|
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||||
<PackageVersion Include="Microsoft.TestPlatform.ObjectModel" Version="17.13.0" />
|
<PackageVersion Include="Microsoft.TestPlatform.ObjectModel" Version="17.14.1" />
|
||||||
<PackageVersion Include="Moq" Version="4.20.72" />
|
<PackageVersion Include="Moq" Version="4.20.72" />
|
||||||
<PackageVersion Include="Nito.AsyncEx" Version="5.1.2" />
|
<PackageVersion Include="Nito.AsyncEx" Version="5.1.2" />
|
||||||
<PackageVersion Include="Nito.AsyncEx.Coordination" 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.Exporter.Console" Version="1.12.0" />
|
||||||
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.11.1" />
|
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.12.0" />
|
||||||
<PackageVersion Include="Portable.BouncyCastle" Version="1.9.0" />
|
<PackageVersion Include="Portable.BouncyCastle" Version="1.9.0" />
|
||||||
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
||||||
<PackageVersion Include="Swashbuckle.AspNetCore" Version="8.1.1" />
|
<PackageVersion Include="Swashbuckle.AspNetCore" Version="9.0.4" />
|
||||||
<PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="9.0.4" />
|
<PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="9.0.8" />
|
||||||
<PackageVersion Include="System.IO.Abstractions.TestingHelpers" Version="22.0.14" />
|
<PackageVersion Include="System.IO.Abstractions.TestingHelpers" Version="22.0.15" />
|
||||||
<PackageVersion Include="System.Reactive" Version="6.0.1" />
|
<PackageVersion Include="System.Reactive" Version="6.0.2" />
|
||||||
<PackageVersion Include="System.Text.Json" Version="9.0.4" />
|
<PackageVersion Include="System.Text.Json" Version="9.0.8" />
|
||||||
<PackageVersion Include="Vecc.YamlDotNet.Analyzers.StaticGenerator" Version="16.3.0" />
|
<PackageVersion Include="Vecc.YamlDotNet.Analyzers.StaticGenerator" Version="16.3.0" />
|
||||||
<PackageVersion Include="Wiremock.Net" Version="1.7.4" />
|
<PackageVersion Include="Wiremock.Net" Version="1.12.0" />
|
||||||
<PackageVersion Include="xunit" Version="2.9.3" />
|
<PackageVersion Include="xunit" Version="2.9.3" />
|
||||||
<PackageVersion Include="xunit.runner.visualstudio" Version="3.0.2" />
|
<PackageVersion Include="xunit.runner.visualstudio" Version="3.0.2" />
|
||||||
<PackageVersion Include="Xunit.StaFact" Version="1.2.69" />
|
<PackageVersion Include="Xunit.StaFact" Version="1.2.69" />
|
||||||
<PackageVersion Include="YamlDotNet" Version="16.3.0" />
|
<PackageVersion Include="YamlDotNet" Version="16.3.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageVersion Include="Autofac" Version="8.2.1" />
|
<PackageVersion Include="Autofac" Version="8.4.0" />
|
||||||
<PackageVersion Include="CaseExtensions" Version="1.1.0" />
|
<PackageVersion Include="CaseExtensions" Version="1.1.0" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.13.0" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.14.0" />
|
||||||
<PackageVersion Include="Namotion.Reflection" Version="3.3.0" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" />
|
||||||
|
<PackageVersion Include="Namotion.Reflection" Version="3.4.2" />
|
||||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
<PackageVersion Include="NJsonSchema" Version="11.2.0" />
|
<PackageVersion Include="NJsonSchema" Version="11.4.0" />
|
||||||
<PackageVersion Include="NJsonSchema.Annotations" Version="11.2.0" />
|
<PackageVersion Include="NJsonSchema.Annotations" Version="11.4.0" />
|
||||||
<PackageVersion Include="NSwag.Core" Version="14.3.0" />
|
<PackageVersion Include="NSwag.Core" Version="14.5.0" />
|
||||||
<PackageVersion Include="Scriban" Version="6.2.1" />
|
<PackageVersion Include="Scriban" Version="6.2.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<GlobalPackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0" />
|
<GlobalPackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0" />
|
||||||
<GlobalPackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
|
<GlobalPackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
|
||||||
<GlobalPackageReference Include="Microsoft.VisualStudio.SlnGen" Version="12.0.3" />
|
<GlobalPackageReference Include="Microsoft.VisualStudio.SlnGen" Version="12.0.23" />
|
||||||
<GlobalPackageReference Include="Nerdbank.GitVersioning" Version="3.7.112" />
|
<GlobalPackageReference Include="Nerdbank.GitVersioning" Version="3.7.115" />
|
||||||
<GlobalPackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
|
<GlobalPackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -154,6 +154,7 @@ ${GEN_DIR}/openapi/csharp.sh ${REPO_DIR}/src/KubernetesClient ${REPO_DIR}/csharp
|
|||||||
|
|
||||||
| SDK Version | Kubernetes Version | .NET Targeting |
|
| SDK Version | Kubernetes Version | .NET Targeting |
|
||||||
|-------------|--------------------|-----------------------------------------------------|
|
|-------------|--------------------|-----------------------------------------------------|
|
||||||
|
| 18.0 | 1.34 | net8.0;net9.0;net48*;netstandard2.0* |
|
||||||
| 17.0 | 1.33 | net8.0;net9.0;net48*;netstandard2.0* |
|
| 17.0 | 1.33 | net8.0;net9.0;net48*;netstandard2.0* |
|
||||||
| 16.0 | 1.32 | 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* |
|
| 15.0 | 1.31 | net6.0;net8.0;net48*;netstandard2.0* |
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
export KUBERNETES_BRANCH=v1.33.0
|
export KUBERNETES_BRANCH=v1.34.0
|
||||||
export CLIENT_VERSION=0.0.1
|
export CLIENT_VERSION=0.0.1
|
||||||
export PACKAGE_NAME=k8s
|
export PACKAGE_NAME=k8s
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// See https://aka.ms/new-console-template for more information
|
using k8s;
|
||||||
using k8s;
|
using k8s.Models;
|
||||||
using k8s.ClientSets;
|
using k8s.ClientSets;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@@ -12,15 +12,21 @@ namespace clientset
|
|||||||
var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
|
var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
|
||||||
var client = new Kubernetes(config);
|
var client = new Kubernetes(config);
|
||||||
|
|
||||||
ClientSet clientSet = new ClientSet(client);
|
var clientSet = new ClientSet(client);
|
||||||
var list = await clientSet.CoreV1.Pod.ListAsync("default").ConfigureAwait(false);
|
var list = await clientSet.CoreV1.Pod.ListAsync("default").ConfigureAwait(false);
|
||||||
foreach (var item in list)
|
foreach (var item in list)
|
||||||
{
|
{
|
||||||
System.Console.WriteLine(item.Metadata.Name);
|
System.Console.WriteLine(item.Metadata.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
var pod = await clientSet.CoreV1.Pod.GetAsync("test","default").ConfigureAwait(false);
|
var pod = await clientSet.CoreV1.Pod.GetAsync("test", "default").ConfigureAwait(false);
|
||||||
System.Console.WriteLine(pod?.Metadata?.Name);
|
System.Console.WriteLine(pod?.Metadata?.Name);
|
||||||
|
|
||||||
|
var watch = clientSet.CoreV1.Pod.WatchListAsync("default");
|
||||||
|
await foreach (var (_, item) in watch.ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
System.Console.WriteLine(item.Metadata.Name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,7 +67,15 @@ var old = JsonSerializer.SerializeToDocument(readCert, serializeOptions);
|
|||||||
|
|
||||||
var replace = new List<V1CertificateSigningRequestCondition>
|
var replace = new List<V1CertificateSigningRequestCondition>
|
||||||
{
|
{
|
||||||
new ("True", "Approved", DateTime.UtcNow, DateTime.UtcNow, "This certificate was approved by k8s client", "Approve"),
|
new V1CertificateSigningRequestCondition
|
||||||
|
{
|
||||||
|
Type = "Approved",
|
||||||
|
Status = "True",
|
||||||
|
Reason = "Approve",
|
||||||
|
Message = "This certificate was approved by k8s client",
|
||||||
|
LastUpdateTime = DateTime.UtcNow,
|
||||||
|
LastTransitionTime = DateTime.UtcNow,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
readCert.Status.Conditions = replace;
|
readCert.Status.Conditions = replace;
|
||||||
|
|
||||||
|
|||||||
@@ -19,13 +19,13 @@ namespace customResource
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CResourceSpec
|
public record CResourceSpec
|
||||||
{
|
{
|
||||||
[JsonPropertyName("cityName")]
|
[JsonPropertyName("cityName")]
|
||||||
public string CityName { get; set; }
|
public string CityName { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CResourceStatus : V1Status
|
public record CResourceStatus : V1Status
|
||||||
{
|
{
|
||||||
[JsonPropertyName("temperature")]
|
[JsonPropertyName("temperature")]
|
||||||
public string Temperature { get; set; }
|
public string Temperature { get; set; }
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ var pod = new V1Pod
|
|||||||
{
|
{
|
||||||
Requests = new Dictionary<string, ResourceQuantity>()
|
Requests = new Dictionary<string, ResourceQuantity>()
|
||||||
{
|
{
|
||||||
["cpu"] = new ResourceQuantity("100m"),
|
["cpu"] = "100m",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using k8s;
|
using k8s;
|
||||||
using k8s.Models;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -8,9 +7,10 @@ var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
|
|||||||
|
|
||||||
IKubernetes client = new Kubernetes(config);
|
IKubernetes client = new Kubernetes(config);
|
||||||
|
|
||||||
var podlistResp = client.CoreV1.ListNamespacedPodWithHttpMessagesAsync("default", watch: true);
|
var podlistResp = client.CoreV1.WatchListNamespacedPodAsync("default");
|
||||||
|
|
||||||
// C# 8 required https://docs.microsoft.com/en-us/archive/msdn-magazine/2019/november/csharp-iterating-with-async-enumerables-in-csharp-8
|
// 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>().ConfigureAwait(false))
|
await foreach (var (type, item) in podlistResp.ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
Console.WriteLine("==on watch event==");
|
Console.WriteLine("==on watch event==");
|
||||||
Console.WriteLine(type);
|
Console.WriteLine(type);
|
||||||
@@ -22,8 +22,7 @@ await foreach (var (type, item) in podlistResp.WatchAsync<V1Pod, V1PodList>().Co
|
|||||||
void WatchUsingCallback(IKubernetes client)
|
void WatchUsingCallback(IKubernetes client)
|
||||||
#pragma warning restore CS8321 // Remove unused private members
|
#pragma warning restore CS8321 // Remove unused private members
|
||||||
{
|
{
|
||||||
var podlistResp = client.CoreV1.ListNamespacedPodWithHttpMessagesAsync("default", watch: true);
|
using (var podlistResp = client.CoreV1.WatchListNamespacedPod("default", onEvent: (type, item) =>
|
||||||
using (podlistResp.Watch<V1Pod, V1PodList>((type, item) =>
|
|
||||||
{
|
{
|
||||||
Console.WriteLine("==on watch event==");
|
Console.WriteLine("==on watch event==");
|
||||||
Console.WriteLine(type);
|
Console.WriteLine(type);
|
||||||
@@ -37,4 +36,4 @@ void WatchUsingCallback(IKubernetes client)
|
|||||||
Console.CancelKeyPress += (sender, eventArgs) => ctrlc.Set();
|
Console.CancelKeyPress += (sender, eventArgs) => ctrlc.Set();
|
||||||
ctrlc.Wait();
|
ctrlc.Wait();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
|
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
|
||||||
@@ -20,20 +20,18 @@
|
|||||||
<Compile Include="..\KubernetesClient\Extensions.cs" />
|
<Compile Include="..\KubernetesClient\Extensions.cs" />
|
||||||
<Compile Include="..\KubernetesClient\FloatEmitter.cs" />
|
<Compile Include="..\KubernetesClient\FloatEmitter.cs" />
|
||||||
<Compile Include="..\KubernetesClient\Models\GeneratedModelVersion.cs" />
|
<Compile Include="..\KubernetesClient\Models\GeneratedModelVersion.cs" />
|
||||||
<Compile Include="..\KubernetesClient\IItems.cs" />
|
<Compile Include="..\KubernetesClient\Models\IItems.cs" />
|
||||||
<Compile Include="..\KubernetesClient\IKubernetesObject.cs" />
|
<Compile Include="..\KubernetesClient\IKubernetesObject.cs" />
|
||||||
<Compile Include="..\KubernetesClient\IMetadata.cs" />
|
<Compile Include="..\KubernetesClient\Models\IMetadata.cs" />
|
||||||
<Compile Include="..\KubernetesClient\Models\IntOrStringJsonConverter.cs" />
|
<Compile Include="..\KubernetesClient\Models\IntOrStringJsonConverter.cs" />
|
||||||
<Compile Include="..\KubernetesClient\Models\IntOrStringYamlConverter.cs" />
|
<Compile Include="..\KubernetesClient\Models\IntOrStringYamlConverter.cs" />
|
||||||
<Compile Include="..\KubernetesClient\Models\IntstrIntOrString.cs" />
|
<Compile Include="..\KubernetesClient\Models\IntOrString.cs" />
|
||||||
<Compile Include="..\KubernetesClient\ISpec.cs" />
|
<Compile Include="..\KubernetesClient\Models\ISpec.cs" />
|
||||||
<Compile Include="..\KubernetesClient\IStatus.cs" />
|
<Compile Include="..\KubernetesClient\Models\IStatus.cs" />
|
||||||
<Compile Include="..\KubernetesClient\IValidate.cs" />
|
|
||||||
<Compile Include="..\KubernetesClient\Models\KubernetesEntityAttribute.cs" />
|
<Compile Include="..\KubernetesClient\Models\KubernetesEntityAttribute.cs" />
|
||||||
<Compile Include="..\KubernetesClient\Models\KubernetesList.cs" />
|
<Compile Include="..\KubernetesClient\Models\KubernetesList.cs" />
|
||||||
<Compile Include="..\KubernetesClient\KubernetesObject.cs" />
|
<Compile Include="..\KubernetesClient\KubernetesObject.cs" />
|
||||||
<Compile Include="..\KubernetesClient\Models\ModelExtensions.cs" />
|
<Compile Include="..\KubernetesClient\Models\ModelExtensions.cs" />
|
||||||
<Compile Include="..\KubernetesClient\Models\ModelVersionConverter.cs" />
|
|
||||||
<Compile Include="..\KubernetesClient\Models\NodeMetrics.cs" />
|
<Compile Include="..\KubernetesClient\Models\NodeMetrics.cs" />
|
||||||
<Compile Include="..\KubernetesClient\Models\NodeMetricsList.cs" />
|
<Compile Include="..\KubernetesClient\Models\NodeMetricsList.cs" />
|
||||||
<Compile Include="..\KubernetesClient\Models\PodMetrics.cs" />
|
<Compile Include="..\KubernetesClient\Models\PodMetrics.cs" />
|
||||||
@@ -45,7 +43,11 @@
|
|||||||
<Compile Include="..\KubernetesClient\Models\V1Patch.cs" />
|
<Compile Include="..\KubernetesClient\Models\V1Patch.cs" />
|
||||||
<Compile Include="..\KubernetesClient\Models\V1PodTemplateSpec.cs" />
|
<Compile Include="..\KubernetesClient\Models\V1PodTemplateSpec.cs" />
|
||||||
<Compile Include="..\KubernetesClient\Models\V1Status.cs" />
|
<Compile Include="..\KubernetesClient\Models\V1Status.cs" />
|
||||||
|
<Compile Include="..\KubernetesClient\KubernetesJson.cs" />
|
||||||
|
<Compile Include="..\KubernetesClient\SourceGenerationContext.cs" />
|
||||||
|
<Compile Include="..\KubernetesClient\Models\V1Status.ObjectView.cs" />
|
||||||
|
<Compile Include="..\KubernetesClient\Models\KubernetesDateTimeOffsetYamlConverter.cs" />
|
||||||
|
<Compile Include="..\KubernetesClient\Models\KubernetesDateTimeYamlConverter.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="..\KubernetesClient\ClientSets\ClientSet.cs" />
|
<Compile Include="..\KubernetesClient\ClientSets\ClientSet.cs" />
|
||||||
|
|||||||
@@ -1,103 +0,0 @@
|
|||||||
using System.Globalization;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Xml;
|
|
||||||
|
|
||||||
namespace k8s
|
|
||||||
{
|
|
||||||
internal static class KubernetesJson
|
|
||||||
{
|
|
||||||
internal sealed class Iso8601TimeSpanConverter : JsonConverter<TimeSpan>
|
|
||||||
{
|
|
||||||
public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
|
||||||
{
|
|
||||||
var str = reader.GetString();
|
|
||||||
return XmlConvert.ToTimeSpan(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options)
|
|
||||||
{
|
|
||||||
var iso8601TimeSpanString = XmlConvert.ToString(value); // XmlConvert for TimeSpan uses ISO8601, so delegate serialization to it
|
|
||||||
writer.WriteStringValue(iso8601TimeSpanString);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal sealed class KubernetesDateTimeOffsetConverter : JsonConverter<DateTimeOffset>
|
|
||||||
{
|
|
||||||
private const string RFC3339MicroFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.ffffffK";
|
|
||||||
private const string RFC3339NanoFormat = "yyyy-MM-dd'T'HH':'mm':'ss.fffffffK";
|
|
||||||
private const string RFC3339Format = "yyyy'-'MM'-'dd'T'HH':'mm':'ssK";
|
|
||||||
|
|
||||||
public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
|
||||||
{
|
|
||||||
var str = reader.GetString();
|
|
||||||
|
|
||||||
if (DateTimeOffset.TryParseExact(str, new[] { RFC3339Format, RFC3339MicroFormat }, CultureInfo.InvariantCulture, DateTimeStyles.None, out var result))
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// try RFC3339NanoLenient by trimming 1-9 digits to 7 digits
|
|
||||||
var originalstr = str;
|
|
||||||
str = Regex.Replace(str, @"\.\d+", m => (m.Value + "000000000").Substring(0, 7 + 1)); // 7 digits + 1 for the dot
|
|
||||||
if (DateTimeOffset.TryParseExact(str, new[] { RFC3339NanoFormat }, CultureInfo.InvariantCulture, DateTimeStyles.None, out result))
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new FormatException($"Unable to parse {originalstr} as RFC3339 RFC3339Micro or RFC3339Nano");
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options)
|
|
||||||
{
|
|
||||||
writer.WriteStringValue(value.ToString(RFC3339MicroFormat));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal sealed class KubernetesDateTimeConverter : JsonConverter<DateTime>
|
|
||||||
{
|
|
||||||
private static readonly JsonConverter<DateTimeOffset> UtcConverter = new KubernetesDateTimeOffsetConverter();
|
|
||||||
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
|
||||||
{
|
|
||||||
return UtcConverter.Read(ref reader, typeToConvert, options).UtcDateTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
|
|
||||||
{
|
|
||||||
UtcConverter.Write(writer, value, options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Configures <see cref="JsonSerializerOptions"/> for the <see cref="JsonSerializer"/>.
|
|
||||||
/// To override existing converters, add them to the top of the <see cref="JsonSerializerOptions.Converters"/> list
|
|
||||||
/// e.g. as follows: <code>options.Converters.Insert(index: 0, new JsonStringEnumConverter(JsonNamingPolicy.CamelCase));</code>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="configure">An <see cref="Action"/> to configure the <see cref="JsonSerializerOptions"/>.</param>
|
|
||||||
public static void AddJsonOptions(Action<JsonSerializerOptions> configure)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TValue Deserialize<TValue>(string json, JsonSerializerOptions jsonSerializerOptions = null)
|
|
||||||
{
|
|
||||||
var info = SourceGenerationContext.Default.GetTypeInfo(typeof(TValue));
|
|
||||||
return (TValue)JsonSerializer.Deserialize(json, info);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TValue Deserialize<TValue>(Stream json, JsonSerializerOptions jsonSerializerOptions = null)
|
|
||||||
{
|
|
||||||
var info = SourceGenerationContext.Default.GetTypeInfo(typeof(TValue));
|
|
||||||
return (TValue)JsonSerializer.Deserialize(json, info);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string Serialize(object value, JsonSerializerOptions jsonSerializerOptions = null)
|
|
||||||
{
|
|
||||||
if (value is V1Patch { Content: string jsonValue })
|
|
||||||
{
|
|
||||||
return jsonValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var info = SourceGenerationContext.Default.GetTypeInfo(value.GetType());
|
|
||||||
return JsonSerializer.Serialize(value, info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -17,6 +17,8 @@ namespace k8s
|
|||||||
.WithTypeConverter(new IntOrStringYamlConverter())
|
.WithTypeConverter(new IntOrStringYamlConverter())
|
||||||
.WithTypeConverter(new ByteArrayStringYamlConverter())
|
.WithTypeConverter(new ByteArrayStringYamlConverter())
|
||||||
.WithTypeConverter(new ResourceQuantityYamlConverter())
|
.WithTypeConverter(new ResourceQuantityYamlConverter())
|
||||||
|
.WithTypeConverter(new KubernetesDateTimeYamlConverter())
|
||||||
|
.WithTypeConverter(new KubernetesDateTimeOffsetYamlConverter())
|
||||||
.WithAttemptingUnquotedStringTypeDeserialization()
|
.WithAttemptingUnquotedStringTypeDeserialization()
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -33,6 +35,8 @@ namespace k8s
|
|||||||
.WithTypeConverter(new IntOrStringYamlConverter())
|
.WithTypeConverter(new IntOrStringYamlConverter())
|
||||||
.WithTypeConverter(new ByteArrayStringYamlConverter())
|
.WithTypeConverter(new ByteArrayStringYamlConverter())
|
||||||
.WithTypeConverter(new ResourceQuantityYamlConverter())
|
.WithTypeConverter(new ResourceQuantityYamlConverter())
|
||||||
|
.WithTypeConverter(new KubernetesDateTimeYamlConverter())
|
||||||
|
.WithTypeConverter(new KubernetesDateTimeOffsetYamlConverter())
|
||||||
.WithEventEmitter(e => new StringQuotingEmitter(e))
|
.WithEventEmitter(e => new StringQuotingEmitter(e))
|
||||||
.WithEventEmitter(e => new FloatEmitter(e))
|
.WithEventEmitter(e => new FloatEmitter(e))
|
||||||
.ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull)
|
.ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull)
|
||||||
@@ -56,7 +60,7 @@ namespace k8s
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Encoding.UTF8.GetBytes(scalar.Value);
|
return Convert.FromBase64String(scalar.Value);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -69,8 +73,15 @@ namespace k8s
|
|||||||
|
|
||||||
public void WriteYaml(IEmitter emitter, object value, Type type, ObjectSerializer serializer)
|
public void WriteYaml(IEmitter emitter, object value, Type type, ObjectSerializer serializer)
|
||||||
{
|
{
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
emitter.Emit(new Scalar(string.Empty));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var obj = (byte[])value;
|
var obj = (byte[])value;
|
||||||
emitter?.Emit(new Scalar(Encoding.UTF8.GetString(obj)));
|
var encoded = Convert.ToBase64String(obj);
|
||||||
|
emitter.Emit(new Scalar(encoded));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
using static k8s.KubernetesJson;
|
|
||||||
|
|
||||||
namespace k8s;
|
|
||||||
|
|
||||||
[JsonSourceGenerationOptions(
|
|
||||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
|
||||||
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
|
|
||||||
Converters = new[] { typeof(Iso8601TimeSpanConverter), typeof(KubernetesDateTimeConverter), typeof(KubernetesDateTimeOffsetConverter) })
|
|
||||||
]
|
|
||||||
internal partial class SourceGenerationContext : JsonSerializerContext
|
|
||||||
{
|
|
||||||
}
|
|
||||||
5
src/KubernetesClient.Classic/IsExternalInit.cs
Normal file
5
src/KubernetesClient.Classic/IsExternalInit.cs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
// IntOrString.cs(7,36): error CS0518: Predefined type 'System.Runtime.CompilerServices.IsExternalInit' is not defined or imported
|
||||||
|
namespace System.Runtime.CompilerServices
|
||||||
|
{
|
||||||
|
internal static class IsExternalInit { }
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netstandard2.0;net48</TargetFrameworks>
|
<TargetFrameworks>netstandard2.0;net48</TargetFrameworks>
|
||||||
@@ -22,22 +22,20 @@
|
|||||||
<Compile Include="..\KubernetesClient\Extensions.cs" />
|
<Compile Include="..\KubernetesClient\Extensions.cs" />
|
||||||
<Compile Include="..\KubernetesClient\FloatEmitter.cs" />
|
<Compile Include="..\KubernetesClient\FloatEmitter.cs" />
|
||||||
<Compile Include="..\KubernetesClient\Models\GeneratedModelVersion.cs" />
|
<Compile Include="..\KubernetesClient\Models\GeneratedModelVersion.cs" />
|
||||||
<Compile Include="..\KubernetesClient\IItems.cs" />
|
<Compile Include="..\KubernetesClient\Models\IItems.cs" />
|
||||||
<Compile Include="..\KubernetesClient\IKubernetesObject.cs" />
|
<Compile Include="..\KubernetesClient\IKubernetesObject.cs" />
|
||||||
<Compile Include="..\KubernetesClient\IMetadata.cs" />
|
<Compile Include="..\KubernetesClient\Models\IMetadata.cs" />
|
||||||
<Compile Include="..\KubernetesClient\Models\IntOrStringJsonConverter.cs" />
|
<Compile Include="..\KubernetesClient\Models\IntOrStringJsonConverter.cs" />
|
||||||
<Compile Include="..\KubernetesClient\Models\IntOrStringYamlConverter.cs" />
|
<Compile Include="..\KubernetesClient\Models\IntOrStringYamlConverter.cs" />
|
||||||
<Compile Include="..\KubernetesClient\Models\IntstrIntOrString.cs" />
|
<Compile Include="..\KubernetesClient\Models\IntOrString.cs" />
|
||||||
<Compile Include="..\KubernetesClient\ISpec.cs" />
|
<Compile Include="..\KubernetesClient\Models\ISpec.cs" />
|
||||||
<Compile Include="..\KubernetesClient\IStatus.cs" />
|
<Compile Include="..\KubernetesClient\Models\IStatus.cs" />
|
||||||
<Compile Include="..\KubernetesClient\IValidate.cs" />
|
|
||||||
<Compile Include="..\KubernetesClient\Models\KubernetesEntityAttribute.cs" />
|
<Compile Include="..\KubernetesClient\Models\KubernetesEntityAttribute.cs" />
|
||||||
<Compile Include="..\KubernetesClient\KubernetesJson.cs" />
|
<Compile Include="..\KubernetesClient\KubernetesJson.cs" />
|
||||||
<Compile Include="..\KubernetesClient\Models\KubernetesList.cs" />
|
<Compile Include="..\KubernetesClient\Models\KubernetesList.cs" />
|
||||||
<Compile Include="..\KubernetesClient\KubernetesObject.cs" />
|
<Compile Include="..\KubernetesClient\KubernetesObject.cs" />
|
||||||
<Compile Include="..\KubernetesClient\KubernetesYaml.cs" />
|
<Compile Include="..\KubernetesClient\KubernetesYaml.cs" />
|
||||||
<Compile Include="..\KubernetesClient\Models\ModelExtensions.cs" />
|
<Compile Include="..\KubernetesClient\Models\ModelExtensions.cs" />
|
||||||
<Compile Include="..\KubernetesClient\Models\ModelVersionConverter.cs" />
|
|
||||||
<Compile Include="..\KubernetesClient\Models\NodeMetrics.cs" />
|
<Compile Include="..\KubernetesClient\Models\NodeMetrics.cs" />
|
||||||
<Compile Include="..\KubernetesClient\Models\NodeMetricsList.cs" />
|
<Compile Include="..\KubernetesClient\Models\NodeMetricsList.cs" />
|
||||||
<Compile Include="..\KubernetesClient\Models\PodMetrics.cs" />
|
<Compile Include="..\KubernetesClient\Models\PodMetrics.cs" />
|
||||||
@@ -51,6 +49,9 @@
|
|||||||
<Compile Include="..\KubernetesClient\Models\V1PodTemplateSpec.cs" />
|
<Compile Include="..\KubernetesClient\Models\V1PodTemplateSpec.cs" />
|
||||||
<Compile Include="..\KubernetesClient\Models\V1Status.cs" />
|
<Compile Include="..\KubernetesClient\Models\V1Status.cs" />
|
||||||
<Compile Include="..\KubernetesClient\Models\V1Status.ObjectView.cs" />
|
<Compile Include="..\KubernetesClient\Models\V1Status.ObjectView.cs" />
|
||||||
|
<Compile Include="..\KubernetesClient\Models\KubernetesDateTimeOffsetYamlConverter.cs" />
|
||||||
|
<Compile Include="..\KubernetesClient\Models\KubernetesDateTimeYamlConverter.cs" />
|
||||||
|
|
||||||
|
|
||||||
<Compile Include="..\KubernetesClient\KubeConfigModels\ClusterEndpoint.cs" />
|
<Compile Include="..\KubernetesClient\KubeConfigModels\ClusterEndpoint.cs" />
|
||||||
<Compile Include="..\KubernetesClient\KubeConfigModels\Context.cs" />
|
<Compile Include="..\KubernetesClient\KubeConfigModels\Context.cs" />
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
namespace k8s
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Kubernetes object that exposes list of objects
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">type of the objects</typeparam>
|
|
||||||
public interface IItems<T>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets list of objects. More info:
|
|
||||||
/// https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md
|
|
||||||
/// </summary>
|
|
||||||
IList<T> Items { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ItemsExt
|
|
||||||
{
|
|
||||||
public static IEnumerator<T> GetEnumerator<T>(this IItems<T> items)
|
|
||||||
{
|
|
||||||
if (items is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(items));
|
|
||||||
}
|
|
||||||
|
|
||||||
return items.Items.GetEnumerator();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace k8s;
|
namespace k8s;
|
||||||
|
|
||||||
public partial interface IKubernetes : IBasicKubernetes, IDisposable
|
public partial interface IKubernetes : IDisposable
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The base URI of the service.
|
/// The base URI of the service.
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
namespace k8s
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Kubernetes object that exposes metadata
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">Type of metadata exposed. Usually this will be either
|
|
||||||
/// <see cref="V1ListMeta"/> for lists or <see cref="V1ObjectMeta"/> for objects</typeparam>
|
|
||||||
public interface IMetadata<T>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets standard object's metadata. More info:
|
|
||||||
/// https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
|
|
||||||
/// </summary>
|
|
||||||
T Metadata { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
namespace k8s
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a Kubernetes object that has a spec
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">type of Kubernetes object</typeparam>
|
|
||||||
public interface ISpec<T>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets specification of the desired behavior of the entity. More
|
|
||||||
/// info:
|
|
||||||
/// https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
|
|
||||||
/// </summary>
|
|
||||||
T Spec { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
namespace k8s
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Object that allows self validation
|
|
||||||
/// </summary>
|
|
||||||
public interface IValidate
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Validate the object.
|
|
||||||
/// </summary>
|
|
||||||
void Validate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,13 +3,17 @@ using System.Text.Json.Nodes;
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
|
||||||
|
#if NET8_0_OR_GREATER
|
||||||
|
using System.Text.Json.Serialization.Metadata;
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace k8s
|
namespace k8s
|
||||||
{
|
{
|
||||||
public static class KubernetesJson
|
public static class KubernetesJson
|
||||||
{
|
{
|
||||||
private static readonly JsonSerializerOptions JsonSerializerOptions = new JsonSerializerOptions();
|
internal static readonly JsonSerializerOptions JsonSerializerOptions = new JsonSerializerOptions();
|
||||||
|
|
||||||
private sealed class Iso8601TimeSpanConverter : JsonConverter<TimeSpan>
|
public sealed class Iso8601TimeSpanConverter : JsonConverter<TimeSpan>
|
||||||
{
|
{
|
||||||
public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
{
|
{
|
||||||
@@ -24,11 +28,11 @@ namespace k8s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class KubernetesDateTimeOffsetConverter : JsonConverter<DateTimeOffset>
|
public sealed class KubernetesDateTimeOffsetConverter : JsonConverter<DateTimeOffset>
|
||||||
{
|
{
|
||||||
private const string RFC3339MicroFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.ffffffK";
|
private const string RFC3339MicroFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.ffffffZ";
|
||||||
private const string RFC3339NanoFormat = "yyyy-MM-dd'T'HH':'mm':'ss.fffffffK";
|
private const string RFC3339NanoFormat = "yyyy-MM-dd'T'HH':'mm':'ss.fffffffZ";
|
||||||
private const string RFC3339Format = "yyyy'-'MM'-'dd'T'HH':'mm':'ssK";
|
private const string RFC3339Format = "yyyy'-'MM'-'dd'T'HH':'mm':'ssZ";
|
||||||
|
|
||||||
public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
{
|
{
|
||||||
@@ -50,13 +54,22 @@ namespace k8s
|
|||||||
throw new FormatException($"Unable to parse {originalstr} as RFC3339 RFC3339Micro or RFC3339Nano");
|
throw new FormatException($"Unable to parse {originalstr} as RFC3339 RFC3339Micro or RFC3339Nano");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options)
|
public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options)
|
||||||
{
|
{
|
||||||
writer.WriteStringValue(value.ToString(RFC3339MicroFormat));
|
// Output as RFC3339Micro
|
||||||
|
var date = value.ToUniversalTime();
|
||||||
|
|
||||||
|
var basePart = date.ToString("yyyy-MM-dd'T'HH:mm:ss", CultureInfo.InvariantCulture);
|
||||||
|
var frac = date.ToString(".ffffff", CultureInfo.InvariantCulture)
|
||||||
|
.TrimEnd('0')
|
||||||
|
.TrimEnd('.');
|
||||||
|
|
||||||
|
writer.WriteStringValue(basePart + frac + "Z");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class KubernetesDateTimeConverter : JsonConverter<DateTime>
|
public sealed class KubernetesDateTimeConverter : JsonConverter<DateTime>
|
||||||
{
|
{
|
||||||
private static readonly JsonConverter<DateTimeOffset> UtcConverter = new KubernetesDateTimeOffsetConverter();
|
private static readonly JsonConverter<DateTimeOffset> UtcConverter = new KubernetesDateTimeOffsetConverter();
|
||||||
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
@@ -72,13 +85,22 @@ namespace k8s
|
|||||||
|
|
||||||
static KubernetesJson()
|
static KubernetesJson()
|
||||||
{
|
{
|
||||||
|
#if K8S_AOT
|
||||||
|
// Uses Source Generated IJsonTypeInfoResolver
|
||||||
|
JsonSerializerOptions.TypeInfoResolver = SourceGenerationContext.Default;
|
||||||
|
#else
|
||||||
|
#if NET8_0_OR_GREATER
|
||||||
|
// Uses Source Generated IJsonTypeInfoResolver when available and falls back to reflection
|
||||||
|
JsonSerializerOptions.TypeInfoResolver = JsonTypeInfoResolver.Combine(SourceGenerationContext.Default, new DefaultJsonTypeInfoResolver());
|
||||||
|
#endif
|
||||||
|
JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
|
||||||
|
#endif
|
||||||
JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
|
JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
|
||||||
JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
|
JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
|
||||||
JsonSerializerOptions.Converters.Add(new Iso8601TimeSpanConverter());
|
JsonSerializerOptions.Converters.Add(new Iso8601TimeSpanConverter());
|
||||||
JsonSerializerOptions.Converters.Add(new KubernetesDateTimeConverter());
|
JsonSerializerOptions.Converters.Add(new KubernetesDateTimeConverter());
|
||||||
JsonSerializerOptions.Converters.Add(new KubernetesDateTimeOffsetConverter());
|
JsonSerializerOptions.Converters.Add(new KubernetesDateTimeOffsetConverter());
|
||||||
JsonSerializerOptions.Converters.Add(new V1Status.V1StatusObjectViewConverter());
|
JsonSerializerOptions.Converters.Add(new V1Status.V1StatusObjectViewConverter());
|
||||||
JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -99,47 +121,92 @@ namespace k8s
|
|||||||
|
|
||||||
public static TValue Deserialize<TValue>(string json, JsonSerializerOptions jsonSerializerOptions = null)
|
public static TValue Deserialize<TValue>(string json, JsonSerializerOptions jsonSerializerOptions = null)
|
||||||
{
|
{
|
||||||
|
#if NET8_0_OR_GREATER
|
||||||
|
var info = (JsonTypeInfo<TValue>)(jsonSerializerOptions ?? JsonSerializerOptions).GetTypeInfo(typeof(TValue));
|
||||||
|
return JsonSerializer.Deserialize(json, info);
|
||||||
|
#else
|
||||||
return JsonSerializer.Deserialize<TValue>(json, jsonSerializerOptions ?? JsonSerializerOptions);
|
return JsonSerializer.Deserialize<TValue>(json, jsonSerializerOptions ?? JsonSerializerOptions);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TValue Deserialize<TValue>(Stream json, JsonSerializerOptions jsonSerializerOptions = null)
|
public static TValue Deserialize<TValue>(Stream json, JsonSerializerOptions jsonSerializerOptions = null)
|
||||||
{
|
{
|
||||||
|
#if NET8_0_OR_GREATER
|
||||||
|
var info = (JsonTypeInfo<TValue>)(jsonSerializerOptions ?? JsonSerializerOptions).GetTypeInfo(typeof(TValue));
|
||||||
|
return JsonSerializer.Deserialize(json, info);
|
||||||
|
#else
|
||||||
return JsonSerializer.Deserialize<TValue>(json, jsonSerializerOptions ?? JsonSerializerOptions);
|
return JsonSerializer.Deserialize<TValue>(json, jsonSerializerOptions ?? JsonSerializerOptions);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TValue Deserialize<TValue>(JsonDocument json, JsonSerializerOptions jsonSerializerOptions = null)
|
public static TValue Deserialize<TValue>(JsonDocument json, JsonSerializerOptions jsonSerializerOptions = null)
|
||||||
{
|
{
|
||||||
|
#if NET8_0_OR_GREATER
|
||||||
|
var info = (JsonTypeInfo<TValue>)(jsonSerializerOptions ?? JsonSerializerOptions).GetTypeInfo(typeof(TValue));
|
||||||
|
return JsonSerializer.Deserialize(json, info);
|
||||||
|
#else
|
||||||
return JsonSerializer.Deserialize<TValue>(json, jsonSerializerOptions ?? JsonSerializerOptions);
|
return JsonSerializer.Deserialize<TValue>(json, jsonSerializerOptions ?? JsonSerializerOptions);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TValue Deserialize<TValue>(JsonElement json, JsonSerializerOptions jsonSerializerOptions = null)
|
public static TValue Deserialize<TValue>(JsonElement json, JsonSerializerOptions jsonSerializerOptions = null)
|
||||||
{
|
{
|
||||||
|
#if NET8_0_OR_GREATER
|
||||||
|
var info = (JsonTypeInfo<TValue>)(jsonSerializerOptions ?? JsonSerializerOptions).GetTypeInfo(typeof(TValue));
|
||||||
|
return JsonSerializer.Deserialize(json, info);
|
||||||
|
#else
|
||||||
return JsonSerializer.Deserialize<TValue>(json, jsonSerializerOptions ?? JsonSerializerOptions);
|
return JsonSerializer.Deserialize<TValue>(json, jsonSerializerOptions ?? JsonSerializerOptions);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TValue Deserialize<TValue>(JsonNode json, JsonSerializerOptions jsonSerializerOptions = null)
|
public static TValue Deserialize<TValue>(JsonNode json, JsonSerializerOptions jsonSerializerOptions = null)
|
||||||
{
|
{
|
||||||
|
#if NET8_0_OR_GREATER
|
||||||
|
var info = (JsonTypeInfo<TValue>)(jsonSerializerOptions ?? JsonSerializerOptions).GetTypeInfo(typeof(TValue));
|
||||||
|
return JsonSerializer.Deserialize(json, info);
|
||||||
|
#else
|
||||||
return JsonSerializer.Deserialize<TValue>(json, jsonSerializerOptions ?? JsonSerializerOptions);
|
return JsonSerializer.Deserialize<TValue>(json, jsonSerializerOptions ?? JsonSerializerOptions);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string Serialize(object value, JsonSerializerOptions jsonSerializerOptions = null)
|
public static string Serialize(object value, JsonSerializerOptions jsonSerializerOptions = null)
|
||||||
{
|
{
|
||||||
|
#if NET8_0_OR_GREATER
|
||||||
|
var info = (jsonSerializerOptions ?? JsonSerializerOptions).GetTypeInfo(value.GetType());
|
||||||
|
return JsonSerializer.Serialize(value, info);
|
||||||
|
#else
|
||||||
return JsonSerializer.Serialize(value, jsonSerializerOptions ?? JsonSerializerOptions);
|
return JsonSerializer.Serialize(value, jsonSerializerOptions ?? JsonSerializerOptions);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string Serialize(JsonDocument value, JsonSerializerOptions jsonSerializerOptions = null)
|
public static string Serialize(JsonDocument value, JsonSerializerOptions jsonSerializerOptions = null)
|
||||||
{
|
{
|
||||||
|
#if NET8_0_OR_GREATER
|
||||||
|
var info = (jsonSerializerOptions ?? JsonSerializerOptions).GetTypeInfo(value.GetType());
|
||||||
|
return JsonSerializer.Serialize(value, info);
|
||||||
|
#else
|
||||||
return JsonSerializer.Serialize(value, jsonSerializerOptions ?? JsonSerializerOptions);
|
return JsonSerializer.Serialize(value, jsonSerializerOptions ?? JsonSerializerOptions);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string Serialize(JsonElement value, JsonSerializerOptions jsonSerializerOptions = null)
|
public static string Serialize(JsonElement value, JsonSerializerOptions jsonSerializerOptions = null)
|
||||||
{
|
{
|
||||||
|
#if NET8_0_OR_GREATER
|
||||||
|
var info = (jsonSerializerOptions ?? JsonSerializerOptions).GetTypeInfo(value.GetType());
|
||||||
|
return JsonSerializer.Serialize(value, info);
|
||||||
|
#else
|
||||||
return JsonSerializer.Serialize(value, jsonSerializerOptions ?? JsonSerializerOptions);
|
return JsonSerializer.Serialize(value, jsonSerializerOptions ?? JsonSerializerOptions);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string Serialize(JsonNode value, JsonSerializerOptions jsonSerializerOptions = null)
|
public static string Serialize(JsonNode value, JsonSerializerOptions jsonSerializerOptions = null)
|
||||||
{
|
{
|
||||||
|
#if NET8_0_OR_GREATER
|
||||||
|
var info = (jsonSerializerOptions ?? JsonSerializerOptions).GetTypeInfo(value.GetType());
|
||||||
|
return JsonSerializer.Serialize(value, info);
|
||||||
|
#else
|
||||||
return JsonSerializer.Serialize(value, jsonSerializerOptions ?? JsonSerializerOptions);
|
return JsonSerializer.Serialize(value, jsonSerializerOptions ?? JsonSerializerOptions);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ namespace k8s
|
|||||||
.WithTypeConverter(new IntOrStringYamlConverter())
|
.WithTypeConverter(new IntOrStringYamlConverter())
|
||||||
.WithTypeConverter(new ByteArrayStringYamlConverter())
|
.WithTypeConverter(new ByteArrayStringYamlConverter())
|
||||||
.WithTypeConverter(new ResourceQuantityYamlConverter())
|
.WithTypeConverter(new ResourceQuantityYamlConverter())
|
||||||
|
.WithTypeConverter(new KubernetesDateTimeYamlConverter())
|
||||||
|
.WithTypeConverter(new KubernetesDateTimeOffsetYamlConverter())
|
||||||
.WithAttemptingUnquotedStringTypeDeserialization()
|
.WithAttemptingUnquotedStringTypeDeserialization()
|
||||||
.WithOverridesFromJsonPropertyAttributes();
|
.WithOverridesFromJsonPropertyAttributes();
|
||||||
|
|
||||||
@@ -41,6 +43,8 @@ namespace k8s
|
|||||||
.WithTypeConverter(new IntOrStringYamlConverter())
|
.WithTypeConverter(new IntOrStringYamlConverter())
|
||||||
.WithTypeConverter(new ByteArrayStringYamlConverter())
|
.WithTypeConverter(new ByteArrayStringYamlConverter())
|
||||||
.WithTypeConverter(new ResourceQuantityYamlConverter())
|
.WithTypeConverter(new ResourceQuantityYamlConverter())
|
||||||
|
.WithTypeConverter(new KubernetesDateTimeYamlConverter())
|
||||||
|
.WithTypeConverter(new KubernetesDateTimeOffsetYamlConverter())
|
||||||
.WithEventEmitter(e => new StringQuotingEmitter(e))
|
.WithEventEmitter(e => new StringQuotingEmitter(e))
|
||||||
.WithEventEmitter(e => new FloatEmitter(e))
|
.WithEventEmitter(e => new FloatEmitter(e))
|
||||||
.ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull)
|
.ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull)
|
||||||
@@ -78,7 +82,7 @@ namespace k8s
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Encoding.UTF8.GetBytes(scalar.Value);
|
return Convert.FromBase64String(scalar.Value);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -91,19 +95,15 @@ namespace k8s
|
|||||||
|
|
||||||
public void WriteYaml(IEmitter emitter, object value, Type type, ObjectSerializer serializer)
|
public void WriteYaml(IEmitter emitter, object value, Type type, ObjectSerializer serializer)
|
||||||
{
|
{
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
emitter.Emit(new Scalar(string.Empty));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var obj = (byte[])value;
|
var obj = (byte[])value;
|
||||||
var strValue = Encoding.UTF8.GetString(obj);
|
var encoded = Convert.ToBase64String(obj);
|
||||||
|
emitter.Emit(new Scalar(encoded));
|
||||||
// Check if the string is multi-line by looking for a newline character.
|
|
||||||
var scalarStyle = strValue.Contains('\n') ? ScalarStyle.Literal : ScalarStyle.Any;
|
|
||||||
|
|
||||||
emitter.Emit(new Scalar(
|
|
||||||
AnchorName.Empty,
|
|
||||||
TagName.Empty,
|
|
||||||
strValue,
|
|
||||||
scalarStyle,
|
|
||||||
true,
|
|
||||||
true));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
27
src/KubernetesClient/Models/IItems.cs
Normal file
27
src/KubernetesClient/Models/IItems.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
namespace k8s.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Kubernetes object that exposes list of objects
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">type of the objects</typeparam>
|
||||||
|
public interface IItems<T>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets list of objects. More info:
|
||||||
|
/// https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md
|
||||||
|
/// </summary>
|
||||||
|
IList<T> Items { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ItemsExt
|
||||||
|
{
|
||||||
|
public static IEnumerator<T> GetEnumerator<T>(this IItems<T> items)
|
||||||
|
{
|
||||||
|
if (items is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(items));
|
||||||
|
}
|
||||||
|
|
||||||
|
return items.Items.GetEnumerator();
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/KubernetesClient/Models/IMetadata.cs
Normal file
15
src/KubernetesClient/Models/IMetadata.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
namespace k8s.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Kubernetes object that exposes metadata
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Type of metadata exposed. Usually this will be either
|
||||||
|
/// <see cref="V1ListMeta"/> for lists or <see cref="V1ObjectMeta"/> for objects</typeparam>
|
||||||
|
public interface IMetadata<T>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets standard object's metadata. More info:
|
||||||
|
/// https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
|
||||||
|
/// </summary>
|
||||||
|
T Metadata { get; set; }
|
||||||
|
}
|
||||||
15
src/KubernetesClient/Models/ISpec.cs
Normal file
15
src/KubernetesClient/Models/ISpec.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
namespace k8s.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a Kubernetes object that has a spec
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">type of Kubernetes object</typeparam>
|
||||||
|
public interface ISpec<T>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets specification of the desired behavior of the entity. More
|
||||||
|
/// info:
|
||||||
|
/// https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
|
||||||
|
/// </summary>
|
||||||
|
T Spec { get; set; }
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace k8s
|
namespace k8s.Models
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Kubernetes object that exposes status
|
/// Kubernetes object that exposes status
|
||||||
38
src/KubernetesClient/Models/IntOrString.cs
Normal file
38
src/KubernetesClient/Models/IntOrString.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
namespace k8s.Models
|
||||||
|
{
|
||||||
|
[JsonConverter(typeof(IntOrStringJsonConverter))]
|
||||||
|
public class IntOrString
|
||||||
|
{
|
||||||
|
public string Value { get; private init; }
|
||||||
|
|
||||||
|
public static implicit operator IntOrString(int v)
|
||||||
|
{
|
||||||
|
return Convert.ToString(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static implicit operator IntOrString(long v)
|
||||||
|
{
|
||||||
|
return Convert.ToString(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static implicit operator string(IntOrString v)
|
||||||
|
{
|
||||||
|
return v?.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static implicit operator IntOrString(string v)
|
||||||
|
{
|
||||||
|
return new IntOrString { Value = v };
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int ToInt()
|
||||||
|
{
|
||||||
|
return int.Parse(Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
namespace k8s.Models
|
namespace k8s.Models
|
||||||
{
|
{
|
||||||
internal sealed class IntOrStringJsonConverter : JsonConverter<IntstrIntOrString>
|
internal sealed class IntOrStringJsonConverter : JsonConverter<IntOrString>
|
||||||
{
|
{
|
||||||
public override IntstrIntOrString Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
public override IntOrString Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
{
|
{
|
||||||
switch (reader.TokenType)
|
switch (reader.TokenType)
|
||||||
{
|
{
|
||||||
@@ -17,14 +17,14 @@ namespace k8s.Models
|
|||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Write(Utf8JsonWriter writer, IntstrIntOrString value, JsonSerializerOptions options)
|
public override void Write(Utf8JsonWriter writer, IntOrString value, JsonSerializerOptions options)
|
||||||
{
|
{
|
||||||
if (writer == null)
|
if (writer == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(writer));
|
throw new ArgumentNullException(nameof(writer));
|
||||||
}
|
}
|
||||||
|
|
||||||
var s = value?.Value;
|
var s = value.Value;
|
||||||
|
|
||||||
if (long.TryParse(s, out var intv))
|
if (long.TryParse(s, out var intv))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace k8s.Models
|
|||||||
{
|
{
|
||||||
public bool Accepts(Type type)
|
public bool Accepts(Type type)
|
||||||
{
|
{
|
||||||
return type == typeof(IntstrIntOrString);
|
return type == typeof(IntOrString);
|
||||||
}
|
}
|
||||||
|
|
||||||
public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer)
|
public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer)
|
||||||
@@ -21,7 +21,7 @@ namespace k8s.Models
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new IntstrIntOrString(scalar?.Value);
|
return scalar?.Value;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -34,7 +34,7 @@ namespace k8s.Models
|
|||||||
|
|
||||||
public void WriteYaml(IEmitter emitter, object value, Type type, ObjectSerializer serializer)
|
public void WriteYaml(IEmitter emitter, object value, Type type, ObjectSerializer serializer)
|
||||||
{
|
{
|
||||||
var obj = (IntstrIntOrString)value;
|
var obj = (IntOrString)value;
|
||||||
emitter?.Emit(new YamlDotNet.Core.Events.Scalar(obj?.Value));
|
emitter?.Emit(new YamlDotNet.Core.Events.Scalar(obj?.Value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
namespace k8s.Models
|
|
||||||
{
|
|
||||||
[JsonConverter(typeof(IntOrStringJsonConverter))]
|
|
||||||
public partial class IntstrIntOrString
|
|
||||||
{
|
|
||||||
public static implicit operator IntstrIntOrString(int v)
|
|
||||||
{
|
|
||||||
return new IntstrIntOrString(Convert.ToString(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static implicit operator IntstrIntOrString(long v)
|
|
||||||
{
|
|
||||||
return new IntstrIntOrString(Convert.ToString(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static implicit operator string(IntstrIntOrString v)
|
|
||||||
{
|
|
||||||
return v?.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static implicit operator IntstrIntOrString(string v)
|
|
||||||
{
|
|
||||||
return new IntstrIntOrString(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected bool Equals(IntstrIntOrString other)
|
|
||||||
{
|
|
||||||
return string.Equals(Value, other?.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
|
||||||
{
|
|
||||||
if (ReferenceEquals(null, obj))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ReferenceEquals(this, obj))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (obj.GetType() != GetType())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Equals((IntstrIntOrString)obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return Value != null ? Value.GetHashCode() : 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
using System.Globalization;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using YamlDotNet.Core;
|
||||||
|
using YamlDotNet.Core.Events;
|
||||||
|
using YamlDotNet.Serialization;
|
||||||
|
|
||||||
|
namespace k8s.Models;
|
||||||
|
|
||||||
|
public sealed class KubernetesDateTimeOffsetYamlConverter : IYamlTypeConverter
|
||||||
|
{
|
||||||
|
private const string RFC3339MicroFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.ffffff'Z'";
|
||||||
|
private const string RFC3339NanoFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffffff'Z'";
|
||||||
|
private const string RFC3339Format = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'";
|
||||||
|
|
||||||
|
public bool Accepts(Type type) => type == typeof(DateTimeOffset);
|
||||||
|
|
||||||
|
public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer)
|
||||||
|
{
|
||||||
|
if (parser?.Current is Scalar scalar)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(scalar.Value))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var str = scalar.Value;
|
||||||
|
|
||||||
|
if (DateTimeOffset.TryParseExact(str, new[] { RFC3339Format, RFC3339MicroFormat }, CultureInfo.InvariantCulture, DateTimeStyles.None, out var result))
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// try RFC3339NanoLenient by trimming 1-9 digits to 7 digits
|
||||||
|
var originalstr = str;
|
||||||
|
str = Regex.Replace(str, @"\.\d+", m => (m.Value + "000000000").Substring(0, 7 + 1)); // 7 digits + 1 for the dot
|
||||||
|
if (DateTimeOffset.TryParseExact(str, new[] { RFC3339NanoFormat }, CultureInfo.InvariantCulture, DateTimeStyles.None, out result))
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
parser.MoveNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidOperationException($"Unable to parse '{parser.Current?.ToString()}' as RFC3339, RFC3339Micro, or RFC3339Nano");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteYaml(IEmitter emitter, object value, Type type, ObjectSerializer serializer)
|
||||||
|
{
|
||||||
|
// Output as RFC3339Micro
|
||||||
|
var date = ((DateTimeOffset)value).ToUniversalTime();
|
||||||
|
|
||||||
|
var basePart = date.ToString("yyyy-MM-dd'T'HH:mm:ss", CultureInfo.InvariantCulture);
|
||||||
|
var frac = date.ToString(".ffffff", CultureInfo.InvariantCulture)
|
||||||
|
.TrimEnd('0')
|
||||||
|
.TrimEnd('.');
|
||||||
|
|
||||||
|
emitter.Emit(new Scalar(AnchorName.Empty, TagName.Empty, basePart + frac + "Z", ScalarStyle.DoubleQuoted, true, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
using YamlDotNet.Core;
|
||||||
|
using YamlDotNet.Serialization;
|
||||||
|
|
||||||
|
namespace k8s.Models;
|
||||||
|
|
||||||
|
public sealed class KubernetesDateTimeYamlConverter : IYamlTypeConverter
|
||||||
|
{
|
||||||
|
private static readonly KubernetesDateTimeOffsetYamlConverter OffsetConverter = new();
|
||||||
|
|
||||||
|
public bool Accepts(Type type) => type == typeof(DateTime);
|
||||||
|
|
||||||
|
public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer)
|
||||||
|
{
|
||||||
|
var dto = (DateTimeOffset)OffsetConverter.ReadYaml(parser, typeof(DateTimeOffset), rootDeserializer);
|
||||||
|
return dto.DateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteYaml(IEmitter emitter, object value, Type type, ObjectSerializer serializer)
|
||||||
|
{
|
||||||
|
var date = new DateTimeOffset((DateTime)value);
|
||||||
|
OffsetConverter.WriteYaml(emitter, date, typeof(DateTimeOffset), serializer);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -40,24 +40,5 @@ namespace k8s.Models
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("metadata")]
|
[JsonPropertyName("metadata")]
|
||||||
public V1ListMeta Metadata { get; set; }
|
public V1ListMeta Metadata { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Validate the object.
|
|
||||||
/// </summary>
|
|
||||||
public void Validate()
|
|
||||||
{
|
|
||||||
if (Items == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException("Items");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Items != null)
|
|
||||||
{
|
|
||||||
foreach (var element in Items.OfType<IValidate>())
|
|
||||||
{
|
|
||||||
element.Validate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
namespace k8s.Models;
|
|
||||||
|
|
||||||
public static class ModelVersionConverter
|
|
||||||
{
|
|
||||||
public interface IModelVersionConverter
|
|
||||||
{
|
|
||||||
TTo Convert<TFrom, TTo>(TFrom from);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IModelVersionConverter Converter { get; set; }
|
|
||||||
|
|
||||||
internal static TTo Convert<TFrom, TTo>(TFrom from)
|
|
||||||
{
|
|
||||||
if (Converter == null)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Converter is not set");
|
|
||||||
}
|
|
||||||
|
|
||||||
return Converter.Convert<TFrom, TTo>(from);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -54,7 +54,7 @@ namespace k8s.Models
|
|||||||
/// cause implementors to also use a fixed point implementation.
|
/// cause implementors to also use a fixed point implementation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonConverter(typeof(ResourceQuantityJsonConverter))]
|
[JsonConverter(typeof(ResourceQuantityJsonConverter))]
|
||||||
public partial class ResourceQuantity
|
public class ResourceQuantity
|
||||||
{
|
{
|
||||||
public enum SuffixFormat
|
public enum SuffixFormat
|
||||||
{
|
{
|
||||||
@@ -97,39 +97,6 @@ namespace k8s.Models
|
|||||||
return CanonicalizeString();
|
return CanonicalizeString();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected bool Equals(ResourceQuantity other)
|
|
||||||
{
|
|
||||||
return _unitlessValue.Equals(other?._unitlessValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
|
||||||
{
|
|
||||||
if (ReferenceEquals(null, obj))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ReferenceEquals(this, obj))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (obj.GetType() != GetType())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Equals((ResourceQuantity)obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
unchecked
|
|
||||||
{
|
|
||||||
return ((int)Format * 397) ^ _unitlessValue.GetHashCode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// CanonicalizeString = go version CanonicalizeBytes
|
// CanonicalizeString = go version CanonicalizeBytes
|
||||||
// CanonicalizeBytes returns the canonical form of q and its suffix (see comment on Quantity).
|
// CanonicalizeBytes returns the canonical form of q and its suffix (see comment on Quantity).
|
||||||
@@ -157,10 +124,9 @@ namespace k8s.Models
|
|||||||
return Suffixer.AppendMaxSuffix(_unitlessValue, suffixFormat);
|
return Suffixer.AppendMaxSuffix(_unitlessValue, suffixFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ctor
|
public ResourceQuantity(string v)
|
||||||
partial void CustomInit()
|
|
||||||
{
|
{
|
||||||
if (Value == null)
|
if (v == null)
|
||||||
{
|
{
|
||||||
// No value has been defined, initialize to 0.
|
// No value has been defined, initialize to 0.
|
||||||
_unitlessValue = new Fraction(0);
|
_unitlessValue = new Fraction(0);
|
||||||
@@ -168,7 +134,7 @@ namespace k8s.Models
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var value = Value.Trim();
|
var value = v.Trim();
|
||||||
|
|
||||||
var si = value.IndexOfAny(SuffixChars);
|
var si = value.IndexOfAny(SuffixChars);
|
||||||
if (si == -1)
|
if (si == -1)
|
||||||
@@ -188,6 +154,11 @@ namespace k8s.Models
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static implicit operator ResourceQuantity(string v)
|
||||||
|
{
|
||||||
|
return new ResourceQuantity(v);
|
||||||
|
}
|
||||||
|
|
||||||
private static bool HasMantissa(Fraction value)
|
private static bool HasMantissa(Fraction value)
|
||||||
{
|
{
|
||||||
if (value.IsZero)
|
if (value.IsZero)
|
||||||
@@ -200,7 +171,7 @@ namespace k8s.Models
|
|||||||
|
|
||||||
public static implicit operator decimal(ResourceQuantity v)
|
public static implicit operator decimal(ResourceQuantity v)
|
||||||
{
|
{
|
||||||
return v?.ToDecimal() ?? 0;
|
return v.ToDecimal();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static implicit operator ResourceQuantity(decimal v)
|
public static implicit operator ResourceQuantity(decimal v)
|
||||||
@@ -208,6 +179,46 @@ namespace k8s.Models
|
|||||||
return new ResourceQuantity(v, 0, SuffixFormat.DecimalExponent);
|
return new ResourceQuantity(v, 0, SuffixFormat.DecimalExponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool Equals(ResourceQuantity other)
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(null, other))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ReferenceEquals(this, other))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _unitlessValue.Equals(other._unitlessValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return Equals(obj as ResourceQuantity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return _unitlessValue.GetHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator ==(ResourceQuantity left, ResourceQuantity right)
|
||||||
|
{
|
||||||
|
if (left is null)
|
||||||
|
{
|
||||||
|
return right is null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return left.Equals(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator !=(ResourceQuantity left, ResourceQuantity right)
|
||||||
|
{
|
||||||
|
return !(left == right);
|
||||||
|
}
|
||||||
|
|
||||||
private sealed class Suffixer
|
private sealed class Suffixer
|
||||||
{
|
{
|
||||||
private static readonly IReadOnlyDictionary<string, (int, int)> BinSuffixes =
|
private static readonly IReadOnlyDictionary<string, (int, int)> BinSuffixes =
|
||||||
|
|||||||
@@ -8,16 +8,16 @@ namespace k8s.Models
|
|||||||
switch (reader.TokenType)
|
switch (reader.TokenType)
|
||||||
{
|
{
|
||||||
case JsonTokenType.Null:
|
case JsonTokenType.Null:
|
||||||
return new ResourceQuantity(null);
|
return null;
|
||||||
case JsonTokenType.Number:
|
case JsonTokenType.Number:
|
||||||
if (reader.TryGetDouble(out var val))
|
if (reader.TryGetDouble(out var val))
|
||||||
{
|
{
|
||||||
return new ResourceQuantity(Convert.ToString(val));
|
return Convert.ToString(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
return reader.GetDecimal();
|
return reader.GetDecimal();
|
||||||
default:
|
default:
|
||||||
return new ResourceQuantity(reader.GetString());
|
return reader.GetString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ namespace k8s.Models
|
|||||||
throw new ArgumentNullException(nameof(writer));
|
throw new ArgumentNullException(nameof(writer));
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.WriteStringValue(value?.ToString());
|
writer.WriteStringValue(value.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ namespace k8s.Models
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ResourceQuantity(scalar?.Value);
|
return scalar?.Value;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
namespace k8s.Models
|
namespace k8s.Models
|
||||||
{
|
{
|
||||||
[JsonConverter(typeof(V1PatchJsonConverter))]
|
[JsonConverter(typeof(V1PatchJsonConverter))]
|
||||||
public partial class V1Patch
|
public record V1Patch
|
||||||
{
|
{
|
||||||
public enum PatchType
|
public enum PatchType
|
||||||
{
|
{
|
||||||
@@ -31,26 +31,21 @@ namespace k8s.Models
|
|||||||
ApplyPatch,
|
ApplyPatch,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[JsonPropertyName("content")]
|
||||||
|
[JsonInclude]
|
||||||
|
public object Content { get; private set; }
|
||||||
|
|
||||||
public PatchType Type { get; private set; }
|
public PatchType Type { get; private set; }
|
||||||
|
|
||||||
public V1Patch(object body, PatchType type)
|
public V1Patch(object body, PatchType type)
|
||||||
{
|
{
|
||||||
Content = body;
|
if (type == PatchType.Unknown)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("patch type must be set", nameof(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
Content = body ?? throw new ArgumentNullException(nameof(body), "object must be set");
|
||||||
Type = type;
|
Type = type;
|
||||||
CustomInit();
|
|
||||||
}
|
|
||||||
|
|
||||||
partial void CustomInit()
|
|
||||||
{
|
|
||||||
if (Content == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(Content), "object must be set");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Type == PatchType.Unknown)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("patch type must be set", nameof(Type));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ namespace k8s.Models
|
|||||||
/// Partial implementation of the IMetadata interface
|
/// Partial implementation of the IMetadata interface
|
||||||
/// to open this class up to ModelExtensions methods
|
/// to open this class up to ModelExtensions methods
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class V1PodTemplateSpec : IMetadata<V1ObjectMeta>
|
public partial record V1PodTemplateSpec : IMetadata<V1ObjectMeta>
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,28 @@
|
|||||||
namespace k8s.Models
|
namespace k8s.Models
|
||||||
{
|
{
|
||||||
public partial class V1Status
|
public partial record V1Status
|
||||||
{
|
{
|
||||||
internal sealed class V1StatusObjectViewConverter : JsonConverter<V1Status>
|
public sealed class V1StatusObjectViewConverter : JsonConverter<V1Status>
|
||||||
{
|
{
|
||||||
public override V1Status Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
public override V1Status Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
{
|
{
|
||||||
var obj = JsonElement.ParseValue(ref reader);
|
using var doc = JsonDocument.ParseValue(ref reader);
|
||||||
|
var ele = doc.RootElement.Clone();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return obj.Deserialize<V1Status>();
|
#if NET8_0_OR_GREATER
|
||||||
|
return JsonSerializer.Deserialize(ele, StatusSourceGenerationContext.Default.V1Status);
|
||||||
|
#else
|
||||||
|
return ele.Deserialize<V1Status>();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
catch (JsonException)
|
catch (JsonException)
|
||||||
{
|
{
|
||||||
// should be an object
|
// should be an object
|
||||||
}
|
}
|
||||||
|
|
||||||
return new V1Status { _original = obj, HasObject = true };
|
return new V1Status { _original = ele, HasObject = true };
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Write(Utf8JsonWriter writer, V1Status value, JsonSerializerOptions options)
|
public override void Write(Utf8JsonWriter writer, V1Status value, JsonSerializerOptions options)
|
||||||
@@ -32,7 +37,11 @@ namespace k8s.Models
|
|||||||
|
|
||||||
public T ObjectView<T>()
|
public T ObjectView<T>()
|
||||||
{
|
{
|
||||||
|
#if NET8_0_OR_GREATER
|
||||||
|
return KubernetesJson.Deserialize<T>(_original);
|
||||||
|
#else
|
||||||
return _original.Deserialize<T>();
|
return _original.Deserialize<T>();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ using System.Net;
|
|||||||
|
|
||||||
namespace k8s.Models
|
namespace k8s.Models
|
||||||
{
|
{
|
||||||
public partial class V1Status
|
public partial record V1Status
|
||||||
{
|
{
|
||||||
/// <summary>Converts a <see cref="V1Status"/> object into a short description of the status.</summary>
|
/// <summary>Converts a <see cref="V1Status"/> object into a short description of the status.</summary>
|
||||||
/// <returns>string description of the status</returns>
|
/// <returns>string description of the status</returns>
|
||||||
|
|||||||
28
src/KubernetesClient/SourceGenerationContext.cs
Normal file
28
src/KubernetesClient/SourceGenerationContext.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using static k8s.KubernetesJson;
|
||||||
|
using static k8s.Models.V1Status;
|
||||||
|
|
||||||
|
namespace k8s;
|
||||||
|
|
||||||
|
[JsonSourceGenerationOptions(
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||||
|
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
|
||||||
|
UseStringEnumConverter = true,
|
||||||
|
Converters = new[] { typeof(Iso8601TimeSpanConverter), typeof(KubernetesDateTimeConverter), typeof(KubernetesDateTimeOffsetConverter), typeof(V1StatusObjectViewConverter) })
|
||||||
|
]
|
||||||
|
public partial class SourceGenerationContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used by V1Status in order to avoid the recursive loop as SourceGenerationContext contains V1StatusObjectViewConverter
|
||||||
|
/// </summary>
|
||||||
|
[JsonSerializable(typeof(V1Status))]
|
||||||
|
[JsonSourceGenerationOptions(
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||||
|
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
|
||||||
|
UseStringEnumConverter = true,
|
||||||
|
Converters = new[] { typeof(Iso8601TimeSpanConverter), typeof(KubernetesDateTimeConverter), typeof(KubernetesDateTimeOffsetConverter) })
|
||||||
|
]
|
||||||
|
public partial class StatusSourceGenerationContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ namespace k8s
|
|||||||
/// The action to invoke when the server closes the connection.
|
/// The action to invoke when the server closes the connection.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <returns>a watch object</returns>
|
/// <returns>a watch object</returns>
|
||||||
|
[Obsolete("This method will be deprecated in future versions. Please use the Watch method instead.")]
|
||||||
public static Watcher<T> Watch<T, L>(
|
public static Watcher<T> Watch<T, L>(
|
||||||
this Task<HttpOperationResponse<L>> responseTask,
|
this Task<HttpOperationResponse<L>> responseTask,
|
||||||
Action<WatchEventType, T> onEvent,
|
Action<WatchEventType, T> onEvent,
|
||||||
@@ -52,6 +53,7 @@ namespace k8s
|
|||||||
/// The action to invoke when the server closes the connection.
|
/// The action to invoke when the server closes the connection.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <returns>a watch object</returns>
|
/// <returns>a watch object</returns>
|
||||||
|
[Obsolete("This method will be deprecated in future versions. Please use the Watch method instead.")]
|
||||||
public static Watcher<T> Watch<T, L>(
|
public static Watcher<T> Watch<T, L>(
|
||||||
this HttpOperationResponse<L> response,
|
this HttpOperationResponse<L> response,
|
||||||
Action<WatchEventType, T> onEvent,
|
Action<WatchEventType, T> onEvent,
|
||||||
@@ -71,6 +73,7 @@ namespace k8s
|
|||||||
/// <param name="onError">a callback when any exception was caught during watching</param>
|
/// <param name="onError">a callback when any exception was caught during watching</param>
|
||||||
/// <param name="cancellationToken">cancellation token</param>
|
/// <param name="cancellationToken">cancellation token</param>
|
||||||
/// <returns>IAsyncEnumerable of watch events</returns>
|
/// <returns>IAsyncEnumerable of watch events</returns>
|
||||||
|
[Obsolete("This method will be deprecated in future versions. Please use the WatchAsync method instead.")]
|
||||||
public static IAsyncEnumerable<(WatchEventType, T)> WatchAsync<T, L>(
|
public static IAsyncEnumerable<(WatchEventType, T)> WatchAsync<T, L>(
|
||||||
this Task<HttpOperationResponse<L>> responseTask,
|
this Task<HttpOperationResponse<L>> responseTask,
|
||||||
Action<Exception> onError = null,
|
Action<Exception> onError = null,
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ namespace LibKubernetesGenerator
|
|||||||
sc = scriptObjectFactory.CreateScriptObject();
|
sc = scriptObjectFactory.CreateScriptObject();
|
||||||
sc.SetValue("groups", groups, true);
|
sc.SetValue("groups", groups, true);
|
||||||
|
|
||||||
context.RenderToContext($"IBasicKubernetes.cs.template", sc, $"IBasicKubernetes.g.cs");
|
context.RenderToContext($"IKubernetes.cs.template", sc, $"IKubernetes.g.cs");
|
||||||
context.RenderToContext($"AbstractKubernetes.cs.template", sc, $"AbstractKubernetes.g.cs");
|
context.RenderToContext($"AbstractKubernetes.cs.template", sc, $"AbstractKubernetes.g.cs");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,6 +78,12 @@ namespace LibKubernetesGenerator
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (definition.Format == "int-or-string")
|
||||||
|
{
|
||||||
|
return "IntOrString";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return schemaToNameMapCooked[definition];
|
return schemaToNameMapCooked[definition];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,8 +57,6 @@ namespace LibKubernetesGenerator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interfaces.Add("IValidate");
|
|
||||||
|
|
||||||
return string.Join(", ", interfaces);
|
return string.Join(", ", interfaces);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +66,7 @@ namespace LibKubernetesGenerator
|
|||||||
|
|
||||||
if (init == "true" && !parameter.IsRequired)
|
if (init == "true" && !parameter.IsRequired)
|
||||||
{
|
{
|
||||||
name += " = null";
|
name += " = default";
|
||||||
}
|
}
|
||||||
|
|
||||||
return name;
|
return name;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.CodeAnalysis.Text;
|
using Microsoft.CodeAnalysis.Text;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp;
|
||||||
using Scriban;
|
using Scriban;
|
||||||
using Scriban.Runtime;
|
using Scriban.Runtime;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -19,7 +20,10 @@ namespace LibKubernetesGenerator
|
|||||||
{
|
{
|
||||||
var template = Template.Parse(EmbedResource.GetResource(templatefile));
|
var template = Template.Parse(EmbedResource.GetResource(templatefile));
|
||||||
var generated = template.Render(tc);
|
var generated = template.Render(tc);
|
||||||
context.AddSource(generatedfile, SourceText.From(generated, Encoding.UTF8));
|
|
||||||
|
var syntaxTree = CSharpSyntaxTree.ParseText(generated);
|
||||||
|
var normalized = syntaxTree.GetRoot().NormalizeWhitespace().ToFullString();
|
||||||
|
context.AddSource(generatedfile, SourceText.From(normalized, Encoding.UTF8));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,12 +58,10 @@ namespace LibKubernetesGenerator
|
|||||||
builder.RegisterType<ScriptObjectFactory>()
|
builder.RegisterType<ScriptObjectFactory>()
|
||||||
;
|
;
|
||||||
|
|
||||||
builder.RegisterType<ModelExtGenerator>();
|
|
||||||
builder.RegisterType<SourceGenerationContextGenerator>();
|
builder.RegisterType<SourceGenerationContextGenerator>();
|
||||||
builder.RegisterType<ModelGenerator>();
|
builder.RegisterType<ModelGenerator>();
|
||||||
builder.RegisterType<ApiGenerator>();
|
builder.RegisterType<ApiGenerator>();
|
||||||
builder.RegisterType<ClientSetGenerator>();
|
builder.RegisterType<ClientSetGenerator>();
|
||||||
builder.RegisterType<VersionConverterStubGenerator>();
|
|
||||||
builder.RegisterType<VersionGenerator>();
|
builder.RegisterType<VersionGenerator>();
|
||||||
|
|
||||||
return builder.Build();
|
return builder.Build();
|
||||||
@@ -79,9 +77,7 @@ namespace LibKubernetesGenerator
|
|||||||
container.Resolve<VersionGenerator>().Generate(swagger, ctx);
|
container.Resolve<VersionGenerator>().Generate(swagger, ctx);
|
||||||
|
|
||||||
container.Resolve<ModelGenerator>().Generate(swagger, ctx);
|
container.Resolve<ModelGenerator>().Generate(swagger, ctx);
|
||||||
container.Resolve<ModelExtGenerator>().Generate(swagger, ctx);
|
|
||||||
container.Resolve<SourceGenerationContextGenerator>().Generate(swagger, ctx);
|
container.Resolve<SourceGenerationContextGenerator>().Generate(swagger, ctx);
|
||||||
container.Resolve<VersionConverterStubGenerator>().Generate(swagger, ctx);
|
|
||||||
container.Resolve<ApiGenerator>().Generate(swagger, ctx);
|
container.Resolve<ApiGenerator>().Generate(swagger, ctx);
|
||||||
container.Resolve<ClientSetGenerator>().Generate(swagger, ctx);
|
container.Resolve<ClientSetGenerator>().Generate(swagger, ctx);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.Common" PrivateAssets="all" />
|
<PackageReference Include="Microsoft.CodeAnalysis.Common" PrivateAssets="all" />
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" PrivateAssets="all" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
using Microsoft.CodeAnalysis;
|
|
||||||
using NSwag;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace LibKubernetesGenerator
|
|
||||||
{
|
|
||||||
internal class ModelExtGenerator
|
|
||||||
{
|
|
||||||
private readonly ClassNameHelper classNameHelper;
|
|
||||||
private readonly ScriptObjectFactory scriptObjectFactory;
|
|
||||||
|
|
||||||
public ModelExtGenerator(ClassNameHelper classNameHelper, ScriptObjectFactory scriptObjectFactory)
|
|
||||||
{
|
|
||||||
this.classNameHelper = classNameHelper;
|
|
||||||
this.scriptObjectFactory = scriptObjectFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Generate(OpenApiDocument swagger, IncrementalGeneratorPostInitializationContext context)
|
|
||||||
{
|
|
||||||
// Generate the interface declarations
|
|
||||||
var skippedTypes = new HashSet<string> { "V1WatchEvent" };
|
|
||||||
|
|
||||||
var definitions = swagger.Definitions.Values
|
|
||||||
.Where(
|
|
||||||
d => d.ExtensionData != null
|
|
||||||
&& d.ExtensionData.ContainsKey("x-kubernetes-group-version-kind")
|
|
||||||
&& !skippedTypes.Contains(classNameHelper.GetClassName(d)));
|
|
||||||
|
|
||||||
var sc = scriptObjectFactory.CreateScriptObject();
|
|
||||||
sc.SetValue("definitions", definitions, true);
|
|
||||||
|
|
||||||
context.RenderToContext("ModelExtensions.cs.template", sc, "ModelExtensions.g.cs");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using NSwag;
|
using NSwag;
|
||||||
|
|
||||||
@@ -18,15 +19,49 @@ namespace LibKubernetesGenerator
|
|||||||
{
|
{
|
||||||
var sc = scriptObjectFactory.CreateScriptObject();
|
var sc = scriptObjectFactory.CreateScriptObject();
|
||||||
|
|
||||||
|
var genSkippedTypes = new HashSet<string>
|
||||||
|
{
|
||||||
|
"IntOrString",
|
||||||
|
"ResourceQuantity",
|
||||||
|
"V1Patch",
|
||||||
|
};
|
||||||
|
|
||||||
|
var extSkippedTypes = new HashSet<string>
|
||||||
|
{
|
||||||
|
"V1WatchEvent",
|
||||||
|
};
|
||||||
|
|
||||||
|
var typeOverrides = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
// not used at the moment
|
||||||
|
};
|
||||||
|
|
||||||
foreach (var kv in swagger.Definitions)
|
foreach (var kv in swagger.Definitions)
|
||||||
{
|
{
|
||||||
var def = kv.Value;
|
var def = kv.Value;
|
||||||
var clz = classNameHelper.GetClassNameForSchemaDefinition(def);
|
var clz = classNameHelper.GetClassNameForSchemaDefinition(def);
|
||||||
|
|
||||||
|
if (genSkippedTypes.Contains(clz))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var hasExt = def.ExtensionData != null
|
||||||
|
&& def.ExtensionData.ContainsKey("x-kubernetes-group-version-kind")
|
||||||
|
&& !extSkippedTypes.Contains(clz);
|
||||||
|
|
||||||
|
|
||||||
|
var typ = "record";
|
||||||
|
if (typeOverrides.TryGetValue(clz, out var to))
|
||||||
|
{
|
||||||
|
typ = to;
|
||||||
|
}
|
||||||
|
|
||||||
sc.SetValue("clz", clz, true);
|
sc.SetValue("clz", clz, true);
|
||||||
sc.SetValue("def", def, true);
|
sc.SetValue("def", def, true);
|
||||||
sc.SetValue("properties", def.Properties.Values, true);
|
sc.SetValue("properties", def.Properties.Values, true);
|
||||||
|
sc.SetValue("typ", typ, true);
|
||||||
|
sc.SetValue("hasExt", hasExt, true);
|
||||||
|
|
||||||
context.RenderToContext("Model.cs.template", sc, $"Models_{clz}.g.cs");
|
context.RenderToContext("Model.cs.template", sc, $"Models_{clz}.g.cs");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using NSwag;
|
|||||||
using Scriban.Runtime;
|
using Scriban.Runtime;
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace LibKubernetesGenerator
|
namespace LibKubernetesGenerator
|
||||||
{
|
{
|
||||||
@@ -21,6 +22,8 @@ namespace LibKubernetesGenerator
|
|||||||
{
|
{
|
||||||
scriptObject.Import(nameof(GetModelCtorParam), new Func<JsonSchema, string>(GetModelCtorParam));
|
scriptObject.Import(nameof(GetModelCtorParam), new Func<JsonSchema, string>(GetModelCtorParam));
|
||||||
scriptObject.Import(nameof(IfParamContains), IfParamContains);
|
scriptObject.Import(nameof(IfParamContains), IfParamContains);
|
||||||
|
scriptObject.Import(nameof(FilterParameters), FilterParameters);
|
||||||
|
scriptObject.Import(nameof(GetParameterValueForWatch), new Func<OpenApiParameter, bool, string, string>(GetParameterValueForWatch));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IfParamContains(OpenApiOperation operation, string name)
|
public static bool IfParamContains(OpenApiOperation operation, string name)
|
||||||
@@ -39,6 +42,23 @@ namespace LibKubernetesGenerator
|
|||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<OpenApiParameter> FilterParameters(OpenApiOperation operation, string excludeParam)
|
||||||
|
{
|
||||||
|
return operation.Parameters.Where(p => p.Name != excludeParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetParameterValueForWatch(OpenApiParameter parameter, bool watch, string init = "false")
|
||||||
|
{
|
||||||
|
if (parameter.Name == "watch")
|
||||||
|
{
|
||||||
|
return watch ? "true" : "false";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return generalNameHelper.GetDotNetNameOpenApiParameter(parameter, init);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public string GetModelCtorParam(JsonSchema schema)
|
public string GetModelCtorParam(JsonSchema schema)
|
||||||
{
|
{
|
||||||
return string.Join(", ", schema.Properties.Values
|
return string.Join(", ", schema.Properties.Values
|
||||||
@@ -57,4 +77,4 @@ namespace LibKubernetesGenerator
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,11 +11,12 @@ namespace LibKubernetesGenerator
|
|||||||
{
|
{
|
||||||
private readonly Dictionary<string, string> _classNameToPluralMap;
|
private readonly Dictionary<string, string> _classNameToPluralMap;
|
||||||
private readonly ClassNameHelper classNameHelper;
|
private readonly ClassNameHelper classNameHelper;
|
||||||
private readonly HashSet<string> opblackList = new HashSet<string>()
|
private readonly HashSet<string> opblackList =
|
||||||
{
|
[
|
||||||
"listClusterCustomObject",
|
"listClusterCustomObject",
|
||||||
"listNamespacedCustomObject",
|
"listNamespacedCustomObject",
|
||||||
};
|
"listCustomObjectForAllNamespaces",
|
||||||
|
];
|
||||||
|
|
||||||
public PluralHelper(ClassNameHelper classNameHelper, OpenApiDocument swagger)
|
public PluralHelper(ClassNameHelper classNameHelper, OpenApiDocument swagger)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -122,7 +122,6 @@ namespace LibKubernetesGenerator
|
|||||||
return $"IDictionary<string, {GetDotNetType(schema.AdditionalPropertiesSchema, parent)}>";
|
return $"IDictionary<string, {GetDotNetType(schema.AdditionalPropertiesSchema, parent)}>";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (schema?.Reference != null)
|
if (schema?.Reference != null)
|
||||||
{
|
{
|
||||||
return classNameHelper.GetClassNameForSchemaDefinition(schema.Reference);
|
return classNameHelper.GetClassNameForSchemaDefinition(schema.Reference);
|
||||||
@@ -245,6 +244,16 @@ namespace LibKubernetesGenerator
|
|||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
case "T":
|
||||||
|
var itemType = TryGetItemTypeFromSchema(response);
|
||||||
|
if (itemType != null)
|
||||||
|
{
|
||||||
|
return itemType;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "TList":
|
||||||
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
return t;
|
return t;
|
||||||
@@ -283,5 +292,26 @@ namespace LibKubernetesGenerator
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string TryGetItemTypeFromSchema(OpenApiResponse response)
|
||||||
|
{
|
||||||
|
var listSchema = response?.Schema?.Reference;
|
||||||
|
if (listSchema?.Properties?.TryGetValue("items", out var itemsProperty) != true)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itemsProperty.Reference != null)
|
||||||
|
{
|
||||||
|
return classNameHelper.GetClassNameForSchemaDefinition(itemsProperty.Reference);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itemsProperty.Item?.Reference != null)
|
||||||
|
{
|
||||||
|
return classNameHelper.GetClassNameForSchemaDefinition(itemsProperty.Item.Reference);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,70 +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 VersionConverterStubGenerator
|
|
||||||
{
|
|
||||||
private readonly ClassNameHelper classNameHelper;
|
|
||||||
|
|
||||||
public VersionConverterStubGenerator(ClassNameHelper classNameHelper)
|
|
||||||
{
|
|
||||||
this.classNameHelper = classNameHelper;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Generate(OpenApiDocument swagger, IncrementalGeneratorPostInitializationContext context)
|
|
||||||
{
|
|
||||||
var allGeneratedModelClassNames = new List<string>();
|
|
||||||
|
|
||||||
foreach (var kv in swagger.Definitions)
|
|
||||||
{
|
|
||||||
var def = kv.Value;
|
|
||||||
var clz = classNameHelper.GetClassNameForSchemaDefinition(def);
|
|
||||||
allGeneratedModelClassNames.Add(clz);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 sbmodel = new StringBuilder(@"// <auto-generated />
|
|
||||||
namespace k8s.Models;
|
|
||||||
");
|
|
||||||
|
|
||||||
foreach (var (t0, t1) in typePairs)
|
|
||||||
{
|
|
||||||
sbmodel.AppendLine($@"
|
|
||||||
public partial class {t0}
|
|
||||||
{{
|
|
||||||
public static explicit operator {t0}({t1} s) => ModelVersionConverter.Convert<{t1}, {t0}>(s);
|
|
||||||
}}
|
|
||||||
public partial class {t1}
|
|
||||||
{{
|
|
||||||
public static explicit operator {t1}({t0} s) => ModelVersionConverter.Convert<{t0}, {t1}>(s);
|
|
||||||
}}");
|
|
||||||
}
|
|
||||||
|
|
||||||
context.AddSource($"ModelOperators.g.cs", SourceText.From(sbmodel.ToString(), Encoding.UTF8));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -17,10 +17,11 @@ public partial class {{name}}Client : ResourceClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
{{for api in apis }}
|
{{for api in apis }}
|
||||||
|
{{~ $filteredParams = FilterParameters api.operation "watch" ~}}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// {{ToXmlDoc api.operation.description}}
|
/// {{ToXmlDoc api.operation.description}}
|
||||||
/// </summary>
|
/// </summary>
|
||||||
{{ for parameter in api.operation.parameters}}
|
{{ for parameter in $filteredParams}}
|
||||||
/// <param name="{{GetDotNetNameOpenApiParameter parameter "false"}}">
|
/// <param name="{{GetDotNetNameOpenApiParameter parameter "false"}}">
|
||||||
/// {{ToXmlDoc parameter.description}}
|
/// {{ToXmlDoc parameter.description}}
|
||||||
/// </param>
|
/// </param>
|
||||||
@@ -29,7 +30,7 @@ public partial class {{name}}Client : ResourceClient
|
|||||||
/// A <see cref="CancellationToken"/> which can be used to cancel the asynchronous operation.
|
/// A <see cref="CancellationToken"/> which can be used to cancel the asynchronous operation.
|
||||||
/// </param>
|
/// </param>
|
||||||
public async Task{{GetReturnType api.operation "<>"}} {{GetActionName api.operation name "Async"}}(
|
public async Task{{GetReturnType api.operation "<>"}} {{GetActionName api.operation name "Async"}}(
|
||||||
{{ for parameter in api.operation.parameters}}
|
{{ for parameter in $filteredParams}}
|
||||||
{{GetDotNetTypeOpenApiParameter parameter}} {{GetDotNetNameOpenApiParameter parameter "true"}},
|
{{GetDotNetTypeOpenApiParameter parameter}} {{GetDotNetNameOpenApiParameter parameter "true"}},
|
||||||
{{ end }}
|
{{ end }}
|
||||||
CancellationToken cancellationToken = default(CancellationToken))
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
@@ -37,7 +38,7 @@ public partial class {{name}}Client : ResourceClient
|
|||||||
{{if IfReturnType api.operation "stream"}}
|
{{if IfReturnType api.operation "stream"}}
|
||||||
var _result = await Client.{{group}}.{{GetOperationId api.operation "WithHttpMessagesAsync"}}(
|
var _result = await Client.{{group}}.{{GetOperationId api.operation "WithHttpMessagesAsync"}}(
|
||||||
{{ for parameter in api.operation.parameters}}
|
{{ for parameter in api.operation.parameters}}
|
||||||
{{GetDotNetNameOpenApiParameter parameter "false"}},
|
{{GetParameterValueForWatch parameter false}},
|
||||||
{{end}}
|
{{end}}
|
||||||
null,
|
null,
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
@@ -47,7 +48,7 @@ public partial class {{name}}Client : ResourceClient
|
|||||||
{{if IfReturnType api.operation "obj"}}
|
{{if IfReturnType api.operation "obj"}}
|
||||||
using (var _result = await Client.{{group}}.{{GetOperationId api.operation "WithHttpMessagesAsync"}}(
|
using (var _result = await Client.{{group}}.{{GetOperationId api.operation "WithHttpMessagesAsync"}}(
|
||||||
{{ for parameter in api.operation.parameters}}
|
{{ for parameter in api.operation.parameters}}
|
||||||
{{GetDotNetNameOpenApiParameter parameter "false"}},
|
{{GetParameterValueForWatch parameter false}},
|
||||||
{{end}}
|
{{end}}
|
||||||
null,
|
null,
|
||||||
cancellationToken).ConfigureAwait(false))
|
cancellationToken).ConfigureAwait(false))
|
||||||
@@ -58,7 +59,7 @@ public partial class {{name}}Client : ResourceClient
|
|||||||
{{if IfReturnType api.operation "void"}}
|
{{if IfReturnType api.operation "void"}}
|
||||||
using (var _result = await Client.{{group}}.{{GetOperationId api.operation "WithHttpMessagesAsync"}}(
|
using (var _result = await Client.{{group}}.{{GetOperationId api.operation "WithHttpMessagesAsync"}}(
|
||||||
{{ for parameter in api.operation.parameters}}
|
{{ for parameter in api.operation.parameters}}
|
||||||
{{GetDotNetNameOpenApiParameter parameter "false"}},
|
{{GetParameterValueForWatch parameter false}},
|
||||||
{{end}}
|
{{end}}
|
||||||
null,
|
null,
|
||||||
cancellationToken).ConfigureAwait(false))
|
cancellationToken).ConfigureAwait(false))
|
||||||
@@ -71,7 +72,7 @@ public partial class {{name}}Client : ResourceClient
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// {{ToXmlDoc api.operation.description}}
|
/// {{ToXmlDoc api.operation.description}}
|
||||||
/// </summary>
|
/// </summary>
|
||||||
{{ for parameter in api.operation.parameters}}
|
{{ for parameter in $filteredParams}}
|
||||||
/// <param name="{{GetDotNetNameOpenApiParameter parameter "false"}}">
|
/// <param name="{{GetDotNetNameOpenApiParameter parameter "false"}}">
|
||||||
/// {{ToXmlDoc parameter.description}}
|
/// {{ToXmlDoc parameter.description}}
|
||||||
/// </param>
|
/// </param>
|
||||||
@@ -80,14 +81,14 @@ public partial class {{name}}Client : ResourceClient
|
|||||||
/// A <see cref="CancellationToken"/> which can be used to cancel the asynchronous operation.
|
/// A <see cref="CancellationToken"/> which can be used to cancel the asynchronous operation.
|
||||||
/// </param>
|
/// </param>
|
||||||
public async Task<T> {{GetActionName api.operation name "Async"}}<T>(
|
public async Task<T> {{GetActionName api.operation name "Async"}}<T>(
|
||||||
{{ for parameter in api.operation.parameters}}
|
{{ for parameter in $filteredParams}}
|
||||||
{{GetDotNetTypeOpenApiParameter parameter}} {{GetDotNetNameOpenApiParameter parameter "false"}},
|
{{GetDotNetTypeOpenApiParameter parameter}} {{GetDotNetNameOpenApiParameter parameter "false"}},
|
||||||
{{ end }}
|
{{ end }}
|
||||||
CancellationToken cancellationToken = default(CancellationToken))
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
using (var _result = await Client.{{group}}.{{GetOperationId api.operation "WithHttpMessagesAsync"}}<T>(
|
using (var _result = await Client.{{group}}.{{GetOperationId api.operation "WithHttpMessagesAsync"}}<T>(
|
||||||
{{ for parameter in api.operation.parameters}}
|
{{ for parameter in api.operation.parameters}}
|
||||||
{{GetDotNetNameOpenApiParameter parameter "false"}},
|
{{GetParameterValueForWatch parameter false}},
|
||||||
{{end}}
|
{{end}}
|
||||||
null,
|
null,
|
||||||
cancellationToken).ConfigureAwait(false))
|
cancellationToken).ConfigureAwait(false))
|
||||||
@@ -96,5 +97,69 @@ public partial class {{name}}Client : ResourceClient
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
#if !K8S_AOT
|
||||||
|
{{if IfParamContains api.operation "watch"}}
|
||||||
|
/// <summary>
|
||||||
|
/// Watch {{ToXmlDoc api.operation.description}}
|
||||||
|
/// </summary>
|
||||||
|
{{ for parameter in $filteredParams}}
|
||||||
|
/// <param name="{{GetDotNetNameOpenApiParameter parameter "false"}}">
|
||||||
|
/// {{ToXmlDoc parameter.description}}
|
||||||
|
/// </param>
|
||||||
|
{{ end }}
|
||||||
|
/// <param name="onEvent">Callback when any event raised from api server</param>
|
||||||
|
/// <param name="onError">Callback when any exception was caught during watching</param>
|
||||||
|
/// <param name="onClosed">Callback when the server closes the connection</param>
|
||||||
|
public Watcher<{{GetReturnType api.operation "T"}}> Watch{{GetActionName api.operation name ""}}(
|
||||||
|
{{ for parameter in $filteredParams}}
|
||||||
|
{{GetDotNetTypeOpenApiParameter parameter}} {{GetDotNetNameOpenApiParameter parameter "true"}},
|
||||||
|
{{ end }}
|
||||||
|
Action<WatchEventType, {{GetReturnType api.operation "T"}}> onEvent = null,
|
||||||
|
Action<Exception> onError = null,
|
||||||
|
Action onClosed = null)
|
||||||
|
{
|
||||||
|
if (onEvent == null) throw new ArgumentNullException(nameof(onEvent));
|
||||||
|
|
||||||
|
var responseTask = Client.{{group}}.{{GetOperationId api.operation "WithHttpMessagesAsync"}}(
|
||||||
|
{{ for parameter in api.operation.parameters}}
|
||||||
|
{{GetParameterValueForWatch parameter true}},
|
||||||
|
{{ end }}
|
||||||
|
null,
|
||||||
|
CancellationToken.None);
|
||||||
|
|
||||||
|
return responseTask.Watch<{{GetReturnType api.operation "T"}}, {{GetReturnType api.operation "TList"}}>(
|
||||||
|
onEvent, onError, onClosed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Watch {{ToXmlDoc api.operation.description}} as async enumerable
|
||||||
|
/// </summary>
|
||||||
|
{{ for parameter in $filteredParams}}
|
||||||
|
/// <param name="{{GetDotNetNameOpenApiParameter parameter "false"}}">
|
||||||
|
/// {{ToXmlDoc parameter.description}}
|
||||||
|
/// </param>
|
||||||
|
{{ end }}
|
||||||
|
/// <param name="onError">Callback when any exception was caught during watching</param>
|
||||||
|
/// <param name="cancellationToken">Cancellation token</param>
|
||||||
|
public IAsyncEnumerable<(WatchEventType, {{GetReturnType api.operation "T"}})> Watch{{GetActionName api.operation name "Async"}}(
|
||||||
|
{{ for parameter in $filteredParams}}
|
||||||
|
{{GetDotNetTypeOpenApiParameter parameter}} {{GetDotNetNameOpenApiParameter parameter "true"}},
|
||||||
|
{{ end }}
|
||||||
|
Action<Exception> onError = null,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var responseTask = Client.{{group}}.{{GetOperationId api.operation "WithHttpMessagesAsync"}}(
|
||||||
|
{{ for parameter in api.operation.parameters}}
|
||||||
|
{{GetParameterValueForWatch parameter true}},
|
||||||
|
{{ end }}
|
||||||
|
null,
|
||||||
|
cancellationToken);
|
||||||
|
|
||||||
|
return responseTask.WatchAsync<{{GetReturnType api.operation "T"}}, {{GetReturnType api.operation "TList"}}>(
|
||||||
|
onError, cancellationToken);
|
||||||
|
}
|
||||||
{{end}}
|
{{end}}
|
||||||
}
|
#endif
|
||||||
|
{{end}}
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ namespace k8s;
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial interface IBasicKubernetes
|
public partial interface IKubernetes
|
||||||
{
|
{
|
||||||
{{for group in groups}}
|
{{for group in groups}}
|
||||||
I{{group}}Operations {{group}} { get; }
|
I{{group}}Operations {{group}} { get; }
|
||||||
@@ -4,92 +4,29 @@
|
|||||||
// regenerated.
|
// regenerated.
|
||||||
// </auto-generated>
|
// </auto-generated>
|
||||||
|
|
||||||
namespace k8s.Models
|
namespace k8s.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// {{ToXmlDoc def.description}}
|
||||||
|
/// </summary>
|
||||||
|
{{ if hasExt }}
|
||||||
|
[KubernetesEntity(Group=KubeGroup, Kind=KubeKind, ApiVersion=KubeApiVersion, PluralName=KubePluralName)]
|
||||||
|
{{ end }}
|
||||||
|
public partial {{typ}} {{clz}} {{ if hasExt }} : {{ GetInterfaceName def }} {{ end }}
|
||||||
{
|
{
|
||||||
|
{{ if hasExt}}
|
||||||
|
public const string KubeApiVersion = "{{ GetApiVersion def }}";
|
||||||
|
public const string KubeKind = "{{ GetKind def }}";
|
||||||
|
public const string KubeGroup = "{{ GetGroup def }}";
|
||||||
|
public const string KubePluralName = "{{ GetPlural def }}";
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ for property in properties }}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// {{ToXmlDoc def.description}}
|
/// {{ToXmlDoc property.description}}
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class {{clz}}
|
[JsonPropertyName("{{property.name}}")]
|
||||||
{
|
public {{ if property.IsRequired }} required {{ end }} {{GetDotNetType property}} {{GetDotNetName property.name "field"}} { get; set; }
|
||||||
/// <summary>
|
{{ end }}
|
||||||
/// Initializes a new instance of the {{GetClassName def}} class.
|
|
||||||
/// </summary>
|
|
||||||
public {{clz}}()
|
|
||||||
{
|
|
||||||
CustomInit();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the {{GetClassName def}} class.
|
|
||||||
/// </summary>
|
|
||||||
{{ for property in properties }}
|
|
||||||
{{ if property.IsRequired }}
|
|
||||||
/// <param name="{{GetDotNetName property.name "fieldctor"}}">
|
|
||||||
/// {{ToXmlDoc property.description}}
|
|
||||||
/// </param>
|
|
||||||
{{ end }}
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
{{ for property in properties }}
|
|
||||||
{{ if !property.IsRequired }}
|
|
||||||
/// <param name="{{GetDotNetName property.name "fieldctor"}}">
|
|
||||||
/// {{ToXmlDoc property.description}}
|
|
||||||
/// </param>
|
|
||||||
{{ end }}
|
|
||||||
{{ end }}
|
|
||||||
public {{clz}}({{GetModelCtorParam def}})
|
|
||||||
{
|
|
||||||
{{ for property in properties }}
|
|
||||||
{{GetDotNetName property.name "field"}} = {{GetDotNetName property.name "fieldctor"}};
|
|
||||||
{{ end }}
|
|
||||||
CustomInit();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// An initialization method that performs custom operations like setting defaults
|
|
||||||
/// </summary>
|
|
||||||
partial void CustomInit();
|
|
||||||
|
|
||||||
{{ for property in properties }}
|
|
||||||
/// <summary>
|
|
||||||
/// {{ToXmlDoc property.description}}
|
|
||||||
/// </summary>
|
|
||||||
[JsonPropertyName("{{property.name}}")]
|
|
||||||
public {{GetDotNetType property}} {{GetDotNetName property.name "field"}} { get; set; }
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Validate the object.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public virtual void Validate()
|
|
||||||
{
|
|
||||||
{{ for property in properties }}
|
|
||||||
{{if IfType property "object" }}
|
|
||||||
{{ if property.IsRequired }}
|
|
||||||
if ({{GetDotNetName property.name "field"}} == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException("{{GetDotNetName property.name "field"}}");
|
|
||||||
}
|
|
||||||
{{ end }}
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
{{ for property in properties }}
|
|
||||||
{{if IfType property "object" }}
|
|
||||||
{{GetDotNetName property.name "field"}}?.Validate();
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
{{if IfType property "objectarray" }}
|
|
||||||
if ({{GetDotNetName property.name "field"}} != null){
|
|
||||||
foreach(var obj in {{GetDotNetName property.name "field"}})
|
|
||||||
{
|
|
||||||
obj.Validate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{{ end }}
|
|
||||||
{{ end }}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
// <auto-generated>
|
|
||||||
// Code generated by https://github.com/kubernetes-client/csharp/tree/master/src/LibKubernetesGenerator
|
|
||||||
// Changes may cause incorrect behavior and will be lost if the code is regenerated.
|
|
||||||
// </auto-generated>
|
|
||||||
namespace k8s.Models
|
|
||||||
{
|
|
||||||
{{ for definition in definitions }}
|
|
||||||
[KubernetesEntity(Group=KubeGroup, Kind=KubeKind, ApiVersion=KubeApiVersion, PluralName=KubePluralName)]
|
|
||||||
public partial class {{ GetClassName definition }} : {{ GetInterfaceName definition }}
|
|
||||||
{
|
|
||||||
public const string KubeApiVersion = "{{ GetApiVersion definition }}";
|
|
||||||
public const string KubeKind = "{{ GetKind definition }}";
|
|
||||||
public const string KubeGroup = "{{ GetGroup definition }}";
|
|
||||||
public const string KubePluralName = "{{ GetPlural definition }}";
|
|
||||||
}
|
|
||||||
{{ end }}
|
|
||||||
}
|
|
||||||
@@ -12,26 +12,27 @@ namespace k8s;
|
|||||||
public static partial class {{name}}OperationsExtensions
|
public static partial class {{name}}OperationsExtensions
|
||||||
{
|
{
|
||||||
{{for api in apis }}
|
{{for api in apis }}
|
||||||
|
{{~ $filteredParams = FilterParameters api.operation "watch" ~}}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// {{ToXmlDoc api.operation.description}}
|
/// {{ToXmlDoc api.operation.description}}
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name='operations'>
|
/// <param name='operations'>
|
||||||
/// The operations group for this extension method.
|
/// The operations group for this extension method.
|
||||||
/// </param>
|
/// </param>
|
||||||
{{ for parameter in api.operation.parameters}}
|
{{ for parameter in $filteredParams}}
|
||||||
/// <param name="{{GetDotNetNameOpenApiParameter parameter "false"}}">
|
/// <param name="{{GetDotNetNameOpenApiParameter parameter "false"}}">
|
||||||
/// {{ToXmlDoc api.description}}
|
/// {{ToXmlDoc parameter.description}}
|
||||||
/// </param>
|
/// </param>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
public static {{GetReturnType api.operation "void"}} {{GetOperationId api.operation ""}}(
|
public static {{GetReturnType api.operation "void"}} {{GetOperationId api.operation ""}}(
|
||||||
this I{{name}}Operations operations
|
this I{{name}}Operations operations
|
||||||
{{ for parameter in api.operation.parameters}}
|
{{ for parameter in $filteredParams}}
|
||||||
,{{GetDotNetTypeOpenApiParameter parameter}} {{GetDotNetNameOpenApiParameter parameter "true"}}
|
,{{GetDotNetTypeOpenApiParameter parameter}} {{GetDotNetNameOpenApiParameter parameter "true"}}
|
||||||
{{end}}
|
{{end}}
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
{{GetReturnType api.operation "return"}} operations.{{GetOperationId api.operation "Async"}}(
|
{{GetReturnType api.operation "return"}} operations.{{GetOperationId api.operation "Async"}}(
|
||||||
{{ for parameter in api.operation.parameters}}
|
{{ for parameter in $filteredParams}}
|
||||||
{{GetDotNetNameOpenApiParameter parameter "false"}},
|
{{GetDotNetNameOpenApiParameter parameter "false"}},
|
||||||
{{end}}
|
{{end}}
|
||||||
CancellationToken.None
|
CancellationToken.None
|
||||||
@@ -45,20 +46,20 @@ public static partial class {{name}}OperationsExtensions
|
|||||||
/// <param name='operations'>
|
/// <param name='operations'>
|
||||||
/// The operations group for this extension method.
|
/// The operations group for this extension method.
|
||||||
/// </param>
|
/// </param>
|
||||||
{{ for parameter in api.operation.parameters}}
|
{{ for parameter in $filteredParams}}
|
||||||
/// <param name="{{GetDotNetNameOpenApiParameter parameter "false"}}">
|
/// <param name="{{GetDotNetNameOpenApiParameter parameter "false"}}">
|
||||||
/// {{ToXmlDoc parameter.description}}
|
/// {{ToXmlDoc parameter.description}}
|
||||||
/// </param>
|
/// </param>
|
||||||
{{end}}
|
{{end}}
|
||||||
public static T {{GetOperationId api.operation ""}}<T>(
|
public static T {{GetOperationId api.operation ""}}<T>(
|
||||||
this I{{name}}Operations operations
|
this I{{name}}Operations operations
|
||||||
{{ for parameter in api.operation.parameters}}
|
{{ for parameter in $filteredParams}}
|
||||||
,{{GetDotNetTypeOpenApiParameter parameter}} {{GetDotNetNameOpenApiParameter parameter "true"}}
|
,{{GetDotNetTypeOpenApiParameter parameter}} {{GetDotNetNameOpenApiParameter parameter "true"}}
|
||||||
{{end}}
|
{{end}}
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return operations.{{GetOperationId api.operation "Async"}}<T>(
|
return operations.{{GetOperationId api.operation "Async"}}<T>(
|
||||||
{{ for parameter in api.operation.parameters}}
|
{{ for parameter in $filteredParams}}
|
||||||
{{GetDotNetNameOpenApiParameter parameter "false"}},
|
{{GetDotNetNameOpenApiParameter parameter "false"}},
|
||||||
{{end}}
|
{{end}}
|
||||||
CancellationToken.None
|
CancellationToken.None
|
||||||
@@ -72,7 +73,7 @@ public static partial class {{name}}OperationsExtensions
|
|||||||
/// <param name='operations'>
|
/// <param name='operations'>
|
||||||
/// The operations group for this extension method.
|
/// The operations group for this extension method.
|
||||||
/// </param>
|
/// </param>
|
||||||
{{ for parameter in api.operation.parameters}}
|
{{ for parameter in $filteredParams}}
|
||||||
/// <param name="{{GetDotNetNameOpenApiParameter parameter "false"}}">
|
/// <param name="{{GetDotNetNameOpenApiParameter parameter "false"}}">
|
||||||
/// {{ToXmlDoc parameter.description}}
|
/// {{ToXmlDoc parameter.description}}
|
||||||
/// </param>
|
/// </param>
|
||||||
@@ -82,7 +83,7 @@ public static partial class {{name}}OperationsExtensions
|
|||||||
/// </param>
|
/// </param>
|
||||||
public static async Task{{GetReturnType api.operation "<>"}} {{GetOperationId api.operation "Async"}}(
|
public static async Task{{GetReturnType api.operation "<>"}} {{GetOperationId api.operation "Async"}}(
|
||||||
this I{{name}}Operations operations,
|
this I{{name}}Operations operations,
|
||||||
{{ for parameter in api.operation.parameters}}
|
{{ for parameter in $filteredParams}}
|
||||||
{{GetDotNetTypeOpenApiParameter parameter}} {{GetDotNetNameOpenApiParameter parameter "true"}},
|
{{GetDotNetTypeOpenApiParameter parameter}} {{GetDotNetNameOpenApiParameter parameter "true"}},
|
||||||
{{ end }}
|
{{ end }}
|
||||||
CancellationToken cancellationToken = default(CancellationToken))
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
@@ -90,7 +91,7 @@ public static partial class {{name}}OperationsExtensions
|
|||||||
{{if IfReturnType api.operation "stream"}}
|
{{if IfReturnType api.operation "stream"}}
|
||||||
var _result = await operations.{{GetOperationId api.operation "WithHttpMessagesAsync"}}(
|
var _result = await operations.{{GetOperationId api.operation "WithHttpMessagesAsync"}}(
|
||||||
{{ for parameter in api.operation.parameters}}
|
{{ for parameter in api.operation.parameters}}
|
||||||
{{GetDotNetNameOpenApiParameter parameter "false"}},
|
{{GetParameterValueForWatch parameter false}},
|
||||||
{{end}}
|
{{end}}
|
||||||
null,
|
null,
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
@@ -100,7 +101,7 @@ public static partial class {{name}}OperationsExtensions
|
|||||||
{{if IfReturnType api.operation "obj"}}
|
{{if IfReturnType api.operation "obj"}}
|
||||||
using (var _result = await operations.{{GetOperationId api.operation "WithHttpMessagesAsync"}}(
|
using (var _result = await operations.{{GetOperationId api.operation "WithHttpMessagesAsync"}}(
|
||||||
{{ for parameter in api.operation.parameters}}
|
{{ for parameter in api.operation.parameters}}
|
||||||
{{GetDotNetNameOpenApiParameter parameter "false"}},
|
{{GetParameterValueForWatch parameter false}},
|
||||||
{{end}}
|
{{end}}
|
||||||
null,
|
null,
|
||||||
cancellationToken).ConfigureAwait(false))
|
cancellationToken).ConfigureAwait(false))
|
||||||
@@ -111,7 +112,7 @@ public static partial class {{name}}OperationsExtensions
|
|||||||
{{if IfReturnType api.operation "void"}}
|
{{if IfReturnType api.operation "void"}}
|
||||||
using (var _result = await operations.{{GetOperationId api.operation "WithHttpMessagesAsync"}}(
|
using (var _result = await operations.{{GetOperationId api.operation "WithHttpMessagesAsync"}}(
|
||||||
{{ for parameter in api.operation.parameters}}
|
{{ for parameter in api.operation.parameters}}
|
||||||
{{GetDotNetNameOpenApiParameter parameter "false"}},
|
{{GetParameterValueForWatch parameter false}},
|
||||||
{{end}}
|
{{end}}
|
||||||
null,
|
null,
|
||||||
cancellationToken).ConfigureAwait(false))
|
cancellationToken).ConfigureAwait(false))
|
||||||
@@ -127,7 +128,7 @@ public static partial class {{name}}OperationsExtensions
|
|||||||
/// <param name='operations'>
|
/// <param name='operations'>
|
||||||
/// The operations group for this extension method.
|
/// The operations group for this extension method.
|
||||||
/// </param>
|
/// </param>
|
||||||
{{ for parameter in api.operation.parameters}}
|
{{ for parameter in $filteredParams}}
|
||||||
/// <param name="{{GetDotNetNameOpenApiParameter parameter "false"}}">
|
/// <param name="{{GetDotNetNameOpenApiParameter parameter "false"}}">
|
||||||
/// {{ToXmlDoc parameter.description}}
|
/// {{ToXmlDoc parameter.description}}
|
||||||
/// </param>
|
/// </param>
|
||||||
@@ -137,14 +138,14 @@ public static partial class {{name}}OperationsExtensions
|
|||||||
/// </param>
|
/// </param>
|
||||||
public static async Task<T> {{GetOperationId api.operation "Async"}}<T>(
|
public static async Task<T> {{GetOperationId api.operation "Async"}}<T>(
|
||||||
this I{{name}}Operations operations,
|
this I{{name}}Operations operations,
|
||||||
{{ for parameter in api.operation.parameters}}
|
{{ for parameter in $filteredParams}}
|
||||||
{{GetDotNetTypeOpenApiParameter parameter}} {{GetDotNetNameOpenApiParameter parameter "true"}},
|
{{GetDotNetTypeOpenApiParameter parameter}} {{GetDotNetNameOpenApiParameter parameter "true"}},
|
||||||
{{ end }}
|
{{ end }}
|
||||||
CancellationToken cancellationToken = default(CancellationToken))
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
using (var _result = await operations.{{GetOperationId api.operation "WithHttpMessagesAsync"}}<T>(
|
using (var _result = await operations.{{GetOperationId api.operation "WithHttpMessagesAsync"}}<T>(
|
||||||
{{ for parameter in api.operation.parameters}}
|
{{ for parameter in api.operation.parameters}}
|
||||||
{{GetDotNetNameOpenApiParameter parameter "false"}},
|
{{GetParameterValueForWatch parameter false}},
|
||||||
{{end}}
|
{{end}}
|
||||||
null,
|
null,
|
||||||
cancellationToken).ConfigureAwait(false))
|
cancellationToken).ConfigureAwait(false))
|
||||||
@@ -154,5 +155,77 @@ public static partial class {{name}}OperationsExtensions
|
|||||||
}
|
}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{end}}
|
#if !K8S_AOT
|
||||||
|
{{if IfParamContains api.operation "watch"}}
|
||||||
|
{{~ $filteredParams = FilterParameters api.operation "watch" ~}}
|
||||||
|
/// <summary>
|
||||||
|
/// Watch {{ToXmlDoc api.operation.description}}
|
||||||
|
/// </summary>
|
||||||
|
/// <param name='operations'>
|
||||||
|
/// The operations group for this extension method.
|
||||||
|
/// </param>
|
||||||
|
{{ for parameter in $filteredParams}}
|
||||||
|
/// <param name="{{GetDotNetNameOpenApiParameter parameter "false"}}">
|
||||||
|
/// {{ToXmlDoc parameter.description}}
|
||||||
|
/// </param>
|
||||||
|
{{ end }}
|
||||||
|
/// <param name="onEvent">Callback when any event raised from api server</param>
|
||||||
|
/// <param name="onError">Callback when any exception was caught during watching</param>
|
||||||
|
/// <param name="onClosed">Callback when the server closes the connection</param>
|
||||||
|
public static Watcher<{{GetReturnType api.operation "T"}}> Watch{{GetOperationId api.operation ""}}(
|
||||||
|
this I{{name}}Operations operations,
|
||||||
|
{{ for parameter in $filteredParams}}
|
||||||
|
{{GetDotNetTypeOpenApiParameter parameter}} {{GetDotNetNameOpenApiParameter parameter "true"}},
|
||||||
|
{{end}}
|
||||||
|
Action<WatchEventType, {{GetReturnType api.operation "T"}}> onEvent = null,
|
||||||
|
Action<Exception> onError = null,
|
||||||
|
Action onClosed = null)
|
||||||
|
{
|
||||||
|
if (onEvent == null) throw new ArgumentNullException(nameof(onEvent));
|
||||||
|
|
||||||
|
var responseTask = operations.{{GetOperationId api.operation "WithHttpMessagesAsync"}}(
|
||||||
|
{{ for parameter in api.operation.parameters}}
|
||||||
|
{{GetParameterValueForWatch parameter true}},
|
||||||
|
{{end}}
|
||||||
|
null,
|
||||||
|
CancellationToken.None);
|
||||||
|
|
||||||
|
return responseTask.Watch<{{GetReturnType api.operation "T"}}, {{GetReturnType api.operation "TList"}}>(
|
||||||
|
onEvent, onError, onClosed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Watch {{ToXmlDoc api.operation.description}} as async enumerable
|
||||||
|
/// </summary>
|
||||||
|
/// <param name='operations'>
|
||||||
|
/// The operations group for this extension method.
|
||||||
|
/// </param>
|
||||||
|
{{ for parameter in $filteredParams}}
|
||||||
|
/// <param name="{{GetDotNetNameOpenApiParameter parameter "false"}}">
|
||||||
|
/// {{ToXmlDoc parameter.description}}
|
||||||
|
/// </param>
|
||||||
|
{{ end }}
|
||||||
|
/// <param name="onError">Callback when any exception was caught during watching</param>
|
||||||
|
/// <param name="cancellationToken">Cancellation token</param>
|
||||||
|
public static IAsyncEnumerable<(WatchEventType, {{GetReturnType api.operation "T"}})> Watch{{GetOperationId api.operation "Async"}}(
|
||||||
|
this I{{name}}Operations operations,
|
||||||
|
{{ for parameter in $filteredParams}}
|
||||||
|
{{GetDotNetTypeOpenApiParameter parameter}} {{GetDotNetNameOpenApiParameter parameter "true"}},
|
||||||
|
{{end}}
|
||||||
|
Action<Exception> onError = null,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var responseTask = operations.{{GetOperationId api.operation "WithHttpMessagesAsync"}}(
|
||||||
|
{{ for parameter in api.operation.parameters}}
|
||||||
|
{{GetParameterValueForWatch parameter true}},
|
||||||
|
{{end}}
|
||||||
|
null,
|
||||||
|
cancellationToken);
|
||||||
|
|
||||||
|
return responseTask.WatchAsync<{{GetReturnType api.operation "T"}}, {{GetReturnType api.operation "TList"}}>(
|
||||||
|
onError, cancellationToken);
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
|
#endif
|
||||||
|
{{end}}
|
||||||
|
}
|
||||||
@@ -8,8 +8,8 @@ namespace k8s
|
|||||||
{{ for definition in definitions }}
|
{{ for definition in definitions }}
|
||||||
[JsonSerializable(typeof({{ GetClassName definition }}))]
|
[JsonSerializable(typeof({{ GetClassName definition }}))]
|
||||||
{{ end }}
|
{{ end }}
|
||||||
internal partial class SourceGenerationContext : JsonSerializerContext
|
public partial class SourceGenerationContext : JsonSerializerContext
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
11715
swagger.json
11715
swagger.json
File diff suppressed because it is too large
Load Diff
@@ -172,27 +172,33 @@ namespace k8s.E2E
|
|||||||
using var kubernetes = CreateClient();
|
using var kubernetes = CreateClient();
|
||||||
|
|
||||||
await kubernetes.CoreV1.CreateNamespacedEventAsync(
|
await kubernetes.CoreV1.CreateNamespacedEventAsync(
|
||||||
new Corev1Event(
|
new Corev1Event
|
||||||
new V1ObjectReference(
|
{
|
||||||
"v1alpha1",
|
InvolvedObject = new V1ObjectReference
|
||||||
kind: "Test",
|
{
|
||||||
name: "test",
|
ApiVersion = "v1alpha1",
|
||||||
namespaceProperty: "default",
|
Kind = "Test",
|
||||||
resourceVersion: "1",
|
Name = "test",
|
||||||
uid: "1"),
|
NamespaceProperty = "default",
|
||||||
new V1ObjectMeta()
|
ResourceVersion = "1",
|
||||||
|
Uid = "1",
|
||||||
|
},
|
||||||
|
Metadata = new V1ObjectMeta
|
||||||
{
|
{
|
||||||
GenerateName = "started-",
|
GenerateName = "started-",
|
||||||
},
|
},
|
||||||
action: "STARTED",
|
Action = "STARTED",
|
||||||
type: "Normal",
|
Type = "Normal",
|
||||||
reason: "STARTED",
|
Reason = "STARTED",
|
||||||
message: "Started",
|
Message = "Started",
|
||||||
eventTime: DateTime.Now,
|
EventTime = DateTime.Now,
|
||||||
firstTimestamp: DateTime.Now,
|
FirstTimestamp = DateTime.Now,
|
||||||
lastTimestamp: DateTime.Now,
|
LastTimestamp = DateTime.Now,
|
||||||
reportingComponent: "37",
|
ReportingComponent = "37",
|
||||||
reportingInstance: "38"), "default").ConfigureAwait(false);
|
ReportingInstance = "38",
|
||||||
|
},
|
||||||
|
"default"
|
||||||
|
).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MinikubeFact]
|
[MinikubeFact]
|
||||||
@@ -370,7 +376,7 @@ namespace k8s.E2E
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[MinikubeFact]
|
[MinikubeFact]
|
||||||
public async Task VersionTestAsync()
|
public async Task VersionTestAsync()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -224,13 +224,12 @@ namespace k8s.E2E
|
|||||||
var started = new AsyncManualResetEvent();
|
var started = new AsyncManualResetEvent();
|
||||||
var connectionClosed = new AsyncManualResetEvent();
|
var connectionClosed = new AsyncManualResetEvent();
|
||||||
|
|
||||||
var watcher = kubernetes.BatchV1.ListNamespacedJobWithHttpMessagesAsync(
|
var watcher = kubernetes.BatchV1.WatchListNamespacedJob(
|
||||||
job.Metadata.NamespaceProperty,
|
job.Metadata.NamespaceProperty,
|
||||||
fieldSelector: $"metadata.name={job.Metadata.Name}",
|
fieldSelector: $"metadata.name={job.Metadata.Name}",
|
||||||
resourceVersion: job.Metadata.ResourceVersion,
|
resourceVersion: job.Metadata.ResourceVersion,
|
||||||
timeoutSeconds: 30,
|
timeoutSeconds: 30,
|
||||||
watch: true).Watch<V1Job, V1JobList>(
|
onEvent: (type, source) =>
|
||||||
(type, source) =>
|
|
||||||
{
|
{
|
||||||
Debug.WriteLine($"Watcher 1: {type}, {source}");
|
Debug.WriteLine($"Watcher 1: {type}, {source}");
|
||||||
events.Add(new Tuple<WatchEventType, V1Job>(type, source));
|
events.Add(new Tuple<WatchEventType, V1Job>(type, source));
|
||||||
@@ -250,6 +249,86 @@ namespace k8s.E2E
|
|||||||
new V1DeleteOptions() { PropagationPolicy = "Foreground" }).ConfigureAwait(false);
|
new V1DeleteOptions() { PropagationPolicy = "Foreground" }).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MinikubeFact]
|
||||||
|
public async Task WatcherIntegrationTestAsyncEnumerable()
|
||||||
|
{
|
||||||
|
using var kubernetes = CreateClient();
|
||||||
|
|
||||||
|
var job = await kubernetes.BatchV1.CreateNamespacedJobAsync(
|
||||||
|
new V1Job()
|
||||||
|
{
|
||||||
|
ApiVersion = "batch/v1",
|
||||||
|
Kind = V1Job.KubeKind,
|
||||||
|
Metadata = new V1ObjectMeta() { Name = nameof(WatcherIntegrationTestAsyncEnumerable).ToLowerInvariant() },
|
||||||
|
Spec = new V1JobSpec()
|
||||||
|
{
|
||||||
|
Template = new V1PodTemplateSpec()
|
||||||
|
{
|
||||||
|
Spec = new V1PodSpec()
|
||||||
|
{
|
||||||
|
Containers = new List<V1Container>()
|
||||||
|
{
|
||||||
|
new V1Container()
|
||||||
|
{
|
||||||
|
Image = "ubuntu",
|
||||||
|
Name = "runner",
|
||||||
|
Command = new List<string>() { "/bin/bash", "-c", "--" },
|
||||||
|
Args = new List<string>()
|
||||||
|
{
|
||||||
|
"trap : TERM INT; sleep infinity & wait",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RestartPolicy = "Never",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"default").ConfigureAwait(false);
|
||||||
|
|
||||||
|
var events = new Collection<Tuple<WatchEventType, V1Job>>();
|
||||||
|
|
||||||
|
var started = new AsyncManualResetEvent();
|
||||||
|
var watchCompleted = new AsyncManualResetEvent();
|
||||||
|
|
||||||
|
// Start async enumerable watch in background task to mimic callback behavior
|
||||||
|
var watchTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await foreach (var (type, source) in kubernetes.BatchV1.WatchListNamespacedJobAsync(
|
||||||
|
job.Metadata.NamespaceProperty,
|
||||||
|
fieldSelector: $"metadata.name={job.Metadata.Name}",
|
||||||
|
resourceVersion: job.Metadata.ResourceVersion,
|
||||||
|
timeoutSeconds: 30).ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"AsyncEnumerable Watcher: {type}, {source}");
|
||||||
|
events.Add(new Tuple<WatchEventType, V1Job>(type, source));
|
||||||
|
job = source;
|
||||||
|
started.Set();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Watch exception: {ex.GetType().FullName}: {ex.Message}");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
watchCompleted.Set();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await started.WaitAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
await Task.WhenAny(watchCompleted.WaitAsync(), Task.Delay(TimeSpan.FromMinutes(3))).ConfigureAwait(false);
|
||||||
|
Assert.True(watchCompleted.IsSet);
|
||||||
|
|
||||||
|
var st = await kubernetes.BatchV1.DeleteNamespacedJobAsync(
|
||||||
|
job.Metadata.Name,
|
||||||
|
job.Metadata.NamespaceProperty,
|
||||||
|
new V1DeleteOptions() { PropagationPolicy = "Foreground" }).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
[MinikubeFact]
|
[MinikubeFact]
|
||||||
public void LeaderIntegrationTest()
|
public void LeaderIntegrationTest()
|
||||||
{
|
{
|
||||||
@@ -451,27 +530,33 @@ namespace k8s.E2E
|
|||||||
using var kubernetes = CreateClient();
|
using var kubernetes = CreateClient();
|
||||||
|
|
||||||
await kubernetes.CoreV1.CreateNamespacedEventAsync(
|
await kubernetes.CoreV1.CreateNamespacedEventAsync(
|
||||||
new Corev1Event(
|
new Corev1Event
|
||||||
new V1ObjectReference(
|
{
|
||||||
"v1alpha1",
|
Metadata = new V1ObjectMeta
|
||||||
kind: "Test",
|
|
||||||
name: "test",
|
|
||||||
namespaceProperty: "default",
|
|
||||||
resourceVersion: "1",
|
|
||||||
uid: "1"),
|
|
||||||
new V1ObjectMeta()
|
|
||||||
{
|
{
|
||||||
GenerateName = "started-",
|
GenerateName = "started-",
|
||||||
|
NamespaceProperty = "default",
|
||||||
},
|
},
|
||||||
action: "STARTED",
|
InvolvedObject = new V1ObjectReference
|
||||||
type: "Normal",
|
{
|
||||||
reason: "STARTED",
|
ApiVersion = "v1alpha1",
|
||||||
message: "Started",
|
Kind = "Test",
|
||||||
eventTime: DateTime.Now,
|
Name = "test",
|
||||||
firstTimestamp: DateTime.Now,
|
NamespaceProperty = "default",
|
||||||
lastTimestamp: DateTime.Now,
|
ResourceVersion = "1",
|
||||||
reportingComponent: "37",
|
Uid = "1",
|
||||||
reportingInstance: "38"), "default").ConfigureAwait(false);
|
},
|
||||||
|
Action = "STARTED",
|
||||||
|
Type = "Normal",
|
||||||
|
Reason = "STARTED",
|
||||||
|
Message = "Started",
|
||||||
|
EventTime = DateTime.Now,
|
||||||
|
FirstTimestamp = DateTime.Now,
|
||||||
|
LastTimestamp = DateTime.Now,
|
||||||
|
ReportingComponent = "37",
|
||||||
|
ReportingInstance = "38",
|
||||||
|
},
|
||||||
|
"default").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MinikubeFact]
|
[MinikubeFact]
|
||||||
@@ -873,6 +958,171 @@ namespace k8s.E2E
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MinikubeFact]
|
||||||
|
public async Task V2HorizontalPodAutoscalerTestAsync()
|
||||||
|
{
|
||||||
|
var namespaceParameter = "default";
|
||||||
|
var deploymentName = "k8scsharp-e2e-hpa-deployment";
|
||||||
|
var hpaName = "k8scsharp-e2e-hpa";
|
||||||
|
|
||||||
|
using var client = CreateClient();
|
||||||
|
|
||||||
|
async Task CleanupAsync()
|
||||||
|
{
|
||||||
|
var deleteOptions = new V1DeleteOptions { PropagationPolicy = "Foreground" };
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await client.AutoscalingV2.DeleteNamespacedHorizontalPodAutoscalerAsync(hpaName, namespaceParameter, deleteOptions).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (HttpOperationException e)
|
||||||
|
{
|
||||||
|
if (e.Response?.StatusCode != System.Net.HttpStatusCode.NotFound)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await client.AppsV1.DeleteNamespacedDeploymentAsync(deploymentName, namespaceParameter, deleteOptions).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (HttpOperationException e)
|
||||||
|
{
|
||||||
|
if (e.Response?.StatusCode != System.Net.HttpStatusCode.NotFound)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var attempts = 10;
|
||||||
|
while (attempts-- > 0)
|
||||||
|
{
|
||||||
|
var hpaList = await client.AutoscalingV2.ListNamespacedHorizontalPodAutoscalerAsync(namespaceParameter).ConfigureAwait(false);
|
||||||
|
var deploymentList = await client.AppsV1.ListNamespacedDeploymentAsync(namespaceParameter).ConfigureAwait(false);
|
||||||
|
if (hpaList.Items.All(item => item.Metadata.Name != hpaName) && deploymentList.Items.All(item => item.Metadata.Name != deploymentName))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(2)).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await CleanupAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
var labels = new Dictionary<string, string> { ["app"] = "k8scsharp-hpa" };
|
||||||
|
|
||||||
|
await client.AppsV1.CreateNamespacedDeploymentAsync(
|
||||||
|
new V1Deployment
|
||||||
|
{
|
||||||
|
Metadata = new V1ObjectMeta { Name = deploymentName, Labels = new Dictionary<string, string>(labels) },
|
||||||
|
Spec = new V1DeploymentSpec
|
||||||
|
{
|
||||||
|
Replicas = 1,
|
||||||
|
Selector = new V1LabelSelector { MatchLabels = new Dictionary<string, string>(labels) },
|
||||||
|
Template = new V1PodTemplateSpec
|
||||||
|
{
|
||||||
|
Metadata = new V1ObjectMeta { Labels = new Dictionary<string, string>(labels) },
|
||||||
|
Spec = new V1PodSpec
|
||||||
|
{
|
||||||
|
Containers = new[]
|
||||||
|
{
|
||||||
|
new V1Container
|
||||||
|
{
|
||||||
|
Name = "k8scsharp-hpa",
|
||||||
|
Image = "nginx",
|
||||||
|
Resources = new V1ResourceRequirements
|
||||||
|
{
|
||||||
|
Requests = new Dictionary<string, ResourceQuantity>
|
||||||
|
{
|
||||||
|
{ "cpu", new ResourceQuantity("100m") },
|
||||||
|
{ "memory", new ResourceQuantity("128Mi") },
|
||||||
|
},
|
||||||
|
Limits = new Dictionary<string, ResourceQuantity>
|
||||||
|
{
|
||||||
|
{ "cpu", new ResourceQuantity("200m") },
|
||||||
|
{ "memory", new ResourceQuantity("256Mi") },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
namespaceParameter).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var hpa = new V2HorizontalPodAutoscaler
|
||||||
|
{
|
||||||
|
Metadata = new V1ObjectMeta { Name = hpaName },
|
||||||
|
Spec = new V2HorizontalPodAutoscalerSpec
|
||||||
|
{
|
||||||
|
MinReplicas = 1,
|
||||||
|
MaxReplicas = 3,
|
||||||
|
ScaleTargetRef = new V2CrossVersionObjectReference
|
||||||
|
{
|
||||||
|
ApiVersion = "apps/v1",
|
||||||
|
Kind = "Deployment",
|
||||||
|
Name = deploymentName,
|
||||||
|
},
|
||||||
|
Metrics = new List<V2MetricSpec>
|
||||||
|
{
|
||||||
|
new V2MetricSpec
|
||||||
|
{
|
||||||
|
Type = "Resource",
|
||||||
|
Resource = new V2ResourceMetricSource
|
||||||
|
{
|
||||||
|
Name = "cpu",
|
||||||
|
Target = new V2MetricTarget
|
||||||
|
{
|
||||||
|
Type = "Utilization",
|
||||||
|
AverageUtilization = 50,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
await client.AutoscalingV2.CreateNamespacedHorizontalPodAutoscalerAsync(hpa, namespaceParameter).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var hpaList = await client.AutoscalingV2.ListNamespacedHorizontalPodAutoscalerAsync(namespaceParameter).ConfigureAwait(false);
|
||||||
|
Assert.Contains(hpaList.Items, item => item.Metadata.Name == hpaName);
|
||||||
|
|
||||||
|
var created = await client.AutoscalingV2.ReadNamespacedHorizontalPodAutoscalerAsync(hpaName, namespaceParameter).ConfigureAwait(false);
|
||||||
|
Assert.Equal(1, created.Spec.MinReplicas);
|
||||||
|
|
||||||
|
created.Spec.MinReplicas = 2;
|
||||||
|
await client.AutoscalingV2.ReplaceNamespacedHorizontalPodAutoscalerAsync(created, hpaName, namespaceParameter).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var updated = await client.AutoscalingV2.ReadNamespacedHorizontalPodAutoscalerAsync(hpaName, namespaceParameter).ConfigureAwait(false);
|
||||||
|
Assert.Equal(2, updated.Spec.MinReplicas);
|
||||||
|
|
||||||
|
await client.AutoscalingV2.DeleteNamespacedHorizontalPodAutoscalerAsync(hpaName, namespaceParameter, new V1DeleteOptions { PropagationPolicy = "Foreground" }).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var retries = 10;
|
||||||
|
while (retries-- > 0)
|
||||||
|
{
|
||||||
|
hpaList = await client.AutoscalingV2.ListNamespacedHorizontalPodAutoscalerAsync(namespaceParameter).ConfigureAwait(false);
|
||||||
|
if (hpaList.Items.All(item => item.Metadata.Name != hpaName))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(2)).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.DoesNotContain(hpaList.Items, item => item.Metadata.Name == hpaName);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
await CleanupAsync().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static IKubernetes CreateClient()
|
public static IKubernetes CreateClient()
|
||||||
{
|
{
|
||||||
return new Kubernetes(KubernetesClientConfiguration.BuildDefaultConfig());
|
return new Kubernetes(KubernetesClientConfiguration.BuildDefaultConfig());
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ using Xunit;
|
|||||||
|
|
||||||
namespace k8s.tests;
|
namespace k8s.tests;
|
||||||
|
|
||||||
public class BasicTests
|
public class SimpleTests
|
||||||
{
|
{
|
||||||
// TODO: fail to setup asp.net core 6 on net48
|
// TODO: fail to setup asp.net core 6 on net48
|
||||||
private class DummyHttpServer : System.IDisposable
|
private class DummyHttpServer : System.IDisposable
|
||||||
|
|||||||
@@ -188,10 +188,18 @@ namespace k8s.Tests
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
#if NET9_0_OR_GREATER
|
||||||
|
serverCertificate = X509CertificateLoader.LoadPkcs12(Convert.FromBase64String(serverCertificateData), "");
|
||||||
|
#else
|
||||||
serverCertificate = new X509Certificate2(Convert.FromBase64String(serverCertificateData), "");
|
serverCertificate = new X509Certificate2(Convert.FromBase64String(serverCertificateData), "");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if NET9_0_OR_GREATER
|
||||||
|
var clientCertificate = X509CertificateLoader.LoadCertificate(Convert.FromBase64String(clientCertificateData));
|
||||||
|
#else
|
||||||
var clientCertificate = new X509Certificate2(Convert.FromBase64String(clientCertificateData), "");
|
var clientCertificate = new X509Certificate2(Convert.FromBase64String(clientCertificateData), "");
|
||||||
|
#endif
|
||||||
|
|
||||||
var clientCertificateValidationCalled = false;
|
var clientCertificateValidationCalled = false;
|
||||||
|
|
||||||
@@ -294,10 +302,18 @@ namespace k8s.Tests
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
#if NET9_0_OR_GREATER
|
||||||
|
serverCertificate = X509CertificateLoader.LoadPkcs12(serverCertificateData, "");
|
||||||
|
#else
|
||||||
serverCertificate = new X509Certificate2(serverCertificateData, "");
|
serverCertificate = new X509Certificate2(serverCertificateData, "");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if NET9_0_OR_GREATER
|
||||||
|
var clientCertificate = X509CertificateLoader.LoadCertificate(clientCertificateData);
|
||||||
|
#else
|
||||||
var clientCertificate = new X509Certificate2(clientCertificateData, "");
|
var clientCertificate = new X509Certificate2(clientCertificateData, "");
|
||||||
|
#endif
|
||||||
|
|
||||||
var clientCertificateValidationCalled = false;
|
var clientCertificateValidationCalled = false;
|
||||||
|
|
||||||
|
|||||||
@@ -85,8 +85,13 @@ namespace k8s.Tests
|
|||||||
{
|
{
|
||||||
var certCollection = CertUtils.LoadPemFileCert("assets/ca-bundle.crt");
|
var certCollection = CertUtils.LoadPemFileCert("assets/ca-bundle.crt");
|
||||||
|
|
||||||
|
#if NET9_0_OR_GREATER
|
||||||
|
using var intermediateCert = X509CertificateLoader.LoadCertificateFromFile("assets/ca-bundle-intermediate.crt");
|
||||||
|
using var rootCert = X509CertificateLoader.LoadCertificateFromFile("assets/ca-bundle-root.crt");
|
||||||
|
#else
|
||||||
using var intermediateCert = new X509Certificate2("assets/ca-bundle-intermediate.crt");
|
using var intermediateCert = new X509Certificate2("assets/ca-bundle-intermediate.crt");
|
||||||
using var rootCert = new X509Certificate2("assets/ca-bundle-root.crt");
|
using var rootCert = new X509Certificate2("assets/ca-bundle-root.crt");
|
||||||
|
#endif
|
||||||
|
|
||||||
Assert.Equal(2, certCollection.Count);
|
Assert.Equal(2, certCollection.Count);
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,11 @@ namespace k8s.Tests
|
|||||||
public void ValidCert()
|
public void ValidCert()
|
||||||
{
|
{
|
||||||
var caCert = CertUtils.LoadPemFileCert("assets/ca.crt");
|
var caCert = CertUtils.LoadPemFileCert("assets/ca.crt");
|
||||||
|
#if NET9_0_OR_GREATER
|
||||||
|
var testCert = X509CertificateLoader.LoadCertificateFromFile("assets/ca.crt");
|
||||||
|
#else
|
||||||
var testCert = new X509Certificate2("assets/ca.crt");
|
var testCert = new X509Certificate2("assets/ca.crt");
|
||||||
|
#endif
|
||||||
var chain = new X509Chain();
|
var chain = new X509Chain();
|
||||||
var errors = SslPolicyErrors.RemoteCertificateChainErrors;
|
var errors = SslPolicyErrors.RemoteCertificateChainErrors;
|
||||||
|
|
||||||
@@ -81,7 +85,11 @@ namespace k8s.Tests
|
|||||||
public void InvalidCert()
|
public void InvalidCert()
|
||||||
{
|
{
|
||||||
var caCert = CertUtils.LoadPemFileCert("assets/ca.crt");
|
var caCert = CertUtils.LoadPemFileCert("assets/ca.crt");
|
||||||
|
#if NET9_0_OR_GREATER
|
||||||
|
var testCert = X509CertificateLoader.LoadCertificateFromFile("assets/ca2.crt");
|
||||||
|
#else
|
||||||
var testCert = new X509Certificate2("assets/ca2.crt");
|
var testCert = new X509Certificate2("assets/ca2.crt");
|
||||||
|
#endif
|
||||||
var chain = new X509Chain();
|
var chain = new X509Chain();
|
||||||
var errors = SslPolicyErrors.RemoteCertificateChainErrors;
|
var errors = SslPolicyErrors.RemoteCertificateChainErrors;
|
||||||
|
|
||||||
@@ -110,7 +118,11 @@ namespace k8s.Tests
|
|||||||
public void InvalidBundleCert()
|
public void InvalidBundleCert()
|
||||||
{
|
{
|
||||||
var caCert = CertUtils.LoadPemFileCert("assets/ca-bundle.crt");
|
var caCert = CertUtils.LoadPemFileCert("assets/ca-bundle.crt");
|
||||||
|
#if NET9_0_OR_GREATER
|
||||||
|
var testCert = X509CertificateLoader.LoadCertificateFromFile("assets/ca2.crt");
|
||||||
|
#else
|
||||||
var testCert = new X509Certificate2("assets/ca2.crt");
|
var testCert = new X509Certificate2("assets/ca2.crt");
|
||||||
|
#endif
|
||||||
var chain = new X509Chain();
|
var chain = new X509Chain();
|
||||||
var errors = SslPolicyErrors.RemoteCertificateChainErrors;
|
var errors = SslPolicyErrors.RemoteCertificateChainErrors;
|
||||||
|
|
||||||
|
|||||||
@@ -10,13 +10,13 @@ namespace k8s.Tests
|
|||||||
{
|
{
|
||||||
{
|
{
|
||||||
var v = 123;
|
var v = 123;
|
||||||
IntstrIntOrString intorstr = v;
|
IntOrString intorstr = v;
|
||||||
|
|
||||||
Assert.Equal("123", KubernetesJson.Serialize(intorstr));
|
Assert.Equal("123", KubernetesJson.Serialize(intorstr));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
IntstrIntOrString intorstr = "12%";
|
IntOrString intorstr = "12%";
|
||||||
Assert.Equal("\"12%\"", KubernetesJson.Serialize(intorstr));
|
Assert.Equal("\"12%\"", KubernetesJson.Serialize(intorstr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -25,12 +25,12 @@ namespace k8s.Tests
|
|||||||
public void Deserialize()
|
public void Deserialize()
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
var v = KubernetesJson.Deserialize<IntstrIntOrString>("1234");
|
var v = KubernetesJson.Deserialize<IntOrString>("1234");
|
||||||
Assert.Equal("1234", v.Value);
|
Assert.Equal("1234", v.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var v = KubernetesJson.Deserialize<IntstrIntOrString>("\"12%\"");
|
var v = KubernetesJson.Deserialize<IntOrString>("\"12%\"");
|
||||||
Assert.Equal("12%", v.Value);
|
Assert.Equal("12%", v.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Text.Json;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
using k8s.Models;
|
||||||
|
|
||||||
namespace k8s.Tests;
|
namespace k8s.Tests;
|
||||||
|
|
||||||
@@ -116,4 +118,28 @@ public class KubernetesJsonTests
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ReadWriteDatesJson()
|
||||||
|
{
|
||||||
|
var kManifest = """
|
||||||
|
{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "Secret",
|
||||||
|
"metadata": {
|
||||||
|
"creationTimestamp": "2025-09-03T05:15:53Z",
|
||||||
|
"name": "test-secret"
|
||||||
|
},
|
||||||
|
"type": "Opaque"
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
var objFromJson = KubernetesJson.Deserialize<V1Secret>(kManifest);
|
||||||
|
var jsonFromObj = KubernetesJson.Serialize(objFromJson);
|
||||||
|
|
||||||
|
// Format Json
|
||||||
|
var jsonFromObj2 = JsonSerializer.Serialize(JsonSerializer.Deserialize<JsonElement>(jsonFromObj), new JsonSerializerOptions() { WriteIndented = true });
|
||||||
|
|
||||||
|
Assert.Equal(kManifest, jsonFromObj2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ metadata:
|
|||||||
}
|
}
|
||||||
|
|
||||||
#pragma warning disable CA1812 // Class is used for YAML deserialization tests
|
#pragma warning disable CA1812 // Class is used for YAML deserialization tests
|
||||||
private class MyPod : V1Pod
|
private record MyPod : V1Pod
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
#pragma warning restore CA1812
|
#pragma warning restore CA1812
|
||||||
@@ -531,7 +531,7 @@ spec:
|
|||||||
var obj = new V1Service
|
var obj = new V1Service
|
||||||
{
|
{
|
||||||
Kind = "Service",
|
Kind = "Service",
|
||||||
Metadata = new V1ObjectMeta(labels: labels, name: "test-svc"),
|
Metadata = new V1ObjectMeta { Name = "test-svc", Labels = labels },
|
||||||
ApiVersion = "v1",
|
ApiVersion = "v1",
|
||||||
Spec = new V1ServiceSpec
|
Spec = new V1ServiceSpec
|
||||||
{
|
{
|
||||||
@@ -804,12 +804,12 @@ spec:
|
|||||||
{
|
{
|
||||||
var kManifest = @"
|
var kManifest = @"
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
username: YlhrdFlYQnc=
|
||||||
|
password: TXprMU1qZ2tkbVJuTjBwaQ==
|
||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
name: test-secret
|
name: test-secret
|
||||||
data:
|
|
||||||
username: bXktYXBw
|
|
||||||
password: Mzk1MjgkdmRnN0pi
|
|
||||||
";
|
";
|
||||||
|
|
||||||
var result = KubernetesYaml.Deserialize<V1Secret>(kManifest, true);
|
var result = KubernetesYaml.Deserialize<V1Secret>(kManifest, true);
|
||||||
@@ -823,13 +823,8 @@ data:
|
|||||||
var kManifest = """
|
var kManifest = """
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
data:
|
data:
|
||||||
username: bXktYXBw
|
username: YlhrdFlYQnc=
|
||||||
tls2.crt: |
|
password: TXprMU1qZ2tkbVJuTjBwaQ==
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
FAKE CERT
|
|
||||||
FAKE CERT
|
|
||||||
FAKE CERT
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
name: test-secret
|
name: test-secret
|
||||||
@@ -841,6 +836,45 @@ metadata:
|
|||||||
Assert.Equal(kManifest, yaml);
|
Assert.Equal(kManifest, yaml);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LoadConfigMap()
|
||||||
|
{
|
||||||
|
var kManifest = @"
|
||||||
|
apiVersion: v1
|
||||||
|
binaryData:
|
||||||
|
username: YlhrdFlYQnc=
|
||||||
|
data:
|
||||||
|
password: Mzk1MjgkdmRnN0pi
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: test-configmap
|
||||||
|
";
|
||||||
|
|
||||||
|
var result = KubernetesYaml.Deserialize<V1ConfigMap>(kManifest, true);
|
||||||
|
Assert.Equal("bXktYXBw", Encoding.UTF8.GetString(result.BinaryData["username"]));
|
||||||
|
Assert.Equal("Mzk1MjgkdmRnN0pi", result.Data["password"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void WriteConfigMap()
|
||||||
|
{
|
||||||
|
var kManifest = """
|
||||||
|
apiVersion: v1
|
||||||
|
binaryData:
|
||||||
|
username: YlhrdFlYQnc=
|
||||||
|
data:
|
||||||
|
password: Mzk1MjgkdmRnN0pi
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: test-configmap
|
||||||
|
""";
|
||||||
|
|
||||||
|
var result = KubernetesYaml.Deserialize<V1ConfigMap>(kManifest, true);
|
||||||
|
var yaml = KubernetesYaml.Serialize(result);
|
||||||
|
|
||||||
|
Assert.Equal(kManifest, yaml);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void DeserializeWithJsonPropertyName()
|
public void DeserializeWithJsonPropertyName()
|
||||||
{
|
{
|
||||||
@@ -1136,5 +1170,148 @@ spec:
|
|||||||
CultureInfo.CurrentCulture = old;
|
CultureInfo.CurrentCulture = old;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ReadWriteDatesYaml()
|
||||||
|
{
|
||||||
|
var kManifest = """
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
creationTimestamp: "2025-09-03T05:15:53Z"
|
||||||
|
name: test-secret
|
||||||
|
type: Opaque
|
||||||
|
""";
|
||||||
|
|
||||||
|
var objFromYaml = KubernetesYaml.Deserialize<V1Secret>(kManifest, true);
|
||||||
|
var yamlFromObj = KubernetesYaml.Serialize(objFromYaml);
|
||||||
|
|
||||||
|
Assert.Equal(kManifest, yamlFromObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Build", "CA1812:'KubernetesYamlTests.RfcTime' is an internal class that is apparently never instantiated. If so, remove the code from the assembly. If this class is intended to contain only static members, make it 'static' (Module in Visual Basic). (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1812)", Justification = "json type")]
|
||||||
|
private class RfcTime
|
||||||
|
{
|
||||||
|
public DateTime Rfc3339 { get; set; }
|
||||||
|
public DateTime Rfc3339micro { get; set; }
|
||||||
|
public DateTime Rfc3339nano { get; set; }
|
||||||
|
public DateTime Rfc3339nanolenient1 { get; set; }
|
||||||
|
public DateTime Rfc3339nanolenient2 { get; set; }
|
||||||
|
public DateTime Rfc3339nanolenient3 { get; set; }
|
||||||
|
public DateTime Rfc3339nanolenient4 { get; set; }
|
||||||
|
public DateTime Rfc3339nanolenient5 { get; set; }
|
||||||
|
public DateTime Rfc3339nanolenient6 { get; set; }
|
||||||
|
public DateTime Rfc3339nanolenient7 { get; set; }
|
||||||
|
public DateTime Rfc3339nanolenient8 { get; set; }
|
||||||
|
public DateTime Rfc3339nanolenient9 { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RFC3339()
|
||||||
|
{
|
||||||
|
/* go code to generate the json https://go.dev/play/p/VL95pugm6o8
|
||||||
|
|
||||||
|
const RFC3339Micro = "2006-01-02T15:04:05.000000Z07:00"
|
||||||
|
const RFC3339Nano = "2006-01-02T15:04:05.000000000Z07:00"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
t := time.Now()
|
||||||
|
type Time struct {
|
||||||
|
RFC3339 string `json:"rfc3339"`
|
||||||
|
RFC3339Micro string `json:"rfc3339micro"`
|
||||||
|
RFC3339Nano string `json:"rfc3339nano"`
|
||||||
|
|
||||||
|
RFC3339NanoLenient1 string `json:"rfc3339nanolenient1"`
|
||||||
|
RFC3339NanoLenient2 string `json:"rfc3339nanolenient2"`
|
||||||
|
RFC3339NanoLenient3 string `json:"rfc3339nanolenient3"`
|
||||||
|
RFC3339NanoLenient4 string `json:"rfc3339nanolenient4"`
|
||||||
|
RFC3339NanoLenient5 string `json:"rfc3339nanolenient5"`
|
||||||
|
RFC3339NanoLenient6 string `json:"rfc3339nanolenient6"`
|
||||||
|
RFC3339NanoLenient7 string `json:"rfc3339nanolenient7"`
|
||||||
|
RFC3339NanoLenient8 string `json:"rfc3339nanolenient8"`
|
||||||
|
RFC3339NanoLenient9 string `json:"rfc3339nanolenient9"`
|
||||||
|
}
|
||||||
|
t1 := Time{
|
||||||
|
RFC3339: t.Add(45 * time.Minute).Add(12 * time.Second).Add(123456789 * time.Nanosecond).Format(time.RFC3339),
|
||||||
|
RFC3339Micro: t.Add(45 * time.Minute).Add(12 * time.Second).Add(123456789 * time.Nanosecond).Format(RFC3339Micro),
|
||||||
|
RFC3339Nano: t.Add(24 * time.Minute).Add(56 * time.Second).Add(123456789 * time.Nanosecond).Format(RFC3339Nano),
|
||||||
|
|
||||||
|
RFC3339NanoLenient1: t.Add(100000000 * time.Nanosecond).Format(time.RFC3339Nano),
|
||||||
|
RFC3339NanoLenient2: t.Add(120000000 * time.Nanosecond).Format(time.RFC3339Nano),
|
||||||
|
RFC3339NanoLenient3: t.Add(123000000 * time.Nanosecond).Format(time.RFC3339Nano),
|
||||||
|
RFC3339NanoLenient4: t.Add(123400000 * time.Nanosecond).Format(time.RFC3339Nano),
|
||||||
|
RFC3339NanoLenient5: t.Add(123450000 * time.Nanosecond).Format(time.RFC3339Nano),
|
||||||
|
RFC3339NanoLenient6: t.Add(123456000 * time.Nanosecond).Format(time.RFC3339Nano),
|
||||||
|
RFC3339NanoLenient7: t.Add(123456700 * time.Nanosecond).Format(time.RFC3339Nano),
|
||||||
|
RFC3339NanoLenient8: t.Add(123456780 * time.Nanosecond).Format(time.RFC3339Nano),
|
||||||
|
RFC3339NanoLenient9: t.Add(123456789 * time.Nanosecond).Format(time.RFC3339Nano),
|
||||||
|
}
|
||||||
|
b, err := json.Marshal(t1)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("error:", err)
|
||||||
|
}
|
||||||
|
fmt.Println(string(b))
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
var yaml = """
|
||||||
|
rfc3339: '2009-11-10T23:45:12Z'
|
||||||
|
rfc3339micro: '2009-11-10T23:45:12.123456Z'
|
||||||
|
rfc3339nano: '2009-11-10T23:24:56.123456789Z'
|
||||||
|
rfc3339nanolenient1: '2009-11-10T23:00:00.1Z'
|
||||||
|
rfc3339nanolenient2: '2009-11-10T23:00:00.12Z'
|
||||||
|
rfc3339nanolenient3: '2009-11-10T23:00:00.123Z'
|
||||||
|
rfc3339nanolenient4: '2009-11-10T23:00:00.1234Z'
|
||||||
|
rfc3339nanolenient5: '2009-11-10T23:00:00.12345Z'
|
||||||
|
rfc3339nanolenient6: '2009-11-10T23:00:00.123456Z'
|
||||||
|
rfc3339nanolenient7: '2009-11-10T23:00:00.1234567Z'
|
||||||
|
rfc3339nanolenient8: '2009-11-10T23:00:00.12345678Z'
|
||||||
|
rfc3339nanolenient9: '2009-11-10T23:00:00.123456789Z'
|
||||||
|
""";
|
||||||
|
|
||||||
|
var t = KubernetesYaml.Deserialize<RfcTime>(yaml);
|
||||||
|
|
||||||
|
Assert.Equal(new DateTime(2009, 11, 10, 23, 45, 12, DateTimeKind.Utc), t.Rfc3339);
|
||||||
|
|
||||||
|
Assert.Equal(2009, t.Rfc3339micro.Year);
|
||||||
|
Assert.Equal(11, t.Rfc3339micro.Month);
|
||||||
|
Assert.Equal(10, t.Rfc3339micro.Day);
|
||||||
|
Assert.Equal(23, t.Rfc3339micro.Hour);
|
||||||
|
Assert.Equal(45, t.Rfc3339micro.Minute);
|
||||||
|
Assert.Equal(12, t.Rfc3339micro.Second);
|
||||||
|
Assert.Equal(123, t.Rfc3339micro.Millisecond);
|
||||||
|
|
||||||
|
Assert.Equal(2009, t.Rfc3339nano.Year);
|
||||||
|
Assert.Equal(11, t.Rfc3339nano.Month);
|
||||||
|
Assert.Equal(10, t.Rfc3339nano.Day);
|
||||||
|
Assert.Equal(23, t.Rfc3339nano.Hour);
|
||||||
|
Assert.Equal(24, t.Rfc3339nano.Minute);
|
||||||
|
Assert.Equal(56, t.Rfc3339nano.Second);
|
||||||
|
Assert.Equal(123, t.Rfc3339nano.Millisecond);
|
||||||
|
|
||||||
|
#if NET7_0_OR_GREATER
|
||||||
|
Assert.Equal(456, t.Rfc3339micro.Microsecond);
|
||||||
|
Assert.Equal(456, t.Rfc3339nano.Microsecond);
|
||||||
|
Assert.Equal(700, t.Rfc3339nano.Nanosecond);
|
||||||
|
|
||||||
|
Assert.Equal(100, t.Rfc3339nanolenient1.Millisecond);
|
||||||
|
Assert.Equal(120, t.Rfc3339nanolenient2.Millisecond);
|
||||||
|
Assert.Equal(123, t.Rfc3339nanolenient3.Millisecond);
|
||||||
|
|
||||||
|
Assert.Equal(400, t.Rfc3339nanolenient4.Microsecond);
|
||||||
|
Assert.Equal(450, t.Rfc3339nanolenient5.Microsecond);
|
||||||
|
Assert.Equal(456, t.Rfc3339nanolenient6.Microsecond);
|
||||||
|
|
||||||
|
Assert.Equal(456, t.Rfc3339nanolenient7.Microsecond);
|
||||||
|
Assert.Equal(456, t.Rfc3339nanolenient8.Microsecond);
|
||||||
|
Assert.Equal(456, t.Rfc3339nanolenient9.Microsecond);
|
||||||
|
|
||||||
|
Assert.Equal(700, t.Rfc3339nanolenient7.Nanosecond);
|
||||||
|
Assert.Equal(700, t.Rfc3339nanolenient8.Nanosecond);
|
||||||
|
Assert.Equal(700, t.Rfc3339nanolenient9.Nanosecond);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,24 +54,29 @@ namespace k8s.Tests
|
|||||||
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
|
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
|
||||||
|
|
||||||
// did not pass watch param
|
// did not pass watch param
|
||||||
var listTask = client.CoreV1.ListNamespacedPodWithHttpMessagesAsync("default", watch: true);
|
|
||||||
var onErrorCalled = false;
|
var onErrorCalled = false;
|
||||||
|
|
||||||
using (listTask.Watch<V1Pod, V1PodList>((type, item) => { }, e => { onErrorCalled = true; }))
|
using (var watcher = client.CoreV1.WatchListNamespacedPod(
|
||||||
|
"default",
|
||||||
|
onEvent: (type, item) => { },
|
||||||
|
onError: e => { onErrorCalled = true; }))
|
||||||
{
|
{
|
||||||
await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(true); // delay for onerror to be called
|
await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(true); // delay for onerror to be called
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.True(onErrorCalled);
|
Assert.True(onErrorCalled);
|
||||||
|
|
||||||
|
|
||||||
// server did not response line by line
|
// server did not response line by line
|
||||||
await Assert.ThrowsAnyAsync<Exception>(() =>
|
await Assert.ThrowsAnyAsync<Exception>(() =>
|
||||||
{
|
{
|
||||||
return client.CoreV1.ListNamespacedPodWithHttpMessagesAsync("default", watch: true);
|
using (var testWatcher = client.CoreV1.WatchListNamespacedPod(
|
||||||
|
"default"))
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
// this line did not throw
|
// this line did not throw
|
||||||
// listTask.Watch<Corev1Pod>((type, item) => { });
|
// using (var testWatcher = client.CoreV1.WatchListNamespacedPod("default", onEvent: (type, item) => { }))
|
||||||
}).ConfigureAwait(true);
|
}).ConfigureAwait(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,8 +98,7 @@ namespace k8s.Tests
|
|||||||
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
|
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
|
||||||
|
|
||||||
|
|
||||||
var listTask = client.CoreV1.ListNamespacedPodWithHttpMessagesAsync("default", watch: true);
|
using (var watcher = client.CoreV1.WatchListNamespacedPod("default", onEvent: (type, item) => { eventsReceived.Set(); }))
|
||||||
using (listTask.Watch<V1Pod, V1PodList>((type, item) => { eventsReceived.Set(); }))
|
|
||||||
{
|
{
|
||||||
// here watcher is ready to use, but http server has not responsed yet.
|
// here watcher is ready to use, but http server has not responsed yet.
|
||||||
created.Set();
|
created.Set();
|
||||||
@@ -134,27 +138,26 @@ namespace k8s.Tests
|
|||||||
{
|
{
|
||||||
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
|
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
|
||||||
|
|
||||||
var listTask = await client.CoreV1.ListNamespacedPodWithHttpMessagesAsync("default", watch: true).ConfigureAwait(true);
|
|
||||||
|
|
||||||
var events = new HashSet<WatchEventType>();
|
var events = new HashSet<WatchEventType>();
|
||||||
var errors = 0;
|
var errors = 0;
|
||||||
|
|
||||||
var watcher = listTask.Watch<V1Pod, V1PodList>(
|
var watcher = client.CoreV1.WatchListNamespacedPod(
|
||||||
(type, item) =>
|
"default",
|
||||||
|
onEvent: (type, item) =>
|
||||||
{
|
{
|
||||||
testOutput.WriteLine($"Watcher received '{type}' event.");
|
testOutput.WriteLine($"Watcher received '{type}' event.");
|
||||||
|
|
||||||
events.Add(type);
|
events.Add(type);
|
||||||
eventsReceived.Signal();
|
eventsReceived.Signal();
|
||||||
},
|
},
|
||||||
error =>
|
onError: error =>
|
||||||
{
|
{
|
||||||
testOutput.WriteLine($"Watcher received '{error.GetType().FullName}' error.");
|
testOutput.WriteLine($"Watcher received '{error.GetType().FullName}' error.");
|
||||||
|
|
||||||
errors += 1;
|
errors += 1;
|
||||||
eventsReceived.Signal();
|
eventsReceived.Signal();
|
||||||
},
|
},
|
||||||
connectionClosed.Set);
|
onClosed: connectionClosed.Set);
|
||||||
|
|
||||||
// wait server yields all events
|
// wait server yields all events
|
||||||
await Task.WhenAny(eventsReceived.WaitAsync(), Task.Delay(TestTimeout)).ConfigureAwait(true);
|
await Task.WhenAny(eventsReceived.WaitAsync(), Task.Delay(TestTimeout)).ConfigureAwait(true);
|
||||||
@@ -195,17 +198,16 @@ namespace k8s.Tests
|
|||||||
{
|
{
|
||||||
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
|
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
|
||||||
|
|
||||||
var listTask = await client.CoreV1.ListNamespacedPodWithHttpMessagesAsync("default", watch: true).ConfigureAwait(true);
|
|
||||||
|
|
||||||
var events = new HashSet<WatchEventType>();
|
var events = new HashSet<WatchEventType>();
|
||||||
|
|
||||||
var watcher = listTask.Watch<V1Pod, V1PodList>(
|
var watcher = client.CoreV1.WatchListNamespacedPod(
|
||||||
(type, item) =>
|
"default",
|
||||||
|
onEvent: (type, item) =>
|
||||||
{
|
{
|
||||||
events.Add(type);
|
events.Add(type);
|
||||||
eventsReceived.Signal();
|
eventsReceived.Signal();
|
||||||
},
|
},
|
||||||
error =>
|
onError: error =>
|
||||||
{
|
{
|
||||||
testOutput.WriteLine($"Watcher received '{error.GetType().FullName}' error.");
|
testOutput.WriteLine($"Watcher received '{error.GetType().FullName}' error.");
|
||||||
},
|
},
|
||||||
@@ -255,27 +257,26 @@ namespace k8s.Tests
|
|||||||
{
|
{
|
||||||
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
|
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
|
||||||
|
|
||||||
var listTask = await client.CoreV1.ListNamespacedPodWithHttpMessagesAsync("default", watch: true).ConfigureAwait(true);
|
|
||||||
|
|
||||||
var events = new HashSet<WatchEventType>();
|
var events = new HashSet<WatchEventType>();
|
||||||
var errors = 0;
|
var errors = 0;
|
||||||
|
|
||||||
var watcher = listTask.Watch<V1Pod, V1PodList>(
|
var watcher = client.CoreV1.WatchListNamespacedPod(
|
||||||
(type, item) =>
|
"default",
|
||||||
|
onEvent: (type, item) =>
|
||||||
{
|
{
|
||||||
testOutput.WriteLine($"Watcher received '{type}' event.");
|
testOutput.WriteLine($"Watcher received '{type}' event.");
|
||||||
|
|
||||||
events.Add(type);
|
events.Add(type);
|
||||||
eventsReceived.Signal();
|
eventsReceived.Signal();
|
||||||
},
|
},
|
||||||
error =>
|
onError: error =>
|
||||||
{
|
{
|
||||||
testOutput.WriteLine($"Watcher received '{error.GetType().FullName}' error.");
|
testOutput.WriteLine($"Watcher received '{error.GetType().FullName}' error.");
|
||||||
|
|
||||||
errors += 1;
|
errors += 1;
|
||||||
eventsReceived.Signal();
|
eventsReceived.Signal();
|
||||||
},
|
},
|
||||||
waitForClosed.Set);
|
onClosed: waitForClosed.Set);
|
||||||
|
|
||||||
// wait server yields all events
|
// wait server yields all events
|
||||||
await Task.WhenAny(eventsReceived.WaitAsync(), Task.Delay(TestTimeout)).ConfigureAwait(true);
|
await Task.WhenAny(eventsReceived.WaitAsync(), Task.Delay(TestTimeout)).ConfigureAwait(true);
|
||||||
@@ -324,27 +325,26 @@ namespace k8s.Tests
|
|||||||
{
|
{
|
||||||
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
|
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
|
||||||
|
|
||||||
var listTask = await client.CoreV1.ListNamespacedPodWithHttpMessagesAsync("default", watch: true).ConfigureAwait(true);
|
|
||||||
|
|
||||||
var events = new HashSet<WatchEventType>();
|
var events = new HashSet<WatchEventType>();
|
||||||
var errors = 0;
|
var errors = 0;
|
||||||
|
|
||||||
var watcher = listTask.Watch<V1Pod, V1PodList>(
|
var watcher = client.CoreV1.WatchListNamespacedPod(
|
||||||
(type, item) =>
|
"default",
|
||||||
|
onEvent: (type, item) =>
|
||||||
{
|
{
|
||||||
testOutput.WriteLine($"Watcher received '{type}' event.");
|
testOutput.WriteLine($"Watcher received '{type}' event.");
|
||||||
|
|
||||||
events.Add(type);
|
events.Add(type);
|
||||||
eventsReceived.Signal();
|
eventsReceived.Signal();
|
||||||
},
|
},
|
||||||
error =>
|
onError: error =>
|
||||||
{
|
{
|
||||||
testOutput.WriteLine($"Watcher received '{error.GetType().FullName}' error.");
|
testOutput.WriteLine($"Watcher received '{error.GetType().FullName}' error.");
|
||||||
|
|
||||||
errors += 1;
|
errors += 1;
|
||||||
eventsReceived.Signal();
|
eventsReceived.Signal();
|
||||||
},
|
},
|
||||||
connectionClosed.Set);
|
onClosed: connectionClosed.Set);
|
||||||
|
|
||||||
// wait server yields all events
|
// wait server yields all events
|
||||||
await Task.WhenAny(eventsReceived.WaitAsync(), Task.Delay(TestTimeout)).ConfigureAwait(true);
|
await Task.WhenAny(eventsReceived.WaitAsync(), Task.Delay(TestTimeout)).ConfigureAwait(true);
|
||||||
@@ -386,18 +386,17 @@ namespace k8s.Tests
|
|||||||
{
|
{
|
||||||
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
|
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
|
||||||
|
|
||||||
var listTask = await client.CoreV1.ListNamespacedPodWithHttpMessagesAsync("default", watch: true).ConfigureAwait(true);
|
|
||||||
|
|
||||||
waitForException.Set();
|
waitForException.Set();
|
||||||
Watcher<V1Pod> watcher;
|
Watcher<V1Pod> watcher;
|
||||||
watcher = listTask.Watch<V1Pod, V1PodList>(
|
watcher = client.CoreV1.WatchListNamespacedPod(
|
||||||
(type, item) => { },
|
"default",
|
||||||
e =>
|
onEvent: (type, item) => { },
|
||||||
|
onError: e =>
|
||||||
{
|
{
|
||||||
exceptionCatched = e;
|
exceptionCatched = e;
|
||||||
exceptionReceived.Set();
|
exceptionReceived.Set();
|
||||||
},
|
},
|
||||||
waitForClosed.Set);
|
onClosed: waitForClosed.Set);
|
||||||
|
|
||||||
// wait server down
|
// wait server down
|
||||||
await Task.WhenAny(exceptionReceived.WaitAsync(), Task.Delay(TestTimeout)).ConfigureAwait(true);
|
await Task.WhenAny(exceptionReceived.WaitAsync(), Task.Delay(TestTimeout)).ConfigureAwait(true);
|
||||||
@@ -456,12 +455,11 @@ namespace k8s.Tests
|
|||||||
Assert.False(handler1.Called);
|
Assert.False(handler1.Called);
|
||||||
Assert.False(handler2.Called);
|
Assert.False(handler2.Called);
|
||||||
|
|
||||||
var listTask = await client.CoreV1.ListNamespacedPodWithHttpMessagesAsync("default", watch: true).ConfigureAwait(true);
|
|
||||||
|
|
||||||
var events = new HashSet<WatchEventType>();
|
var events = new HashSet<WatchEventType>();
|
||||||
|
|
||||||
var watcher = listTask.Watch<V1Pod, V1PodList>(
|
var watcher = client.CoreV1.WatchListNamespacedPod(
|
||||||
(type, item) =>
|
"default",
|
||||||
|
onEvent: (type, item) =>
|
||||||
{
|
{
|
||||||
events.Add(type);
|
events.Add(type);
|
||||||
eventsReceived.Signal();
|
eventsReceived.Signal();
|
||||||
@@ -507,7 +505,9 @@ namespace k8s.Tests
|
|||||||
var events = new HashSet<WatchEventType>();
|
var events = new HashSet<WatchEventType>();
|
||||||
var errors = 0;
|
var errors = 0;
|
||||||
|
|
||||||
var watcher = client.CoreV1.ListNamespacedPodWithHttpMessagesAsync("default", fieldSelector: $"metadata.name=${"myPod"}", watch: true).Watch<V1Pod, V1PodList>(
|
var watcher = client.CoreV1.WatchListNamespacedPod(
|
||||||
|
"default",
|
||||||
|
fieldSelector: $"metadata.name=${"myPod"}",
|
||||||
onEvent:
|
onEvent:
|
||||||
(type, item) =>
|
(type, item) =>
|
||||||
{
|
{
|
||||||
@@ -568,7 +568,7 @@ namespace k8s.Tests
|
|||||||
Host = server.Uri.ToString(),
|
Host = server.Uri.ToString(),
|
||||||
HttpClientTimeout = TimeSpan.FromSeconds(5),
|
HttpClientTimeout = TimeSpan.FromSeconds(5),
|
||||||
});
|
});
|
||||||
await client.CoreV1.ListNamespacedPodWithHttpMessagesAsync("default").ConfigureAwait(true);
|
await client.CoreV1.ListNamespacedPodAsync("default").ConfigureAwait(true);
|
||||||
}).ConfigureAwait(true);
|
}).ConfigureAwait(true);
|
||||||
|
|
||||||
// cts
|
// cts
|
||||||
@@ -580,7 +580,7 @@ namespace k8s.Tests
|
|||||||
{
|
{
|
||||||
Host = server.Uri.ToString(),
|
Host = server.Uri.ToString(),
|
||||||
});
|
});
|
||||||
await client.CoreV1.ListNamespacedPodWithHttpMessagesAsync("default", cancellationToken: cts.Token).ConfigureAwait(true);
|
await client.CoreV1.ListNamespacedPodAsync("default", cancellationToken: cts.Token).ConfigureAwait(true);
|
||||||
}).ConfigureAwait(true);
|
}).ConfigureAwait(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -608,7 +608,9 @@ namespace k8s.Tests
|
|||||||
var events = new HashSet<WatchEventType>();
|
var events = new HashSet<WatchEventType>();
|
||||||
var errors = 0;
|
var errors = 0;
|
||||||
|
|
||||||
var watcher = client.CoreV1.ListNamespacedPodWithHttpMessagesAsync("default", fieldSelector: $"metadata.name=${"myPod"}", watch: true).Watch<V1Pod, V1PodList>(
|
var watcher = client.CoreV1.WatchListNamespacedPod(
|
||||||
|
"default",
|
||||||
|
fieldSelector: $"metadata.name=${"myPod"}",
|
||||||
onEvent:
|
onEvent:
|
||||||
(type, item) =>
|
(type, item) =>
|
||||||
{
|
{
|
||||||
@@ -656,7 +658,6 @@ namespace k8s.Tests
|
|||||||
httpContext.Response.StatusCode = 200;
|
httpContext.Response.StatusCode = 200;
|
||||||
await httpContext.Response.Body.FlushAsync().ConfigureAwait(true);
|
await httpContext.Response.Body.FlushAsync().ConfigureAwait(true);
|
||||||
await Task.Delay(TimeSpan.FromSeconds(5)).ConfigureAwait(true); // The default timeout is 100 seconds
|
await Task.Delay(TimeSpan.FromSeconds(5)).ConfigureAwait(true); // The default timeout is 100 seconds
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}, resp: ""))
|
}, resp: ""))
|
||||||
{
|
{
|
||||||
@@ -667,8 +668,10 @@ namespace k8s.Tests
|
|||||||
|
|
||||||
await Assert.ThrowsAnyAsync<OperationCanceledException>(async () =>
|
await Assert.ThrowsAnyAsync<OperationCanceledException>(async () =>
|
||||||
{
|
{
|
||||||
await client.CoreV1.ListNamespacedPodWithHttpMessagesAsync("default", watch: true,
|
using var watcher = client.CoreV1.WatchListNamespacedPod(
|
||||||
cancellationToken: cts.Token).ConfigureAwait(true);
|
"default",
|
||||||
|
onEvent: (type, item) => { });
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(5), cts.Token).ConfigureAwait(true);
|
||||||
}).ConfigureAwait(true);
|
}).ConfigureAwait(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -742,8 +745,288 @@ namespace k8s.Tests
|
|||||||
new KubernetesClientConfiguration { Host = server.Uri.ToString() }, handler);
|
new KubernetesClientConfiguration { Host = server.Uri.ToString() }, handler);
|
||||||
|
|
||||||
Assert.Null(handler.Version);
|
Assert.Null(handler.Version);
|
||||||
await client.CoreV1.ListNamespacedPodWithHttpMessagesAsync("default", watch: true).ConfigureAwait(true);
|
using var watcher = client.CoreV1.WatchListNamespacedPod("default", onEvent: (type, item) => { });
|
||||||
Assert.Equal(HttpVersion.Version20, handler.Version);
|
Assert.Equal(HttpVersion.Version20, handler.Version);
|
||||||
|
await Task.CompletedTask.ConfigureAwait(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AsyncEnumerableWatchAllEvents()
|
||||||
|
{
|
||||||
|
var eventsReceived = new AsyncCountdownEvent(4);
|
||||||
|
var serverShutdown = new AsyncManualResetEvent();
|
||||||
|
var watchCompleted = new AsyncManualResetEvent();
|
||||||
|
|
||||||
|
using (var server = new MockKubeApiServer(testOutput, async httpContext =>
|
||||||
|
{
|
||||||
|
await WriteStreamLine(httpContext, MockAddedEventStreamLine).ConfigureAwait(true);
|
||||||
|
await WriteStreamLine(httpContext, MockDeletedStreamLine).ConfigureAwait(true);
|
||||||
|
await WriteStreamLine(httpContext, MockModifiedStreamLine).ConfigureAwait(true);
|
||||||
|
await WriteStreamLine(httpContext, MockErrorStreamLine).ConfigureAwait(true);
|
||||||
|
|
||||||
|
// make server alive, cannot set to int.max as of it would block response
|
||||||
|
await serverShutdown.WaitAsync().ConfigureAwait(true);
|
||||||
|
return false;
|
||||||
|
}))
|
||||||
|
{
|
||||||
|
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
|
||||||
|
|
||||||
|
var events = new HashSet<WatchEventType>();
|
||||||
|
var errors = 0;
|
||||||
|
|
||||||
|
// Start async enumerable watch in background task
|
||||||
|
var watchTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await foreach (var (type, item) in client.CoreV1.WatchListNamespacedPodAsync("default").ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
testOutput.WriteLine($"AsyncEnumerable Watcher received '{type}' event.");
|
||||||
|
events.Add(type);
|
||||||
|
eventsReceived.Signal();
|
||||||
|
|
||||||
|
// Break when we have all expected events
|
||||||
|
if (events.Count >= 4)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
testOutput.WriteLine($"AsyncEnumerable Watcher received exception: {ex.GetType().FullName}");
|
||||||
|
errors++;
|
||||||
|
eventsReceived.Signal();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
watchCompleted.Set();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// wait server yields all events
|
||||||
|
await Task.WhenAny(eventsReceived.WaitAsync(), Task.Delay(TestTimeout)).ConfigureAwait(true);
|
||||||
|
|
||||||
|
Assert.True(
|
||||||
|
eventsReceived.CurrentCount == 0,
|
||||||
|
"Timed out waiting for all events / errors to be received.");
|
||||||
|
|
||||||
|
Assert.Contains(WatchEventType.Added, events);
|
||||||
|
Assert.Contains(WatchEventType.Deleted, events);
|
||||||
|
Assert.Contains(WatchEventType.Modified, events);
|
||||||
|
Assert.Contains(WatchEventType.Error, events);
|
||||||
|
|
||||||
|
Assert.Equal(0, errors);
|
||||||
|
|
||||||
|
serverShutdown.Set();
|
||||||
|
|
||||||
|
await Task.WhenAny(watchCompleted.WaitAsync(), Task.Delay(TestTimeout)).ConfigureAwait(true);
|
||||||
|
Assert.True(watchCompleted.IsSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AsyncEnumerableWatchWithCancellation()
|
||||||
|
{
|
||||||
|
var eventsReceived = new AsyncCountdownEvent(2);
|
||||||
|
var serverShutdown = new AsyncManualResetEvent();
|
||||||
|
|
||||||
|
using (var server = new MockKubeApiServer(testOutput, async httpContext =>
|
||||||
|
{
|
||||||
|
await WriteStreamLine(httpContext, MockAddedEventStreamLine).ConfigureAwait(true);
|
||||||
|
await WriteStreamLine(httpContext, MockModifiedStreamLine).ConfigureAwait(true);
|
||||||
|
|
||||||
|
// Keep server alive
|
||||||
|
await serverShutdown.WaitAsync().ConfigureAwait(true);
|
||||||
|
return false;
|
||||||
|
}))
|
||||||
|
{
|
||||||
|
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
|
||||||
|
|
||||||
|
var events = new HashSet<WatchEventType>();
|
||||||
|
var cts = new CancellationTokenSource();
|
||||||
|
|
||||||
|
var watchTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await foreach (var (type, item) in client.CoreV1.WatchListNamespacedPodAsync("default", cancellationToken: cts.Token).ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
testOutput.WriteLine($"AsyncEnumerable Watcher received '{type}' event.");
|
||||||
|
events.Add(type);
|
||||||
|
eventsReceived.Signal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
testOutput.WriteLine("AsyncEnumerable Watcher was cancelled as expected.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait for some events to be received
|
||||||
|
await Task.WhenAny(eventsReceived.WaitAsync(), Task.Delay(TestTimeout)).ConfigureAwait(true);
|
||||||
|
|
||||||
|
Assert.True(
|
||||||
|
eventsReceived.CurrentCount == 0,
|
||||||
|
"Timed out waiting for events to be received.");
|
||||||
|
|
||||||
|
Assert.Contains(WatchEventType.Added, events);
|
||||||
|
Assert.Contains(WatchEventType.Modified, events);
|
||||||
|
|
||||||
|
// Cancel the watch
|
||||||
|
cts.Cancel();
|
||||||
|
|
||||||
|
// Wait for watch task to complete
|
||||||
|
await Task.WhenAny(watchTask, Task.Delay(TimeSpan.FromSeconds(5))).ConfigureAwait(true);
|
||||||
|
Assert.True(watchTask.IsCompletedSuccessfully || watchTask.IsCanceled);
|
||||||
|
|
||||||
|
serverShutdown.Set();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AsyncEnumerableWatchWithFieldSelector()
|
||||||
|
{
|
||||||
|
var eventsReceived = new AsyncCountdownEvent(3);
|
||||||
|
var serverShutdown = new AsyncManualResetEvent();
|
||||||
|
var watchCompleted = new AsyncManualResetEvent();
|
||||||
|
|
||||||
|
using (var server = new MockKubeApiServer(testOutput, async httpContext =>
|
||||||
|
{
|
||||||
|
await WriteStreamLine(httpContext, MockAddedEventStreamLine).ConfigureAwait(true);
|
||||||
|
await WriteStreamLine(httpContext, MockDeletedStreamLine).ConfigureAwait(true);
|
||||||
|
await WriteStreamLine(httpContext, MockModifiedStreamLine).ConfigureAwait(true);
|
||||||
|
|
||||||
|
await serverShutdown.WaitAsync().ConfigureAwait(true);
|
||||||
|
return false;
|
||||||
|
}))
|
||||||
|
{
|
||||||
|
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
|
||||||
|
|
||||||
|
var events = new List<(WatchEventType, V1Pod)>();
|
||||||
|
|
||||||
|
var watchTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await foreach (var (type, item) in client.CoreV1.WatchListNamespacedPodAsync(
|
||||||
|
"default",
|
||||||
|
fieldSelector: $"metadata.name={"testPod"}").ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
testOutput.WriteLine($"AsyncEnumerable Watcher received '{type}' event for pod '{item?.Metadata?.Name}'.");
|
||||||
|
events.Add((type, item));
|
||||||
|
eventsReceived.Signal();
|
||||||
|
|
||||||
|
if (events.Count >= 3)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
testOutput.WriteLine($"AsyncEnumerable Watcher received exception: {ex.GetType().FullName}");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
watchCompleted.Set();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait for events
|
||||||
|
await Task.WhenAny(eventsReceived.WaitAsync(), Task.Delay(TestTimeout)).ConfigureAwait(true);
|
||||||
|
|
||||||
|
Assert.True(
|
||||||
|
eventsReceived.CurrentCount == 0,
|
||||||
|
"Timed out waiting for all events to be received.");
|
||||||
|
|
||||||
|
Assert.Equal(3, events.Count);
|
||||||
|
Assert.Contains(events, e => e.Item1 == WatchEventType.Added);
|
||||||
|
Assert.Contains(events, e => e.Item1 == WatchEventType.Deleted);
|
||||||
|
Assert.Contains(events, e => e.Item1 == WatchEventType.Modified);
|
||||||
|
|
||||||
|
serverShutdown.Set();
|
||||||
|
|
||||||
|
await Task.WhenAny(watchCompleted.WaitAsync(), Task.Delay(TestTimeout)).ConfigureAwait(true);
|
||||||
|
Assert.True(watchCompleted.IsSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AsyncEnumerableWatchErrorHandling()
|
||||||
|
{
|
||||||
|
var eventsReceived = new AsyncCountdownEvent(3);
|
||||||
|
var serverShutdown = new AsyncManualResetEvent();
|
||||||
|
var watchCompleted = new AsyncManualResetEvent();
|
||||||
|
var errorReceived = new AsyncManualResetEvent();
|
||||||
|
|
||||||
|
using (var server = new MockKubeApiServer(testOutput, async httpContext =>
|
||||||
|
{
|
||||||
|
await WriteStreamLine(httpContext, MockKubeApiServer.MockPodResponse).ConfigureAwait(true);
|
||||||
|
await WriteStreamLine(httpContext, MockBadStreamLine).ConfigureAwait(true);
|
||||||
|
await WriteStreamLine(httpContext, MockAddedEventStreamLine).ConfigureAwait(true);
|
||||||
|
|
||||||
|
await serverShutdown.WaitAsync().ConfigureAwait(true);
|
||||||
|
return false;
|
||||||
|
}))
|
||||||
|
{
|
||||||
|
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
|
||||||
|
|
||||||
|
var events = new List<(WatchEventType, V1Pod)>();
|
||||||
|
var errorCaught = false;
|
||||||
|
|
||||||
|
var watchTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await foreach (var (type, item) in client.CoreV1.WatchListNamespacedPodAsync(
|
||||||
|
"default",
|
||||||
|
onError: ex =>
|
||||||
|
{
|
||||||
|
testOutput.WriteLine($"AsyncEnumerable Watcher onError called: {ex.GetType().FullName}");
|
||||||
|
errorCaught = true;
|
||||||
|
errorReceived.Set();
|
||||||
|
eventsReceived.Signal();
|
||||||
|
}).ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
testOutput.WriteLine($"AsyncEnumerable Watcher received '{type}' event.");
|
||||||
|
events.Add((type, item));
|
||||||
|
eventsReceived.Signal();
|
||||||
|
|
||||||
|
// Expect some valid events plus error handling
|
||||||
|
if (events.Count >= 2)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
testOutput.WriteLine($"AsyncEnumerable Watcher caught exception: {ex.GetType().FullName}");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
watchCompleted.Set();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait for events and errors
|
||||||
|
await Task.WhenAny(eventsReceived.WaitAsync(), Task.Delay(TestTimeout)).ConfigureAwait(true);
|
||||||
|
|
||||||
|
Assert.True(
|
||||||
|
eventsReceived.CurrentCount == 0,
|
||||||
|
"Timed out waiting for events and errors to be received.");
|
||||||
|
|
||||||
|
// Should have received at least one valid event and one error
|
||||||
|
Assert.True(events.Count >= 1, "Should have received at least one valid event");
|
||||||
|
Assert.True(errorCaught, "Should have caught parsing error");
|
||||||
|
Assert.True(errorReceived.IsSet, "Error callback should have been called");
|
||||||
|
|
||||||
|
serverShutdown.Set();
|
||||||
|
|
||||||
|
await Task.WhenAny(watchCompleted.WaitAsync(), Task.Delay(TestTimeout)).ConfigureAwait(true);
|
||||||
|
Assert.True(watchCompleted.IsSet);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
|
"$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
|
||||||
"version": "17.0",
|
"version": "18.0",
|
||||||
"publicReleaseRefSpec": [
|
"publicReleaseRefSpec": [
|
||||||
"^refs/heads/master$",
|
"^refs/heads/master$",
|
||||||
"^refs/tags/v\\d+\\.\\d+\\.\\d+"
|
"^refs/tags/v\\d+\\.\\d+\\.\\d+"
|
||||||
|
|||||||
Reference in New Issue
Block a user