From 5e061d5093984413b534598bc7c578a326af134c Mon Sep 17 00:00:00 2001 From: Morgan Creekmore Date: Mon, 6 Jan 2025 07:50:02 -0600 Subject: [PATCH] fix: Default to setting Nullable to null in CRD generation (#830) Default to setting Nullable to null and only explicitly set it to true. This avoids generating `nullable: false` (the default for nullable) which causes issues in ArgoCD --- src/KubeOps.Transpiler/Crds.cs | 35 +++++----- test/KubeOps.Transpiler.Test/Crds.Mlc.Test.cs | 70 +++++++++---------- 2 files changed, 53 insertions(+), 52 deletions(-) diff --git a/src/KubeOps.Transpiler/Crds.cs b/src/KubeOps.Transpiler/Crds.cs index e41d9f0d..802c1d70 100644 --- a/src/KubeOps.Transpiler/Crds.cs +++ b/src/KubeOps.Transpiler/Crds.cs @@ -215,7 +215,11 @@ private static V1JSONSchemaProps Map(this MetadataLoadContext context, PropertyI props.Description ??= prop.GetCustomAttributeData() ?.GetCustomAttributeCtorArg(context, 0); - props.Nullable = prop.IsNullable(); + if (prop.IsNullable()) + { + // Default to Nullable to null to avoid generating `nullable:false` + props.Nullable = true; + } if (prop.GetCustomAttributeData() is { } extDocs) { @@ -280,7 +284,7 @@ private static V1JSONSchemaProps Map(this MetadataLoadContext context, Type type { if (type.FullName == "System.String") { - return new V1JSONSchemaProps { Type = String, Nullable = false }; + return new V1JSONSchemaProps { Type = String }; } if (type.Name == typeof(Nullable<>).Name && type.GenericTypeArguments.Length == 1) @@ -310,13 +314,12 @@ private static V1JSONSchemaProps Map(this MetadataLoadContext context, Type type { Type = Object, AdditionalProperties = additionalProperties, - Nullable = false, }; } if (interfaceNames.Contains(typeof(IDictionary).FullName)) { - return new V1JSONSchemaProps { Type = Object, XKubernetesPreserveUnknownFields = true, Nullable = false, }; + return new V1JSONSchemaProps { Type = Object, XKubernetesPreserveUnknownFields = true }; } if (interfaceNames.Contains(typeof(IEnumerable<>).FullName)) @@ -347,9 +350,9 @@ private static V1JSONSchemaProps MapObjectType(this MetadataLoadContext context, switch (type.FullName) { case "k8s.Models.V1ObjectMeta": - return new V1JSONSchemaProps { Type = Object, Nullable = false }; + return new V1JSONSchemaProps { Type = Object }; case "k8s.Models.IntstrIntOrString": - return new V1JSONSchemaProps { XKubernetesIntOrString = true, Nullable = false }; + return new V1JSONSchemaProps { XKubernetesIntOrString = true }; default: if (context.GetContextType().IsAssignableFrom(type) && type is { IsAbstract: false, IsInterface: false } && @@ -361,7 +364,6 @@ private static V1JSONSchemaProps MapObjectType(this MetadataLoadContext context, Properties = null, XKubernetesPreserveUnknownFields = true, XKubernetesEmbeddedResource = true, - Nullable = false, }; } @@ -412,25 +414,24 @@ private static V1JSONSchemaProps MapEnumerationType( { Type = Object, AdditionalProperties = additionalProperties, - Nullable = false, }; } var items = context.Map(listType); - return new V1JSONSchemaProps { Type = Array, Items = items, Nullable = false }; + return new V1JSONSchemaProps { Type = Array, Items = items }; } private static V1JSONSchemaProps MapValueType(this MetadataLoadContext _, Type type) => type.FullName switch { - "System.Int32" => new V1JSONSchemaProps { Type = Integer, Format = Int32, Nullable = false }, - "System.Int64" => new V1JSONSchemaProps { Type = Integer, Format = Int64, Nullable = false }, - "System.Single" => new V1JSONSchemaProps { Type = Number, Format = Float, Nullable = false }, - "System.Double" => new V1JSONSchemaProps { Type = Number, Format = Double, Nullable = false }, - "System.Decimal" => new V1JSONSchemaProps { Type = Number, Format = Decimal, Nullable = false }, - "System.Boolean" => new V1JSONSchemaProps { Type = Boolean, Nullable = false }, - "System.DateTime" => new V1JSONSchemaProps { Type = String, Format = DateTime, Nullable = false }, - "System.DateTimeOffset" => new V1JSONSchemaProps { Type = String, Format = DateTime, Nullable = false }, + "System.Int32" => new V1JSONSchemaProps { Type = Integer, Format = Int32 }, + "System.Int64" => new V1JSONSchemaProps { Type = Integer, Format = Int64 }, + "System.Single" => new V1JSONSchemaProps { Type = Number, Format = Float }, + "System.Double" => new V1JSONSchemaProps { Type = Number, Format = Double }, + "System.Decimal" => new V1JSONSchemaProps { Type = Number, Format = Decimal }, + "System.Boolean" => new V1JSONSchemaProps { Type = Boolean }, + "System.DateTime" => new V1JSONSchemaProps { Type = String, Format = DateTime }, + "System.DateTimeOffset" => new V1JSONSchemaProps { Type = String, Format = DateTime }, _ => throw InvalidType(type), }; diff --git a/test/KubeOps.Transpiler.Test/Crds.Mlc.Test.cs b/test/KubeOps.Transpiler.Test/Crds.Mlc.Test.cs index 17a72d2f..0d9c1552 100644 --- a/test/KubeOps.Transpiler.Test/Crds.Mlc.Test.cs +++ b/test/KubeOps.Transpiler.Test/Crds.Mlc.Test.cs @@ -14,42 +14,42 @@ namespace KubeOps.Transpiler.Test; public class CrdsMlcTest(MlcProvider provider) : TranspilerTestBase(provider) { [Theory] - [InlineData(typeof(StringTestEntity), "string", null, false)] + [InlineData(typeof(StringTestEntity), "string", null, null)] [InlineData(typeof(NullableStringTestEntity), "string", null, true)] - [InlineData(typeof(IntTestEntity), "integer", "int32", false)] + [InlineData(typeof(IntTestEntity), "integer", "int32", null)] [InlineData(typeof(NullableIntTestEntity), "integer", "int32", true)] - [InlineData(typeof(LongTestEntity), "integer", "int64", false)] + [InlineData(typeof(LongTestEntity), "integer", "int64", null)] [InlineData(typeof(NullableLongTestEntity), "integer", "int64", true)] - [InlineData(typeof(FloatTestEntity), "number", "float", false)] + [InlineData(typeof(FloatTestEntity), "number", "float", null)] [InlineData(typeof(NullableFloatTestEntity), "number", "float", true)] - [InlineData(typeof(DecimalTestEntity), "number", "decimal", false)] + [InlineData(typeof(DecimalTestEntity), "number", "decimal", null)] [InlineData(typeof(NullableDecimalTestEntity), "number", "decimal", true)] - [InlineData(typeof(DoubleTestEntity), "number", "double", false)] + [InlineData(typeof(DoubleTestEntity), "number", "double", null)] [InlineData(typeof(NullableDoubleTestEntity), "number", "double", true)] - [InlineData(typeof(BoolTestEntity), "boolean", null, false)] + [InlineData(typeof(BoolTestEntity), "boolean", null, null)] [InlineData(typeof(NullableBoolTestEntity), "boolean", null, true)] - [InlineData(typeof(DateTimeTestEntity), "string", "date-time", false)] + [InlineData(typeof(DateTimeTestEntity), "string", "date-time", null)] [InlineData(typeof(NullableDateTimeTestEntity), "string", "date-time", true)] - [InlineData(typeof(DateTimeOffsetTestEntity), "string", "date-time", false)] + [InlineData(typeof(DateTimeOffsetTestEntity), "string", "date-time", null)] [InlineData(typeof(NullableDateTimeOffsetTestEntity), "string", "date-time", true)] - [InlineData(typeof(V1ObjectMetaTestEntity), "object", null, false)] - [InlineData(typeof(StringArrayEntity), "array", null, false)] + [InlineData(typeof(V1ObjectMetaTestEntity), "object", null, null)] + [InlineData(typeof(StringArrayEntity), "array", null, null)] [InlineData(typeof(NullableStringArrayEntity), "array", null, true)] - [InlineData(typeof(EnumerableIntEntity), "array", null, false)] - [InlineData(typeof(HashSetIntEntity), "array", null, false)] - [InlineData(typeof(SetIntEntity), "array", null, false)] - [InlineData(typeof(InheritedEnumerableEntity), "array", null, false)] - [InlineData(typeof(EnumEntity), "string", null, false)] + [InlineData(typeof(EnumerableIntEntity), "array", null, null)] + [InlineData(typeof(HashSetIntEntity), "array", null, null)] + [InlineData(typeof(SetIntEntity), "array", null, null)] + [InlineData(typeof(InheritedEnumerableEntity), "array", null, null)] + [InlineData(typeof(EnumEntity), "string", null, null)] [InlineData(typeof(NullableEnumEntity), "string", null, true)] - [InlineData(typeof(DictionaryEntity), "object", null, false)] - [InlineData(typeof(EnumerableKeyPairsEntity), "object", null, false)] - [InlineData(typeof(IntstrOrStringEntity), null, null, false)] - [InlineData(typeof(EmbeddedResourceEntity), "object", null, false)] - [InlineData(typeof(EmbeddedCustomResourceEntity), "object", null, false)] - [InlineData(typeof(EmbeddedCustomResourceGenericEntity), "object", null, false)] - [InlineData(typeof(EmbeddedResourceListEntity), "array", null, false)] + [InlineData(typeof(DictionaryEntity), "object", null, null)] + [InlineData(typeof(EnumerableKeyPairsEntity), "object", null, null)] + [InlineData(typeof(IntstrOrStringEntity), null, null, null)] + [InlineData(typeof(EmbeddedResourceEntity), "object", null, null)] + [InlineData(typeof(EmbeddedCustomResourceEntity), "object", null, null)] + [InlineData(typeof(EmbeddedCustomResourceGenericEntity), "object", null, null)] + [InlineData(typeof(EmbeddedResourceListEntity), "array", null, null)] public void Should_Transpile_Entity_Type_Correctly(Type type, string? expectedType, string? expectedFormat, - bool isNullable) + bool? isNullable) { var crd = _mlc.Transpile(type); var prop = crd.Spec.Versions.First().Schema.OpenAPIV3Schema.Properties["property"]; @@ -59,15 +59,15 @@ public void Should_Transpile_Entity_Type_Correctly(Type type, string? expectedTy } [Theory] - [InlineData(typeof(StringArrayEntity), "string", false)] - [InlineData(typeof(NullableStringArrayEntity), "string", false)] - [InlineData(typeof(EnumerableIntEntity), "integer", false)] + [InlineData(typeof(StringArrayEntity), "string", null)] + [InlineData(typeof(NullableStringArrayEntity), "string", null)] + [InlineData(typeof(EnumerableIntEntity), "integer", null)] [InlineData(typeof(EnumerableNullableIntEntity), "integer", true)] - [InlineData(typeof(HashSetIntEntity), "integer", false)] - [InlineData(typeof(SetIntEntity), "integer", false)] - [InlineData(typeof(InheritedEnumerableEntity), "integer", false)] - [InlineData(typeof(EmbeddedResourceListEntity), "object", false)] - public void Should_Set_Correct_Array_Type(Type type, string expectedType, bool isNullable) + [InlineData(typeof(HashSetIntEntity), "integer", null)] + [InlineData(typeof(SetIntEntity), "integer", null)] + [InlineData(typeof(InheritedEnumerableEntity), "integer", null)] + [InlineData(typeof(EmbeddedResourceListEntity), "object", null)] + public void Should_Set_Correct_Array_Type(Type type, string expectedType, bool? isNullable) { var crd = _mlc.Transpile(type); var prop = crd.Spec.Versions.First().Schema.OpenAPIV3Schema.Properties["property"].Items as V1JSONSchemaProps; @@ -76,9 +76,9 @@ public void Should_Set_Correct_Array_Type(Type type, string expectedType, bool i } [Theory] - [InlineData(typeof(DictionaryEntity), "string", false)] - [InlineData(typeof(EnumerableKeyPairsEntity), "string", false)] - public void Should_Set_Correct_Dictionary_Additional_Properties_Type(Type type, string expectedType, bool isNullable) + [InlineData(typeof(DictionaryEntity), "string", null)] + [InlineData(typeof(EnumerableKeyPairsEntity), "string", null)] + public void Should_Set_Correct_Dictionary_Additional_Properties_Type(Type type, string expectedType, bool? isNullable) { var crd = _mlc.Transpile(type); var prop = crd.Spec.Versions.First().Schema.OpenAPIV3Schema.Properties["property"].AdditionalProperties as V1JSONSchemaProps;