Files
svcforge/catalog.yaml
T
Nguyen Minh Phuc 08a529fa63
ci / lint (push) Successful in 24s
ci / types (push) Successful in 34s
ci / unit (push) Successful in 26s
ci / security (push) Successful in 37s
ci / dockerfile (push) Successful in 6s
ci / chart (push) Successful in 7s
ci / integration (push) Successful in 41s
ci / image (api) (push) Successful in 2m9s
ci / image (reconciler) (push) Successful in 2m1s
ci / image (worker) (push) Successful in 2m7s
ci / bump (push) Successful in 13s
Get off Docker Hub, add a catalog values passthrough, fix the dind prune
Docker Hub rate-limits anonymous pulls per source IP and every node here
shares one NAT address, so a busy afternoon fails an unrelated build with
`toomanyrequests`. Nothing in this repo needs to be there.

Every base image now comes from mirror.gcr.io (python, alpine/helm,
postgres) or ghcr.io (uv, trivy). Verified digest-for-digest against
Docker Hub before switching, including the superseded postgres digest
this repo still pins, so every existing pin stays valid — same bytes,
different transport.

catalog.yaml: the three bitnami entries named `bitnamilegacy/<chart>`, a
repo alias nothing in the worker image configures, so they could never
resolve at provision time. All five entries are now `oci://` refs, which
need no `helm repo add`, and all are on latest stable:

  elasticsearch 21.3.15 -> 22.1.6     redis    20.6.2 -> 27.0.15
  postgresql    16.4.5  -> 18.8.0     podinfo  6.7.1  -> 6.14.0

Moving the chart pull is only half of it, though: a bitnami chart
defaults its own images to registry-1.docker.io. CatalogEntry gains a
`values:` dict, merged under the size's replicas and resources, so an
entry can set `global.imageRegistry` and move the image pull too. Size
wins on conflict — otherwise an entry setting replicaCount would make
every size deploy the same shape. Deep merge, because a shallow one
drops sibling keys of a shared nested map.

Bitnami charts reject a substituted registry unless
`global.security.allowInsecureImages` is set. That check is about
provenance, and the mirror serves byte-identical manifests, so it is set
deliberately and only for entries whose digests were verified.

The dind prune had `--filter until=168h` on both prunes, and it got both
cases exactly backwards. `until` reads an image's CREATED time, so it
deleted trivy every leg (a released tool image is always older than any
window) while protecting the dangling build layers it existed to remove.
Measured on node0: 21 dangling images / 5.96GB, and exactly 1 of them
older than 168h. Trivy is protected by a tag now, so the image prune
drops the filter; buildx keeps it, where age genuinely matters.

Tests: +10 unit (deep merge, precedence, no-mutation, and a guard that
fails if any catalog entry points at Docker Hub). Both new guards were
control-tested by breaking the code and watching them fail. The API test
that hardcoded `21.3.15` now reads the catalog — its subject is where
the value comes from, not what it is.
2026-07-21 15:32:12 +00:00

177 lines
5.6 KiB
YAML

# The product catalog: what svcforge will provision, and in which sizes.
# service_type -> chart / chart_version / security / sizes{name -> replicas + resources}
#
# security: true bypasses every tenant's maintenance window for this entry. Set it for a
# CVE with a public exploit; leave it false and the bump waits for 03:00 Sunday.
#
# Every chart is addressed as `oci://`, which is not cosmetic: an OCI chart is pulled by
# reference, with no `helm repo add` first. The three entries below previously named
# `bitnamilegacy/<chart>`, a classic repo alias that nothing in the worker image
# configures — so they could never resolve at provision time. OCI is the form that works
# from a bare container.
#
# All of them go through mirror.gcr.io rather than registry-1.docker.io. Docker Hub
# rate-limits anonymous pulls per source IP and every node here shares one NAT address, so
# a busy afternoon becomes `toomanyrequests` on an unrelated deploy. mirror.gcr.io is a
# pull-through cache with no such limit, verified digest-for-digest identical.
#
# The chart pull is only half of it. A bitnami chart defaults its own images to
# `registry-1.docker.io/bitnami/<name>`, so the pods would still go to Docker Hub even
# though the chart did not. `values:` on an entry is the fix: anything there is passed to
# helm underneath the size's replicas and resources, so `global.imageRegistry` moves the
# image pull too. `podinfo` needs none of this — its chart already points at ghcr.io.
services:
elasticsearch:
chart: oci://mirror.gcr.io/bitnamicharts/elasticsearch
chart_version: 22.1.6
security: false
# Sends the chart's own image pulls through the mirror as well, so nothing in this
# entry touches a rate-limited registry. Merged under the size below.
values:
global:
imageRegistry: mirror.gcr.io
sizes:
small:
replicas: 1
resources:
requests:
cpu: 250m
memory: 1Gi
limits:
cpu: "1"
memory: 2Gi
medium:
replicas: 3
resources:
requests:
cpu: "1"
memory: 4Gi
limits:
cpu: "2"
memory: 8Gi
redis:
chart: oci://mirror.gcr.io/bitnamicharts/redis
chart_version: 27.0.15
security: false
# Sends the chart's own image pulls through the mirror as well, so nothing in this
# entry touches a rate-limited registry. Merged under the size below.
values:
global:
imageRegistry: mirror.gcr.io
sizes:
small:
replicas: 1
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
medium:
replicas: 3
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: "1"
memory: 2Gi
postgres:
chart: oci://mirror.gcr.io/bitnamicharts/postgresql
chart_version: 18.8.0
security: false
# Sends the chart's own image pulls through the mirror as well, so nothing in this
# entry touches a rate-limited registry. Merged under the size below.
values:
global:
imageRegistry: mirror.gcr.io
sizes:
small:
replicas: 1
resources:
requests:
cpu: 250m
memory: 512Mi
limits:
cpu: "1"
memory: 1Gi
medium:
replicas: 2
resources:
requests:
cpu: "1"
memory: 2Gi
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.
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.14.0
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://mirror.gcr.io/bitnamicharts/nginx
chart_version: 25.0.14
security: false
# Sends the chart's own image pulls through the mirror as well, so nothing in this
# entry touches a rate-limited registry. Merged under the size below.
values:
global:
imageRegistry: mirror.gcr.io
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