Files
devops-infra-helm-charts-gcp/docs/platform/procedures/add-contour-route.md
T
2026-08-26 03:39:42 +05:30

9.1 KiB
Raw Blame History

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 curl Pod.

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-dns pattern.
  • 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

  1. Wrong Contour release. A route mounted on contour-internal-intra-0 is unreachable from outside the cluster. Cross-check the matrix.
  2. Curling the production FQDN from a developer machine to "test." Forbidden — see SANCTITY_RULES R3.
  3. Hand-edited values for a single host across all Contour releases — pick one release, justify it.
  4. tls.passthrough for plain HTTP services — silent: TLS terminates upstream, doesn't.
  5. Wildcard hosts that overlap an existing HTTPProxy — Contour status will mark one of them invalid; check before merging.
  6. 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.