buildDocker.groovy hardcoded harbor.35.238.248.203.nip.io as the push target, so the registry could not move without editing this shared library and every consumer moving in the same commit. It now reads config.harbor_registry, whose default lives in homelabPipeline.groovy beside harbor_project and every other key. The stage errors rather than defaulting when the value is missing. Carrying a second copy of the literal would leave two defaults free to disagree, and an unset value would otherwise build an image named "null/<project>/<repo>" — which docker accepts as a hostname and then fails to resolve, pointing nowhere near the cause. The dind pod no longer mounts the registry CA. That mount existed because the registry was a nip.io name, which no public CA will issue for, so cert-manager signed Harbor from a private CA; the node pool was told to trust it for pulls, but a push comes from dockerd inside the build pod, which has its own trust store. harbor.infra.deployshed.com carries a Let's Encrypt certificate that both already trust, so the mount, its volume and the whole arrangement go away rather than being repointed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LEsTefWWifp4ikvhHF5s6N
112 lines
5.4 KiB
YAML
112 lines
5.4 KiB
YAML
# New resource — didn't exist in the original devops-lib. Homelab's real
|
|
# pipelines built on static agents (node('slave02')) with Docker already
|
|
# available; this homelab's Jenkins uses dynamic per-build Kubernetes
|
|
# agents (agent.enabled in the jenkins chart), which don't have a Docker
|
|
# daemon by default. This pod template adds one as a sidecar container —
|
|
# the "docker" container runs privileged dind, "docker-cli" is what the
|
|
# pipeline actually execs into via container('docker-cli'), talking to
|
|
# its sibling over localhost since containers in one pod share a network
|
|
# namespace.
|
|
apiVersion: v1
|
|
kind: Pod
|
|
spec:
|
|
containers:
|
|
- name: docker
|
|
image: docker:27-dind
|
|
securityContext:
|
|
privileged: true
|
|
# No --insecure-registry and no mounted CA: Harbor is reached at
|
|
# harbor.infra.deployshed.com, which carries a publicly trusted Let's
|
|
# Encrypt certificate, so dockerd's own trust store already accepts it
|
|
# with nothing configured.
|
|
#
|
|
# This previously mounted the private registry CA here, because the
|
|
# registry was a nip.io name that no public CA can issue for (nip.io is
|
|
# not on the public suffix list, and every *.nip.io certificate shares
|
|
# one rate limit), so cert-manager signed it from a private CA instead.
|
|
# A pull was fine — the node pool was told to trust that CA and
|
|
# containerd performs pulls — but a push is a different client with its
|
|
# own trust store, which is why dockerd needed the CA at
|
|
# /etc/docker/certs.d/<registry host>/ca.crt. Moving to a real domain
|
|
# removes the whole arrangement rather than repointing it.
|
|
#
|
|
# The registry is still named by its ingress hostname rather than
|
|
# harbor-core.harbor.svc.cluster.local, for a reason that still holds:
|
|
# cluster DNS resolves from this pod but not from the node's containerd
|
|
# doing the real Deployment pull, and Docker matches both stored
|
|
# credentials and trust by exact hostname — so push and pull have to
|
|
# name the registry identically.
|
|
env:
|
|
- name: DOCKER_TLS_CERTDIR
|
|
value: ""
|
|
volumeMounts:
|
|
- name: docker-graph-storage
|
|
mountPath: /var/lib/docker
|
|
- name: docker-cli
|
|
# Custom image, built and pushed by hand from devops-base-images-gcp
|
|
# (build-tools.Dockerfile there) — bakes in git/yq/bash/python3 with
|
|
# pip and venv/curl so nothing needs installing on every single build,
|
|
# which was slow and quietly undermined reproducibility.
|
|
#
|
|
# It lives in the base-images project rather than homelab because that
|
|
# project is public: this pod pulls the image before any credential is
|
|
# available to it.
|
|
#
|
|
# Versioned tag, never :latest — rebuilding the tools image must not
|
|
# roll out until this pin is bumped deliberately.
|
|
image: harbor.infra.deployshed.com/base-images/build-tools:1
|
|
command: ["cat"]
|
|
tty: true
|
|
env:
|
|
- name: DOCKER_HOST
|
|
value: tcp://localhost:2375
|
|
# docker-config below mounts the Harbor push-auth secret
|
|
# read-only at /root/.docker (needed so `docker push` finds
|
|
# config.json without an explicit `docker login` step) — but
|
|
# modern `docker build` defaults to BuildKit/buildx, which wants
|
|
# to create its own state dir at /root/.docker/buildx and fails
|
|
# with "read-only file system" since the whole mount is
|
|
# read-only. Forcing the classic builder avoids needing to write
|
|
# there at all.
|
|
- name: DOCKER_BUILDKIT
|
|
value: "0"
|
|
# For syncArgoApp.groovy — read directly from the ESO-managed
|
|
# Secret, not a Jenkins-native credential (nothing in this
|
|
# pipeline uses Jenkins' own credential store; staying consistent
|
|
# rather than mixing the two approaches).
|
|
- name: ARGOCD_TOKEN
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: argocd-jenkins-ci-token
|
|
key: token
|
|
# Without this, every build using this pod template hard-fails
|
|
# to even start until the token secret exists — including the
|
|
# very first demo-go-app run, before the manual
|
|
# `argocd account generate-token` bootstrap step has happened.
|
|
optional: true
|
|
volumeMounts:
|
|
- name: docker-config
|
|
mountPath: /root/.docker
|
|
readOnly: true
|
|
volumes:
|
|
- name: docker-graph-storage
|
|
emptyDir: {}
|
|
- name: docker-config
|
|
secret:
|
|
secretName: harbor-robot-dockerconfig
|
|
# kubernetes.io/dockerconfigjson secrets store their data under
|
|
# the fixed key `.dockerconfigjson` (mandated, since that's what
|
|
# kubelet reads for imagePullSecrets) — without this remap, the
|
|
# mounted file at /root/.docker is literally named
|
|
# `.dockerconfigjson`, not `config.json`, which is the only
|
|
# filename the docker CLI itself ever reads for stored
|
|
# credentials. Docker found nothing there and silently pushed
|
|
# unauthenticated, which Harbor correctly rejected as
|
|
# unauthorized — confirmed the robot account/credentials
|
|
# themselves were fine the whole time by requesting a push token
|
|
# directly from Harbor's token endpoint with the same username/
|
|
# password and getting one back with push access granted.
|
|
items:
|
|
- key: .dockerconfigjson
|
|
path: config.json
|