Files
Entkube/tests/EntKube.Clusters.Tests/Features/KyvernoClusterSettingsProviderTests.cs
2026-05-13 14:01:32 +02:00

177 lines
6.1 KiB
C#

using System.Text.Json;
using EntKube.Clusters.Features.AdoptCluster.Components.SecurityPolicies;
using EntKube.Clusters.Features.ClusterSettings;
using FluentAssertions;
namespace EntKube.Clusters.Tests.Features;
/// <summary>
/// Tests the JSON parsing logic that extracts allowed registries from a Kyverno
/// ClusterPolicy. This is the core logic that reads the restrict-image-registries
/// policy and returns the list of approved registries.
/// </summary>
public class KyvernoClusterSettingsProviderTests
{
[Fact]
public void ExtractRegistries_WithValidPolicy_ReturnsRegistryList()
{
// Arrange — a policy JSON with three allowed registries.
string json = """
{
"spec": {
"validationFailureAction": "Audit",
"rules": [{
"name": "validate-image-registry",
"validate": {
"foreach": [{
"list": "request.object.spec.[initContainers, containers][]",
"deny": {
"conditions": {
"all": [
{ "key": "{{ element.image }}", "operator": "NotEquals", "value": "docker.io/*" },
{ "key": "{{ element.image }}", "operator": "NotEquals", "value": "ghcr.io/*" },
{ "key": "{{ element.image }}", "operator": "NotEquals", "value": "registry.k8s.io/*" }
]
}
}
}]
}
}]
}
}
""";
// Act
List<string> registries = KyvernoClusterSettingsProvider.ExtractRegistriesFromPolicyJson(json);
// Assert
registries.Should().BeEquivalentTo(new[] { "docker.io", "ghcr.io", "registry.k8s.io" });
}
[Fact]
public void ExtractRegistries_WithEmptyJson_ReturnsEmptyList()
{
// Act
List<string> registries = KyvernoClusterSettingsProvider.ExtractRegistriesFromPolicyJson("{}");
// Assert
registries.Should().BeEmpty();
}
[Fact]
public void ExtractRegistries_WithMalformedJson_ReturnsEmptyList()
{
// Act
List<string> registries = KyvernoClusterSettingsProvider.ExtractRegistriesFromPolicyJson("not json");
// Assert
registries.Should().BeEmpty();
}
[Fact]
public void ExtractRegistries_WithNoPolicyRules_ReturnsEmptyList()
{
// Arrange
string json = """{ "spec": { "rules": [] } }""";
// Act
List<string> registries = KyvernoClusterSettingsProvider.ExtractRegistriesFromPolicyJson(json);
// Assert
registries.Should().BeEmpty();
}
// ─── Round-trip tests ──────────────────────────────────────────────────
[Fact]
public void BuildRegistryPolicy_RoundTrip_PreservesRegistries()
{
// When the settings provider writes a policy and then reads it back,
// the extracted registries should match what was written. This simulates
// the full Kubernetes API round-trip: build → serialize → parse.
// Arrange — a custom list of registries.
List<string> registries = new() { "docker.io", "ghcr.io", "myregistry.example.com" };
// Act — build the policy, serialize it to JSON (simulating K8s storage),
// then extract registries back out.
object policy = KyvernoClusterSettingsProvider.BuildRegistryPolicy(registries, "test message", "Audit");
string json = JsonSerializer.Serialize(policy);
List<string> extracted = KyvernoClusterSettingsProvider.ExtractRegistriesFromPolicyJson(json);
// Assert — the registries should survive the round-trip intact.
extracted.Should().BeEquivalentTo(registries);
}
[Fact]
public void BuildRegistryPolicy_IncludesExcludeBlock()
{
// The policy must include the exclude block so system namespaces
// (kube-system, cert-manager, etc.) are not subject to registry
// restrictions. Without this, saving registries via the Settings tab
// would create a policy that differs from the installer's policy and
// could break system pods.
// Arrange
List<string> registries = new() { "docker.io" };
// Act
object policy = KyvernoClusterSettingsProvider.BuildRegistryPolicy(registries, "test", "Audit");
string json = JsonSerializer.Serialize(policy);
using JsonDocument doc = JsonDocument.Parse(json);
// Assert — the rule should have an "exclude" block with namespace exclusions.
JsonElement rule = doc.RootElement
.GetProperty("spec")
.GetProperty("rules")[0];
rule.TryGetProperty("exclude", out JsonElement exclude).Should().BeTrue(
"the policy rule must include an exclude block to protect system namespaces");
string excludeJson = exclude.GetRawText();
excludeJson.Should().Contain("kube-system");
}
[Fact]
public void BuildRegistryPolicy_IncludesManagedByLabels()
{
// The policy labels must match what the SecurityPoliciesInstaller creates
// so the installer can find and replace the policy during reconfiguration.
// Arrange
List<string> registries = new() { "docker.io" };
// Act
object policy = KyvernoClusterSettingsProvider.BuildRegistryPolicy(registries, "test", "Enforce");
string json = JsonSerializer.Serialize(policy);
using JsonDocument doc = JsonDocument.Parse(json);
// Assert — should have both managed-by and part-of labels.
JsonElement labels = doc.RootElement
.GetProperty("metadata")
.GetProperty("labels");
labels.GetProperty("app.kubernetes.io/managed-by").GetString().Should().Be("entkube");
labels.GetProperty("app.kubernetes.io/part-of").GetString().Should().Be("security-policies");
}
}