Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 24 additions & 6 deletions src/KubeOps/Operator/Entities/Extensions/EntityToCrdExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using DotnetKubernetesClient.Entities;
Expand Down Expand Up @@ -214,6 +215,8 @@ private static V1JSONSchemaProps MapType(
// this description is on the class
props.Description ??= type.GetCustomAttributes<DescriptionAttribute>(true).FirstOrDefault()?.Description;

var isSimpleType = IsSimpleType(type);

if (type == typeof(V1ObjectMeta))
{
props.Type = Object;
Expand All @@ -226,7 +229,7 @@ private static V1JSONSchemaProps MapType(
additionalColumns,
jsonPath);
}
else if (!IsSimpleType(type) &&
else if (!isSimpleType &&
(typeof(IDictionary).IsAssignableFrom(type) ||
(type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IDictionary<,>)) ||
(type.IsGenericType &&
Expand All @@ -237,18 +240,16 @@ private static V1JSONSchemaProps MapType(
props.Type = Object;
props.XKubernetesPreserveUnknownFields = true;
}
else if (!IsSimpleType(type) &&
type.IsGenericType &&
typeof(IEnumerable<>).IsAssignableFrom(type.GetGenericTypeDefinition()))
else if (!isSimpleType && IsGenericEnumerableType(type, out Type? closingType))
{
props.Type = Array;
props.Items = MapType(type.GetGenericArguments()[0], additionalColumns, jsonPath);
props.Items = MapType(closingType, additionalColumns, jsonPath);
}
else if (type == typeof(IntstrIntOrString))
{
props.XKubernetesIntOrString = true;
}
else if (!IsSimpleType(type))
else if (!isSimpleType)
{
ProcessType(type, props, additionalColumns, jsonPath);
}
Expand Down Expand Up @@ -348,5 +349,22 @@ private static bool IsSimpleType(Type type) =>
IsSimpleType(type.GetGenericArguments()[0]));

private static string CamelCase(string str) => $"{str.Substring(0, 1).ToLower()}{str.Substring(1)}";

private static bool IsGenericEnumerableType(Type type, [NotNullWhen(true)] out Type? closingType)
{
if (type.IsGenericType && typeof(IEnumerable<>).IsAssignableFrom(type.GetGenericTypeDefinition()))
{
closingType = type.GetGenericArguments()[0];
return true;
}

closingType = type
.GetInterfaces()
.Where(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>))
.Select(t => t.GetGenericArguments()[0])
.FirstOrDefault();

return closingType != null;
}
}
}
44 changes: 28 additions & 16 deletions tests/KubeOps.Test/Operator/Entities/CrdGeneration.Test.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using KubeOps.Operator.Commands.Generators;
using KubeOps.Operator.Entities.Extensions;
using KubeOps.Operator.Errors;
using KubeOps.Operator.Services;
using KubeOps.Test.TestEntities;
using Xunit;

Expand Down Expand Up @@ -74,30 +73,43 @@ public void Should_Set_The_Correct_Type_And_Format_For_Types(string fieldName, s
nullableField.Nullable.Should().BeTrue();
}

[Fact]
public void Should_Set_The_Correct_Array_Type()
{
[Theory]
[InlineData(nameof(TestSpecEntitySpec.StringArray), "string", null)]
[InlineData(nameof(TestSpecEntitySpec.NullableStringArray), "string", true)]
[InlineData(nameof(TestSpecEntitySpec.EnumerableInteger), "integer", null)]
[InlineData(nameof(TestSpecEntitySpec.EnumerableNullableInteger), "integer", null)]
[InlineData(nameof(TestSpecEntitySpec.IntegerList), "integer", null)]
[InlineData(nameof(TestSpecEntitySpec.IntegerHashSet), "integer", null)]
[InlineData(nameof(TestSpecEntitySpec.IntegerISet), "integer", null)]
[InlineData(nameof(TestSpecEntitySpec.IntegerIReadOnlySet), "integer", null)]
public void Should_Set_The_Correct_Array_Type(string property, string expectedType, bool? expectedNullable)
{
var propertyName = char.ToLowerInvariant(property[0]) + property[1..];
var crd = _testSpecEntity.CreateCrd();
var specProperties = crd.Spec.Versions.First().Schema.OpenAPIV3Schema.Properties["spec"];

var normalField = specProperties.Properties["stringArray"];
var normalField = specProperties.Properties[propertyName];
normalField.Type.Should().Be("array");
(normalField.Items as V1JSONSchemaProps)?.Type?.Should().Be("string");
normalField.Nullable.Should().BeNull();

var nullableField = specProperties.Properties["nullableStringArray"];
nullableField.Type.Should().Be("array");
(nullableField.Items as V1JSONSchemaProps)?.Type?.Should().Be("string");
nullableField.Nullable.Should().BeTrue();
(normalField.Items as V1JSONSchemaProps)?.Type?.Should().Be(expectedType);
normalField.Nullable.Should().Be(expectedNullable);
}

[Fact]
public void Should_Set_The_Correct_Complex_Array_Type()
{
[Theory]
[InlineData(nameof(TestSpecEntitySpec.ComplexItemsEnumerable))]
[InlineData(nameof(TestSpecEntitySpec.ComplexItemsList))]
[InlineData(nameof(TestSpecEntitySpec.ComplexItemsIList))]
[InlineData(nameof(TestSpecEntitySpec.ComplexItemsReadOnlyList))]
[InlineData(nameof(TestSpecEntitySpec.ComplexItemsCollection))]
[InlineData(nameof(TestSpecEntitySpec.ComplexItemsICollection))]
[InlineData(nameof(TestSpecEntitySpec.ComplexItemsReadOnlyCollection))]
[InlineData(nameof(TestSpecEntitySpec.ComplexItemsDerivedList))]
public void Should_Set_The_Correct_Complex_Array_Type(string property)
{
var propertyName = char.ToLowerInvariant(property[0]) + property[1..];
var crd = _testSpecEntity.CreateCrd();
var specProperties = crd.Spec.Versions.First().Schema.OpenAPIV3Schema.Properties["spec"];

var complexItemsArray = specProperties.Properties["complexItems"];
var complexItemsArray = specProperties.Properties[propertyName];
complexItemsArray.Type.Should().Be("array");
(complexItemsArray.Items as V1JSONSchemaProps)?.Type?.Should().Be("object");
complexItemsArray.Nullable.Should().BeNull();
Expand Down
39 changes: 37 additions & 2 deletions tests/KubeOps.Test/TestEntities/TestSpecEntity.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using k8s.Models;
using KubeOps.Operator.Entities;
Expand All @@ -11,10 +12,22 @@ namespace KubeOps.Test.TestEntities
[Description("This is the Spec Class Description")]
public class TestSpecEntitySpec
{
public string[] StringArray { get; set; } = new string[0];
public string[] StringArray { get; set; } = Array.Empty<string>();

public string[]? NullableStringArray { get; set; }

public IEnumerable<int> EnumerableInteger { get; set; } = Array.Empty<int>();

public IEnumerable<int?> EnumerableNullableInteger { get; set; } = Array.Empty<int?>();

public IntegerList IntegerList { get; set; } = new();

public HashSet<int> IntegerHashSet { get; set; } = new();

public ISet<int> IntegerISet { get; set; } = new HashSet<int>();

public IReadOnlySet<int> IntegerIReadOnlySet { get; set; } = new HashSet<int>();

[AdditionalPrinterColumn]
public string NormalString { get; set; } = string.Empty;

Expand Down Expand Up @@ -80,7 +93,21 @@ public class TestSpecEntitySpec
[Required]
public int Required { get; set; }

public IEnumerable<TestItem> ComplexItems { get; set; } = Enumerable.Empty<TestItem>();
public IEnumerable<TestItem> ComplexItemsEnumerable { get; set; } = Enumerable.Empty<TestItem>();

public List<TestItem> ComplexItemsList { get; set; } = new();

public IList<TestItem> ComplexItemsIList { get; set; } = Array.Empty<TestItem>();

public IReadOnlyList<TestItem> ComplexItemsReadOnlyList { get; set; } = Array.Empty<TestItem>();

public Collection<TestItem> ComplexItemsCollection { get; set; } = new();

public ICollection<TestItem> ComplexItemsICollection { get; set; } = Array.Empty<TestItem>();

public IReadOnlyCollection<TestItem> ComplexItemsReadOnlyCollection { get; set; } = Array.Empty<TestItem>();

public TestItemList ComplexItemsDerivedList { get; set; } = new();

public IDictionary Dictionary { get; set; } = new Dictionary<string, string>();

Expand Down Expand Up @@ -117,4 +144,12 @@ public class TestItem
public string Item { get; set; } = null!;
public string Extra { get; set; } = null!;
}

public class TestItemList : List<TestItem>
{
}

public class IntegerList : Collection<int>
{
}
}