9.1 KiB
Per AI Blitz Plan §platform.procedures. Layer: 1. Repo: devops-infra-helm-charts.
Procedure — Add or modify a Contour HTTPProxy route
Layer: Layer 1 — values diff + PR. Blast radius: one Contour release × one cluster (a misrouted host can break ingress for the whole BU). Approval: consuming-app owner + cluster owner. Platform team if the change touches
contour-external*.
This procedure covers HTTPProxy route changes that flow through one of the cluster's Contour releases. Most clusters run multiple Contour instances — read the contour-nodeselector-tolerations-summary.md (repo root) authoritative scheduling matrix before editing any Contour values.
Multi-Contour topology
| Release name | Plane | Typical purpose |
|---|---|---|
contour-external |
North-south | Public / external traffic (terminates at GCP external LB) |
contour-external-1 |
North-south | Second external Contour (blue-green or capacity split) |
contour-internal-0 |
East-west | Internal traffic, plane 0 |
contour-internal-1 |
East-west | Internal traffic, plane 1 |
contour-internal-intra-0 |
Intra-cluster | Cluster-local east-west, plane 0 |
contour-internal-intra-1 |
Intra-cluster | Cluster-local east-west, plane 1 |
Each is a separate Helm release, on a separate node pool, with its own nodeSelector / tolerations / computeClass. Picking the wrong release is the most common authoring mistake.
When to use
- Adding a new HTTPProxy / route for a service.
- Changing TLS, retry, timeout, or rate-limit policy on an existing route.
- Re-pointing an HTTPProxy at a different upstream Service.
- Adding a new host to an existing route's
virtualhost.fqdn.
Do not use this procedure for:
- Changing Contour itself (sizing, scheduling, image) — that's modify-observability-config.md-style infra editing on the Contour release.
- Bumping Contour's chart version — use update-chart-version.md and consult the versioned sibling (
contour-v1.33.3). - Curling production hostnames to test — SANCTITY_RULES R3 forbids it. Test from inside the cluster with a
curlPod.
Inputs
| Input | Example |
|---|---|
| Target cluster | k8s-supply-prd-ase1 |
| Target Contour release | contour-internal-0 |
| HTTPProxy host | api-foo.internal.meeshogcp.in |
| Upstream Service | foo-svc.foo-ns:8080 |
| TLS source | cert-manager / external-secret / none |
| Path prefixes | /v1, /health |
| Approval ticket | CMR-… (if BU policy) |
Pre-conditions
- You know which Contour release is the right one for this host (north-south =
external*; east-west between BUs =internal-{0,1}; cluster-local =internal-intra-{0,1}). When in doubt, sample existing HTTPProxies in the same namespace. - The upstream Service exists or will exist by the time of Sync.
- The DNS name follows the cluster's
external-dnspattern. - The TLS source (Secret, Issuer, etc.) exists on the cluster.
Steps
1. Identify the right Contour release
ls helm-overrides/<cluster>/ | grep '^contour'
Pick the release whose plane matches the new route's traffic class. Cross-reference contour-nodeselector-tolerations-summary.md. If unsure, look at existing HTTPProxies on the cluster:
# Read existing routes already shipped via this repo
yq e '.. | select(has("httpproxies"))' \
helm-overrides/<cluster>/contour-internal-0/custom-values.yaml
2. Decide where the HTTPProxy lives
Two patterns exist:
| Pattern | When | Where to author |
|---|---|---|
| Inline in Contour values | Routes shared by the cluster's infra layer (e.g. Grafana, Argo CD). | helm-overrides/<cluster>/<contour-release>/custom-values.yaml under httpproxies: (if the chart supports it) or as a sidecar manifest in the same dir. |
| In the consuming app's repo | Routes for a specific service. | The service's own deployment artifacts. Out of scope for this repo. |
If the route is service-owned, redirect to the consuming team's repo and stop. This procedure only covers infra-layer HTTPProxies.
3. Author the HTTPProxy
Skeleton (sidecar manifest pattern):
apiVersion: projectcontour.io/v1
kind: HTTPProxy
metadata:
name: <name>
namespace: <ns>
spec:
virtualhost:
fqdn: <fqdn>
tls:
secretName: <tls-secret> # cert-manager-managed Secret
routes:
- conditions:
- prefix: /
services:
- name: <upstream-service>
port: <port>
timeoutPolicy:
response: 30s
retryPolicy:
count: 2
retryOn: 5xx
Drop into helm-overrides/<cluster>/<contour-release>/httpproxies/<name>.yaml (sidecar manifest) — see ../schemas/raw-manifest-sidecar-schema.md.
4. Lint the HTTPProxy
# Schema check (kubectl --dry-run against the cluster's CRD)
kubectl --context=<ctx> --dry-run=server -f helm-overrides/<cluster>/<contour-release>/httpproxies/<name>.yaml apply
# Or local: validate against the projectcontour CRD schema
kubeconform -schema-location default -schema-location \
'https://raw.githubusercontent.com/projectcontour/contour/main/examples/contour/01-crds.yaml' \
helm-overrides/<cluster>/<contour-release>/httpproxies/<name>.yaml
5. Render the chart
helm template <contour-release> helm-templates/<contour-chart> \
-f helm-overrides/<cluster>/<contour-release>/custom-values.yaml > /tmp/contour.yaml
The render must succeed; the HTTPProxy sidecar is applied alongside, not through Helm — but rendering catches values-side errors that would block sync of the same Argo Application.
6. Test from inside the cluster (post-merge, not pre-merge)
Do not curl <fqdn>.meeshogcp.in from your laptop / a build agent. Run a curl Pod on-cluster:
kubectl --context=<ctx> run -it --rm curl-test \
--image=curlimages/curl --restart=Never -- \
curl -v -H "Host: <fqdn>" http://<contour-svc>.projectcontour.svc.cluster.local
7. Open the PR
git checkout -b contour/<cluster>-<route-name>
git add helm-overrides/<cluster>/<contour-release>/
git commit -m "contour(<cluster>/<contour-release>): add route <name>"
git push origin contour/<cluster>-<route-name>
gh pr create --base main
PR description template
## Summary
Adds (or modifies) HTTPProxy `<name>` on `<cluster>` via `<contour-release>` for FQDN `<fqdn>`.
## Why
<1-2 sentences>
## Topology
- Cluster: `<cluster>`
- Contour release: `<contour-release>` (plane: external / internal / intra)
- FQDN: `<fqdn>`
- Upstream Service: `<svc>:<port>` in namespace `<ns>`
- TLS: cert-manager Secret `<tls-secret>`
## Validation
- [ ] HTTPProxy CRD schema validation passed (kubeconform / kubectl --dry-run)
- [ ] `helm template` rendered cleanly
- [ ] On-cluster curl from a curl Pod returns expected status
- [ ] DNS / external-dns plumbed (existing wildcard or new external-dns Service)
## Approvers
- App owner: <handle>
- Cluster owner: <handle>
- Platform (if `contour-external*`): <handle>
8. After merge — Sync
Open the cluster's Argo CD UI, find the Contour release's Application, Sync. Verify:
kubectl --context=<ctx> -n projectcontour get httpproxy <name>
kubectl --context=<ctx> -n projectcontour describe httpproxy <name> | grep -A5 'Status:'
A Valid: true status means Contour accepted the route. Valid: false with a reason → fix the values and re-PR.
If the HTTPProxy is Valid but traffic still 5xx → ../runbooks/ingress-down.md §4.
Anti-patterns
- Wrong Contour release. A route mounted on
contour-internal-intra-0is unreachable from outside the cluster. Cross-check the matrix. - Curling the production FQDN from a developer machine to "test." Forbidden — see SANCTITY_RULES R3.
- Hand-edited values for a single host across all Contour releases — pick one release, justify it.
tls.passthroughfor plain HTTP services — silent: TLS terminates upstream, doesn't.- Wildcard hosts that overlap an existing HTTPProxy — Contour status will mark one of them invalid; check before merging.
- Editing
helm-templates/contour*/to "tweak the chart." Layer 1 forbids casual chart edits — the chart is vanilla upstream.
Rollback
- Revert the values PR.
- Sync the Contour Application — Argo will prune the HTTPProxy or restore the prior values.
Related
- Reference (authoritative scheduling): ../../../contour-nodeselector-tolerations-summary.md.
- Runbook: ../runbooks/ingress-down.md.
- Schema: ../schemas/raw-manifest-sidecar-schema.md.
- Schema: ../schemas/custom-values-schema.md.
- ADR: ../../../wiki/analyses/ADR-A3-per-cluster-scheduling.md.