177 lines
6.1 KiB
C#
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");
|
|
}
|
|
}
|