From 6974b3620f305a636464721bfed483eaa339f2f3 Mon Sep 17 00:00:00 2001 From: Nguyen Minh Phuc Date: Tue, 21 Jul 2026 05:47:41 +0000 Subject: [PATCH] catalog: two tiny services, so the loop can be exercised on a full cluster MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The three existing entries are sized like real products: `elasticsearch` small asks for 1Gi and medium for 4Gi across three replicas. On this cluster that is a request that never schedules, so provisioning them proves something about the node and nothing about svcforge. podinfo 16Mi/10m — one small Go binary, no dependencies, no PVC nginx 32Mi/10m — recognisable, still small Both are addressed as `oci://`, which is load-bearing rather than cosmetic. An OCI chart is pulled by reference with no `helm repo add` first. The three existing entries name `bitnamilegacy/`, a classic repo alias that nothing in the worker image configures — so as written they cannot resolve at provision time. OCI is the form that works from a bare container, and it is why the e2e test already provisions podinfo. Chart versions were resolved against the real registries before committing (podinfo 6.7.1, nginx 25.0.14 / app 1.31.3) rather than guessed, since a wrong version fails only at provision time. Also updates the CLI ServiceType enum, the catalog test's expected set, and the service list in the OpenAPI description. --- catalog.yaml | 67 ++++++++++++++++++++++++++++++++++++++ services/api/main.py | 4 ++- services/api/models.py | 3 +- services/cli/main.py | 3 ++ tests/unit/test_catalog.py | 2 +- 5 files changed, 76 insertions(+), 3 deletions(-) diff --git a/catalog.yaml b/catalog.yaml index 774b876..f32e717 100644 --- a/catalog.yaml +++ b/catalog.yaml @@ -75,3 +75,70 @@ services: limits: cpu: "2" memory: 4Gi + + # --- Small services, for exercising the control plane on a cluster with no room --------- + # + # The three entries above are real products and size accordingly: one `elasticsearch` + # small asks for 1Gi, and a medium asks for 4Gi across three replicas. On a test cluster + # that is a request that never schedules, so provisioning them proves nothing about + # svcforge and everything about the node. + # + # These two exist to exercise the actual loop — claim, helm install, CAS to ready, drift, + # TTL, deprovision — in seconds and in tens of megabytes. + # + # Both are addressed as `oci://`, which is not cosmetic: an OCI chart is pulled by + # reference with no `helm repo add` first. The entries above name `bitnamilegacy/`, + # a classic repo alias that nothing in the worker image configures, so they cannot + # actually resolve at provision time. OCI is the form that works from a bare container. + + podinfo: + # A single small Go binary with no dependencies, no PVC and a fast image pull. The e2e + # test provisions exactly this for the same reason. + chart: oci://ghcr.io/stefanprodan/charts/podinfo + chart_version: 6.7.1 + security: false + sizes: + small: + replicas: 1 + resources: + requests: + cpu: 10m + memory: 16Mi + limits: + cpu: 50m + memory: 32Mi + medium: + replicas: 2 + resources: + requests: + cpu: 25m + memory: 32Mi + limits: + cpu: 100m + memory: 64Mi + + nginx: + # A recognisable web server, still small. Useful when the thing being demonstrated needs + # to look like a service someone would actually ask for. + chart: oci://registry-1.docker.io/bitnamicharts/nginx + chart_version: 25.0.14 + security: false + sizes: + small: + replicas: 1 + resources: + requests: + cpu: 10m + memory: 32Mi + limits: + cpu: 100m + memory: 64Mi + medium: + replicas: 2 + resources: + requests: + cpu: 25m + memory: 64Mi + limits: + cpu: 200m + memory: 128Mi diff --git a/services/api/main.py b/services/api/main.py index e5b2176..2a41a91 100644 --- a/services/api/main.py +++ b/services/api/main.py @@ -91,7 +91,9 @@ async def lifespan(app: FastAPI) -> AsyncIterator[None]: # to create_app rather than in a README because /docs is what someone integrating actually # reads, and a README in this repo is not something they have. API_DESCRIPTION = """ -Provision managed service instances (Elasticsearch, Redis, Postgres) into Kubernetes. +Provision managed service instances into Kubernetes. The catalog offers Elasticsearch, +Redis and Postgres, plus two deliberately tiny entries — `podinfo` and `nginx` — for +exercising the platform where there is no room for the real thing. ## Authentication diff --git a/services/api/models.py b/services/api/models.py index 65ff111..a4c0b8a 100644 --- a/services/api/models.py +++ b/services/api/models.py @@ -32,7 +32,8 @@ class CreateInstanceRequest(BaseModel): service_type: str = Field( min_length=1, description=( - "A service type in the catalog, e.g. `elasticsearch`, `redis`, `postgres`. " + "A service type in the catalog, e.g. `elasticsearch`, `redis`, `postgres`, " + "`podinfo`, `nginx`. " "Unknown values return 404." ), ) diff --git a/services/cli/main.py b/services/cli/main.py index 024e982..c238a03 100644 --- a/services/cli/main.py +++ b/services/cli/main.py @@ -31,6 +31,9 @@ class ServiceType(StrEnum): ELASTICSEARCH = "elasticsearch" REDIS = "redis" POSTGRES = "postgres" + # Small enough to provision on a cluster with no spare memory; see catalog.yaml. + PODINFO = "podinfo" + NGINX = "nginx" class Size(StrEnum): diff --git a/tests/unit/test_catalog.py b/tests/unit/test_catalog.py index 11e8138..43d1264 100644 --- a/tests/unit/test_catalog.py +++ b/tests/unit/test_catalog.py @@ -150,6 +150,6 @@ def test_bare_mapping_without_services_key_is_accepted(tmp_path: Path) -> None: def test_repo_catalog_yaml_is_valid() -> None: catalog = load_catalog(Path(__file__).parents[2] / "catalog.yaml") - assert set(catalog) == {"elasticsearch", "redis", "postgres"} + assert set(catalog) == {"elasticsearch", "redis", "postgres", "podinfo", "nginx"} for entry in catalog.values(): assert set(entry.sizes) == {"small", "medium"}