Fold the deployshed ingresses into their charts, keep nip.io for Harbor
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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LEsTefWWifp4ikvhHF5s6N
This commit is contained in:
co-authored by
Claude Opus 5
parent
5a008ffa08
commit
1e8cc7a028
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
||||||
@@ -29,13 +29,26 @@ spec:
|
|||||||
data:
|
data:
|
||||||
# Keyed by exact registry hostname — docker matches credentials
|
# Keyed by exact registry hostname — docker matches credentials
|
||||||
# against the host portion of the image reference, so this must be
|
# against the host portion of the image reference, so this must be
|
||||||
# the same hostname the pipeline pushes to, the node pool was told
|
# the same hostname the pipeline pushes to and Harbor's own
|
||||||
# to trust, and Harbor's own externalURL uses. On GKE that is the
|
# externalURL uses. On GKE that is the Contour ingress host, never
|
||||||
# Contour ingress host, never harbor-core.harbor.svc.cluster.local:
|
# harbor-core.harbor.svc.cluster.local: cluster DNS resolves from a
|
||||||
# cluster DNS resolves from a pod but not from the node's containerd
|
# pod but not from the node's containerd doing the actual image pull.
|
||||||
# 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: |
|
.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:
|
data:
|
||||||
- secretKey: username
|
- secretKey: username
|
||||||
remoteRef:
|
remoteRef:
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ appSpec:
|
|||||||
valuesDir: gitea
|
valuesDir: gitea
|
||||||
- name: contour
|
- name: contour
|
||||||
# The cluster's only inbound path, pinned to the reserved IP that every
|
# 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
|
# hand, which is only possible because nothing in the bootstrap needed
|
||||||
# an ingress: ArgoCD reaches Gitea over cluster DNS.
|
# an ingress: ArgoCD reaches Gitea over cluster DNS.
|
||||||
nameOverride: contour
|
nameOverride: contour
|
||||||
|
|||||||
Reference in New Issue
Block a user