diff --git a/src/KubernetesClient/KubernetesYaml.cs b/src/KubernetesClient/KubernetesYaml.cs index ef5384c7..4891ce5f 100644 --- a/src/KubernetesClient/KubernetesYaml.cs +++ b/src/KubernetesClient/KubernetesYaml.cs @@ -293,6 +293,7 @@ public static string Serialize(object value) private static TBuilder WithOverridesFromJsonPropertyAttributes(this TBuilder builder) where TBuilder : BuilderSkeleton { + var preferredPropertyOrder = new[] { "ApiVersion", "Kind", "Metadata" }; // Use VersionInfo from the model namespace as that should be stable. // If this is not generated in the future we will get an obvious compiler error. var targetNamespace = typeof(VersionInfo).Namespace; @@ -307,15 +308,39 @@ private static TBuilder WithOverridesFromJsonPropertyAttributes(this T // Map any JsonPropertyAttribute instances to YamlMemberAttribute instances. foreach (var type in types) { - foreach (var property in type.GetProperties()) + var properties = type.GetProperties() + .OrderBy(property => property.MetadataToken) + .ToArray(); + var usePreferredOrder = properties.Any(property => + property.Name == "ApiVersion" || + property.Name == "Kind"); + var nextOrder = usePreferredOrder ? preferredPropertyOrder.Length : 0; + foreach (var property in properties) { var jsonAttribute = property.GetCustomAttribute(); - if (jsonAttribute == null) + if (jsonAttribute == null && !usePreferredOrder) { continue; } - var yamlAttribute = new YamlMemberAttribute { Alias = jsonAttribute.Name, ApplyNamingConventions = false }; + var yamlAttribute = new YamlMemberAttribute(); + if (usePreferredOrder) + { + var order = Array.IndexOf(preferredPropertyOrder, property.Name); + if (order < 0) + { + order = nextOrder++; + } + + yamlAttribute.Order = order; + } + + if (jsonAttribute != null) + { + yamlAttribute.Alias = jsonAttribute.Name; + yamlAttribute.ApplyNamingConventions = false; + } + builder.WithAttributeOverride(type, property.Name, yamlAttribute); } } diff --git a/tests/KubernetesClient.Tests/KubernetesYamlTests.cs b/tests/KubernetesClient.Tests/KubernetesYamlTests.cs index 95440e9e..501d2d14 100644 --- a/tests/KubernetesClient.Tests/KubernetesYamlTests.cs +++ b/tests/KubernetesClient.Tests/KubernetesYamlTests.cs @@ -822,12 +822,12 @@ public void WriteSecret() { var kManifest = """ apiVersion: v1 -data: - username: YlhrdFlYQnc= - password: TXprMU1qZ2tkbVJuTjBwaQ== kind: Secret metadata: name: test-secret +data: + username: YlhrdFlYQnc= + password: TXprMU1qZ2tkbVJuTjBwaQ== """; var result = KubernetesYaml.Deserialize(kManifest, true); @@ -860,13 +860,13 @@ public void WriteConfigMap() { var kManifest = """ apiVersion: v1 +kind: ConfigMap +metadata: + name: test-configmap binaryData: username: YlhrdFlYQnc= data: password: Mzk1MjgkdmRnN0pi -kind: ConfigMap -metadata: - name: test-configmap """; var result = KubernetesYaml.Deserialize(kManifest, true); @@ -875,6 +875,28 @@ public void WriteConfigMap() Assert.Equal(kManifest, yaml); } + [Fact] + public void WriteConfigMapOrdersTypeMetaFirst() + { + var configMap = new V1ConfigMap + { + ApiVersion = "v1", + Kind = "ConfigMap", + Metadata = new V1ObjectMeta { Name = "my-configmap", NamespaceProperty = "my-namespace" }, + Data = new Dictionary { { "array.json", "value" } }, + }; + + var yaml = KubernetesYaml.Serialize(configMap); + + Assert.Equal(ToLines(@"apiVersion: v1 +kind: ConfigMap +metadata: + name: my-configmap + namespace: my-namespace +data: + array.json: value"), ToLines(yaml)); + } + [Fact] public void DeserializeWithJsonPropertyName() {