From 0dfabb7d89b3cbb606fcce3ed79ff2064bd0e0f9 Mon Sep 17 00:00:00 2001 From: Boshi LIAN Date: Wed, 10 Sep 2025 01:49:41 -0700 Subject: [PATCH 01/13] migrate to record --- .../KubernetesClient.Aot.csproj | 3 +- .../KubernetesClient.Classic.csproj | 3 +- src/KubernetesClient/IValidate.cs | 13 -- .../{IntstrIntOrString.cs => IntOrString.cs} | 20 +-- .../Models/IntOrStringJsonConverter.cs | 6 +- .../Models/IntOrStringYamlConverter.cs | 6 +- src/KubernetesClient/Models/KubernetesList.cs | 19 --- .../Models/ModelVersionConverter.cs | 21 ---- .../Models/ResourceQuantityJsonConverter.cs | 6 +- .../Models/ResourceQuantityYamlConverter.cs | 2 +- src/KubernetesClient/Models/V1Patch.cs | 2 +- .../Models/V1PodTemplateSpec.cs | 2 +- .../Models/V1Status.ObjectView.cs | 2 +- src/KubernetesClient/Models/V1Status.cs | 2 +- src/LibKubernetesGenerator/ClassNameHelper.cs | 6 + .../GeneralNameHelper.cs | 4 +- .../KubernetesClientSourceGenerator.cs | 4 - .../ModelExtGenerator.cs | 36 ------ src/LibKubernetesGenerator/ModelGenerator.cs | 26 ++++ .../VersionConverterStubGenerator.cs | 70 ----------- .../templates/Model.cs.template | 114 +++++------------- .../templates/ModelExtensions.cs.template | 17 --- .../IntOrStringTests.cs | 8 +- 23 files changed, 95 insertions(+), 297 deletions(-) delete mode 100644 src/KubernetesClient/IValidate.cs rename src/KubernetesClient/Models/{IntstrIntOrString.cs => IntOrString.cs} (57%) delete mode 100644 src/KubernetesClient/Models/ModelVersionConverter.cs delete mode 100644 src/LibKubernetesGenerator/ModelExtGenerator.cs delete mode 100644 src/LibKubernetesGenerator/VersionConverterStubGenerator.cs delete mode 100644 src/LibKubernetesGenerator/templates/ModelExtensions.cs.template diff --git a/src/KubernetesClient.Aot/KubernetesClient.Aot.csproj b/src/KubernetesClient.Aot/KubernetesClient.Aot.csproj index ced074146..602f6d7c8 100644 --- a/src/KubernetesClient.Aot/KubernetesClient.Aot.csproj +++ b/src/KubernetesClient.Aot/KubernetesClient.Aot.csproj @@ -25,10 +25,9 @@ - + - diff --git a/src/KubernetesClient.Classic/KubernetesClient.Classic.csproj b/src/KubernetesClient.Classic/KubernetesClient.Classic.csproj index 254e745f3..a09fb95f5 100644 --- a/src/KubernetesClient.Classic/KubernetesClient.Classic.csproj +++ b/src/KubernetesClient.Classic/KubernetesClient.Classic.csproj @@ -27,10 +27,9 @@ - + - diff --git a/src/KubernetesClient/IValidate.cs b/src/KubernetesClient/IValidate.cs deleted file mode 100644 index c81f553f2..000000000 --- a/src/KubernetesClient/IValidate.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace k8s -{ - /// - /// Object that allows self validation - /// - public interface IValidate - { - /// - /// Validate the object. - /// - void Validate(); - } -} diff --git a/src/KubernetesClient/Models/IntstrIntOrString.cs b/src/KubernetesClient/Models/IntOrString.cs similarity index 57% rename from src/KubernetesClient/Models/IntstrIntOrString.cs rename to src/KubernetesClient/Models/IntOrString.cs index 6f378df9b..39141e890 100644 --- a/src/KubernetesClient/Models/IntstrIntOrString.cs +++ b/src/KubernetesClient/Models/IntOrString.cs @@ -1,29 +1,29 @@ namespace k8s.Models { [JsonConverter(typeof(IntOrStringJsonConverter))] - public partial class IntstrIntOrString + public partial class IntOrString { - public static implicit operator IntstrIntOrString(int v) + public static implicit operator IntOrString(int v) { - return new IntstrIntOrString(Convert.ToString(v)); + return new IntOrString { Value = Convert.ToString(v) }; } - public static implicit operator IntstrIntOrString(long v) + public static implicit operator IntOrString(long v) { - return new IntstrIntOrString(Convert.ToString(v)); + return new IntOrString { Value = Convert.ToString(v) }; } - public static implicit operator string(IntstrIntOrString v) + public static implicit operator string(IntOrString v) { return v?.Value; } - public static implicit operator IntstrIntOrString(string v) + public static implicit operator IntOrString(string v) { - return new IntstrIntOrString(v); + return new IntOrString { Value = v }; } - protected bool Equals(IntstrIntOrString other) + protected bool Equals(IntOrString other) { return string.Equals(Value, other?.Value); } @@ -45,7 +45,7 @@ public override bool Equals(object obj) return false; } - return Equals((IntstrIntOrString)obj); + return Equals((IntOrString)obj); } public override int GetHashCode() diff --git a/src/KubernetesClient/Models/IntOrStringJsonConverter.cs b/src/KubernetesClient/Models/IntOrStringJsonConverter.cs index 9b665a30c..9880a35a4 100644 --- a/src/KubernetesClient/Models/IntOrStringJsonConverter.cs +++ b/src/KubernetesClient/Models/IntOrStringJsonConverter.cs @@ -1,8 +1,8 @@ namespace k8s.Models { - internal sealed class IntOrStringJsonConverter : JsonConverter + internal sealed class IntOrStringJsonConverter : JsonConverter { - 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) { @@ -17,7 +17,7 @@ public override IntstrIntOrString Read(ref Utf8JsonReader reader, Type typeToCon 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) { diff --git a/src/KubernetesClient/Models/IntOrStringYamlConverter.cs b/src/KubernetesClient/Models/IntOrStringYamlConverter.cs index 49116bd6c..248934bac 100644 --- a/src/KubernetesClient/Models/IntOrStringYamlConverter.cs +++ b/src/KubernetesClient/Models/IntOrStringYamlConverter.cs @@ -7,7 +7,7 @@ public class IntOrStringYamlConverter : IYamlTypeConverter { public bool Accepts(Type type) { - return type == typeof(IntstrIntOrString); + return type == typeof(IntOrString); } public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) @@ -21,7 +21,7 @@ public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeseria return null; } - return new IntstrIntOrString(scalar?.Value); + return new IntOrString { Value = scalar?.Value }; } finally { @@ -34,7 +34,7 @@ public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeseria 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)); } } diff --git a/src/KubernetesClient/Models/KubernetesList.cs b/src/KubernetesClient/Models/KubernetesList.cs index 5a1e326de..069c410b2 100644 --- a/src/KubernetesClient/Models/KubernetesList.cs +++ b/src/KubernetesClient/Models/KubernetesList.cs @@ -40,24 +40,5 @@ public KubernetesList(IList items, string apiVersion = default, string kind = /// [JsonPropertyName("metadata")] public V1ListMeta Metadata { get; set; } - - /// - /// Validate the object. - /// - public void Validate() - { - if (Items == null) - { - throw new ArgumentNullException("Items"); - } - - if (Items != null) - { - foreach (var element in Items.OfType()) - { - element.Validate(); - } - } - } } } diff --git a/src/KubernetesClient/Models/ModelVersionConverter.cs b/src/KubernetesClient/Models/ModelVersionConverter.cs deleted file mode 100644 index e48f39787..000000000 --- a/src/KubernetesClient/Models/ModelVersionConverter.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace k8s.Models; - -public static class ModelVersionConverter -{ - public interface IModelVersionConverter - { - TTo Convert(TFrom from); - } - - public static IModelVersionConverter Converter { get; set; } - - internal static TTo Convert(TFrom from) - { - if (Converter == null) - { - throw new InvalidOperationException("Converter is not set"); - } - - return Converter.Convert(from); - } -} diff --git a/src/KubernetesClient/Models/ResourceQuantityJsonConverter.cs b/src/KubernetesClient/Models/ResourceQuantityJsonConverter.cs index 613208679..7ce0df78b 100644 --- a/src/KubernetesClient/Models/ResourceQuantityJsonConverter.cs +++ b/src/KubernetesClient/Models/ResourceQuantityJsonConverter.cs @@ -8,16 +8,16 @@ public override ResourceQuantity Read(ref Utf8JsonReader reader, Type typeToConv switch (reader.TokenType) { case JsonTokenType.Null: - return new ResourceQuantity(null); + return new ResourceQuantity { Value = null }; case JsonTokenType.Number: if (reader.TryGetDouble(out var val)) { - return new ResourceQuantity(Convert.ToString(val)); + return new ResourceQuantity { Value = Convert.ToString(val) }; } return reader.GetDecimal(); default: - return new ResourceQuantity(reader.GetString()); + return new ResourceQuantity { Value = reader.GetString() }; } } diff --git a/src/KubernetesClient/Models/ResourceQuantityYamlConverter.cs b/src/KubernetesClient/Models/ResourceQuantityYamlConverter.cs index ce0ec8e54..cdc07f25e 100644 --- a/src/KubernetesClient/Models/ResourceQuantityYamlConverter.cs +++ b/src/KubernetesClient/Models/ResourceQuantityYamlConverter.cs @@ -21,7 +21,7 @@ public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeseria return null; } - return new ResourceQuantity(scalar?.Value); + return new ResourceQuantity { Value = scalar?.Value }; } finally { diff --git a/src/KubernetesClient/Models/V1Patch.cs b/src/KubernetesClient/Models/V1Patch.cs index 4f8f44c50..1763ccc38 100644 --- a/src/KubernetesClient/Models/V1Patch.cs +++ b/src/KubernetesClient/Models/V1Patch.cs @@ -1,7 +1,7 @@ namespace k8s.Models { [JsonConverter(typeof(V1PatchJsonConverter))] - public partial class V1Patch + public partial record class V1Patch { public enum PatchType { diff --git a/src/KubernetesClient/Models/V1PodTemplateSpec.cs b/src/KubernetesClient/Models/V1PodTemplateSpec.cs index 0462b5373..4efa3cdbb 100644 --- a/src/KubernetesClient/Models/V1PodTemplateSpec.cs +++ b/src/KubernetesClient/Models/V1PodTemplateSpec.cs @@ -4,7 +4,7 @@ namespace k8s.Models /// Partial implementation of the IMetadata interface /// to open this class up to ModelExtensions methods /// - public partial class V1PodTemplateSpec : IMetadata + public partial record V1PodTemplateSpec : IMetadata { } } diff --git a/src/KubernetesClient/Models/V1Status.ObjectView.cs b/src/KubernetesClient/Models/V1Status.ObjectView.cs index 6f5a1ae85..aab834a95 100644 --- a/src/KubernetesClient/Models/V1Status.ObjectView.cs +++ b/src/KubernetesClient/Models/V1Status.ObjectView.cs @@ -1,6 +1,6 @@ namespace k8s.Models { - public partial class V1Status + public partial record V1Status { internal sealed class V1StatusObjectViewConverter : JsonConverter { diff --git a/src/KubernetesClient/Models/V1Status.cs b/src/KubernetesClient/Models/V1Status.cs index 87e4be055..263673c8a 100644 --- a/src/KubernetesClient/Models/V1Status.cs +++ b/src/KubernetesClient/Models/V1Status.cs @@ -2,7 +2,7 @@ namespace k8s.Models { - public partial class V1Status + public partial record class V1Status { /// Converts a object into a short description of the status. /// string description of the status diff --git a/src/LibKubernetesGenerator/ClassNameHelper.cs b/src/LibKubernetesGenerator/ClassNameHelper.cs index a307a160f..a3b012f29 100644 --- a/src/LibKubernetesGenerator/ClassNameHelper.cs +++ b/src/LibKubernetesGenerator/ClassNameHelper.cs @@ -78,6 +78,12 @@ public string GetClassNameForSchemaDefinition(JsonSchema definition) } + if (definition.Format == "int-or-string") + { + return "IntOrString"; + } + + return schemaToNameMapCooked[definition]; } } diff --git a/src/LibKubernetesGenerator/GeneralNameHelper.cs b/src/LibKubernetesGenerator/GeneralNameHelper.cs index 15194bb90..2d4f646f7 100644 --- a/src/LibKubernetesGenerator/GeneralNameHelper.cs +++ b/src/LibKubernetesGenerator/GeneralNameHelper.cs @@ -57,8 +57,6 @@ private string GetInterfaceName(JsonSchema definition) } } - interfaces.Add("IValidate"); - return string.Join(", ", interfaces); } @@ -68,7 +66,7 @@ public string GetDotNetNameOpenApiParameter(OpenApiParameter parameter, string i if (init == "true" && !parameter.IsRequired) { - name += " = null"; + name += " = default"; } return name; diff --git a/src/LibKubernetesGenerator/KubernetesClientSourceGenerator.cs b/src/LibKubernetesGenerator/KubernetesClientSourceGenerator.cs index 7adf6ef1d..fd2713260 100644 --- a/src/LibKubernetesGenerator/KubernetesClientSourceGenerator.cs +++ b/src/LibKubernetesGenerator/KubernetesClientSourceGenerator.cs @@ -58,12 +58,10 @@ private static IContainer BuildContainer(OpenApiDocument swagger) builder.RegisterType() ; - builder.RegisterType(); builder.RegisterType(); builder.RegisterType(); builder.RegisterType(); builder.RegisterType(); - builder.RegisterType(); builder.RegisterType(); return builder.Build(); @@ -79,9 +77,7 @@ public void Initialize(IncrementalGeneratorInitializationContext generatorContex container.Resolve().Generate(swagger, ctx); container.Resolve().Generate(swagger, ctx); - container.Resolve().Generate(swagger, ctx); container.Resolve().Generate(swagger, ctx); - container.Resolve().Generate(swagger, ctx); container.Resolve().Generate(swagger, ctx); container.Resolve().Generate(swagger, ctx); }); diff --git a/src/LibKubernetesGenerator/ModelExtGenerator.cs b/src/LibKubernetesGenerator/ModelExtGenerator.cs deleted file mode 100644 index bbadcb5c0..000000000 --- a/src/LibKubernetesGenerator/ModelExtGenerator.cs +++ /dev/null @@ -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 { "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"); - } - } -} diff --git a/src/LibKubernetesGenerator/ModelGenerator.cs b/src/LibKubernetesGenerator/ModelGenerator.cs index 98b9576cd..ca72e5fef 100644 --- a/src/LibKubernetesGenerator/ModelGenerator.cs +++ b/src/LibKubernetesGenerator/ModelGenerator.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using Microsoft.CodeAnalysis; using NSwag; @@ -18,15 +19,40 @@ public void Generate(OpenApiDocument swagger, IncrementalGeneratorPostInitializa { var sc = scriptObjectFactory.CreateScriptObject(); + var extSkippedTypes = new HashSet + { + "V1WatchEvent", + "V1Status", + "V1Patch", + }; + + var typeOverrides = new Dictionary + { + { "IntOrString", "class" }, + { "ResourceQuantity", "class" }, + }; foreach (var kv in swagger.Definitions) { var def = kv.Value; var clz = classNameHelper.GetClassNameForSchemaDefinition(def); + var hasExt = def.ExtensionData != null + && def.ExtensionData.ContainsKey("x-kubernetes-group-version-kind") + && !extSkippedTypes.Contains(classNameHelper.GetClassName(def)); + + + var typ = "record"; + if (typeOverrides.TryGetValue(clz, out var to)) + { + typ = to; + } sc.SetValue("clz", clz, true); sc.SetValue("def", def, 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"); } diff --git a/src/LibKubernetesGenerator/VersionConverterStubGenerator.cs b/src/LibKubernetesGenerator/VersionConverterStubGenerator.cs deleted file mode 100644 index 58550006d..000000000 --- a/src/LibKubernetesGenerator/VersionConverterStubGenerator.cs +++ /dev/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(); - - 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(@"// -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)); - } - } -} diff --git a/src/LibKubernetesGenerator/templates/Model.cs.template b/src/LibKubernetesGenerator/templates/Model.cs.template index 42e0bd0e6..46a822e33 100644 --- a/src/LibKubernetesGenerator/templates/Model.cs.template +++ b/src/LibKubernetesGenerator/templates/Model.cs.template @@ -4,92 +4,42 @@ // regenerated. // -namespace k8s.Models +namespace k8s.Models; + +/// +/// {{ToXmlDoc def.description}} +/// +{{ 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 }} + /// - /// {{ToXmlDoc def.description}} + /// Initializes a new instance of the {{GetClassName def}} class. /// - public partial class {{clz}} + public {{clz}}() { - /// - /// Initializes a new instance of the {{GetClassName def}} class. - /// - public {{clz}}() - { - CustomInit(); - } - - /// - /// Initializes a new instance of the {{GetClassName def}} class. - /// - {{ for property in properties }} - {{ if property.IsRequired }} - /// - /// {{ToXmlDoc property.description}} - /// - {{ end }} - {{ end }} - - {{ for property in properties }} - {{ if !property.IsRequired }} - /// - /// {{ToXmlDoc property.description}} - /// - {{ end }} - {{ end }} - public {{clz}}({{GetModelCtorParam def}}) - { - {{ for property in properties }} - {{GetDotNetName property.name "field"}} = {{GetDotNetName property.name "fieldctor"}}; - {{ end }} - CustomInit(); - } - - /// - /// An initialization method that performs custom operations like setting defaults - /// - partial void CustomInit(); - - {{ for property in properties }} - /// - /// {{ToXmlDoc property.description}} - /// - [JsonPropertyName("{{property.name}}")] - public {{GetDotNetType property}} {{GetDotNetName property.name "field"}} { get; set; } - {{ end }} - - /// - /// Validate the object. - /// - - 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 }} + CustomInit(); + } - {{ for property in properties }} - {{if IfType property "object" }} - {{GetDotNetName property.name "field"}}?.Validate(); - {{ end }} + /// + /// An initialization method that performs custom operations like setting defaults + /// + partial void CustomInit(); - {{if IfType property "objectarray" }} - if ({{GetDotNetName property.name "field"}} != null){ - foreach(var obj in {{GetDotNetName property.name "field"}}) - { - obj.Validate(); - } - } - {{ end }} - {{ end }} - } - } + {{ for property in properties }} + /// + /// {{ToXmlDoc property.description}} + /// + [JsonPropertyName("{{property.name}}")] + public {{ if property.IsRequired }} required {{ end }} {{GetDotNetType property}} {{GetDotNetName property.name "field"}} { get; set; } + {{ end }} } + diff --git a/src/LibKubernetesGenerator/templates/ModelExtensions.cs.template b/src/LibKubernetesGenerator/templates/ModelExtensions.cs.template deleted file mode 100644 index d52a3e714..000000000 --- a/src/LibKubernetesGenerator/templates/ModelExtensions.cs.template +++ /dev/null @@ -1,17 +0,0 @@ -// -// 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. -// -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 }} -} diff --git a/tests/KubernetesClient.Tests/IntOrStringTests.cs b/tests/KubernetesClient.Tests/IntOrStringTests.cs index 9fb3415a5..281308b1f 100644 --- a/tests/KubernetesClient.Tests/IntOrStringTests.cs +++ b/tests/KubernetesClient.Tests/IntOrStringTests.cs @@ -10,13 +10,13 @@ public void Serialize() { { var v = 123; - IntstrIntOrString intorstr = v; + IntOrString intorstr = v; Assert.Equal("123", KubernetesJson.Serialize(intorstr)); } { - IntstrIntOrString intorstr = "12%"; + IntOrString intorstr = "12%"; Assert.Equal("\"12%\"", KubernetesJson.Serialize(intorstr)); } } @@ -25,12 +25,12 @@ public void Serialize() public void Deserialize() { { - var v = KubernetesJson.Deserialize("1234"); + var v = KubernetesJson.Deserialize("1234"); Assert.Equal("1234", v.Value); } { - var v = KubernetesJson.Deserialize("\"12%\""); + var v = KubernetesJson.Deserialize("\"12%\""); Assert.Equal("12%", v.Value); } } From 638f6144280284b8955e424b248433a0c654cf18 Mon Sep 17 00:00:00 2001 From: Boshi LIAN Date: Wed, 10 Sep 2025 02:23:54 -0700 Subject: [PATCH 02/13] chore: update project files and clean up unused references --- examples/csrApproval/Program.cs | 10 ++++- examples/customResource/cResource.cs | 4 +- .../KubernetesClient.Aot.csproj | 1 - .../KubernetesClient.Classic.csproj | 1 - .../Models/ResourceQuantity.cs | 6 +++ src/LibKubernetesGenerator/ModelGenerator.cs | 2 - tests/E2E.Aot.Tests/MinikubeTests.cs | 42 +++++++++++-------- tests/E2E.Tests/MinikubeTests.cs | 42 +++++++++++-------- .../KubernetesYamlTests.cs | 4 +- 9 files changed, 67 insertions(+), 45 deletions(-) diff --git a/examples/csrApproval/Program.cs b/examples/csrApproval/Program.cs index fc94ae9a8..6c374105b 100644 --- a/examples/csrApproval/Program.cs +++ b/examples/csrApproval/Program.cs @@ -67,7 +67,15 @@ string GenerateCertificate(string name) var replace = new List { - 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; diff --git a/examples/customResource/cResource.cs b/examples/customResource/cResource.cs index f9df60b22..67440aee9 100644 --- a/examples/customResource/cResource.cs +++ b/examples/customResource/cResource.cs @@ -19,13 +19,13 @@ public override string ToString() } } - public class CResourceSpec + public record CResourceSpec { [JsonPropertyName("cityName")] public string CityName { get; set; } } - public class CResourceStatus : V1Status + public record CResourceStatus : V1Status { [JsonPropertyName("temperature")] public string Temperature { get; set; } diff --git a/src/KubernetesClient.Aot/KubernetesClient.Aot.csproj b/src/KubernetesClient.Aot/KubernetesClient.Aot.csproj index 602f6d7c8..a49e4e093 100644 --- a/src/KubernetesClient.Aot/KubernetesClient.Aot.csproj +++ b/src/KubernetesClient.Aot/KubernetesClient.Aot.csproj @@ -32,7 +32,6 @@ - diff --git a/src/KubernetesClient.Classic/KubernetesClient.Classic.csproj b/src/KubernetesClient.Classic/KubernetesClient.Classic.csproj index a09fb95f5..1af9440df 100644 --- a/src/KubernetesClient.Classic/KubernetesClient.Classic.csproj +++ b/src/KubernetesClient.Classic/KubernetesClient.Classic.csproj @@ -36,7 +36,6 @@ - diff --git a/src/KubernetesClient/Models/ResourceQuantity.cs b/src/KubernetesClient/Models/ResourceQuantity.cs index 2d1a4898d..9a83e3084 100644 --- a/src/KubernetesClient/Models/ResourceQuantity.cs +++ b/src/KubernetesClient/Models/ResourceQuantity.cs @@ -85,6 +85,12 @@ public ResourceQuantity(decimal n, int exp, SuffixFormat format) Format = format; } + public ResourceQuantity(string s) + { + Value = s; + CustomInit(); + } + public SuffixFormat Format { get; private set; } public string CanonicalizeString() diff --git a/src/LibKubernetesGenerator/ModelGenerator.cs b/src/LibKubernetesGenerator/ModelGenerator.cs index ca72e5fef..4c84cb86b 100644 --- a/src/LibKubernetesGenerator/ModelGenerator.cs +++ b/src/LibKubernetesGenerator/ModelGenerator.cs @@ -22,8 +22,6 @@ public void Generate(OpenApiDocument swagger, IncrementalGeneratorPostInitializa var extSkippedTypes = new HashSet { "V1WatchEvent", - "V1Status", - "V1Patch", }; var typeOverrides = new Dictionary diff --git a/tests/E2E.Aot.Tests/MinikubeTests.cs b/tests/E2E.Aot.Tests/MinikubeTests.cs index 505d9a4b2..4fd4ed044 100644 --- a/tests/E2E.Aot.Tests/MinikubeTests.cs +++ b/tests/E2E.Aot.Tests/MinikubeTests.cs @@ -172,27 +172,33 @@ public async Task DatetimeFieldTest() using var kubernetes = CreateClient(); await kubernetes.CoreV1.CreateNamespacedEventAsync( - new Corev1Event( - new V1ObjectReference( - "v1alpha1", - kind: "Test", - name: "test", - namespaceProperty: "default", - resourceVersion: "1", - uid: "1"), - new V1ObjectMeta() + new Corev1Event + { + InvolvedObject = new V1ObjectReference + { + ApiVersion = "v1alpha1", + Kind = "Test", + Name = "test", + NamespaceProperty = "default", + ResourceVersion = "1", + Uid = "1", + }, + Metadata = new V1ObjectMeta { GenerateName = "started-", }, - action: "STARTED", - type: "Normal", - reason: "STARTED", - message: "Started", - eventTime: DateTime.Now, - firstTimestamp: DateTime.Now, - lastTimestamp: DateTime.Now, - reportingComponent: "37", - 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] diff --git a/tests/E2E.Tests/MinikubeTests.cs b/tests/E2E.Tests/MinikubeTests.cs index 547ee90ea..a04141c77 100644 --- a/tests/E2E.Tests/MinikubeTests.cs +++ b/tests/E2E.Tests/MinikubeTests.cs @@ -451,27 +451,33 @@ public async Task DatetimeFieldTest() using var kubernetes = CreateClient(); await kubernetes.CoreV1.CreateNamespacedEventAsync( - new Corev1Event( - new V1ObjectReference( - "v1alpha1", - kind: "Test", - name: "test", - namespaceProperty: "default", - resourceVersion: "1", - uid: "1"), - new V1ObjectMeta() + new Corev1Event + { + Metadata = new V1ObjectMeta { GenerateName = "started-", + NamespaceProperty = "default", + }, + InvolvedObject = new V1ObjectReference + { + ApiVersion = "v1alpha1", + Kind = "Test", + Name = "test", + NamespaceProperty = "default", + ResourceVersion = "1", + Uid = "1", }, - action: "STARTED", - type: "Normal", - reason: "STARTED", - message: "Started", - eventTime: DateTime.Now, - firstTimestamp: DateTime.Now, - lastTimestamp: DateTime.Now, - reportingComponent: "37", - 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] diff --git a/tests/KubernetesClient.Tests/KubernetesYamlTests.cs b/tests/KubernetesClient.Tests/KubernetesYamlTests.cs index 19b03fdaa..1b18f5493 100644 --- a/tests/KubernetesClient.Tests/KubernetesYamlTests.cs +++ b/tests/KubernetesClient.Tests/KubernetesYamlTests.cs @@ -33,7 +33,7 @@ public void LoadAllFromString() } #pragma warning disable CA1812 // Class is used for YAML deserialization tests - private class MyPod : V1Pod + private record MyPod : V1Pod { } #pragma warning restore CA1812 @@ -531,7 +531,7 @@ public void SerializeIntOrString() var obj = new V1Service { Kind = "Service", - Metadata = new V1ObjectMeta(labels: labels, name: "test-svc"), + Metadata = new V1ObjectMeta { Name = "test-svc", Labels = labels }, ApiVersion = "v1", Spec = new V1ServiceSpec { From 04d7445fea95ad84bee32967ee94607a2d4aae89 Mon Sep 17 00:00:00 2001 From: Boshi LIAN Date: Wed, 10 Sep 2025 12:29:41 -0700 Subject: [PATCH 03/13] refactor: convert classes to records and simplify constructors for IntOrString, ResourceQuantity, and V1Patch --- examples/resize/Program.cs | 2 +- src/KubernetesClient/Models/IntOrString.cs | 40 ++++---------- .../Models/IntOrStringYamlConverter.cs | 2 +- .../Models/ResourceQuantity.cs | 53 ++++--------------- .../Models/ResourceQuantityJsonConverter.cs | 6 +-- .../Models/ResourceQuantityYamlConverter.cs | 2 +- src/KubernetesClient/Models/V1Patch.cs | 10 ++-- src/LibKubernetesGenerator/ModelGenerator.cs | 19 +++++-- .../templates/Model.cs.template | 15 +----- 9 files changed, 45 insertions(+), 104 deletions(-) diff --git a/examples/resize/Program.cs b/examples/resize/Program.cs index 58682c5b7..85fbeb9b2 100644 --- a/examples/resize/Program.cs +++ b/examples/resize/Program.cs @@ -23,7 +23,7 @@ { Requests = new Dictionary() { - ["cpu"] = new ResourceQuantity("100m"), + ["cpu"] = "100m", }, }, }, diff --git a/src/KubernetesClient/Models/IntOrString.cs b/src/KubernetesClient/Models/IntOrString.cs index 39141e890..dd8d919a3 100644 --- a/src/KubernetesClient/Models/IntOrString.cs +++ b/src/KubernetesClient/Models/IntOrString.cs @@ -1,16 +1,19 @@ namespace k8s.Models { [JsonConverter(typeof(IntOrStringJsonConverter))] - public partial class IntOrString + public record IntOrString { + [JsonPropertyName("value")] + public string Value { get; init; } + public static implicit operator IntOrString(int v) { - return new IntOrString { Value = Convert.ToString(v) }; + return new IntOrString(Convert.ToString(v)); } public static implicit operator IntOrString(long v) { - return new IntOrString { Value = Convert.ToString(v) }; + return new IntOrString(Convert.ToString(v)); } public static implicit operator string(IntOrString v) @@ -20,37 +23,12 @@ public static implicit operator string(IntOrString v) public static implicit operator IntOrString(string v) { - return new IntOrString { Value = v }; - } - - protected bool Equals(IntOrString 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((IntOrString)obj); + return new IntOrString(v); } - public override int GetHashCode() + public override string ToString() { - return Value != null ? Value.GetHashCode() : 0; + return Value; } } } diff --git a/src/KubernetesClient/Models/IntOrStringYamlConverter.cs b/src/KubernetesClient/Models/IntOrStringYamlConverter.cs index 248934bac..cfaa42205 100644 --- a/src/KubernetesClient/Models/IntOrStringYamlConverter.cs +++ b/src/KubernetesClient/Models/IntOrStringYamlConverter.cs @@ -21,7 +21,7 @@ public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeseria return null; } - return new IntOrString { Value = scalar?.Value }; + return scalar?.Value; } finally { diff --git a/src/KubernetesClient/Models/ResourceQuantity.cs b/src/KubernetesClient/Models/ResourceQuantity.cs index 9a83e3084..520b42825 100644 --- a/src/KubernetesClient/Models/ResourceQuantity.cs +++ b/src/KubernetesClient/Models/ResourceQuantity.cs @@ -54,7 +54,7 @@ namespace k8s.Models /// cause implementors to also use a fixed point implementation. /// [JsonConverter(typeof(ResourceQuantityJsonConverter))] - public partial class ResourceQuantity + public record ResourceQuantity { public enum SuffixFormat { @@ -85,12 +85,6 @@ public ResourceQuantity(decimal n, int exp, SuffixFormat format) Format = format; } - public ResourceQuantity(string s) - { - Value = s; - CustomInit(); - } - public SuffixFormat Format { get; private set; } public string CanonicalizeString() @@ -103,39 +97,6 @@ public override string ToString() 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 // CanonicalizeBytes returns the canonical form of q and its suffix (see comment on Quantity). @@ -163,10 +124,9 @@ public string CanonicalizeString(SuffixFormat suffixFormat) return Suffixer.AppendMaxSuffix(_unitlessValue, suffixFormat); } - // ctor - partial void CustomInit() + public ResourceQuantity(string v) { - if (Value == null) + if (v == null) { // No value has been defined, initialize to 0. _unitlessValue = new Fraction(0); @@ -174,7 +134,7 @@ partial void CustomInit() return; } - var value = Value.Trim(); + var value = v.Trim(); var si = value.IndexOfAny(SuffixChars); if (si == -1) @@ -194,6 +154,11 @@ partial void CustomInit() } } + public static implicit operator ResourceQuantity(string v) + { + return new ResourceQuantity(v); + } + private static bool HasMantissa(Fraction value) { if (value.IsZero) diff --git a/src/KubernetesClient/Models/ResourceQuantityJsonConverter.cs b/src/KubernetesClient/Models/ResourceQuantityJsonConverter.cs index 7ce0df78b..ef9f3fe12 100644 --- a/src/KubernetesClient/Models/ResourceQuantityJsonConverter.cs +++ b/src/KubernetesClient/Models/ResourceQuantityJsonConverter.cs @@ -8,16 +8,16 @@ public override ResourceQuantity Read(ref Utf8JsonReader reader, Type typeToConv switch (reader.TokenType) { case JsonTokenType.Null: - return new ResourceQuantity { Value = null }; + return null; case JsonTokenType.Number: if (reader.TryGetDouble(out var val)) { - return new ResourceQuantity { Value = Convert.ToString(val) }; + return Convert.ToString(val); } return reader.GetDecimal(); default: - return new ResourceQuantity { Value = reader.GetString() }; + return reader.GetString(); } } diff --git a/src/KubernetesClient/Models/ResourceQuantityYamlConverter.cs b/src/KubernetesClient/Models/ResourceQuantityYamlConverter.cs index cdc07f25e..1006a3cd7 100644 --- a/src/KubernetesClient/Models/ResourceQuantityYamlConverter.cs +++ b/src/KubernetesClient/Models/ResourceQuantityYamlConverter.cs @@ -21,7 +21,7 @@ public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeseria return null; } - return new ResourceQuantity { Value = scalar?.Value }; + return scalar?.Value; } finally { diff --git a/src/KubernetesClient/Models/V1Patch.cs b/src/KubernetesClient/Models/V1Patch.cs index 1763ccc38..e78660379 100644 --- a/src/KubernetesClient/Models/V1Patch.cs +++ b/src/KubernetesClient/Models/V1Patch.cs @@ -1,7 +1,7 @@ namespace k8s.Models { [JsonConverter(typeof(V1PatchJsonConverter))] - public partial record class V1Patch + public record V1Patch { public enum PatchType { @@ -31,17 +31,17 @@ public enum PatchType ApplyPatch, } + [JsonPropertyName("content")] + [JsonInclude] + public object Content { get; private set; } + public PatchType Type { get; private set; } public V1Patch(object body, PatchType type) { Content = body; Type = type; - CustomInit(); - } - partial void CustomInit() - { if (Content == null) { throw new ArgumentNullException(nameof(Content), "object must be set"); diff --git a/src/LibKubernetesGenerator/ModelGenerator.cs b/src/LibKubernetesGenerator/ModelGenerator.cs index 4c84cb86b..df9f8d3ea 100644 --- a/src/LibKubernetesGenerator/ModelGenerator.cs +++ b/src/LibKubernetesGenerator/ModelGenerator.cs @@ -19,6 +19,13 @@ public void Generate(OpenApiDocument swagger, IncrementalGeneratorPostInitializa { var sc = scriptObjectFactory.CreateScriptObject(); + var genSkippedTypes = new HashSet + { + "IntOrString", + "ResourceQuantity", + "V1Patch", + }; + var extSkippedTypes = new HashSet { "V1WatchEvent", @@ -26,17 +33,22 @@ public void Generate(OpenApiDocument swagger, IncrementalGeneratorPostInitializa var typeOverrides = new Dictionary { - { "IntOrString", "class" }, - { "ResourceQuantity", "class" }, + // not used at the moment }; foreach (var kv in swagger.Definitions) { var def = kv.Value; 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(classNameHelper.GetClassName(def)); + && !extSkippedTypes.Contains(clz); var typ = "record"; @@ -51,7 +63,6 @@ public void Generate(OpenApiDocument swagger, IncrementalGeneratorPostInitializa sc.SetValue("typ", typ, true); sc.SetValue("hasExt", hasExt, true); - context.RenderToContext("Model.cs.template", sc, $"Models_{clz}.g.cs"); } } diff --git a/src/LibKubernetesGenerator/templates/Model.cs.template b/src/LibKubernetesGenerator/templates/Model.cs.template index 46a822e33..721709f1e 100644 --- a/src/LibKubernetesGenerator/templates/Model.cs.template +++ b/src/LibKubernetesGenerator/templates/Model.cs.template @@ -21,25 +21,12 @@ public partial {{typ}} {{clz}} {{ if hasExt }} : {{ GetInterfaceName def }} {{ e public const string KubePluralName = "{{ GetPlural def }}"; {{ end }} - /// - /// Initializes a new instance of the {{GetClassName def}} class. - /// - public {{clz}}() - { - CustomInit(); - } - - /// - /// An initialization method that performs custom operations like setting defaults - /// - partial void CustomInit(); - {{ for property in properties }} /// /// {{ToXmlDoc property.description}} /// [JsonPropertyName("{{property.name}}")] - public {{ if property.IsRequired }} required {{ end }} {{GetDotNetType property}} {{GetDotNetName property.name "field"}} { get; set; } + public {{ if property.IsRequired }} required {{ end }} {{GetDotNetType property}} {{GetDotNetName property.name "field"}} { get; set; } {{ end }} } From ff806885edd7bedb851def6af4ae480275c92d3a Mon Sep 17 00:00:00 2001 From: Boshi LIAN Date: Wed, 10 Sep 2025 12:41:49 -0700 Subject: [PATCH 04/13] fix: define IsExternalInit to resolve CS0518 error in IntOrString --- src/KubernetesClient.Classic/IsExternalInit.cs | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 src/KubernetesClient.Classic/IsExternalInit.cs diff --git a/src/KubernetesClient.Classic/IsExternalInit.cs b/src/KubernetesClient.Classic/IsExternalInit.cs new file mode 100644 index 000000000..749f30858 --- /dev/null +++ b/src/KubernetesClient.Classic/IsExternalInit.cs @@ -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 { } +} \ No newline at end of file From 0bd43e4bdf3a4b363939d7e244a2cdbc917e0594 Mon Sep 17 00:00:00 2001 From: Boshi LIAN Date: Wed, 10 Sep 2025 14:26:45 -0700 Subject: [PATCH 05/13] refactor: change IntOrString and ResourceQuantity from records to structs, update implicit conversions, and simplify null checks --- src/KubernetesClient/Models/IntOrString.cs | 12 ++++++------ .../Models/IntOrStringJsonConverter.cs | 2 +- .../Models/IntOrStringYamlConverter.cs | 2 +- src/KubernetesClient/Models/ResourceQuantity.cs | 4 ++-- .../Models/ResourceQuantityJsonConverter.cs | 2 +- .../Models/ResourceQuantityYamlConverter.cs | 2 +- tests/KubernetesClient.Classic.Tests/SimpleTests.cs | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/KubernetesClient/Models/IntOrString.cs b/src/KubernetesClient/Models/IntOrString.cs index dd8d919a3..ca54dbc96 100644 --- a/src/KubernetesClient/Models/IntOrString.cs +++ b/src/KubernetesClient/Models/IntOrString.cs @@ -1,29 +1,29 @@ namespace k8s.Models { [JsonConverter(typeof(IntOrStringJsonConverter))] - public record IntOrString + public struct IntOrString { [JsonPropertyName("value")] - public string Value { get; init; } + public string Value { get; private init; } public static implicit operator IntOrString(int v) { - return new IntOrString(Convert.ToString(v)); + return Convert.ToString(v); } public static implicit operator IntOrString(long v) { - return new IntOrString(Convert.ToString(v)); + return Convert.ToString(v); } public static implicit operator string(IntOrString v) { - return v?.Value; + return v.Value; } public static implicit operator IntOrString(string v) { - return new IntOrString(v); + return new IntOrString { Value = v }; } public override string ToString() diff --git a/src/KubernetesClient/Models/IntOrStringJsonConverter.cs b/src/KubernetesClient/Models/IntOrStringJsonConverter.cs index 9880a35a4..c7cbe273a 100644 --- a/src/KubernetesClient/Models/IntOrStringJsonConverter.cs +++ b/src/KubernetesClient/Models/IntOrStringJsonConverter.cs @@ -24,7 +24,7 @@ public override void Write(Utf8JsonWriter writer, IntOrString value, JsonSeriali throw new ArgumentNullException(nameof(writer)); } - var s = value?.Value; + var s = value.Value; if (long.TryParse(s, out var intv)) { diff --git a/src/KubernetesClient/Models/IntOrStringYamlConverter.cs b/src/KubernetesClient/Models/IntOrStringYamlConverter.cs index cfaa42205..514f2be91 100644 --- a/src/KubernetesClient/Models/IntOrStringYamlConverter.cs +++ b/src/KubernetesClient/Models/IntOrStringYamlConverter.cs @@ -35,7 +35,7 @@ public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeseria public void WriteYaml(IEmitter emitter, object value, Type type, ObjectSerializer serializer) { var obj = (IntOrString)value; - emitter?.Emit(new YamlDotNet.Core.Events.Scalar(obj?.Value)); + emitter?.Emit(new YamlDotNet.Core.Events.Scalar(obj.Value)); } } } diff --git a/src/KubernetesClient/Models/ResourceQuantity.cs b/src/KubernetesClient/Models/ResourceQuantity.cs index 520b42825..6b4ff8d88 100644 --- a/src/KubernetesClient/Models/ResourceQuantity.cs +++ b/src/KubernetesClient/Models/ResourceQuantity.cs @@ -54,7 +54,7 @@ namespace k8s.Models /// cause implementors to also use a fixed point implementation. /// [JsonConverter(typeof(ResourceQuantityJsonConverter))] - public record ResourceQuantity + public struct ResourceQuantity { public enum SuffixFormat { @@ -171,7 +171,7 @@ private static bool HasMantissa(Fraction value) public static implicit operator decimal(ResourceQuantity v) { - return v?.ToDecimal() ?? 0; + return v.ToDecimal(); } public static implicit operator ResourceQuantity(decimal v) diff --git a/src/KubernetesClient/Models/ResourceQuantityJsonConverter.cs b/src/KubernetesClient/Models/ResourceQuantityJsonConverter.cs index ef9f3fe12..a99eb334b 100644 --- a/src/KubernetesClient/Models/ResourceQuantityJsonConverter.cs +++ b/src/KubernetesClient/Models/ResourceQuantityJsonConverter.cs @@ -28,7 +28,7 @@ public override void Write(Utf8JsonWriter writer, ResourceQuantity value, JsonSe throw new ArgumentNullException(nameof(writer)); } - writer.WriteStringValue(value?.ToString()); + writer.WriteStringValue(value.ToString()); } } } diff --git a/src/KubernetesClient/Models/ResourceQuantityYamlConverter.cs b/src/KubernetesClient/Models/ResourceQuantityYamlConverter.cs index 1006a3cd7..50523ca6e 100644 --- a/src/KubernetesClient/Models/ResourceQuantityYamlConverter.cs +++ b/src/KubernetesClient/Models/ResourceQuantityYamlConverter.cs @@ -35,7 +35,7 @@ public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeseria public void WriteYaml(IEmitter emitter, object value, Type type, ObjectSerializer serializer) { var obj = (ResourceQuantity)value; - emitter?.Emit(new YamlDotNet.Core.Events.Scalar(obj?.ToString())); + emitter?.Emit(new YamlDotNet.Core.Events.Scalar(obj.ToString())); } } } diff --git a/tests/KubernetesClient.Classic.Tests/SimpleTests.cs b/tests/KubernetesClient.Classic.Tests/SimpleTests.cs index f0437defb..3e17a5854 100644 --- a/tests/KubernetesClient.Classic.Tests/SimpleTests.cs +++ b/tests/KubernetesClient.Classic.Tests/SimpleTests.cs @@ -7,7 +7,7 @@ namespace k8s.tests; -public class BasicTests +public class SimpleTests { // TODO: fail to setup asp.net core 6 on net48 private class DummyHttpServer : System.IDisposable From f695e4b41191dd08ba160da9b73df58f969484f6 Mon Sep 17 00:00:00 2001 From: Boshi LIAN Date: Wed, 10 Sep 2025 14:42:13 -0700 Subject: [PATCH 06/13] refactor: add JsonPropertyName attribute to Value property in IntOrString struct --- src/KubernetesClient/Models/IntOrString.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/KubernetesClient/Models/IntOrString.cs b/src/KubernetesClient/Models/IntOrString.cs index ca54dbc96..be3bafa5f 100644 --- a/src/KubernetesClient/Models/IntOrString.cs +++ b/src/KubernetesClient/Models/IntOrString.cs @@ -1,9 +1,10 @@ +using System.Globalization; + namespace k8s.Models { [JsonConverter(typeof(IntOrStringJsonConverter))] public struct IntOrString { - [JsonPropertyName("value")] public string Value { get; private init; } public static implicit operator IntOrString(int v) @@ -30,5 +31,10 @@ public override string ToString() { return Value; } + + public int ToInt() + { + return int.Parse(Value, CultureInfo.InvariantCulture); + } } } From c1a7c99e70909b37f4c912da5691617e886ae336 Mon Sep 17 00:00:00 2001 From: Boshi LIAN Date: Wed, 10 Sep 2025 14:58:41 -0700 Subject: [PATCH 07/13] refactor: simplify V1Patch constructor and improve argument validation --- src/KubernetesClient/Models/V1Patch.cs | 13 ++++--------- src/KubernetesClient/Models/V1Status.cs | 2 +- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/KubernetesClient/Models/V1Patch.cs b/src/KubernetesClient/Models/V1Patch.cs index e78660379..5838e4b05 100644 --- a/src/KubernetesClient/Models/V1Patch.cs +++ b/src/KubernetesClient/Models/V1Patch.cs @@ -39,18 +39,13 @@ public enum PatchType public V1Patch(object body, PatchType type) { - Content = body; - Type = type; - - if (Content == null) + if (type == PatchType.Unknown) { - throw new ArgumentNullException(nameof(Content), "object must be set"); + throw new ArgumentException("patch type must be set", nameof(type)); } - 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; } } } diff --git a/src/KubernetesClient/Models/V1Status.cs b/src/KubernetesClient/Models/V1Status.cs index 263673c8a..d69116d2e 100644 --- a/src/KubernetesClient/Models/V1Status.cs +++ b/src/KubernetesClient/Models/V1Status.cs @@ -2,7 +2,7 @@ namespace k8s.Models { - public partial record class V1Status + public partial record V1Status { /// Converts a object into a short description of the status. /// string description of the status From a20419bdeef66357841048dc508a82af999f56c3 Mon Sep 17 00:00:00 2001 From: Boshi LIAN Date: Wed, 10 Sep 2025 14:59:42 -0700 Subject: [PATCH 08/13] refactor: remove unnecessary CultureInfo parameter in ToInt method --- src/KubernetesClient/Models/IntOrString.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/KubernetesClient/Models/IntOrString.cs b/src/KubernetesClient/Models/IntOrString.cs index be3bafa5f..a3b3bedfb 100644 --- a/src/KubernetesClient/Models/IntOrString.cs +++ b/src/KubernetesClient/Models/IntOrString.cs @@ -1,5 +1,3 @@ -using System.Globalization; - namespace k8s.Models { [JsonConverter(typeof(IntOrStringJsonConverter))] @@ -34,7 +32,7 @@ public override string ToString() public int ToInt() { - return int.Parse(Value, CultureInfo.InvariantCulture); + return int.Parse(Value); } } } From 62b20a691554659e28d419067220dc1a0620133b Mon Sep 17 00:00:00 2001 From: Boshi Lian Date: Wed, 10 Sep 2025 15:51:10 -0700 Subject: [PATCH 09/13] Update src/KubernetesClient/Models/ResourceQuantity.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/KubernetesClient/Models/ResourceQuantity.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/KubernetesClient/Models/ResourceQuantity.cs b/src/KubernetesClient/Models/ResourceQuantity.cs index 6b4ff8d88..fe938e925 100644 --- a/src/KubernetesClient/Models/ResourceQuantity.cs +++ b/src/KubernetesClient/Models/ResourceQuantity.cs @@ -171,6 +171,12 @@ private static bool HasMantissa(Fraction value) public static implicit operator decimal(ResourceQuantity v) { + // Handle default/uninitialized struct by returning zero + // Fraction is a struct, so check if _unitlessValue is zero + if (v._unitlessValue.IsZero) + { + return 0m; + } return v.ToDecimal(); } From d4c548a305ea46f9c9341fc65096ac8d23de9e37 Mon Sep 17 00:00:00 2001 From: Boshi Lian Date: Wed, 10 Sep 2025 15:51:16 -0700 Subject: [PATCH 10/13] Update src/KubernetesClient/Models/IntOrString.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/KubernetesClient/Models/IntOrString.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/KubernetesClient/Models/IntOrString.cs b/src/KubernetesClient/Models/IntOrString.cs index a3b3bedfb..e79ef2465 100644 --- a/src/KubernetesClient/Models/IntOrString.cs +++ b/src/KubernetesClient/Models/IntOrString.cs @@ -3,7 +3,7 @@ namespace k8s.Models [JsonConverter(typeof(IntOrStringJsonConverter))] public struct IntOrString { - public string Value { get; private init; } + public string? Value { get; private init; } public static implicit operator IntOrString(int v) { From daa7870360b3ddaceefac86840f72108f04c5324 Mon Sep 17 00:00:00 2001 From: Boshi LIAN Date: Wed, 10 Sep 2025 16:25:25 -0700 Subject: [PATCH 11/13] Revert "Update src/KubernetesClient/Models/ResourceQuantity.cs" This reverts commit 62b20a691554659e28d419067220dc1a0620133b. --- src/KubernetesClient/Models/ResourceQuantity.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/KubernetesClient/Models/ResourceQuantity.cs b/src/KubernetesClient/Models/ResourceQuantity.cs index fe938e925..6b4ff8d88 100644 --- a/src/KubernetesClient/Models/ResourceQuantity.cs +++ b/src/KubernetesClient/Models/ResourceQuantity.cs @@ -171,12 +171,6 @@ private static bool HasMantissa(Fraction value) public static implicit operator decimal(ResourceQuantity v) { - // Handle default/uninitialized struct by returning zero - // Fraction is a struct, so check if _unitlessValue is zero - if (v._unitlessValue.IsZero) - { - return 0m; - } return v.ToDecimal(); } From 03049202fb40c2a8135ec4553f875997320e9d0d Mon Sep 17 00:00:00 2001 From: Boshi LIAN Date: Wed, 10 Sep 2025 16:34:27 -0700 Subject: [PATCH 12/13] refactor: remove commented-out formatting check and simplify build command --- .github/workflows/buildtest.yaml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/buildtest.yaml b/.github/workflows/buildtest.yaml index 1870d99e8..b97747c7b 100644 --- a/.github/workflows/buildtest.yaml +++ b/.github/workflows/buildtest.yaml @@ -17,12 +17,8 @@ jobs: dotnet-version: | 8.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 - run: dotnet build --configuration Release -v detailed + run: dotnet build --configuration Release - name: Test 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 From 9e986c6693e8925a954c8b4c59c2b4ca4530a278 Mon Sep 17 00:00:00 2001 From: Boshi LIAN Date: Tue, 16 Sep 2025 22:13:02 -0700 Subject: [PATCH 13/13] refactor: remove IValidate.cs from project references in Aot and Classic --- src/KubernetesClient.Aot/KubernetesClient.Aot.csproj | 1 - src/KubernetesClient.Classic/KubernetesClient.Classic.csproj | 1 - 2 files changed, 2 deletions(-) diff --git a/src/KubernetesClient.Aot/KubernetesClient.Aot.csproj b/src/KubernetesClient.Aot/KubernetesClient.Aot.csproj index fddbe54ab..55305a1a1 100644 --- a/src/KubernetesClient.Aot/KubernetesClient.Aot.csproj +++ b/src/KubernetesClient.Aot/KubernetesClient.Aot.csproj @@ -28,7 +28,6 @@ - diff --git a/src/KubernetesClient.Classic/KubernetesClient.Classic.csproj b/src/KubernetesClient.Classic/KubernetesClient.Classic.csproj index 750e3d1b8..81070f066 100644 --- a/src/KubernetesClient.Classic/KubernetesClient.Classic.csproj +++ b/src/KubernetesClient.Classic/KubernetesClient.Classic.csproj @@ -30,7 +30,6 @@ -