diff --git a/apps/fc-desktop/resourcequota.yaml b/apps/fc-desktop/resourcequota.yaml new file mode 100644 index 0000000..2d4f0d5 --- /dev/null +++ b/apps/fc-desktop/resourcequota.yaml @@ -0,0 +1,24 @@ +# FlowerCore Remote Desktop - Namespace ResourceQuota (GitOps-managed) +# +# Codifies the live cap applied on 2026-05-19 after disabling automatic +# desktop pool prewarm: no more than 15 pods and no more than 8 CPU / 16Gi +# memory requested across the fc-desktop namespace. +# +# ArgoCD adoption note: this manifest uses the same kind/name/namespace as +# the live ResourceQuota and avoids hook, force, or replace annotations, so +# automated sync should patch/adopt the existing object in place instead of +# recreating it. +apiVersion: v1 +kind: ResourceQuota +metadata: + name: fc-desktop-cap + namespace: fc-desktop + labels: + app.kubernetes.io/part-of: remotedesktop + app.kubernetes.io/component: capacity-guard + app.kubernetes.io/managed-by: argocd +spec: + hard: + count/pods: "15" + cpu: "8" + memory: 16Gi diff --git a/tests/bluejay-infra-lint/FleetManifestLintTests.cs b/tests/bluejay-infra-lint/FleetManifestLintTests.cs index eb9683d..396b27c 100644 --- a/tests/bluejay-infra-lint/FleetManifestLintTests.cs +++ b/tests/bluejay-infra-lint/FleetManifestLintTests.cs @@ -421,6 +421,35 @@ public sealed class FleetManifestLintTests violations.Should().BeEmpty(); } + [Fact] + [Trait("Question", "Q-RD-DESKTOP-CAP-4")] + public void FcDesktop_ResourceQuotaMustCodifyLivePodCpuMemoryCap() + { + var quota = FcDesktopDocuments() + .Single(document => document.Kind == "ResourceQuota" && document.Name == "fc-desktop-cap"); + + quota.RelativePath.Should().Be("fc-desktop/resourcequota.yaml"); + quota.Namespace.Should().Be("fc-desktop"); + quota.Scalar("spec", "hard", "count/pods").Should().Be("15"); + quota.Scalar("spec", "hard", "cpu").Should().Be("8"); + quota.Scalar("spec", "hard", "memory").Should().Be("16Gi"); + } + + [Fact] + [Trait("Question", "Q-RD-DESKTOP-CAP-5")] + public void FcDesktop_ResourceQuotaMustBeArgoCdAdoptableInPlace() + { + var quota = FcDesktopDocuments() + .Single(document => document.Kind == "ResourceQuota" && document.Name == "fc-desktop-cap"); + + quota.RelativePath.Should().StartWith("fc-desktop/"); + quota.Scalar("metadata", "annotations", "argocd.argoproj.io/hook").Should().BeNull(); + + var syncOptions = quota.Scalar("metadata", "annotations", "argocd.argoproj.io/sync-options") ?? string.Empty; + syncOptions.Should().NotContain("Force=true"); + syncOptions.Should().NotContain("Replace=true"); + } + [Fact] public void FcDeviceManagement_MustShipExpectedManifestSet() { @@ -667,6 +696,13 @@ public sealed class FleetManifestLintTests .Where(document => document.RelativePath.StartsWith("fc-devicemgmt/", StringComparison.Ordinal)) .ToList(); } + + private static IReadOnlyList FcDesktopDocuments() + { + return Inventory.Documents + .Where(document => document.RelativePath.StartsWith("fc-desktop/", StringComparison.Ordinal)) + .ToList(); + } } internal sealed class ManifestInventory