From 7a65fed003b63919b3d342b6a6d6f4980bb1a198 Mon Sep 17 00:00:00 2001 From: Marcus Kimpenhaus Date: Wed, 29 Oct 2025 16:52:32 +0100 Subject: [PATCH 1/6] chore(deps): upgrade `KubernetesClient` to version `18.0.5` - Updated project files to use the latest version of `KubernetesClient` across the solution. - Adjusted `Crds` logic to align with the updated library API. --- .../KubeOps.Abstractions.csproj | 2 +- src/KubeOps.Transpiler/Crds.cs | 37 +++++++++++-------- test/KubeOps.Cli.Test/KubeOps.Cli.Test.csproj | 2 +- .../KubeOps.Operator.Test.csproj | 2 +- .../KubeOps.Operator.Web.Test.csproj | 2 +- .../KubeOps.Transpiler.Test.csproj | 2 +- 6 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/KubeOps.Abstractions/KubeOps.Abstractions.csproj b/src/KubeOps.Abstractions/KubeOps.Abstractions.csproj index 996b4f2a..84762526 100644 --- a/src/KubeOps.Abstractions/KubeOps.Abstractions.csproj +++ b/src/KubeOps.Abstractions/KubeOps.Abstractions.csproj @@ -15,7 +15,7 @@ - + diff --git a/src/KubeOps.Transpiler/Crds.cs b/src/KubeOps.Transpiler/Crds.cs index 104f67b2..dff72d60 100644 --- a/src/KubeOps.Transpiler/Crds.cs +++ b/src/KubeOps.Transpiler/Crds.cs @@ -48,13 +48,13 @@ public static V1CustomResourceDefinition Transpile(this MetadataLoadContext cont { type = context.GetContextType(type); var (meta, scope) = context.ToEntityMetadata(type); - var crd = new V1CustomResourceDefinition(new()).Initialize(); + var crd = new V1CustomResourceDefinition { Spec = new() }.Initialize(); crd.Metadata.Name = $"{meta.PluralName}.{meta.Group}"; crd.Spec.Group = meta.Group; crd.Spec.Names = - new V1CustomResourceDefinitionNames + new() { Kind = meta.Kind, ListKind = meta.ListKind, @@ -68,25 +68,32 @@ public static V1CustomResourceDefinition Transpile(this MetadataLoadContext cont crd.Spec.Names.ShortNames = shortNames.Select(a => a.Value?.ToString()).ToList(); } - var version = new V1CustomResourceDefinitionVersion(meta.Version, true, true); + var version = new V1CustomResourceDefinitionVersion { Name = meta.Version, Served = true, Storage = true }; if (type.GetProperty("Status") != null || type.GetProperty("status") != null) { - version.Subresources = new V1CustomResourceSubresources(null, new object()); + version.Subresources = new() + { + Scale = null, + Status = new(), + }; } - version.Schema = new V1CustomResourceValidation(new V1JSONSchemaProps - { - Type = Object, - Description = - type.GetCustomAttributeData()?.GetCustomAttributeCtorArg(context, 0), - Properties = type.GetProperties() - .Where(p => !IgnoredToplevelProperties.Contains(p.Name.ToLowerInvariant()) - && p.GetCustomAttributeData() == null) - .Select(p => (Name: p.GetPropertyName(context), Schema: context.Map(p))) - .ToDictionary(t => t.Name, t => t.Schema), - }); + version.Schema = new() + { + OpenAPIV3Schema = new() + { + Type = Object, + Description = + type.GetCustomAttributeData()?.GetCustomAttributeCtorArg(context, 0), + Properties = type.GetProperties() + .Where(p => !IgnoredToplevelProperties.Contains(p.Name.ToLowerInvariant()) + && p.GetCustomAttributeData() == null) + .Select(p => (Name: p.GetPropertyName(context), Schema: context.Map(p))) + .ToDictionary(t => t.Name, t => t.Schema), + }, + }; version.AdditionalPrinterColumns = context.MapPrinterColumns(type).ToList() switch { diff --git a/test/KubeOps.Cli.Test/KubeOps.Cli.Test.csproj b/test/KubeOps.Cli.Test/KubeOps.Cli.Test.csproj index 8357c7f7..e14f730b 100644 --- a/test/KubeOps.Cli.Test/KubeOps.Cli.Test.csproj +++ b/test/KubeOps.Cli.Test/KubeOps.Cli.Test.csproj @@ -6,7 +6,7 @@ - + diff --git a/test/KubeOps.Operator.Test/KubeOps.Operator.Test.csproj b/test/KubeOps.Operator.Test/KubeOps.Operator.Test.csproj index 1b545ac7..8f9449e9 100644 --- a/test/KubeOps.Operator.Test/KubeOps.Operator.Test.csproj +++ b/test/KubeOps.Operator.Test/KubeOps.Operator.Test.csproj @@ -1,6 +1,6 @@ - + diff --git a/test/KubeOps.Operator.Web.Test/KubeOps.Operator.Web.Test.csproj b/test/KubeOps.Operator.Web.Test/KubeOps.Operator.Web.Test.csproj index 5ac72a08..c9b338e3 100644 --- a/test/KubeOps.Operator.Web.Test/KubeOps.Operator.Web.Test.csproj +++ b/test/KubeOps.Operator.Web.Test/KubeOps.Operator.Web.Test.csproj @@ -1,6 +1,6 @@ - + diff --git a/test/KubeOps.Transpiler.Test/KubeOps.Transpiler.Test.csproj b/test/KubeOps.Transpiler.Test/KubeOps.Transpiler.Test.csproj index 6a1b39c8..fdafcc13 100644 --- a/test/KubeOps.Transpiler.Test/KubeOps.Transpiler.Test.csproj +++ b/test/KubeOps.Transpiler.Test/KubeOps.Transpiler.Test.csproj @@ -1,7 +1,7 @@ - + From 014a335ce9bd292cb47015b83c02c74f094c8609 Mon Sep 17 00:00:00 2001 From: Marcus Kimpenhaus Date: Fri, 31 Oct 2025 07:46:53 +0100 Subject: [PATCH 2/6] refactor: update object initialization to use object initializer shorthand across tests and core classes - Refactored entity `Metadata` and other class initializations to use object initializer shorthand consistently. - Marked test classes as `sealed` for improved optimization and design clarity. - Adjusted tests and core logic to align with the updated initialization style and coding standards. --- .../CustomKubernetesEntity{TSpec,TStatus}.cs | 2 +- .../Entities/CustomKubernetesEntity{TSpec}.cs | 2 +- .../Entities/KubernetesExtensions.cs | 12 +-- .../Commands/Generator/OperatorGenerator.cs | 5 +- .../Webhooks/WebhookServiceBase.cs | 35 +++++---- src/KubeOps.Transpiler/Crds.cs | 70 +++++++++--------- .../KubernetesClient.Test.cs | 68 ++++++++++++++--- .../KubernetesClientAsync.Test.cs | 74 +++++++++++++++---- .../IntegrationTestCollection.cs | 5 +- .../NamespacedOperator.Integration.Test.cs | 7 +- test/KubeOps.Transpiler.Test/Crds.Mlc.Test.cs | 2 +- 11 files changed, 194 insertions(+), 88 deletions(-) diff --git a/src/KubeOps.Abstractions/Entities/CustomKubernetesEntity{TSpec,TStatus}.cs b/src/KubeOps.Abstractions/Entities/CustomKubernetesEntity{TSpec,TStatus}.cs index c8cfa990..3b3eb3ad 100644 --- a/src/KubeOps.Abstractions/Entities/CustomKubernetesEntity{TSpec,TStatus}.cs +++ b/src/KubeOps.Abstractions/Entities/CustomKubernetesEntity{TSpec,TStatus}.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information. -using k8s; +using k8s.Models; namespace KubeOps.Abstractions.Entities; diff --git a/src/KubeOps.Abstractions/Entities/CustomKubernetesEntity{TSpec}.cs b/src/KubeOps.Abstractions/Entities/CustomKubernetesEntity{TSpec}.cs index fe2f6369..7ffac91d 100644 --- a/src/KubeOps.Abstractions/Entities/CustomKubernetesEntity{TSpec}.cs +++ b/src/KubeOps.Abstractions/Entities/CustomKubernetesEntity{TSpec}.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information. -using k8s; +using k8s.Models; namespace KubeOps.Abstractions.Entities; diff --git a/src/KubeOps.Abstractions/Entities/KubernetesExtensions.cs b/src/KubeOps.Abstractions/Entities/KubernetesExtensions.cs index f9304aab..408db1a5 100644 --- a/src/KubeOps.Abstractions/Entities/KubernetesExtensions.cs +++ b/src/KubeOps.Abstractions/Entities/KubernetesExtensions.cs @@ -83,11 +83,13 @@ public static TEntity WithOwnerReference( /// The object that should be translated. /// The created . public static V1OwnerReference MakeOwnerReference(this IKubernetesObject kubernetesObject) - => new( - kubernetesObject.ApiVersion, - kubernetesObject.Kind, - kubernetesObject.Metadata.Name, - kubernetesObject.Metadata.Uid); + => new() + { + ApiVersion = kubernetesObject.ApiVersion, + Kind = kubernetesObject.Kind, + Name = kubernetesObject.Metadata.Name, + Uid = kubernetesObject.Metadata.Uid, + }; private static IList EnsureOwnerReferences(this V1ObjectMeta meta) => meta.OwnerReferences ??= new List(); diff --git a/src/KubeOps.Cli/Commands/Generator/OperatorGenerator.cs b/src/KubeOps.Cli/Commands/Generator/OperatorGenerator.cs index 227eb389..5c94fb81 100644 --- a/src/KubeOps.Cli/Commands/Generator/OperatorGenerator.cs +++ b/src/KubeOps.Cli/Commands/Generator/OperatorGenerator.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.CommandLine; -using System.CommandLine.Invocation; using System.Text; using k8s; @@ -116,7 +115,7 @@ internal static async Task Handler(IAnsiConsole console, ParseResult parseR result.Add( $"namespace.{format.GetFileExtension()}", - new V1Namespace(metadata: new(name: "system")).Initialize()); + new V1Namespace { Metadata = new() { Name = "system" } }.Initialize()); result.Add( $"kustomization.{format.GetFileExtension()}", @@ -124,7 +123,7 @@ internal static async Task Handler(IAnsiConsole console, ParseResult parseR { NamePrefix = $"{name}-", Namespace = $"{name}-system", - Labels = [new KustomizationCommonLabels(new Dictionary { { "operator", name }, })], + Labels = [new(new Dictionary { { "operator", name }, })], Resources = result.DefaultFormatFiles.ToList(), Images = new List diff --git a/src/KubeOps.Operator.Web/Webhooks/WebhookServiceBase.cs b/src/KubeOps.Operator.Web/Webhooks/WebhookServiceBase.cs index 4948cb19..5d8911c3 100644 --- a/src/KubeOps.Operator.Web/Webhooks/WebhookServiceBase.cs +++ b/src/KubeOps.Operator.Web/Webhooks/WebhookServiceBase.cs @@ -50,12 +50,13 @@ internal async Task RegisterConverters() } var whUrl = $"{Uri}convert/{metadata.Group}/{metadata.PluralName}"; - crd.Spec.Conversion = new V1CustomResourceConversion("Webhook") + crd.Spec.Conversion = new() { - Webhook = new V1WebhookConversion + Strategy = "Webhook", + Webhook = new() { ConversionReviewVersions = new[] { "v1" }, - ClientConfig = new Apiextensionsv1WebhookClientConfig + ClientConfig = new() { Url = whUrl, CaBundle = CaBundle, @@ -89,16 +90,18 @@ internal async Task RegisterMutators() ApiVersions = new[] { hook.Metadata.Version }, }, }, - ClientConfig = new Admissionregistrationv1WebhookClientConfig + ClientConfig = new() { Url = $"{Uri}mutate/{hook.HookTypeName}", CaBundle = CaBundle, }, }); - var mutatorConfig = new V1MutatingWebhookConfiguration( - metadata: new V1ObjectMeta(name: "dev-mutators"), - webhooks: mutationWebhooks.ToList()).Initialize(); + var mutatorConfig = new V1MutatingWebhookConfiguration() + { + Metadata = new() { Name = "dev-mutators" }, + Webhooks = mutationWebhooks.ToList(), + }.Initialize(); if (mutatorConfig.Webhooks.Any()) { @@ -106,7 +109,10 @@ internal async Task RegisterMutators() } } - internal async Task RegisterValidators() + private static string Defaulted(string? value, string defaultValue) => + string.IsNullOrWhiteSpace(value) ? defaultValue : value; + + private async Task RegisterValidators() { var validationWebhooks = loader .ValidationWebhooks @@ -128,23 +134,22 @@ internal async Task RegisterValidators() ApiVersions = new[] { hook.Metadata.Version }, }, }, - ClientConfig = new Admissionregistrationv1WebhookClientConfig + ClientConfig = new() { Url = $"{Uri}validate/{hook.HookTypeName}", CaBundle = CaBundle, }, }); - var validatorConfig = new V1ValidatingWebhookConfiguration( - metadata: new V1ObjectMeta(name: "dev-validators"), - webhooks: validationWebhooks.ToList()).Initialize(); + var validatorConfig = new V1ValidatingWebhookConfiguration() + { + Metadata = new() { Name = "dev-validators" }, + Webhooks = validationWebhooks.ToList(), + }.Initialize(); if (validatorConfig.Webhooks.Any()) { await Client.SaveAsync(validatorConfig); } } - - private static string Defaulted(string? value, string defaultValue) => - string.IsNullOrWhiteSpace(value) ? defaultValue : value; } diff --git a/src/KubeOps.Transpiler/Crds.cs b/src/KubeOps.Transpiler/Crds.cs index dff72d60..9329a920 100644 --- a/src/KubeOps.Transpiler/Crds.cs +++ b/src/KubeOps.Transpiler/Crds.cs @@ -5,7 +5,6 @@ using System.Collections; using System.Collections.ObjectModel; using System.Reflection; -using System.Runtime.Serialization; using System.Text.Json.Serialization; using k8s; @@ -101,7 +100,6 @@ public static V1CustomResourceDefinition Transpile(this MetadataLoadContext cont _ => null, }; crd.Spec.Versions = new List { version }; - crd.Validate(); return crd; } @@ -184,7 +182,7 @@ private static IEnumerable MapPrinterColumns( } var mapped = context.Map(prop); - yield return new V1CustomResourceColumnDefinition + yield return new() { Name = attr.GetCustomAttributeCtorArg(context, 1) ?? prop.GetPropertyName(context), JsonPath = $"{path}.{prop.GetPropertyName(context)}", @@ -201,7 +199,7 @@ private static IEnumerable MapPrinterColumns( foreach (var attr in type.GetCustomAttributesData()) { - yield return new V1CustomResourceColumnDefinition + yield return new() { Name = attr.GetCustomAttributeCtorArg(context, 1), JsonPath = attr.GetCustomAttributeCtorArg(context, 0), @@ -232,9 +230,11 @@ private static V1JSONSchemaProps Map(this MetadataLoadContext context, PropertyI if (prop.GetCustomAttributeData() is { } extDocs) { - props.ExternalDocs = new V1ExternalDocumentation( - extDocs.GetCustomAttributeCtorArg(context, 0), - extDocs.GetCustomAttributeCtorArg(context, 1)); + props.ExternalDocs = new() + { + Url = extDocs.GetCustomAttributeCtorArg(context, 0), + Description = extDocs.GetCustomAttributeCtorArg(context, 1), + }; } if (prop.GetCustomAttributeData() is { } items) @@ -289,12 +289,14 @@ private static V1JSONSchemaProps Map(this MetadataLoadContext context, PropertyI if (prop.GetCustomAttributesData().ToArray() is { Length: > 0 } validations) { props.XKubernetesValidations = validations - .Select(validation => new V1ValidationRule( - validation.GetCustomAttributeCtorArg(context, 0), - fieldPath: validation.GetCustomAttributeCtorArg(context, 1), - message: validation.GetCustomAttributeCtorArg(context, 2), - messageExpression: validation.GetCustomAttributeCtorArg(context, 3), - reason: validation.GetCustomAttributeCtorArg(context, 4))) + .Select(validation => new V1ValidationRule() + { + Rule = validation.GetCustomAttributeCtorArg(context, 0), + FieldPath = validation.GetCustomAttributeCtorArg(context, 1), + Message = validation.GetCustomAttributeCtorArg(context, 2), + MessageExpression = validation.GetCustomAttributeCtorArg(context, 3), + Reason = validation.GetCustomAttributeCtorArg(context, 4), + }) .ToList(); } @@ -305,12 +307,12 @@ private static V1JSONSchemaProps Map(this MetadataLoadContext context, Type type { if (type.FullName == "System.String") { - return new V1JSONSchemaProps { Type = String }; + return new() { Type = String }; } if (type.FullName == "System.Object") { - return new V1JSONSchemaProps { Type = Object, XKubernetesPreserveUnknownFields = true }; + return new() { Type = Object, XKubernetesPreserveUnknownFields = true }; } if (type.Name == typeof(Nullable<>).Name && type.GenericTypeArguments.Length == 1) @@ -336,12 +338,12 @@ private static V1JSONSchemaProps Map(this MetadataLoadContext context, Type type && i.GetGenericTypeDefinition().FullName == typeof(IDictionary<,>).FullName); var additionalProperties = context.Map(dictionaryImpl.GenericTypeArguments[1]); - return new V1JSONSchemaProps { Type = Object, AdditionalProperties = additionalProperties, }; + return new() { Type = Object, AdditionalProperties = additionalProperties, }; } if (interfaceNames.Contains(typeof(IDictionary).FullName)) { - return new V1JSONSchemaProps { Type = Object, XKubernetesPreserveUnknownFields = true }; + return new() { Type = Object, XKubernetesPreserveUnknownFields = true }; } if (interfaceNames.Contains(typeof(IEnumerable<>).FullName)) @@ -381,7 +383,7 @@ static Type GetRootBaseType(Type type) { "System.Object" => context.MapObjectType(type), "System.ValueType" => context.MapValueType(type), - "System.Enum" => new V1JSONSchemaProps { Type = String, EnumProperty = GetEnumNames(context, type), }, + "System.Enum" => new() { Type = String, EnumProperty = GetEnumNames(context, type), }, _ => throw InvalidType(type), }; } @@ -426,17 +428,17 @@ private static V1JSONSchemaProps MapObjectType(this MetadataLoadContext context, { case "k8s.Models.ResourceQuantity": // Quantities are serialized as strings in CRDs (e.g., "500m", "2Gi") - return new V1JSONSchemaProps { Type = String }; + return new() { Type = String }; case "k8s.Models.V1ObjectMeta": - return new V1JSONSchemaProps { Type = Object }; - case "k8s.Models.IntstrIntOrString": - return new V1JSONSchemaProps { XKubernetesIntOrString = true }; + return new() { Type = Object }; + case "k8s.Models.IntOrString": + return new() { XKubernetesIntOrString = true }; default: if (context.GetContextType().IsAssignableFrom(type) && type is { IsAbstract: false, IsInterface: false } && type.Assembly == context.GetContextType().Assembly) { - return new V1JSONSchemaProps + return new() { Type = Object, Properties = null, @@ -445,7 +447,7 @@ private static V1JSONSchemaProps MapObjectType(this MetadataLoadContext context, }; } - return new V1JSONSchemaProps + return new() { Type = Object, Description = @@ -490,24 +492,24 @@ private static V1JSONSchemaProps MapEnumerationType( if (listType.IsGenericType && listType.GetGenericTypeDefinition().FullName == typeof(KeyValuePair<,>).FullName) { var additionalProperties = context.Map(listType.GenericTypeArguments[1]); - return new V1JSONSchemaProps { Type = Object, AdditionalProperties = additionalProperties, }; + return new() { Type = Object, AdditionalProperties = additionalProperties, }; } var items = context.Map(listType); - return new V1JSONSchemaProps { Type = Array, Items = items }; + return new() { Type = Array, Items = items }; } private static V1JSONSchemaProps MapValueType(this MetadataLoadContext _, Type type) => type.FullName switch { - "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 }, + "System.Int32" => new() { Type = Integer, Format = Int32 }, + "System.Int64" => new() { Type = Integer, Format = Int64 }, + "System.Single" => new() { Type = Number, Format = Float }, + "System.Double" => new() { Type = Number, Format = Double }, + "System.Decimal" => new() { Type = Number, Format = Decimal }, + "System.Boolean" => new() { Type = Boolean }, + "System.DateTime" => new() { Type = String, Format = DateTime }, + "System.DateTimeOffset" => new() { Type = String, Format = DateTime }, _ => throw InvalidType(type), }; diff --git a/test/KubeOps.KubernetesClient.Test/KubernetesClient.Test.cs b/test/KubeOps.KubernetesClient.Test/KubernetesClient.Test.cs index aaa88540..ac162447 100644 --- a/test/KubeOps.KubernetesClient.Test/KubernetesClient.Test.cs +++ b/test/KubeOps.KubernetesClient.Test/KubernetesClient.Test.cs @@ -8,7 +8,7 @@ namespace KubeOps.KubernetesClient.Test; -public class KubernetesClientTest : IntegrationTestBase, IDisposable +public sealed class KubernetesClientTest : IntegrationTestBase, IDisposable { private readonly IKubernetesClient _client = new KubernetesClient(); @@ -30,7 +30,11 @@ public void Should_Create_Some_Object() { Kind = V1ConfigMap.KubeKind, ApiVersion = V1ConfigMap.KubeApiVersion, - Metadata = new(name: RandomName(), namespaceProperty: "default"), + Metadata = new() + { + Name = RandomName(), + NamespaceProperty = "default", + }, Data = new Dictionary { { "Hello", "World" } }, }); @@ -48,7 +52,11 @@ public void Should_Get_Some_Object() { Kind = V1ConfigMap.KubeKind, ApiVersion = V1ConfigMap.KubeApiVersion, - Metadata = new(name: RandomName(), namespaceProperty: "default"), + Metadata = new() + { + Name = RandomName(), + NamespaceProperty = "default", + }, Data = new Dictionary { { "Hello", "World" } }, }); @@ -58,7 +66,11 @@ public void Should_Get_Some_Object() { Kind = V1ConfigMap.KubeKind, ApiVersion = V1ConfigMap.KubeApiVersion, - Metadata = new(name: RandomName(), namespaceProperty: "default"), + Metadata = new() + { + Name = RandomName(), + NamespaceProperty = "default", + }, Data = new Dictionary { { "Hello", "World" } }, })); _objects.Add(_client.Create( @@ -66,7 +78,11 @@ public void Should_Get_Some_Object() { Kind = V1ConfigMap.KubeKind, ApiVersion = V1ConfigMap.KubeApiVersion, - Metadata = new(name: RandomName(), namespaceProperty: "default"), + Metadata = new() + { + Name = RandomName(), + NamespaceProperty = "default", + }, Data = new Dictionary { { "Hello", "World" } }, })); @@ -82,7 +98,11 @@ public void Should_Update_Some_Object() { Kind = V1ConfigMap.KubeKind, ApiVersion = V1ConfigMap.KubeApiVersion, - Metadata = new V1ObjectMeta(name: RandomName(), namespaceProperty: "default"), + Metadata = new() + { + Name = RandomName(), + NamespaceProperty = "default", + }, Data = new Dictionary { { "Hello", "World" } }, }); var r1 = config.Metadata.ResourceVersion; @@ -103,7 +123,11 @@ public void Should_List_Some_Objects() { Kind = V1ConfigMap.KubeKind, ApiVersion = V1ConfigMap.KubeApiVersion, - Metadata = new(name: RandomName(), namespaceProperty: "default"), + Metadata = new() + { + Name = RandomName(), + NamespaceProperty = "default", + }, Data = new Dictionary { { "Hello", "World" } }, }); var config2 = _client.Create( @@ -111,7 +135,11 @@ public void Should_List_Some_Objects() { Kind = V1ConfigMap.KubeKind, ApiVersion = V1ConfigMap.KubeApiVersion, - Metadata = new(name: RandomName(), namespaceProperty: "default"), + Metadata = new() + { + Name = RandomName(), + NamespaceProperty = "default", + }, Data = new Dictionary { { "Hello", "World" } }, }); @@ -132,7 +160,11 @@ public void Should_Delete_Some_Object() { Kind = V1ConfigMap.KubeKind, ApiVersion = V1ConfigMap.KubeApiVersion, - Metadata = new(name: RandomName(), namespaceProperty: "default"), + Metadata = new() + { + Name = RandomName(), + NamespaceProperty = "default", + }, Data = new Dictionary { { "Hello", "World" } }, }); var config2 = _client.Create( @@ -140,7 +172,11 @@ public void Should_Delete_Some_Object() { Kind = V1ConfigMap.KubeKind, ApiVersion = V1ConfigMap.KubeApiVersion, - Metadata = new(name: RandomName(), namespaceProperty: "default"), + Metadata = new() + { + Name = RandomName(), + NamespaceProperty = "default", + }, Data = new Dictionary { { "Hello", "World" } }, }); _objects.Add(config1); @@ -161,7 +197,11 @@ public void Should_Not_Throw_On_Not_Found_Delete() { Kind = V1ConfigMap.KubeKind, ApiVersion = V1ConfigMap.KubeApiVersion, - Metadata = new(name: RandomName(), namespaceProperty: "default"), + Metadata = new() + { + Name = RandomName(), + NamespaceProperty = "default", + }, Data = new Dictionary { { "Hello", "World" } }, }; _client.Delete(config); @@ -176,7 +216,11 @@ public void Should_Patch_ConfigMap_Sync() { Kind = V1ConfigMap.KubeKind, ApiVersion = V1ConfigMap.KubeApiVersion, - Metadata = new(name: RandomName(), namespaceProperty: "default"), + Metadata = new() + { + Name = RandomName(), + NamespaceProperty = "default", + }, Data = new Dictionary { { "foo", "bar" } }, }); _objects.Add(config); diff --git a/test/KubeOps.KubernetesClient.Test/KubernetesClientAsync.Test.cs b/test/KubeOps.KubernetesClient.Test/KubernetesClientAsync.Test.cs index 3a844a9a..cc14e0ab 100644 --- a/test/KubeOps.KubernetesClient.Test/KubernetesClientAsync.Test.cs +++ b/test/KubeOps.KubernetesClient.Test/KubernetesClientAsync.Test.cs @@ -8,7 +8,7 @@ namespace KubeOps.KubernetesClient.Test; -public class KubernetesClientAsyncTest : IntegrationTestBase, IDisposable +public sealed class KubernetesClientAsyncTest : IntegrationTestBase, IDisposable { private readonly IKubernetesClient _client = new KubernetesClient(); @@ -30,7 +30,11 @@ public async Task Should_Create_Some_Object() { Kind = V1ConfigMap.KubeKind, ApiVersion = V1ConfigMap.KubeApiVersion, - Metadata = new(name: RandomName(), namespaceProperty: "default"), + Metadata = new() + { + Name = RandomName(), + NamespaceProperty = "default", + }, Data = new Dictionary { { "Hello", "World" } }, }); @@ -48,7 +52,11 @@ public async Task Should_Get_Some_Object() { Kind = V1ConfigMap.KubeKind, ApiVersion = V1ConfigMap.KubeApiVersion, - Metadata = new(name: RandomName(), namespaceProperty: "default"), + Metadata = new() + { + Name = RandomName(), + NamespaceProperty = "default", + }, Data = new Dictionary { { "Hello", "World" } }, }); @@ -58,7 +66,11 @@ public async Task Should_Get_Some_Object() { Kind = V1ConfigMap.KubeKind, ApiVersion = V1ConfigMap.KubeApiVersion, - Metadata = new(name: RandomName(), namespaceProperty: "default"), + Metadata = new() + { + Name = RandomName(), + NamespaceProperty = "default", + }, Data = new Dictionary { { "Hello", "World" } }, })); _objects.Add(await _client.CreateAsync( @@ -66,7 +78,11 @@ public async Task Should_Get_Some_Object() { Kind = V1ConfigMap.KubeKind, ApiVersion = V1ConfigMap.KubeApiVersion, - Metadata = new(name: RandomName(), namespaceProperty: "default"), + Metadata = new() + { + Name = RandomName(), + NamespaceProperty = "default", + }, Data = new Dictionary { { "Hello", "World" } }, })); @@ -82,7 +98,11 @@ public async Task Should_Update_Some_Object() { Kind = V1ConfigMap.KubeKind, ApiVersion = V1ConfigMap.KubeApiVersion, - Metadata = new V1ObjectMeta(name: RandomName(), namespaceProperty: "default"), + Metadata = new() + { + Name = RandomName(), + NamespaceProperty = "default", + }, Data = new Dictionary { { "Hello", "World" } }, }); var r1 = config.Metadata.ResourceVersion; @@ -103,7 +123,11 @@ public async Task Should_List_Some_Objects() { Kind = V1ConfigMap.KubeKind, ApiVersion = V1ConfigMap.KubeApiVersion, - Metadata = new(name: RandomName(), namespaceProperty: "default"), + Metadata = new() + { + Name = RandomName(), + NamespaceProperty = "default", + }, Data = new Dictionary { { "Hello", "World" } }, }); var config2 = await _client.CreateAsync( @@ -111,7 +135,11 @@ public async Task Should_List_Some_Objects() { Kind = V1ConfigMap.KubeKind, ApiVersion = V1ConfigMap.KubeApiVersion, - Metadata = new(name: RandomName(), namespaceProperty: "default"), + Metadata = new() + { + Name = RandomName(), + NamespaceProperty = "default", + }, Data = new Dictionary { { "Hello", "World" } }, }); @@ -132,7 +160,11 @@ public async Task Should_Delete_Some_Object() { Kind = V1ConfigMap.KubeKind, ApiVersion = V1ConfigMap.KubeApiVersion, - Metadata = new(name: RandomName(), namespaceProperty: "default"), + Metadata = new() + { + Name = RandomName(), + NamespaceProperty = "default", + }, Data = new Dictionary { { "Hello", "World" } }, }); var config2 = await _client.CreateAsync( @@ -140,7 +172,11 @@ public async Task Should_Delete_Some_Object() { Kind = V1ConfigMap.KubeKind, ApiVersion = V1ConfigMap.KubeApiVersion, - Metadata = new(name: RandomName(), namespaceProperty: "default"), + Metadata = new() + { + Name = RandomName(), + NamespaceProperty = "default", + }, Data = new Dictionary { { "Hello", "World" } }, }); _objects.Add(config1); @@ -161,7 +197,11 @@ public async Task Should_Not_Throw_On_Not_Found_Delete() { Kind = V1ConfigMap.KubeKind, ApiVersion = V1ConfigMap.KubeApiVersion, - Metadata = new(name: RandomName(), namespaceProperty: "default"), + Metadata = new() + { + Name = RandomName(), + NamespaceProperty = "default", + }, Data = new Dictionary { { "Hello", "World" } }, }; await _client.DeleteAsync(config); @@ -176,7 +216,11 @@ public async Task Should_Patch_ConfigMap_Async() { Kind = V1ConfigMap.KubeKind, ApiVersion = V1ConfigMap.KubeApiVersion, - Metadata = new(name: RandomName(), namespaceProperty: "default"), + Metadata = new() + { + Name = RandomName(), + NamespaceProperty = "default", + }, Data = new Dictionary { { "foo", "bar" } }, }); _objects.Add(config); @@ -213,7 +257,11 @@ public async Task Should_Patch_ConfigMap_With_Stale_Base_Async() { Kind = V1ConfigMap.KubeKind, ApiVersion = V1ConfigMap.KubeApiVersion, - Metadata = new(name: RandomName(), namespaceProperty: "default"), + Metadata = new() + { + Name = RandomName(), + NamespaceProperty = "default", + }, Data = new Dictionary { { "foo", "bar" } }, }); _objects.Add(original); diff --git a/test/KubeOps.Operator.Test/IntegrationTestCollection.cs b/test/KubeOps.Operator.Test/IntegrationTestCollection.cs index 35ea16be..fba46a9d 100644 --- a/test/KubeOps.Operator.Test/IntegrationTestCollection.cs +++ b/test/KubeOps.Operator.Test/IntegrationTestCollection.cs @@ -64,7 +64,10 @@ public sealed class TestNamespaceProvider : IAsyncLifetime public async Task InitializeAsync() { _namespace = - await _client.CreateAsync(new V1Namespace(metadata: new V1ObjectMeta(name: Namespace)).Initialize()); + await _client.CreateAsync(new V1Namespace() + { + Metadata = new() { Name = Namespace }, + }.Initialize()); } public async Task DisposeAsync() diff --git a/test/KubeOps.Operator.Test/NamespacedOperator.Integration.Test.cs b/test/KubeOps.Operator.Test/NamespacedOperator.Integration.Test.cs index 4b754878..9b7296e7 100644 --- a/test/KubeOps.Operator.Test/NamespacedOperator.Integration.Test.cs +++ b/test/KubeOps.Operator.Test/NamespacedOperator.Integration.Test.cs @@ -16,7 +16,7 @@ namespace KubeOps.Operator.Test; -public class NamespacedOperatorIntegrationTest : IntegrationTestBase +public sealed class NamespacedOperatorIntegrationTest : IntegrationTestBase { private readonly InvocationCounter _mock = new(); private readonly IKubernetesClient _client = new KubernetesClient.KubernetesClient(); @@ -58,7 +58,10 @@ public override async Task InitializeAsync() { await base.InitializeAsync(); _otherNamespace = - await _client.CreateAsync(new V1Namespace(metadata: new(name: Guid.NewGuid().ToString().ToLower())) + await _client.CreateAsync(new V1Namespace + { + Metadata = new() { Name = Guid.NewGuid().ToString().ToLower() }, + } .Initialize()); await _ns.InitializeAsync(); } diff --git a/test/KubeOps.Transpiler.Test/Crds.Mlc.Test.cs b/test/KubeOps.Transpiler.Test/Crds.Mlc.Test.cs index db2e217f..4bb2cc56 100644 --- a/test/KubeOps.Transpiler.Test/Crds.Mlc.Test.cs +++ b/test/KubeOps.Transpiler.Test/Crds.Mlc.Test.cs @@ -769,7 +769,7 @@ private class EnumerableKeyPairsEntity : CustomKubernetesEntity [KubernetesEntity(Group = "testing.dev", ApiVersion = "v1", Kind = "TestEntity")] private class IntstrOrStringEntity : CustomKubernetesEntity { - public IntstrIntOrString Property { get; set; } = null!; + public IntOrString Property { get; set; } = null!; } [KubernetesEntity(Group = "testing.dev", ApiVersion = "v1", Kind = "TestEntity")] From 464730ad8ab746716bfca2c87490e97892001c59 Mon Sep 17 00:00:00 2001 From: Marcus Kimpenhaus Date: Fri, 31 Oct 2025 08:05:05 +0100 Subject: [PATCH 3/6] refactor: apply object initializer shorthand and seal generator classes - Updated object initializations across generator classes to use initializer shorthand for consistency. - Marked generator classes as `sealed` for optimization and better design practices. - Improved readability and alignment with modern C# coding standards. --- .../Generators/DeploymentGenerator.cs | 45 ++++++----- .../Generators/MutationWebhookGenerator.cs | 14 ++-- src/KubeOps.Cli/Generators/RbacGenerator.cs | 26 +++++-- .../Generators/ValidationWebhookGenerator.cs | 16 ++-- .../Generators/WebhookDeploymentGenerator.cs | 78 +++++++++++-------- 5 files changed, 110 insertions(+), 69 deletions(-) diff --git a/src/KubeOps.Cli/Generators/DeploymentGenerator.cs b/src/KubeOps.Cli/Generators/DeploymentGenerator.cs index 04669be4..b8e31627 100644 --- a/src/KubeOps.Cli/Generators/DeploymentGenerator.cs +++ b/src/KubeOps.Cli/Generators/DeploymentGenerator.cs @@ -9,24 +9,33 @@ namespace KubeOps.Cli.Generators; -internal class DeploymentGenerator(OutputFormat format) : IConfigGenerator +internal sealed class DeploymentGenerator(OutputFormat format) : IConfigGenerator { public void Generate(ResultOutput output) { - var deployment = new V1Deployment(metadata: new V1ObjectMeta( - labels: new Dictionary { { "operator-deployment", "kubernetes-operator" } }, - name: "operator")).Initialize(); - deployment.Spec = new V1DeploymentSpec + var deployment = new V1Deployment + { + Metadata = new() + { + Name = "operator", + Labels = new Dictionary { { "operator-deployment", "kubernetes-operator" } }, + }, + }.Initialize(); + deployment.Spec = new() { Replicas = 1, RevisionHistoryLimit = 0, - Selector = new V1LabelSelector( - matchLabels: new Dictionary { { "operator-deployment", "kubernetes-operator" } }), - Template = new V1PodTemplateSpec + Selector = new() { - Metadata = new V1ObjectMeta( - labels: new Dictionary { { "operator-deployment", "kubernetes-operator" } }), - Spec = new V1PodSpec + MatchLabels = new Dictionary { { "operator-deployment", "kubernetes-operator" } }, + }, + Template = new() + { + Metadata = new() + { + Labels = new Dictionary { { "operator-deployment", "kubernetes-operator" } }, + }, + Spec = new() { TerminationGracePeriodSeconds = 10, Containers = new List @@ -41,26 +50,26 @@ public void Generate(ResultOutput output) { Name = "POD_NAMESPACE", ValueFrom = - new V1EnvVarSource + new() { - FieldRef = new V1ObjectFieldSelector + FieldRef = new() { FieldPath = "metadata.namespace", }, }, }, }, - Resources = new V1ResourceRequirements + Resources = new() { Requests = new Dictionary { - { "cpu", new ResourceQuantity("100m") }, - { "memory", new ResourceQuantity("64Mi") }, + { "cpu", new("100m") }, + { "memory", new("64Mi") }, }, Limits = new Dictionary { - { "cpu", new ResourceQuantity("100m") }, - { "memory", new ResourceQuantity("128Mi") }, + { "cpu", new("100m") }, + { "memory", new("128Mi") }, }, }, }, diff --git a/src/KubeOps.Cli/Generators/MutationWebhookGenerator.cs b/src/KubeOps.Cli/Generators/MutationWebhookGenerator.cs index 1e49a9fa..bf904e75 100644 --- a/src/KubeOps.Cli/Generators/MutationWebhookGenerator.cs +++ b/src/KubeOps.Cli/Generators/MutationWebhookGenerator.cs @@ -20,13 +20,15 @@ public void Generate(ResultOutput output) return; } - var mutatorConfig = new V1MutatingWebhookConfiguration( - metadata: new V1ObjectMeta(name: "mutators"), - webhooks: new List()).Initialize(); + var mutatorConfig = new V1MutatingWebhookConfiguration + { + Metadata = new() { Name = "mutators" }, + Webhooks = new List(), + }.Initialize(); foreach (var hook in webhooks) { - mutatorConfig.Webhooks.Add(new V1MutatingWebhook + mutatorConfig.Webhooks.Add(new() { Name = $"mutate.{hook.Metadata.SingularName}.{hook.Metadata.Group}.{hook.Metadata.Version}", MatchPolicy = "Exact", @@ -42,10 +44,10 @@ public void Generate(ResultOutput output) ApiVersions = new[] { hook.Metadata.Version }, }, }, - ClientConfig = new Admissionregistrationv1WebhookClientConfig + ClientConfig = new() { CaBundle = caBundle, - Service = new Admissionregistrationv1ServiceReference + Service = new() { Name = "operator", Path = hook.WebhookPath, diff --git a/src/KubeOps.Cli/Generators/RbacGenerator.cs b/src/KubeOps.Cli/Generators/RbacGenerator.cs index a1001516..c419fc1e 100644 --- a/src/KubeOps.Cli/Generators/RbacGenerator.cs +++ b/src/KubeOps.Cli/Generators/RbacGenerator.cs @@ -14,7 +14,7 @@ namespace KubeOps.Cli.Generators; -internal class RbacGenerator(MetadataLoadContext parser, +internal sealed class RbacGenerator(MetadataLoadContext parser, OutputFormat outputFormat) : IConfigGenerator { public void Generate(ResultOutput output) @@ -24,16 +24,28 @@ public void Generate(ResultOutput output) .Concat(parser.GetContextType().GetCustomAttributesData()) .ToList(); - var role = new V1ClusterRole(rules: parser.Transpile(attributes).ToList()).Initialize(); + var role = new V1ClusterRole { Rules = parser.Transpile(attributes).ToList() }.Initialize(); role.Metadata.Name = "operator-role"; output.Add($"operator-role.{outputFormat.GetFileExtension()}", role); - var roleBinding = new V1ClusterRoleBinding( - roleRef: new V1RoleRef(V1ClusterRole.KubeGroup, V1ClusterRole.KubeKind, "operator-role"), - subjects: new List + var roleBinding = new V1ClusterRoleBinding + { + RoleRef = new() { - new(V1ServiceAccount.KubeKind, "default", namespaceProperty: "system"), - }) + ApiGroup = V1ClusterRole.KubeGroup, + Kind = V1ClusterRole.KubeKind, + Name = "operator-role", + }, + Subjects = new List + { + new() + { + Kind = V1ServiceAccount.KubeKind, + Name = "default", + NamespaceProperty = "system", + }, + }, + } .Initialize(); roleBinding.Metadata.Name = "operator-role-binding"; output.Add($"operator-role-binding.{outputFormat.GetFileExtension()}", roleBinding); diff --git a/src/KubeOps.Cli/Generators/ValidationWebhookGenerator.cs b/src/KubeOps.Cli/Generators/ValidationWebhookGenerator.cs index bb70a3a4..0b1faec1 100644 --- a/src/KubeOps.Cli/Generators/ValidationWebhookGenerator.cs +++ b/src/KubeOps.Cli/Generators/ValidationWebhookGenerator.cs @@ -10,7 +10,7 @@ namespace KubeOps.Cli.Generators; -internal class ValidationWebhookGenerator +internal sealed class ValidationWebhookGenerator (List webhooks, byte[] caBundle, OutputFormat format) : IConfigGenerator { public void Generate(ResultOutput output) @@ -20,13 +20,15 @@ public void Generate(ResultOutput output) return; } - var validatorConfig = new V1ValidatingWebhookConfiguration( - metadata: new V1ObjectMeta(name: "validators"), - webhooks: new List()).Initialize(); + var validatorConfig = new V1ValidatingWebhookConfiguration + { + Metadata = new() { Name = "validators" }, + Webhooks = new List(), + }.Initialize(); foreach (var hook in webhooks) { - validatorConfig.Webhooks.Add(new V1ValidatingWebhook + validatorConfig.Webhooks.Add(new() { Name = $"validate.{hook.Metadata.SingularName}.{hook.Metadata.Group}.{hook.Metadata.Version}", MatchPolicy = "Exact", @@ -42,10 +44,10 @@ public void Generate(ResultOutput output) ApiVersions = new[] { hook.Metadata.Version }, }, }, - ClientConfig = new Admissionregistrationv1WebhookClientConfig + ClientConfig = new() { CaBundle = caBundle, - Service = new Admissionregistrationv1ServiceReference + Service = new() { Name = "operator", Path = hook.WebhookPath, diff --git a/src/KubeOps.Cli/Generators/WebhookDeploymentGenerator.cs b/src/KubeOps.Cli/Generators/WebhookDeploymentGenerator.cs index 6f4f449a..4ef06745 100644 --- a/src/KubeOps.Cli/Generators/WebhookDeploymentGenerator.cs +++ b/src/KubeOps.Cli/Generators/WebhookDeploymentGenerator.cs @@ -2,37 +2,40 @@ // The .NET Foundation licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information. -using System.Reflection; - using k8s; using k8s.Models; using KubeOps.Cli.Output; -using KubeOps.Cli.Transpilation; -using KubeOps.Transpiler; namespace KubeOps.Cli.Generators; -internal class WebhookDeploymentGenerator(OutputFormat format) : IConfigGenerator +internal sealed class WebhookDeploymentGenerator(OutputFormat format) : IConfigGenerator { public void Generate(ResultOutput output) { - var deployment = new V1Deployment(metadata: new V1ObjectMeta( - labels: new Dictionary { { "operator-deployment", "kubernetes-operator" } }, - name: "operator")).Initialize(); - deployment.Spec = new V1DeploymentSpec + var deployment = new V1Deployment + { + Metadata = new() + { + Name = "operator", + Labels = new Dictionary { { "operator-deployment", "kubernetes-operator" } }, + }, + }.Initialize(); + deployment.Spec = new() { Replicas = 1, RevisionHistoryLimit = 0, - Selector = new V1LabelSelector( - matchLabels: - new Dictionary { { "operator-deployment", "kubernetes-operator" } }), - Template = new V1PodTemplateSpec + Selector = new() { - Metadata = new V1ObjectMeta( - labels: - new Dictionary { { "operator-deployment", "kubernetes-operator" }, }), - Spec = new V1PodSpec + MatchLabels = new Dictionary { { "operator-deployment", "kubernetes-operator" } }, + }, + Template = new() + { + Metadata = new() + { + Labels = new Dictionary { { "operator-deployment", "kubernetes-operator" } }, + }, + Spec = new() { TerminationGracePeriodSeconds = 10, Volumes = new List @@ -57,9 +60,9 @@ public void Generate(ResultOutput output) { Name = "POD_NAMESPACE", ValueFrom = - new V1EnvVarSource + new() { - FieldRef = new V1ObjectFieldSelector + FieldRef = new() { FieldPath = "metadata.namespace", }, @@ -71,18 +74,18 @@ public void Generate(ResultOutput output) { new() { ConfigMapRef = new() { Name = "webhook-config" } }, }, - Ports = new List { new(5001, name: "https"), }, - Resources = new V1ResourceRequirements + Ports = new List { new() { HostPort = 5001, Name = "https" } }, + Resources = new() { Requests = new Dictionary { - { "cpu", new ResourceQuantity("100m") }, - { "memory", new ResourceQuantity("64Mi") }, + { "cpu", new("100m") }, + { "memory", new("64Mi") }, }, Limits = new Dictionary { - { "cpu", new ResourceQuantity("100m") }, - { "memory", new ResourceQuantity("128Mi") }, + { "cpu", new("100m") }, + { "memory", new("128Mi") }, }, }, }, @@ -94,13 +97,26 @@ public void Generate(ResultOutput output) output.Add( $"service.{format.GetFileExtension()}", - new V1Service( - metadata: new V1ObjectMeta(name: "operator"), - spec: new V1ServiceSpec + new V1Service + { + Metadata = new() { Name = "operator" }, + Spec = new() { Ports = - new List { new() { Name = "https", TargetPort = "https", Port = 443, }, }, - Selector = new Dictionary { { "operator-deployment", "kubernetes-operator" }, }, - }).Initialize()); + new List + { + new() + { + Name = "https", + TargetPort = "https", + Port = 443, + }, + }, + Selector = new Dictionary + { + { "operator-deployment", "kubernetes-operator" }, + }, + }, + }.Initialize()); } } From a8a1366b391702fef6a6e4494dae7ee82f50d356 Mon Sep 17 00:00:00 2001 From: Marcus Kimpenhaus Date: Fri, 31 Oct 2025 08:34:02 +0100 Subject: [PATCH 4/6] refactor: mark entities and tests as `sealed`, add null checks in finalizer tests - Marked entities (`V1OperatorIntegrationTestEntity` and its sub-classes) and test classes as `sealed` for better design and optimization. - Added null checks in finalizer integration tests to improve safety and adherence to modern C# standards. - Disabled obsolete warning in `KubernetesClient` with a TODO for clarification. --- src/KubeOps.KubernetesClient/KubernetesClient.cs | 2 ++ .../Finalizer/EntityFinalizer.Integration.Test.cs | 6 ++++-- .../TestEntities/V1OperatorIntegrationTestEntity.cs | 6 +++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/KubeOps.KubernetesClient/KubernetesClient.cs b/src/KubeOps.KubernetesClient/KubernetesClient.cs index 95383aab..18853eca 100644 --- a/src/KubeOps.KubernetesClient/KubernetesClient.cs +++ b/src/KubeOps.KubernetesClient/KubernetesClient.cs @@ -16,6 +16,8 @@ namespace KubeOps.KubernetesClient; +#pragma warning disable CS0618 // Type or member is obsolete - TODO: clarify with k8s team + /// public class KubernetesClient : IKubernetesClient { diff --git a/test/KubeOps.Operator.Test/Finalizer/EntityFinalizer.Integration.Test.cs b/test/KubeOps.Operator.Test/Finalizer/EntityFinalizer.Integration.Test.cs index 2c046943..36f979f8 100644 --- a/test/KubeOps.Operator.Test/Finalizer/EntityFinalizer.Integration.Test.cs +++ b/test/KubeOps.Operator.Test/Finalizer/EntityFinalizer.Integration.Test.cs @@ -16,7 +16,7 @@ namespace KubeOps.Operator.Test.Finalizer; -public class EntityFinalizerIntegrationTest : IntegrationTestBase +public sealed class EntityFinalizerIntegrationTest : IntegrationTestBase { private readonly InvocationCounter _mock = new(); private readonly IKubernetesClient _client = new KubernetesClient.KubernetesClient(); @@ -91,7 +91,9 @@ public async Task Should_Attach_Multiple_Finalizer_On_Entity() await watcherCounter.WaitForInvocations; var result = await _client.GetAsync("first-second", _ns.Namespace); - result!.Metadata.Finalizers.Should().Contain("first"); + result.Should().NotBeNull(); + result.Metadata.Should().NotBeNull(); + result.Metadata.Finalizers.Should().Contain("first"); result.Metadata.Finalizers.Should().Contain("second"); } diff --git a/test/KubeOps.Operator.Test/TestEntities/V1OperatorIntegrationTestEntity.cs b/test/KubeOps.Operator.Test/TestEntities/V1OperatorIntegrationTestEntity.cs index 39cb0652..3e9547c4 100644 --- a/test/KubeOps.Operator.Test/TestEntities/V1OperatorIntegrationTestEntity.cs +++ b/test/KubeOps.Operator.Test/TestEntities/V1OperatorIntegrationTestEntity.cs @@ -9,7 +9,7 @@ namespace KubeOps.Operator.Test.TestEntities; [KubernetesEntity(Group = "operator.test", ApiVersion = "v1", Kind = "OperatorIntegrationTest")] -public class V1OperatorIntegrationTestEntity : CustomKubernetesEntity { public V1OperatorIntegrationTestEntity() @@ -27,12 +27,12 @@ public V1OperatorIntegrationTestEntity(string name, string username, string ns) public override string ToString() => $"Test Entity ({Metadata.Name}): {Spec.Username}"; - public class EntitySpec + public sealed class EntitySpec { public string Username { get; set; } = string.Empty; } - public class EntityStatus + public sealed class EntityStatus { public string Status { get; set; } = string.Empty; } From 812252caa178b4995e8466f5bfdc50ec4a3ccb99 Mon Sep 17 00:00:00 2001 From: Marcus Kimpenhaus Date: Fri, 31 Oct 2025 08:57:12 +0100 Subject: [PATCH 5/6] refactor: adjust formatting for consistency and readability across tests and core classes - Updated object and dictionary initializations to improve readability. - Applied consistent formatting to multiline constructs and private methods. - Improved alignment with modern C# coding style. --- src/KubeOps.Cli/Generators/RbacGenerator.cs | 3 ++- .../KubernetesClient.Test.cs | 17 +++++++++++------ .../KubernetesClientAsync.Test.cs | 17 +++++++++++------ .../NamespacedOperator.Integration.Test.cs | 3 ++- 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/KubeOps.Cli/Generators/RbacGenerator.cs b/src/KubeOps.Cli/Generators/RbacGenerator.cs index c419fc1e..767e2e0e 100644 --- a/src/KubeOps.Cli/Generators/RbacGenerator.cs +++ b/src/KubeOps.Cli/Generators/RbacGenerator.cs @@ -14,7 +14,8 @@ namespace KubeOps.Cli.Generators; -internal sealed class RbacGenerator(MetadataLoadContext parser, +internal sealed class RbacGenerator( + MetadataLoadContext parser, OutputFormat outputFormat) : IConfigGenerator { public void Generate(ResultOutput output) diff --git a/test/KubeOps.KubernetesClient.Test/KubernetesClient.Test.cs b/test/KubeOps.KubernetesClient.Test/KubernetesClient.Test.cs index ac162447..6f5b81ec 100644 --- a/test/KubeOps.KubernetesClient.Test/KubernetesClient.Test.cs +++ b/test/KubeOps.KubernetesClient.Test/KubernetesClient.Test.cs @@ -198,10 +198,10 @@ public void Should_Not_Throw_On_Not_Found_Delete() Kind = V1ConfigMap.KubeKind, ApiVersion = V1ConfigMap.KubeApiVersion, Metadata = new() - { - Name = RandomName(), - NamespaceProperty = "default", - }, + { + Name = RandomName(), + NamespaceProperty = "default", + }, Data = new Dictionary { { "Hello", "World" } }, }; _client.Delete(config); @@ -237,7 +237,11 @@ public void Should_Patch_ConfigMap_Sync() Kind = V1ConfigMap.KubeKind, ApiVersion = V1ConfigMap.KubeApiVersion, Metadata = from.Metadata, - Data = new Dictionary { { "foo", "baz" }, { "hello", "world" } }, + Data = new Dictionary + { + { "foo", "baz" }, + { "hello", "world" } + }, }; config = _client.Patch(from, to); config.Data["foo"].Should().Be("baz"); @@ -253,5 +257,6 @@ public void Dispose() _client.Delete(_objects); } - private static string RandomName() => "cm-" + Guid.NewGuid().ToString().ToLower(); + private static string RandomName() + => "cm-" + Guid.NewGuid().ToString().ToLower(); } diff --git a/test/KubeOps.KubernetesClient.Test/KubernetesClientAsync.Test.cs b/test/KubeOps.KubernetesClient.Test/KubernetesClientAsync.Test.cs index cc14e0ab..bc74000b 100644 --- a/test/KubeOps.KubernetesClient.Test/KubernetesClientAsync.Test.cs +++ b/test/KubeOps.KubernetesClient.Test/KubernetesClientAsync.Test.cs @@ -198,10 +198,10 @@ public async Task Should_Not_Throw_On_Not_Found_Delete() Kind = V1ConfigMap.KubeKind, ApiVersion = V1ConfigMap.KubeApiVersion, Metadata = new() - { - Name = RandomName(), - NamespaceProperty = "default", - }, + { + Name = RandomName(), + NamespaceProperty = "default", + }, Data = new Dictionary { { "Hello", "World" } }, }; await _client.DeleteAsync(config); @@ -237,7 +237,11 @@ public async Task Should_Patch_ConfigMap_Async() Kind = V1ConfigMap.KubeKind, ApiVersion = V1ConfigMap.KubeApiVersion, Metadata = from.Metadata, - Data = new Dictionary { { "foo", "baz" }, { "hello", "world" } }, + Data = new Dictionary + { + { "foo", "baz" }, + { "hello", "world" } + }, }; config = await _client.PatchAsync(from, to); config.Data["foo"].Should().Be("baz"); @@ -283,5 +287,6 @@ public void Dispose() _client.Delete(_objects); } - private static string RandomName() => "cm-" + Guid.NewGuid().ToString().ToLower(); + private static string RandomName() + => "cm-" + Guid.NewGuid().ToString().ToLower(); } diff --git a/test/KubeOps.Operator.Test/NamespacedOperator.Integration.Test.cs b/test/KubeOps.Operator.Test/NamespacedOperator.Integration.Test.cs index 9b7296e7..e8e8a790 100644 --- a/test/KubeOps.Operator.Test/NamespacedOperator.Integration.Test.cs +++ b/test/KubeOps.Operator.Test/NamespacedOperator.Integration.Test.cs @@ -82,7 +82,8 @@ protected override void ConfigureHost(HostApplicationBuilder builder) .AddController(); } - private class TestController(InvocationCounter svc) : IEntityController + private class TestController(InvocationCounter svc) + : IEntityController { public Task ReconcileAsync(V1OperatorIntegrationTestEntity entity, CancellationToken cancellationToken) { From ed5c1402e55e3554884c5220a91c857068a49936 Mon Sep 17 00:00:00 2001 From: Marcus Kimpenhaus Date: Fri, 31 Oct 2025 09:00:48 +0100 Subject: [PATCH 6/6] refactor: fixed whitespace formatting - Adjusted formatting in `RbacGenerator` and `NamespacedOperator.Integration.Test` to enhance consistency and readability. --- src/KubeOps.Cli/Generators/RbacGenerator.cs | 30 +++++++++---------- .../NamespacedOperator.Integration.Test.cs | 3 +- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/KubeOps.Cli/Generators/RbacGenerator.cs b/src/KubeOps.Cli/Generators/RbacGenerator.cs index 767e2e0e..8975a1de 100644 --- a/src/KubeOps.Cli/Generators/RbacGenerator.cs +++ b/src/KubeOps.Cli/Generators/RbacGenerator.cs @@ -30,24 +30,24 @@ public void Generate(ResultOutput output) output.Add($"operator-role.{outputFormat.GetFileExtension()}", role); var roleBinding = new V1ClusterRoleBinding + { + RoleRef = new() { - RoleRef = new() - { - ApiGroup = V1ClusterRole.KubeGroup, - Kind = V1ClusterRole.KubeKind, - Name = "operator-role", - }, - Subjects = new List + ApiGroup = V1ClusterRole.KubeGroup, + Kind = V1ClusterRole.KubeKind, + Name = "operator-role", + }, + Subjects = new List + { + new() { - new() - { - Kind = V1ServiceAccount.KubeKind, - Name = "default", - NamespaceProperty = "system", - }, + Kind = V1ServiceAccount.KubeKind, + Name = "default", + NamespaceProperty = "system", }, - } - .Initialize(); + }, + } + .Initialize(); roleBinding.Metadata.Name = "operator-role-binding"; output.Add($"operator-role-binding.{outputFormat.GetFileExtension()}", roleBinding); } diff --git a/test/KubeOps.Operator.Test/NamespacedOperator.Integration.Test.cs b/test/KubeOps.Operator.Test/NamespacedOperator.Integration.Test.cs index e8e8a790..936fe057 100644 --- a/test/KubeOps.Operator.Test/NamespacedOperator.Integration.Test.cs +++ b/test/KubeOps.Operator.Test/NamespacedOperator.Integration.Test.cs @@ -58,7 +58,8 @@ public override async Task InitializeAsync() { await base.InitializeAsync(); _otherNamespace = - await _client.CreateAsync(new V1Namespace + await _client.CreateAsync( + new V1Namespace { Metadata = new() { Name = Guid.NewGuid().ToString().ToLower() }, }