From 1e8cc7a0282aefa515f100703555226cadc9685d Mon Sep 17 00:00:00 2001 From: Mukul Sharma Date: Thu, 17 Sep 2026 09:32:33 +0530 Subject: [PATCH] Fold the deployshed ingresses into their charts, keep nip.io for Harbor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Argo CD and Harbor each had a standalone Ingress serving the real domain while their charts still served nip.io. Both charts now own their real hostname directly, so the standalone objects are removed — extra-manifests syncs the directory with prune, so deleting the files deletes the objects. harbor-ingress-legacy-nipio.yaml is the inverse of the file it replaces. Harbor's chart now serves harbor.infra.deployshed.com with a Let's Encrypt certificate, and this object serves the old nip.io name with the private CA the chart used to use. It has to exist: apps deployed before the move recorded harbor.35.238.248.203.nip.io image references in toolshed's database and in already-rendered pod specs, and nothing in git can rewrite those — they change only when each app is rebuilt. Until then a pod that restarts, reschedules or scales up pulls the old reference, and if the name stops resolving that pull fails looking like a broken registry rather than a retired hostname. It retires together with the registry-ca manifests and Terraform's node-pool trust. harbor-robot-dockerconfig lists both hostnames. Docker matches stored credentials by exact host and nothing else, so a single-value swap breaks whichever side lands second: change the credential first and the pipeline pushes unauthenticated, change the pipeline first and the same happens in reverse. Two entries make the order irrelevant — it is one robot account on one Harbor reached by two names. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01LEsTefWWifp4ikvhHF5s6N --- .../argocd-ingress-deployshed.yaml | 45 --------- .../harbor-ingress-deployshed.yaml | 85 ----------------- .../harbor-ingress-legacy-nipio.yaml | 92 +++++++++++++++++++ secretstores/harbor-robot-dockerconfig.yaml | 25 +++-- ...or-infra-gke-toolshed-prd-usc1-values.yaml | 2 +- 5 files changed, 112 insertions(+), 137 deletions(-) delete mode 100644 extra-manifests/argocd-ingress-deployshed.yaml delete mode 100644 extra-manifests/harbor-ingress-deployshed.yaml create mode 100644 extra-manifests/harbor-ingress-legacy-nipio.yaml diff --git a/extra-manifests/argocd-ingress-deployshed.yaml b/extra-manifests/argocd-ingress-deployshed.yaml deleted file mode 100644 index a1097bd..0000000 --- a/extra-manifests/argocd-ingress-deployshed.yaml +++ /dev/null @@ -1,45 +0,0 @@ -# ArgoCD's own hostname on the real domain, with TLS. -# -# A standalone Ingress rather than the chart's, because the argo-cd chart -# cannot express this. Its server.ingress.tls is a BOOLEAN covering -# `hostname` plus every entry in `extraHosts`, all from one fixed secret, -# and there is no extraTls to scope it more narrowly. Turning it on while -# the nip.io hostname is still served would request a single certificate -# spanning both names — and Let's Encrypt cannot issue for nip.io, so the -# request fails and NEITHER name ends up with TLS. -# -# So the chart keeps serving argocd.35.238.248.203.nip.io over plain HTTP -# through its own Ingress, and this object serves the real domain over TLS. -# Both route to the same backend. When nip.io is retired, this file should -# go away and the chart's own tls: true should be turned on instead — one -# hostname, one certificate, no second object to remember. -# -# Backed by port 80, not 443: this deployment runs argocd-server with -# server.insecure: true, so it speaks plain HTTP and TLS terminates at -# Envoy. Pointing at 443 would have Envoy attempt TLS to a backend that -# does not serve it. -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: argocd-ingress-deployshed - namespace: argocd - annotations: - # ingress-shim issues and renews the certificate named in tls below. - cert-manager.io/cluster-issuer: letsencrypt-prod -spec: - ingressClassName: contour - tls: - - hosts: - - argocd.infra.deployshed.com - secretName: argocd-deployshed-tls - rules: - - host: argocd.infra.deployshed.com - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: argocd-admin-prd-server - port: - number: 80 diff --git a/extra-manifests/harbor-ingress-deployshed.yaml b/extra-manifests/harbor-ingress-deployshed.yaml deleted file mode 100644 index 5f5a748..0000000 --- a/extra-manifests/harbor-ingress-deployshed.yaml +++ /dev/null @@ -1,85 +0,0 @@ -# Harbor's hostname on the real domain, with a public certificate. -# -# A standalone Ingress because Harbor's chart has no multi-host mechanism at -# all — no extraHosts like argo-cd, no secondaryingress like jenkins. Its -# expose.ingress.hosts.core is a single scalar, so the only way to serve a -# second name is a second object. Checked against the chart, not assumed. -# -# The chart's own Ingress is untouched and keeps serving -# harbor.35.238.248.203.nip.io with its private-CA certificate. Both names -# route to the same backends. -# -# THE PATH SPLIT IS LOAD-BEARING. harbor-core is the API, auth and registry -# backend; harbor-portal is only the web UI. Mirrored from this cluster's own -# generated Ingress (read from the live object, not copied blind): /api/, -# /service/, /v2/ and /c/ go to core, everything else to the portal. Sending -# /v2/ to the portal instead breaks every image pull, and it fails looking -# like an authentication problem rather than a routing one. -# -# Contour matches the longest prefix, so the trailing / rule cannot shadow -# the four above it regardless of order — they are written first anyway, so -# the intent is obvious to whoever reads this next. -# -# WHAT THIS DOES NOT DO: Harbor still advertises the nip.io address. -# externalURL is a single value handed to docker clients in Harbor's own API -# responses, so the UI may redirect there and a docker login against this -# hostname is issued a token endpoint pointing at the old one. Both names -# resolve, so it works — but this name is not truly primary until -# externalURL moves, and that cannot happen until every image reference in -# the cluster moves with it. That is the nip.io removal, done deliberately -# and on its own. -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: harbor-ingress-deployshed - namespace: harbor - annotations: - # A real certificate, unlike the chart's Ingress beside it: that one - # serves a nip.io name, which Let's Encrypt cannot issue for, and is - # signed by the private CA the node pool was told to trust instead. - cert-manager.io/cluster-issuer: letsencrypt-prod -spec: - ingressClassName: contour - tls: - - hosts: - - harbor.infra.deployshed.com - secretName: harbor-deployshed-tls - rules: - - host: harbor.infra.deployshed.com - http: - paths: - - path: /api/ - pathType: Prefix - backend: - service: - name: harbor-core - port: - number: 80 - - path: /service/ - pathType: Prefix - backend: - service: - name: harbor-core - port: - number: 80 - - path: /v2/ - pathType: Prefix - backend: - service: - name: harbor-core - port: - number: 80 - - path: /c/ - pathType: Prefix - backend: - service: - name: harbor-core - port: - number: 80 - - path: / - pathType: Prefix - backend: - service: - name: harbor-portal - port: - number: 80 diff --git a/extra-manifests/harbor-ingress-legacy-nipio.yaml b/extra-manifests/harbor-ingress-legacy-nipio.yaml new file mode 100644 index 0000000..80a8354 --- /dev/null +++ b/extra-manifests/harbor-ingress-legacy-nipio.yaml @@ -0,0 +1,92 @@ +# Harbor's OLD hostname, kept alive on the private CA. +# +# This is the inverse of what this file used to be. Harbor's own chart now +# serves harbor.infra.deployshed.com with a Let's Encrypt certificate, and +# this standalone object serves the legacy nip.io name that the chart used +# to own. +# +# WHY IT STILL EXISTS, and when it can go. Apps deployed before the move +# recorded their image reference — harbor.35.238.248.203.nip.io/... — in +# toolshed's database (apps.current_image) and in already-rendered pod +# specs. Nothing in git can rewrite those; they change only when each app is +# rebuilt and redeployed. Until then a running pod that restarts, reschedules +# or scales up pulls the old reference, and if this name stops resolving that +# pull fails. The failure would look like a broken registry rather than a +# retired hostname. +# +# Delete this file, registry-ca-clusterissuer.yaml and registry-ca-configmap.yaml +# together, once no image reference anywhere names the old host. Check with: +# +# kubectl get pods -A -o jsonpath='{range .items[*]}{.spec.containers[*].image}{"\n"}{end}' | grep nip.io +# (and toolshed's own: select current_image from apps) +# +# The node pool's private_registry_access_config in Terraform retires at the +# same time, but that one recycles nodes, so it belongs in a maintenance +# window rather than in a cleanup commit. +# +# THE PATH SPLIT IS LOAD-BEARING. harbor-core is the API, auth and registry +# backend; harbor-portal is only the web UI. /api/, /service/, /v2/ and /c/ +# go to core, everything else to the portal. Sending /v2/ to the portal +# instead breaks every image pull, and it fails looking like an +# authentication problem rather than a routing one. +# +# Contour matches the longest prefix, so the trailing / rule cannot shadow +# the four above it regardless of order — they are written first anyway, so +# the intent is obvious to whoever reads this next. +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: harbor-ingress-legacy-nipio + namespace: harbor + annotations: + # The private CA, not Let's Encrypt, which cannot issue for a nip.io + # name: it is not on the public suffix list, and every *.nip.io + # certificate shares one rate limit. The GKE node pool was told to trust + # this CA for exactly this hostname when it was created, which is what + # makes the old image references still pullable. + cert-manager.io/cluster-issuer: registry-ca-issuer +spec: + ingressClassName: contour + tls: + - hosts: + - harbor.35.238.248.203.nip.io + secretName: harbor-tls + rules: + - host: harbor.35.238.248.203.nip.io + http: + paths: + - path: /api/ + pathType: Prefix + backend: + service: + name: harbor-core + port: + number: 80 + - path: /service/ + pathType: Prefix + backend: + service: + name: harbor-core + port: + number: 80 + - path: /v2/ + pathType: Prefix + backend: + service: + name: harbor-core + port: + number: 80 + - path: /c/ + pathType: Prefix + backend: + service: + name: harbor-core + port: + number: 80 + - path: / + pathType: Prefix + backend: + service: + name: harbor-portal + port: + number: 80 diff --git a/secretstores/harbor-robot-dockerconfig.yaml b/secretstores/harbor-robot-dockerconfig.yaml index a7b14c4..332f174 100644 --- a/secretstores/harbor-robot-dockerconfig.yaml +++ b/secretstores/harbor-robot-dockerconfig.yaml @@ -29,13 +29,26 @@ spec: data: # Keyed by exact registry hostname — docker matches credentials # against the host portion of the image reference, so this must be - # the same hostname the pipeline pushes to, the node pool was told - # to trust, and Harbor's own externalURL uses. On GKE that is the - # Contour ingress host, never harbor-core.harbor.svc.cluster.local: - # cluster DNS resolves from a pod but not from the node's containerd - # doing the actual image pull. + # the same hostname the pipeline pushes to and Harbor's own + # externalURL uses. On GKE that is the Contour ingress host, never + # harbor-core.harbor.svc.cluster.local: cluster DNS resolves from a + # pod but not from the node's containerd doing the actual image pull. + # + # BOTH hostnames are listed, deliberately, while the deployment moves + # off nip.io. Because the match is by exact host and nothing else, a + # single-value swap breaks whichever side lands second: change this + # first and the pipeline (still pushing to nip.io) pushes + # unauthenticated, which Harbor rejects as "unauthorized"; change the + # pipeline first and the same happens in reverse. Two entries make + # the order irrelevant — the credential is identical either way, + # since it is one robot account on one Harbor reached by two names. + # + # The nip.io entry comes out once no image reference anywhere names + # it: the pipeline's push target, the fallback Dockerfiles, the + # mirrored base images, and every app already deployed from an image + # tagged under the old name. .dockerconfigjson: | - {"auths":{"harbor.35.238.248.203.nip.io":{"username":"{{ .username }}","password":"{{ .password }}","auth":"{{ printf "%s:%s" .username .password | b64enc }}"}}} + {"auths":{"harbor.infra.deployshed.com":{"username":"{{ .username }}","password":"{{ .password }}","auth":"{{ printf "%s:%s" .username .password | b64enc }}"},"harbor.35.238.248.203.nip.io":{"username":"{{ .username }}","password":"{{ .password }}","auth":"{{ printf "%s:%s" .username .password | b64enc }}"}}} data: - secretKey: username remoteRef: diff --git a/values/incubator-infra-gke-toolshed-prd-usc1-values.yaml b/values/incubator-infra-gke-toolshed-prd-usc1-values.yaml index d3521eb..0796449 100644 --- a/values/incubator-infra-gke-toolshed-prd-usc1-values.yaml +++ b/values/incubator-infra-gke-toolshed-prd-usc1-values.yaml @@ -51,7 +51,7 @@ appSpec: valuesDir: gitea - name: contour # The cluster's only inbound path, pinned to the reserved IP that every - # nip.io hostname is built from. Installed by ArgoCD rather than by + # deployshed.com record resolves to. Installed by ArgoCD rather than by # hand, which is only possible because nothing in the bootstrap needed # an ingress: ArgoCD reaches Gitea over cluster DNS. nameOverride: contour