276 lines
9.2 KiB
C#
276 lines
9.2 KiB
C#
using EntKube.Provisioning.Domain;
|
|
using FluentAssertions;
|
|
|
|
namespace EntKube.Provisioning.Tests.Domain;
|
|
|
|
public class PrometheusStackTests
|
|
{
|
|
private readonly Guid clusterId = Guid.NewGuid();
|
|
private readonly Guid environmentId = Guid.NewGuid();
|
|
|
|
// ─── Creation ───────────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public void Create_WithValidInputs_CreatesStackInPendingState()
|
|
{
|
|
// Arrange & Act — An operator provisions a new Prometheus stack on a cluster.
|
|
// Prometheus is per-cluster: each cluster gets its own metrics collector.
|
|
|
|
PrometheusStack stack = PrometheusStack.Create(clusterId, environmentId, "prod-prometheus", "monitoring");
|
|
|
|
// Assert — The stack should be in a Pending state, with sensible defaults
|
|
// for Prometheus retention, replicas, and Alertmanager.
|
|
|
|
stack.Id.Should().NotBe(Guid.Empty);
|
|
stack.ClusterId.Should().Be(clusterId);
|
|
stack.EnvironmentId.Should().Be(environmentId);
|
|
stack.Name.Should().Be("prod-prometheus");
|
|
stack.Namespace.Should().Be("monitoring");
|
|
stack.Status.Should().Be(PrometheusStackStatus.Pending);
|
|
stack.CreatedAt.Should().BeCloseTo(DateTimeOffset.UtcNow, TimeSpan.FromSeconds(5));
|
|
|
|
// Prometheus defaults
|
|
|
|
stack.Prometheus.Should().NotBeNull();
|
|
stack.Prometheus.Retention.Should().Be("30d");
|
|
stack.Prometheus.Replicas.Should().Be(2);
|
|
stack.Prometheus.StorageSize.Should().Be("50Gi");
|
|
|
|
// Alertmanager defaults
|
|
|
|
stack.Alertmanager.Should().NotBeNull();
|
|
stack.Alertmanager.Enabled.Should().BeTrue();
|
|
stack.Alertmanager.Replicas.Should().Be(2);
|
|
}
|
|
|
|
[Fact]
|
|
public void Create_WithEmptyName_ThrowsArgumentException()
|
|
{
|
|
// Arrange & Act — A Prometheus stack must have a name.
|
|
|
|
Action act = () => PrometheusStack.Create(clusterId, environmentId, "", "monitoring");
|
|
|
|
// Assert
|
|
|
|
act.Should().Throw<ArgumentException>()
|
|
.WithParameterName("name");
|
|
}
|
|
|
|
[Fact]
|
|
public void Create_WithEmptyNamespace_ThrowsArgumentException()
|
|
{
|
|
// Arrange & Act — A Prometheus stack must have a namespace.
|
|
|
|
Action act = () => PrometheusStack.Create(clusterId, environmentId, "prod-prometheus", "");
|
|
|
|
// Assert
|
|
|
|
act.Should().Throw<ArgumentException>()
|
|
.WithParameterName("ns");
|
|
}
|
|
|
|
[Fact]
|
|
public void Create_WithEmptyClusterId_ThrowsArgumentException()
|
|
{
|
|
// Arrange & Act — A Prometheus stack must belong to a cluster.
|
|
|
|
Action act = () => PrometheusStack.Create(Guid.Empty, environmentId, "prod-prometheus", "monitoring");
|
|
|
|
// Assert
|
|
|
|
act.Should().Throw<ArgumentException>()
|
|
.WithParameterName("clusterId");
|
|
}
|
|
|
|
[Fact]
|
|
public void Create_WithEmptyEnvironmentId_ThrowsArgumentException()
|
|
{
|
|
// Arrange & Act — A Prometheus stack must belong to an environment.
|
|
|
|
Action act = () => PrometheusStack.Create(clusterId, Guid.Empty, "prod-prometheus", "monitoring");
|
|
|
|
// Assert
|
|
|
|
act.Should().Throw<ArgumentException>()
|
|
.WithParameterName("environmentId");
|
|
}
|
|
|
|
// ─── Prometheus Configuration ─────────────────────────────────────
|
|
|
|
[Fact]
|
|
public void ConfigurePrometheus_UpdatesRetentionAndReplicas()
|
|
{
|
|
// Arrange — Start with a Prometheus stack using defaults.
|
|
|
|
PrometheusStack stack = PrometheusStack.Create(clusterId, environmentId, "prod-prometheus", "monitoring");
|
|
|
|
// Act — The operator increases retention to 90 days and scales to 3 replicas.
|
|
|
|
stack.ConfigurePrometheus(retention: "90d", replicas: 3, storageSize: "100Gi", retentionSize: "90GB");
|
|
|
|
// Assert — Prometheus settings should reflect the new configuration.
|
|
|
|
stack.Prometheus.Retention.Should().Be("90d");
|
|
stack.Prometheus.Replicas.Should().Be(3);
|
|
stack.Prometheus.StorageSize.Should().Be("100Gi");
|
|
stack.Prometheus.RetentionSize.Should().Be("90GB");
|
|
}
|
|
|
|
[Fact]
|
|
public void ConfigurePrometheus_WithInvalidReplicas_ThrowsArgumentException()
|
|
{
|
|
// Arrange
|
|
|
|
PrometheusStack stack = PrometheusStack.Create(clusterId, environmentId, "prod-prometheus", "monitoring");
|
|
|
|
// Act & Assert — Zero replicas doesn't make sense for Prometheus.
|
|
|
|
Action act = () => stack.ConfigurePrometheus(replicas: 0);
|
|
|
|
act.Should().Throw<ArgumentException>()
|
|
.WithParameterName("replicas");
|
|
}
|
|
|
|
// ─── Alertmanager Configuration ─────────────────────────────────
|
|
|
|
[Fact]
|
|
public void ConfigureAlertmanager_UpdatesReplicasAndEnablement()
|
|
{
|
|
// Arrange
|
|
|
|
PrometheusStack stack = PrometheusStack.Create(clusterId, environmentId, "prod-prometheus", "monitoring");
|
|
|
|
// Act — The operator disables Alertmanager for a dev cluster.
|
|
|
|
stack.ConfigureAlertmanager(enabled: false, replicas: 1);
|
|
|
|
// Assert
|
|
|
|
stack.Alertmanager.Enabled.Should().BeFalse();
|
|
stack.Alertmanager.Replicas.Should().Be(1);
|
|
}
|
|
|
|
// ─── Ingress (Prometheus + Alertmanager) ────────────────────────
|
|
|
|
[Fact]
|
|
public void ConfigureIngress_SetsPrometheusAndAlertmanagerRouting()
|
|
{
|
|
// Arrange — Publish Prometheus and Alertmanager via Gateway API.
|
|
|
|
PrometheusStack stack = PrometheusStack.Create(clusterId, environmentId, "prod-prometheus", "monitoring");
|
|
|
|
// Act
|
|
|
|
IngressConfiguration ingress = new(
|
|
Enabled: true,
|
|
Provider: IngressProvider.GatewayApi,
|
|
PrometheusHostname: "prometheus.prod01.example.com",
|
|
AlertmanagerHostname: "alertmanager.prod01.example.com",
|
|
TlsEnabled: true,
|
|
TlsCertificateMode: TlsCertificateMode.Manual,
|
|
TlsSecretName: "monitoring-tls");
|
|
|
|
stack.ConfigureIngress(ingress);
|
|
|
|
// Assert
|
|
|
|
stack.Ingress.Should().NotBeNull();
|
|
stack.Ingress!.Enabled.Should().BeTrue();
|
|
stack.Ingress.Provider.Should().Be(IngressProvider.GatewayApi);
|
|
stack.Ingress.PrometheusHostname.Should().Be("prometheus.prod01.example.com");
|
|
stack.Ingress.AlertmanagerHostname.Should().Be("alertmanager.prod01.example.com");
|
|
stack.Ingress.TlsEnabled.Should().BeTrue();
|
|
stack.Ingress.TlsCertificateMode.Should().Be(TlsCertificateMode.Manual);
|
|
stack.Ingress.TlsSecretName.Should().Be("monitoring-tls");
|
|
}
|
|
|
|
[Fact]
|
|
public void ConfigureIngress_WithCertManager_DoesNotRequireSecretName()
|
|
{
|
|
// Arrange
|
|
|
|
PrometheusStack stack = PrometheusStack.Create(clusterId, environmentId, "prod-prometheus", "monitoring");
|
|
|
|
// Act — cert-manager with Let's Encrypt handles certificates automatically.
|
|
|
|
IngressConfiguration ingress = new(
|
|
Enabled: true,
|
|
Provider: IngressProvider.GatewayApi,
|
|
PrometheusHostname: "prometheus.example.com",
|
|
AlertmanagerHostname: "alertmanager.example.com",
|
|
TlsEnabled: true,
|
|
TlsCertificateMode: TlsCertificateMode.CertManager,
|
|
TlsSecretName: null,
|
|
ClusterIssuer: "letsencrypt-prod");
|
|
|
|
stack.ConfigureIngress(ingress);
|
|
|
|
// Assert
|
|
|
|
stack.Ingress!.TlsCertificateMode.Should().Be(TlsCertificateMode.CertManager);
|
|
stack.Ingress.TlsSecretName.Should().BeNull();
|
|
stack.Ingress.ClusterIssuer.Should().Be("letsencrypt-prod");
|
|
}
|
|
|
|
[Fact]
|
|
public void DisableIngress_ClearsIngressConfig()
|
|
{
|
|
// Arrange
|
|
|
|
PrometheusStack stack = PrometheusStack.Create(clusterId, environmentId, "prod-prometheus", "monitoring");
|
|
|
|
IngressConfiguration ingress = new(
|
|
Enabled: true,
|
|
Provider: IngressProvider.GatewayApi,
|
|
PrometheusHostname: "prometheus.example.com",
|
|
AlertmanagerHostname: null,
|
|
TlsEnabled: true,
|
|
TlsCertificateMode: TlsCertificateMode.Manual,
|
|
TlsSecretName: "tls-secret");
|
|
|
|
stack.ConfigureIngress(ingress);
|
|
|
|
// Act
|
|
|
|
stack.DisableIngress();
|
|
|
|
// Assert
|
|
|
|
stack.Ingress.Should().BeNull();
|
|
}
|
|
|
|
// ─── Lifecycle ──────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public void MarkRunning_SetsStatusToRunning()
|
|
{
|
|
// Arrange
|
|
|
|
PrometheusStack stack = PrometheusStack.Create(clusterId, environmentId, "prod-prometheus", "monitoring");
|
|
|
|
// Act
|
|
|
|
stack.MarkRunning();
|
|
|
|
// Assert
|
|
|
|
stack.Status.Should().Be(PrometheusStackStatus.Running);
|
|
}
|
|
|
|
[Fact]
|
|
public void MarkDegraded_SetsStatusToDegraded()
|
|
{
|
|
// Arrange
|
|
|
|
PrometheusStack stack = PrometheusStack.Create(clusterId, environmentId, "prod-prometheus", "monitoring");
|
|
|
|
// Act
|
|
|
|
stack.MarkDegraded();
|
|
|
|
// Assert
|
|
|
|
stack.Status.Should().Be(PrometheusStackStatus.Degraded);
|
|
}
|
|
}
|