catalog: two tiny services, so the loop can be exercised on a full cluster
ci / lint (push) Successful in 24s
ci / types (push) Successful in 35s
ci / unit (push) Successful in 26s
ci / security (push) Successful in 41s
ci / dockerfile (push) Successful in 8s
ci / chart (push) Successful in 9s
ci / integration (push) Successful in 46s
ci / image (api) (push) Successful in 2m32s
ci / image (reconciler) (push) Successful in 2m50s
ci / image (worker) (push) Successful in 2m43s
ci / bump (push) Successful in 21s

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/<chart>`, 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.
This commit is contained in:
Nguyen Minh Phuc
2026-07-21 05:47:41 +00:00
parent bb8b14ef03
commit 6974b3620f
5 changed files with 76 additions and 3 deletions
+67
View File
@@ -75,3 +75,70 @@ services:
limits: limits:
cpu: "2" cpu: "2"
memory: 4Gi 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/<chart>`,
# 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
+3 -1
View File
@@ -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 # 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. # reads, and a README in this repo is not something they have.
API_DESCRIPTION = """ 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 ## Authentication
+2 -1
View File
@@ -32,7 +32,8 @@ class CreateInstanceRequest(BaseModel):
service_type: str = Field( service_type: str = Field(
min_length=1, min_length=1,
description=( 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." "Unknown values return 404."
), ),
) )
+3
View File
@@ -31,6 +31,9 @@ class ServiceType(StrEnum):
ELASTICSEARCH = "elasticsearch" ELASTICSEARCH = "elasticsearch"
REDIS = "redis" REDIS = "redis"
POSTGRES = "postgres" POSTGRES = "postgres"
# Small enough to provision on a cluster with no spare memory; see catalog.yaml.
PODINFO = "podinfo"
NGINX = "nginx"
class Size(StrEnum): class Size(StrEnum):
+1 -1
View File
@@ -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: def test_repo_catalog_yaml_is_valid() -> None:
catalog = load_catalog(Path(__file__).parents[2] / "catalog.yaml") 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(): for entry in catalog.values():
assert set(entry.sizes) == {"small", "medium"} assert set(entry.sizes) == {"small", "medium"}