Serve the Zoo sites from files, not from pods
CI/CD / render (push) Waiting to run
CI/CD / render (push) Waiting to run
zoo.vote and zoo.industries are static bundles a pod was re-serving on every request. Both move to the site plane: the `public/` tree of the image digest each Deployment ran is synced to s3://hanzo-sites/zoo/vote and s3://hanzo-sites/zoo/industries, and each file now declares the Middleware that reads the prefix together with the route that names it. zoo-industries' server generated /public/config.json at boot from three SPA_* variables; the plane has no boot, so that file travels in the prefix carrying the values the pod served. zoo-ngo is deleted rather than converted. zoo.ngo and www.zoo.ngo already route to s3://hanzo-sites/zoo/ngo, and that prefix holds a newer build than the pod was serving to nobody -- no route in this namespace named it. A site declared twice is a site that goes stale in one of them. Co-authored-by: Hanzo AI <[email protected]>
This commit is contained in:
@@ -1,72 +1,49 @@
|
||||
# Generated from the live zoo-mainnet/zoo-industries Deployment on do-sfo3-zoo-k8s
|
||||
# by hack/live2values.py. Live is the source: a repo of hand-applied
|
||||
# manifests has unknown currency, the running fleet does not.
|
||||
replicas: 2
|
||||
selectorLabels:
|
||||
app.kubernetes.io/name: zoo-industries
|
||||
containerName: spa
|
||||
image:
|
||||
repository: ghcr.io/zooai/zoo-industries
|
||||
tag: 1.0.5-amd64
|
||||
digest: sha256:7546cebac2b23f3131ca3b888c42db9c55f5e2b66b69bf0a9263dce1445dc26c
|
||||
pullPolicy: IfNotPresent
|
||||
partOf: zoo
|
||||
component: marketing-site
|
||||
labels:
|
||||
app.kubernetes.io/cluster: zoo-k8s
|
||||
app.kubernetes.io/env: mainnet
|
||||
env:
|
||||
- name: SPA_API_HOST
|
||||
value: https://api.zoo.ngo
|
||||
- name: SPA_IAM_HOST
|
||||
value: https://zoo.id
|
||||
- name: SPA_ENV
|
||||
value: prod
|
||||
resources:
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 256Mi
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 64Mi
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 3000
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 15
|
||||
successThreshold: 1
|
||||
timeoutSeconds: 1
|
||||
readinessProbe:
|
||||
failureThreshold: 3
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 3000
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 2
|
||||
periodSeconds: 5
|
||||
successThreshold: 1
|
||||
timeoutSeconds: 1
|
||||
ports:
|
||||
- containerPort: 3000
|
||||
name: http
|
||||
servicePort: 80
|
||||
imagePullSecrets:
|
||||
- name: ghcr-secret
|
||||
- name: hanzo
|
||||
- name: ghcr-credentials-hanzodev
|
||||
rollingUpdate:
|
||||
maxSurge: 1
|
||||
maxUnavailable: 0
|
||||
topologySpread:
|
||||
enabled: false
|
||||
lifecycle: null
|
||||
terminationGracePeriodSeconds: 30
|
||||
|
||||
# Enforcement switch, read by the zoo-fleet ApplicationSet's templatePatch --
|
||||
# not by the chart. selfHeal returns a hand-edited object to what is written
|
||||
# here; prune stays off, so nothing is ever deleted.
|
||||
# zoo.industries is files: one Middleware that serves them, one route that names
|
||||
# it, and the endpoint-less Service the route schema requires. No image, so the
|
||||
# chart renders no Deployment -- a static site is not a pod.
|
||||
#
|
||||
# The objects are the `public/` tree of
|
||||
# ghcr.io/zooai/zoo-industries@sha256:7546cebac2b23f3131ca3b888c42db9c55f5e2b66b69bf0a9263dce1445dc26c,
|
||||
# the digest the Deployment ran, plus one file it used to generate. That server
|
||||
# wrote /public/config.json at boot from SPA_API_HOST, SPA_IAM_HOST and SPA_ENV;
|
||||
# the plane has no boot, so the same three values travel as an object in the
|
||||
# prefix, byte-for-byte what the running pod served:
|
||||
#
|
||||
# {"apiHost":"https://api.zoo.ngo","env":"prod","iamHost":"https://zoo.id","v":1}
|
||||
#
|
||||
# A rebuild ships by syncing `public/` to this prefix and keeping that file.
|
||||
#
|
||||
# The Deployment carried no route at all, so nothing has reached this bundle from
|
||||
# inside the cluster. The route below is the first one.
|
||||
cd:
|
||||
automated: true
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
middlewares:
|
||||
- name: industries
|
||||
spec:
|
||||
staticFiles:
|
||||
root: s3://hanzo-sites/zoo/industries
|
||||
# Next `output: export` writes real HTML per route -- 56 files, one each
|
||||
# for /about, /capabilities and the rest. A fallback would answer
|
||||
# 200-with-the-homepage for a missing chunk, which reads exactly like a
|
||||
# good visit, so a miss stays a miss.
|
||||
spaMode: false
|
||||
indexFiles:
|
||||
- index.html
|
||||
routes:
|
||||
- name: industries
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: Host(`zoo.industries`) || Host(`www.zoo.industries`)
|
||||
kind: Rule
|
||||
priority: 100
|
||||
middlewares:
|
||||
- name: industries
|
||||
services:
|
||||
- name: zoo-industries
|
||||
port: 80
|
||||
tls: {}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
# Generated from the live zoo-mainnet/zoo-ngo Deployment on do-sfo3-zoo-k8s
|
||||
# by hack/live2values.py. Live is the source: a repo of hand-applied
|
||||
# manifests has unknown currency, the running fleet does not.
|
||||
replicas: 2
|
||||
selectorLabels:
|
||||
app: zoo-ngo
|
||||
containerName: ngo
|
||||
image:
|
||||
repository: ghcr.io/zooai/ngo
|
||||
tag: main-amd64
|
||||
digest: sha256:7905525af45fd3955d391c0696c286bb7a1f1a8399efe7aaa09a0b3747a1cf83
|
||||
pullPolicy: IfNotPresent
|
||||
partOf: zoo
|
||||
labels:
|
||||
app.kubernetes.io/cluster: zoo-k8s
|
||||
app.kubernetes.io/env: mainnet
|
||||
resources:
|
||||
limits:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 64Mi
|
||||
ports:
|
||||
- containerPort: 80
|
||||
imagePullSecrets:
|
||||
- name: ghcr-zooai
|
||||
rollingUpdate:
|
||||
maxSurge: 25%
|
||||
maxUnavailable: 25%
|
||||
topologySpread:
|
||||
enabled: false
|
||||
lifecycle: null
|
||||
terminationGracePeriodSeconds: 30
|
||||
defaultReadinessProbe: false
|
||||
|
||||
# Enforcement switch, read by the zoo-fleet ApplicationSet's templatePatch --
|
||||
# not by the chart. selfHeal returns a hand-edited object to what is written
|
||||
# here; prune stays off, so nothing is ever deleted.
|
||||
cd:
|
||||
automated: true
|
||||
@@ -1,91 +1,51 @@
|
||||
# zoo.vote — the governance interface, on Zoo's own fleet.
|
||||
# zoo.vote is files: one Middleware that serves them, one route that names it,
|
||||
# and the endpoint-less Service the route schema requires. No image, so the
|
||||
# chart renders no Deployment -- a static site is not a pod.
|
||||
#
|
||||
# It was declared in lux/universe deploy/zoo-mainnet/zoo-vote.yaml, which
|
||||
# lux-fleet reads. zoo-fleet reads THIS repository (hanzo/universe
|
||||
# infra/k8s/hanzo-cd/project-zoo.yaml, generator path deploy/*/*.yaml), so a
|
||||
# Zoo service declared in Lux's universe is a second Application for one
|
||||
# workload. Declared here, deleted there.
|
||||
replicas: 1
|
||||
selectorLabels:
|
||||
app: zoo-vote
|
||||
containerName: spa
|
||||
image:
|
||||
repository: ghcr.io/zooai/vote
|
||||
# ONE source: luxfi/vote. Its `.hanzo/workflows/image.yml` builds that tree
|
||||
# three times — lux, zoo, hanzo — each into its own org's registry, so this
|
||||
# image is Zoo's and is pulled with Zoo's credential. The tenant is a
|
||||
# build-arg (VITE_VOTE_HOME=zoo) and the site is settled at runtime by the
|
||||
# host, so the bundle here is Zoo's by both.
|
||||
#
|
||||
# TODO: pin the first tag that workflow publishes, with
|
||||
# `deploy/pin.sh zoo-mainnet/zoo-vote <version>` so the digest is written
|
||||
# beside it. The tag below predates the workflow: it is what the live
|
||||
# Deployment is running, kept because naming a tag the registry does not have
|
||||
# takes zoo.vote down. It could not be verified from a workstation — ghcr.io
|
||||
# refuses an anonymous pull token for this repository — so it is a tag and
|
||||
# not a digest.
|
||||
tag: votesguard-zoo2
|
||||
pullPolicy: Always
|
||||
partOf: zoo
|
||||
labels:
|
||||
app.kubernetes.io/cluster: zoo-k8s
|
||||
app.kubernetes.io/env: mainnet
|
||||
resources:
|
||||
limits:
|
||||
cpu: 200m
|
||||
memory: 256Mi
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 32Mi
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 3000
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 30
|
||||
successThreshold: 1
|
||||
timeoutSeconds: 1
|
||||
readinessProbe:
|
||||
failureThreshold: 3
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 3000
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 2
|
||||
periodSeconds: 10
|
||||
successThreshold: 1
|
||||
timeoutSeconds: 1
|
||||
ports:
|
||||
- containerPort: 3000
|
||||
name: http
|
||||
servicePort: 80
|
||||
imagePullSecrets:
|
||||
- name: ghcr-zooai
|
||||
rollingUpdate:
|
||||
maxSurge: 25%
|
||||
maxUnavailable: 25%
|
||||
topologySpread:
|
||||
enabled: false
|
||||
lifecycle: null
|
||||
terminationGracePeriodSeconds: 30
|
||||
|
||||
# The host is what tells the bundle it is Zoo — every label of the hostname is
|
||||
# read against the tenants it knows — so the names routed here are part of the
|
||||
# app's behaviour and not only of its reachability. www included, because the
|
||||
# app resolves it to the same tenant and a visitor who types it should not land
|
||||
# on a certificate error.
|
||||
ingress:
|
||||
enabled: true
|
||||
hosts:
|
||||
- zoo.vote
|
||||
- www.zoo.vote
|
||||
tls: true
|
||||
className: ingress
|
||||
|
||||
# Enforcement switch, read by the zoo-fleet ApplicationSet's templatePatch --
|
||||
# not by the chart. selfHeal returns a hand-edited object to what is written
|
||||
# here; prune stays off, so nothing is ever deleted.
|
||||
# The bundle is Zoo's own build. luxfi/vote is built once per tenant and the Zoo
|
||||
# build carries VITE_VOTE_HOME=zoo, so the objects in this prefix are the
|
||||
# `public/` tree of
|
||||
# ghcr.io/zooai/vote@sha256:22c778f5b14a61116680bf4d1bcfb34d9499099af306df71ef8bae120a65e131,
|
||||
# the digest the Deployment ran. That server read no SPA_* variable and so wrote
|
||||
# nothing at boot -- the prefix is the whole site, with nothing a pod added.
|
||||
#
|
||||
# The tag it named, votesguard-zoo2, is mutable and no lane publishes here yet. A
|
||||
# rebuild ships by syncing `public/` to this prefix, the way every other site on
|
||||
# the plane does.
|
||||
cd:
|
||||
automated: true
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
middlewares:
|
||||
- name: vote
|
||||
spec:
|
||||
staticFiles:
|
||||
root: s3://hanzo-sites/zoo/vote
|
||||
# Vite emits ONE index.html for the whole app and the client router
|
||||
# resolves every path, so a deep link has no file behind it and a miss must
|
||||
# answer with the shell. Contrast zoo-industries, a file per route.
|
||||
spaMode: true
|
||||
spaIndex: index.html
|
||||
indexFiles:
|
||||
- index.html
|
||||
routes:
|
||||
- name: vote
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: Host(`zoo.vote`) || Host(`www.zoo.vote`)
|
||||
kind: Rule
|
||||
# Above the Ingress this replaces, so the route is authoritative from the
|
||||
# moment it exists rather than racing an object prune does not remove.
|
||||
priority: 100
|
||||
middlewares:
|
||||
- name: vote
|
||||
services:
|
||||
- name: zoo-vote
|
||||
port: 80
|
||||
# The same default certificate the Ingress served: its cert-manager
|
||||
# Certificate never went Ready, so naming the empty secret would change the
|
||||
# bytes on the wire for nothing.
|
||||
tls: {}
|
||||
|
||||
Reference in New Issue
Block a user